Recombine Luminance

Replace the luminance channel of an RGB image with luminance taken from another open image — while preserving the RGB image's hue and chroma. The classic astrophotography application is L+RGB processing: build a deep, well-processed mono master (stacked L, extracted L, denoised L, deconvolved L…) and swap it into a colour image whose own luminance is noisier or less resolved without disturbing the colour balance. Also useful for borrowing narrowband detail into an RGB continuum shot, or for pulling in a synthetic L (e.g. from a star-removed deconv) when iterating on colour and detail separately.

1. Opening the dialog

The active image window is the target — its colour is kept and its luminance is replaced. It must be a 3-channel RGB image; mono or RGBA targets are refused with a message. The dialog then lists every other open image that could serve as the luminance source (mono, or any image whose first channel can be used as L); if nothing qualifies, you're told to open one.

This dialog is a modal picker — there's no live preview and no Push. On OK, the chosen settings run immediately against the target document as a single undoable edit named “Recombine Luminance”. Cancel leaves everything alone.

2. Luminance source

Dropdown of candidate source windows. The dialog auto-selects the first mono image if one is open; otherwise it defaults to the first eligible RGB. For RGB sources, SASpro computes a luminance plane on the fly using the weighting profile you pick below — so pointing the source at another colour image is a legitimate workflow, not a mistake.

3. Luminance weighting profile

The profile controls how R, G, B are weighted when SASpro either computes Y from an RGB source, or reconstructs Y from the target in order to compute the per-pixel scale that replaces it. Both operations need to agree on what “luminance” means, so this one setting drives both ends.

Standard profiles

EntryWeights (R, G, B)Use for
Rec.709 (default)(0.2126, 0.7152, 0.0722) Broadband RGB, modern monitors, generic astrophoto work. Safe fallback.
Rec.601(0.2990, 0.5870, 0.1140) Legacy sRGB video, scans of older material.
Rec.2020(0.2627, 0.6780, 0.0593) Wide-gamut HDR targets.
Equal RGB(⅓, ⅓, ⅓) When the three channels really do carry equal information content (unusual).
Max (Narrowband mappings)per-pixel channel max HOO, SHO and other narrowband palettes where each channel is a distinct emission line and “brightness” is whichever line is strongest at that pixel.
Median RGBper-pixel channel median Robust-against-outlier variant; blocks single-channel artefacts (cosmic hits, star-core clipping in one filter) from dominating Y.
Unequal noise (SNR)∝ 1/σc² Weighted by inverse channel variance, so the quietest channel contributes most. See § 8.4.

Sensor profiles

The dropdown groups a library of per-sensor luminance weights below the standard entries, with disabled header rows acting as section dividers. The sensors are organised:

Each entry carries a tooltip (hover to read) describing what the sensor is best for — e.g. “Gold standard APS-C. Excellent balance for broadband” for the IMX571, or “NIR optimised. Excellent for H-Alpha/Narrowband” for the IMX585. Canon/Nikon aren't auto-detected (modern vs legacy depends on model, not make).

Auto-selection

When the dialog opens, and whenever you change the source dropdown, SASpro picks a profile automatically along this cascade:

Once you manually pick a profile from the dropdown (as opposed to accepting the auto-selection), SASpro stops re-selecting on subsequent source-combo changes — your choice sticks for the rest of the dialog's lifetime. Clicking OK persists that choice as the new global default.

4. Blend strength

Slider + spin box, 0.00–1.00, default 1.00. Linear mix between the untouched target and the recombined result:

\mathrm{out} \;=\; (1-b)\,\mathrm{rgb}_{\mathrm{target}} \;+\; b\,\mathrm{recombined}

At 1.00 the new L replaces the old entirely; at 0.70 you keep 30% of the original luminance structure, useful if the new L is sharper but noisier and you want a softer substitution. Dropping below ~0.5 is rarely what you want; use the Advanced sliders instead to protect specific tonal regions.

5. Advanced controls

Collapsed by default; click the ▶ Advanced button to expand. All four sliders operate before the final result is written, in the order Saturation → Chrominance NR → Recombine.

Saturation boost

−1.00 to +3.00, default 0.00. HSV-space saturation multiplier, applied to the target RGB before luminance is swapped. Because the boost happens pre-recombine, it changes colour richness but cannot distort the new luminance SASpro is about to place (see § 8.5 for why).

