Tone Compression in Nuke
I recently packaged up a Nuke setup for compressing values in images to a single “compressor” node and wanted to share it. It’s sometimes useful to be able to compress or roll off high values in comps, either for technical reasons (to match the pixel limit of the sensor that captured the plate or extract just the detail or colour info) or for more creative reasons (pulling down the top end of a comp without breaking anything). This can be done using Nuke’s build in nodes such as the SoftClip or combining a string of Clamp nodes to gradually roll off highlights. However the opposite is also useful sometimes too - compressing either just shadows, or highlights as well to flatten the entire image in a smooth way. I wanted to emulate Adobe’s methods of shadow/highlight control in their Camera Raw Utility, which manages to compress in a very subtle and understated way, rarely with any artefacts. I also built into this Nuke setup a way to push tone compression in a “tone mapping” sort of way for a HDR aesthetic, which uses frequency information as well as simple pixel values. The general method uses multiplication of highlights in log space (flipped for the shadows) in separated frequencies.
The shadow/highlights parameter is a ratio to balance how much of either you affect. 0.5 is an equal mix, and the image will compress at the top and bottom end.
Shadow and highlight ranges control how much of the tones will be affected.
Tonemapping will flatten the image in a HDR-like way, with the radius slider controlling the pixel frequencies defining the edges in the image.
For subtle “rolloff” use, lower radius values down to 0 work fine, and tonemapping can be left alone.
The script for the tool:
set cut_paste_input [stack 0] version 12.1 v2 push $cut_paste_input Group { name CT_Compressor tile_color 0xca86600 selected true xpos 48 ypos 6810 addUserKnob {20 CTCompressor l "CT Compressor"} addUserKnob {7 compress R 0 2} addUserKnob {7 balance l shadows/highlights} balance 0.5 addUserKnob {26 ""} addUserKnob {7 shadowRange l "shadow range" R 0 2} shadowRange 1 addUserKnob {7 highlightRange l "highlight range" R 0 2} highlightRange 1 addUserKnob {26 ""} addUserKnob {7 restore l tonemapping} restore 0.3 addUserKnob {7 detail l radius R 0 3} detail 0.5 } Input { inputs 0 name image selected true xpos 1190 ypos 141 } Log2Lin { operation lin2log name Log2Lin3 xpos 1149 ypos 405 } Dot { name Dot13 xpos 1162 ypos 473 } set N736f7440 [stack 0] Dot { name Dot3 xpos 885 ypos 473 } Dot { name Dot2 xpos 879 ypos 1227 } push $N736f7440 Group { name diffusion xpos 1120 ypos 584 addUserKnob {20 User} addUserKnob {41 size T Blur1.size} } Input { inputs 0 name Input1 xpos -8 ypos -105 } Dot { name Dot1 xpos 26 ypos -79 } Dot { name Dot2 xpos 278 ypos 24 } set N731e4b80 [stack 0] Blur { size {{parent.Blur1.size*16}} name Blur5 xpos 911 ypos 137 } push $N731e4b80 Blur { size {{parent.Blur1.size*8}} name Blur4 xpos 623 ypos 119 } push $N731e4b80 Blur { size {{parent.Blur1.size*4}} name Blur3 xpos 486 ypos 118 } push $N731e4b80 Blur { size {{parent.Blur1.size*2}} name Blur2 xpos 358 ypos 111 } push $N731e4b80 Blur { size {{(parent.parent.compress*50)*detail}} name Blur1 xpos 244 ypos 114 } Dissolve { inputs 2 which 0.5 name Dissolve1 xpos 314 ypos 176 } Dissolve { inputs 2 which 0.25 name Dissolve2 xpos 486 ypos 176 } Dissolve { inputs 2 which 0.125 name Dissolve3 xpos 623 ypos 176 } Dissolve { inputs 2 which 0.0625 name Dissolve4 xpos 817 ypos 176 } Output { name Output1 xpos 817 ypos 436 } end_group Dot { name Dot19 xpos 1154 ypos 650 } set Na5efeab0 [stack 0] push $N736f7440 Dot { name Dot1 xpos 1573 ypos 473 } Merge2 { inputs 2 operation from name Merge12 xpos 1613 ypos 623 } Dot { name Dot18 xpos 1564 ypos 1152 } push $Na5efeab0 Dot { name Dot15 xpos 1141 ypos 706 } set N13f70cd0 [stack 0] push $N13f70cd0 Invert { channels rgb name Invert4 xpos 1136 ypos 763 } Multiply { channels rgb value {{parent.shadowRange}} name Multiply1 xpos 1136 ypos 789 } ColorCorrect { highlights.offset {{1-((compress*(1-balance))+1)}} lookup {shadow {} midtone {} highlight {curve x0.265168488 0 s0 x1.309550405 0.9974999428 s0}} name shads xpos 1136 ypos 815 } Multiply { channels rgb value {{1/parent.Multiply1.value}} name Multiply2 xpos 1136 ypos 841 } Invert { channels rgb name Invert2 xpos 1136 ypos 867 } Dot { name Dot11 xpos 1180 ypos 997 } set N57f919c0 [stack 0] Merge2 { inputs 2 operation divide name Merge10 xpos 1457 ypos 969 } push $N57f919c0 Merge2 { inputs 2 operation multiply mix {{1-restore}} name Merge11 xpos 1167 ypos 1090 } Merge2 { inputs 2 operation plus name Merge13 xpos 1167 ypos 1148 } Merge2 { inputs 2 operation max name Merge1 xpos 1188 ypos 1223 } Dot { name Dot4 xpos 1222 ypos 1358 } set N803fc9d0 [stack 0] Dot { name Dot12 xpos 958 ypos 1383 } Dot { name Dot5 xpos 984 ypos 2047 } push $N803fc9d0 Group { name diffusion2 xpos 1236 ypos 1501 addUserKnob {20 User} addUserKnob {41 size T Blur1.size} } Input { inputs 0 name Input1 xpos -8 ypos -105 } Dot { name Dot1 xpos 26 ypos -79 } Dot { name Dot2 xpos 278 ypos 24 } set N729fcdb0 [stack 0] Blur { size {{parent.Blur1.size*16}} name Blur5 xpos 911 ypos 137 } push $N729fcdb0 Blur { size {{parent.Blur1.size*8}} name Blur4 xpos 623 ypos 119 } push $N729fcdb0 Blur { size {{parent.Blur1.size*4}} name Blur3 xpos 486 ypos 118 } push $N729fcdb0 Blur { size {{parent.Blur1.size*2}} name Blur2 xpos 358 ypos 111 } push $N729fcdb0 Blur { size {{(parent.parent.compress*50)*detail}} name Blur1 xpos 244 ypos 114 } Dissolve { inputs 2 which 0.5 name Dissolve1 xpos 314 ypos 176 } Dissolve { inputs 2 which 0.25 name Dissolve2 xpos 486 ypos 176 } Dissolve { inputs 2 which 0.125 name Dissolve3 xpos 623 ypos 176 } Dissolve { inputs 2 which 0.0625 name Dissolve4 xpos 817 ypos 176 } Output { name Output1 xpos 817 ypos 436 } end_group Dot { name Dot6 xpos 1270 ypos 1567 } set N754f9120 [stack 0] push $N803fc9d0 Dot { name Dot7 xpos 1672 ypos 1389 } Merge2 { inputs 2 operation from name Merge2 xpos 1712 ypos 1539 } Dot { name Dot8 xpos 1669 ypos 1972 } push $N754f9120 Dot { name Dot9 xpos 1270 ypos 1614 } set N71ff84b0 [stack 0] push $N71ff84b0 Multiply { channels rgb value {{parent.highlightRange}} name Multiply3 xpos 1252 ypos 1663 } ColorCorrect { highlights.gain {{1-(compress*(balance))}} lookup {shadow {} midtone {} highlight {curve x0.265168488 0 s0 x1.014901519 0.9974999428 s0}} name highlights xpos 1252 ypos 1689 } Clamp { channels rgb minimum 1e-05 maximum_enable false name Clamp1 xpos 1258 ypos 1716 } set N71ef3050 [stack 0] Multiply { channels rgb value {{1/parent.Multiply3.value}} name Multiply4 xpos 1252 ypos 1753 } Dot { name Dot10 xpos 1306 ypos 1800 } set N71dfdc20 [stack 0] Merge2 { inputs 2 operation divide name Merge3 xpos 1543 ypos 1796 } push $N71dfdc20 Merge2 { inputs 2 operation multiply mix {{1-restore}} name Merge4 xpos 1272 ypos 1876 } Merge2 { inputs 2 operation plus name Merge5 xpos 1272 ypos 1968 } Merge2 { inputs 2 operation min name Merge6 xpos 1293 ypos 2043 } Dot { name Dot14 xpos 1308 ypos 2139 } Log2Lin { name Log2Lin4 xpos 1274 ypos 2206 } set N716f9f10 [stack 0] push 0 push 0 push 0 push 0 push 0 push 0 push 0 push $N71ef3050 push $N716f9f10 Output { name Output1 xpos 1271 ypos 2776 } Viewer { inputs 10 frame 1129 frame_range 989-989 colour_sample_bbox {-0.06591796875 0.1059570312 -0.0654296875 0.1062011719} name Viewer1 xpos 2490 ypos -10 } end_group