Why stacking works
A single 10-second frame of a faint galaxy is mostly noise. Every frame records the same galaxy, but the noise in each frame is different and random. Add frames together and the galaxy adds up in proportion to the number of frames, while random noise adds up only as its square root. Stack N frames and the signal-to-noise ratio improves by about √N: 100 frames are ten times cleaner than one.
- Signal and noise
SNR ∝ √N The noise comes from three places: the random arrival of photons (shot noise, which grows with the brightness itself), the sensor's read-out electronics, and heat in the sensor (dark current). Stacking beats down all three, but only the random parts. Anything that is the same in every frame, such as a hot pixel, a dust shadow or a gradient from a streetlight, stacks up just like the galaxy does. That is what calibration and background extraction are for.
- Linear data
RAWSTACK=True A pixel's value in a raw frame is proportional to the light that fell on it. That property, linearity, is what makes measurement possible: double the starlight, double the number. OriginStack keeps the stack linear in the FITS file it saves, and does every measurement, from colour calibration to photometry, on that linear stack. Only the preview image is stretched for viewing, because the eye needs faint things brightened far more than bright ones.
Watch a stack, start to finish
A two-minute walk through the four phases, with narration and captions. The Phase 2 animation is an illustration, and the Phase 4 example is a different target (M51).
The pipeline, end to end
A run has four phases, and each has its own page: pick a box or a heading below. The first three produce the linear stack; the fourth turns it into a picture. Each phase can be checkpointed, so a change to a later setting does not repeat the earlier work.
Phase 1: process and score
Every frame is loaded, calibrated, cleaned of hot pixels, demosaiced into colour, white-balanced and measured: star count, star width, sharpness and noise. Frames run in parallel, one process per CPU core. Clearly bad frames are dropped here.
Phase 2: register
Each frame's offset, rotation and scale relative to a reference frame is measured to a fraction of a pixel, then checked by re-measuring star positions after alignment.
Phase 3: warp and combine
Frames are resampled onto the reference grid, cropped to the area every frame covers, and combined pixel by pixel with outlier rejection, then recombined by spatial frequency (proper coaddition).
Phase 4: finish
Gradients are removed, colour noise and luminance noise reduced, stars tightened, contrast enhanced, and the image stretched for display. This phase reads the linear stack and never changes it.
Speed and memory
- Never more frames than fit
frame store A 500-frame Origin session is tens of gigabytes once demosaiced and aligned. Before creating each large array, OriginStack checks the memory actually available, keeps a fifth of the machine's RAM free, and puts the array in memory only if it fits; otherwise it goes to a temporary file mapped into memory. Where it lives never changes the result: both placements are tested to give bit-identical stacks.
- Rust kernels with a safety net
astro_native The hot loops (calibration, demosaicing, star detection, warping, the combines, coaddition, denoising) are written in Rust and run multi-threaded. Every one has a plain NumPy version of the same algorithm, and a test checks the two agree, in most cases bit for bit. A kernel is only written after measuring that NumPy is actually wasting time there.
- Reproducible
same input, same output Every random choice is seeded, so stacking the same frames twice gives the same image. Each run saves its effective settings; checkpoints are tied to the files and settings they came from, so a stale one is never reused.
Learned methods, and what is not AI
OriginStack uses no generative AI and no cloud AI service: nothing generates, paints in or imagines image content, and no image leaves your computer for processing. Almost every step is classical signal processing, described on these pages and implemented in the project itself. Three optional steps use small neural networks, and they are held to the same test as everything else: they must not add anything that is not in the data.
- Learned noise reduction
--denoiser nnopt-in A small U-Net, a common image-processing network, with 265,587 numbers in it. It was trained with the Noise2Noise method: each real Origin session was split into stacks of the same sky with independent noise, and the network learned to predict one from another. No clean "ground truth" picture, and no image from anywhere else, was involved.
- Learned deconvolution
--deconvolve nnauto for most sessions The same network, first trained to turn the stack of a session's softer frames into the stack of its sharpest 30%, then refined against Pan-STARRS survey images of the same patches of sky, which are far sharper and deeper than any Origin stack. Where a source is too faint to show in the Origin stack itself, the training target is the plain noise-reduced stack instead, so the network is not taught to draw survey stars the Origin cannot see. It learned to undo the Origin's real seeing blur and to remove noise at the same time. The survey images were used only for training: the program contains none of them and adds nothing from them to your image. The automatic settings use it for every session from an Origin, mixed with the learned noise reduction;
--deconvolve offturns it off. On other cameras it first measures the width of the stars in your stack and rescales the image so they match the widths it was trained on, then scales the result back, so a different pixel size does not matter. Colour stacks from other cameras and mono FITS stacks (the filter then comes from the header) are handled the same way. The automatic settings use it there too, but check the stack first: they skip it when fewer than 30 stars can be measured, when the stars are very narrow or very wide, or when they are clearly elongated (trailing).Checked on five targets it was never trained on: stars 15-35% narrower than without it, no ringing around bright stars, faint test sources keep at least 92% of their light, and 94-100% of the faint sources it shows are confirmed in an independent stack of other frames, against 4-22% for random positions. On Seestar S50 and Las Cumbres Observatory stacks, which it was never trained on, it narrows stars by about the same factor as on the Origin and does not sharpen beyond what a survey image shows (checked against Pan-STARRS). It has been tested on those cameras only, so treat a result from another camera as unverified and compare it with
--deconvolve off.- Transient triage
--transient-triageopt-in A small classifier that sorts candidate changes found by difference imaging into likely real and likely artefact. It only ranks candidates; it never changes an image.
- How they run
The weights ship with the program (about 1 MB per network) and run in plain NumPy and the project's own native code on your computer: no machine-learning framework at run time, no download, no network. They are deterministic, so the same stack always gives the same result. The training code is in the repository, so the networks can be retrained from your own sessions.
- What it depends on
No other astronomy software, external programs, accounts or cloud services. It uses standard open-source scientific Python libraries (NumPy, SciPy, Astropy, Pillow, and optionally rawpy and tifffile for camera RAW and TIFF files). By default it looks up the target name on SIMBAD and Gaia star positions for plate solving and colour calibration;
--offlineturns all network access off.
How we check it
An image that looks better is not necessarily more correct. Every step on this page was measured before it became a default, and several ideas that looked good did not survive measurement; they are listed on the advanced page.
- Ground truth
Synthetic scenes with known stars and nebulae let each step's error be measured exactly: demosaicing, drizzle, denoising and the colour pipeline were all chosen this way.
- Real sessions, same stars
On real Origin data there is no truth to compare against, so changes are measured as star width on the same stars and noise matched at equal sharpness, against the previous version and against Siril. tools/bench_vs_siril.py repeats the comparison on your own frames.
- The data checks itself
--noise-validate Stacking the odd and even frames separately and comparing them gives the true noise in the stack and shows which structure is real: it appears in both halves.
The source code is the full reference: every method described in these pages is in src/, with design notes and measurements in dev-notes/. Papers are listed on each topic page.
