diff options
author | 2023-02-10 15:39:08 -0500 | |
---|---|---|
committer | 2023-02-13 13:11:17 -0500 | |
commit | d19e576fef9a1bd99a2118bb7edb6e2549ddde49 (patch) | |
tree | cb2fa818dc0168c7e24abb2c274a6b5b932a5a8f | |
parent | 82a999b81bdbe9650901777523ae4b34fd88b7d1 (diff) |
jpegrecoverymap: Add min boost to metadata.
This change updates recovery map metadata from specifying only max boost
(with implied min boost of its inverse) to specifying both min and max
boost explicitly. Places where this matters are also updated to handle
calculating, encoding, extracting, and applying boost appropriately.
Updated tests and added new tests for relevant new edge cases.
Also add benchmark test. To make this nice, make generateRecoveryMap()
and applyRecoveryMap() functions protected instead of private in the
RecoveryMap class. Current results on Pixel 7 Pro for 12MP are:
* generate map: ~150ms
* apply map: ~95ms
This is a bit slower than before for generate (~110ms) because we now
also sample the SDR image when determining the metadata's boost values.
Also fix a bug in the applyRecoveryLUT test where we weren't actually
iterating over the LUT and instead only tested the first entry, and
another bug with LUTs in general where we were incrementing values by
1/number of entries when we should actuall increment by 1/(number-1).
Bug: 264715926
Test: tests pass
Change-Id: I687eb9c2f788e9220a14311497a129956b2077fd
-rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h | 28 | ||||
-rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h | 36 | ||||
-rw-r--r-- | libs/jpegrecoverymap/recoverymap.cpp | 51 | ||||
-rw-r--r-- | libs/jpegrecoverymap/recoverymapmath.cpp | 72 | ||||
-rw-r--r-- | libs/jpegrecoverymap/tests/recoverymap_test.cpp | 150 | ||||
-rw-r--r-- | libs/jpegrecoverymap/tests/recoverymapmath_test.cpp | 332 |
6 files changed, 463 insertions, 206 deletions
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index aee6602aa4..1fd129b991 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -88,6 +88,8 @@ struct jpegr_metadata { uint32_t version; // Max Content Boost for the map float maxContentBoost; + // Min Content Boost for the map + float minContentBoost; }; typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr; @@ -219,16 +221,9 @@ public: */ status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info); -private: - /* - * This method is called in the encoding pipeline. It will encode the recovery map. - * - * @param uncompressed_recovery_map uncompressed recovery map - * @param dest encoded recover map - * @return NO_ERROR if encoding succeeds, error code if error occurs. - */ - status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, - jr_compressed_ptr dest); + +protected: + // Following functions protected instead of private for testing. /* * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and @@ -239,7 +234,7 @@ private: * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param hdr_tf transfer function of the HDR image * @param dest recovery map; caller responsible for memory of data - * @param metadata max_content_boost is filled in + * @param metadata minContentBoost and maxContentBoost are filled in * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, @@ -265,6 +260,17 @@ private: jr_metadata_ptr metadata, jr_uncompressed_ptr dest); +private: + /* + * This method is called in the encoding pipeline. It will encode the recovery map. + * + * @param uncompressed_recovery_map uncompressed recovery map + * @param dest encoded recover map + * @return NO_ERROR if encoding succeeds, error code if error occurs. + */ + status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, + jr_compressed_ptr dest); + /* * This methoud is called to separate primary image and recovery map image from JPEGR * diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h index 0695bb74ac..6eed08afb4 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -118,11 +118,12 @@ inline Color operator/(const Color& lhs, const float rhs) { constexpr size_t kRecoveryFactorPrecision = 10; constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision; struct RecoveryLUT { - RecoveryLUT(float hdrRatio) { - float increment = 2.0 / kRecoveryFactorNumEntries; - float value = -1.0f; - for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++, value += increment) { - mRecoveryTable[idx] = pow(hdrRatio, value); + RecoveryLUT(jr_metadata_ptr metadata) { + for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); + float logBoost = log2(metadata->minContentBoost) * (1.0f - value) + + log2(metadata->maxContentBoost) * value; + mRecoveryTable[idx] = exp2(logBoost); } } @@ -130,10 +131,10 @@ struct RecoveryLUT { } float getRecoveryFactor(float recovery) { - uint32_t value = static_cast<uint32_t>(((recovery + 1.0f) / 2.0f) * kRecoveryFactorNumEntries); + uint32_t idx = static_cast<uint32_t>(recovery * (kRecoveryFactorNumEntries - 1)); //TODO() : Remove once conversion modules have appropriate clamping in place - value = CLIP3(value, 0, kRecoveryFactorNumEntries - 1); - return mRecoveryTable[value]; + idx = CLIP3(idx, 0, kRecoveryFactorNumEntries - 1); + return mRecoveryTable[idx]; } private: @@ -219,6 +220,9 @@ Color srgbInvOetf(Color e_gamma); float srgbInvOetfLUT(float e_gamma); Color srgbInvOetfLUT(Color e_gamma); +constexpr size_t kSrgbInvOETFPrecision = 10; +constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision; + //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations @@ -260,6 +264,9 @@ Color hlgOetf(Color e); float hlgOetfLUT(float e); Color hlgOetfLUT(Color e); +constexpr size_t kHlgOETFPrecision = 10; +constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision; + /* * Convert from HLG to scene luminance. * @@ -270,6 +277,9 @@ Color hlgInvOetf(Color e_gamma); float hlgInvOetfLUT(float e_gamma); Color hlgInvOetfLUT(Color e_gamma); +constexpr size_t kHlgInvOETFPrecision = 10; +constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision; + /* * Convert from scene luminance to PQ. * @@ -280,6 +290,9 @@ Color pqOetf(Color e); float pqOetfLUT(float e); Color pqOetfLUT(Color e); +constexpr size_t kPqOETFPrecision = 10; +constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision; + /* * Convert from PQ to scene luminance in nits. * @@ -290,6 +303,9 @@ Color pqInvOetf(Color e_gamma); float pqInvOetfLUT(float e_gamma); Color pqInvOetfLUT(Color e_gamma); +constexpr size_t kPqInvOETFPrecision = 10; +constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision; + //////////////////////////////////////////////////////////////////////////////// // Color space conversions @@ -326,13 +342,13 @@ ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gam * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR * luminances in linear space, and the hdr ratio to encode against. */ -uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio); +uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata); /* * Calculates the linear luminance in nits after applying the given recovery * value, with the given hdr ratio, to the given sdr input in the range [0, 1]. */ -Color applyRecovery(Color e, float recovery, float hdr_ratio); +Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata); Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT); /* diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index 218c43017b..349223bb6b 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -573,19 +573,20 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 } std::mutex mutex; - float hdr_y_nits_max = 0.0f; - double hdr_y_nits_avg = 0.0f; + float max_gain = 0.0f; + float min_gain = 1.0f; const int threads = std::clamp(GetCPUCoreCount(), 1, 4); size_t rowStep = threads == 1 ? image_height : kJobSzInRows; JobQueue jobQueue; - std::function<void()> computeMetadata = [uncompressed_p010_image, hdrInvOetf, - hdrGamutConversionFn, luminanceFn, hdr_white_nits, - threads, &mutex, &hdr_y_nits_avg, - &hdr_y_nits_max, &jobQueue]() -> void { + std::function<void()> computeMetadata = [uncompressed_p010_image, uncompressed_yuv_420_image, + hdrInvOetf, hdrGamutConversionFn, luminanceFn, + hdr_white_nits, threads, &mutex, &max_gain, &min_gain, + &jobQueue]() -> void { size_t rowStart, rowEnd; - float hdr_y_nits_max_th = 0.0f; - double hdr_y_nits_avg_th = 0.0f; + float max_gain_th = 0.0f; + float min_gain_th = 1.0f; + while (jobQueue.dequeueJob(rowStart, rowEnd)) { for (size_t y = rowStart; y < rowEnd; ++y) { for (size_t x = 0; x < uncompressed_p010_image->width; ++x) { @@ -595,16 +596,25 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 hdr_rgb = hdrGamutConversionFn(hdr_rgb); float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits; - hdr_y_nits_avg_th += hdr_y_nits; - if (hdr_y_nits > hdr_y_nits_max_th) { - hdr_y_nits_max_th = hdr_y_nits; - } + Color sdr_yuv_gamma = + getYuv420Pixel(uncompressed_yuv_420_image, 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; + + float gain = hdr_y_nits / sdr_y_nits; + max_gain_th = std::max(max_gain_th, gain); + min_gain_th = std::min(min_gain_th, gain); } } } std::unique_lock<std::mutex> lock{mutex}; - hdr_y_nits_avg += hdr_y_nits_avg_th; - hdr_y_nits_max = std::max(hdr_y_nits_max, hdr_y_nits_max_th); + max_gain = std::max(max_gain, max_gain_th); + min_gain = std::min(min_gain, min_gain_th); }; std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image, @@ -634,7 +644,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 size_t pixel_idx = x + y * dest_map_stride; reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] = - encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->maxContentBoost); + encodeRecovery(sdr_y_nits, hdr_y_nits, metadata); } } } @@ -655,9 +665,9 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 computeMetadata(); std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); }); workers.clear(); - hdr_y_nits_avg /= image_width * image_height; - metadata->maxContentBoost = hdr_y_nits_max / kSdrWhiteNits; + metadata->maxContentBoost = max_gain; + metadata->minContentBoost = min_gain; // generate map jobQueue.reset(); @@ -693,7 +703,7 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ dest->width = uncompressed_yuv_420_image->width; dest->height = uncompressed_yuv_420_image->height; ShepardsIDW idwTable(kMapDimensionScaleFactor); - RecoveryLUT recoveryLUT(metadata->maxContentBoost); + RecoveryLUT recoveryLUT(metadata); JobQueue jobQueue; std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map, @@ -729,13 +739,12 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ if (map_scale_factor != floorf(map_scale_factor)) { recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y); } else { - recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y, - idwTable); + recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y, idwTable); } #if USE_APPLY_RECOVERY_LUT Color rgb_hdr = applyRecoveryLUT(rgb_sdr, recovery, recoveryLUT); #else - Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio); + Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata); #endif Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost); uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr); diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp index 4f21ac6372..9c89c8afd4 100644 --- a/libs/jpegrecoverymap/recoverymapmath.cpp +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -20,65 +20,46 @@ namespace android::recoverymap { -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) { + for (int idx = 0; idx < kPqOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kHlgOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1); result.push_back(hlgInvOetf(value)); } return result; }(); -constexpr size_t kSRGBInvOETFPrecision = 10; -constexpr size_t kSRGBInvOETFNumEntries = 1 << kSRGBInvOETFPrecision; -static const std::vector<float> kSRGBInvOETF = [] { +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) { + for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1); result.push_back(srgbInvOetf(value)); } return result; @@ -182,10 +163,10 @@ Color srgbInvOetf(Color e_gamma) { // 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); + 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]; + value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1); + return kSrgbInvOETF[value]; } Color srgbInvOetfLUT(Color e_gamma) { @@ -461,21 +442,24 @@ ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gam //////////////////////////////////////////////////////////////////////////////// // Recovery map calculations - -uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { +uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata) { float gain = 1.0f; if (y_sdr > 0.0f) { gain = y_hdr / y_sdr; } - if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio; - if (gain > hdr_ratio) gain = hdr_ratio; + if (gain < metadata->minContentBoost) gain = metadata->minContentBoost; + if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost; - return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f); + return static_cast<uint8_t>((log2(gain) - log2(metadata->minContentBoost)) + / (log2(metadata->maxContentBoost) - log2(metadata->minContentBoost)) + * 255.0f); } -Color applyRecovery(Color e, float recovery, float hdr_ratio) { - float recoveryFactor = pow(hdr_ratio, recovery); +Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata) { + float logBoost = log2(metadata->minContentBoost) * (1.0f - recovery) + + log2(metadata->maxContentBoost) * recovery; + float recoveryFactor = exp2(logBoost); return e * recoveryFactor; } @@ -550,7 +534,7 @@ static size_t clamp(const size_t& val, const size_t& low, const size_t& high) { } static float mapUintToFloat(uint8_t map_uint) { - return (static_cast<float>(map_uint) - 127.5f) / 127.5f; + return static_cast<float>(map_uint) / 255.0f; } static float pythDistance(float x_diff, float y_diff) { @@ -558,9 +542,9 @@ static float pythDistance(float x_diff, float y_diff) { } // TODO: If map_scale_factor is guaranteed to be an integer, then remove the following. -float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) { - float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor); - float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor); +float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y) { + float x_map = static_cast<float>(x) / map_scale_factor; + float y_map = static_cast<float>(y) / map_scale_factor; size_t x_lower = static_cast<size_t>(floor(x_map)); size_t x_upper = x_lower + 1; diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp index 3e9a76d47a..1b73d94057 100644 --- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp @@ -20,6 +20,7 @@ #include <fcntl.h> #include <fstream> #include <gtest/gtest.h> +#include <sys/time.h> #include <utils/Log.h> #define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010" @@ -35,27 +36,24 @@ namespace android::recoverymap { -class RecoveryMapTest : public testing::Test { -public: - RecoveryMapTest(); - ~RecoveryMapTest(); -protected: - virtual void SetUp(); - virtual void TearDown(); - - struct jpegr_uncompressed_struct mRawP010Image; - struct jpegr_uncompressed_struct mRawYuv420Image; - struct jpegr_compressed_struct mJpegImage; +struct Timer { + struct timeval StartingTime; + struct timeval EndingTime; + struct timeval ElapsedMicroseconds; }; -RecoveryMapTest::RecoveryMapTest() {} -RecoveryMapTest::~RecoveryMapTest() {} +void timerStart(Timer *t) { + gettimeofday(&t->StartingTime, nullptr); +} -void RecoveryMapTest::SetUp() {} -void RecoveryMapTest::TearDown() { - free(mRawP010Image.data); - free(mRawYuv420Image.data); - free(mJpegImage.data); +void timerStop(Timer *t) { + gettimeofday(&t->EndingTime, nullptr); +} + +int64_t elapsedTime(Timer *t) { + t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec; + t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec; + return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec; } static size_t getFileSize(int fd) { @@ -89,6 +87,80 @@ static bool loadFile(const char filename[], void*& result, int* fileLength) { return true; } +class RecoveryMapTest : public testing::Test { +public: + RecoveryMapTest(); + ~RecoveryMapTest(); + +protected: + virtual void SetUp(); + virtual void TearDown(); + + struct jpegr_uncompressed_struct mRawP010Image; + struct jpegr_uncompressed_struct mRawYuv420Image; + struct jpegr_compressed_struct mJpegImage; +}; + +RecoveryMapTest::RecoveryMapTest() {} +RecoveryMapTest::~RecoveryMapTest() {} + +void RecoveryMapTest::SetUp() {} +void RecoveryMapTest::TearDown() { + free(mRawP010Image.data); + free(mRawYuv420Image.data); + free(mJpegImage.data); +} + +class RecoveryMapBenchmark : public RecoveryMap { +public: + void BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image, + jr_metadata_ptr metadata, jr_uncompressed_ptr map); + void BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map, + jr_metadata_ptr metadata, jr_uncompressed_ptr dest); +private: + const int kProfileCount = 10; +}; + +void RecoveryMapBenchmark::BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image, + jr_uncompressed_ptr p010Image, + jr_metadata_ptr metadata, + jr_uncompressed_ptr map) { + ASSERT_EQ(yuv420Image->width, p010Image->width); + ASSERT_EQ(yuv420Image->height, p010Image->height); + + Timer genRecMapTime; + + timerStart(&genRecMapTime); + for (auto i = 0; i < kProfileCount; i++) { + ASSERT_EQ(OK, generateRecoveryMap( + yuv420Image, p010Image, jpegr_transfer_function::JPEGR_TF_HLG, metadata, map)); + if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data); + } + timerStop(&genRecMapTime); + + ALOGE("Generate Recovery Map:- Res = %i x %i, time = %f ms", + yuv420Image->width, yuv420Image->height, + elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f)); + +} + +void RecoveryMapBenchmark::BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, + jr_uncompressed_ptr map, + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest) { + Timer applyRecMapTime; + + timerStart(&applyRecMapTime); + for (auto i = 0; i < kProfileCount; i++) { + ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest)); + } + timerStop(&applyRecMapTime); + + ALOGE("Apply Recovery Map:- Res = %i x %i, time = %f ms", + yuv420Image->width, yuv420Image->height, + elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f)); +} + TEST_F(RecoveryMapTest, build) { // Force all of the recovery map lib to be linked by calling all public functions. RecoveryMap recovery_map; @@ -382,4 +454,46 @@ TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) { free(decodedJpegR.data); } +TEST_F(RecoveryMapTest, ProfileRecoveryMapFuncs) { + const size_t kWidth = TEST_IMAGE_WIDTH; + const size_t kHeight = TEST_IMAGE_HEIGHT; + + // Load input files. + if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { + FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; + } + mRawP010Image.width = kWidth; + mRawP010Image.height = kHeight; + mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100; + + if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) { + FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; + } + mRawYuv420Image.width = kWidth; + mRawYuv420Image.height = kHeight; + mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709; + + RecoveryMapBenchmark benchmark; + + jpegr_metadata metadata = { .version = 1, + .maxContentBoost = 8.0f, + .minContentBoost = 1.0f / 8.0f }; + + jpegr_uncompressed_struct map = { .data = NULL, + .width = 0, + .height = 0, + .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED }; + + benchmark.BenchmarkGenerateRecoveryMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map); + + const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4; + auto bufferDst = std::make_unique<uint8_t[]>(dstSize); + jpegr_uncompressed_struct dest = { .data = bufferDst.get(), + .width = 0, + .height = 0, + .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED }; + + benchmark.BenchmarkApplyRecoveryMap(&mRawYuv420Image, &map, &metadata, &dest); +} + } // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp index 2eec95f01b..80a9596e3c 100644 --- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp @@ -42,7 +42,7 @@ public: } float Map(uint8_t e) { - return (static_cast<float>(e) - 127.5f) / 127.5f; + return static_cast<float>(e) / 255.0f; } Color ColorMin(Color e1, Color e2) { @@ -88,10 +88,10 @@ public: return luminance_scaled * scale_factor; } - Color Recover(Color yuv_gamma, float recovery, float max_content_boost) { + Color Recover(Color yuv_gamma, float recovery, jr_metadata_ptr metadata) { Color rgb_gamma = srgbYuvToRgb(yuv_gamma); Color rgb = srgbInvOetf(rgb_gamma); - return applyRecovery(rgb, recovery, max_content_boost); + return applyRecovery(rgb, recovery, metadata); } jpegr_uncompressed_struct Yuv420Image() { @@ -518,59 +518,95 @@ TEST_F(RecoveryMapMathTest, PqInvOetf) { } TEST_F(RecoveryMapMathTest, PqInvOetfLUT) { - float increment = 1.0 / 1024.0; - float value = 0.0f; - for (int idx = 0; idx < 1024; idx++, value += increment) { + for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kPqOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kHlgOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1); 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) { + for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1); EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value)); } } TEST_F(RecoveryMapMathTest, applyRecoveryLUT) { - float increment = 2.0 / kRecoveryFactorNumEntries; - for (float hdrRatio = 1.0f; hdrRatio <= 10.0f; hdrRatio += 1.0f) { - RecoveryLUT recoveryLUT(hdrRatio); - for (float value = -1.0f; value <= -1.0f; value += increment) { - EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, hdrRatio), + for (int boost = 1; boost <= 10; boost++) { + jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost), + .minContentBoost = 1.0f / static_cast<float>(boost) }; + RecoveryLUT recoveryLUT(&metadata); + for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata), applyRecoveryLUT(RgbBlack(), value, recoveryLUT)); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, hdrRatio), + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata), applyRecoveryLUT(RgbWhite(), value, recoveryLUT)); - EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, hdrRatio), + EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata), applyRecoveryLUT(RgbRed(), value, recoveryLUT)); - EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, hdrRatio), + EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata), applyRecoveryLUT(RgbGreen(), value, recoveryLUT)); - EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, hdrRatio), + EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata), + applyRecoveryLUT(RgbBlue(), value, recoveryLUT)); + } + } + + for (int boost = 1; boost <= 10; boost++) { + jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost), + .minContentBoost = 1.0f }; + RecoveryLUT recoveryLUT(&metadata); + for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata), + applyRecoveryLUT(RgbBlack(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata), + applyRecoveryLUT(RgbWhite(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata), + applyRecoveryLUT(RgbRed(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata), + applyRecoveryLUT(RgbGreen(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata), + applyRecoveryLUT(RgbBlue(), value, recoveryLUT)); + } + } + + for (int boost = 1; boost <= 10; boost++) { + jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost), + .minContentBoost = 1.0f / pow(static_cast<float>(boost), + 1.0f / 3.0f) }; + RecoveryLUT recoveryLUT(&metadata); + for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { + float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata), + applyRecoveryLUT(RgbBlack(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata), + applyRecoveryLUT(RgbWhite(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata), + applyRecoveryLUT(RgbRed(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata), + applyRecoveryLUT(RgbGreen(), value, recoveryLUT)); + EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata), applyRecoveryLUT(RgbBlue(), value, recoveryLUT)); } } @@ -623,60 +659,121 @@ TEST_F(RecoveryMapMathTest, ColorConversionLookup) { } TEST_F(RecoveryMapMathTest, EncodeRecovery) { - EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127); - EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127); - EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0); - EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0); - - EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127); - EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255); - EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255); - EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0); - EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0); - EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191); - EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63); - - EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255); - EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0); - EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191); - EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63); - - EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255); - EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0); - EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191); - EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63); + jpegr_metadata metadata = { .maxContentBoost = 4.0f, + .minContentBoost = 1.0f / 4.0f }; + + EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 127); + EXPECT_EQ(encodeRecovery(0.0f, 1.0f, &metadata), 127); + EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0); + EXPECT_EQ(encodeRecovery(0.5f, 0.0f, &metadata), 0); + + EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 127); + EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 255); + EXPECT_EQ(encodeRecovery(1.0f, 5.0f, &metadata), 255); + EXPECT_EQ(encodeRecovery(4.0f, 1.0f, &metadata), 0); + EXPECT_EQ(encodeRecovery(4.0f, 0.5f, &metadata), 0); + EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 191); + EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 63); + + metadata.maxContentBoost = 2.0f; + metadata.minContentBoost = 1.0f / 2.0f; + + EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 255); + EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 0); + EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, &metadata), 191); + EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, &metadata), 63); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 1.0f / 8.0f; + + EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255); + EXPECT_EQ(encodeRecovery(8.0f, 1.0f, &metadata), 0); + EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, &metadata), 191); + EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, &metadata), 63); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 1.0f; + + EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 0); + EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0); + + EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 0); + EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255); + EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 170); + EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 85); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 0.5f; + + EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 63); + EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0); + + EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 63); + EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255); + EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 191); + EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 127); + EXPECT_EQ(encodeRecovery(1.0f, 0.7071f, &metadata), 31); + EXPECT_EQ(encodeRecovery(1.0f, 0.5f, &metadata), 0); } TEST_F(RecoveryMapMathTest, ApplyRecovery) { - EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack()); - EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack()); - EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack()); - - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite()); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f); - - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite()); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f); - - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite()); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f); - EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f); + jpegr_metadata metadata = { .maxContentBoost = 4.0f, + .minContentBoost = 1.0f / 4.0f }; + + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack()); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack()); + EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, &metadata), RgbBlack()); + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f); + + metadata.maxContentBoost = 2.0f; + metadata.minContentBoost = 1.0f / 2.0f; + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 1.0f / 8.0f; + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 1.0f; + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 0.5f; + + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite()); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f); + EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); Color e = {{{ 0.0f, 0.5f, 1.0f }}}; - - EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f); - EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f); - EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e); - EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f); - EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f); + metadata.maxContentBoost = 4.0f; + metadata.minContentBoost = 1.0f / 4.0f; + + EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, &metadata), e / 4.0f); + EXPECT_RGB_NEAR(applyRecovery(e, 0.25f, &metadata), e / 2.0f); + EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, &metadata), e); + EXPECT_RGB_NEAR(applyRecovery(e, 0.75f, &metadata), e * 2.0f); + EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, &metadata), e * 4.0f); } TEST_F(RecoveryMapMathTest, GetYuv420Pixel) { @@ -785,8 +882,10 @@ TEST_F(RecoveryMapMathTest, SampleMap) { // Instead of reimplementing the sampling algorithm, confirm that the // sample output is within the range of the min and max of the nearest // points. - EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y, idwTable), + EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y), testing::AllOf(testing::Ge(min), testing::Le(max))); + EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable), + sampleMap(&image, kMapScaleFactor, x, y)); } } } @@ -882,60 +981,89 @@ TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) { } TEST_F(RecoveryMapMathTest, ApplyMap) { - EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f), + jpegr_metadata metadata = { .maxContentBoost = 8.0f, + .minContentBoost = 1.0f / 8.0f }; + + EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); - EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata), RgbBlack()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata), RgbRed() * 8.0f); - EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata), RgbGreen() * 8.0f); - EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata), RgbBlue() * 8.0f); - EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata), RgbWhite() * sqrt(8.0f)); - EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata), RgbBlack()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata), RgbRed() * sqrt(8.0f)); - EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata), RgbGreen() * sqrt(8.0f)); - EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata), RgbBlue() * sqrt(8.0f)); - EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite()); - EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata), RgbBlack()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata), RgbRed()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata), RgbGreen()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata), RgbBlue()); - EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite() / sqrt(8.0f)); - EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata), RgbBlack()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata), RgbRed() / sqrt(8.0f)); - EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata), RgbGreen() / sqrt(8.0f)); - EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata), RgbBlue() / sqrt(8.0f)); - EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 8.0f); - EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f), + EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata), RgbBlack()); - EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata), RgbRed() / 8.0f); - EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata), RgbGreen() / 8.0f); - EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f), + EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata), RgbBlue() / 8.0f); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 1.0f; + + EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), + RgbWhite() * 8.0f); + EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata), + RgbWhite() * 4.0f); + EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata), + RgbWhite() * 2.0f); + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), + RgbWhite()); + + metadata.maxContentBoost = 8.0f; + metadata.minContentBoost = 0.5f;; + + EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), + RgbWhite() * 8.0f); + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata), + RgbWhite() * 4.0f); + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), + RgbWhite() * 2.0f); + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), + RgbWhite()); + EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), + RgbWhite() / 2.0f); } } // namespace android::recoverymap |