Integrating the ACES 2.0 Module into My Experimental Branch [Libre DT-Lab] of darktable

Hello everyone,

For several months now, in my fork Libre DT-Lab, I have been experimenting with various tone mappers. It felt natural to add the reference implementation, an ACES 2.0 Output Transform (April 2025) integrated into the darktable pipeline.

Why ACES 2.0

ACES 2.0 unifies what was split into two stages in ACES 1.x (RRT + ODT) into a single Output Transform, and fundamentally changes the approach: instead of a fixed 3D LUT, the rendering goes through a color appearance model based on CAT16/Hellwig CAM, in a perceptual JMh space (lightness J, colorfulness M, hue h). Concretely, this allows:

  • scene-to-display tone mapping (SSTS) that maintains a rolloff characteristic consistent with the target peak luminance
  • hue-dependent and luminance-dependent chroma compression, not a uniform RGB treatment
  • gamut compression based on the actual display cusp geometry (per hue), bringing out-of-gamut colors back without abrupt transitions or hue shifts

Fidelity to the reference

The core of the module (CAT16 matrices, SSTS, chroma compression, gamut compression) has been compared line by line against the official CTL (aces-core, release v2.0.0, April 4, 2025), constants, formulas, and matrices verified, including the CAT16 matrix specific to ACES 2.0 (derived from CAM16_PRI primaries via XYZtoRGB_f33, different from the published “standard” CAT16 matrix) and the AP1 reach primaries.

Three deliberate deviations from bit-exact reproduction, for robustness and production usability:

  • Forward only, no inverse gamut compression. Consistent with standard darktable usage (no proofing/round-trip workflow needed at this stage)
  • Uniform hue table spacing (1°)
  • forward_limit (safety clamp before tone mapping) recalculated for the module’s internal scale rather than copied verbatim from the CTL

Technical features

  • OpenCL accelerated, per-pixel computation, no LUT
  • Strict parity between CPU and GPU paths (same gamut table, same formulas)
  • Adjustable peak luminance (100–4000 nits): standard SDR or HDR headroom
  • Optional output clip (disabled by default): the output is not clamped to [0,1] by default, exactly like the official reference, highlights beyond display white remain available for downstream pipeline stages. An optional soft-clip (cubic roll-off, adjustable width) is available for strict SDR workflows.

The module works and produces colour output consistent with my testing.

As you can see, the module has only a few sliders, this is a deliberate choice to stay as faithful as possible to the reference. All other adjustments can be made with the various modules at our disposal, starting with the excellent RGB colour balance module.

Any comments or criticism are welcome, especially regarding gamut compression and highlight rendering, which are the most sensitive areas of this kind of implementation.

The latest version is available on GitHub.
Greetings from a Belgian; :soccer: :wink:
Christian

8 Likes

So this is a very sophisticated, but very simple tone mapper that would require us to do our other operations in other modules? For instance instead of adjusting the shape of the curve, I would use tone equalizer, CBRGB, shadows & highlights, etc.

I tried it on some photos of performances where people are under lights. I don’t have permission to post those, but I was happy that the highlights on faces seemed less excessive than with agx.

With this sunset photo, I had to do more with tone equalizer to get things nice with ACES. The major difference is the highlight tint was orange with agx and yellow with ACES.
agx:


SIU04839.ARW.lab.xmp (13.0 KB)

With ACES2:


SIU04839_01.ARW.lab.xmp (21.2 KB)
SIU04839.ARW (23.7 MB)
(I license this under CC BY SA, but I don’t think this thread should become a playraw.)

This feels like the unix philosophy of editing: each module does a specific job. Because you can’t force the tone mapper to handle contrast and saturation and how the brightness falls off, etc.

I think for faces, it’s a definite advantage to not have automatic desaturation. But choosing the input brightness to be given highest contrast is definitely harder with tone equalizer than agx, but you have more control with tone equalizer. (And if you need it, you have less control with CBRGB or shadows & highlights.)

Edit: based on the rolloff image below, ACES2 does of course desaturate highlights, but it’s not aggressive like agx.

1 Like

@Christian-B,

I love the work you are doing, the results look awesome. Just wondering are you planning to bring anything of this to darktable itself?

1 Like

Correct me if I’m wrong. Using the notorious Notorious 6 test image, the current implementation somehow rolls-off towards yellow:

4 Likes

ACES (whether 1.x or 2.0) is known for not actually being all that good. AgX and OpenDRT are both much more robust.

1 Like

Hello,
You’re absolutely right in your observation, but this isn’t an artifact or a bug in the implementation: what you’re seeing is a feature of the new Color Appearance Model (CAM) integrated into ACES 2.0!

What you’re seeing on the luminance curve of the “Notorious 6” spheres corresponds to the Bezold-Brücke shift. Under high light intensity, the human eye perceives most hues as shifting either toward blue or toward yellow. In ACES 2.0, the transformation takes place within a perceptual space (JMh) based on Hellwig & Fairchild’s CAM. The “path-to-white” and gamut compression are explicitly designed to model how the human visual system actually perceives pure hues at extreme brightness levels.

Greetings from the Luberon, :herb:
Christian

Hello,
I don’t think that, at this level of quality, we can talk about more robust tone mappers.

To put it simply, this is a showdown between two completely different color management mechanisms:

  1. OpenDRT is based on a strict separation between the tone curve (applied to luminance) and color saturation. By keeping them independent, it avoids hue shifts and offers you very predictable, crisp, and highly adjustable control over the path to white.

  2. AgX uses an empirical, “cinematic-style” approach. It forces interaction between the RGB channels to blend highly saturated highlights into a creamy white. The result looks stunning on flames or skin right out of the box, but it’s a creative formula, not a physiological model.

  3. ACES 2.0 takes a different approach by integrating all these elements into a physiological color appearance model (JMh). Instead of separating luminance and saturation as OpenDRT does, it continuously recalculates their mutual influence based on how the human eye actually perceives color at different brightness levels.

My goal is to provide an implementation of this official ACES 2.0 logic, offering a neutral and mathematically accurate foundation. From there, you can use darktable’s native tools to shape the final rendering exactly as you wish.
Greetings from the Luberon,
Christian

3 Likes