CTL Transformation DLOG to ACES

I am looking to create a CTL that allows the use of DLOG within an ACES pipeline. I have looked at a few DCTL’s and adapted them to CTL, my results don’t seem right. Is anyone able to check and advise?


float dlog_to_lin(input varying float in) {

    float out;

    if ( in <= 0.14 ) 
    {
        out = (in - 0.0929) / 6.025;
    }
    else 
    {
        out = (pow(10, 3.89616 * in - 2.27752) - 0.0108) / 0.9892;
    }
    return out;
}


const float xyz_to_acesAP0[3][3] = {{1.0498110175,     0.0,         -0.0000974845},
                                    {-0.4959030231,  1.3733130458, 0.0982400361},
                                    {0.0,           0.0,         0.9912520182} };

void main
(   input varying float rIn,
	input varying float gIn,
	input varying float bIn,
	input varying float aIn,
	output varying float rOut,
	output varying float gOut,
	output varying float bOut,
	output varying float aOut
) 
{
    float rgb[3];
    rgb[0] = dlog_to_lin(rIn);
    rgb[1] = dlog_to_lin(gIn);
    rgb[2] = dlog_to_lin(gIn);
    
    float ACES[3] = mult_f3_f33(rgb, xyz_to_acesAP0);

    rOut = ACES[0];
    gOut = ACES[1];
    bOut = ACES[2];
    aOut = aIn;
}

One thing I would recommend trying is to transpose your matrix. CTL matrices are transposed from standard textbook notation.

Try this:

const float xyz_to_acesAP0[3][3] = {
       { 1.0498110175, -0.4959030231,  0.0000000000},
       { 0.0000000000,  1.3733130458,  0.0000000000},
       {-0.0000974845,  0.0982400361,  0.9912520182}
     };

There might be other things wrong but I’d start there.

Since posting I have an updated version:

// CTL Dlog to ACES AP0 - Howard Colin

float dlog_to_lin(input varying float in) {

    float out;

    if ( in <= 0.14 ) 
    {
        out = (in - 0.0929) / 6.025;
    }
    else 
    {
        out = (pow(10, 3.89616 * in - 2.27752) - 0.0108) / 0.9892;
    }
    return out;
}

const float dgamut_to_xyz[3][3] = {
    {0.6482, 0.2830, -0.0183},
    {0.1940, 0.8132, -0.0832},
    {0.1082, -0.0962, 1.1903}
};

const float xyz_to_acesAP0[3][3] = {
    {1.0498110175, -0.4959030231, 0.0},
    {0.0, 1.3733130458, 0.0},
    {-0.0000974845, 0.0982400361, 0.9912520182}
};

const float xyz_to_acesAP1[3][3] = {
    {1.6410233797, -0.3248032942,  -0.2364246952},
    {-0.6636628587,  1.6153315917, 0.0167563477},
    {0.0117218943, -0.0082844420, 0.9883948585}
};

const float D65_2_D60_CAT[3][3] = {
    {1.01303, 0.00769823, -0.00284131},
    {0.00610531, 0.998165, 0.00468516},
    {-0.014971, -0.00503203, 0.924507}
};

const float DGAMUT_2_AP0[3][3] =  mult_f33_f33(mult_f33_f33(dgamut_to_xyz, D65_2_D60_CAT), xyz_to_acesAP0);

void main
(   input varying float rIn,
	input varying float gIn,
	input varying float bIn,
	input varying float aIn,
	output varying float rOut,
	output varying float gOut,
	output varying float bOut,
	output varying float aOut
) 
{
    float rgb[3];
    rgb[0] = dlog_to_lin(rIn);
    rgb[1] = dlog_to_lin(gIn);
    rgb[2] = dlog_to_lin(gIn);
        
    float ACES[3] = mult_f3_f33(rgb, DGAMUT_2_AP0);

    rOut = ACES[0];
    gOut = ACES[1];
    bOut = ACES[2];
    aOut = aIn;
}

CTL

The results I am getting are looking de-saturated compared to a conversion generated by Lattice. I will try and transpose those matricies and see if that helps

Yes, you will definitely want to go from D-Gamut to ACES. I was just assuming you had XYZ data in D-Log for some reason. I dug up their white paper and didn’t see anywhere where they specify what CAT to use, so I used CAT02.

Here’s the matrix I get for DGamut to AP0, using CAT02, and formatted for CTL.

const float DGamut_to_AP0[3][3] = {
       { 0.7087983663,  0.0687663912, -0.0183570293},
       { 0.1882350568,  1.0074344769, -0.0773812669},
       { 0.1029665769, -0.0762008680,  1.0957382963}
     };

I can pull your code down and run and use a print to see what your matrix ends up as.

Your DGAMUT_2_AP0 matrix is coming out as:

{ {0.691457,	0.0665755,	-0.0172818},
  {0.212844,	1.00958,	-0.0730158},
  {0.0956382,	-0.0761468,	1.09006} };

which is way off. This is most likely due to errors in using the mult_f33_f33() function and not transposing some matrices when needed. Concatenating matrix multiplications in CTL is not the most intuitive thing in the world.

That doesn’t look way off to me. That’s what I get if I use Bradford instead of CAT02.

There is, however, a typo in the original CTL.

The variable gIn is there twice, when the third one should be bIn. Easy copy/paste error to make!

Ah I have been staring at this for ages, of course it’s something like that! Thanks for spotting. It appears to be working correctly now. the output from lattice is matching the output from the CTL.

I’ve obviously seen too many channel swapped or duplicated images in my life! I recognise them immediately.

There is only one thing that I am not really sure I fully understand in this process. I wondered if anyone could explain the chromatic adaption transform and how it is decided which transform to use and how you know which illuminant (i think this is the correct term) to transfer to and from when converting colourspaces.