diff options
| author | 2022-12-14 09:37:27 -0800 | |
|---|---|---|
| committer | 2023-01-19 02:05:50 +0000 | |
| commit | 555a06b6146d8f695a7655eb7fc17b8bb01a8af9 (patch) | |
| tree | b5e3fc2c34e7c5b185ddac87788dd230e6ee1f7d | |
| parent | 1107ff3272027f7a9a9924f7da9be2ecd5e616f7 (diff) | |
jpegrecoverymap: Added LUT implementations for various OETF and InvOETF functions
Instead of computing these for each pixel, these are computed once
at 10-bit precision (index from 0 to 1023) and are used to speed up
the computations.
Bug: 261877699
Test: push files from tests/data to /sdcard/Documents and then \
atest libjpegdecoder_test libjpegencoder_test libjpegrecoverymap_test
Change-Id: Ifd98a96a78bc38879a77d86ebab137a4efc8fa20
| -rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h | 11 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/recoverymap.cpp | 31 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/recoverymapmath.cpp | 132 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/tests/recoverymapmath_test.cpp | 40 |
4 files changed, 211 insertions, 3 deletions
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h index 2f9799d3ef..f14fafc217 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -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 @@ -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. @@ -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. @@ -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. @@ -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); //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index 222b5f0c9e..a898f1e442 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -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); \ @@ -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: @@ -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); @@ -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. @@ -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; diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp index 7d29a3706e..64fa44b152 100644 --- a/libs/jpegrecoverymap/recoverymapmath.cpp +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -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 @@ -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 @@ -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) { @@ -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, @@ -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; @@ -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 diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp index 58c139dc67..0d0e4fcea3 100644 --- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp @@ -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()); |