Loading libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +10 −1 Original line number Diff line number Diff line Loading @@ -189,7 +189,8 @@ Color srgbRgbToYuv(Color e_gamma); */ float srgbInvOetf(float e_gamma); Color srgbInvOetf(Color e_gamma); float srgbInvOetfLUT(float e_gamma); Color srgbInvOetfLUT(Color e_gamma); //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations Loading Loading @@ -229,6 +230,8 @@ Color bt2100YuvToRgb(Color e_gamma); */ float hlgOetf(float e); Color hlgOetf(Color e); float hlgOetfLUT(float e); Color hlgOetfLUT(Color e); /* * Convert from HLG to scene luminance. Loading @@ -237,6 +240,8 @@ Color hlgOetf(Color e); */ float hlgInvOetf(float e_gamma); Color hlgInvOetf(Color e_gamma); float hlgInvOetfLUT(float e_gamma); Color hlgInvOetfLUT(Color e_gamma); /* * Convert from scene luminance to PQ. Loading @@ -245,6 +250,8 @@ Color hlgInvOetf(Color e_gamma); */ float pqOetf(float e); Color pqOetf(Color e); float pqOetfLUT(float e); Color pqOetfLUT(Color e); /* * Convert from PQ to scene luminance in nits. Loading @@ -253,6 +260,8 @@ Color pqOetf(Color e); */ float pqInvOetf(float e_gamma); Color pqInvOetf(Color e_gamma); float pqInvOetfLUT(float e_gamma); Color pqInvOetfLUT(Color e_gamma); //////////////////////////////////////////////////////////////////////////////// Loading libs/jpegrecoverymap/recoverymap.cpp +30 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,12 @@ using namespace photos_editing_formats::image_io; namespace android::recoverymap { #define USE_SRGB_INVOETF_LUT 1 #define USE_HLG_OETF_LUT 1 #define USE_PQ_OETF_LUT 1 #define USE_HLG_INVOETF_LUT 1 #define USE_PQ_INVOETF_LUT 1 #define JPEGR_CHECK(x) \ { \ status_t status = (x); \ Loading Loading @@ -741,11 +747,19 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 hdrInvOetf = identityConversion; break; case JPEGR_TF_HLG: #if USE_HLG_INVOETF_LUT hdrInvOetf = hlgInvOetfLUT; #else hdrInvOetf = hlgInvOetf; #endif hdr_white_nits = kHlgMaxNits; break; case JPEGR_TF_PQ: #if USE_PQ_INVOETF_LUT hdrInvOetf = pqInvOetfLUT; #else hdrInvOetf = pqInvOetf; #endif hdr_white_nits = kPqMaxNits; break; case JPEGR_TF_UNSPECIFIED: Loading Loading @@ -817,7 +831,11 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y); Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma); #if USE_SRGB_INVOETF_LUT Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma); #else Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma); #endif float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits; Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y); Loading Loading @@ -905,10 +923,18 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ hdrOetf = identityConversion; break; case JPEGR_TF_HLG: #if USE_HLG_OETF_LUT hdrOetf = hlgOetfLUT; #else hdrOetf = hlgOetf; #endif break; case JPEGR_TF_PQ: #if USE_PQ_OETF_LUT hdrOetf = pqOetfLUT; #else hdrOetf = pqOetf; #endif break; case JPEGR_TF_UNSPECIFIED: // Should be impossible to hit after input validation. Loading @@ -921,8 +947,11 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ for (size_t x = 0; x < width; ++x) { Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y); Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr); #if USE_SRGB_INVOETF_LUT Color rgb_sdr = srgbInvOetfLUT(rgb_gamma_sdr); #else Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr); #endif float recovery; // TODO: determine map scaling factor based on actual map dims size_t map_scale_factor = kMapDimensionScaleFactor; Loading libs/jpegrecoverymap/recoverymapmath.cpp +131 −1 Original line number Diff line number Diff line Loading @@ -15,11 +15,76 @@ */ #include <cmath> #include <vector> #include <jpegrecoverymap/recoverymapmath.h> namespace android::recoverymap { #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x) constexpr size_t kPqOETFPrecision = 10; constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision; static const std::vector<float> kPqOETF = [] { std::vector<float> result; float increment = 1.0 / kPqOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kPqOETFNumEntries; idx++, value += increment) { result.push_back(pqOetf(value)); } return result; }(); constexpr size_t kPqInvOETFPrecision = 10; constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision; static const std::vector<float> kPqInvOETF = [] { std::vector<float> result; float increment = 1.0 / kPqInvOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kPqInvOETFNumEntries; idx++, value += increment) { result.push_back(pqInvOetf(value)); } return result; }(); constexpr size_t kHlgOETFPrecision = 10; constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision; static const std::vector<float> kHlgOETF = [] { std::vector<float> result; float increment = 1.0 / kHlgOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kHlgOETFNumEntries; idx++, value += increment) { result.push_back(hlgOetf(value)); } return result; }(); constexpr size_t kHlgInvOETFPrecision = 10; constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision; static const std::vector<float> kHlgInvOETF = [] { std::vector<float> result; float increment = 1.0 / kHlgInvOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++, value += increment) { result.push_back(hlgInvOetf(value)); } return result; }(); constexpr size_t kSRGBInvOETFPrecision = 10; constexpr size_t kSRGBInvOETFNumEntries = 1 << kSRGBInvOETFPrecision; static const std::vector<float> kSRGBInvOETF = [] { std::vector<float> result; float increment = 1.0 / kSRGBInvOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kSRGBInvOETFNumEntries; idx++, value += increment) { result.push_back(srgbInvOetf(value)); } return result; }(); // Use Shepard's method for inverse distance weighting. For more information: // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method Loading Loading @@ -117,6 +182,19 @@ Color srgbInvOetf(Color e_gamma) { srgbInvOetf(e_gamma.b) }}}; } // See IEC 61966-2-1, Equations F.5 and F.6. float srgbInvOetfLUT(float e_gamma) { uint32_t value = static_cast<uint32_t>(e_gamma * kSRGBInvOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kSRGBInvOETFNumEntries - 1); return kSRGBInvOETF[value]; } Color srgbInvOetfLUT(Color e_gamma) { return {{{ srgbInvOetfLUT(e_gamma.r), srgbInvOetfLUT(e_gamma.g), srgbInvOetfLUT(e_gamma.b) }}}; } //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations Loading Loading @@ -194,6 +272,18 @@ Color hlgOetf(Color e) { return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}}; } float hlgOetfLUT(float e) { uint32_t value = static_cast<uint32_t>(e * kHlgOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kHlgOETFNumEntries - 1); return kHlgOETF[value]; } Color hlgOetfLUT(Color e) { return {{{ hlgOetfLUT(e.r), hlgOetfLUT(e.g), hlgOetfLUT(e.b) }}}; } // See ITU-R BT.2100-2, Table 5, HLG Reference EOTF. float hlgInvOetf(float e_gamma) { if (e_gamma <= 0.5f) { Loading @@ -209,6 +299,20 @@ Color hlgInvOetf(Color e_gamma) { hlgInvOetf(e_gamma.b) }}}; } float hlgInvOetfLUT(float e_gamma) { uint32_t value = static_cast<uint32_t>(e_gamma * kHlgInvOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kHlgInvOETFNumEntries - 1); return kHlgInvOETF[value]; } Color hlgInvOetfLUT(Color e_gamma) { return {{{ hlgInvOetfLUT(e_gamma.r), hlgInvOetfLUT(e_gamma.g), hlgInvOetfLUT(e_gamma.b) }}}; } // See ITU-R BT.2100-2, Table 4, Reference PQ OETF. static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f; static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f, Loading @@ -224,6 +328,18 @@ Color pqOetf(Color e) { return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}}; } float pqOetfLUT(float e) { uint32_t value = static_cast<uint32_t>(e * kPqOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kPqOETFNumEntries - 1); return kPqOETF[value]; } Color pqOetfLUT(Color e) { return {{{ pqOetfLUT(e.r), pqOetfLUT(e.g), pqOetfLUT(e.b) }}}; } // Derived from the inverse of the Reference PQ OETF. static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f, kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f; Loading @@ -244,6 +360,20 @@ Color pqInvOetf(Color e_gamma) { pqInvOetf(e_gamma.b) }}}; } float pqInvOetfLUT(float e_gamma) { uint32_t value = static_cast<uint32_t>(e_gamma * kPqInvOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kPqInvOETFNumEntries - 1); return kPqInvOETF[value]; } Color pqInvOetfLUT(Color e_gamma) { return {{{ pqInvOetfLUT(e_gamma.r), pqInvOetfLUT(e_gamma.g), pqInvOetfLUT(e_gamma.b) }}}; } //////////////////////////////////////////////////////////////////////////////// // Color conversions Loading libs/jpegrecoverymap/tests/recoverymapmath_test.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -517,6 +517,46 @@ TEST_F(RecoveryMapMathTest, PqInvOetf) { EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e); } TEST_F(RecoveryMapMathTest, PqInvOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, HlgInvOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, pqOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, hlgOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, srgbInvOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) { EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f); EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon()); Loading Loading
libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +10 −1 Original line number Diff line number Diff line Loading @@ -189,7 +189,8 @@ Color srgbRgbToYuv(Color e_gamma); */ float srgbInvOetf(float e_gamma); Color srgbInvOetf(Color e_gamma); float srgbInvOetfLUT(float e_gamma); Color srgbInvOetfLUT(Color e_gamma); //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations Loading Loading @@ -229,6 +230,8 @@ Color bt2100YuvToRgb(Color e_gamma); */ float hlgOetf(float e); Color hlgOetf(Color e); float hlgOetfLUT(float e); Color hlgOetfLUT(Color e); /* * Convert from HLG to scene luminance. Loading @@ -237,6 +240,8 @@ Color hlgOetf(Color e); */ float hlgInvOetf(float e_gamma); Color hlgInvOetf(Color e_gamma); float hlgInvOetfLUT(float e_gamma); Color hlgInvOetfLUT(Color e_gamma); /* * Convert from scene luminance to PQ. Loading @@ -245,6 +250,8 @@ Color hlgInvOetf(Color e_gamma); */ float pqOetf(float e); Color pqOetf(Color e); float pqOetfLUT(float e); Color pqOetfLUT(Color e); /* * Convert from PQ to scene luminance in nits. Loading @@ -253,6 +260,8 @@ Color pqOetf(Color e); */ float pqInvOetf(float e_gamma); Color pqInvOetf(Color e_gamma); float pqInvOetfLUT(float e_gamma); Color pqInvOetfLUT(Color e_gamma); //////////////////////////////////////////////////////////////////////////////// Loading
libs/jpegrecoverymap/recoverymap.cpp +30 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,12 @@ using namespace photos_editing_formats::image_io; namespace android::recoverymap { #define USE_SRGB_INVOETF_LUT 1 #define USE_HLG_OETF_LUT 1 #define USE_PQ_OETF_LUT 1 #define USE_HLG_INVOETF_LUT 1 #define USE_PQ_INVOETF_LUT 1 #define JPEGR_CHECK(x) \ { \ status_t status = (x); \ Loading Loading @@ -741,11 +747,19 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 hdrInvOetf = identityConversion; break; case JPEGR_TF_HLG: #if USE_HLG_INVOETF_LUT hdrInvOetf = hlgInvOetfLUT; #else hdrInvOetf = hlgInvOetf; #endif hdr_white_nits = kHlgMaxNits; break; case JPEGR_TF_PQ: #if USE_PQ_INVOETF_LUT hdrInvOetf = pqInvOetfLUT; #else hdrInvOetf = pqInvOetf; #endif hdr_white_nits = kPqMaxNits; break; case JPEGR_TF_UNSPECIFIED: Loading Loading @@ -817,7 +831,11 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y); Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma); #if USE_SRGB_INVOETF_LUT Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma); #else Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma); #endif float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits; Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y); Loading Loading @@ -905,10 +923,18 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ hdrOetf = identityConversion; break; case JPEGR_TF_HLG: #if USE_HLG_OETF_LUT hdrOetf = hlgOetfLUT; #else hdrOetf = hlgOetf; #endif break; case JPEGR_TF_PQ: #if USE_PQ_OETF_LUT hdrOetf = pqOetfLUT; #else hdrOetf = pqOetf; #endif break; case JPEGR_TF_UNSPECIFIED: // Should be impossible to hit after input validation. Loading @@ -921,8 +947,11 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ for (size_t x = 0; x < width; ++x) { Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y); Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr); #if USE_SRGB_INVOETF_LUT Color rgb_sdr = srgbInvOetfLUT(rgb_gamma_sdr); #else Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr); #endif float recovery; // TODO: determine map scaling factor based on actual map dims size_t map_scale_factor = kMapDimensionScaleFactor; Loading
libs/jpegrecoverymap/recoverymapmath.cpp +131 −1 Original line number Diff line number Diff line Loading @@ -15,11 +15,76 @@ */ #include <cmath> #include <vector> #include <jpegrecoverymap/recoverymapmath.h> namespace android::recoverymap { #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x) constexpr size_t kPqOETFPrecision = 10; constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision; static const std::vector<float> kPqOETF = [] { std::vector<float> result; float increment = 1.0 / kPqOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kPqOETFNumEntries; idx++, value += increment) { result.push_back(pqOetf(value)); } return result; }(); constexpr size_t kPqInvOETFPrecision = 10; constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision; static const std::vector<float> kPqInvOETF = [] { std::vector<float> result; float increment = 1.0 / kPqInvOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kPqInvOETFNumEntries; idx++, value += increment) { result.push_back(pqInvOetf(value)); } return result; }(); constexpr size_t kHlgOETFPrecision = 10; constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision; static const std::vector<float> kHlgOETF = [] { std::vector<float> result; float increment = 1.0 / kHlgOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kHlgOETFNumEntries; idx++, value += increment) { result.push_back(hlgOetf(value)); } return result; }(); constexpr size_t kHlgInvOETFPrecision = 10; constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision; static const std::vector<float> kHlgInvOETF = [] { std::vector<float> result; float increment = 1.0 / kHlgInvOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++, value += increment) { result.push_back(hlgInvOetf(value)); } return result; }(); constexpr size_t kSRGBInvOETFPrecision = 10; constexpr size_t kSRGBInvOETFNumEntries = 1 << kSRGBInvOETFPrecision; static const std::vector<float> kSRGBInvOETF = [] { std::vector<float> result; float increment = 1.0 / kSRGBInvOETFNumEntries; float value = 0.0f; for (int idx = 0; idx < kSRGBInvOETFNumEntries; idx++, value += increment) { result.push_back(srgbInvOetf(value)); } return result; }(); // Use Shepard's method for inverse distance weighting. For more information: // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method Loading Loading @@ -117,6 +182,19 @@ Color srgbInvOetf(Color e_gamma) { srgbInvOetf(e_gamma.b) }}}; } // See IEC 61966-2-1, Equations F.5 and F.6. float srgbInvOetfLUT(float e_gamma) { uint32_t value = static_cast<uint32_t>(e_gamma * kSRGBInvOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kSRGBInvOETFNumEntries - 1); return kSRGBInvOETF[value]; } Color srgbInvOetfLUT(Color e_gamma) { return {{{ srgbInvOetfLUT(e_gamma.r), srgbInvOetfLUT(e_gamma.g), srgbInvOetfLUT(e_gamma.b) }}}; } //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations Loading Loading @@ -194,6 +272,18 @@ Color hlgOetf(Color e) { return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}}; } float hlgOetfLUT(float e) { uint32_t value = static_cast<uint32_t>(e * kHlgOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kHlgOETFNumEntries - 1); return kHlgOETF[value]; } Color hlgOetfLUT(Color e) { return {{{ hlgOetfLUT(e.r), hlgOetfLUT(e.g), hlgOetfLUT(e.b) }}}; } // See ITU-R BT.2100-2, Table 5, HLG Reference EOTF. float hlgInvOetf(float e_gamma) { if (e_gamma <= 0.5f) { Loading @@ -209,6 +299,20 @@ Color hlgInvOetf(Color e_gamma) { hlgInvOetf(e_gamma.b) }}}; } float hlgInvOetfLUT(float e_gamma) { uint32_t value = static_cast<uint32_t>(e_gamma * kHlgInvOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kHlgInvOETFNumEntries - 1); return kHlgInvOETF[value]; } Color hlgInvOetfLUT(Color e_gamma) { return {{{ hlgInvOetfLUT(e_gamma.r), hlgInvOetfLUT(e_gamma.g), hlgInvOetfLUT(e_gamma.b) }}}; } // See ITU-R BT.2100-2, Table 4, Reference PQ OETF. static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f; static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f, Loading @@ -224,6 +328,18 @@ Color pqOetf(Color e) { return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}}; } float pqOetfLUT(float e) { uint32_t value = static_cast<uint32_t>(e * kPqOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kPqOETFNumEntries - 1); return kPqOETF[value]; } Color pqOetfLUT(Color e) { return {{{ pqOetfLUT(e.r), pqOetfLUT(e.g), pqOetfLUT(e.b) }}}; } // Derived from the inverse of the Reference PQ OETF. static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f, kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f; Loading @@ -244,6 +360,20 @@ Color pqInvOetf(Color e_gamma) { pqInvOetf(e_gamma.b) }}}; } float pqInvOetfLUT(float e_gamma) { uint32_t value = static_cast<uint32_t>(e_gamma * kPqInvOETFNumEntries); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kPqInvOETFNumEntries - 1); return kPqInvOETF[value]; } Color pqInvOetfLUT(Color e_gamma) { return {{{ pqInvOetfLUT(e_gamma.r), pqInvOetfLUT(e_gamma.g), pqInvOetfLUT(e_gamma.b) }}}; } //////////////////////////////////////////////////////////////////////////////// // Color conversions Loading
libs/jpegrecoverymap/tests/recoverymapmath_test.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -517,6 +517,46 @@ TEST_F(RecoveryMapMathTest, PqInvOetf) { EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e); } TEST_F(RecoveryMapMathTest, PqInvOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, HlgInvOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, pqOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, hlgOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, srgbInvOetfLUT) { float increment = 1.0 / 1024.0; float value = 0.0f; for (int idx = 0; idx < 1024; idx++, value += increment) { EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) { EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f); EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon()); Loading