Command-line reference

Every option of originstack.py, version 2.24.0, generated from the program's own definitions, so it matches what --help prints. The desktop app shows the same options under Additional options, with shorter labels.

Core Frames & calibration (Phase 1) Registration & stacking (Phases 2-3) Post-processing (Phase 4) Output, preview & plate solving Multi-session, merge & checkpoint Comet mode Diagnostics & debugging combine

Usage

python originstack.py -d LIGHTS_FOLDER -o OUTPUT.fits [options]
python originstack.py --from-stack STACK.fits -o NEW.fits [options]
python originstack.py combine --mode sho --ha Ha.fits --oiii OIII.fits --sii SII.fits -o sho.fits

Most runs need only -d: the target is detected and the settings tuned for it (--auto, on by default). An option you give always wins over the tuning. Every run saves its effective settings as OUTPUT_config.toml; --config applies a saved file again.

Core

-d, --directory DIRECTORY

Session folder of light frames (required unless --from-stack).

-o, --output OUTPUT

Output FITS path, or a folder (then <session>_stacked.fits and its .jpg are generated inside it, never overwriting an existing pair). Default: <directory>_stacked.fits

--cal-dir PATH

Directory containing calibration frames (darks, flats, bias). Frames are classified automatically and the best-matching subset is selected per type (by ISO, CCD temperature, exposure, filter, and sensor dimensions). Supplements any calibration frames already present in --directory.

--vignette-map PATH

Per-instrument vignetting/background calibration map (FITS, H x W x 3) built offline by tools/build_vignette_map.py from many past sessions of this same telescope+camera. Subtracted per-frame right after debayer, before registration -- reduces the gradient DBE/--bg-method wavelet has to guess at from one session's sparse sky samples alone.

--health-check

Analyse input frames and calibration quality without stacking

--quality-sweep

