Loading libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +17 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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, Loading @@ -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 * Loading libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +26 −10 Original line number Diff line number Diff line Loading @@ -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); } } Loading @@ -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: Loading Loading @@ -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 Loading Loading @@ -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. * Loading @@ -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. * Loading @@ -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. * Loading @@ -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 Loading Loading @@ -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); /* Loading libs/jpegrecoverymap/recoverymap.cpp +30 −21 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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, Loading Loading @@ -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); } } } Loading @@ -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(); Loading Loading @@ -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, Loading Loading @@ -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); Loading libs/jpegrecoverymap/recoverymapmath.cpp +28 −44 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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; Loading libs/jpegrecoverymap/tests/recoverymap_test.cpp +132 −18 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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 Loading
libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +17 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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, Loading @@ -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 * Loading
libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +26 −10 Original line number Diff line number Diff line Loading @@ -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); } } Loading @@ -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: Loading Loading @@ -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 Loading Loading @@ -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. * Loading @@ -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. * Loading @@ -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. * Loading @@ -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 Loading Loading @@ -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); /* Loading
libs/jpegrecoverymap/recoverymap.cpp +30 −21 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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, Loading Loading @@ -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); } } } Loading @@ -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(); Loading Loading @@ -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, Loading Loading @@ -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); Loading
libs/jpegrecoverymap/recoverymapmath.cpp +28 −44 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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; Loading
libs/jpegrecoverymap/tests/recoverymap_test.cpp +132 −18 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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