Sat vs Sat & Brilliance vs Sat curve module - #21596
Conversation
|
If I read this correctly there is no guided-filter, right? I remember that during the development of the color equalizer we had quite some issues with artifacts without a proper filter to smooth things. Won't this happen here too? |
|
as long as smooth curves are used there's no need i my opinion. But might be considered for steep changes. |
|
@MStraeten |
New feature: Edge‑aware mask control with guided filterThe module now computes an additional normalized saturation mask, derived from the same perceptually normalized saturation that drives the curves. Optionally, this mask is smoothed with a fast scalar guided filter, so the strength of the correction varies smoothly across saturation edges instead of switching abruptly. Purpose of the guided filter maskInstead of applying the curve effect with the same strength everywhere, the guided filter lets the module fade the correction in and out based on local saturation differences. This reduces typical artifacts such as halos or stair‑stepping at boundaries between saturated and neutral regions, especially when the curves are aggressive - especially when changing brilliance. New controls in the moduleIn the “guided filter” section, the following parameters are available: use guided filterEnables spatial modulation. The correction is blended per pixel with a strength factor derived from the saturation mask, instead of always being fully applied. filter radiusSets the neighborhood radius that the guided filter uses for its statistics. Larger values produce a more strongly smoothed mask with broader influence; smaller values keep the modulation tighter around local structures. edge featheringControls how strongly the filter follows edges. Lower values preserve harder edges and keep the mask transitions more tightly aligned to the underlying saturation structure, while higher values produce softer, less edge‑anchored transitions. iterationsSpecifies how many times the guided filter runs. More iterations increase smoothing and the effective reach of the filter, at the cost of additional computation. mask quantizationApplies an optional quantization step to the saturation mask before smoothing, merging fine variations into broader levels. This reduces noise in the mask channel and makes transitions more robust against tiny saturation fluctuations. Behaviour of the saturation mask previewThe show saturation mask toggle still displays a grayscale visualization of the normalized input saturation that the curves use as their x‑axis. When the guided filter is enabled, the preview instead shows the filtered mask, i.e. the actual strength map that later blends the corrected pixels with the original ones. This allows you to judge directly how smooth the transitions are and which areas of the image the guided filter influences, and by how much. Internal processingInside the module, the guided‑filter path extends the processing pipeline as follows: The module computes a scalar saturation mask from the perceptually normalized saturation. It smooths this mask with an edge‑aware guided filter, either on CPU or GPU (OpenCL), using downsampling and resampling to keep the computation efficient at large resolutions. The filtered mask then drives the blend between the untouched input image and the fully corrected image: a mask value of 0 keeps the pixel unchanged, a value of 1 applies the full curve correction, and intermediate values produce a smooth interpolation. |
|
Ok, I need to test this, almost on top of my TODO list. |
TurboGit
left a comment
There was a problem hiding this comment.
First try and I had lock.
I have opened the module, moved the saturation curve 2 or 3 times and nothing happens after that. Dt is locked somewhere.
Also, the main window do not update when moving the curve. You need to release the mouse button to see the effect. On other curve (tone curve, rgb curve) one just have to stop moving for some ms to see the update.
This module should be added into the scene referred module group presets.

inspired by the davinci resolve stat vs sat curve here a scene referred replacement for the old chroma vs chroma, lightness vs chroma curves in colorzones.

