Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adsk Contrib - Add DCDM displays and improve HLG OOTF implementation #2051

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class Renderer_REC2100_Surround : public OpCPU

protected:
float m_gamma;
float m_minLum;
};

class Renderer_RGB_TO_HSV : public OpCPU
Expand Down Expand Up @@ -795,7 +796,11 @@ Renderer_REC2100_Surround::Renderer_REC2100_Surround(ConstFixedFunctionOpDataRcP
: OpCPU()
{
const auto fwd = FixedFunctionOpData::REC2100_SURROUND_FWD == data->getStyle();
const float gamma = fwd ? (float)data->getParams()[0] : (float)(1. / data->getParams()[0]);
float gamma = (float)data->getParams()[0];

m_minLum = fwd ? 1e-4f : powf(1e-4f, gamma);

gamma = fwd ? gamma : 1.f / gamma;

m_gamma = gamma - 1.f; // compute Y^gamma / Y
}
Expand All @@ -811,22 +816,20 @@ void Renderer_REC2100_Surround::apply(const void * inImg, void * outImg, long nu
const float grn = in[1];
const float blu = in[2];

// Calculate luminance assuming input is Rec.2100 RGB.
float Y = 0.2627f * red + 0.6780f * grn + 0.0593f * blu;

// Mirror the function around the origin.
Y = std::abs(Y);

// Since the slope may approach infinity as Y approaches 0, limit the min value
// to avoid gaining up the RGB values (which may not be as close to 0).
//
// This threshold needs to be bigger than 1e-10 (used above) to prevent extreme
// gain in dark colors, yet smaller than 1e-2 to prevent distorting the shape of
// the HLG EOTF curve. Max gain = 1e-4 ** (0.78-1) = 7.6 for HLG min gamma of 0.78.
//
// TODO: Should have forward & reverse versions of this so the threshold can be
// adjusted correctly for the reverse direction.
constexpr float minLum = 1e-4f;
Y = std::max(m_minLum, Y);

// Calculate luminance assuming input is Rec.2100 RGB.
// TODO: Add another parameter to allow using other primaries.
const float Y = std::max( minLum, ( 0.2627f * red +
0.6780f * grn +
0.0593f * blu ) );

// TODO: Currently our fast approx. requires SSE registers.
// Either make this whole routine SSE or make fast scalar pow.
const float Ypow_over_Y = powf(Y, m_gamma);

out[0] = red * Ypow_over_Y;
Expand Down
25 changes: 18 additions & 7 deletions src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,15 +362,24 @@ void Add_Surround_10_Fwd_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShader
ss.newLine() << pxl << ".rgb = " << pxl << ".rgb * Ypow_over_Y;";
}

void Add_Surround_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float gamma)
void Add_Rec2100_Surround_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss,
float gamma, bool isForward)
{
const std::string pxl(shaderCreator->getPixelName());

// TODO: -- add vector inner product to GPUShaderUtils
float minLum = 1e-4f;
if (!isForward)
{
minLum = powf(minLum, gamma);
gamma = 1.f / gamma;
}

ss.newLine() << ss.floatDecl("Y")
<< " = max( 1e-4, 0.2627 * " << pxl << ".rgb.r + "
<< "0.6780 * " << pxl << ".rgb.g + "
<< "0.0593 * " << pxl << ".rgb.b );";
<< " = 0.2627 * " << pxl << ".rgb.r + "
<< "0.6780 * " << pxl << ".rgb.g + "
<< "0.0593 * " << pxl << ".rgb.b;";

ss.newLine() << "Y = max( " << minLum << ", abs(Y) );";

ss.newLine() << ss.floatDecl("Ypow_over_Y") << " = pow( Y, " << (gamma - 1.f) << ");";

Expand Down Expand Up @@ -624,12 +633,14 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator,
}
case FixedFunctionOpData::REC2100_SURROUND_FWD:
{
Add_Surround_Shader(shaderCreator, ss, (float) func->getParams()[0]);
Add_Rec2100_Surround_Shader(shaderCreator, ss,
(float) func->getParams()[0], true);
break;
}
case FixedFunctionOpData::REC2100_SURROUND_INV:
{
Add_Surround_Shader(shaderCreator, ss, (float)(1. / func->getParams()[0]));
Add_Rec2100_Surround_Shader(shaderCreator, ss,
(float) func->getParams()[0], false);
break;
}
case FixedFunctionOpData::RGB_TO_HSV:
Expand Down
84 changes: 84 additions & 0 deletions src/OpenColorIO/transforms/builtins/Displays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,50 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept
CIE_XYZ_D65_to_P3_D60_BFD_Functor);
}

