Skip to content

Initial implementation of Color Harmonizer module.#20506

Open
masterpiga wants to merge 4 commits intodarktable-org:masterfrom
masterpiga:color_harmonizer
Open

Initial implementation of Color Harmonizer module.#20506
masterpiga wants to merge 4 commits intodarktable-org:masterfrom
masterpiga:color_harmonizer

Conversation

@masterpiga
Copy link
Contributor

@masterpiga masterpiga commented Mar 13, 2026

Overview

  • Adds a new IOP that applies color harmony corrections in UCS color space, rotating hues toward a target harmony structure (complementary, split-complementary, triadic, tetradic, etc.) while protecting neutrals
  • GPU-accelerated via OpenCL kernel (colorharmonizer.cl)
  • Harmony rules defined by a configurable anchor hue + pull strength, pull width, and smoothing parameters
  • Supports a fully custom harmony mode with up to N free-position nodes
  • Per-node saturation boosts after hue correction
  • RYB color wheel support for perceptually natural hue angles
  • Module documentation:

Vectorscope integration

  • The colorharmonizer module registers a callback (_on_vectorscope_harmony_changed) so dragging the guide in the vectorscope syncs back to the module's params and history
  • Histogram proxy extended with harmony get/set/callback API (histogram.c, lib.h)

Pixelpipe position

  • Placed before colorbalance, colorequal, and colorbalancergb in all pipeline presets (legacy and scene-referred) — position ~32.5/40.5 — so those modules can fine-tune the harmony output (iop_order.c)

Shared utilities extracted

Main technical choices

See business logic implementation details for a more detailed analysis.

Perceptual color space (UCS JCH)

All hue manipulation happens in darktable's Uniform Color Space rather than HSL/HSV. Equal angular steps in UCS correspond to equal perceived hue differences, which makes the Gaussian weighting behave predictably. The cost is a heavier per-pixel pipeline (matrix multiply + chromatic adaptation + nonlinear transforms in both directions), accepted as necessary for perceptual accuracy.

RYB geometry, UCS processing