Chrominance noise reduction (σ, px)

0.0 to 20.0 px, step 0.1, default 0.0. Converts the target to YCbCr, Gaussian-blurs only the Cb and Cr channels, converts back. Luminance structure is untouched. The role is exactly PixInsight's chrominance NR slider: suppress colour mottling in low-SNR regions without softening fine detail. Typical range is 0.5–3.0 px; heavier values are good for very noisy OSC data at the price of a subtle colour-wash look.

Noise-floor pedestal

0.000 to 0.500, step 0.005, default 0.050. Protects deep shadows from hue skew during the per-pixel scale that recombine performs. Without it, pixels whose RGB luminance is near zero are divided by a near-zero denominator and the scale factor can swing wildly — amplifying whatever faint colour cast those pixels happened to have. The pedestal lifts both target RGB and new L by the same amount before the scale, then subtracts it off at the end; the operation is exactly identity-preserving when the new L equals the target's own L (see § 8.6).

Highlight soft-knee

0.00 to 1.00, default 0.00. Compresses large upward scale factors so that highlights don't blow out when the new L is substantially brighter than the target's own L. See § 8.6 for the compression curve — above k ≈ 0.3 the knee becomes visible; most work is fine at 0.00.

6. OK / Cancel

OK persists the chosen profile as the new default and runs apply_recombine_to_doc against the target. The resulting edit shows up in Undo History as “Recombine Luminance”; the log records source title, target title, profile, and all four Advanced values. Cancel discards every slider state — nothing is persisted except through OK.

7. Companion: Extract Luminance (button, no dialog)

There's no dialog for the extract side — a toolbar button on the main window pulls luminance out of the active RGB image into a new mono document, using the global self.luma_method as its profile. The resulting document carries luma_method and (for sensor profiles) luma_profile in its metadata, which is what lets this dialog's auto-selection cascade recover the right profile when you later use that mono as a recombine source. In other words: extract with the sensor-matched profile, do your work on the L document, feed it back here, and the dialog finds the right weights without you having to think about it.

8. Technical details

All processing on float32 [0, 1]. The target RGB is passed through two optional colour-only passes, then recombined with the new L. No per-image renormalisation.

8.1 Pipeline order

Inside apply_recombine_to_doc:

rgb_target ─▶ saturation boost ─▶ chrominance NR ─▶ recombine(., new_L) ─▶ out

Both pre-passes act on colour-space representations that leave luminance invariant, so they can't change the Y value SASpro is about to overwrite. New L is independently computed from the source document (passed through if mono, computed by compute_luminance if RGB).

8.2 Saturation boost (HSV path)

The target RGB is converted to HSV via OpenCV (cv2.cvtColor(., COLOR_RGB2BGR) then COLOR_BGR2HSV), the S channel is scaled, and the image is converted back the same way:

S' \;=\; \mathrm{clip}\!\left(S\,(1 + a),\;0,\;1\right)

Hue and Value are untouched, so the operation is luminance-preserving in the HSV sense. OpenCV uses linear floats for the HSV conversion when passed a float32 image, so no 8-bit round-trip is involved.

8.3 Chrominance NR (YCbCr Gaussian path)

Transform the target to BT.601 YCbCr with Cb and Cr biased to [0, 1] by adding 0.5:

Y = 0.299R + 0.587G + 0.114B
C_b = -0.16875\,R - 0.33126\,G + 0.5\,B + 0.5
C_r = 0.5\,R - 0.41869\,G - 0.08131\,B + 0.5

Gaussian-blur Cb and Cr only (kernel size ⌈6σ⌉, forced odd), then re-centre them and invert the matrix:

R = Y + 1.402\,C_r^{s}
G = Y - 0.34414\,C_b^{s} - 0.71414\,C_r^{s}
B = Y + 1.772\,C_b^{s}

with Cbs = Cb − 0.5 and likewise for Cr. Y is bit-for-bit preserved through the round-trip; only the colour components are smoothed. Result clipped to [0, 1].

8.4 Luminance computation (compute_luminance)

When the source is RGB, Y is computed per the selected profile:

Mono source documents skip all of this — their single plane is used directly as L.

8.5 Why pre-passes preserve the new luminance

