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.

Community
  • 1
  • 1
Hosein Aqajani
  • 1,553
  • 4
  • 26
  • 46
  • Do you know the color encoding? I.e gray scale or color? – Matthieu Jan 23 '16 at 14:35
  • Also bear in mind that java and javascript are *totally* different languages. – Matthieu Jan 23 '16 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 – Hosein Aqajani Jan 23 '16 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). – Goblinlord Jan 23 '16 at 15:16
  • I recommend you take a look at this article. http://www.html5rocks.com/en/tutorials/webgl/typed_arrays/ without more info (like the source file format) its pretty much impossible to give you a solution that works. – Goblinlord Jan 23 '16 at 15:27
  • 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](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas) for details. If you need to transcode for storage and later use, try using [jimp](https://github.com/oliver-moran/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. – Andrew Jan 24 '16 at 04:54
  • Dear @Goblinlord please see edited problem, I have provided the part of my java code. However I should read about `xIntTypedArrayBuffer` that you mentioned. maybe help... – Hosein Aqajani Jan 24 '16 at 06:21
  • Dear @Andrew I have investigated your approach. Indeed, I read image as a raw hex string (header-less) from smart card, and I have not the array of pixels. Hence, I could not use your solution. please help me... – Hosein Aqajani Jan 24 '16 at 12:13

1 Answers1

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);
Andrew
  • 14,204
  • 15
  • 60
  • 104
  • 1
    See https://jsfiddle.net/36gLwqgv/1/ . I'm using `reduce` here rather than a `for()` loop. See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) for how to use it, but feel free to use `for()` instead. – Andrew Jan 25 '16 at 08:02
  • 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. – Hosein Aqajani Jan 25 '16 at 13:30
  • [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. – Andrew Jan 25 '16 at 15:46
  • 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. – Hosein Aqajani Jan 26 '16 at 04:52
  • 1
    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. – Andrew Jan 26 '16 at 06:20
  • Dear @Andrew my problem solved in http://stackoverflow.com/questions/35012500/how-to-convert-a-javascript-array-to-uint8clampedarray-which-works-in-ie eager to your upvote ;) – Hosein Aqajani Jan 27 '16 at 05:57