Is this correct ? ACEScct to linear in Resolve 19.1.3

Resolve : 19.1.3

I was wondering if Resolve’s standard color space conversion is correct?
XYZ ACEScct to XYZ Linear
Only the left side of the screen turns black to indicate a huge negative value.

this test uses four color gradient generator in resolve.

I implemented Acescct to Linear with DCTL but it did not turn black.
Which implementation is wrong?

__DEVICE__ float Acescct_to_Lin(float aces_cct){
    float compare_a = 0.155251141552511f;
    float compare_b = (_log2f(65504.0f) + 9.72f) / (17.52f);
    float a = 10.540377416545f;
    float b = 0.07290553441958355f;

    float lin;
    if(aces_cct <= compare_a){
        lin = (aces_cct - b) / (a);
    }
    else if(compare_a < aces_cct < compare_b){
        lin = _powf(2.0f , aces_cct * 17.52f - 9.72f);
    }
    else{
        lin = 65504.0f;
    }

    return lin;
 
}```

https://docs.acescentral.com/specifications/acescct/#acescct

If your plan is to only convert log to linear the input space primaries should probably remain that of ACEScct on both ends, which would be AP1 rather than XYZ (CIE). It would essentially mean a conversion from ACEScct to ACEScg since they share the same primaries. This conversion can also be achieved with the ACESTransform node that would perhaps make a little more sense in ACES related pipelines.


As to the techinical explanation why this happens in XYZ I’m not entirely sure. It happens with any log curve with a toe and not ACEScc. BMD’s CST may have some preconceived notions on what to do with ‘gamma’ when the input primaries are set to non RGB, even if the ouptut primaries remain the same. Hope someone else can chime in.

Thank you for the info.

If your plan is to only convert log to linear the input space primaries should probably remain that of ACEScct on both ends, which would be AP1 rather than XYZ (CIE). It would essentially mean a conversion from ACEScct to ACEScg since they share the same primaries. This conversion can also be achieved with the ACESTransform node that would perhaps make a little more sense in ACES related pipelines.

Yes, I agree, Usually, this would be the case for a conversion such as AP1 cct to some color gamut and gamma.

This time the question is about the simple cct(gamma) to Linear function.
In Resolve’s Color Space Transform, Gamut and Gamma can be selected separately.
In other words, I believe that cct(gamma) to Linear is theoretically implemented as a stand-alone function.

For study and understanding of Color Science, I am trying to create a clone of Color space transform in DCTL.

Currently, the output from this implementation is the screenshot below.

With ̶s̶o̶m̶e̶ claude.ai assistance :

Looking at the formula in your image, for values where ACEScct < 0.155251141552511, it should be:

lin_AP1 = (ACEScct - 0.07290553441958355) / 10.5402377416545

But your implementation is using:

lin = powf(10.0f, (aces_cct - b) / a);

This is applying a power of 10 that shouldn’t be there, which would explain why near-zero values are becoming extremely bright.

Here’s the correct implementation:

__DEVICE__ float Acescct_to_Lin(float aces_cct){
    float compare_a = 0.155251141552511f;
    float compare_b = (_log2f(65504.0f) + 9.72f) / (17.52f);
    float a = 10.540377416545f;
    float b = 0.07290553441958355f;
    float lin;
    
    if(aces_cct < compare_a){
        lin = (aces_cct - b) / a;  // Direct calculation, no power function
    }
    else if(aces_cct < compare_b) {
        lin = powf(2.0f, aces_cct * 17.52f - 9.72f);
    }
    else {
        lin = 65504.0f;
    }
    
    return lin;
}

This should correctly handle the low-end values according to the ACEScct formula in your image.

Hi,

Do I already use this implementation?
I think I don’t use powf(10.0f, (aces_cct - b) / a); anywhere.

In theory you are correct. However this would appear not to be what happens here. The artefact you are seeing is specific to choosing XYZ (CIE) as the gamut. Something more than a 1D ACEScct to linear transform is happening. If you select e.g. ACES(AP1) as the input and output colour space the artefact does not occur.

So it is a problem on the Resolve side.
Thank you all very much for helping.

Incidentally,
Is it correct that Resolve’s “Apply Forward OOTF” is the rendering used when converting from Scene Referred to Display Referred?
It is like RRT in ACES?
I would like to know how it works, but it is Resolve’s own rendering and not publicly available function, right?

The rendering part would be the choice of tone mapping which is DaVinci by default. Their other options are older versions of their tone mapping. Unlike ACES, DaVinciDRT applies per-channel tone mapping directly after linearizing the input color space, so depending on where you came from the same source image can look wildly different. Generally people use DaVinci Wide Gamut / Intermediate as the working color space when using BMD’s color management.

The purpose of an OOTF is conceptually the same as the ACES RRT – a rendering transform to map scene colorimetry to display colorimetry.

However an OOTF normally consists of a simple gamma function (often applied to luminance only – see the BT.2100 HLG OOTF, for example) rather than the more complex sigmoid curve and colour processing of the RRT.

The Resolve manual does not specify what OOTF is used.

It’s also worth pointing out that “RRT” is no longer a term used in ACES 2.0, with the rendering applied using a colour appearance model (CAM).

1 Like