Saturation and brilliance handling is heavily borrowed from colorbalancergb module using design, implementation, review and documentation help by friendly claude, openai, glm agents.
documentation:
Technical Information:
description : remap saturation and brilliance with curves.
purpose : corrective or creative.
input : linear, RGB, scene-referred.
processing : darktable UCS / JzAzBz (perceptual).
output : linear, RGB, scene-referred.
details
The saturation curve module remaps the saturation and brilliance of an image as a function of their own input value, using two independent editable curves. Unlike a flat saturation or vibrance slider, each curve lets you decide exactly how much correction is applied at every point along the saturation range — for example, protecting already-vivid colors while boosting near-neutral ones, or the reverse.
introduction
Global saturation and vibrance controls apply a single coefficient (or a coefficient weighted by chroma) to the whole image. This is often too blunt an instrument: skin tones, skies and foliage usually sit at different points on the saturation range, and pushing all of them by the same amount rarely gives a natural result.
The saturation curve module solves this by plotting a curve over the full range of a perceptually normalized saturation axis (0 to 100% of the local gamut boundary) and letting you add, move or remove control nodes anywhere along it. A second, independent curve does the same for brilliance — the perceptual dimension that behaves like a saturation-aware exposure change. Both curves are read using the current pixel's own saturation as the lookup value, so the correction is entirely local to each pixel's starting saturation, not to a spatial mask.
general principles
Internally, each pixel's RGB value is converted into a perceptual color space and reduced to two numbers: a hue angle and a normalized saturation value between 0 (fully neutral) and 1 (at the color space's gamut boundary for that hue and lightness). This normalized value is looked up on the saturation and brilliance curves, and the resulting curve heights are converted into multiplicative correction factors, applied at constant hue.
Two perceptual formulas are available for this normalization, selectable in the module footer:
JzAzBz (2021) : The original saturation formula. Uses the JzAzBz uniform color space, which does not fully account for the Helmholtz-Kohlrausch effect and can behave less smoothly near black.
darktable UCS (2022) : The darktable Uniform Color Space, designed specifically for saturation manipulation. It accounts for the Helmholtz-Kohlrausch effect and includes a built-in, more accurate gamut-mapping model, giving smoother results across the lightness range. This is the default and recommended formula.
Colors that would fall outside the destination gamut after correction are soft-clipped at constant hue, retargeting them toward the nearest in-gamut saturation instead of hard-clipping. This allows stronger curve adjustments to be used safely without producing posterization or clipped colors near the gamut boundary.
module controls
The module is organized as two tabs, one per curve, plus a shared histogram/picker/formula row below the curve graph.
saturation tab
saturation curve : Plots the output saturation multiplier (y-axis) as a function of the input, normalized saturation (x-axis). The default curve is a flat line at 50%, which corresponds to a neutral, unity multiplier — moving a node above the midline increases saturation at that point on the input range, moving it below decreases it.
The background of the graph shows a log-compressed histogram of the image's own input saturation distribution, letting you see exactly which parts of the range are populated before deciding where to place nodes. A gradient strip below the graph shows the corresponding hue-less perceptual gradient for reference.
brilliance tab
brilliance curve : Works identically to the saturation curve, but its output multiplier is applied to the perceptual brilliance dimension instead of saturation. Brilliance changes act at constant hue and combine a luminance and chroma shift in a direction orthogonal to pure saturation, giving a result closer to a local, saturation-aware exposure adjustment.
The same interaction (add / move / remove nodes, reset, scroll-to-nudge) applies to this curve. Switching tabs does not reset the other curve — both are always active simultaneously and can be combined.
shared controls
color picker : Samples the normalized saturation from the selected point and overlays them on the active curve's graph as a vertical guide. Use this to locate where a specific subject (skin, sky, foliage) sits on the input axis before shaping the curve around it.
show saturation mask : Toggles a grayscale preview of the normalized input saturation used by both curves, displayed in place of the image. Brighter areas correspond to higher input saturation. Use this to understand which regions of the image will be affected by a given section of the curve before committing to an edit. The mask reflects the module's own input, so it is unaffected by any correction the curves themselves apply.
saturation formula : Selects between the JzAzBz and darktable UCS normalization models described in general-principles. This setting affects both curves and is not meant to be changed regularly, which is why it is placed in the shared control row rather than duplicated per tab.
Usage guide
Basic workflow
When first enabled, both curves default to a flat, neutral line and the image is unchanged.
Choosing a formula
Leave the formula on darktable UCS unless you are matching the behavior of an older edit or style that was built using the JzAzBz model. Switching formulas changes how input saturation is normalized and will therefore change the effect of existing curve nodes — re-check the result after switching.
FAQ
why does the curve reference a normalized value instead of raw chroma?
Raw chroma or saturation values are not bounded consistently across hues and lightness levels — the same numeric chroma can be barely visible for one hue and heavily clipped for another. By normalizing input to the local gamut boundary (0–1) for the current hue and lightness, the curve behaves consistently across the whole image, and a node placed at, say, 80% always means "80% of the way to the most saturated color physically displayable at that hue and lightness."
how is this different from color balance RGB's perceptual saturation grading?
Color balance RGB applies a single perceptual saturation and brilliance coefficient, optionally split between shadows, mid-tones and highlights via luminance masks. The saturation curve module instead lets the correction vary continuously as a function of the pixel's own saturation, independent of its luminance. The two modules are complementary and can be used together — luminance-based masking in color balance RGB, saturation-based shaping here.
internal processing
The following is the internal order of operations within the module, for each active pixel:
caveats
Because the normalization depends on the working color profile's gamut, changing the working RGB space mid-session will change how existing curve nodes behave, since 80% of the gamut boundary under one profile is not the same absolute saturation as under another. If you change the working profile, re-check the curves against the histogram overlay.
The saturation mask preview shows the module's input saturation, not the corrected output — it is a diagnostic aid for shaping the curve, not a preview of the final result.