Function: Statistical Stretch - setiastro/setiastrosuitepro GitHub Wiki
Function: Statistical Stretch
Analytic, target-median stretch for mono and RGB images. This tool performs a closed-form tone mapping to move the image median from the current value (T) to a chosen Target Median (T'), with T,T' ∈ (0,1).
In SASpro, the stretch is implemented as a closed-form median-targeted curve applied after an optional blackpoint lift. It operates on float images (typically normalized to [0,1]) and supports:
- Mono stretch
- RGB Linked (single curve for all channels)
- RGB Unlinked (per-channel curves)
- Optional Curves Boost (gentle S-curve)
- Optional HDR highlight compression
- Optional Luminance-only stretch (RGB) with safe recombination
- Optional High Range manager (robust rescale + soft clip)
Code: imageops/stretch.py
Tone curve & closed-form solution
Midtones-transfer family (conceptual)
Statistical Stretch models the midtones-transfer family:
f_a(x) = \frac{3^{a}\,x}{\left(3^{a}-1\right)x + 1}, \qquad x\in[0,1].
Here a is a real-valued “amount” that analytically continues the effect of applying a midtones operator a times.
Solve for the amount that maps median (T → T')
a \;=\; \frac{\log\!\Big(\dfrac{T'}{1-T'} \cdot \dfrac{1-T}{T}\Big)}{\log 3},
\qquad T,T'\in(0,1).
Then the image is mapped by x ↦ fₐ(x). This is not arcsinh; it is an analytic midtones transfer aimed precisely at a target median.
What SASpro evaluates (fast closed-form)
In SASpro the implementation is optimized to avoid building a full “rescaled image” first. Instead it evaluates the equivalent closed-form expression directly from the image and a few scalars:
Define:
- bp = blackpoint
- denom = max(1 − bp, ε)
- r = (x − bp) / denom
- med = (T − bp) / denom (median in the same rescaled space)
- t = T' (target median)
Then each pixel is mapped by:
y \;=\; \frac{(med-1)\,t\,r}{med\,(t+r-1) - t\,r}.
This is the accelerated form used by the numba_*_from_img() kernels (or NumPy fallbacks) and is mathematically equivalent to the median-targeted midtones transfer.
Blackpoint handling (why the slider “works” now)
Before the analytic stretch, SASpro optionally raises the blackpoint using a robust noise estimate:
- A robust sigma is estimated using MAD on the lower half of the distribution (fast sampling for speed).
- Blackpoint is computed as:
bp = \mathrm{median} - (\sigma_{\text{slider}})\cdot\sigma_{\text{robust}}.
Then bp is clamped to [min(image), 0.99].
No Black Clipping
If No Black Clipping is enabled:
- blackpoint becomes bp = min(image)
- the sigma-based blackpoint lift is bypassed
For RGB Unlinked, blackpoint is computed per channel.
Parameters (UI)
Core
-
Target median (T'): desired median after stretching.
-
Normalize to [0,1]: optional post-stretch scaling so the maximum becomes 1.0.
- Useful for display/workflow consistency.
- Disable if you want to preserve absolute scaling relationships.
-
Blackpoint Sigma: strength of sigma-based blackpoint lift.
-
No Black Clipping: bypass sigma blackpoint; uses
min(image)as blackpoint.
RGB options
-
Linked channels (RGB only)
- On: compute one curve from global statistics and apply to all channels (best color preservation).
- Off: compute/apply per channel (can correct color imbalance but may shift color balance).
Curves Boost (optional)
- Apply Curves Adjustment: applies a gentle S-curve after the analytic stretch.
- Strength (s ∈ [0,1]): controls contrast lift around mid-tones.
Implementation details:
- The curve is defined by 6 control points and applied as a piecewise-linear LUT via
np.interp. - Control points are cached for speed.
HDR highlight compression (optional)
- HDR Compress: smooth soft-knee highlight rolloff after the stretch.
- HDR Amount (0..1): strength of compression.
- HDR Knee (0..1): where highlight compression begins.
For RGB, SASpro compresses luminance, then recombines by linear scaling to reduce color shifts and star bloat.
Luminance-only mode (RGB optional)
-
Luma Only: stretch luminance only, then recombine into RGB by linear luminance scaling.
-
Luma Mode: Rec.709 / Rec.601 / Rec.2020 (and supported profiles).
- If
snrweighting is selected, SASpro can estimate per-channel noise sigma for luminance calculation.
- If
High Range mode (optional)
A “ready-to-use” high dynamic range manager (useful for extreme stacks):
- Robust floor (median − k·sigma)
- Soft/hard ceilings from percentiles
- Safe rescale + pedestal
- Optional MTF steering toward Target Median
- Final soft highlight rolloff
Controls include:
- Pedestal
- Soft Ceil Percentile
- Hard Ceil Percentile
- Softclip Threshold
- Softclip Rolloff
Mask-aware apply
If a mask m ∈ [0,1] is active, the result is blended:
I_{\text{final}} \;=\; m \cdot I_{\text{stretched}} \;+\; (1-m)\cdot I_{\text{source}}.
Workflow
-
Pick Target Median (common starts: 0.20–0.35, often 0.25).
-
Choose Linked vs Unlinked (RGB).
-
Set Blackpoint Sigma (or enable No Black Clipping if you want to preserve faint background).
-
Optional enhancements:
- Curves Boost for midtone contrast
- HDR Compress for star/core highlight control
- Luma Only (RGB) to protect color while changing brightness
- High Range for difficult high-dynamic-range data
-
Preview and adjust; Apply commits (with undo).
Preview & Navigation (non-destructive)
- Zoom In/Out, 100%, Fit
- Ctrl + Wheel zooms at cursor; drag to pan
- Preview shows the stretched image only; the source is untouched until Apply
Notes & tips
-
The stretch is monotone and median-targeted, making it generally star-friendly compared to aggressive global curves.
-
For RGB stacks with color imbalance:
- try Unlinked first to equalize channels,
- then a small Curves Boost to restore contrast.
-
If bright stars/cores are harsh, enable HDR Compress (and adjust Knee).
-
Use Luma Only when you want to change brightness/contrast while keeping chroma stable.
-
Normalize is convenient, but it changes absolute scaling—disable it if downstream steps expect unchanged relative scaling.