Stacking Suite
SASpro's complete calibration-and-integration pipeline: from raw camera RAW/TIFF through master darks and flats, debayer, cosmetic correction, satellite trail masking, registration against any alignment model up to 4th-order polynomial, drizzle, up to multi-panel mosaic sets, multi-frame deconvolution, and a final rejected-and-weighted integrated master — all driven from one window, with CPU, CUDA, MPS and DirectML backends shared across every heavy stage.
The Stacking Suite is laid out as six tabs along the top (Quick Stack, Convert Camera RAW/TIFF, Darks, Flats, Lights, Image Integration) with a header strip above them (wrench, folder path, Change…, Open Log, document) and almost every parameter you'll want to touch living in the orange-wrench Stacking Settings dialog. The sections below walk through each tab in the order you'll meet them, then cover the Settings dialog, then the maths and plumbing behind the scenes.
Top row & the wrench
A single strip sits above the tabs and stays visible at all times:
| Control | What it does |
| π§ wrench (orange) |
Opens the Stacking Settings dialog — profiles, distortion/transform, alignment, performance, drizzle, MFDeconv tuning, rejection, cosmetic correction, comet, comet star removal. See §7. |
| Current Stacking Folder |
Live display of the folder every intermediate file and master will land in. Reads from stacking/dir and tooltips the full path. |
| Change… |
Picks a new stacking folder. The suite creates Calibrated/, Normalized_Images/, Aligned_Images/ and Masters/ underneath it on demand. |
| Open Log |
Raises the dockable Stacking Suite log window. Everything the suite prints is tee'd to this log and to stacking_log.txt inside the stacking folder, so you have a timestamped record of every run. |
| π document |
This help page. Added at the far right of the row. |
1. Quick Stack
The default tab, and the easiest way to get a stack out the door. Five cards walk you top-left to bottom-right through the pipeline, each with a traffic-light indicator:
Workflow
1 Darks → 2 Flats → 3 Lights → 4 Calibrate → 5 Register + Integrate.
Cards go red when nothing is loaded, yellow when raw frames are present but no master has been built, and green when the step is complete.
1.1 Card controls
- 1) Darks — Load Raw Darks, Load Master Dark, Build Master, Darks Tab. Also accepts flat darks and bias frames; they are routed by IMAGETYP into the same tree.
- 2) Flats — Load Raw Flats, Load Master Flat, Build Master, Flats Tab.
- 3) Lights — Load Lights (files) or Load Light Folder (directory), plus Lights Tab to drop into the full view.
- 4) Calibration — Calibrate Lights runs the pipeline (dark sub → flat div → cosmetic correction → optional pedestal → optional satellite masking). Refresh Status re-counts raw and master files.
- 5) Register + Integrate — Register + Integrate runs the full back-half, Integrate Only skips re-registration if
_r.fit files already exist, and Image Integration Tab hands off to the advanced view.
1.2 Pipeline buttons at the bottom
- ποΈ Clean Up Intermediate Files — deletes
Normalized_Images/, Aligned_Images/ and loose .dat/.npy/.tmp inside the stacking folder to reclaim disk space between runs.
- π Run Full Pipeline — chains everything: build master darks → build master flats → calibrate lights → register & integrate. Honours the current traffic-light state (will skip steps you don't need) and uses the current Settings for every stage.
2. Convert Camera RAW/TIFF
A standalone batch converter from camera formats (CR2, NEF, ARW, RAF, DNG, TIFF, PNG, JPEG, … — anything rawpy can read, plus TIFF via tifffile) into debayered FITS. It never touches the Lights/Darks/Flats trees — the output goes to a folder you choose.
- Add Conversion Files — multi-select picker.
- Add Conversion Directory — folder picker; recurses when Recurse subfolders is on elsewhere.
- Select Output Directory — converted
.fit files land here; without one set, you'll be prompted.
- Clear List — empties the tree without touching files.
- Convert All Files to FITS — runs the batch. Each file is debayered (VNG by default), gets a minimal FITS header built from EXIF, and is written as float32.
When to use this tab: only when your lights are in DSLR raw formats and you'd rather preprocess them to FITS once than let the Lights tab decode them on every run. Modern SASpro will happily eat raw files directly through the Lights tab too, so this is primarily for mixed sessions or archival.
3. Darks
Groups raw dark/flat-dark/bias frames and turns them into master darks. The tab is split in two: raw darks on the left, controls on the right, and the master-dark list across the bottom.
3.1 Left pane: raw darks tree
- Dark Frames tree — grouped by exposure (seconds) and image size; metadata column shows file count, total integration time, temperature bucket, gain and offset.
- Add Dark Files, Add Dark Directory — adds files or scans a folder. The directory scan honours Recurse subfolders and sorts by
IMAGETYP: darks stay here, flats are routed to the Flats tab, lights to the Lights tab.
- Recurse subfolders — shared with the Flats and Lights tabs via the
stacking/recurse_dirs setting.
- Auto-detect session — shared with Flats/Lights. For darks this is cosmetic only: masters always bucket by (exposure, size, temp, gain, offset) regardless of capture date, so your January and June darks of the same exposure can combine.
- Clear Selection — removes highlighted items from the tree only.
3.2 Right pane
- Exposure Tolerance (seconds) — 0–30 s window around each exposure for grouping (default 5 s). Prevents a 299.8 s dark being orphaned from a 300.0 s group.
- Turn Those Darks Into Master Darks — the big yellow-bordered button. Builds one master per unique (exposure, size, temperature bucket, gain, offset) group using the Calibration Rejection Algorithm set in Settings (default: Winsorized Sigma Clipping).
3.3 Right-click & context
Right-click any raw-dark row for Set Session Tag…. Darks are session-agnostic for integration (see 3.1), but the tag is preserved in the tree for your reference.
3.4 Master Darks list
Shows every master dark currently loaded, with its exposure and file path. Load Master Dark adds a pre-built master from disk; Clear Selection removes items from the list and persists the change.
4. Flats
Same shape as Darks but with filter awareness and per-group dark-override controls.
4.1 Left pane: raw flats tree
- Flat Frames tree — four columns: Filter & Exposure, Metadata, Master Dark Used, Override Dark.
- Add Flat Files / Add Flat Directory — same semantics as Darks.
- Recurse subfolders, Auto-detect session — shared toggles. When Auto-detect is off, the Manual Session field below applies to every new flat.
- Manual Session — freeform tag (e.g.
Night1, 2025-12-13, SessionA). Only used when Auto-detect is off, and master flats always include their session in the key.
4.2 Right pane
- Exposure Tolerance (seconds) — sub-second precision (0–30 s, step 0.01), persisted. Flats are usually short, so a tight tolerance groups 1.1 s and 1.2 s shots correctly.
- Auto-Select Closest Master Dark — for every flat group, scores the available master darks by exposure proximity (and GAIN/OFFSET match within 1 count) and picks the best.
- Turn Those Flats Into Master Flats — builds one master flat per (filter, size, session) group.
4.3 Right-click & context
Right-click any flat group for:
- Override Master Dark for this group… — file picker; the chosen dark is used only for this flat group. Row's Override column turns gold.
- Clear Dark Override for this group — reverts to Auto (or None if auto-select is off).
- No Dark Calibration for this group — flats go through without any dark subtraction. Row goes red. Useful for cameras where the flat-dark pedestal is baked in.
- Set Session Tag… — retags the whole group.
5. Lights
Where you load the actual signal data, apply the calibration pipeline, and optionally hand off straight to registration.
5.1 Lights tree
Five columns: Filter & Exposure, Metadata, Master Dark, Master Flat, Corrections. Grouped by filter at the top level and by (exposure, size) beneath. Leaves show individual frames with their status and chosen masters.
- Add Light Files, Add Light Directory — files or recursive folder scan.
- Remove Selected — removes highlighted leaves from the tree only.
5.2 Options row
- Recurse subfolders, Auto-detect session — shared toggles.
- Auto-register & integrate after calibration — when checked, the moment calibration finishes the suite switches to Image Integration and clicks Register and Integrate Images for you.
- Interactive flat strength — before saving each calibrated group, pops a side-by-side preview letting you tune α (0 = no flat, 1 = normal, 2 = doubled correction) with a live “raw / corrected” view. Lifesaver when your flats slightly over- or under-correct vignetting.
Manual Session field: same as Flats, shared via stacking/session_keyword.
5.3 Corrections row
- Enable Cosmetic Correction — MAD-based hot/cold pixel detector with structure protection. On by default. Tuning lives in the Settings dialog (§7.9); the Lights tab has a π¬ Tune Cosmetic Correction σ… button that opens a dedicated preview dialog on your selected frame.
- Apply Pedestal — adds a small positive constant before dark subtraction so a near-black sky never clips at zero. Pedestal (0–1000) below is the DN value; divided by 65535 internally (so 50 ≈ +0.000763 in 0–1 units).
- Apply Bias Subtraction (For CCD Users) — subtracts a scaled master bias. Modern CMOS cameras use correlated double sampling and don't need this; tooltip reminds you.
5.4 π¬ Tune Cosmetic Correction σ…
Opens the standalone Cosmetic Correction dialog on your selected light (or the first available). Use the β / picker / βΆ bar at the top to bounce between frames. Adjust Hot σ and Cold σ with the live preview until you're happy with how it handles hot pixels without eating star cores. “Save σ Settings” writes the values back here so calibration uses them.
5.5 Satellite trail masking
- Remove Satellite Trails During Calibration — after calibration, a CNN finds satellite trails and clips affected pixels to zero. Those pixels are then treated as no-data and excluded during integration, so trails are rejected cleanly without leaving residual bands. Because the trail is baked into the frame, it stays perfectly aligned through registration.
5.6 Overrides & context
- Override Dark Frame, Override Flat Frame — file pickers that force a specific master for selected leaves (or every leaf in a selected exposure/filter group).
- π Calibrate Light Frames π — the big orange button. Runs the two-phase GPU pipeline (see §9.1).
- Right-click a light — same three items as the Overrides buttons plus Set Session Tag…. Can be applied to a whole exposure group at once.
6. Image Integration
The back half of the pipeline. The tree lists calibrated lights ready for registration + integration; the row stack below configures tolerances, output cropping, drizzle, registration sets, MFDeconv, comet/trail modes, GPU backend and the big red launch buttons.
6.1 Calibrated Light Frames tree
Four columns: Filter – Exposure – Size, Metadata, Drizzle (per-group indicator), Set (registration-set membership). Grouping follows filter + (exposure ± tolerance) + size, with the tolerances set below.
- Add Light Files — adds calibrated
_c.fit files directly; useful if you pre-calibrated outside the suite.
- Remove Selected — drop frames from this run only.
6.2 Tolerance & output row
- Exposure Tolerance (sec) — 0–900, step 5. 0 = exact match.
- Gain Tolerance — 0–500, step 5 (default 10). 0 = exact match; 500 = ignore gain. Keeps high-gain and low-gain subs from being stacked together by accident.
- Auto-crop output + Coverage % — crops the final image to pixels covered by at least this fraction of frames (default 95%). Prevents the ragged, dark-cornered borders you get from registered mosaics.
- Plate solve master(s) before saving — runs the Gaia DR3 → ASTAP → Astrometry.net waterfall on the final master and merges the WCS into the saved FITS header.
- Split dual-band OSC before integration — splits one-shot narrowband data into channels (SII/OIII → R=SII, G=OIII; Hα/OIII → R=Hα, G=OIII). Produces two mono masters per group.
- Advanced Dual NB Split (matrix) — plate-solves a few subs per dual-band filter, fits an empirical NBExtract mixing matrix, and applies it per-frame instead of the naive R→Hα/G→OIII assignment. Only applies to non-drizzle groups.
6.3 Drizzle controls
- Enable Drizzle — master toggle.
- Scale — 1×, 2×, 3× output grid.
- Drop Shrink — the pixfrac, 0–1.0 (default 1.0). Smaller values give sharper output at the cost of noise and empty pixels.
- CFA Drizzle — maps R/G/B CFA samples directly into channels (no interpolation). Requires Enable Drizzle; the suite's native way to drizzle one-shot colour.
- Debayer — VNG (highest quality, slower) / Edge-aware / Bilinear / Strict CFA. Only used for non-drizzle OSC integration; ignored when CFA Drizzle is on.
6.4 Reference frame
- Select Reference Frame — manual pick, locks the reference until you unlock.
- Auto-accept measured reference — the best-scoring frame (by the weighting criteria in §9.5) is used automatically. Checking this disables the manual Select button and unlocks any previously locked reference.
6.5 Registration Sets
A full subsystem for independent references — use it for mosaic panels or completely different targets loaded in one run. See §9.9 for the full logic. Controls:
- Active set dropdown + New Set… — create a named set.
- Assign Selected → Set — moves the highlighted frames (or whole groups) into the active set.
- Reset Selected → Default — unassigns.
- Filename keyword + Assign from filenames — looks in each filename for the keyword (default:
Panel) followed by a separator (space, underscore, hyphen, or dot) and a tag — e.g. Panel 1, Panel_2, Panel-3 → three sets. Unmatched frames stay in Default.
- Reference for active set — Pick… (manual) / Auto (best in set).
Each registration set integrates separately into its own master, with its own reference. Unassigned frames use the Default set and the global reference above. “Already registered” precheck decisions are reused across every set in a single multi-set run so you only get prompted once.
6.6 MFDeconv — Multi-Frame Deconvolution
SASpro's multi-frame deconvolution engine, implementing the ImageMM method of Sukurdeep et al. (2025) with original engineering extensions for real astrophotography data — most notably a rejection-aware MM update that reuses the stacking rejection maps to exclude satellite trails, aircraft, transient defects and hot-pixel outliers from the per-iteration updates (this is exactly what the Rejection Strength control blends).
Implementation whitepaper — Franklin Marek (2026):
“ImageMM: Multi-Frame PSF-Aware Deconvolution — Practical Notes & Implementation Diagram”, Zenodo working paper, DOI 10.5281/zenodo.17252270 (GPL v3). Documents SASpro's forward model, MM update, super-resolution path, and the rejection-aware extension that is original to this implementation.
Controls (see §9.8 for the maths):
- Enable MFDeconv during integration — master toggle.
- Super Resolution + × factor — reconstruct on an r× super-res grid using SR PSFs. Compute grows roughly as r^4, so 2× is practical, 3× is heavy, 4× is heroic. Drizzle usually gives better results per minute.
- Save intermediate iterative images — dumps the seed and every iteration into a subfolder next to the final output. Essential for diagnosing bad κ/δ.
- Iterations (max), Min iters — iteration bounds.
- Update clip (κ) — limits per-iteration multiplicative step. κ=1 means no change; 1.05–1.5 is conservative, ~2 is punchier. Tooltip walks through this.
- Color mode — PerChannel (RGB independently) or Luma (luminance only).
- ρ (loss) — Huber or L2.
- Huber δ — cutoff between quadratic and linear regimes. Negative values mean scale×RMS (so δ = −2 uses 2×RMS); smaller |δ| → more pixels counted as outliers.
- Rejection Strength — 0–1 blend between no-rejection and full-rejection endpoint solves (intermediate values run both and are therefore slower).
- Auto Star Mask, Auto Noise Map — session-only toggles (always default to on). Star masks protect stellar cores from ringing; the variance map (noise map) weights the data term per pixel.
6.7 Comet + Star-Trail row
- π Create comet stack (comet-aligned) — registers on the comet nucleus instead of stars. Mutually exclusive with Drizzle, MFDeconv and Star-Trail.
- β
β
Star-Trail Mode β
β
(Max-Value Stack) — skip registration/alignment and use Maximum-Intensity projection for star trails. Mutually exclusive with the others.
6.8 Comet options row
- Pick comet center… — opens the reference frame and lets you click the nucleus. Enabled only when comet mode is on.
- Also output Stars+Comet blend — produces a feathered blend of the star-aligned and comet-aligned stacks alongside both masters.
- Mix — 0–1 opacity of the comet layer in the blend (default 1.0).
- Save all starless comet-aligned frames — keeps every intermediate starless frame for inspection. Requires comet star removal to be enabled in Settings (§7.12), otherwise greyed with a hover reason.
6.9 Output options
Save rejection layers with master lights — when on (default), master light FITS files are saved as multi-extension FITS (MEF) with extra HDUs containing the rejection any-mask, fraction map, and count map — useful for post-mortem analysis and for feeding back into MFDeconv. Off = smaller single-HDU FITS.
6.10 Backend row
- Backend: … — shows the currently detected acceleration backend (CUDA, MPS, DirectML, or CPU).
- Install/Update GPU Acceleration… — downloads PyTorch with the right backend for your hardware. One-time per machine; needs supported Python (3.12/3.13/3.14).
- ? — help popup for “GPU still not being used” troubleshooting steps.
6.11 Action buttons
- π₯π Register and Integrate Images π₯π — the full back-half: register every group against its reference, normalize, reject, weight, integrate, and save.
- Skip Registration and Integrate — integrate only. Expects
_r.fit registered frames to already exist (from a previous run). Great for re-running integration with different rejection settings.
- ποΈ Clean Up Temp Files — same cleanup action as on the Quick Stack tab.
7. Stacking Settings dialog (π§ wrench)
Two scrollable columns. Every parameter is live-persisted to QSettings (stacking/… keys), and OK triggers save_stacking_settings for a few derived fields. Cancel reverts the dialog's uncommitted changes but keeps anything already persisted mid-session.
7.1 Profiles (top strip)
Snapshot/restore your entire stacking configuration under named profiles. New… saves the current state under a new name; Save overwrites the selected profile; Load replaces the current configuration (and restarts the Stacking Suite dialog to force every control to re-sync); Delete removes a profile; Export… / Import… round-trip profiles via JSON. See §8 for the detail.
7.2 Distortion / Transform
- Model — Affine (fast), No Distortion (rotate/translate/scale), Homography (projective), Polynomial 3rd-order, Polynomial 4th-order.
- Max control points — 20–2000 (default 250). Caps the astroalign triangle set.
- Solve grid downsample — 1–8 (default 3). Lower is finer but slower — try 2 if you see double stars / bimodal registration error on high-resolution sensors, especially with meridian-flipped frames.
- RANSAC reproj (px) — 0.1–10 (default 3.0). Only used for Homography and No-Distortion models.
7.3 Alignment
- Refinement — Fast (1 pass) or Accurate (3 passes).
- Accept tolerance (px) — 0.05–5.0 (default 0.2). Residual the solver must get under for success.
- Accept max shift (px) — 0–50 (default 2.0). Reject frames whose residual shift exceeds this after alignment.
- Star detect σ — 0.5–50 (default 20). Detection threshold above background.
- Auto threshold (fit Max stars on reference) — tries SEP thresholds 50→25→12→6→3σ on the reference and picks the lowest that does not exceed Max stars. Prevents oversaturation in dense fields.
- Min star area (px) — connected-pixel floor (default 10). Rejects hot pixels and noise.
- Min star FWHM (px) — 0.5–5.0 (default 1.2). Hot pixels are typically <1 px FWHM.
- Max ellipticity — 0.1–1.0 (default 0.6). Rejects cosmic rays and satellite streaks (0 = round, 1 = line).
- Max stars — 50–5000 (default 100). Astroalign
max_control_points cap.
7.4 Performance
- Use hardware acceleration if available — master GPU toggle. Falls back to CPU automatically when unsupported.
- MFDeconv Engine — three radios: Normal, Normal (cuDNN-free) (fallback for older CUDA stacks), High-Octane (Let 'er rip) for maximum throughput.
- Detected backend — read-only readout.
7.5 Normalization & Gradient (ABE Poly²)
Optional 2nd-order background removal applied to every frame before stacking. See §9.3.
- Remove background gradient (ABE Poly²) — master toggle (default off).
- Mode — Subtract (additive) for sky-glow / amp-glow residuals; Divide (flat-like) for multiplicative illumination issues.
- Sample points — 20–600 (default 120) ABE-style background samples.
- Downsample (AREA) — 1–16 (default 6). Scales the frame down before fitting to speed things up and smooth out small structure.
- Patch size (small) — 5–51 odd (default 15). Local median window inside each sample.
- Skip if strength < — 0–0.20 (default 0.01). Range of the fitted model required to apply it; keeps noiseless skies untouched.
- Divide gain limits — lo/hi clip for the division gain map (default 0.20 / 5.0). Division mode only.
7.6 Drizzle (advanced)
- Kernel — Square (pixfrac), Circular (disk), Gaussian.
- Kernel width — pixfrac/radius, 0–1 (default 1.0).
- Gaussian σ (px) — 0.05–3.0, enabled only when Gaussian kernel is selected.
7.7 Multi-frame Deconvolution (tuning)
- Seed image — Integrated prepass image uses the already rejection-cleaned master; Robust μ–σ (live stack) is a running mean with sigma clipping; Median (Sukhdeep et al.) is a tiled median stack.
- Star mask block — Star detect σ, Dilate (+px), Feather σ (px), Max star radius (px), Max stars kept, Keep-floor (0=hard mask, 0.2=gentle), Ellipse scale.
- Variance map block — VarMap sample stride, VarMap smooth σ, VarMap floor (log10, e.g. −8 = 1e−8 DN²).
- Reset MFDeconv to Recommended — restores a known-good seed+mask+varmap set in one click.
7.8 Rejection
- Algorithm — 9 options:
- Weighted Winsorized Sigma Clipping (default — sigma thresholds only)
- Kappa-Sigma Clipping (sigma + kappa + iterations)
- Simple Average (No Rejection)
- Simple Median (No Rejection)
- Trimmed Mean (trim fraction)
- Extreme Studentized Deviate (ESD) (threshold)
- Biweight Estimator (tuning constant)
- Modified Z-Score Clipping (threshold)
- Max Value (star-trail mode internally)
Only the parameters relevant to your choice are shown.
- Frame weighting — Balanced, PSF-weighted, SNR-weighted, Star-count-weighted. See §9.5 for how each shifts the term exponents.
- Sigma thresholds — High / Low (0.1–10 each, default 3.0 / 2.0). Used by all sigma-based algorithms.
- Kappa — 0.1–10 (default 2.5). Used by Kappa-Sigma.
- Iterations — 1–10 (default 3).
- ESD threshold, Biweight constant, Trim fraction, Modified Z threshold — one row each, enabled according to the chosen algorithm.
7.9 Cosmetic Correction (Advanced)
- Enable advanced cosmetic tuning — unlocks the hot/cold sigma spinboxes below. Off = defaults apply.
- Hot σ, Cold σ — 0.1–20 (default 5.0 each).
- Protect σ — always applied, regardless of the advanced toggle. 2–50 (default 5.0). Patches whose local median sits this many σ above background are treated as real signal (star / nebula cores) and left untouched by the hot pass. A lone hot pixel can't move a median, so it's still corrected. Lower = protect more; higher = correct more aggressively. Set very high to disable protection.
- Restore Recommended — one-click defaults.
7.10 Comet (High-Clip Percentile tuning)
High-clip k / Percentile p — parameters of the High-Clip Percentile reducer used for comet nucleus stacks (defaults 1.30 / 25.0). See _high_clip_percentile in §9.6.
7.11 Comet Star Removal (Optional)
- Remove stars on comet-aligned frames — master toggle.
- Tool — StarNet, Cosmic Clarity DarkStar, SyQon (NAFNet), StarXTerminator.
- Protect core radius (px) — keep a disc around the nucleus from being “destarred” (default 22).
- Core mask feather (px) — soften the edge of that disc (default 6).
8. Profiles
Profiles exist to let you save a complete working configuration — sigma thresholds, weighting mode, drizzle, MFDeconv tuning, cosmetic sigmas, every setting in §7 — under a name, and bring it back later.
- Changes made in the Settings dialog are saved live to QSettings. “Loading” a profile overwrites those keys and restarts the Stacking Suite dialog so every control re-reads them. Expect a brief flicker as the dialog re-opens.
- Built-in profiles are seeded on first run (see
_ensure_builtin_stacking_profiles). Your own override anything built-in with the same name.
- Export writes a JSON file with just the shareable keys (nothing machine-specific like absolute paths). Import validates and merges; incompatible or old-format keys are mapped forward where possible.
9. Technical details
What the suite actually does under the hood, in the order a stack passes through it.
9.1 Calibration pipeline
Calibration is a two-phase pipelined worker system (calibrate_lights):
Phase 0 — master upload. Every master dark, master flat and master bias is loaded to CPU, normalized where needed, and uploaded to the GPU exactly once. Interactive flat-strength dialogs (if enabled) are collected upfront so you can configure and walk away.
Phase 1 — three-thread pipeline. A producer thread reads raw lights from disk; a GPU thread runs dark subtraction, flat division and cosmetic correction; a consumer thread writes calibrated frames to Calibrated/. The three run concurrently so I/O, compute and I/O overlap. Per frame:
P → L' = \mathrm{clip}(L + P - D_{\mathrm{scaled}},\;0,\;\infty)
C = L' / F_{\mathrm{norm}}
where P is the pedestal (if enabled), D is the chosen master dark (bias-scaled when a master bias is loaded), and F_norm is the normalized master flat (median = 1 per channel). With interactive flat strength on, the effective flat is
F_{\mathrm{eff}} = 1 + \alpha(F_{\mathrm{norm}} - 1)
with your chosen α. Cosmetic correction then replaces hot and cold outliers with a 3×3 ring median using a MAD-based sigma estimate:
\sigma = 1.4826 \cdot \mathrm{MAD} = 1.4826 \cdot \mathrm{median}(|x - \mathrm{median}(x)|)
x_i \to \mathrm{median}(\mathrm{ring}_i)\quad \mathrm{if}\;|x_i - m_i| > k \cdot \sigma_i
with k taken from Hot σ / Cold σ. Patches above the protect_sigma threshold are left untouched (star / nebula cores). The GPU uses unfold for the 3×3 neighbourhoods; the Numba fallback handles cold-cluster guards.
Phase 2 — satellite trail removal. After Phase 1 fully completes, every master tensor and cosmetic buffer is freed and the CUDA cache emptied, then the satellite CNN runs on the saved frames one at a time. Separate phases mean the CNN never competes with the calibration working set for VRAM — critical on small cards where GPU memory cannot page to host.
9.2 Master-frame integration
Master darks group by (exposure, size, temperature bucket, gain, offset); master flats by (filter, size, session). Each group is read in row-band tiles sized for your RAM budget, stacked with the Calibration Rejection Algorithm (Winsorized Sigma Clipping by default), and written as 32-bit FITS with a header merged from the input group's representative frame.
Temperature bucketing:
T_{\mathrm{bucket}} = \mathrm{round}(T / \Delta T) \cdot \Delta T
with ΔT = 1°C by default (stacking/temp_group_step). Bias scaling for CCD users:
B^{*} = B + (\mathrm{median}(L) - \mathrm{median}(B))
9.3 Normalization & gradient
Before registration, each frame is normalized to a common target median. The method is min-offset + median-match on the luminance channel, with no clipping:
L = 0.2126\,R + 0.7152\,G + 0.0722\,B
f' = (f - \min L)\cdot \frac{m^{*}}{\mathrm{median}(L) - \min L}
Rec. 601 luminance for mono treatment of RGB, so colour balance is preserved and only the overall level is matched.
Optional ABE-style poly² gradient removal fits the model
G(x,y) = a + bx + cy + dx^2 + exy + fy^2
to robustly selected background sample points on a downsampled frame, then either subtracts or divides it back on the full resolution. The “Skip if strength <” guard compares peak-to-peak of G against the frame's robust noise so clean skies pass through untouched.
9.4 Registration
Reference selection: either you lock a frame (Pick… / Select Reference Frame), or the suite measures every candidate by its combined quality score (see §9.5) and picks the best. Reference lock survives tab changes; auto-accept unlocks it.
Each non-reference frame is solved against the reference with astroalign-style triangle matching on detected stars (SEP, with filters: min area, min FWHM, max ellipticity), then refined by the chosen transform model:
Accurate mode re-runs the solve 3 times with the previous solution as warm start; shift tolerance and max shift gate acceptance. Rejected frames are logged and skipped.
9.5 Frame weighting
Every frame gets a weight from five measured terms — detected star count (c), mean eccentricity (ε), background level (bg), FWHM proxy, and MAD-based noise estimate — combined multiplicatively with per-mode exponents:
w = c^{a_c}\,(1 - \varepsilon)^{a_e}\,b^{a_b}\,s^{a_s}\,n^{a_n}
with
b = \frac{2}{\sqrt{\mathrm{bg}} + 1} - 1
s = \mathrm{clip}\!\left(\frac{\mathrm{FWHM}_{\mathrm{ref}}}{\mathrm{FWHM}},\;\frac{1}{3},\;3\right)
n = \mathrm{clip}\!\left(\frac{\mathrm{noise}_{\mathrm{ref}}}{\mathrm{noise}},\;\frac{1}{3},\;3\right)
The modes change only the exponents (as):
| Mode | count | ecc | bg | sharp | snr |
| Balanced (default) | 1.0 | 1.0 | 1.0 | 1.0 | 0.0 |
| PSF-weighted | 0.5 | 1.0 | 0.5 | 2.0 | 0.0 |
| SNR-weighted | 0.5 | 0.5 | 1.0 | 0.5 | 2.0 |
| Star-count-weighted | 2.0 | 0.5 | 0.5 | 0.5 | 0.0 |
The sharpness and SNR ratios are measured against the population median and clamped to [1/3, 3] so a single mismeasured frame can't dominate. A frame measured far tighter than the population (ratio < 0.4× or < 0.25×) is treated as a mismeasurement and set neutral.
9.6 Rejection algorithms
Rejection runs per output pixel across the registered stack, honouring per-frame weights where applicable. All reducers are implemented in NumPy (CPU) and PyTorch (GPU; cuDNN-free fallback and DirectML workarounds included). Each writes a 3-layer rejection map alongside its scalar output: any-mask, fraction (fraction of frames rejected), count (total frames rejected). These layers can be saved into master lights as additional FITS HDUs.
Weighted Winsorized Sigma Clipping (default)
The Pix Insight / APP style: seed with median and robust sigma, Winsorize at k sigma, re-measure, iterate:
\mu_{\mathrm{win}} = \mathrm{mean}(\mathrm{clip}(x,\,\mu - k\sigma,\,\mu + k\sigma))
\sigma_{\mathrm{winsor}} = 1.134\,\sqrt{\mathrm{Var}(x_{\mathrm{clipped}})}
The 1.134 factor is Huber's exact correction for k = 1.5. Outliers (satellite trails) cannot inflate σ, so the rejection bounds track the clean distribution; final output is the weighted mean of survivors only.
Kappa-Sigma Clipping
\mathrm{keep}_i = (\mu - \kappa\sigma \leq x_i \leq \mu + \kappa\sigma)
Median center, std of the kept sample, monotone shrink (rejected pixels stay rejected across iterations), weighted mean of survivors.
Extreme Studentized Deviate (ESD)
Generalized ESD (Rosner), externally studentized, vectorized per pixel. Sequentially removes the most extreme remaining value and tests it against the leave-one-out mean/std of the remaining sample (defeats masking); no early stop — the outlier count is the largest step whose test passed. The critical value adjusts for sample size:
R = \frac{|x_i - \overline{x}|}{s},\quad R > \lambda_{n,\alpha}
\lambda_{n,\alpha} = t_{n-2,\,1-\alpha}
so your threshold keeps its literal “sigmas” meaning on large stacks and inflates automatically on small ones (controls the false-rejection cascade a fixed cutoff would cause).
Biweight Estimator (Tukey)
\hat{\mu} = M + \frac{\sum_{|u_i|<1} (x_i - M)(1-u_i^2)^2}{\sum_{|u_i|<1}(1-u_i^2)^2},\quad u_i = \frac{x_i - M}{c\,\mathrm{MAD}}
with c from the tuning-constant setting. Smooth down-weighting of outliers, no hard clip.
Trimmed Mean
\overline{x}_{\alpha} = \frac{1}{N - 2k} \sum_{i=k+1}^{N-k} x_{(i)},\quad k = \lfloor \alpha N \rfloor
Drops the lowest and highest αN frames at each pixel before weighted averaging.
Modified Z-Score Clipping (Iglewicz & Hoaglin)
M_i = \frac{0.6745\,(x_i - \mathrm{median}(x))}{\mathrm{MAD}}
Rejects where |M_i| exceeds the threshold. Robust to small samples where mean/std fail.
Simple Average, Simple Median, Max Value
No rejection. Max Value is the engine behind Star-Trail mode: per-pixel maximum across all frames.
Comet reducers
Specialised for comet-aligned stacks where stars become trails that must be rejected: Comet Median, Comet High-Clip Percentile (parameters k and p in §7.10), Comet Lower-Trim (30%), Comet Percentile (40th). The high-clip percentile combines a percentile-centre with a sigma-style cap:
P_{40}(x_{xy}) = \mathrm{quantile}_{0.40}(x_{xy})
\mathrm{keep}: x_i \leq \mu + k\,|x_i - P_p|
9.7 Drizzle
Variable-pixel linear reconstruction, after Fruchter & Hook (2002):
I'(X,Y) = \frac{\sum_i w_i\,A_i(X,Y)\,I_i(x_i,y_i)}{\sum_i w_i\,A_i(X,Y)}
where each input pixel is “drizzled” onto the output grid as a kernel of area A_i(X,Y) centred on the warped position. The kernel is:
- Square (pixfrac) — a square of side
p × s, where p is the Drop Shrink (pixfrac, 0–1) and s is the pixel size in output units. p = 1 is PixInsight's “drizzle at native scale”; p < 1 sharpens at the cost of empty-pixel noise.
- Circular (disk) — a disc of the same radius; smoother than square for small datasets.
- Gaussian — a Gaussian with user-set σ; smoothest, but weakest resolution gain.
Scale 2× / 3× means the output grid has 4× / 9× the pixels of the input. With CFA Drizzle on, R/G/B samples from the Bayer pattern are drizzled directly into the three output channels — no interpolation step, which is the only way to gain resolution from one-shot colour.
9.8 Multi-frame deconvolution
ImageMM — a majorization-minimization (MM) framework for joint multi-frame restoration and super-resolution (Sukurdeep et al. 2025). The latent sky image I is recovered by iteratively minimising a surrogate of the robust data fit across all frames, with per-frame PSFs H_j and per-pixel noise variances v_j. The multiplicative update, with your κ cap and ρ loss:
I^{(k+1)} = I^{(k)} \cdot \mathrm{clip}\!\left(\frac{\sum_j H_j^{T}\!\left(\rho'(d_j)/v_j\right)}{\sum_j H_j^{T}(1/v_j)},\;\frac{1}{\kappa},\;\kappa\right)
where H_j is the forward operator (blur + warp + down-sample) for frame j, d_j = D_j - H_j I^{(k)} is the data residual, and v_j is the per-pixel variance:
v_j(x,y) = g\,I_j(x,y) + \sigma_r^2
Huber loss (L2 if you prefer the quadratic regime everywhere):
rho(d) =
(1/2) d^2 if |d| <= delta
delta|d| - (1/2) delta^2 if |d| > delta
Negative δ means scale×RMS (so δ = −2 uses 2×RMS as the cutoff). Lower |δ| → more pixels treated as outliers.
Star masks protect stellar cores from ringing by clamping update ratios near 1:
M = 1 - \mathrm{keep\_floor} \cdot (1 - M_{\mathrm{raw}})
Super-resolution reconstructs I on an r× grid; each H_j then includes an r× down-sampling step. Cost scales roughly as r^4.
Rejection-aware MM — SASpro extension (Marek 2026, DOI 10.5281/zenodo.17252270). Before MFDeconv runs, the suite performs its usual robust integration prepass and keeps the per-frame rejection maps that would normally live in the master light's extra FITS HDUs (any-mask, fraction, count — see §9.6 and §6.9). Those rejected pixels — satellite trails, aircraft, cosmic rays, transient defects, hot-pixel outliers — are then excluded from the forward operator for the frames that saw them, so the sums Σ_j H_j^T(·) only run over good samples per output pixel. The Rejection Strength control (§6.6) blends between the rejection-ignoring solve (0.0) and the fully rejection-aware solve (1.0); intermediate values run both endpoints and interpolate the final image. This is why the suite recommends leaving Save rejection layers with master lights on when you plan to run MFDeconv — those layers are the extension's input.
9.9 Registration Sets
A set is an orthogonal partition that sits above the filter / exposure grouping. Each set has its own reference and integrates to its own master. The queue runs sets serially, buffering the per-set summaries and flushing them as a single run report; the “already registered” precheck choice is remembered across all sets in one run so you aren't prompted repeatedly.
Filename-based assignment uses a regex equivalent to
keyword[\s_\-.]([^\s_\-.]+)
so M31_Panel 1_2024-01-02.fit, M31_Panel_02.fit and M31.Panel.3.fit all match. Unmatched frames stay in the Default set, which keeps the global reference.
9.10 GPU backends
The Stacking Suite shares one PyTorch runtime (installed via Install/Update GPU Acceleration…) across every heavy stage: calibration, cosmetic correction, satellite CNN, rejection reducers, MFDeconv. Backend auto-detection picks the first available of CUDA, MPS (Apple Silicon), DirectML (Windows GPUs via torch-directml), falling back to CPU. The stacking/use_hardware_accel master toggle disables the GPU path app-wide.
DirectML needs a workaround for a known torch-directml bug: nanmedian/nanquantile/nanstd can return empty tensors, so those reductions are forced to CPU and the results moved back to device (one-time warning per session). CUDA uses PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to reduce fragmentation between the cosmetic pass and the satellite CNN; a device-wide RLock serialises the two so they don't both resident at once (which OOMs small cards on Linux/CUDA where VRAM cannot page).
Supported Python runtimes for GPU install: 3.12, 3.13, 3.14. The installer refuses older/newer versions with platform-specific tips.
9.11 File layout & cleanup
Inside your chosen stacking folder the suite creates:
| Folder | Contents |
Masters/ | Master darks, flats, biases — keep these. |
Calibrated/ | Calibrated lights (*_c.fit). Delete after integration if disk is tight. |
Normalized_Images/ | Normalized, pre-registration frames. Intermediate; always deletable. |
Aligned_Images/ | Registered *_r.fit frames. Keep if you plan to re-integrate with different rejection. |
loose .dat / .npy / .tmp | Memmap scratch, transform caches, star catalogues. Always deletable. |
stacking_log.txt | Timestamped log of every run. |
Both Clean Up buttons remove the intermediate folders and loose scratch files, but never Masters/ or Calibrated/.
Credits. SASpro's Stacking Suite is written and maintained by
Franklin Marek (SetiAstro). It integrates many standard astronomy-image-processing conventions, which are credited to their originators here.
- Winsorized Sigma Clipping — after Huber (robust statistics). Reduction formulation familiar to users of PixInsight's ImageIntegration and Astro Pixel Processor.
- Kappa-Sigma Clipping — standard astronomy convention.
- Extreme Studentized Deviate (ESD) — Rosner, B. (1983). Percentage points for a generalized ESD many-outlier procedure. Technometrics 25(2), 165–172.
- Biweight Estimator — Tukey, J. (1977). Exploratory Data Analysis.
- Modified Z-Score — Iglewicz & Hoaglin (1993). How to Detect and Handle Outliers.
- Drizzle — Fruchter, A. S. & Hook, R. N. (2002). Drizzle: A Method for the Linear Reconstruction of Undersampled Images. PASP 114, 144–152.
- Multi-frame deconvolution (ImageMM) — method of Sukurdeep, Y., BudavΓ‘ri, T., Connolly, A. J. & Navarro, F. (2025). ImageMM: Joint multi-frame image restoration and super-resolution, arXiv:2501.03002. SASpro's PyTorch implementation, PSF handling, star-mask floor, variance-map tuning, super-resolution path, and the rejection-aware extension are documented in Marek, F. (2026). ImageMM: Multi-Frame PSF-Aware Deconvolution — Practical Notes & Implementation Diagram, Zenodo working paper, DOI
10.5281/zenodo.17252270 (GPL v3).
- ABE Poly² gradient model — follows the Automatic Background Extraction conventions popularised by PixInsight; SASpro's sample-point selector, downsampled fit and division-mode gain clip are original.
- Rec. 601 luminance weights (0.2126, 0.7152, 0.0722) — standard colorimetric convention.
- Astroalign triangle matching — Beroiz, M. (2020). SASpro supplies the pre-filter (SEP + ellipticity/FWHM gating), transform selection, and multi-pass refinement.
- SEP (Source Extractor on Python) — Barbary, K. (2016), based on Bertin & Arnouts (1996). Used for star detection in reference auto-pick and alignment.
- Satellite trail CNN — third-party CNN packaged with SASpro's runtime; invoked here as a post-calibration pass.
- Spectrophotometric Standard Star Calibration work that informs the plate-solve-first master handling — Franklin Marek, DOI
10.5281/zenodo.23107662 (CC-BY 4.0).
Pipeline design, the quick-stack card UI, the shared GPU runtime, the three-thread calibration pipeline, Registration Sets, dual-band advanced split, the frame-weighting modes, and the per-group dark/flat overrides are original to this suite.