diff options
| author | 2023-02-13 20:28:12 +0000 | |
|---|---|---|
| committer | 2023-02-13 20:28:12 +0000 | |
| commit | 34d28d124310ee328ec27be068ce3be6001ccb77 (patch) | |
| tree | 526aa15e8e0d6d8687a50689fdd28f9a4bbf6ff8 | |
| parent | 8e8eb792bd30ebb42767869ef1fcace987a1a25c (diff) | |
| parent | d19e576fef9a1bd99a2118bb7edb6e2549ddde49 (diff) | |
Merge "jpegrecoverymap: Add min boost to metadata."
| -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 |