LUT Conversion - Unexpected Results

I have a number of LUTs I want to apply an image of them being a LUT_3D_SIZE 16, which I have enclosed. If I use ffmpeg, I get a “good looking” result, with gmic, I don’t when I use it as follows

gmic 'TESTIMAGE.jpg' 'Hard.cube' 'map_clut[0]' '[1]' 'o[0]' './_lut-converted/TESTIMAGE__Hard_gmic.jpg

All other LUT are LUT_3D_SIZE 32 - and there it seems to work.

Is there anything I need to change in order to get similar results as when using ffmpeg? I tried to upload the images and the LUT but the upload function seems to be broken at the moment.

Could you upload the .cube file somewhere else ?
It’ nearly impossible to check what happens if I can’t reproduce the problem here.

1 Like

PMed it to you. And thanks for your continued support.

OK, so after quick investigation, that is what I thought :
This LUT contains out-of-bounds values (not in range [0,1]), whicn means that when you apply it on a sRGB image, you end up with an image that has also out-of-bounds values.

Basically;

$ gmic sp colorful Hard.cube map_clut.. .
[gmic]./ Start G'MIC interpreter (v.3.3.3).
[gmic]./ Input sample image 'colorful' (1 image 800x800x1x3).
[gmic]./ Input CLUT from file 'Hard.cube' (1 image 16x16x16x3).
[gmic]./ Map color LUT . on image [0].
[gmic]./ Display images [0,1] = 'colorful, Hard.cube'.
[0] = 'colorful':
  size = (800,800,1,3) [7500 Kio of float32].
  data = (13.8492,-19.7799,-99.2392,-95.8161,-95.8161,-85.1882,-19.7799,13.0144,11.9683,13.0144,13.0144,-85.1882,-95.8161,-99.2392,-99.2392,-99.2392,-95.8161,-95.8161,-95.8161,-95.8161,-95.8161,-95.8161,-95.8161,-95.8161,-99.2392,-99.2392,-99.2392,-85.1882,-99.2392,-99.2392,-99.2392,-99.2392,-99.2392,-99.2392,-95.8161,-95.8161,-95.8161,-99.2392,-19.7799,-91.5903,-95.8161,-99.2392,-85.1882,-85.1882,-85.1882,-99.2392,-95.8161,-99.2392,-99.2392,-99.2392,-95.8161,-64.3068,-64.3068,-85.1882,-99.2392,-85.1882,-85.1882,-64.3068,-64.3068,-99.2392,-95.8161,-99.2392,-85.1882,-100.699, ... ,-20.4432,-20.4432,-20.4432,-20.4432,-20.4432,-20.4432,-20.4432,-16.2401,-20.4432,-6.27372,-20.4432,-20.4432,-20.4432,-20.4432,-16.2401,-6.27372,-6.27372,-20.4432,-6.27372,-6.27372,-6.27372,-20.4432,-20.4432,-20.4432,-20.4432,-20.4432,-20.4432,-16.2401,-16.2401,-16.2401,-95.3952,-95.3952,-95.3952,-95.3952,-95.3952,-95.3952,-95.3952,-95.3952,-16.2401,-95.3952,-95.3952,-95.3952,-95.3952,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024,-91.2024).
  min = -138.993, max = 348.724, mean = 84.8826, std = 112.177, coords_min = (684,0,0,0), coords_max = (777,33,0,2).
[1] = 'Hard.cube':
  size = (16,16,16,3) [48 Kio of float32].
  data = (-24.8791,-25.183,-20.7968,-21.3053,-26.8329,17.7911,30.7933,55.4495,80.757,102.814,111.081,119.551,123.261,124.094,132.431,160.014;-25.1647,-25.2154,-20.0371,-22.5869,-29.4905,13.6512,27.5214,55.7792,79.5156,102.074,110.949,118.781,123.184,124.07,132.057,159.837;7.87874,-10.1699,-11.8756,-26.0868,-27.1715,16.5459,28.2203,58.3052,84.3015,104.616,116.385,120.355,123.761,125.642,134.033,160.733;-30.4954,-22.8444,-11.5795,17.2097,-7.70788,19.2377,37.8649,69.716,98.0664,114.223,123.907,125.751,127.94,130.773,136.308,163.958, ... ,211.557,211.445,212.42,211.413,210.288,211.791,216.795,223.799,235.197,245.319,251.503,251.484,245.105,251.977,253.753,259.47;225.232,225.277,225.323,224.974,223.319,226.278,229.668,237.438,247.727,253.605,256.475,257.943,256.541,246.339,254.578,262.357;241.926,241.684,242.415,241.82,241.765,242.101,246.058,251.55,255.071,261.313,265.149,266.491,266.861,264.042,258.95,269.244;267.941,267.922,268.266,268.227,268.306,268.965,270.577,273.511,276.892,279.627,281.513,282.74,284.293,286.095,289.008,297.345).
  min = -228.822, max = 352.56, mean = 151.026, std = 120.036, coords_min = (4,0,3,0), coords_max = (15,15,14,2).
