Blender AgX in darktable (proof of concept)

By raising shadows with RGB Curves, you are most likely losing local contrast. You may like the effect for creative purposes, but it’s not usually a recommended method if you want to preserve local contrast.

I’m actually a fan of RGB Curves myself, but I generally only find it useful for increasing contrast (lowering shadows, lifting highlights).

By the way, this is what I would have expected to see in the photo with the car:

I masked the area around the blue light and used the RGB primaries module to adjust the colors.

3 Likes

Something was weird with that 813 build I used , and I don’t know what. At the file level, there is basically no difference between the agx.c from that build and the latest dev version that I have here (some formatting, some debug prints), but the output is very different.
These are with the same preset, with rotation and inset from this config:

And these are @Eary_Chow 's suggested settings from Blender AgX in darktable (proof of concept) - #1000 by Eary_Chow

Both sides with HQ rendering enabled. Left: 813, right: current.

I don’t want to rush with a new build before I make sure it’s OK. I’ll be taking a walk tomorrow, but maybe I’ll provide a build in the evening.

Presets, in case someone wants to test:
blender-ocio.dtpreset (1.1 KB)
Eary_Chow.dtpreset (1.1 KB)

Update: building with debug produces the expected colours; building in ‘release’ mode gives broken ones.

Update2: The matrices are the same, there are only tiny differences, probably due to compiler flags, e.g.

< 0.396424, 0.511936, 0.091639
---
> 0.396424, 0.511936, 0.091640
22c22
< -1.494007, 2.477592, -0.044939
---
> -1.494008, 2.477593, -0.044939
40c40
< 0.396424, 0.511936, 0.091639
---
> 0.396424, 0.511936, 0.091640

Update3: The curve params are also the same.

Off to bed now.

1 Like

Note I added a comment recently in python script, that these settings were generated from Rec.709, but the end matrix got used on top of Rec.2020, which means if you use these settings on rec.709, then this is completely different from what Blender’s behavior is.

My suggested setting was the new parameters I wrote in the Python script. Using those parameters and generating the matrix in rec.2020 will arrive at approximately the same inset matrix as the one used in the Python script.

The matrix generated using the new settings in Rec.2020:

[[ 0.85662715628877917  0.09512124540253490  0.04825159830868580]
 [ 0.13731897228355167  0.76124198700908063  0.10143904070736748]
 [ 0.11189820804517953  0.07679941456251757  0.81130237739230304]]

Vs. the original matrix used in the python script and the config, which was generated from Rec.709 related parameters, but got used on Rec.2020:

[[ 0.8566271533159830  0.0951212405381588  0.0482516061458583]
 [ 0.1373189729298470  0.7612419906025910  0.1014390364675620]
 [ 0.1118982129999500  0.0767994186031903  0.8113023683968590]]

They have small numerical differences, but I would say they are approximately the same.

Not really related to the bug your are testing, I just want to be sure there’s no confusion around these parameters.

1 Like

The baby is growing

2 Likes

… into Frankenstein’s monster :rofl: (sometimes I feel it is so).

5 Likes

I was outside today, walking and taking photos, so only a small update.

I think yesterday night I was so tired that I goofed up; today I cannot reproduce a difference between debug and release build.

I’m checking the code to see what may cause the difference between darktable and Blender.
Suspects:

  • curve
  • matrices
  • negative avoidance (‘guard-rail’-like code).

