Skip to content

Commit

Permalink
apply ootf and its inverse for hlg transfers
Browse files Browse the repository at this point in the history
Test: ./ultrahdr_unit_test
  • Loading branch information
ram-mohan authored and DichenZhang1 committed Oct 30, 2024
1 parent 98ddcdc commit 232bd43
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 15 deletions.
5 changes: 5 additions & 0 deletions lib/include/ultrahdr/gainmapmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,13 @@ constexpr int32_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;

// hlg ootf (normalized)
Color hlgOotf(Color e, LuminanceFn luminance);
Color hlgOotfApprox(Color e, [[maybe_unused]] LuminanceFn luminance);
inline Color identityOotf(Color e, [[maybe_unused]] LuminanceFn) { return e; }

// hlg inverse ootf (normalized)
Color hlgInverseOotf(Color e, LuminanceFn luminance);
Color hlgInverseOotfApprox(Color e);

// pq oetf
float pqOetf(float e);
Color pqOetf(Color e);
Expand Down
16 changes: 15 additions & 1 deletion lib/src/gainmapmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,20 @@ Color hlgOotf(Color e, LuminanceFn luminance) {
return e * std::pow(y, kOotfGamma - 1.0f);
}

Color hlgOotfApprox(Color e, [[maybe_unused]] LuminanceFn luminance) {
return {{{std::pow(e.r, kOotfGamma), std::pow(e.g, kOotfGamma), std::pow(e.b, kOotfGamma)}}};
}

Color hlgInverseOotf(Color e, LuminanceFn luminance) {
float y = luminance(e);
return e * std::pow(y, (1.0f / kOotfGamma) - 1.0f);
}

Color hlgInverseOotfApprox(Color e) {
return {{{std::pow(e.r, 1.0f / kOotfGamma), std::pow(e.g, 1.0f / kOotfGamma),
std::pow(e.b, 1.0f / kOotfGamma)}}};
}

// See ITU-R BT.2100-2, Table 4, Reference PQ OETF.
static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
Expand Down Expand Up @@ -1122,7 +1136,7 @@ SceneToDisplayLuminanceFn getOotfFn(uhdr_color_transfer_t transfer) {
case UHDR_CT_LINEAR:
return identityOotf;
case UHDR_CT_HLG:
return hlgOotf;
return hlgOotfApprox;
case UHDR_CT_PQ:
return identityOotf;
case UHDR_CT_SRGB:
Expand Down
19 changes: 19 additions & 0 deletions lib/src/gpu/applygainmap_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,21 @@ static const std::string pqOETFShader = R"__SHADER__(
}
)__SHADER__";

static const std::string hlgInverseOOTFShader = R"__SHADER__(
float InverseOOTF(const float linear) {
const float kOotfGamma = 1.2f;
return pow(linear, 1.0f / kOotfGamma);
}
vec3 InverseOOTF(const vec3 linear) {
return vec3(InverseOOTF(linear.r), InverseOOTF(linear.g), InverseOOTF(linear.b));
}
)__SHADER__";

static const std::string IdentityInverseOOTFShader = R"__SHADER__(
vec3 InverseOOTF(const vec3 linear) { return linear; }
)__SHADER__";

std::string getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt, uhdr_img_fmt gm_fmt,
uhdr_color_transfer output_ct) {
std::string shader_code = R"__SHADER__(#version 300 es
Expand All @@ -205,10 +220,13 @@ std::string getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt, uhdr_img_fmt gm_
: getGainMapSampleMultiChannel);
shader_code.append(applyGainMapShader);
if (output_ct == UHDR_CT_LINEAR) {
shader_code.append(IdentityInverseOOTFShader);
shader_code.append(linearOETFShader);
} else if (output_ct == UHDR_CT_HLG) {
shader_code.append(hlgInverseOOTFShader);
shader_code.append(hlgOETFShader);
} else if (output_ct == UHDR_CT_PQ) {
shader_code.append(IdentityInverseOOTFShader);
shader_code.append(pqOETFShader);
}

Expand All @@ -219,6 +237,7 @@ std::string getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt, uhdr_img_fmt gm_
vec3 rgb_sdr = sRGBEOTF(rgb_gamma_sdr);
vec3 gain = sampleMap(gainMapTexture);
vec3 rgb_hdr = applyGain(rgb_sdr, gain);
rgb_hdr = InverseOOTF(rgb_hdr);
vec3 rgb_gamma_hdr = OETF(rgb_hdr);
FragColor = vec4(rgb_gamma_hdr, 1.0);
}
Expand Down
52 changes: 38 additions & 14 deletions lib/src/jpegr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,26 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
return status;
}

LuminanceFn hdrLuminanceFn = getLuminanceFn(hdr_intent->cg);
if (hdrLuminanceFn == nullptr) {
status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"No implementation available for calculating luminance for color gamut %d",
hdr_intent->cg);
return status;
}

SceneToDisplayLuminanceFn hdrOotfFn = getOotfFn(hdr_intent->ct);
if (hdrOotfFn == nullptr) {
status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"No implementation available for calculating Ootf for color transfer %d",
hdr_intent->ct);
return status;
}