[gmic]./ End G'MIC interpreter.

Look at the min/max values you get for images [0] and [1], it’s definitely not [0,255].

So, by chance (for you), ffmpeg did the value clamp automatically, which may be good for video frames, but not that good for general images, when you may want to keep those unbounded values visibles.

You can easily clamp your result as well in G’MIC, with adding cut 0,255 before saving the image.

$ gmic sp colorful Hard.cube map_clut.. . k.. c 0,255 o output.jpg

and you should get something very close to what ffmpeg does (assuming it indeed clamps the values as I think it does).

1 Like

And BTW, just looking at your Hard.cube file, that’s a very weird LUT, it just looks really bad.

$ gmic Hard.cube

It has an almost complete black plane for G==0 and a complete white plane for G==1, with a lot of negative values between the two. Looks like non-sense to me :slight_smile:

If you look at the content of the .cube file, you can see it does not start that well either:

$ more Hard.cube
LUT_3D_SIZE 16
-0.097565 -0.092976 -0.097565
-0.098757 -0.094101 -0.098757
-0.081556 -0.076978 -0.081556
-0.083550 -0.078564 -0.083550
-0.105227 -0.109099 -0.105227
0.069769 0.065046 0.069769
0.120758 0.139960 0.128572
0.217449 0.231766 0.223222
0.316694 0.328851 0.309323
0.403193 0.397478 0.383101
0.435610 0.450181 0.422819
0.468827 0.464416 0.444418
0.483378 0.481327 0.456084
0.486645 0.489613 0.468634
0.519337 0.513052 0.485468
0.627504 0.616512 0.591214
-0.098685 -0.093574 -0.098685
-0.098884 -0.093653 -0.098884
-0.078577 -0.073320 -0.078577
-0.088576 -0.085038 -0.088663
-0.115649 -0.122366 -0.115763
0.053534 0.044668 0.054072
0.107927 0.126797 0.120761
0.218742 0.224018 0.219249
0.311826 0.321831 0.301112
0.400291 0.395240 0.380709
(...)
1 Like

Thanks a lot, David. I was collecting all the black and white LUTs lately that I could get my hand on as I really enjoy black and white images. This was one of them (I need to see where I got it from).

If you want, you could write up a script to fix up values and then replace the bad .cube files only.

I actually enjoyed the results in ffmpeg and was wondering why it looked so odd with gmic. I will now move my workflow to gmic and dump the “wrong” .cubefiles.

By the way, I asked Bing Chat about negative numbers in .cube files and here is the answer:

Yes, .cube LUT (Look-Up Table) files can indeed contain negative numbers. These negative values in the LUT file represent a decrease in the corresponding color value¹. For example, if a data row in the LUT file looks like this:

0.1201138 0.0000000 -0.0063639

And the original output color was:

vec3 = { 0.5, 0.5, 0.5 }

Then after applying the exact LUT data row on this output color, you should get:

outputColor = { 0.5 + 0.5 * 0.1201138, -> positive value thus positive color gain R
                0.5 + 0.5 * 0.0000000, -> 0 thus neutral gain in G
                0.5 + 0.5 * -0.0063639 -> negative value thus negative color gain in B }

This means that the red color component will increase, the green component will remain the same, and the blue component will decrease¹. However, it’s important to note that the interpretation of these values can depend on the specific software or shader you’re using to apply the LUT. It’s always a good idea to refer to the documentation of the specific software you’re using for precise information.

Source: Conversation with Bing, 14/12/2023
(1) What negative values in LUT mean and how to use them?. image processing - What negative values in LUT mean and how to use them? - Stack Overflow.
(2) 3d lut cube files fail to apply with error - cube lut lines number is … 3d lut cube files fail to apply with error - cube lut lines number is not correct · Issue #4420 · darktable-org/darktable · GitHub.
(3) HDK: Lookup Tables (LUTs) - SideFX. HDK: Lookup Tables (LUTs).
(4) Free LUTs Color Negative - free presets. Free Color Grading LUT "Color Negative" for Filmmakers & Photographers.

Honestly, don’t trust what Bing Chat says. You cannot determine if this is true or false.

These models are capable of telling very pertinent things or pure bullshit, just the same way.
I like to call them “Digital Politicians”.

Concerning Color LUTS with negative values : I’ve indeed encountered a few, and I think the real case of having negative values is to model “darker than dark” or “lighter than light” values, meaning saturated values with over- or -under luminance.
While this may be useful in a few cases, it’s generally not desirable if you just want to map colors on an input sRGB image.

Also, most of those LUTs appeared to be synthetically generated, and those negative values appeared more as just errors due to bad generation code :).

2 Likes

I tried Bing and ChatGPT and I fully agree, they sometimes are not very… accurate :wink:
I have looked at a few of the LUTs I have collected and quite a few have negative values. I tried the ‘c 0,255’ as suggested and it comes pretty close when compared to ffmpeg. So, again, thank you.

The Adobe Cube format is not multiplicative, the row represents the output for a given input. (The input increases in equal steps)