The curve is OK. I’ve dumped an x->y mapping of the darktable curve and copied it as a table into the original desmos plot (Power(P) Sigmoid | Desmos), setting it to the same params (no toe/shoulder length, contrast = 2.4, toe/shoulder power = 1.5, dynamic range +/- 10 EV (this is what Blender uses, I think, see here, internal gamma 2.2, so pivot x = 0.5 and y = pow(0.18, 1/2.2) = 0.4586), and got a perfect match:

. Actually, whether those are the exact params Blender uses or not is not that important; I wanted to check that given the same params, the Desmos curve calculations match the way my code works.

So I guess we can rule out the curve.

Checking the matrices is tricky, as in blender’s code D65 is used to calculate the matrices, if I understand correctly, while darktable runs in D50.
I’ve set the working space (darktable’s pipeline space, this is what arrives to the module as input) as Rec 2020 (it’s D50-based, so values are not exactly like yours).
The module is set to use Rec 2020 as the ‘base’ space.

The first step is pipeline → base, where I get the identity matrix, as expected.

Using rotate = [3.0, -1, -2.0], inset = [0.4, 0.22, 0.13] (blender/release/datafiles/colormanagement/config.ocio at main · blender/blender · GitHub), Blender’s matrix looks like:

0.856627153315983, 0.0951212405381588, 0.0482516061458583,
0.137318972929847, 0.761241990602591, 0.101439036467562,
0.11189821299995, 0.0767994186031903, 0.811302368396859

My (D50) calculations give ‘similar’ values.

0.886298, 0.074611, 0.039091
0.209132, 0.723162, 0.067707
0.172641, 0.059884, 0.767475

If ‘restore purity’ is 1, the outgoing matrix is the same (rotations are always fully undone, something I inherited from darktable’s sigmoid module, where primaries were added by @flannelhead).
This is where I am now. I’ll try to check more during the weekend. If anyone can check the matrix calculation (I think it is fine, since I just reused sigmoid’s code), please do.

sigmoid, using those values I mentioned above (rotate = [3.0, -1, -2.0], inset = [0.4, 0.22, 0.13]), with preserve hue = 0, recover purity = 0%, base: Rec 2020 gives the following results:

agx:

With recover purity = 100% in both:
sigmoid:

agx:

(Note that agx is less contrasty and is set to cover a huge dynamic range in the screenshots above.)

What we do not (yet) have in darktable is the display-mapping transformation; if I understand that correctly, that would be in AgX_LUT_Gen/luminance_compenstation_srgb.py at main · EaryChow/AgX_LUT_Gen · GitHub. @Eary_Chow , could that be the missing step, or are you reasonably sure the matrices are wrong? @flannelhead , what do you think?

4 Likes

Again, the parameters were relative to Rec.709, but the generated matrix was then used on Rec.2020.

That was a mistake I made back then that I didn’t realize until recently. My stupid self back then was using Troy’s generate_config.py, which has set the colorspace_in to Rec.709. I tweaked the settings a bit, ran the script to generate the OCIO config, I then copied the matrix from the generated config to Blender’s config to directly apply it to Rec.2020. I was not really aware of how the matrix was generated, until I actually looked into it recently! So this is a confusion caused by my mistake back then!

If you use these parameters on Rec.709 and generate the matrix to use on Rec.709, it won’t match Blender; If you use these parameters on Rec.2020 and generate the matrix to use on Rec.2020, it won’t match. My original doing was using these parameters on Rec.709, and then copy the generated matrix to use in Rec.2020.

Again:

Basically means, the actual parameters starting from Rec.2020 to arrive at Blender’s result should be:
Rotations: [ 2.13976149 -1.22827335 -3.05174246], Inset: [ 0.32965205 0.28051336 0.12475368]

I committed the comment to the python script as soon as I realized the issue, and I made sure to inform you about the comment in the GitHub issue. I guess I was still not clear enough about what I meant.

Some other parts that are different from Blender’s AgX, is that Blender’s AgX uses a different matrix for outset. The Outset parameters in Rec.2020, again from my latest comment in python script:
Rotations: [ 0 0 0], Inset: [ 0.32317438 0.28325605 0.0374326 ]

I think it’s fine to leave this out if it’s hard to implement in Darktable, a simple hard clamp would be good for now.

Thank you for your patience. I’m still digesting the information you’ve sent. I’ll put more work into it before I ask, I really don’t want to get on your nerves.
Using your in- and outgoing inset/rotation params helped, indeed:

darktable folks:

  • I’ve added 8 new sliders (yay!). 6 of them correspond to the attenuation and rotation sliders; additional 2 allow you overall control.
    – example smooth preset: you can see below that the per-channel ‘reversal’ sliders are set to the same values as the original attenuation and rotation sliders. With these settings, master attenuation reversal is basically the same as the ‘purity’ slider in sigmoid. Setting it to 0 does not reverse the attenuation at all (does not ‘recover purity’), setting it to 100% means complete ‘purity recovery’. You can even boost it (I can remove that possibility if useless, let’s test). master rotation reversal is set to 100%, too, which means rotations are fully undone (like in the sigmoid module, where this cannot be controlled).

    – example blender-like preset: the per-channel ‘reversal’ sliders are not set to the same values as the original attenuation and rotation sliders; rather, I used the values kindly provided by @Eary_Chow . With these settings, master attenuation reversal still acts as an overall control, but since red attenuation is 33%, restoration is 32.3%, it won’t ‘recover purity’ fully, to match Blender more closely.

    – I think the red/green/blue reversal sliders could get their own collapsible section, the master attenuation reversal (a.k.a ‘recover purity’ and master rotation reversal sliders would probably be sufficient in most cases.
  • I’ve hidden the gamut compression sliders and deactivated the algorithm for now. I think out-of-gamut values can be handled via attenuation and partial reversal. Let’s see what you think.

The car (not with the preset, but that was the starting point):

The children (blender-like|punchy preset, with look/saturation boost removed, and master attenuation reversal lowered to about 25%):

The code base has been synced with master, up to and including commit 8d1ccdb0. The AppImage:
https://tech.kovacs-telekes.org/dt-agx/Darktable-5.1.0%2B825~g1822aeb79f-x86_64.AppImage

@Dave22152, @MStraeten, @priort : Please update your respective builds.

Have a nice start to the weekend. :slight_smile:

6 Likes

Note: preserve hue is applied before the outgoing transformation (it only affects the shifts directly caused by the per-channel application of the curve), so if you don’t restore rotations in the primaries block, the hue won’t be preserved.

This does seem to be an improvement. The light transition on the children’s faces is less harsh and better controlled. I’m working on the Win 11 build right now.

1 Like

Win 11 executable for build 825 is here:

https://www.dropbox.com/scl/fi/zjfh6qhulvt1uwkuvlmwt/darktable-5.1.0-825-g1822aeb79f-win64.exe?rlkey=npbyo5sr56ka8e71sji0zyird&st=d65a6a03&dl=0

2 Likes

With all these new sliders, I think we definitely need to somehow get the tabs working.

1 Like

maybe there is some other type of UI which could replace some sliders, some kind of colour wheel interface or similar, I have no idea how practical this is for the application, or maybe a similar interface to colour equaliser with the multiple lines that can be dragged up and down…

Hello,

The results of my first tests are very impressive, congratulations.
I feel I have total control over color management.

For me, it’s fine this way, but I think that presets (portrait , nature, etc.) would be useful to save time.

It’s an excellent thing - the first results are excellent and we’re saving space, in the end, we have fewer cursors. :wink:

I’ll try to continue testing this weekend and come back with some examples.
Congratulations again on your development,
Christian

I hope the community can come up with some:)

In the end, maybe, but I added 2 + 6 and disabled 6, so the net change is +2. :wink:

The ones to congratulate are the people who created the algorithms; my copy-pasting is not much of an achievement. But I’m having fun, and hopefully also learn something in the end, so I’m happy - even more so if others also get something out of it.

11 Likes

@kofa Don’t deprecate yourself. Putting together the jigsaw puzzle is just as an expression of competence as creating the individual elements. May be more. Its a great work you have here. Thanks on behalf of the DT community

11 Likes

I have no feel for these extra sliders but I was just messing around with a playraw I did tonight and tweaked a couple of them after my initial edit and I was blown away but the change I got… All these sliders are going to be very powerful…at least in the hands of some :slight_smile: not me for sure but still very interesting…

1 Like

Looking this image that we have used here before…the attenuation reversal sliders really allow you to refine the dress…

Here simply opening the image…autopicker for exposure and then agx basically with those autopickers would leave the dress looking towards magenta…

file000551_01.cr2.xmp (6.5 KB)

I don’t know what the exact shade/hue that dress is meant to be but a simple -5.0 degree attenuation rotation on the red …gives this…

One further tweak and reducing the red attenuation you can bump it more…

Its really powerful for an image like this going more extreme you can drive it orange

or the other way…

These new sliders look like they will be really useful to make tweaks to strong colors…

5 Likes

I’m not deprecating myself. It is a lot of work, more than I imagined (I’ve put in over 20 hours every week since I posted this for the first time, so it’s like a part-time job). That is largely because of my unfamiliarity with both darktable’s UI, and with image processing in general. I’m just saying the real brains behind this (who came up with the ideas and algorithms) is not me.

14 Likes