photometry op• Data kinds: image2d × keypoints → table
• Call: import fullseye as fs; fs.ledger.aperture_photometry(image, centers, r_aperture=5.0, r_inner=8.0, r_outer=12.0, read_sigma=0.0, gain=1.0, supersample=8) (to call the implementation directly, import astrostack; astrostack.aperture_photometry(image, centers, r_aperture=5.0, r_inner=8.0, r_outer=12.0, read_sigma=0.0, gain=1.0, supersample=8); from the registry, opsastrostack.get("aperture_photometry"))
Photometry with a circular aperture and an annular background (classical CCD photometry).
Pixels inside the aperture are weighted at subpixel level (sampling with supersample^2 points), so the area does not jump in steps even when the radius is not an integer. The background is the median of the annulus from r_inner to r_outer (not dragged along even if a neighbouring star enters the annulus).
Flux is sum(w * (I - background)); S/N follows the classical CCD equation (Merline & Howell, *Exp. Astron.* 6, 163 (1995); Howell, *Handbook of CCD Astronomy*)::
SNR = F / sqrt(F/gain + A*(B/gain + read_sigma^2))
where A is the effective pixel count of the aperture and B the background level. With read_sigma=0 and gain=1 it reduces to the pure-Poisson F/sqrt(F + A*B).
Returns a list of one dict per star (table vocabulary). Keys are row / col / flux / background (per pixel) / area_px (effective pixel count of the aperture) / n_annulus / snr / flux_error / mag_instrumental (-2.5 log10(flux), nan if the flux is non-positive).
Ground truth it reproduces (tests/test_astrostack.py): for a noise-free Gaussian star (flux 10000 e-), the fraction captured by an aperture of radius r is 1 - exp(-r^2/(2 sigma^2)). Widening the aperture makes it agree exactly —— at r = 8 sigma, for every sigma = 1.0 / 1.5 / 2.0 / 3.0 the measurement is 10000.00000, error -0.0000 %.
An honest note: small apertures retain a systematic negative offset. At r = 3 sigma the measurement falls below theory::
sigma = 1.0 -> 9798.57 / 9888.91 = -0.914 %
sigma = 1.5 -> 9850.70 / 9888.91 = -0.386 %
sigma = 2.0 -> 9868.61 / 9888.91 = -0.205 %
sigma = 3.0 -> 9879.86 / 9888.91 = -0.092 %
This is not a bug but pixelisation itself. The closed form integrates a continuous Gaussian over a circle, whereas here "total pixel flux × fraction of area inside the circle" is summed. For pixels on the aperture edge, the inner part of the circle (towards the centre) is actually brighter, so representing it by the pixel mean always comes out too low. The error falls in inverse proportion to the square of sigma (10 times from 1.0→3.0), which is the proof: the better the sampling, the smaller the gap between pixel mean and the true distribution. Widening the aperture removes the contribution of edge pixels altogether, so the error vanishes too.
supersample only fixes the discretisation of the circle's area (measured: at r=4.5 relative 1.6e-3 against pi r^2, at r=3 2.5e-4). This is a separate matter from the offset above; raising it does not remove the bias of the edge pixel mean.
Raises ValueError: if the radii are not ordered as 0 < r_aperture <= r_inner < r_outer / *supersample* is less than 1 / *gain* is non-positive / *centers* is not (N, 2).
• 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_nuclei_ploidy — py -3.11 examples/poc_nuclei_ploidy.py
• poc_real_sky_photometry — py -3.11 examples/poc_real_sky_photometry.py
table as input)—
photometry)*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.