GIO LUT: check R3D exposure in post production!

In Manifold Recording, studio tech, technique, video by Michael Tiemann

When RED released the GIO Scope tool in October 2014, it bridged the worlds of cinematography and The Zone System. Zebras and false-color IRE tools are all well and good, but when you have a camera that can record 16 stops of dynamic range, you want to know more than just “where are the guardrails?” You want to know how much latitude you have (or need) to really push or pull exposure in post. GIO Scope gives you that, but only in the camera, when the meters (all the meters!) are running.

After a shoot, I often found myself editing some interesting imagery which, nevertheless, gave me some challenge. Waveform and vector scope monitors have their place, but what I really wanted in my post environment was access to my GIO Scope, both as a way to understand my image, but also as a way to learn what I needed to see and understand better the next time around.

“Easy!” I thought. I’ll just make a LUT that gives me the same false colors as the GIO Scope. But from what reference? I knew I could get RAW data from RED images using 32-bit Linear light. But that doesn’t divide very neatly into a 65x65x65 3D cube, nor even a 4096 entry 1D LUT. In the latter case, half of all the entries encode the last (16th) stop; half of what remains encodes the 15th stop, and so forth. The top 10 stops take all but the last 4 entries–not nearly enough to encode what’s left of the remaining 6 stops. And that’s not even accounting for noise! And even if it did work (which it doesn’t), Red Cine-X doesn’t really work with 1D LUTs anyway (despite its promise to do so once upon a time). And it’s image pipeline via the GPU is only 8 bits–not enough to do high-precision math on linear values. 3D cubes? Forget about it.

Meanwhile, the maths for various log curves (RedLogFilm, RedGamma4, etc.) were all black magic (pardon the pun). Knowledge existed that 0.18 was middle gray in Linear light. member Gavin Greenwalt published “unofficial” LUTs to convert between Linear light and various RED gammas, including RedLogFilm and RedGamma4. Published right next to those files was a big disclaimer that if you have the choice, built-in mathematical transforms (which can preserve values with arbitrary precision) should be used in preference to the LUTs. Nevertheless, I used the LUTs to derive my way to middle gray in RedLogFilm (and other gammas) and tried to construct a log-based GIO LUT. But I remained stymied by lack of precision in the lower stops. Which is the first place you want to look when you want to understand shadow noise. Ugh.

RED’s new IPP2 changed everything. Now there a rock-solid definition of middle gray (0.18 maps to 1/3 or 0.3333) based on Log3G10, and both Red Cine-X and Davinci Resolve supported the Log3G10 image pipeline end-to-end. I renewed my efforts and got closer to a good result, but I still could not get the bottom 5 (!) stops to behave themselves. Log3G10 can encode up to 10 stops above middle gray, which means it has 2/3 (or 0.6666) to allocate between those 10 stops. Which means that above middle gray, a stop of light is 0.06666. Being that middle gray is the 11th chip in the GIO scope, we really want to decode 10 stops below middle gray, too. But we only have half that space logarithmically (we have 1/3 below 0.18 and 2/3 above it). What to do?

Answer: dig deeper into the full power of IPP2 and use a CDL adjustment prior to applying the LUT.

I created a series of 33 LUTs, each of which responded to one and only one level of RGB stimulus ([0/32,0/32,0/32], [1/32,1/32,1/32], [2/32,2/32,2/32], …, [32/32,32/32,32/32]). With a lens cap in place, I noticed that neither the first, nor the second, nor the third LUTs registered any input. Once I found one that did, I had an idea of what was the true level of my digital black. By subtracting that number from my input (a negative offset in the CDL), I could use all the buckets available to me on the low side. I also noticed that having subtracted that offset, not only did the last LUT never light up when reading the brightest white, but neither did the 32nd nor the 31st. By increasing the slope I could expand the range of my high values to use all the high buckets. Of course increasing the slope also changed my offset calculation, so I had to go and fix that. And re-validate the slope. But once the experiment converged, I had a LUT and CDL that gave me the fine accuracy of the GIO scope. And it worked on camera, in Red Cine-X, and in Resolve 14.2. Hooray!

Here’s a video of the demonstration:

The video describes version 0.0 of our GIO LUT. We are now up to 0.2! Right now we’re using REC 709 monochromatic parameters to convert colorful light to basic brightnesses. I have some notes that with DRAGON, the CCIR 601 monochromatic parameters work better with Chroma Du Monde color chips. In addition to interpreting RGB (actually, RedWideGamutRGB) input as a monochromatic source, there may be some validity to building a LUT that keys off of the darkest (or brightest) pixel of each RGB triple. Tuning the LUT to min(RGB) instead of mono(RGB) may help one better protect from shadow noise, while tuning the LUT to max(RGB) may help one better protect from highlight clipping. And some may quibble with my interpretation or implementation of the colors I used. We’re excited to know what you like and what you think should be in the next version.

You can download the 3D LUT, two CDL files, and some basic documentation in the R3D/GIO LUT dropbox folder.

The current CDL parameters we are using are:

Slope = 1.1
Offset = -0.10184
Power = 0.5

It produces the following color map for a 33x33x33 LUT:

gioColors33 = [[0.29,0,0.36],# UV-1 @ 0
[0.42,0,0.51], # Magenta-2 @ 1
[0.4,0.39,0.61], # Violet-3 @ 2
[0, 0.32, 0.46], # DkBlue-4 @ 3
[0, 0.42, 0.51], # MedBlue-5 @ 4
[0, 0.48, 0.55], # Sky-6 @ 5
[0, 0.48, 0.55], # Sky-6 @ 6
[0, 0.61, 0.61], # Cyan-7 @ 7
[0, 0.61, 0.61], # Cyan-7 @ 8
[0.3, 0.51, 0.47], # CyanG-8 @ 9
[0.3, 0.51, 0.47], # CyanG-8 @ 10
[0, 0.51, 0.44], # Aqua-9 @ 11
[0, 0.51, 0.44], # Aqua-9 @ 12
[0, 0.51, 0], # Green-10 @ 13
[0, 0.51, 0], # Green-10 @ 14
[0.35, 0.48, 0], # GreenY-11 @ 15
[0.35, 0.48, 0], # GreenY-11 @ 16
[0.41, 0.45, 0], # YellowG-12 @ 17
[0.41, 0.45, 0], # YellowG-12 @ 18
[0.45, 0.51, 0], # YYG-13 @ 19
[0.45, 0.51, 0], # YYG-13 @ 20
[0.55, 0.53, 0], # Yellow-14 @ 21
[0.55, 0.53, 0], # Yellow-14 @ 22
[0.51, 0.37, 0], # Orange-15 @ 23
[0.51, 0.37, 0], # Orange-15 @ 24
[0.51, 0, 0], # Red-16 @ 25
[0.34, 0, 0], # Red-16.1 @ 26
[0.34, 0, 0], # Red-16.1 @ 27
[0.23, 0, 0], # Red-16.2 @ 28
[0.1, 0, 0], # Red-16.3 @ 29
[0.05, 0, 0], # Red-16.4 @ 30
[0.05, 0, 0], # Red-16.4 @ 31
[0.03, 0, 0], # Red-16.5 @ 32