Skip to content

feat: add HDR to SDR tone mapping support#274

Merged
salarkhan merged 10 commits intomasterfrom
salar/tone-mapping
Nov 24, 2025
Merged

feat: add HDR to SDR tone mapping support#274
salarkhan merged 10 commits intomasterfrom
salar/tone-mapping

Conversation

@salarkhan
Copy link
Contributor

summary

adds HDR to SDR tone mapping functionality for PNG and WebP encoding to handle high dynamic range images with PQ (Perceptual Quantizer) color profiles. this prevents overly bright/blown-out images when converting HDR content to SDR formats.

motivation

when encoding some images with HDR PQ profiles (such as Rec. 2100 or Rec. 2020 with PQ transfer function) without tone mapping, the resulting images appear extremely bright to the point of it being an accessibility issue.

changes

the implementation uses a luminance-based Reinhard tone mapping approach:

  1. calculates luminance using Rec. 709 coefficients: luma = 0.2126R + 0.7152G + 0.0722*B
  2. applies Reinhard operator with gentle scaling: luma_mapped = (luma * 1.2) / (1.0 + luma * 1.2)
  3. scales RGB channels proportionally to preserve color: RGB_out = RGB_in * (luma_mapped / luma_in)

this approach prevents oversaturation by maintaining color relationships while reducing brightness.

@salarkhan salarkhan changed the title support encoder write with tone mapping add HDR to SDR tone mapping support Nov 13, 2025
@salarkhan salarkhan requested a review from a team November 20, 2025 00:57
webp.hpp Outdated
int dispose,
int x_offset,
int y_offset);
size_t webp_encoder_write_with_tone_mapping(webp_encoder e,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

architecturally it seems like tone mapping should be done independently of the format specific encoders?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh damn, i didnt think of that. that would def be cleaner, let me take a crack at it

// Apply Reinhard tone mapping
std::unique_ptr<cv::Mat> dst_bgr = std::make_unique<cv::Mat>(src_for_transform->rows, src_for_transform->cols, CV_8UC3);

// Apply luminance-based tone mapping to preserve color relationships
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit odd to hardcode a tone mapping algorithm here ... i thought opencv had some functions for applying tone mapping or there could be other alternative libraries out there worth looking at?

Copy link
Contributor Author

@salarkhan salarkhan Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smh, i didnt realize opencv had a built in. yeah, lemme swap that in and see how it goes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, the built in one seems to do a pretty poor job of handling the super-brightness that we're running into. gonna keep fiddling with it to see what's up, but may just stick with my algo for now if i can't get it to play nice

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would leave some comments in the code to that effect but yeah, makes sense overall. Some iteration to improve would help when we have the cycles.

- low level API: Users call fb.ApplyToneMapping(icc) explicitly before encoder.Encode()
- hi level API: Users can still use ImageOptions.ForceSdr with Transform()
// Apply Reinhard tone mapping
std::unique_ptr<cv::Mat> dst_bgr = std::make_unique<cv::Mat>(src_for_transform->rows, src_for_transform->cols, CV_8UC3);

// Apply luminance-based tone mapping to preserve color relationships
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would leave some comments in the code to that effect but yeah, makes sense overall. Some iteration to improve would help when we have the cycles.

}

// Load ICC profile
cmsHPROFILE src_profile = cmsOpenProfileFromMem(icc_data, icc_len);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may not be reliable to use just the icc profile for detecting HDR. some formats contain flags and not profiles ... would recommend looking more into this. This possibly could get complex so it's probably fine to narrow the scope to what we've been seeing in the wild as long as we make a note of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah jesus. okay, i'm going to kick this can down the road a bit for now

@salarkhan salarkhan merged commit d0bcbbf into master Nov 24, 2025
6 checks passed
@salarkhan salarkhan deleted the salar/tone-mapping branch November 24, 2025 21:54
@salarkhan salarkhan changed the title add HDR to SDR tone mapping support feat: add HDR to SDR tone mapping support Nov 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants