module proposal: gamut compression

That highlight roll-off is quite wonderful!

EDIT: would be interesting to see a comparison with sigmoid.

2 Likes

Sigmoid with smooth preset comes very close. However, it needs much more saturation regulation with color balance RGB.

Here is a comparison. Sigmoid on the left, AgX on the right. I increased the hue preservation a little on both to make the colors more realistic:

The great thing is that now you can have much more saturation with gamut compression without clipping.

6 Likes

Cool, thanks!

1 Like

Hello,
Congratulations, I just tested it on some old family photos that are quite difficult to process because I had overused the flash and the faces were difficult to correct.
I had never achieved such good results before. I was able to easily “oversaturate” without flattening. I am grateful to you just for these few photos.
For me, this is an excellent module that deserves a PR and, if possible, to be integrated into 5.4. By using it at the end of the workflow, it also allows you to use old modules safely. Congratulations on this work.
Best regards,
Christian

2 Likes

Thank you for the encouragement and for testing. It’s way too early to talk about a PR, we’ve just started experimenting. Boris has already identified colour shifts in certain cases that I’ll need to check.

Right now, we’re where agx was in March: testing, exploring, learning, with absolutely no guarantees of incorporating the module in darktable, or even the algorithm, in any shape or form.

3 Likes

Yes, that’s the branch. I’m grateful for your continued effort providing builds.

2 Likes

So any colour can be given coordinates in any space and that shows where the colour is in xyY. That seems fine to me.
However when it comes to actual photo processing, negatives are no good, as you found using logs. So I’d say the colour in the example (-0.12 etc) doesn’t have an actual rec709 encoding because it doesn’t exist in rec709.
So it’s necessary to decide - do you want to keep the colour, in which case work in a bigger space; or do you want to work in rec709, in which case the colour should be converted into rec709 before doing whatever it is you want to do.
So this is why I think the pipeline should have no negatives and modules should not output them.
Kofa, you clearly like exploring and that’s great. Can I suggest you look into Input Color Profile and see what’s involved in preventing negatives there. It would be brilliant if changes there resulted in improvements to sunsets etc like some of the examples above.

My first run seemed to include the module…I watched the progress in the terminal window but I could not find the module after installing DT …I’ll go back and double check where I might have gone wrong

EDIT
I had to go into darktablerc and set it to visible=true then it would come up in a search…

https://www.dropbox.com/scl/fi/oxxjrtx0jaac5ug12xowu/DT_gamut-portable.7z?rlkey=wp5nnbtgr8tmqfu3xxr606cc7&st=rwjol45h&dl=0

Its just unzip and run with the batchfile…as before I take care to work on separate images and turn off xmp writing…I will try to remember to do that ahead of time for any future versions…

1 Like

You’re right, I ran a few tests and found that reds tend to shift toward magenta.

No worries, it’s better to take your time. I’m convinced that a separate module for gamut management would be a plus for darktable.

1 Like

Are the hue shifts happening because the OOG colours are being shifted in along the x-axis (or nearest border?), not towards white/grey?

It’s a pure RGB-based algorithm. An ‘achromatic’ is constructed from the highest of the RGB components. The values of the components to be compressed are shifted towards this value, so towards the maximum of the components, depending on their distance (component-wise difference). The original algorithm also contains a mixing step (mixing with the original value).

Wasn’t there an issue with hue, in that in most colour spaces, points with identical hue do not form a straight line, but a curve? In that case, any translation towards white will cause a hue shift…

Iirc, it was one of the issues Aurélien Pierre had to deal with in the color calibration module

Possibly a non-linear (-affine) conversion instead of the current matrix-based approach. I think that vkdt does this already.

right. see @ggbutcher’s work here:

and the vkdt interpretation here:
https://jo.dreggn.org/vkdt/src/tools/clut/readme.html

2 Likes

@hanatos: Yes, this is what I was referring to. Thanks for linking in everything.

There are three ingredients to this approach:

  1. accurate profiling using spectral responses,
  2. using an interpolation,
  3. that always yields in-gamut results.

While (1) is the most fascinating from a nerdsniping perspective, (3) is probably the essential ingredient. (2) is a nice and intuitive way of parametrizing it.

I think that always getting in-gamut colors (regardless of how “correct” they are, as there is no generic mapping that is always correct) should be the first step and would handle a lot of problems. I agree that a lot of the out-of-gamut colors are just artifacts of using a linear approach.

