3D Lut in software

Gonzalo Garramuno <[email protected]> Sun, 22 Feb 2015 20:35:46 -0300
Newsgroups gmane.comp.video.openexr.devel
Message-ID <[email protected]>
My application uses a 3D OpenGL Texture in log space for its table 
lookups, with code borrowed from playexr.  However, now I need to do the 
3d lut lookup in software so I can display the color picker correctly 
showing the transformed colors.
I ask here for some help in seeing what I am doing wrong.  Sorry for the 
long code.

The shader code looks like:

       uniform sampler3D lut;
...
       c.rgb = lutT + lutM * log( clamp(c.rgb, lutMin, lutMax) );
       c.rgb = exp( texture3D(lut, c.rgb).rgb );
...

The pixel values passed to CTL is built like:

   void GLLut3d::init_pixel_values( Imf::Array< float >& pixelValues )
   {
     //
     // Compute lutMin, lutMax, and scale and offset
     // values, lutM and lutT, so that
     //
     //    lutM * lutMin + lutT == 0
     //    lutM * lutMax + lutT == 1
     //

     static const int NUM_STOPS = 7;
     static const float MIDDLE_GRAY = 0.18f;

     lutMin = MIDDLE_GRAY / (1 << NUM_STOPS);
     lutMax = MIDDLE_GRAY * (1 << NUM_STOPS);

     float logLutMin = logf (lutMin);
     float logLutMax = logf (lutMax);

     lutM = 1 / (logLutMax - logLutMin);
     lutT = -lutM * logLutMin;

     //
     // Build a 3D array of RGB input pixel values.
     // such that R, G and B are between lutMin and lutMax.
     //
     for (size_t ib = 0; ib < _lutN; ++ib)
     {
         float b = float(ib) / float(_lutN - 1.0);
         half B = expf((b - lutT) / lutM);

     for (size_t ig = 0; ig < _lutN; ++ig)
       {
               float g = float(ig) / float(_lutN - 1.0);
               half G = expf ((g - lutT) / lutM);

         for (size_t ir = 0; ir < _lutN; ++ir)
           {
                   float r = float(ir) / float(_lutN - 1.0);
                   half R = expf ((r - lutT) / lutM);

                   size_t i = (ib * _lutN * _lutN + ig * _lutN + ir) * 4;
                   pixelValues[i + 0] = R;
                   pixelValues[i + 1] = G;
                   pixelValues[i + 2] = B;
                   pixelValues[i + 3] = 1.0f;
           }
       }
     }
   }


The GL texture is built like:

   void GLLut3d::create_gl_texture()
   {
     //
     // Take the logarithm of the output values that were
     // produced by the CTL transforms.
     //

       size_t num = lut_size();
       for ( size_t i = 0; i < num; ++i )
       {
           if ( lut[i] >= std::numeric_limits<float>::min()
                && lut[i] <= std::numeric_limits<float>::max() )
       {
         //
         // lut[i] is finite and positive.
         //
             lut[i] = (float) log (lut[i]);
       }
           else
       {
         //
         // lut[i] is zero, negative or not finite;
         // log (lut[i]) is undefined.
         //
              lut[i] = (float) log( std::numeric_limits<float>::min() );
       }
       }

     //
     // Convert the output values into a 3D texture.
     //
     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );

     glActiveTexture( GL_TEXTURE3 );

     glBindTexture( GL_TEXTURE_3D, texId );

     glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
     glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

     GLenum gl_clamp = GL_CLAMP;
     if ( GLEW_EXT_texture_edge_clamp )
       gl_clamp = GL_CLAMP_TO_EDGE;

     glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, gl_clamp );
     glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, gl_clamp );
     glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, gl_clamp );

     glTexImage3D( GL_TEXTURE_3D,
           0,            // level
                   GL_RGBA32F,           // internal format
           _lutN, _lutN, _lutN,    // width, height, depth
           0,            // border
           GL_RGBA,        // format
           GL_FLOAT,    // type
           (char *) &lut[0] );
}

And here's my software evaluation routine which tries to mimic the 
shader code:

void GLLut3d::evaluate( const Imath::V3f& rgb, Imath::V3f& out ) const
{
     using namespace Imath;

     V3f pMin( lutMin, lutMin, lutMin );
     V3f pMax( lutMax, lutMax, lutMax );
     V3i size( _lutN, _lutN, _lutN );

     out.x = lutT + lutM * log( Imath::clamp( rgb.x, lutMin, lutMax ) );
     out.y = lutT + lutM * log( Imath::clamp( rgb.y, lutMin, lutMax ) );
     out.z = lutT + lutM * log( Imath::clamp( rgb.z, lutMin, lutMax ) );

     out = Ctl::lookup3D( (V3f*)&lut[0], size, pMin, pMax, out );
     out.x = exp( out.x );
     out.y = exp( out.y );
     out.z = exp( out.z );
}


The problem seems to be the Ctl::lookup3D function, which returns all 
negative numbers no matter what.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.