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.
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.
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.
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.
| Entry | Weights (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 RGB | per-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. |
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).
When the dialog opens, and whenever you change the source dropdown, SASpro picks a profile automatically along this cascade:
luma_method key naming
the exact profile used; that key is reused here.luma_profile
with luma_method collapsed to rec709.
SASpro reverse-looks-up the display name to recover the correct
profile key.INSTRUME, CAMERA or CAMNAME
is scanned for known sensor substrings
(imx571, asi2600, seestar s50, …)
and the matching profile is picked.self.luma_method and reused.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.
Slider + spin box, 0.00–1.00, default 1.00. Linear mix between the untouched target and the recombined result:
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.
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.
−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).
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.
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).
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.
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.
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.
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.
Inside apply_recombine_to_doc:
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).
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:
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.
Transform the target to BT.601 YCbCr with Cb and Cr biased to [0, 1] by adding 0.5:
Gaussian-blur Cb and Cr only (kernel size ⌈6σ⌉,
forced odd), then re-centre them and invert the matrix:
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].
compute_luminance)When the source is RGB, Y is computed per the selected profile:
normalize_weights = False, their absolute
sum is kept (allowing deliberate scaling).Mono source documents skip all of this — their single plane is used directly as L.
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.
The core recombine, with pedestal p, replacement
luminance L, and weight vector w:
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:
with ε = 10−6. Apply the
scale to every channel of the lifted target and invert the pedestal:
Clipped to [0, 1] and finally blended against the untouched target by the global blend slider.
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:
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.
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.
When enabled (k > 0), the raw scale s
is passed through the compression curve:
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.
Each sensor entry is a declared triple
(wR, wG, wB)
measured against the sensor's spectral response. A few representative
values:
| Sensor | wR | wG | wB | Note |
|---|---|---|---|---|
| Rec.709 (reference) | 0.2126 | 0.7152 | 0.0722 | Standard broadcast luma |
| IMX571 (ASI2600) | 0.2944 | 0.5021 | 0.2035 | Modern BSI, APS-C |
| IMX455 (ASI6200) | 0.2987 | 0.5001 | 0.2013 | Full-frame reference |
| IMX585 (STARVIS 2) | 0.3431 | 0.4822 | 0.1747 | NIR-extended; red carries more weight |
| IMX224 (ASI224) | 0.3402 | 0.4765 | 0.1833 | Classic planetary; high red response |
| Seestar S50 (IMX462) | 0.3333 | 0.4866 | 0.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.
When Extract Luminance writes a mono document, its metadata gets:
luma_method — the canonical LUMA_PROFILES key
used (e.g. sensor:Sony IMX571 (ASI2600/QHY268), or
rec709 for standard profiles). This is the durable
profile identity: later re-resolution with
resolve_luma_profile_weights() recovers the exact
weight vector.luma_profile — the sensor's display name (for
sensor keys only). Redundant with the key for current writers,
but kept because older extracted-L docs stored only this and
find_sensor_profile_key_by_display_name uses it
to recover the key.luma_weights — the actual weight vector as a
3-list, present whenever weights were used. Lets code
downstream that doesn't want to re-import LUMA_PROFILES still
read the numbers.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.