Skip to content

Commit

Permalink
fix incorrect ootf impl used during tonemapping
Browse files Browse the repository at this point in the history
The ootf function of hlg is being used for all color transfers during
tonemap. This is fixed.

Test: ./ultrahdr_unit_test
  • Loading branch information
ram-mohan authored and DichenZhang1 committed Oct 30, 2024
1 parent 6b85f33 commit 98ddcdc
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 13 deletions.
6 changes: 6 additions & 0 deletions lib/include/ultrahdr/gainmapmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct Color {

typedef Color (*ColorTransformFn)(Color);
typedef float (*LuminanceFn)(Color);
typedef Color (*SceneToDisplayLuminanceFn)(Color, LuminanceFn);
typedef Color (*GetPixelFn)(uhdr_raw_image_t*, size_t, size_t);
typedef Color (*SamplePixelFn)(uhdr_raw_image_t*, size_t, size_t, size_t);
typedef void (*PutPixelFn)(uhdr_raw_image_t*, size_t, size_t, Color&);
Expand Down Expand Up @@ -275,6 +276,10 @@ Color hlgInvOetfLUT(Color e_gamma);
constexpr int32_t kHlgInvOETFPrecision = 12;
constexpr int32_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;

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

// pq oetf
float pqOetf(float e);
Color pqOetf(Color e);
Expand Down Expand Up @@ -469,6 +474,7 @@ ColorTransformFn getGamutConversionFn(uhdr_color_gamut_t dst_gamut, uhdr_color_g
ColorTransformFn getYuvToRgbFn(uhdr_color_gamut_t gamut);
LuminanceFn getLuminanceFn(uhdr_color_gamut_t gamut);
ColorTransformFn getInverseOetfFn(uhdr_color_transfer_t transfer);
SceneToDisplayLuminanceFn getOotfFn(uhdr_color_transfer_t transfer);
GetPixelFn getPixelFn(uhdr_img_fmt_t format);
SamplePixelFn getSamplePixelFn(uhdr_img_fmt_t format);
PutPixelFn putPixelFn(uhdr_img_fmt_t format);
Expand Down
3 changes: 1 addition & 2 deletions lib/include/ultrahdr/jpegr.h
Original file line number Diff line number Diff line change
Expand Up @@ -621,8 +621,7 @@ struct GlobalTonemapOutputs {
// `rgb_out` is returned in this same range. `headroom` describes the ratio
// between the HDR and SDR peak luminances and must be > 1. The `y_sdr` output
// is in the range [0.0, 1.0] while `y_hdr` is in the range [0.0, headroom].
GlobalTonemapOutputs globalTonemap(const std::array<float, 3>& rgb_in, float headroom,
float luminance);
GlobalTonemapOutputs globalTonemap(const std::array<float, 3>& rgb_in, float headroom);

} // namespace ultrahdr

Expand Down
24 changes: 24 additions & 0 deletions lib/src/gainmapmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ Color hlgInvOetfLUT(Color e_gamma) {
return {{{hlgInvOetfLUT(e_gamma.r), hlgInvOetfLUT(e_gamma.g), hlgInvOetfLUT(e_gamma.b)}}};
}

// 1.2f + 0.42 * log(kHlgMaxNits / 1000)
static const float kOotfGamma = 1.2f;

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

// 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 @@ -1109,6 +1117,22 @@ ColorTransformFn getInverseOetfFn(uhdr_color_transfer_t transfer) {
return nullptr;
}

SceneToDisplayLuminanceFn getOotfFn(uhdr_color_transfer_t transfer) {
switch (transfer) {
case UHDR_CT_LINEAR:
return identityOotf;
case UHDR_CT_HLG:
return hlgOotf;
case UHDR_CT_PQ:
return identityOotf;
case UHDR_CT_SRGB:
return identityOotf;
case UHDR_CT_UNSPECIFIED:
return nullptr;
}
return nullptr;
}

GetPixelFn getPixelFn(uhdr_img_fmt_t format) {
switch (format) {
case UHDR_IMG_FMT_24bppYCbCr444:
Expand Down
29 changes: 18 additions & 11 deletions lib/src/jpegr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1678,16 +1678,12 @@ static float ReinhardMap(float y_hdr, float headroom) {
return out * y_hdr;
}

GlobalTonemapOutputs globalTonemap(const std::array<float, 3>& rgb_in, float headroom, float y_in) {
constexpr float kOotfGamma = 1.2f;

// Apply OOTF and Scale to Headroom to get HDR values that are referenced to
// SDR white. The range [0.0, 1.0] is linearly stretched to [0.0, headroom]
// after the OOTF.
const float y_ootf_div_y_in = std::pow(y_in, kOotfGamma - 1.0f);
GlobalTonemapOutputs globalTonemap(const std::array<float, 3>& rgb_in, float headroom) {
// Scale to Headroom to get HDR values that are referenced to SDR white. The range [0.0, 1.0] is
// linearly stretched to [0.0, headroom].
std::array<float, 3> rgb_hdr;
std::transform(rgb_in.begin(), rgb_in.end(), rgb_hdr.begin(),
[&](float x) { return x * headroom * y_ootf_div_y_in; });
[&](float x) { return x * headroom; });

// Apply a tone mapping to compress the range [0, headroom] to [0, 1] by
// keeping the shadows the same and crushing the highlights.
Expand Down Expand Up @@ -1788,6 +1784,17 @@ uhdr_error_info_t JpegR::toneMap(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t*
return status;
}

SceneToDisplayLuminanceFn hdrOotfFn = getOotfFn(hdr_intent->ct);
if (hdrOotfFn == nullptr) {
uhdr_error_info_t status;
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;
}

ColorTransformFn hdrInvOetf = getInverseOetfFn(hdr_intent->ct);
if (hdrInvOetf == nullptr) {
uhdr_error_info_t status;
Expand Down Expand Up @@ -1848,7 +1855,7 @@ uhdr_error_info_t JpegR::toneMap(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t*
std::function<void()> toneMapInternal;

toneMapInternal = [hdr_intent, sdr_intent, hdrInvOetf, hdrGamutConversionFn, hdrYuvToRgbFn,
hdr_white_nits, get_pixel_fn, put_pixel_fn, hdrLuminanceFn,
hdr_white_nits, get_pixel_fn, put_pixel_fn, hdrLuminanceFn, hdrOotfFn,
&jobQueue]() -> void {
size_t rowStart, rowEnd;
const int hfactor = hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010 ? 2 : 1;
Expand Down Expand Up @@ -1880,10 +1887,10 @@ uhdr_error_info_t JpegR::toneMap(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t*
hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
}
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrOotfFn(hdr_rgb, hdrLuminanceFn);

GlobalTonemapOutputs tonemap_outputs =
globalTonemap({hdr_rgb.r, hdr_rgb.g, hdr_rgb.b}, hdr_white_nits / kSdrWhiteNits,
hdrLuminanceFn({{{hdr_rgb.r, hdr_rgb.g, hdr_rgb.b}}}));
globalTonemap({hdr_rgb.r, hdr_rgb.g, hdr_rgb.b}, hdr_white_nits / kSdrWhiteNits);
Color sdr_rgb_linear_bt2100 = {
{{tonemap_outputs.rgb_out[0], tonemap_outputs.rgb_out[1],
tonemap_outputs.rgb_out[2]}}};
Expand Down

0 comments on commit 98ddcdc

Please sign in to comment.