From 1754818f2ea6aa7c3b85f23f0d0d55464a6b1492 Mon Sep 17 00:00:00 2001 From: "cuneyt.ozdas" Date: Fri, 19 Jul 2024 18:22:23 -0700 Subject: [PATCH] - WIP Fixed-function for PQ<->Linear function. Doesn't have the GPU implementation yet and the rest may need tweaks. Signed-off-by: cuneyt.ozdas --- include/OpenColorIO/OpenColorTypes.h | 3 +- src/OpenColorIO/ParseUtils.cpp | 2 + .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 167 ++++++++++++++++++ .../ops/fixedfunction/FixedFunctionOpData.cpp | 35 ++++ .../ops/fixedfunction/FixedFunctionOpData.h | 4 +- .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 8 + .../transforms/builtins/Displays.cpp | 56 +----- 7 files changed, 225 insertions(+), 50 deletions(-) diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 33654baf83..570c27a068 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -486,7 +486,8 @@ enum FixedFunctionStyle FIXED_FUNCTION_XYZ_TO_LUV, ///< CIE XYZ to 1976 CIELUV colour space (D65 white) FIXED_FUNCTION_ACES_GAMUTMAP_02, ///< ACES 0.2 Gamut clamping algorithm -- NOT IMPLEMENTED YET FIXED_FUNCTION_ACES_GAMUTMAP_07, ///< ACES 0.7 Gamut clamping algorithm -- NOT IMPLEMENTED YET - FIXED_FUNCTION_ACES_GAMUT_COMP_13 ///< ACES 1.3 Parametric Gamut Compression (expects ACEScg values) + FIXED_FUNCTION_ACES_GAMUT_COMP_13, ///< ACES 1.3 Parametric Gamut Compression (expects ACEScg values) + FIXED_FUNCTION_PQ_TO_LINEAR, ///< SMPTE ST 2084:2014 EOTF linearization equation }; /// Enumeration of the :cpp:class:`ExposureContrastTransform` transform algorithms. diff --git a/src/OpenColorIO/ParseUtils.cpp b/src/OpenColorIO/ParseUtils.cpp index eaeefc00e4..3c8b24db72 100644 --- a/src/OpenColorIO/ParseUtils.cpp +++ b/src/OpenColorIO/ParseUtils.cpp @@ -364,6 +364,7 @@ const char * FixedFunctionStyleToString(FixedFunctionStyle style) case FIXED_FUNCTION_XYZ_TO_xyY: return "XYZ_TO_xyY"; case FIXED_FUNCTION_XYZ_TO_uvY: return "XYZ_TO_uvY"; case FIXED_FUNCTION_XYZ_TO_LUV: return "XYZ_TO_LUV"; + case FIXED_FUNCTION_PQ_TO_LINEAR: return "PQ_TO_LINEAR"; case FIXED_FUNCTION_ACES_GAMUTMAP_02: case FIXED_FUNCTION_ACES_GAMUTMAP_07: throw Exception("Unimplemented fixed function types: " @@ -391,6 +392,7 @@ FixedFunctionStyle FixedFunctionStyleFromString(const char * style) else if(str == "xyz_to_xyy") return FIXED_FUNCTION_XYZ_TO_xyY; else if(str == "xyz_to_uvy") return FIXED_FUNCTION_XYZ_TO_uvY; else if(str == "xyz_to_luv") return FIXED_FUNCTION_XYZ_TO_LUV; + else if(str == "pq_to_linear") return FIXED_FUNCTION_PQ_TO_LINEAR; // Default style is meaningless. std::stringstream ss; diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index a974cb7880..067d051477 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -210,6 +210,22 @@ class Renderer_LUV_TO_XYZ : public OpCPU void apply(const void * inImg, void * outImg, long numPixels) const override; }; +class Renderer_PQ_TO_LINEAR : public OpCPU { + public: + Renderer_PQ_TO_LINEAR() = delete; + explicit Renderer_PQ_TO_LINEAR(ConstFixedFunctionOpDataRcPtr &data); + + void apply(const void *inImg, void *outImg, long numPixels) const override; +}; + +class Renderer_LINEAR_TO_PQ : public OpCPU { + public: + Renderer_LINEAR_TO_PQ() = delete; + explicit Renderer_LINEAR_TO_PQ(ConstFixedFunctionOpDataRcPtr &data); + + void apply(const void *inImg, void *outImg, long numPixels) const override; +}; + /////////////////////////////////////////////////////////////////////////////// @@ -1178,7 +1194,150 @@ void Renderer_LUV_TO_XYZ::apply(const void * inImg, void * outImg, long numPixel } +namespace ST_2084 +{ + static constexpr float m1 = float(0.25 * 2610. / 4096.); + static constexpr float m2 = float(128. * 2523. / 4096.); + static constexpr float c2 = float(32. * 2413. / 4096.); + static constexpr float c3 = float(32. * 2392. / 4096.); + static constexpr float c1 = c3 - c2 + 1.; +} // ST_2084 + +Renderer_PQ_TO_LINEAR::Renderer_PQ_TO_LINEAR(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_PQ_TO_LINEAR::apply(const void *inImg, void *outImg, long numPixels) const +{ + // TODO optimize + using namespace ST_2084; + const float *in = (const float *)inImg; + float *out = (float *)outImg; + + for (long idx = 0; idx < numPixels; ++idx, out += 4, in += 4) + { + // (0..1) values will be pass through + // output values are scaled by 100 to convert nits/10,000 into nits/100 + + // R + { + float r = in[0]; + if ((r <= 0.0f) || (r >= 1.0f)) + { + out[0] = r * 100.0f; + } + else + { + const float x = std::pow(r, 1.f / m2); + out[0] = 100.0f * std::pow(std::max(0.f, x - c1) / (c2 - c3 * x), 1.f / m1); + }; + } + // G + { + float g = in[1]; + if ((g <= 0.0f) || (g >= 1.0f)) + { + out[1] = g * 100.0f; + } + else + { + const float x = std::pow(g, 1.f / m2); + out[1] = 100.0f * std::pow(std::max(0.f, x - c1) / (c2 - c3 * x), 1.f / m1); + }; + } + + // B + { + float b = in[2]; + if ((b <= 0.0f) || (b >= 1.0f)) + { + out[2] = b * 100.0f; + } + else + { + const float x = std::pow(b, 1.f / m2); + out[2] = 100.0f * std::pow(std::max(0.f, x - c1) / (c2 - c3 * x), 1.f / m1); + }; + } + + // A + out[3] = in[3]; + } +} + +Renderer_LINEAR_TO_PQ::Renderer_LINEAR_TO_PQ(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ + +} + +void Renderer_LINEAR_TO_PQ::apply(const void *inImg, void *outImg, long numPixels) const +{ + using namespace ST_2084; + const float* in = (const float*)inImg; + float* out = (float*)outImg; + + // Input is in nits/100, convert to [0,1], where 1 is 10000 nits. + + for (long idx = 0; idx < numPixels; ++idx, out += 4, in += 4) + { + // R + { + float r = 0.01f * in[0]; + if (r < 0.0f || r > 1.0f) + { + out[0] = r; + } + else + { + const float L = std::max(0.0f, r); + const float y = std::pow(L, m1); + const float ratpoly = (c1 + c2 * y) / (1.f + c3 * y); + const float N = std::pow(std::max(0.f, ratpoly), m2); + out[0] = N; + } + } + + // G + { + float g = 0.01f * in[1]; + if (g < 0.0f || g > 1.0f) + { + out[1] = g; + } + else + { + const float L = std::max(0.0f, g); + const float y = std::pow(L, m1); + const float ratpoly = (c1 + c2 * y) / (1.f + c3 * y); + const float N = std::pow(std::max(0.f, ratpoly), m2); + out[1] = N; + } + } + + // B + { + float b = 0.01f * in[2]; + if (b < 0.0f || b > 1.0f) + { + out[2] = b; + } + else + { + const float L = std::max(0.0f, b); + const float y = std::pow(L, m1); + const float ratpoly = (c1 + c2 * y) / (1.f + c3 * y); + const float N = std::pow(std::max(0.f, ratpoly), m2); + out[2] = N; + } + } + + //A + out[3] = in[3]; + }; +} /////////////////////////////////////////////////////////////////////////////// @@ -1278,6 +1437,14 @@ ConstOpCPURcPtr GetFixedFunctionCPURenderer(ConstFixedFunctionOpDataRcPtr & func { return std::make_shared(func); } + case FixedFunctionOpData::PQ_TO_LINEAR: + { + return std::make_shared(func); + } + case FixedFunctionOpData::LINEAR_TO_PQ: + { + return std::make_shared(func); + } } throw Exception("Unsupported FixedFunction style"); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp index ce835fd6a8..d4e78f171c 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp @@ -39,6 +39,8 @@ constexpr char XYZ_TO_uvY_STR[] = "XYZ_TO_uvY"; constexpr char uvY_TO_XYZ_STR[] = "uvY_TO_XYZ"; constexpr char XYZ_TO_LUV_STR[] = "XYZ_TO_LUV"; constexpr char LUV_TO_XYZ_STR[] = "LUV_TO_XYZ"; +constexpr char PQ_TO_LINEAR_STR[] = "PQ_TO_LINEAR"; +constexpr char LINEAR_TO_PQ_STR[] = "LINEAR_TO_PQ"; // NOTE: Converts the enumeration value to its string representation (i.e. CLF reader). @@ -94,6 +96,10 @@ const char * FixedFunctionOpData::ConvertStyleToString(Style style, bool detaile return XYZ_TO_LUV_STR; case LUV_TO_XYZ: return LUV_TO_XYZ_STR; + case PQ_TO_LINEAR: + return PQ_TO_LINEAR_STR; + case LINEAR_TO_PQ: + return LINEAR_TO_PQ_STR; } std::stringstream ss("Unknown FixedFunction style: "); @@ -196,6 +202,14 @@ FixedFunctionOpData::Style FixedFunctionOpData::GetStyle(const char * name) { return LUV_TO_XYZ; } + else if (0 == Platform::Strcasecmp(name, PQ_TO_LINEAR_STR)) + { + return PQ_TO_LINEAR; + } + else if (0 == Platform::Strcasecmp(name, LINEAR_TO_PQ_STR)) + { + return LINEAR_TO_PQ; + } } std::string st("Unknown FixedFunction style: "); @@ -270,6 +284,10 @@ FixedFunctionOpData::Style FixedFunctionOpData::ConvertStyle(FixedFunctionStyle "FIXED_FUNCTION_ACES_GAMUTMAP_02, " "FIXED_FUNCTION_ACES_GAMUTMAP_07."); } + case FIXED_FUNCTION_PQ_TO_LINEAR: + { + return FixedFunctionOpData::PQ_TO_LINEAR; + } } std::stringstream ss("Unknown FixedFunction transform style: "); @@ -326,6 +344,10 @@ FixedFunctionStyle FixedFunctionOpData::ConvertStyle(FixedFunctionOpData::Style case FixedFunctionOpData::XYZ_TO_LUV: case FixedFunctionOpData::LUV_TO_XYZ: return FIXED_FUNCTION_XYZ_TO_LUV; + + case FixedFunctionOpData::PQ_TO_LINEAR: + case FixedFunctionOpData::LINEAR_TO_PQ: + return FIXED_FUNCTION_PQ_TO_LINEAR; } std::stringstream ss("Unknown FixedFunction style: "); @@ -584,6 +606,17 @@ void FixedFunctionOpData::invert() noexcept setStyle(XYZ_TO_LUV); break; } + + case PQ_TO_LINEAR: + { + setStyle(LINEAR_TO_PQ); + break; + } + case LINEAR_TO_PQ: + { + setStyle(PQ_TO_LINEAR); + break; + } } // Note that any existing metadata could become stale at this point but @@ -614,6 +647,7 @@ TransformDirection FixedFunctionOpData::getDirection() const noexcept case FixedFunctionOpData::XYZ_TO_xyY: case FixedFunctionOpData::XYZ_TO_uvY: case FixedFunctionOpData::XYZ_TO_LUV: + case FixedFunctionOpData::PQ_TO_LINEAR: return TRANSFORM_DIR_FORWARD; case FixedFunctionOpData::ACES_RED_MOD_03_INV: @@ -627,6 +661,7 @@ TransformDirection FixedFunctionOpData::getDirection() const noexcept case FixedFunctionOpData::xyY_TO_XYZ: case FixedFunctionOpData::uvY_TO_XYZ: case FixedFunctionOpData::LUV_TO_XYZ: + case FixedFunctionOpData::LINEAR_TO_PQ: return TRANSFORM_DIR_INVERSE; } return TRANSFORM_DIR_FORWARD; diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h index a640c66161..df9d949941 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h @@ -47,7 +47,9 @@ class FixedFunctionOpData : public OpData XYZ_TO_uvY, // CIE XYZ to 1976 u'v' chromaticity coordinates uvY_TO_XYZ, // Inverse of above XYZ_TO_LUV, // CIE XYZ to 1976 CIELUV colour space (D65 white) - LUV_TO_XYZ // Inverse of above + LUV_TO_XYZ, // Inverse of above + PQ_TO_LINEAR, // Perceptual Quantizer curve to linear + LINEAR_TO_PQ, // Inverse of above }; static const char * ConvertStyleToString(Style style, bool detailed); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index 20c1b1b1b4..30089f09c2 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -671,6 +671,14 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, { Add_LUV_TO_XYZ(shaderCreator, ss); } + case FixedFunctionOpData::PQ_TO_LINEAR: + { + // TODO: Add function + } + case FixedFunctionOpData::LINEAR_TO_PQ: + { + // TODO: Add function + } } ss.dedent(); diff --git a/src/OpenColorIO/transforms/builtins/Displays.cpp b/src/OpenColorIO/transforms/builtins/Displays.cpp index 8d8af97826..d4f2a9e25e 100644 --- a/src/OpenColorIO/transforms/builtins/Displays.cpp +++ b/src/OpenColorIO/transforms/builtins/Displays.cpp @@ -22,50 +22,6 @@ namespace OCIO_NAMESPACE namespace DISPLAY { -namespace ST_2084 -{ - -static constexpr double m1 = 0.25 * 2610. / 4096.; -static constexpr double m2 = 128. * 2523. / 4096.; -static constexpr double c2 = 32. * 2413. / 4096.; -static constexpr double c3 = 32. * 2392. / 4096.; -static constexpr double c1 = c3 - c2 + 1.; - -void GeneratePQToLinearOps(OpRcPtrVec & ops) -{ - auto GenerateLutValues = [](double input) -> float - { - const double N = std::max(0., input); - const double x = std::pow(N, 1. / m2); - double L = std::pow( std::max(0., x - c1) / (c2 - c3 * x), 1. / m1 ); - // L is in nits/10000, convert to nits/100. - L *= 100.; - - return float(L); - }; - - CreateLut(ops, 4096, GenerateLutValues); -} - -void GenerateLinearToPQOps(OpRcPtrVec & ops) -{ - auto GenerateLutValues = [](double input) -> float - { - // Input is in nits/100, convert to [0,1], where 1 is 10000 nits. - const double L = std::max(0., input * 0.01); - const double y = std::pow(L, m1); - const double ratpoly = (c1 + c2 * y) / (1. + c3 * y); - const double N = std::pow( std::max(0., ratpoly), m2 ); - - return float(N); - }; - - CreateHalfLut(ops, GenerateLutValues); -} - -} // ST_2084 - - void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept { { @@ -236,7 +192,8 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept { auto ST2084_to_Linear_Functor = [](OpRcPtrVec & ops) { - ST_2084::GeneratePQToLinearOps(ops); + //ST_2084::GeneratePQToLinearOps(ops); + CreateFixedFunctionOp(ops, FixedFunctionOpData::PQ_TO_LINEAR, {}); }; registry.addBuiltin("CURVE - ST-2084_to_LINEAR", @@ -247,7 +204,8 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept { auto Linear_to_ST2084_Functor = [](OpRcPtrVec & ops) { - ST_2084::GenerateLinearToPQOps(ops); + //ST_2084::GenerateLinearToPQOps(ops); + CreateFixedFunctionOp(ops, FixedFunctionOpData::LINEAR_TO_PQ, {}); }; registry.addBuiltin("CURVE - LINEAR_to_ST-2084", @@ -262,7 +220,8 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept = build_conversion_matrix_from_XYZ_D65(REC2020::primaries, ADAPTATION_NONE); CreateMatrixOp(ops, matrix, TRANSFORM_DIR_FORWARD); - ST_2084::GenerateLinearToPQOps(ops); + //ST_2084::GenerateLinearToPQOps(ops); + CreateFixedFunctionOp(ops, FixedFunctionOpData::LINEAR_TO_PQ, {}); }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_REC.2100-PQ", @@ -277,7 +236,8 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept = build_conversion_matrix_from_XYZ_D65(P3_D65::primaries, ADAPTATION_NONE); CreateMatrixOp(ops, matrix, TRANSFORM_DIR_FORWARD); - ST_2084::GenerateLinearToPQOps(ops); + // ST_2084::GenerateLinearToPQOps(ops); + CreateFixedFunctionOp(ops, FixedFunctionOpData::LINEAR_TO_PQ, {}); }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_ST2084-P3-D65",