Saturation boost is HSV-space scaling of S only, leaving H and V unchanged — so by construction the HSV Value doesn't move. The Y used by recombine is a weighted RGB sum, which isn't HSV Value, but in HSV scaling of S only, the only operation that mixes RGB is the HSV → RGB inverse; across that inverse the colour variation stays within a constant-Value hyperplane, so Y excursions are small and — crucially — they're part of the target's Y, not the new L. The chrominance NR path leaves Y bit-exact. So when SASpro computes Y = w·RGBpre to build the scale factor, it's reading the target's post-pre-pass luminance, and the replacement L is unaffected by either pre-pass.

8.6 Linear-scale recombine with pedestal lift

The core recombine, with pedestal p, replacement luminance L, and weight vector w:

\mathbf{rgb}_p \;=\; \frac{\mathbf{rgb} + p}{1 + p}\,,\qquad L_p \;=\; \frac{L + p}{1 + p}

Both target and new L are lifted by the same affine map so black sits at p/(1+p) instead of 0. Compute Y and the per-pixel scale:

Y_p \;=\; w_R R_p + w_G G_p + w_B B_p\,,\qquad s \;=\; \frac{L_p}{Y_p + \varepsilon}

with ε = 10−6. Apply the scale to every channel of the lifted target and invert the pedestal:

\mathbf{out}_p \;=\; s\,\mathbf{rgb}_p\,,\qquad \mathbf{out} \;=\; \mathbf{out}_p\,(1+p) \;-\; p

Clipped to [0, 1] and finally blended against the untouched target by the global blend slider.

Round-trip identity

If the new L equals the target's own luminance (L = Y, i.e. you fed back the extracted L of the target itself), then Lp = Yp, so s = 1, and:

L = Y \;\Rightarrow\; s = 1 \;\Rightarrow\; \mathbf{out} = \mathbf{rgb}

bit-equal to the input (modulo the ε). The pedestal lift doesn't break this because the same lift and inverse are applied symmetrically to RGB and L.

Why linear scaling preserves hue

The per-pixel scale s is one scalar applied uniformly to R, G and B. In linear light, chromaticity depends only on the ratios of R:G:B, and multiplying all three by the same factor leaves those ratios unchanged. So the recombined pixel has exactly the same hue as the original — only its brightness is modulated to match the new L. This is why doing recombine in linear space, rather than in Lab or HSL, is the right choice for photometric data.

Highlight soft-knee

When enabled (k > 0), the raw scale s is passed through the compression curve:

s' \;=\; \frac{s}{1 + k\,(s - 1)}

which fixes s'(1) = 1, so scales at or below 1 (the common case of lowering brightness) are untouched, and asymptotes to 1/k for very large s. Setting k = 0 recovers plain linear scaling.

8.7 Sensor profile library

Each sensor entry is a declared triple (wR, wG, wB) measured against the sensor's spectral response. A few representative values:

SensorwRwGwBNote
Rec.709 (reference)0.21260.71520.0722 Standard broadcast luma
IMX571 (ASI2600)0.29440.50210.2035 Modern BSI, APS-C
IMX455 (ASI6200)0.29870.50010.2013 Full-frame reference
IMX585 (STARVIS 2)0.34310.48220.1747 NIR-extended; red carries more weight
IMX224 (ASI224)0.34020.47650.1833 Classic planetary; high red response
Seestar S50 (IMX462)0.33330.48660.1801 Smart-telescope OSC

Notice how the STARVIS 2 and classic planetary profiles push noticeably more weight onto R compared to Rec.709 — using Rec.709 on an IMX585 undercounts real red signal in the computed Y, and that error then propagates into every subsequent scale factor. The sensor profiles make this correct automatically.

8.8 Metadata handshake with Extract Luminance

When Extract Luminance writes a mono document, its metadata gets:

Recombine's auto-selection cascade reads them in that order; the same metadata survives into the recombined target as part of its Undo-History entry.

Credits. OpenCV's HSV conversion and Gaussian blur, NumPy's tensordot and MAD-based variance estimation, and the BT.601 YCbCr matrices are standard primitives used here. The sensor profile library with its per-camera weight vectors, the five-step metadata auto-selection cascade, the pedestal-lift round-trip, the pre-recombine saturation + chrominance NR pipeline (and the proof that they can't contaminate the replacement luminance), and the whole dialog's behaviour are SASpro's own design.