admin管理员组

文章数量:1195732

I'm trying to download 16-bit image data from a server and push it into a WebGL texture without browser plug-ins. texImage2d will work with: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement. I'm looking for some javascript (a library or code sample) which can decode 16-bit TIFF or similar (hdf5, etc.) image data into one of these object types.

I have no problem doing this is 8-bit per channel RGB by using an to load a PNG but this doesn't work with 16-bit per channel data since there aren't any "standard" browser supported image formats which are 16-bit.

I'm trying to download 16-bit image data from a server and push it into a WebGL texture without browser plug-ins. texImage2d will work with: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement. I'm looking for some javascript (a library or code sample) which can decode 16-bit TIFF or similar (hdf5, etc.) image data into one of these object types.

I have no problem doing this is 8-bit per channel RGB by using an to load a PNG but this doesn't work with 16-bit per channel data since there aren't any "standard" browser supported image formats which are 16-bit.

Share Improve this question asked Jun 20, 2011 at 15:34 TrevorTrevor 1,4192 gold badges15 silver badges31 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 10

In case of combining two PNG images, one with the top 8 bits and the second with the low 8 bits, I think it should be:

highp vec4 texCol = texture2D(tex_low, vec2(vTexCoord.s, vTexCoord.t)) * (1.0 / 257.0);
texCol += texture2D(tex_up, vec2(vTexCoord.s, vTexCoord.t)) * (256.0 / 257.0);

In 8 bits per channel RGB colors will range from 0 to 255 = 2^8 - 1.
In 16 bits per channel RGB colors will range from 0 to 65535 = 2^16 - 1 = 255*257.

Explanation

WebGL works using colour values from 0 to 1 and makes it by dividing 8 bit color value by 255. So the divided value belongs to the range <0,1>.
In case of 16 bit per channel we would like to divide it by 65535 to get the proper number from range <0,1>.

What we want is 16 bit color value reduced to range <0,1>.
Let low and up be color value from range 0..255. up is top 8 bits and low is low 8 bits.
To get 16 bit value we can compute: low + up*256. Now we have number in range 0..65535. To get value from range <0,1> we divide it by 65535. Note that WebGL works using color values from range <0,1> , it is Lw=low/255 and Uw=up/255. So, we don't have to multiply it by 255 and divide it by 65535 because 65535 = 255*257. Instead we just divide by 257.

Also I could not find any software to split 16 bit / channel image into two 8 bit/channel image, so here is my code, feel free to use it, it splits 16 bit / channel Tiff into two 8 bit/channel PNGs:

https://github.com/czero69/ImageSplitter

PNGToy is a pretty featured library for extracting PNG chunks of almost all depths and channel modes with javascript (really client-side / without node.js, just Promise.js dependencies). The decode method will return the desired buffer. Here is an example for 16 bits grayscale PNG (16 bits RGB should work as well) :

var dataObj;
var img = new PngImage();
var buffer;

img.onload = function() {

    var pngtoy = this.pngtoy;

    dataObj = pngtoy.decode().then(function(results) {

        buffer = new Uint16Array(results.bitmap);

        for(var i = 0, j; i < buffer.length; i++) {

          j = buffer[i];
          buffer[i] = ((j & 0xff) << 8) | ((j & 0xff00) >>> 8); // needed to swap bytes for correct unsigned integer values  
        }

        console.log(buffer);
    });     
};

img.onerror = function(e) {
    console.log(e.message);
};

img.src = "image.png";

I don't think the main browsers natively support any 16-bit/channel image format at the moment.

One way to achieve the same effect would be to create two PNG images, one with the top 8 bits of each colour channel in the image and one with the bottom 8 bits. Then bind the images as two textures and combine the values in your shader, e.g.

highp float val = texture2d(samplerTop8bits, tex_coord) * (256.0 / 257.0);
val += texture2d(samplerBottom8bits, tex_coord) * (1.0 / 257.0);

(Note: you need highp precision to represent your data correctly in a 16-bit range)

Another method is only possible if floating point textures are supported in your target browser(s). You would, in the browser, combine the two PNG images into a floating point texture then access that normally. This may not be any faster and will probably use twice the amount of texture memory.

Digging this up. This is how I use 16-bit floating point textures

    function createDataTexture16F(gl, width, height) {
      const texture = gl.createTexture()
      gl.bindTexture(gl.TEXTURE_2D, texture)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
      gl.texStorage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, width, height)
      return texture
    }

    function updateDataTexture16F(gl, texture, width, height, data32) {
      const data = float32ToHalfFloat(data32)
      gl.bindTexture(gl.TEXTURE_2D, texture)
      if (data.length !== width * height * 4) throw new Error('Incorrect data length')
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, width, height, 0, gl.RGBA, gl.HALF_FLOAT, data)
    }

float32ToHalfFloat packs the data into Uint16Array as there's no Float16Array in JavaScript. data32 can be any array, either [] or Float32Array

    var toHalfFn = (function() {
      const floatView = new Float32Array(1)
      const int32View = new Int32Array(floatView.buffer)
      return function toHalf(val) {
        floatView[0] = val
        var x = int32View[0]
        var bits = (x >> 16) & 0x8000
        var m = (x >> 12) & 0x07ff
        var e = (x >> 23) & 0xff
        if (e < 103) {
          return bits
        }
        if (e > 142) {
          bits |= 0x7c00
          bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff)
          return bits
        }
        if (e < 113) {
          m |= 0x0800
          bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1)
          return bits
        }
        bits |= ((e - 112) << 10) | (m >> 1)
        bits += m & 1
        return bits
      }
    
    }())
    export const toHalf = toHalfFn
    
    function float32ToHalfFloat(arr) {
      const data = new Uint16Array(arr.length)
      for (var i = 0, il = arr.length; i < il; i ++) {
        data[i] = toHalf(arr[i])
      }
      return data
    }

本文标签: Looking to access 16bit image data in JavascriptWebGLStack Overflow