stack op• Data kinds: images → image2d
• Call: import fullseye as fs; fs.ledger.sigma_clip_stack(frames, mode='sigma_clip', kappa=3.0, iters=5, center='median', scale='mad') (to call the implementation directly, import astrostack; astrostack.sigma_clip_stack(frames, mode='sigma_clip', kappa=3.0, iters=5, center='median', scale='mad'); from the registry, opsastrostack.get("sigma_clip_stack"))
• Return value through the ledger: fullseye.ledger.sigma_clip_stack(...) returns only the declared out type image2d (the underlying function also returns auxiliary values). When you need what was dropped, use fullseye.ledger.sigma_clip_stack.raw(...) or call astrostack.sigma_clip_stack directly.
• Underlying return: (stack, accepted) -> image2d
Combine a series of frames (mean / median / κ-σ clipping), with the accept/reject mask.
*mode*:
• "mean" —— simple mean. Noise falls with sqrt(N), but outliers (cosmic rays, satellite trails) are only diluted by 1/N and always remain.
• "median" —— median. Robust to outliers, but for a normal distribution the noise falls only sqrt(pi/2) = 1.2533 times less than with the mean (= effectively throwing away 36 % of the frames).
• "sigma_clip" —— with the median as centre and scale as the scale, drops |x - center| > kappa * scale and averages the rest. This is repeated iters times. Robust to outliers, and since the surviving pixels are averaged, the noise also falls with sqrt(N_accepted) —— the practical default.
The breakdown point is 50 %. As long as the centre is the median and the scale the MAD, for a pixel where contaminated frames exceed half, the median itself sits on the contaminated side, and the clip discards the correct side. This is not a defect of this implementation but the very definition of the median; with center="mean" it breaks much earlier (even with 1 contaminated frame). The test measures the error at contamination rates of 0–60 %, and keeps the breaking side as it is.
That the default of scale is "mad" is the result of measurement. With scale="std", "the outliers themselves inflate the scale used to find outliers", so as contamination increases it actually drops nothing —— measured on 20 flat fields of 24x24 (ground truth 100, σ=2) with +500 contamination in 4 of them (20 %):
scale="std" has rejection rate 0.0 % and error +99.9975 (= exactly identical to the simple mean down to the decimals), scale="mad" has rejection rate 22.0 % and error -0.019. The breakdown point is between 10–20 % for std and exactly 50 % for mad, nearly a factor of 5 apart.
Returns (stack, accepted):
• stack —— (H, W) float64.
• accepted —— (N, H, W) bool, True = pixel accepted. For mode="mean" / "median" all True (neither clips, so the absence of an "accept/reject" concept is indicated by there being no False at all).
Raises ValueError: *frames* not a list / tuple / fewer than 2 frames / shapes do not match / *mode* not in STACK_MODES / *kappa* not positive / *center* other than "median" / "mean" / *scale* other than "std" / "mad".
• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).
• Operator provenance and references — the sources of the research/methods this op family came from.
• The canonical algorithm (author, year) and its uses are named in the family usage guide above.
• astro_stacking — py -3.11 examples/astro_stacking.py
• poc_astro_photometry — py -3.11 examples/poc_astro_photometry.py
• poc_exoplanet_transit — py -3.11 examples/poc_exoplanet_transit.py
• poc_river_surface_velocity — py -3.11 examples/poc_river_surface_velocity.py
image2d as input)frame_quality · noise_sigma · cosmic_ray_reject · star_detect · psf_fit · aperture_photometry · frame_align
stack)*Provenance: astrostack.py — ASTROSTACK operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.