Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/include/ultrahdr/gainmapmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,14 @@ PutPixelFn putPixelFn(uhdr_img_fmt_t format);

////////////////////////////////////////////////////////////////////////////////
// common utils
static const float kHdrOffset = 1e-7f;
static const float kSdrOffset = 1e-7f;

static inline float clipNegatives(float value) { return (value < 0.0f) ? 0.0f : value; }

static inline Color clipNegatives(Color e) {
return {{{clipNegatives(e.r), clipNegatives(e.g), clipNegatives(e.b)}}};
}

// maximum limit of normalized pixel value in float representation
static const float kMaxPixelFloat = 1.0f;
Expand Down
12 changes: 7 additions & 5 deletions lib/src/gainmapmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,12 +782,14 @@ uint8_t encodeGain(float y_sdr, float y_hdr, uhdr_gainmap_metadata_ext_t* metada
}

float computeGain(float sdr, float hdr) {
if (sdr == 0.0f) return 0.0f; // for sdr black return no gain
if (hdr == 0.0f) { // for hdr black, return a gain large enough to attenuate the sdr pel
float offset = (1.0f / 64);
return log2(offset / (offset + sdr));
float gain = log2((hdr + kHdrOffset) / (sdr + kSdrOffset));
if (sdr < 2.f / 255.0f) {
// If sdr is zero and hdr is non zero, it can result in very large gain values. In compression -
// decompression process, if the same sdr pixel increases to 1, the hdr recovered pixel will
// blow out. Dont allow dark pixels to signal large gains.
gain = (std::min)(gain, 2.3f);
}
return log2(hdr / sdr);
return gain;
}

uint8_t affineMapGain(float gainlog2, float mingainlog2, float maxgainlog2, float gamma) {
Expand Down
23 changes: 9 additions & 14 deletions lib/src/jpegr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,9 +697,6 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
const float hdrSampleToNitsFactor =
hdr_intent->ct == UHDR_CT_LINEAR ? kSdrWhiteNits : hdr_white_nits;
ColorTransformFn clampPixel = hdr_intent->ct == UHDR_CT_LINEAR
? static_cast<ColorTransformFn>(clampPixelFloatLinear)
: static_cast<ColorTransformFn>(clampPixelFloat);
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
for (size_t y = rowStart; y < rowEnd; ++y) {
for (size_t x = 0; x < dest->w; ++x) {
Expand Down Expand Up @@ -730,7 +727,7 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
hdr_rgb = hdrGamutConversionFn(hdr_rgb);
hdr_rgb = clampPixel(hdr_rgb);
hdr_rgb = clipNegatives(hdr_rgb);

if (mUseMultiChannelGainMap) {
Color sdr_rgb_nits = sdr_rgb * kSdrWhiteNits;
Expand Down Expand Up @@ -807,9 +804,6 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
const float hdrSampleToNitsFactor =
hdr_intent->ct == UHDR_CT_LINEAR ? kSdrWhiteNits : hdr_white_nits;
ColorTransformFn clampPixel = hdr_intent->ct == UHDR_CT_LINEAR
? static_cast<ColorTransformFn>(clampPixelFloatLinear)
: static_cast<ColorTransformFn>(clampPixelFloat);
float gainmap_min_th[3] = {127.0f, 127.0f, 127.0f};
float gainmap_max_th[3] = {-128.0f, -128.0f, -128.0f};

Expand Down Expand Up @@ -843,7 +837,7 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
hdr_rgb = hdrGamutConversionFn(hdr_rgb);
hdr_rgb = clampPixel(hdr_rgb);
hdr_rgb = clipNegatives(hdr_rgb);

if (mUseMultiChannelGainMap) {
Color sdr_rgb_nits = sdr_rgb * kSdrWhiteNits;
Expand Down Expand Up @@ -907,10 +901,11 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
min_content_boost_log2 = (std::min)(gainmap_min[index], min_content_boost_log2);
max_content_boost_log2 = (std::max)(gainmap_max[index], max_content_boost_log2);
}
// -13.0 emphirically is a small enough gain factor that is capable of representing hdr
// black from any sdr luminance. Allowing further excursion might not offer any benefit and on
// the downside can cause bigger error during affine map and inverse map.
min_content_boost_log2 = (std::max)(-13.0f, min_content_boost_log2);
// gain coefficient range [-14.3, 15.6] is capable of representing hdr pels from sdr pels.
// Allowing further excursion might not offer any benefit and on the downside can cause bigger
// error during affine map and inverse affine map.
min_content_boost_log2 = (std::clamp)(min_content_boost_log2, -14.3f, 15.6f);
max_content_boost_log2 = (std::clamp)(max_content_boost_log2, -14.3f, 15.6f);
if (this->mMaxContentBoost != FLT_MAX) {
float suggestion = log2(this->mMaxContentBoost);
max_content_boost_log2 = (std::min)(max_content_boost_log2, suggestion);
Expand Down Expand Up @@ -969,8 +964,8 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
gainmap_metadata->max_content_boost = exp2(max_content_boost_log2);
gainmap_metadata->min_content_boost = exp2(min_content_boost_log2);
gainmap_metadata->gamma = this->mGamma;
gainmap_metadata->offset_sdr = 0.0f;
gainmap_metadata->offset_hdr = 0.0f;
gainmap_metadata->offset_sdr = kSdrOffset;
gainmap_metadata->offset_hdr = kHdrOffset;
gainmap_metadata->hdr_capacity_min = 1.0f;
if (this->mTargetDispPeakBrightness != -1.0f) {
gainmap_metadata->hdr_capacity_max = this->mTargetDispPeakBrightness / kSdrWhiteNits;
Expand Down
4 changes: 2 additions & 2 deletions tests/gainmapmath_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ TEST_F(GainMapMathTest, EncodeGain) {
float max_boost = log2(4.0f);
float gamma = 1.0f;

EXPECT_EQ(affineMapGain(computeGain(0.0f, 1.0f), min_boost, max_boost, 1.0f), 128);
EXPECT_EQ(affineMapGain(computeGain(0.0f, 1.0f), min_boost, max_boost, 1.0f), 255);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.0f), min_boost, max_boost, 1.0f), 0);
EXPECT_EQ(affineMapGain(computeGain(0.5f, 0.0f), min_boost, max_boost, 1.0f), 0);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0), min_boost, max_boost, 1.0f), 128);
Expand Down Expand Up @@ -1322,7 +1322,7 @@ TEST_F(GainMapMathTest, EncodeGain) {
EXPECT_EQ(affineMapGain(computeGain(1.0f, 1.0f), min_boost, max_boost, 1.0f), 64);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 8.0f), min_boost, max_boost, 1.0f), 255);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 4.0f), min_boost, max_boost, 1.0f), 191);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 128);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 2.0f), min_boost, max_boost, 1.0f), 127);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.7071f), min_boost, max_boost, 1.0f), 32);
EXPECT_EQ(affineMapGain(computeGain(1.0f, 0.5f), min_boost, max_boost, 1.0f), 0);
}
Expand Down