1
\$\begingroup\$

I am currently on a medical application which needs different kinds (up to totally individual) lookup tables (LUT) for image display. And this done with 10bit finish. So it is most of the time required to use 16-bit gray scale pixels.

I chose to use modern OpenGL and thus I'll want the lookup to happen in a GLSL shader program.

I had a try in uploading a 1D-Tedture (R16) using sampler1D to do the lookup as such:

        uniform sampler2D tex;
        uniform sampler1D lutData;
        in vec2 fragTexCoord;
        out vec4 finalColor;
        void main() 
        {
            float primaryLvl = texture(tex, fragTexCoord);
            // index image 'color' in Lut-Texture
            float red = texture(lutData, primaryLvl);
            finalColor = vec4(red, red, red, 1.0);
        }

It works about as expected - however Textures (also 1D aparently) are limited to 8192 in size (on my machine). So the requirement for full range 16-bit images is not met.

For this above test I upload my images as R16 (for me it is gray) - normalized float 0-1.0.

Would it be feasible to upload a buffer (arbitrary data) and do the lookup there? How would I do this (Syntax)?

And will my current pixel format interfere with my idea so I have to upload it as R16_INT instead. Or can (and should) I convert it in the shader from Normalized to Int (how is this done)?

Thanks

\$\endgroup\$
  • \$\begingroup\$ Do you mean GLSL? \$\endgroup\$ – Anko Mar 4 '15 at 9:59
  • \$\begingroup\$ sure that was a typo. \$\endgroup\$ – Robetto Mar 4 '15 at 13:34
1
\$\begingroup\$

The answer is spread across the following links. Due to size constraints / requirements it had to be a Buffer Texture.

Overview listing of possible technologies: https://stackoverflow.com/questions/7954927/glsl-passing-a-list-of-values-to-fragment-shader

OpenGL reference to Buffer Texture: https://www.opengl.org/wiki/Buffer_Texture

Different question but the poster has a nice usage pattern provided: https://stackoverflow.com/questions/20681347/openglwith-glsl-using-gltexbuffer-and-texelfetch

OpenGL reference how to fetch pixels directly: https://www.opengl.org/wiki/Sampler_%28GLSL%29#Direct_texel_fetches - however it will be called samplerBuffer in GLSL.

Details to my solution:

  • Using OpenTK and C# BTW.
  • I started up loading my Image (DICOM 12 bit) with as ushorts with the internal Format GL_R16 (one channel, normalized float). This is because I want to fetch the texture with interpolation and even mipmapping using texture() in GLSL.
  • The Lookup table is declated as ushort[] with 64k items. Those are actual int values but for the conversion precesision between normalized and ushort it does not matter. The right values will map to the same bit patterns.
  • The Buffer Texture is loaded off a Buffer of those ushort[] (length: 64k). At initialization of the Buffer Texture I use the internal format GL_R16 again. This implies some kind of 'conversion' (but bit patterns are equal) from my ushort[] to the buffer texture (noop).
  • Probably GL_R16I would have worked, too. But At the end in GLSL I want to get a normalized float value in order to fill the final color attribute. So I went right into normalized upload.

My shader source:

        #version 150

        uniform sampler2D tex;
        uniform samplerBuffer lutData;
        in vec2 fragTexCoord;
        out vec4 finalColor;

        void main() 
        {
            float primaryLvl = texture(tex, fragTexCoord);
            int index = int(primaryLvl * 65535);
            float gray= texelFetch(lutData, index);
            finalColor = vec4(gray, gray, gray, 1.0);
        }

Fetch the texel from the image texture (normalized) so we have to convert to int index. Index in LUT buffer texture - get normalized float again. Use right away for pixel color.

        var buf = GL.GenBuffers(1);
        GL.BindBuffer( BufferTarget.TextureBuffer, buf);
        GL.BufferData( BufferTarget.TextureBuffer,sizeof(texData),texData,BufferUsageHint.DynamicRead)

        var handle = GL.GenTexture();
        GL.BindTexture(TextureTarget.TextureBuffer, handle);
        GL.TexBuffer(TextureBufferTarget.TextureBuffer, SizedInternalFormat.R16, buf);

In the main loop you'll use the buffer texture just as a texture:

  • Activate texture unit X
  • Bind texture
  • set uniform value to texture unit X
\$\endgroup\$
  • \$\begingroup\$ You may want to consider using the terminology unsigned normalized instead of normalized. It sounds pedantic, but when I read normalized by itself in relation to a texture, that makes me think of texture coordinates. Signed/Unsigned Normalized, on the other hand, immediately tells me it's referring to image format. The texture is still technically stored in a fixed-point format (in response to your 2nd bullet point), but its sampled values are converted to floating-point using signed/unsigned normalization when the texture lookup occurs. \$\endgroup\$ – Andon M. Coleman Mar 6 '15 at 12:17
  • \$\begingroup\$ There's nothing wrong with anything else in your description, this is all just some issues with terminology that you can brush up on here. unorm and snorm formats are called "[un]signed normalized integer" by GL or fixed-point by me (because that's what they really are - R16 is a 0.16 fixed-point format). \$\endgroup\$ – Andon M. Coleman Mar 6 '15 at 12:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.