Sister tool to Selective Color Correction. Instead of building a mask from a hue range, you build it from a luminance band — shadows, dark mids, midtones, bright mids, highlights, or any custom range — and push CMY / RGB / Luminance / Chroma / Saturation / Contrast adjustments through it. The contrast slider is the one Selective Color doesn't have: an anchored sigmoid S-curve pinned to the band endpoints, so contrast changes only reshape the midrange of your selected band without ever pushing a pixel outside it.
Why this exists. Classic selective-colour tools let you edit by hue; curves and levels let you edit globally by luminance. What very few tools let you do is combine both — “only shift the bright blues toward cyan”, “only lift the shadows saturation”, “only add contrast to the midtone dust lanes without clipping the galaxy core”. This dialog does exactly that, with the same freehand lasso, imported-mask, and live-preview niceties as Selective Color.
Same two-pane split as Selective Color: a scrollable left column
with the Luminance Mask group, three adjustment groups
(CMY / RGB / LSC), live-preview toggle and action
buttons; a right column with zoom toolbar, help banner, large
preview, picked-luminance readout, and region-mask readout. Window
size, splitter position, small-preview toggle, live-preview toggle,
mask-overlay toggle, and current zoom are all persisted to QSettings
under the sellum/ namespace.
The Preset dropdown offers five standard luminance bands plus Custom:
| Preset | Range (0–1) | 8-bit range |
|---|---|---|
| Shadows | 0.00 – 0.25 | 0–64 |
| Dark Mids | 0.20 – 0.45 | 51–115 |
| Midtones | 0.35 – 0.65 | 89–166 |
| Bright Mids | 0.55 – 0.80 | 140–204 |
| Highlights | 0.75 – 1.00 | 191–255 |
Any manual change to the Luminance Wheel or either slider switches the preset to Custom automatically.
A circular grayscale ring with two draggable white handles for the lo and hi ends of the band. The convention is unusual but intuitive once you see it: black sits at the bottom (270°), white sits at the top (90°), and the ring transitions smoothly between the two around the circle. The yellow arc between the handles shows your current luminance range. A small orange marker appears when you click to sample a pixel (see § 3).
0–255 integer sliders paired with 0.0–1.0 double spinboxes. Edit either side of each pair and the other updates in step. Both also mirror the Luminance Wheel.
Range 0.0–0.5, default 0.05. Feather width (in luminance units) applied at both ends of the band. At 0.0 the mask is a hard rectangle; at 0.05 you get soft edges roughly one 8-bit code value wide; at 0.5 the band bleeds halfway across the luminance axis in each direction. See § 12.1 for the exact formula.
Replaces the mask with its complement before any downstream step. Useful for “adjust everything except the midtones” operations.
Switches the band from a hard rectangle with linear feathers to a smooth-shoulder bump: the mask peaks at 1.0 inside the band and tapers off through smoothstep curves outside. For a narrow band this becomes a bell curve; for a wide band it keeps a soft-shouldered plateau. The result removes iso-luminance contours (“blobbiness”) that pure threshold masks produce. See § 12.2.
Special case: a band that reaches a pure endpoint
(lo = 0.0 or hi = 1.0)
keeps that endpoint fully in at 1.0 regardless of smoothness, so a
Shadows preset (0.00–0.25) still selects true black at full
strength instead of tapering off into it.
Range 0.0–2.0, default 1.0. Scalar multiplier on the mask before it's handed to the adjustment pipeline. Same semantics as in Selective Color — think of it as the strength of the adjustment.
Gaussian blur applied to the mask after all other mask steps, 0–150 px radius. Useful for softening sharp luminance boundaries into gradient transitions. Default 5 px (not 0 like Selective Color): luminance masks tend to have harder edges than hue masks, and a few pixels of blur almost always improves the result.
Red semi-transparent overlay in the preview showing exactly which pixels the current mask is selecting. Alpha scales with the mask value, so a soft-mode bell curve looks visibly feathered. Pair with the overlay to find the right luminance range for the structure you're trying to isolate.
Two controls:
Hold Alt and drag in the preview to draw a freehand closed region. On release, the polygon is filled, converted to a binary mask at full resolution, and AND'd with the luminance mask. This is particularly powerful here: you can select, say, “the midtone dust lanes only on the north side of the galaxy” without having to craft a precise mask externally. The dashed-orange outline marks the active region in the preview until you click the × Clear Region Mask button.
The toggle at the top-left controls whether the preview path operates on a downsampled copy (longest side ≤ 1200 px) or full resolution. On by default; always off for the Apply / Apply-as-New / Export Mask paths.
Toggle between the raw image and the adjustment-applied image. Useful for before/after without undoing anything. The mask overlay works independently.
Same as Selective Color — pick Chroma (L-preserving) or Saturation (HSV S) for the colourfulness slider in the LSC group. See the Selective Color docs for the trade-off; the math is identical here.
Identical to Selective Color in both range and semantics:
When the Contrast slider drops below −0.667
(precisely −2/3), an orange warning appears
underneath the LSC group:
The slider itself turns red (groove, fill, handle) while the condition holds. This isn't a bug — the sigmoid compression math naturally stops being monotonic past this point, so very dark pixels can end up slightly brighter than slightly-less-dark pixels, producing wavy artefacts. The slider still works; the warning just says “you're off the beaten path, verify the preview looks right”. See § 12.5.
Only the adjustment sliders are reset to zero. The luminance range, preset, smoothness, invert, blur, and intensity are kept exactly as you had them. This is a deliberate difference from Selective Color — Selective Color resets everything after Apply because hue selections tend to be one-and-done, but luminance bands are often reused for multiple passes (adjust shadows for colour, then for contrast, then for luminance tweak, same band each time).
Full reset: luminance range back to the Shadows preset (0.00–0.25), smoothness 0.05, invert off, soft off, blur 0, mask overlay off, all adjustment sliders at 0.0, intensity 1.0, Chroma mode selected. Loaded image, imported mask, and lasso region are preserved.
Removes any lasso region constraint. Enabled only when a region is active.
self.img, writes back to the source document,
promotes the result to the new working base, resets only the
adjustment sliders (see § 6.1).<name> [SelLum] and switches the dialog's
target to it.<name> [SelLum MASK]. Useful for feeding it
into another SASpro tool or for round-tripping into Selective
Color via its Pick-mask-from-view path.A few things this tool can do that are awkward or impossible in general-purpose editors:
Chain this with Selective Color for “only touch the bright blues”: export a luminance mask from here, then import it into Selective Color via Pick-mask-from-view and pick the Blue preset. The two masks AND together.
Everything in float32 in [0, 1]. Luminance is Rec. BT.601. Core pipeline runs on the GUI thread with debounced timers (200 ms for mask and adjustment paths), identical to Selective Color. The band-locked contrast formula and the luminance wheel are the two pieces that set this tool apart.
Rec. BT.601 weights, same as Selective Color:
Let L be the pixel luminance, L_lo and
L_hi the band endpoints, and s the
smoothness. In soft mode, the mask is a bump function with
smoothstep shoulders:
where t is the smoothstep from Selective Color
§ 13.2. The clamp around the pure endpoints (0 and 1) is
applied by replacing the relevant factor with 1 when the band
reaches that endpoint, so Shadows and Highlights presets include
true black and true white without a false taper.
In hard mode, the band is 1 inside and linearly feathered at the ends:
The Invert checkbox replaces the result with 1 − m.
The wheel maps angle (in degrees, measured counterclockwise from the positive x-axis) to luminance in [0, 1]:
Which places L = 0 (black) at the 270°
position (bottom), L = 1 (white) at 90°
(top), and midtones at the sides. The arc from lo to
hi always moves in the direction of decreasing
angle (i.e. toward higher luminance).
This is the maths that distinguishes Selective Luminance from
Selective Color. Let κ be the Contrast slider
value, x a pixel's channel value, and
[L_lo, L_hi] the current luminance band. The steepness
is:
The sigmoid is normalised against its own endpoints so the
transformation maps L_lo to L_lo and
L_hi to L_hi exactly:
Then the pixel is remapped by first centring on the band:
Running through the normalised sigmoid:
and then expand or compress depending on the slider sign:
and finally remap back out:
The output pixel is a mask-weighted blend of original and remapped:
Because the sigmoid is anchored at both endpoints, pixels
outside the band (where t is outside
[0, 1]) still get remapped by the formula —
but their mask value m is near zero, so the blend keeps
them untouched. The feathering at the band edges comes entirely from
the mask, which means contrast transitions at the band boundary are
as smooth as the mask's smoothness setting allows.
In compression mode (κ < 0) the remap is
anchored(t) = 2t − e(t).
Differentiating with respect to t:
The curve stops being monotonically increasing where
de/dt > 2, which happens for sufficiently
large k. The threshold works out to:
Past this point (slider positions below −0.667), the compressed curve wiggles rather than monotonically decreasing in slope, so dark pixels can end up slightly brighter than slightly-less-dark pixels at the band ends — the wavy artefact the on-screen warning calls out. The slider still functions; the warning just makes the regime explicit.
CMY, RGB primaries, Luminance additive, Chroma (L-preserving), Saturation (HSV S), and Intensity scaling are all identical to Selective Color — see that tool's § 13.6 for the formulas. Region-mask AND composition (§ 13.8) and the dual debounced-timer architecture (§ 13.9) also carry over unchanged.
Two 200 ms single-shot QTimers:
Slider release and spin-box editing-finished force an immediate repaint so you don't wait after letting go.
Required for Gaussian edge blur, lasso polygon fill, mask resize,
region outline rendering, and HSV conversion inside the saturation
path. The _build_lum_mask core itself is pure NumPy and
runs without cv2; the UI will still work for the band mask path, but
blur and lasso fall back to no-op / Matplotlib path respectively.
κ < −2/3, the reset-adjustments-only
behaviour after Apply (as opposed to Selective Color's full reset),
and the UI around all of it. HSV conversion, Gaussian blur, lasso
polygon fill, and image resize use OpenCV. The
luminance-preserving chroma formula and the CMY/RGB adjustment math
are the same classical approaches used in Selective Color
(§ 13.6 there).