{
auto CIE_XYZ_D65_to_DCDM_D65_Functor = [](OpRcPtrVec & ops)
{
const double scale = 48.0 / 52.37;
const double scale4[4] = { scale, scale, scale, 1. };
CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD);

const GammaOpData::Params rgbParams = { 2.6 };
const GammaOpData::Params alphaParams = { 1.0 };
auto gammaData = std::make_shared<GammaOpData>(GammaOpData::BASIC_REV,
rgbParams, rgbParams, rgbParams, alphaParams);
CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD);
};

registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_DCDM-D65",
"Convert CIE XYZ (D65 white) to Gamma 2.6, XYZ-E with D65 white",
CIE_XYZ_D65_to_DCDM_D65_Functor);
}

{
auto CIE_XYZ_D65_to_DCDM_D60_BFD_Functor = [](OpRcPtrVec & ops)
{
const MatrixOpData::Offsets d65_wht_XYZ(0.95045592705167, 1., 1.08905775075988, 0.);
const MatrixOpData::Offsets d60_wht_XYZ(0.95264607456985, 1., 1.00882518435159, 0.);
MatrixOpData::MatrixArrayPtr matrix
= build_vonkries_adapt(d65_wht_XYZ, d60_wht_XYZ, ADAPTATION_BRADFORD);
CreateMatrixOp(ops, matrix, TRANSFORM_DIR_FORWARD);

const double scale = 48.0 / 52.37;
const double scale4[4] = { scale, scale, scale, 1. };
CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD);

const GammaOpData::Params rgbParams = { 2.6 };
const GammaOpData::Params alphaParams = { 1.0 };
auto gammaData = std::make_shared<GammaOpData>(GammaOpData::BASIC_REV,
rgbParams, rgbParams, rgbParams, alphaParams);
CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD);
};

registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_DCDM-D60-BFD",
remia marked this conversation as resolved.
Show resolved Hide resolved
"Convert CIE XYZ (D65 white) to Gamma 2.6, XYZ-E with D60 white (Bradford adaptation)",
CIE_XYZ_D65_to_DCDM_D60_BFD_Functor);
}

{
auto CIE_XYZ_D65_to_DisplayP3_Functor = [](OpRcPtrVec & ops)
{
Expand Down Expand Up @@ -285,6 +329,46 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept
CIE_XYZ_D65_to_ST2084_P3_D65_Functor);
}

{
auto CIE_XYZ_D65_to_ST2084_DCDM_D65_Functor = [](OpRcPtrVec & ops)
{
// Scale 1.0 in the reference space to 0.48. The PQ function will interpret
remia marked this conversation as resolved.
Show resolved Hide resolved
// this as 48 nits.
const double scale = 0.48;
const double scale4[4] = { scale, scale, scale, 1. };
CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD);

ST_2084::GenerateLinearToPQOps(ops);
};

registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65",
"Convert CIE XYZ (D65 white) to ST-2084 (PQ), XYZ-E with D65 white",
CIE_XYZ_D65_to_ST2084_DCDM_D65_Functor);
}