Yes, but AFAIK some modern color spaces solve this problem, eg OKLab/OKLCh.

100%. that’s why vkdt has this slightly adventurous mode of guessing a spectral response from a dcp profile. it’s overall not very accurate, but doesn’t go overboard.

i found that to be surprisingly bad in some cases (though better than Lab). i’m using tabulated curves for saturation changes and either aurelien’s DTUCS or a grid search structure using the Munsell dataset for hue preserving application of a curve. having three solutions is always worse than one, go figure :slight_smile:

2 Likes

Wow, deja-vu, all over again… :laughing:

Gotta remember, any transform from the camera measurements is not “accurate”, it’s damage to accommodate our rendition media. Dragging hues in-gamut is more about ‘look-nice’ than ‘accurate’

3 Likes

just to defend the honor of spectral/non-linear input profiles: while in general you can’t even find a bijection from camera rgb to xyz, what you can do is move the observed camera rgb coordinate to an existing xyz coordinate. one that would hypothetically be constructed by a positive spectral energy distribution of your choice (or rather the lut’s/algorithm’s choice). of course completely ignoring any issues with metamerism.

dragging around coordinates into gamuts after the initial input device transform is certainly not something you’d do for accuracy.

3 Likes

Sometimes, it’s not about accuracy, but about mathematical necessity, like valid, but out-of-gamut (for Rec2020) colours, so we can take their logarithms.

I’m beginning to feel we’d need 2 different approaches, one for mitigating sensor profile issues, one for genuine compression, e.g. from Rec 2020 to sRGB for output. Jed’s examples are mostly about the former: relatively small compressions, often involving non-spectral (invalid) values.

Some compression charts, with Rec 2020 primaries into Rec 709, distance limits set to 1.5 for all, thresholds to 0.2.

--- Input Color Representations ---
  Linear Rec.709: R=1.6602, G=-0.1246, B=-0.0182
  Linear Rec.2020:R=1.0000, G=0.0000, B=0.0000
  XYZ:            X=0.6370, Y=0.2627, Z=0.0000
  xyY:            x=0.7080, y=0.2920, Y=0.2627

--- Algorithm Internals ---
  Original RGB distances:   d=[0.0000, 1.0750, 1.0109]
  Compressed RGB distances: cd=[0.0000, 0.9136, 0.8959]

--- Output Color Representations ---
  Linear Rec.709: R=1.6602, G=0.1434, B=0.1728
  Linear Rec.2020:R=1.0965, G=0.2486, B=0.1946
  XYZ:            X=0.7672, Y=0.4681, Z=0.2134
  xyY:            x=0.5296, y=0.3231, Y=0.4681

Green (still a bit out of gamut):

--- Input Color Representations ---
  Linear Rec.709: R=-0.5875, G=1.1329, B=-0.1006
  Linear Rec.2020:R=0.0000, G=1.0000, B=0.0000
  XYZ:            X=0.1446, Y=0.6780, Z=0.0281
  xyY:            x=0.1700, y=0.7970, Y=0.6780

--- Algorithm Internals ---
  Original RGB distances:   d=[1.5186, 0.0000, 1.0888]
  Compressed RGB distances: cd=[1.0031, 0.0000, 0.9172]

--- Output Color Representations ---
  Linear Rec.709: R=-0.0035, G=1.1329, B=0.0938
  Linear Rec.2020:R=0.3749, G=1.0426, B=0.1837
  XYZ:            X=0.4206, Y=0.8162, Z=0.2241
  xyY:            x=0.2879, y=0.5587, Y=0.8162

Blue:

--- Input Color Representations ---
  Linear Rec.709: R=-0.0728, G=-0.0083, B=1.1190
  Linear Rec.2020:R=0.0000, G=0.0000, B=1.0000
  XYZ:            X=0.1689, Y=0.0593, Z=1.0610
  xyY:            x=0.1310, y=0.0460, Y=0.0593

--- Algorithm Internals ---
  Original RGB distances:   d=[1.0651, 1.0075, 0.0000]
  Compressed RGB distances: cd=[0.9110, 0.8949, 0.0000]

--- Output Color Representations ---
  Linear Rec.709: R=0.0996, G=0.1176, B=1.1190
  Linear Rec.2020:R=0.1497, G=0.1277, B=1.0139
  XYZ:            X=0.2850, Y=0.1860, Z=1.0793
  xyY:            x=0.1838, y=0.1200, Y=0.1860