SLog3/BT.2020 IDT(.ctl) Creation Issue: How to Find a Correct BT.2020 to AP0 Matrix?

Hello buddy,

I’m trying to bring the ACES Pipeline into live music production.

But my camera signal is SLog3/BT.2020, and ACES doesn’t officially have an IDT for this, so I tried to make my own IDT (.ctl) for that. I used the DCTL (Source: SLog3/Rec.2020, Target: ACES (AP0), CAT: CIECAT02) generated by the ACES IDT Generator created by Tommy Zenth, combined with the official SLog3/SGamut3.Cine IDT, with some modifications — just replaces the matrix transform in SLog3/SGamut3.Cine IDT (.ctl) with the matrix in SLog3/BT.2020 DCTL (.dctl).

I tested this IDT (.ctl) and found that the image that comes out this way is not really right. I’m not sure if this matrix conversion is the reason. Here is the SLog3/BT.2020 IDT (.ctl), Is anyone able to check and advise? Thank a lot!

//------------------------------------------------------------------------------------
//  BT.2020 To ACES(Primaries0) matrix
//------------------------------------------------------------------------------------
const float matrixCoef[3][3] =
{
	{  0.6788911506598102, 0.15886842237789234, 0.16224042703562752 },
	{  0.04557083087232135,  0.8607127720474108, 0.0937163970408747 },
	{  -0.0004857103518124508, 0.025060195735059528,  0.9754255145687619 }
};

//------------------------------------------------------------------------------------
//  S-Log 3 to linear
//------------------------------------------------------------------------------------
float SLog3_to_linear( float SLog )
{
	float out;

	if (SLog >= 171.2102946929 / 1023.0)
	{
		out = pow(10.0, (SLog*1023.0-420.0)/261.5)*(0.18+0.01)-0.01;
	}
	else
	{
		out = (SLog*1023.0-95.0)*0.01125000/(171.2102946929-95.0);
	}

	return out;
}

//------------------------------------------------------------------------------------
//  main
//------------------------------------------------------------------------------------
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 SLog3[3];
	SLog3[0] = rIn;
	SLog3[1] = gIn;
	SLog3[2] = bIn;

	float linear[3];
	linear[0] = SLog3_to_linear( SLog3[0] );
	linear[1] = SLog3_to_linear( SLog3[1] );
	linear[2] = SLog3_to_linear( SLog3[2] );

	float ACES[3] = mult_f3_f33( linear, matrixCoef );

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

Hi @thomjiji,

The matrix seems correct but it needs to be transposed:

np.transpose(
    np.array([[ 0.6788911506,  0.1588684224,  0.1622404270],
              [ 0.0455708309,  0.8607127720,  0.0937163971],
              [-0.0004857104,  0.0250601957,  0.9754255146]]))
Out[37]: 
array([[  6.78891151e-01,   4.55708309e-02,  -4.85710400e-04],
       [  1.58868422e-01,   8.60712772e-01,   2.50601957e-02],
       [  1.62240427e-01,   9.37163971e-02,   9.75425515e-01]])

Cheers,

Thomas

Hi @Thomas_Mansencal ,

Thank you~ I think it works well now. And the final result is this:

//------------------------------------------------------------------------------------
//  BT.2020 To ACES(Primaries0) matrix
//------------------------------------------------------------------------------------
const float matrixCoef[3][3] =
{
	{  6.78891151e-01, 4.55708309e-02, -4.85710400e-04 },
	{  1.58868422e-01,  8.60712772e-01, 2.50601957e-02 },
	{  1.62240427e-01, 9.37163971e-02,  9.75425515e-01 }
};

//------------------------------------------------------------------------------------
//  S-Log 3 to linear
//------------------------------------------------------------------------------------
float SLog3_to_linear( float SLog )
{
	float out;

	if (SLog >= 171.2102946929 / 1023.0)
	{
		out = pow(10.0, (SLog*1023.0-420.0)/261.5)*(0.18+0.01)-0.01;
	}
	else
	{
		out = (SLog*1023.0-95.0)*0.01125000/(171.2102946929-95.0);
	}

	return out;
}

//------------------------------------------------------------------------------------
//  main
//------------------------------------------------------------------------------------
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 SLog3[3];
	SLog3[0] = rIn;
	SLog3[1] = gIn;
	SLog3[2] = bIn;

	float linear[3];
	linear[0] = SLog3_to_linear( SLog3[0] );
	linear[1] = SLog3_to_linear( SLog3[1] );
	linear[2] = SLog3_to_linear( SLog3[2] );

	float ACES[3] = mult_f3_f33( linear, matrixCoef );

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

1 Like

I have a question regarding the color conversion matrix from Rec.2020 to ACES AP0.
From Poynton I got the following:

image

But I dont know where to put the CAT.
So am I right that if I assume that it´s calculated like this and the CAT just has to be put at a certain place?

Rec2020 -> XYZ -> ACES AP0

I know that i could use an online calculator but I would like to know how it is calculated.
Thanks in advance.

It would be Rec.2020XYZ_{D65}XYZ_{D60}AP0

The CAT adapts the XYZ from D65 to D60 (strictly ACES white, but I used D60 as shorthand)

Thanks for the quick reply @nick !
I have only one follow up question on that point:
image

What is the textbook notation to calculate this transformation?

This is where you would perform the chromatic adaptation of the tristimulus values from D65 to ~D60, the entire operation can be done by producing a matrix.

We have an implementation of that in Colour:

Cheers,

Thomas

Thanks @Thomas_Mansencal ! Exactly what i was looking for !

1 Like

I recommend Bruce Lindbloom’s explanation of the maths:

http://www.brucelindbloom.com/Eqn_ChromAdapt.html