Gamut Mapping in Cylindrical and Conic Spaces

Hi,

I spent some more time digging tonight and turns out that a very close fitting function is as follows (180 just happened to be a convenient rounded value):

image

Plotted here:

Difference * 5

With vs Without

So this almost fixes the dreaded magenta cast plaguing the HSV model while giving some controls over the hue.

Parameterising the 180 constant allows to vary the clumping around the primary and secondary colours. I have not rolled that yet in the Jupyter Notebook but here are some Nuke nodes (that do not have the Hue Twist Controls):

set cut_paste_input [stack 0]
version 12.1 v1
Read {
 inputs 0
 file_type exr
 file /Users/kelsolaar/Documents/Development/colour-science/gamut-mapping-ramblings/resources/images/A009C002_190210_R0EI_Alexa_LogCWideGamut.exr
 format "1024 659 0 0 1024 659 1 "
 origset true
 name Read15
 selected true
 xpos -40
 ypos -114
}
Shuffle2 {
 fromInput1 {{0} B}
 fromInput2 {{0} B}
 mappings "4 rgba.red 0 0 rgba.red 0 0 rgba.green 0 1 rgba.green 0 1 rgba.blue 0 2 rgba.blue 0 2 white -1 -1 rgba.alpha 0 3"
 name Shuffle1
 selected true
 xpos -40
 ypos -33
}
ColorMatrix {
 matrix {
     {1.451439316 -0.2365107469 -0.2149285693}
     {-0.0765537733 1.1762297 -0.0996759265}
     {0.0083161484 -0.0060324498 0.9977163014}
   }
 name ACES2065-1_to_ACEScg
 selected true
 xpos -40
 ypos -9
}
set N27d0e000 [stack 0]
Expression {
 temp_name0 cmax
 temp_expr0 max(r,g,b)
 temp_name1 cmin
 temp_expr1 min(r,g,b)
 temp_name2 delta
 temp_expr2 cmax-cmin
 expr0 delta==0?0:cmax==r?(((g-b)/delta+6)%6)/6:cmax==g?(((b-r)/delta+2)/6):(((r-g)/delta+4)/6)
 expr1 "cmax == 0 ? 0 : delta / cmax"
 expr2 cmax
 name RGB_to_HSV
 note_font "Bitstream Vera Sans"
 selected true
 xpos -40
 ypos 39
}
set N279a9800 [stack 0]
Expression {
 temp_name0 i
 temp_expr0 i_Floating_Point_Slider
 temp_name1 o
 temp_expr1 o_Floating_Point_Slider
 expr0 "clamp((r - i) / (o - i) ** 2 * (3 - 2 * (r - i) / (o - i)))"
 expr1 "clamp((g - i) / (o - i) ** 2 * (3 - 2 * (g - i) / (o - i)))"
 expr2 "clamp((b - i) / (o - i) ** 2 * (3 - 2 * (b - i) / (o - i)))"
 name Smoothstep
 selected true
 xpos -150
 ypos 87
 addUserKnob {20 User}
 addUserKnob {7 i_Floating_Point_Slider l i}
 i_Floating_Point_Slider {0.7}
 addUserKnob {7 o_Floating_Point_Slider l o}
 o_Floating_Point_Slider {{parent.tanh_Compression.t_Floating_Point_Slider}}
}
push $N27d0e000
push $N279a9800
Dot {
 name Dot17
 selected true
 xpos 104
 ypos 42
}
Expression {
 temp_name0 t
 temp_expr0 t_Floating_Point_Slider
 temp_name1 l
 temp_expr1 l_Floating_Point_Slider
 expr0 "r > t ? t + l * tanh((r - t) / l) : r"
 expr1 "g > t ? t + l * tanh((g - t) / l) : g"
 expr2 "b > t ? t + l * tanh((b - t) / l) : b"
 name tanh_Compression
 selected true
 xpos 70
 ypos 87
 addUserKnob {20 User}
 addUserKnob {7 t_Floating_Point_Slider l a}
 t_Floating_Point_Slider 0.8
 addUserKnob {7 l_Floating_Point_Slider l b}
 l_Floating_Point_Slider {{"1 - t_Floating_Point_Slider"}}
}
Dot {
 name Dot18
 selected true
 xpos 104
 ypos 162
}
push $N279a9800
Expression {
 expr0 "r + sin(r*pi*6+pi)*(pi/a_Floating_Point_Slider)"
 expr1 "g + sin(g*pi*6+pi)*(pi/a_Floating_Point_Slider)"
 expr2 "b + sin(b*pi*6+pi)*(pi/a_Floating_Point_Slider)"
 name Clumping_Expression
 selected true
 xpos -40
 ypos 111
 addUserKnob {20 User}
 addUserKnob {7 a_Floating_Point_Slider l a R 90 270}
 a_Floating_Point_Slider 180
}
Expression {
 expr0 "r % 1"
 name Modulus_Expression
 selected true
 xpos -40
 ypos 135
}
ShuffleCopy {
 inputs 2
 green green
 alpha alpha2
 name ShuffleCopy
 note_font "Bitstream Vera Sans"
 selected true
 xpos -40
 ypos 159
}
Expression {
 temp_name0 C
 temp_expr0 b*g
 temp_name1 X
 temp_expr1 C*(1-abs((r*6)%2-1))
 temp_name2 m
 temp_expr2 b-C
 expr0 (r<1/6?C:r<2/6?X:r<3/6?0:r<4/6?0:r<5/6?X:C)+m
 expr1 (r<1/6?X:r<2/6?C:r<3/6?C:r<4/6?X:r<5/6?0:0)+m
 expr2 (r<1/6?0:r<2/6?0:r<3/6?X:r<4/6?C:r<5/6?C:X)+m
 name HSV_to_RGB
 note_font "Bitstream Vera Sans"
 selected true
 xpos -40
 ypos 183
}
Merge2 {
 inputs 2+1
 maskChannelMask rgba.red
 name Merge1
 selected true
 xpos -150
 ypos 183
}
Viewer {
 frame_range 1-100
 viewerProcess "sRGB (ACES)"
 name Viewer1
 selected true
 xpos -150
 ypos 207
}

Cheers,

Thomas

1 Like