Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@Starbix
Copy link

@Starbix Starbix commented Nov 10, 2025

This is a WIP PR trying to fix #3912.

HDR AVIF (and most likely HEIF) files that lack an ICC profile signal their interpretation via CICP values (as defined in H.273) in the nclx field. libvips ignores this information, leading to very desaturated colors.

This PR attempts to create an ICC profile from the CICP. Creating a simple parametric curve ICC profile does not work for PQ/HLG as the curves defined by lcms2 aren't able to cover PQ or HLG. Thus in this PR I simply applied ITU-R BT.2020-2 curves.
image

This doesn't solve the wrong brightness. Defined in ICC v4.4 there is a CICP tag which can signal the exact transfer function. This is interpreted by Chromium and macOS Preview (I didn't test other clients). Unfortunately, this tag is not supported by lcms2: (mm2/Little-CMS#439). Meaning in case of an icc_transform that HDR information is lost again. But at least it makes it already easier for downstream clients (such as Immich) to handle HDR from libheif more easily (e.g. by not doing an icc_transform)
In a second step we should add a tonemapping curve instead so clients (such as libvips itself) that don't support the cicp tag in the ICC profile can still produce a decent looking image. relevant: https://issues.chromium.org/issues/40239687, discord/lilliput#218

I only tested this with a P3 PQ HDR file created by Adobe Lightroom.
I have attached three files: the original HDR AVIF, the output JPG by upstream libvips, and the JPG with an HDR ICC profile.
examples.zip

@Starbix Starbix marked this pull request as draft November 10, 2025 13:53
@jcupitt
Copy link
Member

jcupitt commented Nov 15, 2025

Hello @Starbix, I thought I'd repeat something said in chat so we have a record.

Another possible approach (which I think is maybe what you were proposing in chat) could be to change heifload to tag the image with the NCLX metadata, somewhat in the way uhdrload does.

For example, with git master libvips you get:

$ vipsheader -a ultra-hdr.jpg
ultra-hdr.jpg: 3840x2160 uchar, 3 bands, srgb, uhdrload
width: 3840
height: 2160
bands: 3
format: uchar
coding: none
interpretation: srgb
xoffset: 0
yoffset: 0
xres: 1
yres: 1
filename: ultra-hdr.jpg
vips-loader: uhdrload
icc-profile-data: 588 bytes of binary data
gainmap-data: 31738 bytes of binary data
gainmap-max-content-boost: 100 100 100 
gainmap-min-content-boost: 1 1 1 
gainmap-gamma: 1 1 1 
gainmap-offset-sdr: 0 0 0 
gainmap-offset-hdr: 0 0 0 
gainmap-hdr-capacity-min: 1
gainmap-hdr-capacity-max: 100
gainmap-use-base-cg: 1

ie. there are a set of extra metadata tags holding the transform. On uhdrsave, these are used to set the metadata in the output file.

We could do something similar for heifload (and other loaders and savers which support nclx, like jxl), and add a new operation (maybe called nclx2scRGB?) which applied the nclx metadata to the image, computing scRGB (3 x float bands, 0-1 linear for black to white, out of range values for HDR). This would let us feed these images into an ICC-profile-managed workflow.

We could also add VIPS_INTERPRETATION_NCLX and treat it like any other colourspace. That would let things like:

$ vips uhdrsave x.avif y.jpg

work too, which might be useful (the gainmap would be generated automatically).

What do you think? It might be simpler than trying to get lcms to accept an HDR profile reliably.

@Starbix
Copy link
Author

Starbix commented Nov 15, 2025

Yes that sounds like a very sensible approach that doesn't abuse ICC. I'd need to further understand the inner workings of lcms2 to try some approach (and also investigate how to map to scRGB). But the code should probably live somewhere else than heifload.c.

@jcupitt
Copy link
Member

jcupitt commented Nov 15, 2025

Great! Let's try this approach.

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.

Add nclx->icc colour management to heifload

2 participants