4

Introduction:

I am reading image data of fingerprints from smart card and as you know this data save as raw image in smart card. I am developing a client side program which only use java script to read image from scanner of a card reader and show that in the client page.

Now my question:

How can I convert hex string of my raw data to a hex string which accomplished with appropriate header of bitmap image? Note that I have width and height of my image.

Tried methods:

I have been developed this program in java by get buffered image from raw data. Also, I could convert a hex string of a bit map image to base64 by Hex2Base64 and then I could show base64 string in an image tag by base64AsImage. However these functions work well if and only if the hex contains header, while our data is raw.

My code (that only works for Hex String which contains header):

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>

    <script>
        if (!window.atob) {
            var tableStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
            var table = tableStr.split("");

            window.atob = function (base64) {
                if (/(=[^=]+|={3,})$/.test(base64)) throw new Error("String contains an invalid character");
                base64 = base64.replace(/=/g, "");
                var n = base64.length & 3;
                if (n === 1) throw new Error("String contains an invalid character");
                for (var i = 0, j = 0, len = base64.length / 4, bin = []; i < len; ++i) {
                    var a = tableStr.indexOf(base64[j++] || "A"), b = tableStr.indexOf(base64[j++] || "A");
                    var c = tableStr.indexOf(base64[j++] || "A"), d = tableStr.indexOf(base64[j++] || "A");
                    if ((a | b | c | d) < 0) throw new Error("String contains an invalid character");
                    bin[bin.length] = ((a << 2) | (b >> 4)) & 255;
                    bin[bin.length] = ((b << 4) | (c >> 2)) & 255;
                    bin[bin.length] = ((c << 6) | d) & 255;
                };
                return String.fromCharCode.apply(null, bin).substr(0, bin.length + n - 4);
            };

            window.btoa = function (bin) {
                for (var i = 0, j = 0, len = bin.length / 3, base64 = []; i < len; ++i) {
                    var a = bin.charCodeAt(j++), b = bin.charCodeAt(j++), c = bin.charCodeAt(j++);
                    if ((a | b | c) > 255) throw new Error("String contains an invalid character");
                    base64[base64.length] = table[a >> 2] + table[((a << 4) & 63) | (b >> 4)] +
                    (isNaN(b) ? "=" : table[((b << 2) & 63) | (c >> 6)]) +
                    (isNaN(b + c) ? "=" : table[c & 63]);
                }
                return base64.join("");
            };

        }

        function hexToBase64(str) {
            return btoa(String.fromCharCode.apply(null,
                            str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))
            );
        }

        function base64ToHex(str) {
            for (var i = 0, bin = atob(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
                var tmp = bin.charCodeAt(i).toString(16);
                if (tmp.length === 1) tmp = "0" + tmp;
                hex[hex.length] = tmp;
            }
            return hex.join(" ");
        }
        function doConvert() {
            var myHex =  document.getElementById('myText').value;
            var myBase64 = hexToBase64(myHex);
            document.getElementById('myImage').src = "data:image/bmp;base64," +  myBase64;
        }
    </script>



</head>
<body>
<div>
    <p>
        Enter Raw Hex:
        <br>
        <textarea rows="4" cols="50" id="myText">Enter Raw Hex String here ...</textarea>
        <br>
        <button id="myButton" onclick="doConvert()"> Click me </button>
        <br>
        <img id="myImage" alt="img1"/>
    </p>
</div>
</body>
</html>

a part of code which solve the problem in java:

private static BufferedImage byte2Buffered(byte[] rawData, int width, int height){
    BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_BYTE_GRAY);
    byte[] array = ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
    System.arraycopy(rawData, 0, array, 0, rawData.length);
    return image;
}

Notice that, as there is not BufferedImage type in JavaScript, we could not equivalent this approach in JavaScript.