Walk the folder tree under -d, score every light frame with the same quality gate stacking uses (hard failures, statistical outliers, score below --quality-threshold% of each folder's reference), and report poor frames. Dry run by default; add --apply to rename flagged files to *.fits.rejected (invisible to stacking, reversible with --sweep-undo).

--apply

With --quality-sweep: actually rename flagged files (default is a dry-run report)

--sweep-undo

Recursively strip the .rejected suffix applied by --quality-sweep --apply, restoring all flagged files

--sweep-no-cache

With --quality-sweep: ignore and do not write the .sweepcache.json score cache (by default unchanged frames are served from it, making re-sweeps fast)

--live

Real-time (live) stacking: watch the directory and fold each new sub into a running stack as it lands, pushing the growing result and a running SNR to whatever UI is attached (console-only on a plain CLI run; the desktop app shows it live). Runs until Ctrl-C.

--live-interval SEC

Live stacking directory poll interval in seconds (default: 4).

Default: 4.0

--live-duration MIN

Optional live stacking time limit in minutes (default: until Ctrl-C).

--stream

Two-pass streaming stack of an ALREADY-COMPLETE directory: O(1) full-resolution memory via an online (single-pass) sigma-clip Welford accumulator, instead of materializing the whole (N,H,W,C) aligned stack --stack-method needs. v1 limitations vs the default pipeline: reference frame is picked by quality score alone (no shift-centrality blend), hard-limit quality gating only (no statistical/percentile stages), no --elastic-registration, no drizzle, no patch-weighted combine, no --merge. Incompatible with --live (one watches a growing directory forever, the other expects a closed one).

--stream-burnin N

--stream: number of frames MAD-rejected as a batch to seed the running sigma-clip state before streaming begins (default: 10).

Default: 10

--stream-sigma SIGMA

--stream: sigma-clip rejection threshold (default: --rejection-sigma).

--ui-frame-every N

Publish a per-frame thumbnail to the desktop app every Nth processed light in Phase 1 (default: 1; 0 = off). The first frame is always shown. No effect on a plain CLI run (nothing is attached to receive it).

Default: 1

--config PATH|KEY=VALUE

Load parameters from a TOML configuration file, or set one as KEY=VALUE. Repeatable, e.g. --config nn_deconv_blend=0.5. CLI arguments override config file values; KEY=VALUE overrides the file and the automatic settings. Fine-grained tuning parameters removed from the CLI can still be set this way.

--preset

Processing preset. Individual flags still override preset values. quick: fast (mean stack, minimal denoise, no deconvolution). quality: maximum quality (sigma_clip, all denoisers, deconvolution). narrowband: tuned for Ha/OIII/SII data. galaxy: galaxy imaging (GHS stretch, star reduction, bilateral denoise). starfield: star fields (no star reduction, minimal processing). nebula: emission/reflection nebula (GHS stretch, curvelet+ACDNR denoise). planetary: planetary targets (no background extraction, deconvolution). lunar: lunar surface (linear stretch, no star reduction).

Choices: quick, quality, narrowband, galaxy, starfield, nebula, planetary, lunar

--dry-run

Discover and classify frames, show calibration info, print effective parameters, and estimate resource usage — without processing anything

-v, --verbose

--use-gpu

Use CuPy for available operations (experimental)

--frame-store

Where the per-session frame arrays live (tens of GB on long sessions). auto (default): the aligned stack in RAM when it fits (30% of total memory plus room for the workers stays free), the Phase 1 arrays in temp files unless the temp disk would be left short. ram: everything in RAM, no temp files. disk: everything in temp files.

Choices: auto, ram, disk · Default: auto

--no-compress-phase1

Disable automatic/forced lossless compression of Phase 1 frames.

--compress-phase1

Force lossless Rust LZ4 compression of Phase 1 RGB/luminance frames, even when the memory estimate says it is not needed.

--temp-dir DIR

Folder for temporary files (default: the system temp folder). A long session needs a lot of room there -- about 100 MB per light frame plus 70 MB per frame for alignment, so ~145 GB for 850 lights -- so point this at a drive with space. Leftover files from a run that crashed are removed at the next start.

--gpu-phase1

With --use-gpu: where Phase 1 frame processing runs. "auto" (default) keeps it on the CPU process pool unless the card fits at least one GPU worker per CPU core -- only calibration/hot-pixel/white balance run on the GPU, and a VRAM-capped worker count measured slower than all CPU cores. The GPU is still used for later phases (e.g. RL deconvolution).

Choices: auto, on, off · Default: auto

-j, --parallel PARALLEL

Parallel workers for frame processing (default: 0=auto, 1=sequential)

Default: 0

--offline

Make no network requests at all. By default a run may look up a target name it does not recognise on SIMBAD (only the name is sent), and plate solving, catalogue colour calibration, photometry, annotation and comet ephemerides go online when you enable them. With --offline none of those run.

--no-auto

Disable the auto advisor: classify the target after Phase 1 and apply optimised settings automatically (on by default, no API key required). Explicit CLI flags you pass still override whatever the advisor would have picked.

--target-type

What you imaged. --auto uses it in place of the type it would infer from the session, FITS header, folder name or SIMBAD (it still tunes from the measured frames). No effect with --no-auto.

Choices: galaxy, emission_nebula, reflection_nebula, planetary_nebula, globular_cluster, star_field, wide_field

Frames & calibration (Phase 1)

--exclude-frames NAMES

Light frames to leave out of the stack, by file name (comma separated, e.g. light_003.fits,light_017.fits). The desktop app fills this in from its frame-quality graph. Named frames that are not found are ignored.

--master-method

Master bias/dark/flat combine method (default: median). robust_pca: Principal Component Pursuit low-rank + sparse decomposition -- separates the true shared pattern (flat-field vignetting, fixed dark current) from sparse outliers (dust motes that shifted between sessions, transient hot pixels) more explicitly than median's per-pixel order statistic. Needs >= 5 frames per calibration type (falls back to median otherwise); loads the full stack into memory and is slower than median/mean.

Choices: median, mean, robust_pca · Default: median

--dark-temp-model

Fit a per-pixel polynomial of dark signal vs. sensor temperature across the whole dark library, then evaluate it at the lights' own temperature -- instead of select_matching_darks' nearest-temperature selection. Lets a smaller dark library cover a wider range of session temperatures via interpolation rather than requiring a near-exact match. Assumes the dark library is already homogeneous in ISO/gain and exposure time (does not also model those). Needs >= 3 distinct temperatures across the dark library; falls back to select_matching_darks otherwise.

--flat-from-lights

When no flat frames are available, derive a synthetic flat/vignetting map from the light frames themselves via robust-PCA low-rank/sparse decomposition (same decomposition --master-method robust_pca uses for real calibration frames): the low-rank component of the raw light stack is the shared sensor pattern (vignetting, dust), while stars and nebula structure -- sparse and slightly shifted frame-to-frame by dithering -- are separated out as the sparse component. Approximate (a static, undithered target can partially leak into the low-rank component) -- opt-in, not a substitute for real flats when they exist. Needs >= 5 light frames; ignored when dedicated flat frames were discovered.

--no-session-cfa-eq

Re-measure the G1/G2 gain and 2x2 green offsets on every frame (6 sigma-clipped medians each) instead of measuring them once per session (>= 12 light frames, Malvar debayer). The per-session values are a property of the sensor and are less noisy than a per-frame estimate; Debayer runs ~2.4x faster.

--no-quality-filter

Disable automatic rejection of the lowest-quality frames

--quality-threshold QUALITY_THRESHOLD

Reject frames whose score falls more than this percent below the reference score (90th-percentile of the session, default: 50). E.g. 50 keeps every frame with score >= 50% of the reference. Use --no-quality-filter to disable entirely.

Default: 50.0

--debayer-method

Debayering method (all native Rust kernels, no external dependency). rcd (default; Ratio Corrected Demosaicing, also Siril's default): on a real 158-frame session its stack had 5/15/12% less R/G/B noise than malvar at 1.6% wider stars and lower interpolation error on stars; ~1.1 s/frame vs malvar's 0.2. malvar (Malvar-He-Cutler, the default before 2026-10): fastest, and the only one with session-constant CFA equalisation. menon2007 (DDFAPD directional filtering): higher fidelity on fine periodic detail, slower.

Choices: rcd, malvar, menon2007 · Default: rcd

--white-balance

Choices: none, grayworld, whitepatch · Default: grayworld

--no-bayer-autodetect

Disable Bayer row-orientation autodetection (see autodetect_bayer_orientation in src/debayer.py): some capture software writes a BAYERPAT header that does not match the actual row orientation of the pixel data, and by default the session's reference frame is checked once and corrected if the G1/G2 green sub-pixel imbalance exceeds the sensor-noise range. Disable if this heuristic misfires on a legitimately imbalanced sensor/target.

--no-ca-correction

Disable chromatic aberration correction

--trail-reject

Detect and erase satellite/aircraft trails per frame before stacking (Hough line detection + local-background inpaint). Robust even at low frame counts where sigma-clip cannot reject a trail seen in only one or two subs.

--spike-reject

Replace sharp 1-2 pixel spikes (cosmic rays, residual hot pixels) on the calibrated mosaic before debayering, using noise measured from each frame. Much cheaper than --cosmic-ray-rejection and leaves ordinary sky/star pixels untouched.

--no-cosmic-ray-rejection

Disable per-frame cosmic ray rejection (L.A.Cosmic)

--cosmic-ray-rejection

Force per-frame cosmic ray rejection even on deep stacks (default: automatic — skipped when >=20 frames are stacked with a rejection method, which removes cosmic rays per-pixel)

Registration & stacking (Phases 2-3)

--no-proper-coadd

Turn off proper image coaddition (Zackay & Ofek 2017), on by default: after the normal stack is built, the same frames are recombined weighting each spatial frequency by each frame's own measured PSF, transparency and noise, so the sharpest frames carry the fine detail (outliers are first replaced against the normal stack). Measured vs the plain stack on three real sessions: stars 1.3-2.7% narrower, noise equal or lower; costs ~15-40 s. Falls back to the normal stack when no PSF can be measured.

--gpu-coadd

Run the combine pass of the proper coadd on the GPU (Vulkan, DX12 or Metal through wgpu; any vendor). Opt-in: needs astro_native 0.66 or later (see dev-notes/gpu-coadd.md), a GPU with about 1 GB free, and frames of at most 4064 px with 3 channels. On a 206-frame session the pass took 9 s instead of 17 s and the stack differed from the CPU stack by about 1e-5 of its noise (float32 on the GPU: close, not bit-identical). Falls back to the CPU pass, with a note, when the GPU is missing, integrated (slower than the CPU on a real session) or fails. On a laptop use a high-performance power plan (Windows "Balanced" held an RTX 3050 at a 210 MHz compute clock, 2.5x slower than the CPU); a GPU that proves slower than the CPU during the pass is dropped.

--full-field [FRAC]

Keep the field outside the common crop (on by default; --full-field 0 or --no-full-field turns it off). The common crop is the rectangle EVERY frame covers, which dithering, drift and field rotation shrink; this extends it to the largest rectangle in which every pixel is covered by at least FRAC of the frames (default 0.5), combining each outer pixel from the frames that cover it. Depth is not uniform: edges are noisier (<output>_coverage.fits records the per-pixel frame count). Skipped with --drizzle-scale > 1, --cfa-drizzle, --elastic-registration, --comet-mode or --uncertainty-map.

Default: 0.5

--cfa-drizzle

Bayer-aware drizzle: after stacking, recombine each frame's MEASURED colour samples (never the interpolated ones) onto the output grid, so R/B/G are not built from debayer guesses. Needs OSC data, --debayer-method malvar and a dithered/rotating session (more frames = better; the field rotation of an alt-az mount is the dither). Sharper stars and less colour error, but higher per-pixel noise than a normal stack (no interpolation smoothing). MEASURED on a real, well-sampled stack (FWHM 4.9 px, 148 frames): luma noise -13% but chroma noise 2.1x, no sharpness gain (26 s with the native kernel) -- so it is for undersampled data (FWHM under ~2.5 px), not a general quality upgrade. Uses --drizzle-scale and --drizzle-pixfrac. Not supported with --elastic-registration. Off by default.

--banding-removal

Remove per-row / per-column offsets (CMOS banding) from each calibrated frame before debayering: measured per Bayer colour plane against its own trend, highlight-protected, and only where the offset exceeds its own standard error, so a clean frame is left untouched. Tune via --config banding_amount (0-1, default 1.0) and banding_sigma (highlight cut, default 3.0). Off by default.

--transparency-min 0-1

Drop frames whose transparency -- the median flux of a fixed star ensemble relative to the session median (1.0 = typical) -- falls below this. Catches thin cloud and haze that dim the stars without changing FWHM or SNR much. 0 (default) only reports it; 0.8 is a sensible starting point.

Default: 0.0

--noise-validate

Stack the odd and even frames separately and use their difference as an empirical noise measurement: writes <output>_noise.fits (the full stack's per-pixel noise sigma, per channel) and <output>_consistency.fits (local correlation between the halves: where structure is repeatable vs noise), and prints the measured noise against per-frame noise / sqrt(N). Needs a non-drizzle stack method with an aligned-frame array.

--moving-objects

Search the aligned frames for asteroids and other slow movers: per-frame residuals against the stack are linked into straight tracks by voting in velocity space (a mover must appear in >= 25% of frames and move more than ~2 FWHM over the session). Writes <output>_moving_objects.csv. Needs a non-drizzle stack method with an aligned-frame array.

--moving-objects-stack

Implies --moving-objects; also writes <output>_moving_N.fits, a median stack of a small window following each of the first three tracks, so a mover too faint for any one frame comes up out of the noise.

--distortion-model

Fit one radial optical distortion for the whole session (two coefficients, from every frame's star matches) and apply it as a per-frame correction in the single resample pass. Reduces the registration residual a rigid fit leaves on rotated/shifted frames and gives every frame the same correction, even those with few stars. Only applied when it clearly helps on frames the fit did not see. Alternative to --elastic-registration (which is skipped if both are set).

--no-registration

--no-affine

Disable affine (rotation+translation) registration; use translation-only

--no-reg-residual-reject

Disable dropping frames whose post-registration star-position RMS residual exceeds an adaptive threshold (1.5-6.0px, scaled to the reference frame's measured FWHM/SNR; on by default: this catches frames a bad affine/FFT registration passed through undetected -- an actual measured alignment error, not a heuristic). Rejected frames still count in the summary; reg_residual_px is recorded in each frame's metrics either way.

--no-reg-residual-check

Skip the post-registration residual check entirely (no diagnostic, no rejection) -- faster but no safety net

--elastic-registration

Fit a smooth per-frame local (non-rigid) displacement field from matched-star residuals after global affine registration, correcting spatially-varying distortion (differential atmospheric refraction, field rotation across the frame, tube flexure) that a single affine transform per frame cannot fix. Composed into the same single resampling pass as the affine warp (no extra blur pass), and honoured by both plain and --drizzle-scale stacking. Requires >= 12 matched stars per frame (falls back to affine-only otherwise) and clamps the fitted displacement to 8.0px. Off by default -- opt-in, higher-risk correction.

--stack-method

Stacking/rejection method. sigma_clip: MAD-based iterative rejection (default for dithered data). winsorized: like sigma_clip but clips to boundary instead of rejecting. percentile: reject outside [low,high] percentile (good for <8 frames; subsumes the removed trimmed_mean method -- same reject-tails-then-average operation, just parameterized by percentile bounds instead of a trim fraction). esd: Grubbs/ESD test (best for <15 frames, needs scipy). linear_fit: PixInsight-style Linear Fit Clipping -- sorts each pixel's per-frame stack ascending and fits a line to value-vs-rank, rejecting samples whose residual from that fit exceeds sigma_low/sigma_high; more robust to non-Gaussian tails than sigma-clip's mean/std test (tune via --config: linear_fit_sigma_low/_sigma_high/_iters). ivw: inverse-noise-variance-weighted mean (the Gauss-Markov-optimal linear combiner) -- weights each frame by 1/noise^2 from its own measured Phase 1 background sigma; no rejection, pair with --cosmic-ray-rejection/--trail-reject (tune gain via --config ivw_gain for an added per-pixel shot-noise term). wavelet: decompose every frame and sigma-clip-combine each wavelet subband across frames (reuses --rejection-sigma/-iters/-estimator), then reconstruct -- preserves faint fine-scale structure a pixel-domain outlier test can shave off, at the cost of N wavelet transforms per channel (slower than the pixel-domain methods). Not compatible with --drizzle-scale > 1 (v1 limitation); tune depth via --config wavelet_combine_levels (default 4). auto: choose based on frame count (<8->percentile, else sigma_clip). mean/median: no rejection.

Choices: mean, median, sigma_clip, winsorized, percentile, esd, linear_fit, ivw, wavelet, auto · Default: auto

--uncertainty-map

With --stack-method ivw, also write a per-pixel uncertainty (standard error) map to <output>_sigma.fits -- the Gauss-Markov estimator's own 1/sqrt(sum of inverse-variance weights), exposing bookkeeping the combine already computes rather than adding new analysis. One confidence map (channel weights averaged), not per-channel. No effect with other stack methods (only ivw computes an exact analytic per-pixel variance).

--uncertainty-propagate

Carry the Phase 3 uncertainty map through the Phase 4 post-processing chain and write the result to <output>_sigma_final.fits, plus a confidence (signal-to-noise) map to <output>_snr.fits. Works by pushing --uncertainty-realizations noise realizations through the real post-processing chain and measuring the per-pixel spread, which handles the nonlinear denoise/deconvolve steps that have no analytic variance propagation, at the cost of that many extra Phase 4 passes. Steps that estimate their parameters from the data (BayesShrink, sky sigma) see slightly more noise in each realization than in the real image, so the result is biased a few percent low for this pipeline's own denoisers; the log warns if the chain adapts strongly enough for that to matter. Memory: holds a copy of the linear stack plus two float64 accumulators (~1.1 GB at 24 MP). Best paired with --stack-method ivw (a real analytic per-pixel input sigma); with any other combine the input sigma falls back to a spatially flat sky-noise estimate and the log says so.

--uncertainty-realizations K

Noise realizations for --uncertainty-propagate (default: 8). Relative error on the propagated sigma is roughly 1/sqrt(2K) (~25% at 8, ~18% at 16). Runtime is K extra post-processing passes.

Default: 8

--error-aware-stretch SIGMA

Set the preview JPEG black point at the SIGMA-confidence contour from --uncertainty-propagate, so anything the propagated error bars cannot separate from sky is clipped to black instead of being stretched into apparent structure. Typical: 3. Requires --uncertainty-propagate.

--rejection-sigma REJECTION_SIGMA

Sigma threshold for pixel rejection in sigma_clip/winsorized stacking (default: 3.0)

Default: 3.0

--rejection-iters REJECTION_ITERS

Number of clipping iterations for sigma_clip stacking (default: 3)

Default: 3

--drizzle-scale DRIZZLE_SCALE

Drizzle scale factor (e.g. 2.0 for 2x super-resolution, 1.0 = disabled)

Default: 1.0

--super-res-iters N

Iterative back-projection (IBP, Irani & Peleg 1991) super-resolution refinement passes after drizzle (default: 0 = off; only meaningful with --drizzle-scale > 1). Each iteration forward-simulates what every original frame should look like given the current estimate (inverse-warp + PSF blur), compares to what was actually observed, and back-projects the residual correction. Genuine resolution gain beyond drizzle's one-shot linear resample -- but like any inverse-problem iteration, more isn't always better: it converges then starts amplifying noise past a scene-dependent point (measured on synthetic data: RMSE bottoms out around 5 iterations, then rises again by ~10). 5 is a reasonable starting point; watch the output rather than maximising N. Needs a PSF estimate (same estimator as --drizzle-kernel psf/--deconvolve rl) -- skipped with a warning if that fails. Not yet compatible with --elastic-registration (tune step size via --config ibp_relax, default 0.1).

Default: 0

--drizzle-kernel

Drizzle resample kernel (default: lanczos3; only matters when --drizzle-scale > 1). psf: build the resample kernel from the session's own estimated PSF (Moffat fit to reference-frame stars, same estimator as --deconvolve rl) via a windowed Wiener inverse filter -- mild built-in sharpening during resample (tune aggressiveness via --config drizzle_psf_wiener_k, default 0.020; lower = sharper but more prone to ringing/noise gain). NOT simply the raw PSF shape as the kernel -- that measurably broadens stars (convolving an already-blurred profile with itself again), which was verified and corrected during development. Cropped to a small 9x9 tap radius (a resample kernel, not a full deconvolution pass -- pair with --deconvolve for a bigger correction). Falls back to lanczos3 if PSF estimation fails (too few reference stars) or with --elastic-registration (the elastic warp path doesn't use this kernel). magic: Costella's base Magic Kernel (quadratic B-spline, provably non-negative -- ringing-free unlike Lanczos-3's negative sidelobes, at the cost of being softer by construction). This is the base kernel only, not the sharpened "Magic Kernel Sharp" variant.

Choices: lanczos3, psf, magic · Default: lanczos3

--aligned-stack

How the aligned frames are held between alignment and the combine. half (default): 16-bit floats relative to each frame's sky level -- half the memory, and half the disk traffic on a session that does not fit in RAM; each sample is rounded by at most 1/4096 of its distance from the sky level, thousands of times below the noise. Used with the default combine (sigma-clip with patch weighting, proper coadd); other stack methods, --local-normalize, --moving-objects and --gpu-coadd keep 32-bit floats. full: 32-bit floats always, as in earlier releases.

Choices: half, full · Default: half

--local-normalize

Additively match every frame background to the per-frame median before the rejection combine (removes per-frame gradients from moonlight / light-pollution drift / thin cloud, and sharpens sigma-clip). Applies to rejection stack methods (not plain mean or drizzle).

--drizzle-method

How drizzle builds the output (default: resample; only matters when --drizzle-scale > 1). resample: each output pixel is a Lanczos-3 gather from the input, weighted afterwards by --drizzle-pixfrac. splat: the original drizzle algorithm -- every input pixel is a square drop of side pixfrac*scale output pixels deposited by exact area overlap. About 3x faster, no Lanczos ringing, slightly softer; with pixfrac 1.0 pixels a frame never covers are left empty, so use it with several dithered frames. Uses the Lanczos-3 kernel only: skipped (with a message) for --drizzle-kernel psf/magic and --elastic-registration.

Choices: resample, splat · Default: resample

--drizzle-pixfrac P

Drizzle pixel fraction (tent-kernel weight; < 1.0 = sharper at cost of noise; default: 1.0).

Default: 1.0

Post-processing (Phase 4)

--denoiser

Primary luma denoiser (default: auto — wavelet unless a preset/--auto selects otherwise). wavelet: adaptive BayesShrink DWT whose per-subband threshold is locally reduced wherever a structure-tensor coherence map detects elongated structure (filaments, galaxy arms) -- curvelet/shearlet-*inspired*, not an actual ridgelet/shearlet transform. How much that protection reduces the threshold is --wavelet-protect (0 = plain BayesShrink). curvelet: an alias for wavelet, kept for old command lines. acdnr: contrast-gated sky smoothing. bilateral / aniso: edge-preserving filters. nn: a small neural network trained on real Origin sessions (Noise2Noise on odd/even sub-stacks); denoises colour too, so chroma noise reduction is skipped. none: disable luma denoising. Chroma noise reduction is separate (--no-chroma-nr). Fine tuning via --config.

Choices: auto, wavelet, curvelet, acdnr, bilateral, aniso, nn, none · Default: auto

--wavelet-protect 0-1

Structure protection for the wavelet denoiser: how strongly the threshold is lowered where elongated structure (filaments, galaxy arms) is detected. 0 = plain BayesShrink with no protection; default 0.6 (or the --auto preset's value). Only applies with --denoiser wavelet/curvelet.

Default: 0.6

--deconvolve

Deconvolution: off (default), rl (Richardson-Lucy), tv (Total-Variation regularised; sharper edges, slower), rl-sv (spatially-variant RL: a separate PSF per field tile, corrects off-axis aberration/tilt the corners suffer), or sparse (FISTA, L1-regularised in this project's own wavelet basis rather than TV's spatial-gradient basis -- a different sparsity prior, tune via --config deconvolve_sparse_lambda, default 0.01). nn: a network trained on Origin sessions that narrows stars by 8-18% and removes noise; it replaces the denoiser and chroma noise reduction. PSF options (blind PSF, iterations, model) via --config.

Choices: off, rl, tv, rl-sv, sparse, nn · Default: off

--nn-deconv-blend FRAC

With --deconvolve nn: the share (0-1) of the deconvolver output kept; the rest is the neural denoiser output. Lower values leave less sky noise and keep faint galaxy contrast, higher values sharpen more. Default: automatic (0.75; with --auto galaxies 0.35).

--nn-local-optics

With --deconvolve nn: apply a conservative local optical correction that measures star width across the field and selectively sharpens softer regions toward the sharpest region. Off by default.

--skip-step STEP

Skip a named post-processing step. Can be specified multiple times. Steps: hot_pixel, background, chroma_nr, sky_floor, sky_residual, sky_pedestal, bilateral, acdnr, wavelet (alias: curvelet), nn, deconvolve, star_reduce, local_contrast, sky_neutralize, edge_bands, remove_stars, starless_process

--lightcurve-analysis

After --photometry-timeseries, search the target and the most variable stars for periods (Lomb-Scargle, with a false-alarm probability) and dips (box-least-squares search + a trapezoid transit fit with errors and a BIC test); writes <output>_lightcurve_analysis.csv. Needs --photometry-timeseries.

--session-report

Write <output>_session.png (FWHM, transparency, SNR, background, drift, field rotation, ellipticity, residual and temperature against time) and <output>_session.csv, and print the drift rate, any periodic tracking error, the field-rotation rate and any FWHM-vs-temperature focus drift.

--no-background-extraction

Disable background extraction

--bg-method

Background extraction method (default: dbe). mesh: legacy polynomial grid (fastest). dbe: Dynamic Background Extraction, robust local regression (native/Rust accelerated). wavelet: starlet (a-trous) low-pass fit on the same sky-patch samples as dbe -- a hard pixel-scale cutoff instead of a blur radius, better at leaving faint extended nebulosity alone while still flattening the sky (see --bg-wavelet-scales). physical: EXPERIMENTAL. Fits a first-principles sky model (moonlight via Krisciunas-Schaefer, van Rhijn airglow, zodiacal light, ground-source skyglow) whose spatial shapes are fixed by geometry, leaving one non-negative amplitude per component free, so unlike the surface fitters above it has nowhere to put a nebula. BUT on a typical ~1 deg deep-sky field the physical components are nearly constant across the frame and the model cannot fit the real gradient, which at that scale is mostly instrumental (vignetting, amp glow); measured on a real Lagoon session it made the gradient worse where dbe removed 68% of it. It now measures whether it actually flattened the background and falls back to dbe when it did not, so expect it to engage only on wide fields. Needs a session info.json with a WCS, GPS and timestamp (it runs before --plate-solve).

Choices: mesh, dbe, wavelet, physical · Default: dbe

--transient-detect REF.fits

Compare this stack against an earlier epoch of the same field and report what changed -- novae, dwarf-nova outbursts, supernovae, asteroids crossing the field, variable stars. Uses ZOGY proper image subtraction (Zackay, Ofek & Gal-Yam 2016), which cross-convolves each epoch with the other's PSF so stellar residuals cancel even when the two nights had different seeing -- a plain subtraction leaves a bright dipole at every star and buries any real transient. The reference is registered onto this stack with the same blind rotation-agnostic star match --merge uses. Writes <output>_difference.fits, <output>_scorr.fits (significance in sigma) and <output>_transients.csv. Diagnostic only -- never alters the stack.

--transient-threshold SIGMA

Detection threshold for --transient-detect, in sigma of the corrected score image (default: 5.0). The score is calibrated (source + astrometric noise are propagated), so this is a real significance, not an arbitrary cut.

Default: 5.0

--transient-triage

Score each --transient-detect candidate with a small CNN for a real_probability (real transient vs. cosmic ray / registration-slip dipole / hot pixel) -- the same role ZTF's BTSbot / Rubin's DIA triage play downstream of classical image differencing. Advisory only: never drops a candidate, just adds a column to <output>_transients.csv. Runs in numpy (or the native kernel in a "triage"-feature build); no extra dependency. The bundled model is trained on synthetic pairs plus real two-night pairs with injected sources (no labelled real transients exist yet). Requires --transient-detect.

--transient-triage-model PATH

Override the bundled transient-triage model (src/data/transient_triage.onnx). Requires --transient-triage.

--light-pollution-azimuth DEG

physical bg-method only: compass azimuth (degrees east of north) of the dominant ground light source, e.g. the town you are shooting away from. Shapes the skyglow term's azimuthal asymmetry (default: 0 = north).

Default: 0.0

--bg-wavelet-scales N

wavelet bg-method only: starlet scale count -- background is the coarsest approximation after N dyadic scales, i.e. structure smaller than roughly 2**N sample-grid cells is treated as sky and removed, anything larger is left alone (default: 6). Lower = more aggressive/smaller-scale gradient removal, risks eating broad nebulosity; higher = leaves more large-scale gradient in.

Default: 6

--variance-stabilize

Apply a generalized Anscombe transform to the luma plane before wavelet denoising (--denoiser curvelet/wavelet), inverting it after. Shot noise on bright pixels is Poisson, not Gaussian; BayesShrink's single per-subband threshold (from the finest detail subband's MAD) implicitly assumes uniform Gaussian noise, which the transform makes closer to true everywhere in the frame rather than just near the sky background level it was effectively tuned at. Gain/read-noise are estimated from the image's own local mean-variance relationship, not sensor specs. Chroma channels are untouched (not a photon-count quantity). No effect with other --denoiser choices.

--no-chroma-nr

Disable chroma noise reduction

--repair-stars

Rebuild saturated (flat-top) star cores from their unsaturated wings via a per-channel Moffat fit — restores a natural peak and star colour instead of a clipped white disk.

--no-star-reduce

Disable star reduction

--no-local-contrast

Disable multiscale local contrast enhancement

--hdr-combine SHORT_STACK.fits

Blend a short-exposure stack into saturated regions of the main stack for HDR targets (e.g. Orion Nebula core, globular clusters).

--hdr-blend-mode

Blend algorithm for --hdr-combine (default: threshold). threshold: a smooth sigmoid mask centred on the long stack's 98th-percentile value (original behaviour). fusion: Mertens multiresolution exposure fusion (src/exposure_fusion.py) -- blends through a Laplacian pyramid weighted by local contrast/saturation/well-exposedness instead of a single spatial mask, avoiding the seam a threshold blend can leave at the transition band; costs more (several pyramid levels of Gaussian-filter passes).

Choices: threshold, fusion · Default: threshold

--scnr

Apply Subtractive Chromatic Noise Reduction to suppress green cast artefacts common in OSC/DSLR images under light pollution.

--photometric-calibration

Apply gray-locus photometric colour calibration. No external dependencies required.

--halo-removal

Fit and subtract Gaussian PSF halos from bright stars in the stacked image (post-processing step).

--galaxy-mode

Exclude a generous ellipse around the detected galaxy/extended source from background extraction sampling, so its broad, low-contrast halo is not fit and subtracted as a background gradient (a smooth-surface model cannot otherwise tell a tapering galaxy halo apart from real gradient at its edge). The ellipse is fit to the object's own second-moment shape, so it tracks real elongation instead of assuming a circle. Auto-enabled by --auto for galaxy-leaning targets; pass this explicitly under --no-auto.

--galaxy-mask-radius PX

Overrides the fitted ellipse's semi-major axis length (pixels); the semi-minor axis and orientation still come from the fit. Default: sized automatically from the detected object. Only active with --galaxy-mode. Ignored if --galaxy-center is set.

--galaxy-center X,Y

Skip automatic extended-source detection entirely and center the --galaxy-mode exclusion on this pixel coordinate instead (comma-separated, e.g. "1420,930" -- X is the column, Y is the row, same orientation as image-viewer pixel coordinates on the stacked output). A circle of radius --galaxy-mask-radius (default 200px) is used, since there is no fitted shape to take the aspect ratio from. For fields where auto-detection keeps finding the wrong object (a brighter star, a vignetting corner, etc. -- confirmed on a real dense-star-field target) -- open the stacked preview, read off the galaxy's pixel position, and pin it directly.

--remove-stars

Also write a starless sidecar (<output>_starless.fits). Off by default. Detected stars are inpainted with local background (normalised-convolution fill, per-star radius scaled to brightness); the main output is untouched. The sidecar is for downstream nebula/background work (aggressive stretch, external star recombination). Computed last, on the fully post-processed image. Not turned on by --auto.

--remove-stars-method

How --remove-stars removes the stars. inpaint (default): fill each star with smooth local background, on the post-processed image. nn (EXPERIMENTAL): a network trained by Noisier2Noise on Origin sessions removes the stars and keeps the sky noise and the nebula structure under them; saturated stars have their halo subtracted and their core filled. With nn the sidecar is the starless version of the linear stack (like the main FITS), for your own processing. It also sets how --starless-process takes the stars out before the denoisers.

Choices: inpaint, nn · Default: inpaint

--starless-process

Run the denoisers and local contrast on a starless copy of the image, then add the stars back untouched. With no stars in the layer there is nothing to protect (no star mask, no halo leaking into background estimates, no collar around bright stars), so faint galaxy/nebula structure gets cleaner treatment. The layers sum back to the input exactly, so no star flux is lost. Off by default.

--layered-stretch

Preview JPEG: take the stretch black/white points from a starless copy of the image, so faint galaxy/nebula structure owns the tonal range instead of being squeezed under the stars' white point. Only affects the preview JPEG, not the FITS/TIFF. Needs --stretch ghs. Off by default.

Output, preview & plate solving

--export FMT[,FMT]

Extra output formats alongside FITS: tiff (32-bit float), xisf (PixInsight), png (the preview JPEG's image, lossless 8-bit), png16 (the preview as a 16-bit PNG, for further editing without banding). E.g. --export tiff,png16

--plate-solve

Plate-solve the stack and write a WCS (see --plate-solver; the default solves locally against a Gaia index, no API key needed)

--annotate

Circle and label bright stars and named deep-sky objects (galaxies, nebulae, clusters) on a copy of the preview, via live SIMBAD queries. Needs a WCS solution (--plate-solve, or a session info.json that already has one) -- skipped with a message otherwise. Writes <output>_annotated.jpg; the main FITS/TIFF/JPG are untouched.

--annotate-mag-limit MAG

--annotate: only label stars brighter than this V magnitude (default: 9.0; lower = fewer, brighter-only stars)

Default: 9.0

--stretch

Preview JPEG stretch method (default: ghs). masked blends a stronger faint-signal stretch through a smooth luminance mask; multiscale uses separate diffuse and filament masks; seti applies SETI Astro statistical stretch (target median 0.20); noisefloor picks the curve strength from the sky noise (ignores the preset GHS strength and --preview-black-sigma); oklab applies the GHS curve to perceptual lightness and keeps hue and saturation; clahe adds contrast-limited local equalisation to the GHS preview where there is extended signal; object takes the white point and the curve’s focus from the target itself (found where the telescope pointed), so a small galaxy is not stretched for the empty field around it; plain ghs when the target fills the frame.

Choices: linear, arcsinh, ghs, masked, multiscale, seti, noisefloor, oklab, clahe, object · Default: ghs

--stretch-color

How the ghs/arcsinh stretch treats colour (default: preserve). preserve: the curve is applied to luminance and each pixel keeps its RGB ratios, so stars and bright cores keep their colour. channel: the curve is applied to each channel separately (the behaviour before 2.5), which renders bright objects whiter.

Choices: preserve, channel · Default: preserve

--preview-black-sigma PREVIEW_BLACK_SIGMA

Preview black point, in sky-sigma above the sky median (default: -0.0868). Higher (e.g. 2.0) clips background noise to black for a small target on empty sky; negative keeps faint frame-filling nebulosity visible. Set per target by --auto.

Default: -0.0868

--plate-solver

Plate solver backend. auto (default): the built-in solver against a local Gaia star index, then astrometry.net if that fails and an API key is set. local: built-in only -- needs a position hint (session info.json/header WCS or RA/DEC keywords); index tiles it lacks are downloaded once and cached, or prebuilt with tools/build_star_index.py for fully offline use. astap: the ASTAP binary. astrometry: nova.astrometry.net (requires ASTROMETRY_API_KEY).

Choices: auto, local, astap, astrometry · Default: auto

--no-wcs-refine

Do not refine the session info.json WCS against the local Gaia index after stacking (by default it is mapped onto the stack and refined; missing index tiles are fetched once unless --offline).

--astap-path PATH

Explicit path to the ASTAP binary (auto-detected if omitted)

--no-color-calibrate

Do not colour-calibrate the stack against Gaia DR3. By default, when the stack has a WCS (the session solve, refined against Gaia) and the run is online, star colours are measured on the linear stack before post-processing and the channels scaled so a solar-colour star renders white.

--color-calibrate-method

Colour calibration algorithm (default: solar). solar: fit each star's measured B/R and G/R against Gaia BP-RP and scale so a G2V star (BP-RP 0.82) is white. colorindex: fixed Gaia BP-RP -> B-V colour-index formula. spcc: spectrophotometric-style -- integrates a blackbody spectrum at each star's Gaia teff_gspphot against per-channel response curves (falls back to colorindex per-star when a star has no Teff estimate). Uses generic Gaussian R/G/B response curves, not a measured curve for your specific camera + filters -- more physically grounded than colorindex, not a claim of matching a specific sensor's real QE curve.

Choices: solar, colorindex, spcc · Default: solar

--photometry

Aperture-photometer the linear stack against Gaia DR3 and write a calibrated stellar catalogue (<output>_photometry.csv) plus MAGZP_R/G/B zero-point header keywords. Needs a WCS (--plate-solve or a session info.json solve). Uses the site GPS + observation time from info.json for an airmass term; falls back to folding extinction into the zero point when those are absent. OSC channels are mapped coarsely to Gaia RP/G/BP -- absolute accuracy ~0.05 mag, differential better.

--photometry-extinction-k MAG/AIRMASS

Override the per-band atmospheric extinction coefficient used by --photometry (applied to all of R/G/B). Default: nominal R=0.09 G=0.15 B=0.23. Only matters when an airmass could be derived.

--photometry-color-terms

--photometry: also fit a per-channel colour term (slope of the zero-point residual vs Gaia BP-RP) instead of a plain robust median. Written as MAGCT_R/G/B and applied to the catalogue magnitudes. Improves the OSC->passband match for stars far from the reference colour.

--photometry-gain E/ADU

Sensor gain (electrons per ADU) for the Poisson term in the per-star photometric errors. Overrides both the photon-transfer estimate (auto, from raw bias+flat pairs when present) and the FITS GAIN/EGAIN header.

--photometry-timeseries

Per-frame differential photometry: aperture-photometer a fixed Gaia star list on every registered sub and ensemble-calibrate, writing <output>_lightcurves.csv (one row per frame x star) and <output>_lightcurve_stats.csv (per-star mean/rms/reduced-chi2 + a variability flag). Needs a session info.json WCS (Celestron Origin); --plate-solve runs too late for this. Differential only -- no absolute zero point.

--photometry-target RA,DEC

--photometry-timeseries: mark one star as the target. "RA,DEC" in degrees, or "px:X,Y" in stacked-image pixels. Its light-curve stats are printed and flagged in the CSVs.

--aavso-report

With --photometry-timeseries: write <output>_aavso.txt: the light curve of the target in the AAVSO Extended Format, for WebObs upload. Comparison stars come from the AAVSO VSP chart (network), ensemble-calibrated per frame on the green channel (TG band), with a check star. Target: --aavso-star NAME or --photometry-target RA,DEC.

--aavso-star NAME

--aavso-report: AAVSO name of the variable star (e.g. "SS Cyg"); its chart and position come from AAVSO VSP.

--aavso-obscode CODE

--aavso-report: your AAVSO observer code (written as CHANGEME if not given).

--aavso-bin MINUTES

--aavso-report: average the frames in time bins of this many minutes (0 = one point per frame).

Default: 0.0

--aavso-fov ARCMIN

--aavso-report: VSP chart field of view for the comparison stars.

Default: 60.0

--aavso-maglimit MAG

--aavso-report: faintest VSP comparison star.

Default: 15.0

--fix-atmospheric-dispersion

EXPERIMENTAL: shift R/B channels back toward the green channel's position to correct chromatic atmospheric refraction, derived from first principles (Filippenko 1982's standard-atmosphere refractive-index formula) -- no established software reference implementation exists for this, unlike this pipeline's other corrections (real ADCs are physical prism hardware). --plate-scale and --zenith-angle are auto-derived (from the WCS, and from the info.json GPS + observation time) when not given; --parallactic-angle is still required -- a wrong value shifts colour channels the WRONG way, and mapping it onto the detector needs the image north angle, which is not reliably known here. Not wired into --auto. Most useful for low-altitude targets (zenith angle > ~40deg).

--plate-scale ARCSEC/PX

Image plate scale for --fix-atmospheric-dispersion (auto-derived from the WCS when omitted).

--zenith-angle DEG

Target zenith angle (90 - altitude) at capture time, for --fix-atmospheric-dispersion (auto-derived from the info.json GPS + observation time when omitted).

--parallactic-angle DEG

On-detector direction of "toward zenith" (0=up/+y, 90=right/+x), for --fix-atmospheric-dispersion. Depends on site latitude, target RA/Dec, and capture time -- not derived by this tool.

--matched-filter

Write <stem>_matched_filter.fits: the stacked image correlated with its own estimated PSF -- the matched filter theorem's provably SNR-optimal linear filter for detecting a known-shape (point) source in white noise. A per-pixel SNR map when a background noise estimate is available. Complementary to, not a replacement for, --stack-method ivw (that's an optimal per-frame combine weight; this is a post-stack detection filter). Diagnostic/utility output, no effect on the main stack.

--nmf-separate

Non-negative matrix factorization: split the stacked image into a stellar component and a nebular component (2 sources, each with its own per-channel spectral signature and a spatially-varying activation map), written as <stem>_star_component.fits / <stem>_nebula_component.fits. An alternative to --remove-stars's inpainting for use cases that want the star signal separated out, not discarded. Non-convex optimisation with no convergence guarantee -- inspect the output before trusting the split, especially if the log notes it as ambiguous.

--dither-report

Analyse how uniformly the session's sub-pixel dither offsets sample the output grid, and warn if coverage is significantly non-uniform (drizzle output quality is bounded by this, and nothing else in the pipeline measures it). Writes <stem>_dither.png. Diagnostic only -- no effect on the stack.

--aberration-report

Analyse star FWHM/elongation across the field and diagnose sensor tilt, field curvature, and backfocus (spacing) errors. Writes <stem>_aberration.png and FITS header keywords.

Multi-session, merge & checkpoint

--merge STACK.fits ...

Incremental stacking: merge previously saved linear stacks (the main output FITS of earlier runs, RAWSTACK=True) into this run. Each is registered onto the current session's grid (star-match affine, translation fallback) and combined as a per-pixel mean weighted by its NFRAMES header inside its warped footprint. Post-processing runs once on the merged result, and the output is itself a mergeable linear stack. No cross-session outlier rejection (each session already rejected internally). Not supported with --drizzle-scale > 1.

--merge-consistency

With --merge or a multi-session combine: reject what only one night shows. Per wavelet scale, a night is left out where it has a significant feature the other nights, combined, show nothing of (a satellite glint, an unrejected hot-pixel cluster, a transient, a reflection); structure all nights show keeps the plain weighted mean.

--no-merge-proper

With --merge or a multi-session combine: weight each night by its noise only (the plain weighted mean). By default each night is weighted per spatial frequency by its own star sharpness (proper coaddition), so a sharp night carries the fine detail instead of being diluted by longer, softer ones, at the same noise.

--keep-checkpoint

Keep the raw pre-post-processing stack after a successful run. Re-running skips phases 1–3 so you can iterate on post-processing settings quickly.

--from-stack STACK.fits

Re-run only Phase 4 (post-processing) on the linear main FITS (RAWSTACK) of an earlier run -- no light frames needed. Settings come from the earlier run's <stem>_config.toml when present (so --auto choices carry over) and any flag given here overrides them. Writes the preview (and --export files) for -o, default <stem>_reprocessed; the linear FITS is not modified.

--no-resume

Ignore any existing checkpoint and start from scratch.

--combine-sessions

Always pool all light frames from every subfolder into a single unified stack. Without this flag (or --hierarchical) the choice is made from the data: sessions are pooled unless their field rotation differs by more than 3 deg -- predicted from each info.json and the light-frame times -- in which case each is stacked separately and merged, since pooling rotated sessions crops away the corners. Overrides --hierarchical.

--hierarchical

Always stack each subfolder separately, then combine the per-subfolder stacks with rotation-aware registration (or --mosaic, if given). Without this flag (or --combine-sessions) the choice is made from the predicted field rotation between sessions. Has no effect unless the input directory contains subfolders.

--mosaic

Stitch per-subfolder stacks into one mosaic: each panel is stacked, the panels are matched to each other in their overlaps (gain and gradient) and blended, then the mosaic is finished once. Needs a WCS per panel (the Origin session solve, or a plate solve: enables --plate-solve). Turned on automatically when the subfolders point at overlapping but different parts of the sky.

Comet mode

--comet-mode

Enable comet nucleus tracking. Produces a second stack aligned on the comet nucleus saved as <stem>_comet.fits.

--comet-xy X,Y

Approximate comet nucleus position in the reference frame (pixels, comma-separated, e.g. "1024,768"). X=column, Y=row.

--comet-search-radius PX

Search radius (pixels) around predicted nucleus position for frame-to-frame tracking (default: 50).

Default: 50.0

--comet-affine

Apply rotation+scale correction (affine) on top of nucleus translation shift in comet registration (default: translation only).

--coma-mask-radius PX

Circular exclusion radius (pixels) around the comet nucleus used to prevent background extraction from sampling the coma (default: 150). Only active when --comet-mode is set.

Default: 150

--comet-radial-renorm

Apply radial renormalization filter to flatten the coma gradient and reveal jets/structure. Saves result as <stem>_comet_renorm.fits.

--comet-larson-sekanina

Apply Larson-Sekanina rotational difference filter to enhance comet jet structure. Saves result as <stem>_comet_ls.fits.

--comet-ls-rotation DEG

Rotation angle (degrees) for the Larson-Sekanina filter (default: 15).

Default: 15.0

--comet-designation DESIG

JPL Horizons designation for ephemeris-aided nucleus tracking (e.g. "C/2023 A3").

--observer-site SITE

Observer location for ephemeris queries: MPC code (e.g. "G37") or "lon,lat,elev" in decimal degrees/metres (e.g. "-2.5,51.4,50").

Diagnostics & debugging

--debug KIND[,KIND]

Debug artefacts, comma-separated: registration (per-frame diagnostics, implies -v), diagnostic (FITS snapshot before each post-processing step; directory via --config diagnostic_dir), intermediates (keep aligned per-frame FITS), masks (save the star mask FITS).

--log-level

Minimum log severity printed to stderr (default: WARNING). Use DEBUG for verbose diagnostic output from all modules.

Choices: DEBUG, INFO, WARNING, ERROR · Default: WARNING

--log-file PATH

Write a full DEBUG-level log to this file in addition to console output.

--quality-report PATH

Write per-frame quality metrics CSV after Phase 1 (columns: filename, snr, fwhm, star_count, quality_score, accepted, rejection_reason)

--frame-report

Write <output>_frames.csv and <output>_frames.json after registration: for every light, kept or rejected, which gate decided it and why (e.g. "score 12.0 < 30.0"), notes for kept frames far off the session median (FWHM, stars, background, ellipticity), and the key metrics. Changes no result. With -v also prints the count per reason.

--export-frames-dir PATH

Directory to write a stretched JPEG for every accepted frame after Phase 1

The combine subcommand

Combines separately stacked channel images (LRGB or narrowband) into one colour image: python originstack.py combine [options].

Inputs and output

--mode

Combination mode (default: lrgb). lrgb: luminance-layering. sho: Hubble palette (SII->R, Ha->G, OIII->B). hoo: Ha->R, OIII->G+B. hos: Ha->R, OIII->G, SII->B.

Choices: lrgb, sho, hoo, hos, hso, oho, shs · Default: lrgb

--saturation-boost SATURATION_BOOST

Saturation multiplier applied after LRGB combination (default: 1.0)

Default: 1.0

--continuum CONTINUUM.fits

Broadband/OSC continuum reference to subtract from --continuum-target before narrowband combination (e.g. an OSC stack, for a hybrid narrowband+broadband workflow).

--continuum-target

Which narrowband channel --continuum is subtracted from (default: ha).

Choices: ha, oiii, sii · Default: ha

--continuum-scale SCALE

Manual continuum subtraction scale factor. Default: fit automatically (sweeps candidate scales, picks the one that maximises the background residual's pixel-value skewness -- verified against synthetic ground truth, see optimal_continuum_scale's docstring) instead of eyeballing a slider.

--scnr AMOUNT

Average-neutral SCNR green removal for narrowband palettes (0=off, 1=full; recommended ~1.0 for SHO to kill the green cast)

Default: 0.0

--star-recolor

Fix magenta narrowband stars: 'desaturate' neutralises star colour; 'rgb' transplants colours from --rgb-stars.

Choices: none, desaturate, rgb · Default: none

--rgb-stars RGB.fits

Broadband RGB image whose star colours are transplanted onto the narrowband stars when --star-recolor rgb is used.

--stretch

Preview JPEG stretch (default: ghs)

Choices: linear, arcsinh, ghs · Default: ghs

-o, --output OUTPUT

Output FITS path

Required

LRGB inputs

--lum L.fits

Luminance (L) stack for LRGB combination

--red R.fits

Red channel stack

--green G.fits

Green channel stack

--blue B.fits

Blue channel stack

Narrowband inputs

--ha Ha.fits

Ha (hydrogen-alpha) stack

--oiii OIII.fits

OIII stack

--sii SII.fits

SII stack