WaveScale HDR

SASpro-exclusive dynamic-range compressor for the bright half of the histogram. The classical problem it solves: inside a galaxy core, a planetary nebula shell, or a bright star's near-saturated inner profile, there is real structure — dust lanes, HII regions, concentric shells, a tiny companion — but it has been squeezed into such a narrow luminance range near unity that it reads to the eye as featureless white. WaveScale HDR decomposes the luminance into starlet wavelet planes, amplifies the fine-detail planes only where the image is bright, aligns midtones so the overall look doesn't shift, and finally applies a gentle dimming curve that pulls the result back down into display range. The lost detail reappears without destroying the dark background.

1. Workflow

  1. Open the dialog with an RGB (or mono) image active. A side HDR Mask window pops up immediately showing where the effect will be applied at the current Mask Gamma.
  2. Adjust Mask Gamma until the mask window shows white exactly on the regions you want to decompress (galaxy core, nebula centre, bright star cores) and black everywhere else. This drives where.
  3. Adjust Coarse Compression for strength and Number of Scales for how large a structure the effect can reach. These drive how much and how far.
  4. Press Preview. A worker thread runs the full pipeline and the result is shown in the main preview, mask-blended with the original so untouched regions stay untouched.
  5. Use Show Original to A/B toggle; iterate on the sliders and press Preview again as needed.
  6. Apply to Document commits. The dialog stays open so you can run on another image, or close.

2. HDR controls

Number of Scales

Slider, 2–10, default 5. How many wavelet detail layers the luminance is split into. Each additional scale reaches structures roughly twice as large in pixels, so 5 scales covers everything from single-pixel detail out to ~32-pixel features; 10 scales reaches kilopixel-scale structure.

Also drives the automatic dimming gamma applied at the end (§ 6.6), so more scales means more highlight-taming after the amplification.

Coarse Compression

Slider, 0.10–5.00, default 1.50. The strength of the local-contrast boost per plane. Only takes effect where the luminance mask is non-zero.

Mask Gamma

Slider, 0.10–10.00, default 5.00. Shapes the luminance mask that decides where the effect is applied. The mask is the image's own normalised luminance raised to this gamma, so higher values pull the mask threshold up toward unity.

The side HDR Mask window shows exactly what this slider produces, updated live (debounced to 250 ms) so you can watch the white region shrink as you push gamma higher. If the current document has an active mask, the window title changes to “HDR Mask (L × Active Mask)” and the two masks are multiplied together before display and application.

Tip: for a galaxy core, start with Mask Gamma around 5–7, Coarse Compression around 1.5, and 5 scales. Watch the HDR Mask window as you adjust gamma until only the inner core is white — everything outside that will be left alone, which is exactly what you want for the background sky and the fainter outer arms.

3. The HDR Mask window

A small 400×400 side window that appears when the dialog opens. It shows the current effective mask — the luminance mask built from the image's L* channel at the current gamma, multiplied by the document's active mask if one exists. White means “full effect here”, black means “leave alone”, greys are proportional blends.

The window updates automatically as you drag the Mask Gamma slider (with a 250 ms debounce to avoid lag on large images), and after each Preview completes, so you can always see what mask the next Apply will use. It's purely display — nothing you do in this window changes the result.

4. Preview / Show Original / Apply

Preview

Runs the full pipeline on a background QThread and streams progress updates to the small progress bar at the bottom-right (“Converting to Lab color space”, “Decomposing luminance with starlet”, “Finalizing”). When it completes, the main view updates to show the mask-blended result and the Apply button is enabled.

Show Original

A toggle button next to Preview. When pressed, flips the main view to the untouched original so you can compare before and after; the button label switches to “Show Preview” so a second press flips back. The side mask window is unaffected.

Apply to Document

Commits the current preview to the document as an undoable edit named “WaveScale HDR”, with the current three slider values stored as its preset. The dialog does not close afterward — you can switch to another open document and run again with the same (or edited) settings. Reset returns all sliders to their defaults and clears the preview; Close discards everything.

5. Preset grip

The handle at the bottom-left carries the current three slider values and can be dragged onto the canvas to make a shortcut tile, or dropped onto an image to apply those settings headlessly. Double-clicking the tile reopens this dialog with the settings pre-filled.

6. Technical details

All processing on float32. Luminance channel is CIE L* in [0, 100]; RGB channels are in [0, 1]. The pipeline is non-destructive right up to the final mask-blended composite.

6.1 Pipeline overview

End-to-end, Preview runs:

RGB → Lab → L* → à trous decomposition → per-scale amplification ↓ ↓ mask (L^γ) ─────────────────────────────────┘ ↓ reconstruct L′ → midtone align → put back into Lab ↓ Lab → RGB → dimming curve → mask-blend with original → preview

6.2 Luminance mask

The image's L* channel (0–100) is normalised and raised to the user's gamma to produce a value in [0, 1] per pixel:

m \;=\; \mathrm{clip}\!\left(\frac{L^{*}}{100},\;0,\;1\right)^{\!\gamma}

With γ = 1 the mask is just the normalised luminance itself; higher gamma pulls the mask response toward only the brightest pixels. If the document has an active mask mdoc, the effective mask used downstream is their product:

m_{\mathrm{eff}} \;=\; m_{\mathrm{HDR}} \,\cdot\, m_{\mathrm{doc}}

which lets you spatially restrict the HDR effect to any arbitrary shape drawn in a mask layer, on top of the automatic luminance-driven selection.

6.3 Starlet (à trous) decomposition

SASpro uses the classical B3-spline starlet wavelet for luminance decomposition. The analysis kernel is the normalised B3 spline:

h \;=\; \frac{1}{16}[1,\;4,\;6,\;4,\;1]

applied separably with reflection boundaries. The decomposition produces n detail planes wi and one coarse residual cn, reconstructing the original L* exactly:

L^{*} \;=\; \sum_{i=0}^{n-1} w_i \;+\; c_{n}

Each successive scale is produced by convolving with a spaced version of the base kernel — zeros inserted between its coefficients so that scale i captures features of roughly 2i pixels in extent:

w_i \;=\; c_{i-1} - c_i\,,\qquad c_i \;=\; c_{i-1} \ast h^{(2^{i})}

with c0 = L*. The à trous scheme leaves plane dimensions unchanged (no decimation), which is what makes a plane-by-plane multiplicative amplification possible without resampling artefacts.

6.4 Per-scale amplification with decay

Each detail plane is multiplied by a per-pixel scale factor built from the compression C, the mask meff, and a geometric decay with rate ρ = 0.5 across scales:

s_i \;=\; (1 + (C - 1)\,m\,\rho^{i})\,\cdot\,2\,,\qquad w'_i \;=\; s_i \,\cdot\, w_i

Two things worth unpacking:

6.5 Reconstruction and midtone alignment

After amplification, the modified planes and the untouched coarse residual are summed back to a new L*:

L' \;=\; \sum_{i=0}^{n-1} w'_i \;+\; c_{n}

Because every plane was multiplied by at least 2, the raw L' can land well above the original L* in bright regions. To keep the overall tonal character consistent, SASpro rescales L' so its median matches the original:

L'' \;=\; L' \,\cdot\, \frac{\mathrm{median}(L^{*})}{\mathrm{median}(L')}

and clips to [0, 100]. The median is used because it's robust against the small handful of pixels that got pushed well past 100 — a mean would be dragged upward by them, defeating the alignment.

6.6 Automatic dimming curve

Even after midtone alignment, highlights that got boosted hard in the detail planes can still clip against unity. Rather than hard-clip them, SASpro applies a global gamma to the whole RGB output after converting back from Lab:

g \;=\; 1 \;+\; 0.2\,n
\mathrm{RGB}_{\mathrm{out}} \;=\; \mathrm{clip}(\mathrm{RGB},\,0,\,1)^{\,g}

The dimming exponent scales with the number of wavelet scales precisely because more scales means more overall amplification across the pipeline — so with 5 scales g = 2.0, with 10 scales g = 3.0. Larger g pulls highlights down harder, keeping the output in range even on aggressive settings.

6.7 Mask-blended composite

The gamma-dimmed output of the pipeline (call it RGBtransformed) is blended with the original input through the same mask that drove the amplification:

\mathrm{out} \;=\; (1 - m_{\mathrm{eff}})\,\mathrm{original} \;+\; m_{\mathrm{eff}}\,\mathrm{transformed}

So dark regions — where meff ≈ 0 — come through from the original untouched, while bright regions — where meff ≈ 1 — get the full transformed version. The transitions follow the mask shape, which the user controls via gamma, so you can confine the effect to arbitrarily narrow ranges of luminance. The composite is the preview you see in the main view and what gets written to the document on Apply.

6.8 Mono inputs

Grayscale and single-channel inputs are promoted to three identical channels for processing (the Lab conversion then produces a nonzero L* and zero a*/b*, which the pipeline handles correctly), and on Apply the result is collapsed back to the original shape — 2-D for 2-D inputs, (H, W, 1) for 1-channel 3-D inputs. The effect on mono data is identical to processing the luminance of an equivalent neutral-grey RGB.

Credits. The B3-spline starlet (à trous) wavelet transform is the classical isotropic undecimated wavelet of Starck & Murtagh, long established in astronomical image analysis (see e.g. Astronomical Image and Data Analysis, Starck & Murtagh). SASpro uses it as its luminance decomposition primitive via the shared wavelet_utils module. Lab colour-space conversion follows CIE 1976. The whole WaveScale HDR algorithm — the per-scale amplification with geometric decay across scales, the luminance-raised-to-gamma mask, the baseline-factor-of-2 detail lift, the median-based midtone realignment, the scales-dependent automatic dimming gamma, the integration with the document's active mask, and the final mask-blended composite — is SASpro-exclusive, designed and implemented by SetiAstro to solve the glare-decompression problem in bright astronomical regions.