8
  • Do you know the color encoding? I.e gray scale or color? Commented Jan 23, 2016 at 14:35
  • Also bear in mind that java and javascript are totally different languages. Commented Jan 23, 2016 at 14:36
  • Dear @Matthieu my image is gray scale and of course that java is different from javascript. Absolutely I know this fact :) I told that I have done this prblem in java and I want to do it in javascript Commented Jan 23, 2016 at 14:40
  • Presumably, (since you say you have done before in java) you know the exact format of the file. Given this you can decode/interpret the file header, and you know the how each pixel is represented in the input format. If you know this already, you should be able to read the input into a bufferarray and then read as an xIntTypedArrayBuffer. Then read each bit and render it to canvas (or some other buffered array in which you are encoding into another format). Commented Jan 23, 2016 at 15:16
  • 2
    If you are only interested in writing the content out to the screen then you don't need to worry about image encoding; you can simply write out the pixel data to a canvas element -- see MDN for details. If you need to transcode for storage and later use, try using jimp or try any of the wrapper libraries for ImageMagick (I have yet to find a processing task that ImageMagick can't do). Be very mindful of biometric storage laws in your juristdiction. Commented Jan 24, 2016 at 4:54

1 Answer 1

1

In your window.atob method, you are building a string from an array of 8-bit integers already (that's what bin[length] is creating.) Just return that array instead of the string.

Then, if you have to support older browsers, you will need to write each pixel to the canvas individually. But if you can target modern browsers, just build a Uint8ClampedArray, put that into an ImageData object, and putImageData() into the canvas.

Below is some working sample code. I'm populating a dummy array with random bytes (data), but you would use the byte array returned from atob.

var canvas = document.querySelector('canvas'),
    ctx = canvas.getContext('2d'),
  width = canvas.width,
  height = canvas.height,
  pixelLength = width * height,
  data,
  imageData;

// You can use any kind of array, including a
// Uint8ClampedArray, since it is just going to be
// crammed into a clamped array anyway. I'm using a
// Uint8Array just as an example.
data = new Uint8Array(pixelLength);

// Create an array of random data
data = data.map(function (btye) { return Math.floor(Math.random() * 256); });

// The source data is 8-bit grayscale, but it needs 
// to be 32-bit RGBA in a Uint8ClampedArray. The
// structure is simple. For every byte of the gray-
// scale array, write out three copies of that byte 
// and then `256` as 100% opaque.
data = data.reduce(function (carry, current, index) {
    var baseIndex = index * 4;

  carry[baseIndex] = current;
    carry[baseIndex + 1] = current;
    carry[baseIndex + 2] = current;
    carry[baseIndex + 3] = 256;

return carry;
}, new Uint8ClampedArray(pixelLength * 4));

// The Uint8ClampedArray can now be used to build the image
imageData = new ImageData(data, width, height);
ctx.putImageData(imageData, 0, 0);
Sign up to request clarification or add additional context in comments.

6 Comments

See jsfiddle.net/36gLwqgv/1 . I'm using reduce here rather than a for() loop. See the MDN documentation for how to use it, but feel free to use for() instead.
Dear @Andrew I have caught this error in IE11: 'Uint8ClampedArray' is undefined. Indeed, I have an activeX (IE extension) which read raw image from smart card, so I need IE-based program. Although I have IE11 however I wish to support older IE which do not support HTML5.
[I've edited this comment] You can use the byte array. I would recommend finding an ActiveX drawing library to bitbash the data into an image. I'm sure there is something to do that.
Dear @Andrew, But I think your answer is a very good approach because it works very well in chrome and Firefox, and it only have this error: Uint8ClampedArray is undefined in IE. You know that the types in activeX is restricted and make it cumbersome.
Then, as I said, if you have to support older browsers, you will need to write each pixel to the canvas individually. If you can't use Uint8ClampedArray, then you likely can't use putImageData(), so the technique you may have to use is to draw out 1x1 pixel 'rectangles' instead of writing pixels. It sounds slow, but probably isn't. Also, you may find it easier to use a graphics library, such as Fabric.js, EaselJS, Paper.js, etc., since it will handle the cross-platform issues. I can't recommend one over another. Just try and see what you think.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.