Multiscale Decomposition
Split an image into independent spatial-frequency bands, edit each one on its own, then reassemble — a general-purpose detail amplifier, noise reducer, and texture sculptor built on a dyadic Gaussian pyramid.
SASpro-exclusive tool. Multiscale Decomposition is an original SetiAstro Suite Pro implementation by Franklin Marek, not a port of or derivative of any other platform's multiscale transform. It uses a dyadic Gaussian pyramid with σ-aware per-layer soft thresholding, scale-weighted multiscale denoising, and a torch-accelerated separable-blur engine designed from the ground up for SASpro's pipeline, mask system, and ROI preview.
Each decomposition pass splits the input into N detail layers (increasingly coarse spatial scales) plus a residual (the lowest-frequency "DC" image). Every layer can be independently amplified, soft-thresholded against its own measured noise σ, denoised with a scale-weighted multiscale shrinkage, or turned off — then everything is summed back together. The pyramid is dyadic: each successive layer blurs with twice the sigma of the previous one, so Layer 1 holds the finest grain, Layer 4 holds structure several pixels wide, Layer 7+ holds large nebular or lunar-feature-scale content.
Where it shines. This is a genuine miracle on planetary, lunar, and solar images — crank up the Gain on the mid-scale detail layers and watch surface structure, cloud bands, limb detail and crater rims leap out of a seemingly flat frame. On deep-sky work it doubles as a surgical sharpener for nebular filaments and galaxy spiral arms, and the Denoise column gives you a per-scale noise-reduction knob that most tools don't expose.
1. The preview area
The left pane shows the current reconstruction (or the single layer you've selected, see §2). It uses a graphics view with wheel-zoom under the cursor and click-drag panning — the same interaction model as the rest of SASpro.
Preview controls.
Mouse wheel — zoom in / out, anchored under the cursor.
Left-click drag — pan the view.
Zoom Out / Zoom In toolbar buttons — 0.8× / 1.25× steps.
Fit — fit the whole image into the preview pane.
1:1 — reset to native pixel scale.
Two toggles above the layer table change what the preview is doing:
- Fast ROI preview (compute visible area only) — default on. Only the currently visible portion of the image is decomposed, tuned, and reconstructed, with a few extra pixels of padding on each side so the blur doesn't see an edge. As you pan or zoom the preview recomputes just the new visible rectangle. This makes every slider move feel instant on large frames (16-megapixel+ stacks). Apply and Send to Document always run on the full image.
- Auto-stretch preview — display-only autostretch so you can see linear data clearly while you work. It is never baked into the output — Apply and Send always write linear pixel values.
A small spinner next to the zoom toolbar lights up whenever a recomputation is in flight, and a Computing… overlay appears over the preview if a pass takes longer than ~120 ms.
2. Global settings
The top-right Global group controls the shape of the pyramid itself and the top-level preview mode.
- Layers (1–10, default 4) — number of detail layers. More layers means finer-grained scale separation and reaches to coarser structures (Layer 10 blurs with σ = 512 × base). Add layers when you want to sculpt structures at bigger scales than four layers can reach; drop to 2–3 when you only want to boost fine detail and want to keep the pyramid fast.
- Base sigma (0.3–5.0, default 1.0) — the Gaussian sigma of the first blur pass, in pixels. Later layers double this each time. A larger base sigma gives you a coarser decomposition overall; use 0.5–0.8 to really concentrate on single-pixel grain.
- Linked RGB — apply the same per-layer settings to all channels (default on). Turn it off to decorrelate channels — useful if your blue channel needs more NR than red, for instance. (In the current release the per-layer editor still edits a single parameter set; linked-off mode decomposes each channel independently so future per-channel editing drops in naturally.)
- Mode — μ–σ Thresholding (default) is the full nonlinear pipeline: Denoise, Threshold, Amount and Gain are all active. Linear turns off all σ-aware operations and uses Gain only — a strictly linear multiscale transform. Linear mode is the right choice when you want pure spatial-frequency boost or cut with zero shrinkage artifacts, for example when the decomposition is feeding a downstream math step that assumes linearity.
- Layer preview — switches what the preview is showing. Final shows the full reconstruction (details + residual, your actual output). R (Residual) shows just the lowest-frequency image. Detail Layer k shows a single band's content, remapped to mid-gray so positive and negative detail are both visible.
Layer-preview trick. Flipping through the detail-layer previews is the fastest way to learn where your data lives. On a planetary frame you'll typically see crater rims and cloud edges concentrated in Layers 2–4; on a galaxy frame, spiral-arm structure shows up in Layers 4–6; on a wide nebula, large-scale brightness variations live in Layers 6–8. Edit the layers where the structure you care about is actually present.
3. The Layers table
Below the Global group is the Layers table — one row per detail layer, plus a final R row for the residual. Columns are:
| Column | Meaning |
| On | Checkbox — enable or disable this layer. A disabled detail layer contributes zero to the reconstruction (useful for isolating what a layer is actually doing). Unchecking the residual row holds back the DC image, giving you a high-pass view (see §7). |
| Layer | Layer index: 1 (finest) through N, then R for residual. |
| Scale | The Gaussian sigma this layer corresponds to, in pixels — base_sigma × 2k. Residual shows "—" because it's an accumulated image, not a scale. |
| Gain | Multiplier applied to the layer's detail coefficients. 1.00 is unchanged, >1 boosts, <1 attenuates. Negative values are not supported (use 0 to zero out). |
| Thr (σ) | Soft-threshold level in multiples of the layer's measured noise σ (see §4). 0 is no thresholding; 1–3 is mild to strong grain suppression. |
| Amt | Blend factor between the raw and thresholded version of the layer. 0 means ignore thresholding entirely; 1 means use the thresholded layer fully. Lets you dial in how much of the Thr action to apply. |
| NR | Multiscale noise reduction amount for this layer, 0–1. See §9.3 for the scale-weighted formula. |
| Type | D for a detail layer, R for the residual. |
Click a row to load that layer into the Selected Layer editor below. Clicking any cell in the Gain / Thr / Amt / NR columns lets you edit the value directly — handy for setting the same number across several rows without going slider-by-slider. The checkbox and the editor stay in sync automatically.
4. The Selected Layer editor
Each of the four edit sliders is paired with a spin box, and either side updates the other. The ranges are:
- Gain (0.00 – 10.00) — multiplies the layer's detail coefficients. Values above 2–3 are routine on planetary and lunar frames; stay closer to 1.0–1.8 on deep-sky data to avoid amplifying noise.
- Threshold (σ) (0.00 – 10.00) — the soft-threshold level in units of this layer's robust σ. SASpro re-measures σ per layer (via MAD, see §9.2), so a Thr of 2.0 means the same thing on every scale: "zero out any coefficient under 2× this layer's noise, soften the rest." Typical starting point for grain suppression: 1.5–2.5 on the finest two layers, with Amount at 0.6–1.0.
- Amount (0.00 – 1.00) — the blend ratio toward the thresholded version. Pairs with Threshold.
- Denoise (0.00 – 1.00) — scale-weighted multiscale noise reduction. Internally this applies a soft threshold at 3σ scaled by √(2k), so the shrinkage ramps up on coarser layers automatically — a tuned-for-astro NR profile that behaves well across the whole pyramid at a single value. 0.2–0.4 is typical; 0.6+ is strong NR.
When the R (Residual) row is selected, all four controls grey out — the residual is a reconstructed image, not a set of coefficients, so none of the per-layer operations apply to it. In Linear mode, Threshold / Amount / Denoise grey out on every row (the mode is literally gain-only); Gain stays active.
Threshold vs Denoise. They're both σ-based shrinkage, but they're tuned for different jobs. Threshold + Amount is a sharpening-safe grain filter: it keeps everything above Nσ at its full strength (minus the σ-sized soft knee) and blends how aggressively it suppresses the rest. Denoise is a smoother, scale-aware shrinkage that ramps up on coarse scales, so a single Denoise value gives you balanced NR across the whole pyramid. Use Threshold when you also want to boost — denoising first keeps the Gain from amplifying residual grain.
5. Masking
Multiscale Decomposition respects the document's active mask. If a mask is set when you Apply, the final result is blended against the untouched input using the mask's intensity, so you can confine detail boosts to a nebula while leaving background sky linear and quiet, or protect a star field while sharpening galaxy arms.
Mask handling: non-finite values fall through as "no change," masks are rescaled to 0–1 whether they came in as 8-bit, 16-bit or float, and if the mask resolution doesn't match the image a fast nearest-neighbour resample brings them into alignment. See §9.8 for the formula.
6. Applying your result
Four buttons at the bottom of the right pane finish the session:
- Apply to Document — writes the full-image reconstruction back to the current document as a new history step named "Multiscale Decomposition" and closes the dialog. Full image, full precision, mask-aware. The preset dict and the active mask id are stored in the step metadata so Replay and Shortcuts can reproduce it later.
- Send to New Document — creates a brand-new document containing the reconstruction and leaves the source untouched. The new doc inherits the source's bit depth and headers but is unlinked from the file (no file_path, no ROI/preview flags).
- Split Layers to Documents — creates one new document per detail layer plus one for the residual. Detail layers are visualized mid-gray (0.5 + 4w, clipped) so positive and negative detail are both visible; the residual document holds the raw residual image. Very useful for frequency-separation-style retouching workflows where you want to edit a layer by hand in another tool and then bring it back.
- Close — discard preview, don't touch the document.
7. Residual off: the high-pass trick
If you uncheck the R row's "On" box, the preview and Apply both drop the residual from the reconstruction — you keep only the sum of your tuned detail layers. Because the detail layers are signed (zero-centred), the result is remapped to mid-gray for display:
v = 0.5 + 4 \sum_{k=0}^{N-1} w_k^{(\text{tuned})}
That mid-gray image is the high-pass of your data, filtered through whatever gain / threshold / NR profile you set per layer. It is useful on its own as an edge/structure map, or as input to subsequent processing — the Split Layers button sends each tuned layer out this way automatically.
8. Shortcuts and presets
The small grip icon at the bottom-left of the dialog is a drag-to-canvas preset handle. It captures your current state (layer count, base sigma, mode, per-layer config, residual on/off, linked-RGB flag) and does one of two things depending on where you drop it:
- Drop on empty canvas → creates a Multiscale Decomposition shortcut on the canvas carrying your exact current settings. Double-clicking that shortcut later reopens the dialog seeded with the same values.
- Drop directly on an image → applies the preset headlessly. No dialog, no preview — the reconstruction runs immediately against that document using the preset you captured.
Combined with Replay, this makes it trivial to carry a particular detail-enhancement recipe from one subject to the next — tune it once on a representative frame, drag off a shortcut, and apply it to a whole mosaic panel set.
9. Workflow recipes
Planetary / lunar / solar sharpening.
Base sigma: 0.7–1.0. Layers: 5–6. Mode: μ–σ Thresholding.
• Layers 1–2: Gain 1.2–1.5, Threshold 1.5–2.5 at Amount 0.6–0.8, Denoise 0.15–0.25 — tames high-frequency camera and seeing grain so the next step doesn't just amplify it.
• Layers 3–4: Gain 2.0–3.5, no threshold or denoise — this is where crater rims, cloud banding, and surface structure live. Push until it looks dramatic, back off until it looks real.
• Layer 5+: Gain 1.0 or slightly less; let the base image carry the broad tonality.
• Residual: leave on.
Deep-sky structure boost (galaxies, nebular filaments).
Base sigma: 1.0. Layers: 5. Mode: μ–σ Thresholding.
• Layer 1: Gain 1.0, Threshold 2.0 at Amount 0.8, Denoise 0.3 — pure grain filter.
• Layer 2: Gain 1.15, Threshold 1.5 at Amount 0.6, Denoise 0.2 — small-scale detail with light NR.
• Layers 3–4: Gain 1.3–1.6 — spiral-arm and filament scale.
• Layer 5: Gain 1.0.
• Mask: use a star-protection mask or a range mask excluding the background, so the boost lands on the object only.
Multiscale noise reduction only.
Mode: μ–σ Thresholding. All Gains at 1.0. Denoise 0.25–0.45 on Layers 1–2, 0.15 on Layer 3, 0 beyond. Threshold / Amount left at 0. Residual on. This gives you SASpro's scale-weighted NR profile as a standalone step without any sharpening — a nice complement to the dedicated denoisers.
Frequency-separation retouching.
Click Split Layers to Documents on a portrait or close-up — you now have Layer 1 (grain), Layer 2–3 (texture), Layer 4+ (form), and the residual (tone/colour) as separate documents. Paint, blur, or clone on the one you need, then re-import and combine with Pixel Math or by sending back through this tool with those layers set to Gain 1 and the rest to 0.
10. Technical details
What the pipeline actually does, in order, with the formulas and the engineering choices behind them.
10.1 The multiscale pyramid
Multiscale Decomposition builds a dyadic Gaussian pyramid. Starting from the input image c0 = x, each successive scale is generated by Gaussian blur with a doubling sigma:
c_0 = x
c_{k+1} = G_{\sigma_k}(c_k)
\sigma_k = \sigma_0 \cdot 2^k
where σ0 is Base sigma from the Global group and Gσ is a reflect-padded Gaussian blur. The detail layers are the differences between consecutive scales:
w_k = c_k - c_{k+1}, \quad k = 0,\, 1,\, \dots,\, N-1
The residual is the deepest blur:
r = c_N
This structure is a perfect-reconstruction transform: without any tuning, summing the layers plus the residual recovers the input bit-exactly (to float32 rounding):
x = r + \sum_{k=0}^{N-1} w_k
That identity is why unchecking "On" boxes gives you clean partial reconstructions, why disabling the residual yields a pure high-pass, and why the preview can show you individual layers that mean something.
For RGB images each channel is decomposed independently in parallel (one thread per channel), then merged channel-wise. For mono images the pyramid is computed on the single plane and expanded to 3-channel only for preview display.
10.2 Robust noise estimation
Every detail layer has its own noise σ, estimated by the Median Absolute Deviation:
\hat{\sigma}_k = 1.4826 \cdot \mathrm{median}\!\left(\left|w_k - \mathrm{median}(w_k)\right|\right)
The 1.4826 factor converts MAD to a standard-deviation estimate under Gaussian assumptions. MAD is used instead of plain std because detail layers contain real structure (edges, point sources) that would inflate a straight std and make all σ-based thresholds too generous. For layers larger than ~100,000 samples SASpro grid-subsamples the array first — the cost of a full-image median scales badly, and MAD on a 1% stride samples is statistically indistinguishable from the full computation.
Non-finite values are stripped before the median, so NaNs or Infs in a region of the frame don't poison the σ estimate for the entire layer.
10.3 μ–σ Thresholding mode
In the default mode three operations are applied per layer, in order: denoise, then threshold-with-amount, then gain. The shared primitive is a soft threshold:
S_t(x) = \mathrm{sign}(x) \cdot \max(0,\, |x| - t)
Soft thresholding is preferred over hard thresholding here because it preserves continuity: coefficients just above the threshold aren't yanked to their raw value, they are smoothly attenuated by t. That matters because detail layers are what the reconstruction sums — hard thresholding introduces visible discontinuities at structure boundaries.
Denoise step. With Denoise amount βk, a scale-weighted threshold is applied:
s_k = \sqrt{2^k}
\tau_k = 3\, \beta_k \cdot s_k \cdot \hat{\sigma}_k
w_k^{(1)} = (1 - \beta_k)\, w_k + \beta_k \cdot S_{\tau_k}(w_k)
The √(2k) scale factor is the key piece — coarser layers get a proportionally larger threshold, because that's where the energy of real smooth structure lives and the layers accumulate noise variance across scales. A single Denoise value therefore delivers balanced NR across the whole pyramid without the user needing to tune each layer separately.
Threshold-with-amount step. Using Thr Nσ and Amount αk:
t_k = N_\sigma \cdot \hat{\sigma}_k
w_k^{(2)} = (1 - \alpha_k)\, w_k^{(1)} + \alpha_k \cdot S_{t_k}(w_k^{(1)})
Amount is the blend lever. α = 0 completely ignores the threshold; α = 1 uses it fully. In between is the usual sweet spot — Amount around 0.5–0.8 keeps enough of the original coefficient energy that boosted detail doesn't look thin while still cleaning up the grain.
Gain step. Simple multiply:
w_k^{(\text{tuned})} = g_k \cdot w_k^{(2)}
10.4 Linear mode
Linear mode skips Denoise, Threshold and Amount entirely and keeps only Gain:
w_k^{(\text{tuned})} = g_k \cdot w_k
This makes the whole pipeline strictly linear — a multiscale frequency-weighted multiplier. Use it when you want pure band-boost/cut with zero shrinkage artifacts, when you're prototyping a filter design, or when the output is feeding another linear step and nonlinearities would complicate the math downstream.
10.5 Reconstruction and output modes
After all layers are tuned, the output is the straightforward sum:
y = r_{\text{on}} + \sum_{k=0}^{N-1} w_k^{(\text{tuned})}
where ron is the residual if its "On" box is checked, zero otherwise. When the residual is off, the sum is signed and remapped to mid-gray for display and output:
y_{\text{display}} = \mathrm{clip}\!\left(0.5 + 4\,y,\; 0,\; 1\right)
With the residual on, the result is clipped straight into [0, 1]. Mono inputs are preserved as mono — the pyramid runs on the single plane, and only the preview is expanded to RGB internally; Apply writes back the single-channel reconstruction in the original shape.
10.6 The Gaussian blur engine
The Gaussian pyramid's performance is the dialog's performance — on a 24-megapixel RGB frame with 6 layers, that's 18 full-image blurs with sigmas ranging from 1 to 32 pixels. SASpro picks the best available backend at each call:
- Images under 256×256 or sigma below 0.5 go straight to OpenCV's
GaussianBlur — no backend overhead is worth it at that scale.
- Otherwise, a torch separable-conv path is tried first. The 2D Gaussian factors into two 1D convolutions, which SASpro implements as grouped
conv2d operations with reflect padding — once with a (1 × K) horizontal kernel and once with a (K × 1) vertical kernel. This runs on CUDA when a GPU is present and on torch's CPU backend otherwise. Both paths are genuinely parallel across all cores and don't fight with the per-channel thread pool.
- If torch is unavailable, SASpro falls back to a tiled SciPy path: the image is divided into roughly 2× worker-count tiles sized to the image aspect ratio, each tile is extended by an overlap of ⌈4σ⌉ + 4 pixels on all sides, and the tiles are blurred in parallel with
scipy.ndimage.gaussian_filter before being stitched back without the overlap. The overlap guarantees reflect-pad continuity at the tile seams.
- If neither is available, OpenCV's
GaussianBlur is used unconditionally.
The kernel size is picked from sigma the standard way:
K = \max\!\left(3,\, 2 \lfloor 3\sigma \rfloor + 1\right) \;|\; 1
(the | 1 forces an odd kernel size, which both OpenCV and torch's conv2d want for centred padding).
Separable 1D convolution is the second big win here. A 2D Gaussian is the outer product of two 1D Gaussians, so a K × K blur can be done as:
G_\sigma(I) = \left(I \ast g_\sigma^{\mathsf{T}}\right) \ast g_\sigma
at 2K multiplies per pixel instead of K2. On a 32-pixel-sigma blur that's roughly a 20× arithmetic saving, which stacks with the GPU/multicore gains.
GPU vs CPU vs scipy. On a mid-range CUDA card the torch GPU path is roughly 10–20× faster than tiled scipy on large frames; on CPU torch is typically 2–4× faster than tiled scipy because it keeps the arithmetic in a single contiguous tensor and avoids NumPy per-tile overhead. The dialog's "init progress" dialog you see on first open is almost entirely the time of this first pyramid build; subsequent slider changes reuse the cached pyramid and only recompute what has to change.
10.7 Fast ROI preview
With Fast ROI preview on, the preview computes only the currently visible region of the image, with a safety margin on each side so the deepest blur still sees valid pixels. The pad is sized to the coarsest layer in the pyramid:
\sigma_{\max} = \sigma_0 \cdot 2^{N-1}
p = \lceil 3 \sigma_{\max} \rceil + 2
The ROI is further capped at 1400 px per side to protect responsiveness when the user is zoomed way out. For a 24-megapixel frame zoomed to a 1200×800 ROI with 6 layers, this means decomposing ~1.1M pixels instead of ~24M on each slider move — a 20× cost reduction that puts slider latency in the 20–50 ms range even with CPU-only torch.
The viewport scrollbars and the zoom/pan handlers all schedule the same debounced ROI recompute (60 ms coalescing) so a rapid pan or wheel-roll produces one recomputation at the end rather than a storm of them during the gesture.
ROI is a preview only. Apply, Send to New Document and Split Layers always run on the full image and always use the full-resolution pyramid. The ROI path exists so you can tune parameters interactively on huge frames without waiting; the committed result is never a stitched-up ROI.
10.8 Mask blending
If the document has an active mask, the final reconstruction is blended against the untouched source:
y_{\text{final}} = m \cdot y + (1 - m) \cdot x
where m is the mask value in [0, 1], y is the full reconstruction, and x is the original input. Masks are normalised regardless of their input dtype — integer masks are divided by their type-max, float masks greater than 1 are rescaled by their observed max, and anything outside [0, 1] is clipped. If the mask resolution doesn't match the image, SASpro resamples with a fast nearest-neighbour index map; if the blend produces non-finite values anywhere (bad mask data), the entire mask step is bypassed and the plain reconstruction is written through instead.
The mask id, blend formula and whether the step was masked are all recorded in the history-step metadata so Replay reproduces the exact masked composition on the next frame.
Credits. Multiscale Decomposition — algorithm, implementation, UI and ROI/preview engineering — is original work by Franklin Marek for SetiAstro Suite Pro, with the following standard components credited to community: the Gaussian pyramid is a classic construction (Burt & Adelson 1983, "The Laplacian Pyramid as a Compact Image Code"); soft thresholding is Donoho (1995, "De-noising by soft-thresholding"); robust σ from MAD follows the Rousseeuw & Croux convention; separable Gaussian factorization is textbook image processing. OpenCV, PyTorch and SciPy are credited as the compute backends. The scale-weighted multiscale denoiser (τk = 3βk√(2k)σ̂k), the mode system, the ROI-preview architecture, the mask blending integration, and the overall per-layer editor are SASpro-exclusive.