Predefined harmony rules are defined on the RYB (painter's) wheel because it matches users' intuition and the vectorscope overlay. Processing happens in UCS. The anchor hue does a round-trip UCS→RYB (to apply the geometry table offsets) then RYB→UCS (to recover processing node positions). This ensures the attraction zones are pixel-accurate with the guide lines shown in the vectorscope.

Precomputed LUTs for hue remapping

The UCS→RYB conversion involves sRGB gamma, HSV extraction, and a Gossett piecewise-linear mapping — too expensive per pixel. Two 720-entry tables are built once at init_global: the forward table fills directly from the conversion function; the inverse is found by nearest-match scan over the forward table (O(N²), N=720, runs once at startup, negligible cost). All subsequent calls are O(1) with linear interpolation.

Winner-takes-all node selection

Each pixel is attracted to its nearest harmony node only, not a weighted blend of all nodes. A blend would pull every pixel toward the geometric center of the node set — losing the palette identity and potentially averaging complementary hues into gray. Winner-takes-all preserves each zone's distinct character.

Neutral protection via hyperbolic ramp

The correction is gated by chroma / (chroma + cutoff + ε) — a smooth saturating curve that approaches zero for near-neutral colors and one for vivid ones. The cutoff = np_t³ × 0.03 cubic mapping distributes the slider's perceptual effect evenly; a linear mapping would concentrate most visible change in the bottom 20% of slider travel.

Two processing paths (CPU)

When smoothing is off, a single fused loop runs forward conversion, correction, and inverse conversion per pixel with no intermediate buffers. When smoothing is on, the forward conversion and per-pixel corrections are cached in pass 1; the correction field (not the image) is Gaussian-blurred; pass 2 applies blurred corrections from cache, skipping the expensive forward conversion. Blurring the correction field rather than hue values directly avoids circular-average artifacts near the 0/1 hue wrap point.

Always two-kernel on GPU, always two-pass on CPU when smoothing > 0

The OpenCL path unconditionally uses a map kernel + apply kernel with shared GPU buffers, even at smoothing=0. Adding a third fused kernel for that case would double the shader variants for a marginal benefit — device-local buffer reads are cheap on modern GPUs. The CPU does fuse at smoothing=0 since avoiding the intermediate allocation is free there.

Context

Earlier versions of this module have been shared with the Pixls community, where it has received some preliminary testing and a rather positive welcome.

@masterpiga masterpiga force-pushed the color_harmonizer branch 2 times, most recently from 85d40e0 to 13ddd6d Compare March 13, 2026 14:12
@wpferguson
Copy link
Member

@masterpiga you may have set a dangerous precedent here with the inclusion of all the documentation. While the documentation guys now love you, the devs are having an "oh s!*t" moment 🤣 .

@masterpiga
Copy link
Contributor Author

masterpiga commented Mar 13, 2026

@wpferguson 🤣 Well, the documentation is mostly for myself, since I am swimming in unfamiliar waters. But then I thought that it make the job easier for reviewers, so I included it in the PR.

Anyways, Claude wrote the docs, I just told it what needed documenting, so I can't take credit for that :)

@TurboGit
Copy link
Member

Sorry, after some vectorscope changes it needs a rebase for conflicts resolution.

@masterpiga masterpiga force-pushed the color_harmonizer branch 3 times, most recently from f9e2b08 to f9d37fa Compare March 13, 2026 17:39
@masterpiga
Copy link
Contributor Author

masterpiga commented Mar 13, 2026

Thanks @TurboGit, merged!

@TurboGit
Copy link
Member

@masterpiga : Also please rebase onto master to fix the macOS issue reported by the CI.

@TurboGit TurboGit added this to the 5.6 milestone Mar 13, 2026
@TurboGit TurboGit added priority: medium core features are degraded in a way that is still mostly usable, software stutters feature: new new features to add difficulty: hard big changes across different parts of the code base scope: image processing correcting pixels scope: color management ensuring consistency of colour adaptation through display/output profiles documentation-pending a documentation work is required release notes: pending labels Mar 13, 2026
@TurboGit
Copy link
Member

Very impressive module. I have tested a bit this latest version tonight and I must say I'm bluffed. I'll continue on this tomorrow, time to sleep now.

@masterpiga masterpiga force-pushed the color_harmonizer branch 2 times, most recently from 1ee97c4 to 407ee93 Compare March 14, 2026 05:54
Copy link
Member

@TurboGit TurboGit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for all new modules, we also need to add it into the modulegroup.c (at least in group all and scene-referred).

It also needs an entry into po/POTFILES.in.

Copy link
Member

@TurboGit TurboGit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems also that an harmony is always displayed even for new images.

  • Open darkroom
  • Select vectorscope
  • An harmony is displayed
  • Move to next image
  • An harmony is displayed
  • ...

@masterpiga
Copy link
Contributor Author

It seems also that an harmony is always displayed even for new images.

  • Open darkroom
  • Select vectorscope
  • An harmony is displayed
  • Move to next image
  • An harmony is displayed
  • ...

Good catch, thanks, I had missed that. Now images restore whatever state they had previously.

@masterpiga
Copy link
Contributor Author

@TurboGit thanks! comments addressed and rebased

@TurboGit
Copy link
Member

@masterpiga : Can you rebase on current master, it should fix the macOS CI issue. TIA.

@masterpiga
Copy link
Contributor Author

Thanks @TurboGit, done

Copy link
Member

@TurboGit TurboGit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments after a review.

I must say that I was expecting the code a bit more complex. I don't say that I understand everything there, but clearly the code is clean.

Copy link
Member

@TurboGit TurboGit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments.

@masterpiga
Copy link
Contributor Author

Thanks @TurboGit, all done! I also added a check to enable the "auto-detect" button only after the histogram is ready.

@masterpiga masterpiga requested a review from TurboGit March 15, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

difficulty: hard big changes across different parts of the code base documentation-pending a documentation work is required feature: new new features to add priority: medium core features are degraded in a way that is still mostly usable, software stutters release notes: pending scope: color management ensuring consistency of colour adaptation through display/output profiles scope: image processing correcting pixels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants