lucky_select — ASTROSTACK quality op

• Data kinds: images → indices

• Call: import fullseye as fs; fs.ledger.lucky_select(frames, keep_fraction=0.3, min_keep=1, **quality_kw) (to call the implementation directly, import astrostack; astrostack.lucky_select(frames, keep_fraction=0.3, min_keep=1, **quality_kw); from the registry, opsastrostack.get("lucky_select"))

• Return value through the ledger: fullseye.ledger.lucky_select(...) returns only the declared out type indices (the underlying function also returns auxiliary values). When you need what was dropped, use fullseye.ledger.lucky_select.raw(...) or call astrostack.lucky_select directly.

Usage

Keep only the top keep_fraction by quality score — the lucky-imaging cut.

The number kept is max(min_keep, ceil(keep_fraction * N)). At least 1 frame is always kept, so however small keep_fraction is, the result is never empty (passing an empty stack downstream would be the bigger accident). Sorting is by descending score, a stable sort that keeps the original order for ties —— the same input returns the same order.

Returns (indices, scores):

• indices —— (K,) int64, indices of the kept frames, best first (indices vocabulary). [frames[i] for i in indices] can be passed directly to sigma_clip_stack.

• scores —— (N,) float64, scores of all frames (including the discarded ones). It returns the material rather than the selection result, so that the reason for discarding can be plotted.

Ground truth it reproduces (tests/test_astrostack.py): for a frame series of the same star field imaged with only the FWHM changed, the score falls with FWHM. Measured on 12 frames of 96x96 with 20 stars and FWHM varied over 3.462–6.080 px:

corr(fwhm, score) = -0.925. Taking the top 25 % (3 frames) and mean-stacking, the FWHM after stacking goes 4.403 → 3.672 px compared with averaging all 12 frames, an improvement of 16.6 % —— the image gets sharper even though the number of frames is cut to 1/4, which is exactly the claim of lucky imaging (in exchange, the noise becomes sqrt(4) = 2 times larger).

Raises ValueError: *frames* not a list / tuple (a 3-D array is explicitly rejected) / fewer than 1 frame / keep_fraction outside (0, 1] / *min_keep* exceeds the number of frames.

References (sample data, literature)

• 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.

Runnable examples (verified samples that actually call this op)

• astro_stacking — py -3.11 examples/astro_stacking.py

• poc_astro_photometry — py -3.11 examples/poc_astro_photometry.py

Ops the type connects to (they accept indices as input)

—

Same category (quality)

frame_quality · noise_sigma


*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.