{
auto CIE_XYZ_D65_to_ST2084_DCDM_D60_BFD_Functor = [](OpRcPtrVec & ops)
{
const MatrixOpData::Offsets d65_wht_XYZ(0.95045592705167, 1., 1.08905775075988, 0.);
const MatrixOpData::Offsets d60_wht_XYZ(0.95264607456985, 1., 1.00882518435159, 0.);
MatrixOpData::MatrixArrayPtr matrix
= build_vonkries_adapt(d65_wht_XYZ, d60_wht_XYZ, ADAPTATION_BRADFORD);
CreateMatrixOp(ops, matrix, TRANSFORM_DIR_FORWARD);

// Scale 1.0 in the reference space to 0.48. The PQ function will interpret
// this as 48 nits.
const double scale = 0.48;
const double scale4[4] = { scale, scale, scale, 1. };
CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD);

ST_2084::GenerateLinearToPQOps(ops);
};

registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D60-BFD",
"Convert CIE XYZ (D65 white) to ST-2084 (PQ), XYZ-E with D60 white (Bradford adaptation)",
CIE_XYZ_D65_to_ST2084_DCDM_D60_BFD_Functor);
}

{
auto CIE_XYZ_D65_to_REC2100_HLG_1000nit_Functor = [](OpRcPtrVec & ops)
{
Expand Down
103 changes: 71 additions & 32 deletions tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,44 +409,83 @@ OCIO_ADD_TEST(FixedFunctionOpCPU, aces_gamut_map_13)

OCIO_ADD_TEST(FixedFunctionOpCPU, rec2100_surround)
{
const unsigned num_samples = 4;
const unsigned num_samples = 5;

float input_32f[num_samples*4] = {
0.11f, 0.02f, 0.04f, 0.5f,
0.71f, 0.51f, 0.81f, 1.0f,
0.43f, 0.82f, 0.71f, 0.0f,
-1.0f, -0.001f, 1.2f, 0.0f
8.4e-5f, 2.4e-5f, 1.4e-4f, 0.1f,
0.11f, 0.02f, 0.04f, 0.5f,
0.71f, 0.51f, 0.81f, 1.0f,
0.43f, 0.82f, 0.71f, 0.0f,
-1.00f, -0.001f, 1.2f, 0.0f
};
{
OCIO::FixedFunctionOpData::Params params = { 0.78 };

float input2_32f[num_samples * 4];
memcpy(&input2_32f[0], &input_32f[0], sizeof(float)*num_samples * 4);
float output_32f[num_samples * 4];
memcpy(&output_32f[0], &input_32f[0], sizeof(float)*num_samples * 4);

const float expected_32f[num_samples*4] = {
0.21779590f, 0.03959925f, 0.07919850f, 0.5f,
0.80029451f, 0.57485944f, 0.91301214f, 1.0f,
0.46350446f, 0.88389223f, 0.76532131f, 0.0f,
-7.58577776f, -0.00758577f, 9.10293388f, 0.0f,
};
const float expected_32f[num_samples*4] = {
0.000637205163f, 0.000182058618f, 0.001062008605f, 0.1f,
0.21779590f, 0.03959925f, 0.07919850f, 0.5f,
0.80029451f, 0.57485944f, 0.91301214f, 1.0f,
0.46350446f, 0.88389223f, 0.76532131f, 0.0f,
-1.43735918f, -0.00143735918f, 1.72483102f, 0.0f
};

// Forward transform -- input to expected.
OCIO::ConstFixedFunctionOpDataRcPtr funcData
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD,
params);

ApplyFixedFunction(&output_32f[0], &expected_32f[0], num_samples,
funcData,
4e-7f,
__LINE__);

OCIO::FixedFunctionOpData::Params params = { 0.78 };
OCIO::ConstFixedFunctionOpDataRcPtr funcData
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD,
params);

ApplyFixedFunction(&input_32f[0], &expected_32f[0], num_samples,
funcData,
1e-7f,
__LINE__);

OCIO::FixedFunctionOpData::Params params_inv = { 1 / 0.78 };
OCIO::ConstFixedFunctionOpDataRcPtr funcData2
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV,
params_inv);

ApplyFixedFunction(&input2_32f[0], &expected_32f[0], num_samples,
funcData2,
1e-7f,
__LINE__);
// Inverse transform -- output back to original.
OCIO::ConstFixedFunctionOpDataRcPtr funcDataInv
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV,
params);