float hdr_white_nits = getReferenceDisplayPeakLuminanceInNits(hdr_intent->ct);
if (hdr_white_nits == -1.0f) {
status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
Expand Down Expand Up @@ -651,9 +671,9 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
uhdr_raw_image_ext_t* dest = gainmap_img.get();

auto generateGainMapOnePass = [this, sdr_intent, hdr_intent, gainmap_metadata, dest, map_height,
hdrInvOetf, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn,
hdrYuvToRgbFn, sdr_sample_pixel_fn, hdr_sample_pixel_fn,
hdr_white_nits, use_luminance]() -> void {
hdrInvOetf, hdrLuminanceFn, hdrOotfFn, hdrGamutConversionFn,
luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, sdr_sample_pixel_fn,
hdr_sample_pixel_fn, hdr_white_nits, use_luminance]() -> void {
gainmap_metadata->max_content_boost = hdr_white_nits / kSdrWhiteNits;
gainmap_metadata->min_content_boost = 1.0f;
gainmap_metadata->gamma = mGamma;
Expand All @@ -674,9 +694,10 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
size_t rowStep = threads == 1 ? map_height : jobSizeInRows;
JobQueue jobQueue;
std::function<void()> generateMap =
[this, sdr_intent, hdr_intent, gainmap_metadata, dest, hdrInvOetf, hdrGamutConversionFn,
luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, sdr_sample_pixel_fn, hdr_sample_pixel_fn,
hdr_white_nits, log2MinBoost, log2MaxBoost, use_luminance, &jobQueue]() -> void {
[this, sdr_intent, hdr_intent, gainmap_metadata, dest, hdrInvOetf, hdrLuminanceFn,
hdrOotfFn, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn,
sdr_sample_pixel_fn, hdr_sample_pixel_fn, hdr_white_nits, log2MinBoost, log2MaxBoost,
use_luminance, &jobQueue]() -> void {
size_t rowStart, rowEnd;
const bool isHdrIntentRgb = isPixelFormatRgb(hdr_intent->fmt);
const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
Expand Down Expand Up @@ -708,6 +729,7 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
}
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
hdr_rgb = hdrGamutConversionFn(hdr_rgb);

if (mUseMultiChannelGainMap) {
Expand Down Expand Up @@ -760,10 +782,10 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
};

auto generateGainMapTwoPass = [this, sdr_intent, hdr_intent, gainmap_metadata, dest, map_width,
map_height, hdrInvOetf, hdrGamutConversionFn, luminanceFn,
sdrYuvToRgbFn, hdrYuvToRgbFn, sdr_sample_pixel_fn,
hdr_sample_pixel_fn, hdr_white_nits, use_luminance]() -> void {
auto generateGainMapTwoPass =
[this, sdr_intent, hdr_intent, gainmap_metadata, dest, map_width, map_height, hdrInvOetf,
hdrLuminanceFn, hdrOotfFn, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn,
sdr_sample_pixel_fn, hdr_sample_pixel_fn, hdr_white_nits, use_luminance]() -> void {
uhdr_memory_block_t gainmap_mem(map_width * map_height * sizeof(float) *
(mUseMultiChannelGainMap ? 3 : 1));
float* gainmap_data = reinterpret_cast<float*>(gainmap_mem.m_buffer.get());
Expand All @@ -776,10 +798,10 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
size_t rowStep = threads == 1 ? map_height : jobSizeInRows;
JobQueue jobQueue;
std::function<void()> generateMap =
[this, sdr_intent, hdr_intent, gainmap_data, map_width, hdrInvOetf, hdrGamutConversionFn,
luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, sdr_sample_pixel_fn, hdr_sample_pixel_fn,
hdr_white_nits, use_luminance, &gainmap_min, &gainmap_max, &gainmap_minmax,
&jobQueue]() -> void {
[this, sdr_intent, hdr_intent, gainmap_data, map_width, hdrInvOetf, hdrLuminanceFn,
hdrOotfFn, hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn,
sdr_sample_pixel_fn, hdr_sample_pixel_fn, hdr_white_nits, use_luminance, &gainmap_min,
&gainmap_max, &gainmap_minmax, &jobQueue]() -> void {
size_t rowStart, rowEnd;
const bool isHdrIntentRgb = isPixelFormatRgb(hdr_intent->fmt);
const bool isSdrIntentRgb = isPixelFormatRgb(sdr_intent->fmt);
Expand Down Expand Up @@ -814,6 +836,7 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
}
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);
hdr_rgb = hdrGamutConversionFn(hdr_rgb);

if (mUseMultiChannelGainMap) {
Expand Down Expand Up @@ -1516,6 +1539,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima
#else
ColorTransformFn hdrOetf = hlgOetf;
#endif
rgb_hdr = hlgInverseOotfApprox(rgb_hdr);
Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
reinterpret_cast<uint32_t*>(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] =
Expand Down

0 comments on commit 232bd43

Please sign in to comment.