Simplistic Gamut Mapping Approaches in Nuke

Tags: #<Tag:0x00007fa12c4feac8> #<Tag:0x00007fa12c4fe848>


As mentioned during Meeting #6 and demo’ed at Meeting #7 I’m sharing a Nuke script illustrating some examples of simplistic approaches to the gamut mapping problem.
These are not intended as actual proposals, but rather to illustrate known issues and potential ideas for solutions.
So this is mainly meant as a conversation starter and an easy way for participants to look at some actual images.


The methods shown try to satisfy the following requirements for the algorithm:

  • non-iterative
  • exposure invariant
  • source gamut agnostic
  • adjustable core “confidence” gamut

The Nuke script does not rely on any custom plug-ins or external resources, except the images you want to try it out with.
It is (hopefully) self-explanatory enough to get everyone started, but please reach out with any problems or questions in using it.

Below is a quick explanation of the 6 mapping methods available in the script:

1. Unmapped:
No gamut mapping is being applied.

2. HSV Saturation Soft-Clip:

The image is converted to a cylindrical HSV projection of the target gamut and the saturation component is soft-clipped to 1.0.

Pros: extreme simplicity
Cons: significant over-compression, especially for red-yellow colours

3. Soft-Project x,y:

The image is converted to CIE Yxy and the chromaticity coordinate is projected onto the target gamut boundary using a soft clip function on the distance from the white coordinate

Pros: simple and intuitive?
Cons: The luminance (Y) component is zero or negative for saturated blue colours and results in target gamut RGB values that are too dark or even black.

4. Normalized HSV Saturation Softclip in XYZ:

The image is converted to a cylindrical HSV projection of CIE XYZ. The saturation component is normalized to the boundary of the target gamut and then soft-clipped to 1.0.

Pros: relatively simple, does not suffer from the negative luminance issue of method #3
Cons: significant blue>purple “Abney Effect”, plot shows jagged non-monotonic mapping at target gamut boundary

5. Normalized HSV Saturation Softclip in LMS:

The image is converted to a cylindrical HSV projection of LMS cone fundamentals according to M. Safdar 2017. The saturation component is normalized to the boundary of the target gamut and then soft-clipped to 1.0.

Pros: also does not suffer from the negative luminance issue of method #3, M. Safdar cone fundamentals are supposed to suppress the shift to purple (although it doesn’t seem to have much effect in this example)
Cons: still significant blue>purple “Abney Effect”

6. Normalized HSV Saturation Softclip in LMS + purple suppression:

Like method #5 but also includes a pre-process step that slightly warps blue hue values to suppress the blue>purple “Abney Effect”. Distortion parameters were manually chosen for optimal visual results with available sample images.

Pros: much better mapping result for saturated blue colours
Cons: hue distortion does also affect colours within the “core gamut” and limiting it to the mapping region only results in counterintuitive hue shifts along saturation gradients

I look forward to hearing your feedback, suggestions and other examples.

Best Regards,



Great stuff Matthias!

I only shortly dived into the script, it is clean and quite easy to understand!

What is clear is that very soon a favourite topic will be The Compression Function!

One that I use often for same purposes is : (a + b * tanh((x-a) / b)) Its trigonometric elegance is hard to beat: I added yours too (( -1 / (( x - a ) / ( 1 - a ) +1 ) +1 ) * ( 1 - a ) + a).

Nuke variant for those willing to try, it is a great utility function:

set cut_paste_input [stack 0]
version 12.1 v1
push $cut_paste_input
Expression {
 expr0 "r > a_Floating_Point_Slider ? a_Floating_Point_Slider + b_Floating_Point_Slider * tanh((r - a_Floating_Point_Slider) / b_Floating_Point_Slider) : r"
 expr1 "g > a_Floating_Point_Slider ? a_Floating_Point_Slider + b_Floating_Point_Slider * tanh((g - a_Floating_Point_Slider) / b_Floating_Point_Slider) : g"
 expr2 "b > a_Floating_Point_Slider ? a_Floating_Point_Slider + b_Floating_Point_Slider * tanh((b - a_Floating_Point_Slider) / b_Floating_Point_Slider) : b"
 name Compression_Expression1
 selected true
 xpos 3060
 ypos 1087
 addUserKnob {20 User}
 addUserKnob {7 a_Floating_Point_Slider l a}
 a_Floating_Point_Slider 0.76
 addUserKnob {7 b_Floating_Point_Slider l b}
 b_Floating_Point_Slider {{"1 - a_Floating_Point_Slider"}}

I noticed that 5 and 6 blow with the Grasshoper Cornell Box image, haven’t tried to figure out why yet:



I often use a variation on @matthias.scharfenber’s compression function, which adds two more parameters (c and d) to control the point at which compression begins, and the value it rolls off to (if compressing everything into the 0-1 range is not a requirement).

x < d + a ? x :  d + (-1 / ((x - d - a) / (c - a) + 1) + 1) * (c - a) + a 

I have to credit @Paul_Dore with showing me this one.

1 Like

A paper on the limitations of simplistic hue linear encoding models.



I was poking at a variant of HSV I did not know about to satisfy my curiosity, having thin hopes that it could do something better than HSV: HCL by Sarifuddin and Missaoui (2005).

Unfortunately no cigar, it is even worse than HSV!

sRGB Colour Wheel

ACES 2065-1 to sRGB Colour Wheel

Tasty Negative Colours

HSV Saturation Compression

No More Negative Colours

HCL Chroma Compression

Unsurprising Disappointment

I was considering porting it to Nuke initially but given those results it will stay as Python in my world. You can find an implementation in this issue.



Due to popular demand I have now uploaded a ZIP archive containing debayered ACES/AP0 OpenEXR versions of the submitted test images to Dropbox.

Linked Here

The archive also includes an updated version of the Nuke script that uses these EXRs to make it more accessible for interested users.

The compression function of the script can now be switched between the original soft clip function and the version proposed by @Thomas_Mansencal .
The chromaticity plot can now also be switched between CIE 1931 x,y and 1976 u’,v’ coordinates.