ApplyFixedFunction(&output_32f[0], &input_32f[0], num_samples,
funcDataInv,
3e-7f,
__LINE__);
}
{
OCIO::FixedFunctionOpData::Params params = { 1.2 };

float output_32f[num_samples * 4];
memcpy(&output_32f[0], &input_32f[0], sizeof(float)*num_samples * 4);

const float expected_32f[num_samples*4] = {
1.331310281667e-05f, 3.803743661907e-06f, 2.218850469446e-05f, 0.1f,
0.059115925805f, 0.010748350146f, 0.021496700293f, 0.5f,
0.636785774786f, 0.457409500198f, 0.726473912080f, 1.0f,
0.401647721515f, 0.765932864285f, 0.663185772735f, 0.0f,
-7.190495367684e-01f, -7.190495367684e-04f, 8.628594441221e-01f, 0.0f
};

// Forward transform -- input to expected.
OCIO::ConstFixedFunctionOpDataRcPtr funcData
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD,
params);

ApplyFixedFunction(&output_32f[0], &expected_32f[0], num_samples,
funcData,
2e-7f,
__LINE__);

// Inverse transform -- output back to original.
OCIO::ConstFixedFunctionOpDataRcPtr funcDataInv
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV,
params);

ApplyFixedFunction(&output_32f[0], &input_32f[0], num_samples,
funcDataInv,
2e-7f,
__LINE__);
}
}

OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSV)
Expand Down
8 changes: 8 additions & 0 deletions tests/cpu/transforms/BuiltinTransform_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,10 @@ AllValues UnitTestValues
{ { 0.5f, 0.4f, 0.3f }, { 0.896805202281f, 0.627254277624f, 0.608228132100f } } },
{ "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D60-BFD",
{ { 0.5f, 0.4f, 0.3f }, { 0.892433142142f, 0.627011653770f, 0.608093643982f } } },
{ "DISPLAY - CIE-XYZ-D65_to_DCDM-D65",
{ { 0.5f, 0.4f, 0.3f }, { 0.740738422348f, 0.679816639411f, 0.608609083713f } } },
{ "DISPLAY - CIE-XYZ-D65_to_DCDM-D60-BFD",
{ { 0.5f, 0.4f, 0.3f }, { 0.743277474049f, 0.680864385526f, 0.590880497136f } } },
{ "DISPLAY - CIE-XYZ-D65_to_DisplayP3",
{ { 0.5f, 0.4f, 0.3f }, { 0.882580907776f, 0.581526360743f, 0.5606367050000f } } },

Expand All @@ -485,6 +489,10 @@ AllValues UnitTestValues
{ { 0.5f, 0.4f, 0.3f }, { 0.464008302136f, 0.398157119110f, 0.384828370950f } } },
{ "DISPLAY - CIE-XYZ-D65_to_ST2084-P3-D65",
{ { 0.5f, 0.4f, 0.3f }, { 0.479939091128f, 0.392091860770f, 0.384886051856f } } },
{ "DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65",
{ { 0.5f, 0.4f, 0.3f }, { 0.372935790950f, 0.353493886074f, 0.329196919939f } } },
{ "DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D60-BFD",
{ { 0.5f, 0.4f, 0.3f }, { 0.373721423662f, 0.353838239329f, 0.322856321667f } } },
{ "DISPLAY - CIE-XYZ-D65_to_REC.2100-HLG-1000nit",
{ { 0.5f, 0.4f, 0.3f }, { 0.5649694f, 0.4038837f, 0.3751478f } } }
};
Expand Down
Loading