Repository navigation
Releases: jacotay7/getframes
Release list
getframes 2.6.1
Fixed
- Allow aocore 0.2 (
aocore>=0.1.2,<0.3). aocore 0.2.0 is additive and the
test suite passes against it; the old<0.2cap made getframes impossible to
install next to packages that need aocore 0.2 (e.g. shmpipeline-ao 0.2).
getframes 2.6.0
Added
Calibration: ADU to input-referred electrons and incident photons
(gf.Calibration,Camera.calibration).Calibration.from_camera(cam, exposure, n_darks=64, seed=...)averages exposure-matched dark frames into a
mean master dark on the camera's device and precision;to_electronsapplies
L^-1[(ADU - dark) * gain_e_per_adu / em_gain] / flat, inverting the
configured nonlinearity (closed form or Newton) by default;to_photons
divides by the scalar QEexposeapplies, aquantum_efficiency, the
qe_curveatwavelength_nm, or its effective value over abandpass;
saturatedflags pixels whose charge reached the input well, the output
register or the ADC ceiling. It works on NumPy and CuPy frames, takes an
optional unit-mean flat, and handles binned frames. Tests check that the mean
calibrated flux matches the incident photons to 0.5% for EMCCD, qCMOS, sCMOS,
eAPD, CCD and CMOS presets (issue #10).Camera.effective_read_noise_e(binning=..., binning_mode=...): the
per-frame read noise referred to the input (electrons collected in the
pixel): output read noise (the quadrature mean of a per-pixel map), kTC/reset
noise and avalanche-scaled noise, divided by the EM/avalanche gain. The
docstring and the new guide give the excess-noise-factor SNR.- Guide: A WFS camera in a closed loop:
exposure vs. frame period, ADU calibration and its accuracy, input-referred
noise and the excess noise factor, recommended EMCCD/qCMOS/sCMOS/eAPD/CCD
presets, and keeping small frames cheap. benchmarks/bench_small_frames.py: seeded 256x256 WFS frames for one preset
per detector class, with before/after comparison and a SHA-256 digest of
seeded output for bit-identity checks; results in
benchmarks/small-frame-results.md.
Performance
- Photon-starved EMCCD and eAPD frames are faster on the CPU, with
bit-identical seeded output. NumPy returnsGamma(shape=0)as exactly zero
without consuming a draw, but still visits every such pixel; the gain stage
now samples only the pixels that hold charge when they are a minority (a
wavefront sensor's dark background), and skips the* 1shape scaling at
F = sqrt(2). Same draws, same order, same generator state afterwards;
seeded frames of every preset, in both precisions, are bit-identical to 2.5.0.
On one Neoverse-N1 core (256x256 Shack-Hartmann spots, 1 ms, in-process A/B):
the Gamma draw 1.43 → 0.78 ms; whole framesandor_ocam2k6.57 → 5.95 ms
(1.10x),leonardo_saphira4.79 → 4.29 ms (1.12x),first_light_imaging_cred_one
7.16 → 6.63 ms (1.08x); presets without a gain stage are unchanged. The CPU
chain is otherwise at its sampler floor (about 85% of a frame is NumPy's
Poisson, Gamma and Gaussian draws). An exact vectorised replica of NumPy's
small-rate Poisson for the CIC draw was tried and measured no faster, so it
was not kept. Seebenchmarks/small-frame-results.md.
Documented
- The 1--2% calibrated-flux deficit seen in the dithered Shack-Hartmann
prototype is not a deterministic offset of the signal chain: averaged over
300 frames the prototype recovers 1.002 (OCAM2K), 0.999 (ORCA-Quest 2) and
1.001 (generic sCMOS) of the incident flux, while a single 288x288 frame
scatters by 1.2%, 1.0% and 3.3%. The guide lists the deterministic offsets a
pipeline can introduce (median master dark, scalar vs. spectral QE,
nonlinearity, saturation, unflattened fixed patterns) and their remedies. - The Nüvü HNü 240 / 128 presets cap the amplified charge at their 500 / 800
e-full_well_e(nooutput_full_well_e), so at EM gain 1000 a pixel holding
one electron saturates; the guide warns about it (#11).
getframes 2.5.0
Added
- Arm benchmark data point (
benchmarks/device-results-neoverse-n1.*): the
device table on an Ampere Neoverse-N1 host (16 pinned cores) with an RTX
4060 and an RTX A400.
Performance
- Small GPU frames run up to 2.6x faster, with bit-identical seeded output.
WFS-sized frames were launch-bound: the host spent longer issuing about twenty
small CuPy kernels per frame than the GPU spent running them. The GPU path now
computes the photo and total expectations in one fused kernel and the whole
readout (full-well clip, defects, reset/avalanche/read noise, gain, bias
pedestal and structure, common mode, rounding, ADC saturation,uint32
conversion) in another; keeps scalar inputs (background,extra_electrons,
the EM-gain scale, the CIC rate) on the host instead of uploading them every
frame; samples Poisson counts directly in the working precision; and reuses
the chain'sphoto + dark + extrasum asFrameTruth.mean_electrons. The
noise is drawn by the same calls in the same order and the fused kernels
repeat the same floating-point operations (FMA contraction off), so every
seeded GPU frame and truth array is unchanged; a new GPU test compares them
bit for bit against the separate operations.bench_devices.pyon an Ampere
Neoverse-N1 host (12 pinned cores), frames/s, 2.4.0 → now, median of three
interleaved runs: RTX 4060 — Pyramid 80x80 2,422 → 6,418 (2.65x),
Shack-Hartmann 160x160 2,424 → 6,340 (2.62x), OCAM2K 240x240 1,650 → 2,733
(1.66x), SAPHIRA 256x320 1,809 → 2,072 (1.15x), 1024x1024 240 → 247 (1.03x);
RTX A400 — Pyramid 2,403 → 6,315 (2.63x), the larger frames 1.02–1.06x.
Larger frames are bound by CuPy's double-precision Poisson and Gamma
samplers. See the GPU guide. - The NumPy path skips whole-frame identity arithmetic (adding a zero offset,
dividing by a unit gain, multiplying by a unit avalanche-gain map) and reuses
the expectation sum as the truth: 1–5% faster (1024x1024: 13.4 → 14.1 frames/s
on the same host), with identical seeded output. It remains bound by NumPy's
Poisson sampler (about 80% of a 1024x1024 frame), whose single sequential
stream cannot be threaded without changing seeded frames.
Fixed
benchmarks/bench_devices.pyreported the CPU of Arm hosts asaarch64;
it now reads the model fromlscpu(e.g.Neoverse-N1) when
/proc/cpuinfohas no model name.
getframes 2.4.0
Added
device="gpu:N"anddevice="auto". Everydeviceargument (Camera,
get_backend, and the CLI's[camera]table) now speaks the AO stack's
vocabulary (aocore CONVENTIONS 8.1):"cpu","gpu","gpu:N"for CUDA
deviceN, and"auto", which picks the GPU when CuPy is installed and sees
a device and the CPU otherwise. A"gpu:N"beyond the devices CuPy sees
raises aValueErrornaming the count. The old spellings ("numpy",
"cuda","cupy") still work, now case-insensitively. A GPU camera is
pinned to its card: the fixed-pattern maps and the cuRAND streams are created
on it and every camera method runs with it current, so a"gpu:1"camera
works whichever device is current at the call, andwith_configkeeps it.
New:Camera.device_id,ArrayBackend.device_id,ArrayBackend.spec
("cpu"or"gpu:N") andArrayBackend.activate()(the device context,
for calling the low-levelnoisefunctions on another card).precision="single"/"double". The working precision takes the
shared names (aocore CONVENTIONS 8.2), with"float32"/"float64"kept as
aliases: onCamera, as a newprecisionkeyword on
Scene.photon_rate_map/photoelectron_rate_map(besidedtype), and on the
noisefunctions that take afloat_dtype(simulate_frame,
fixed_pattern_maps,dark_signal_map,photo_signal_map). Adtypeand a
precisionthat disagree raiseValueError.getframes.resolve_precision
maps any of these names to the NumPy dtype.Camera.precisionstill reports
the dtype name ("float32"/"float64") whichever spelling was passed.
dataset.pairs(dtype=...)is unchanged: it is the host storage type of the
finished arrays, not a working precision.- The CLI's
[camera]table takes adevicekey. - Conformance tests for the device and precision vocabulary, including that
each precision name selects the same dtype as in aocore. - Edge-flux conformance for every PSF model.
tests/test_conformance.py
now uses aocore 0.1.3's image-builder checks (check_point_source_centring,
check_point_source_flux) instead of feeding analytic PSFs through the
OPD-driven checks with a dummy OPD, and addscheck_edge_flux_lossfor
Gaussian, Moffat, elliptical Gaussian, Airy and array PSFs, guarding the
2.3.0 fix. Thedevextra pinsaocore>=0.1.3,<0.2; the runtime
requirement is unchanged.
Changed
- An unknown
devicestring now raisesValueErrorlisting the accepted words
('cpu', 'gpu', 'gpu:N' ... or 'auto'), and a non-stringdevicea
TypeError.device="gpu"with CuPy installed but no CUDA device raises
RuntimeErrorat construction rather than failing at the first frame. Camera.__repr__shows the GPU number (device='gpu:0').- The noise functions'
float_dtypedefault is nowNone(still float64).
Fixed
dataset.pairswith a GPU camera failed on an implicit CuPy-to-NumPy
conversion; it now copies each frame to the host throughto_numpy, as do
the CLI's.npy/.npzwriters now that the CLI can select a GPU.
getframes 2.3.0
Fixed
MoffatPSF,EllipticalGaussianPSFandAiryPSFno longer put light that
falls off the frame back onto it. They normalised their stamp after
clipping it to the frame, so a source on the edge column deposited its full
flux instead of the part that lands on the detector (a Moffat star centred on
column 0 kept 100% of its light, whereGaussianPSFcorrectly keeps about
half). The stamp is now normalised before clipping, which follows the AO stack
convention that light lost at a detector edge is lost, not renormalised
(aocore CONVENTIONS 3.3). Sources wholly inside the frame are unchanged.
Changed
- Generic primitives now come from
aocore. getframes is part of an
adaptive-optics stack whose shared conventions and primitives live in
aocore, now a core dependency
(aocore>=0.1.2,<0.2).noise.block_sumisaocore.block_sum, re-exported
so existing imports keep working; it accepts everything it did before and now
also takes a(fy, fx)factor and leading batch axes, and raises a clear
ValueErrorfor a factor below 1 or a shape that does not divide (previously
a bare reshape error, or aZeroDivisionErrorfor a factor of 0).
Frame.binnedsums through it.AiryPSFandThermaltake their radians per
arcsecond fromaocore.ARCSEC_TO_RAD, andVignettingits centred grid from
aocore.coordinate_grid. Every output is bit-for-bit unchanged.
Added
- Conformance tests (
tests/test_conformance.py) run the
aocore.conformancechecks that apply to a detector package: every PSF model
deposits unit flux, a source at(n - 1) / 2and the vignetting pattern are
centred on the optical axis for odd and even windows.
2.2.0
[2.2.0] - 2026-08-24
Added
-
CameraConfig.read_noise_correlated_fraction. The part of read noise that
is common to every read of a nondestructive ramp, and so cancels when two
reads are differenced. Without it every read was an independent draw and
correlated double sampling could only ever cost a factor ofsqrt(2)— but a
real CDS frame is often quieter than a single read, because reference-level
drift, bias settling, and 1/f noise persist between the two reads and
differencing removes them. Defaults to0, which is the previous behaviour.
Only a differenced measurement can constrain it; single-read data cannot. -
Vega-system J, H, and Ks bands.
Bandpass.johnsonpreviously stopped at
I, so any near-infrared work had to leave the Vega system for AB — a
different magnitude for the same star, by +0.89 (J), +1.37 (H), and +1.84
(Ks). The zero points are derived from the 2MASS absolute calibration (Cohen,
Wheaton & Megeath 2003) through the same integral the AB path uses, so the
two systems now differ only in the reference spectrum and not in the
convention.SpectralBandpass.johnsongained the matching tophats, which are
the same shapes the AB survey table already used: a filter's shape is a
property of the filter, not of the magnitude system. -
Correlated double sampling as a first-class readout mode.
Camera.correlated_double_sampleresets, reads the pedestal, integrates, reads
again, and returns the signedint32ADU difference — the frame a camera in
CDS mode actually delivers. It shares one reset-correlated readout core with
nondestructive_series, so kTC noise and fixed bias structure cancel, read
noise grows bysqrt(2), common mode is partly removed through its AR(1)
correlation, and reset settling leaves its pedestal-to-signal residual. The
interval-proportional bias rate survives differencing by construction and is
documented as a dark-subtractable pedestal.pedestal_interval_smodels a
finite reset-to-read delay.correlated_double_sample_spectralis the
wavelength-resolved twin, standing to it asexpose_spectraldoes to
expose; both spectral entry points now share one QE-folding step, so a cube
cannot have QE applied twice or applied differently per readout mode. -
Global-reset nondestructive read series for eAPD arrays.
Camera.nondestructive_seriesanddark_nondestructive_seriesaccumulate
Poisson charge between resets, preserve a ramp's kTC realization, redraw read
noise on every sample, and expose ramp/read timing in frame metadata. -
A measured C-RED One preset and hybrid-array readout structure. The new
first_light_imaging_cred_onepreset separates the complete camera from the
bareleonardo_saphiradetector and models its 32 interleaved output channels,
edge-dependent pedestal/noise, stable per-channel offsets and noise scales, and
correlated frame-wide readout pedestal. Its spatial and temporal dark-frame
parameters are constrained by the July 2026 CRED1 dark stack. -
Reset-aware stack characterization.
nondestructive_stack_statistics
measures strong-reset cadence, ramp slopes, common mode, CDS/pixel noise,
interleaved channels, and edge structure without treating correlated reads as
independent exposures.ramp_photon_transferfits conversion gain and
split-ramp response nonuniformity from repeated NDR ramps. -
examples/16_detector_showcase.pyrenders an animated WebP that exposes one
incident photon field on a CCD, EMCCD, sCMOS, and eAPD side by side, each panel
overlaid with the frame rate that camera sustained on the running machine. The
clip is the README header image.
Changed
-
Refined the measured C-RED One response across gain and read rate. Fixed
avalanche-gain nonuniformity, sublinear gain noise, NDR read-interval scaling,
observed 42/3/5-read electronic cadences, reset settling, and separate broad
side/narrow row-edge pedestal terms now reproduce the local operating-point
stacks. The C-RED One and SAPHIRA QE curves were corrected to the manufacturer
plot: 60% through 950 nm, linear to 80% at 1450 nm, then 80% to 2500 nm. -
The README follows the documentation-first structure used across the sibling
projects: docs link, showcase clip, install, quickstart, benchmarks, then the
feature list. The preset table and the dark-frame model walkthrough now live
only in the presets and noise-model guides, which they already duplicated. -
Refreshed
benchmarks/device-results.{json,md}on the RTX 5090 / Ryzen 9
9950X3D reference machine against getframes 2.1.1, NumPy 2.2.6, and CuPy 14.1.1.
Fixed
-
Benchmark provenance now records the CuPy version when CuPy is installed
under a CUDA-specific wheel name (cupy-cuda12x/cupy-cuda11x). The metadata
block previously reportedcupy: nullon exactly the machines that had
produced the GPU column. -
Persistent cameras now cache the dark-signal expectation. Repeated exposures
at the same exposure time, temperature, precision, and detector configuration
reuse the full-detector DSNU/hot-pixel/glow map; changing either physical key
rebuilds it. Seeded stochastic frames and detector truth remain unchanged. -
sCMOS per-pixel read noise is now a fixed property of the sensor. The
per-pixel read-noise RMS map implied byread_noise_nonuniformitywas drawn from
the per-frame generator, so it was re-randomised in every frame. Single-frame
spatial statistics were unaffected, but every pixel ended up with the same
expected noise through time, which is not how an sCMOS behaves: each pixel has
its own source-follower and column ADC. The map is now built once from
fixed_pattern_seedand cached inFixedPatternMaps, alongside PRNU and DSNU.
Verified against dark stacks from three real back-illuminated sCMOS cameras
(KURO 1200B, Prime 95B, Marana 4.2B-11): splitting a stack in half and
correlating the two per-pixel temporal-variance maps gives r = 0.89–0.94 on the
real detectors and r = 0.004 with the old model, now r ≈ 0.96.
This changes generated pixel values for any configuration with
read_noise_nonuniformity > 0;read_noise_eand the spatial statistics of a
single frame are unchanged.
Added
DetectorWorkspaceplus optionalworkspace=/ caller-ownedout=execution
on scalar and spectral camera exposures. Full-detector ROI inputs, private
photo/total scratch, and digitised destinations can now be reused without
allowing returned truth or default frames to alias mutable scratch. The
workspace is device/shape/precision-bound and rejects concurrent use.CameraConfig.charge_diffusion_fwhm_px,charge_diffusion_kernel(), and
apply_charge_diffusion()make lateral detector charge spreading available to
focal-plane simulators at their own oversampling. The OCAM2K preset declares
its measured 0.37-pixel FWHM; under-resolved kernels fail explicitly, while
native-resolution camera frames state in metadata that diffusion was not
applied rather than silently becoming a numerical no-op.- Full-detector region-of-interest simulation through
CameraConfig.roi=(left, top, width, height). Cameras accept and return
ROI-shaped arrays while evaluating detector physics and fixed patterns on the
native sensor before cropping.Camera.sensor_resolution,
CameraConfig.output_resolution, and active amplifier-boundary properties make
the full-versus-ROI geometry explicit. Exact full-detector split pixels remain
available when an ROI is active. getframes.analysis.characterize: detector characterisation from frame
stacks. Wherephoton_transfer_curvedrives a simulated camera, this works
on stacks that already exist -- raw data off a real detector, or simulated
frames.stack_statisticsreduces any iterable of frames (arrays,Frames, a
dark_seriesgenerator, your own file reader) to per-pixel temporal mean and
variance in one streaming pass, so stacks larger than memory are fine.
characterize_darkthen measures conversion gain, read noise (with its
per-pixel map, log-normal width and RTS tail), dark current, bias and DSNU from
darks alone -- no flat field needed, because dark charge is Poisson and so
serves as the PTC charge source.characterize_flatadds full well, PRNU and
linearity.DarkCharacterization.to_config()returns aCameraConfig, closing
the loop: measure a real camera, then simulate it.StackStats.split=True
additionally givestemporal_repeatability, the split-half test that separates
genuine per-pixel noise structure from chi-squared sampling scatter.
New guide (docs/guides/characterization.md) and example
(examples/15_detector_characterization.py).read_noise_rts_fraction/read_noise_rts_factor: an optional second,
noisier read-noise population modelling the random-telegraph-signal (RTS) pixels
of a real sCMOS array. Measured on three real sensors, ~0.5% of pixels sit above
3x the median read noise where a single log-normal predicts ~0.01%; these are the
pixels that limit faint-source detection. Defaults to off.detector_glow_edge_scale_px: makesdetector_glow_e_per_sedge-concentrated
with an exponential falloff, instead of uniform, modelling amplifier glow emitted
at the array periphery. Renormalised so the array mean is unchanged; still fixed
and exposure-scaling, so an exposure-matched master dark removes it. Defaults to
0(uniform, the previous behaviour).
Changed
-
Float32 cameras now cache amplifier gain/offset and structured-bias maps in
float32 instead of retaining detector-sized float64 coefficients. The float64
reference path is unchanged. A matched 2048x2048 structured-digitization
benchmark improved by 1.328x on the local CPU and 1.382x on a Quadro P620 while
persistent coefficient storage fell from 96 MiB to 48 MiB. -
The `princeton_instruments...
2.1.1
[2.1.1] - 2026-07-26
Added
- Exact multi-amplifier ROI geometry and measured row-major gain/offset response
fields. The OCAM2K preset now declares its eight-output 4x2 layout; cropped
configurations can preserve their true split pixels. - Optional CuPy detector execution via
Camera(..., device="gpu"). Photon-rate,
electron,FrameTruth, and digitised ADU arrays remain device-resident through
scalar and spectral exposure paths, including all detector artifact models and
both binning modes.Frame.device,get_backend,get_array_module, and
to_numpymake the execution and host-copy boundaries explicit. - CUDA parity tests cover seeded reproducibility, CPU/GPU statistics, spectral
truth, CCD/CMOS/sCMOS/EMCCD/eAPD paths, fixed structure, gain, and detector
artifacts. The benchmark runner accepts--device gpu|both. - Added a reproducible paired-device bulk-throughput suite, self-describing JSON
artifact, rendered reference table, and README/GPU-guide CPU-versus-GPU results
for representative WFS, EMCCD, eAPD, and large science-frame workflows.
Changed
- Re-characterized the OCAM2K preset from the Keck unit report and supplied
detector studies: approximately 28 output e-/ADU (21.43 ADU per input electron
at EM gain 600), 0.360 e- input-referred read noise, 1.579 e-/pixel/s dark
current, and a separately derived 0.004912 e-/pixel/frame CIC term. The output
saturation charge and representative Keck bias now use the same conversion
convention. - Fixed PRNU/DSNU/hot-pixel, amplifier, bias-structure, and defect maps are built
once on persistentCameraconstruction rather than regenerated for every
frame. This preserves the fixed-pattern contract and materially improves both
CPU and GPU warm exposure throughput. - Optimized warm detector execution with in-place signal construction and
digitization, direct full-array Gamma sampling, working-precision CPU/GPU
Gaussian and Gamma draws, and a reusable per-call GPU seed stream. The RTX
5090 reference improves GPU CMOS throughput by 1.36x–1.42x and GPU EMCCD/eAPD
throughput by 2.35x–2.41x over the initial implementation without changing the
detector distributions.
Fixed
- Separated image-area and post-multiplication saturation with the optional,
backwards-compatibleCameraConfig.output_full_well_e. Gain-stage input charge
now clips atfull_well_ebefore multiplication. The OCAM2K preset uses Andor's
published 270,000 e- image well and Keck's measured 10,000-count output ceiling,
instead of incorrectly clipping multiplied signal at 250 e- (25 ADU).
getframes 2.1.0
[2.1.0] - 2026-07-19
Added
- Five scientific-camera presets used by the Keck tip/tilt and low-bandwidth WFS
detector trade example: KURO 1200B, Prime 95B, Marana 4.2B-11, QHY530 Pro II,
and Aries 6504 Pro. All five now include a wavelength-resolvedqe_curve:
Marana and Aries from Andor's/Gpixel's published data (the Aries
QE x fill factortrace is digitized from Gpixel's graph); KURO 1200B,
Prime 95B, and QHY530 Pro II are digitized from published manufacturer QE
plots. - A
quantum_efficiencyoverride onCamera.expose_series, matching
Camera.exposeand supporting pre-integrated spectral electron-rate series. - A Keck/SciMeasure Little Joe CCD39 preset with the manufacturer CCD39-01
standard-AR/no-window spectral-response curve. getframes.analysis.matched_filter_centroid, a background-insensitive
cross-correlation centroid with sub-pixel peak refinement.Frame.binned(factor, method=...), post-read digital binning of a frame into
factor x factorsuper-pixels (sum or mean).CameraConfig.supported_binningsandCameraConfig.binning_method— binning is
now a first-class config parameter. The Keck-trade presets set these directly and
drop theextra.detector_modesper-binning tables; cameras with more than one
read-noise operating point keep a slimextra.read_modeslist instead.- Native pixel binning in the signal chain:
Camera.expose,Camera.expose_series,
andnoise.simulate_frameacceptbinningandbinning_mode."digital"
(post-read, the default) reads each native pixel with its own read noise then sums,
so binned read noise grows asbinning;"on_chip"(pre-read charge-domain /
hardware binning) sums the charge before the amplifier, so a single read noise is
applied per super-pixel. Exposednoise.block_sumfor the super-pixel summation. getframes.analysis.centroidnow accepts a per-pixel arraybackground(e.g. a
master sky+dark frame) and an optionalthreshold(scalar or per-pixel noise
map), making it a calibrated thresholded centre-of-gravity estimator suitable
for a real-time controller. Both additions are backwards compatible.- A Keck LGS tip/tilt and low-bandwidth wavefront-sensor detector trade-study
example, including physically sampled AO/sub-aperture PSFs, complete detector
Monte Carlo simulations, blackbody NGS weighting of each camera's QE curve
(--ngs-teff), the 600–950 nm TTS/LBWFS arm bandpass folded into the zero-point
bands, Monte Carlo standard errors on every point, a per-camera closed-loop
cadence optimisation (a physicala·f + b·f²noise model fitted to the Monte
Carlo data, saturation-aware, against a Tyler tilt-lag disturbance model), and
a best/worst-performer frame gallery figure beside the trade curves.
Fixed
- Type-checking compatibility with newer NumPy (2.2): tightened stub types for
_stamp_bounds/_radial_gridshape arguments, pinnedfloat64on a few
np.linspaceconstructions, and dropped now-unusedtype: ignorecomments
around thetrapz/trapezoidshim. No behavioural change.
Full Changelog: 2.0.0...2.1.0
Public Release of getframes
getframes 2.0.0
getframes is a Python library for generating physically realistic synthetic
camera frames — dark, bias, flat, and rendered star fields — from CCD, CMOS,
EMCCD, eAPD, and sCMOS detectors. It models the complete
photon → electron → ADU signal path with auditable noise physics, so you can
build and test image-processing pipelines against known ground truth.
This is the first PyPI release. The full public API is frozen under Semantic
Versioning for the 2.x series.
Install
pip install getframesQuick start
import getframes as gf
# Dark frame from a built-in preset
cam = gf.Camera.from_preset("andor_ikon_m934")
frame = cam.dark_frame(exposure=60.0, temperature=-60.0, seed=0)
frame.data # 2-D NumPy array of ADU
frame.truth # noise-free ground truth
# Render a star field
scene = gf.Scene(
shape=(512, 512),
optics=gf.Telescope(
aperture_diameter_m=2.5,
throughput=0.3,
plate_scale_arcsec_per_pixel=0.4,
band=gf.Bandpass.johnson("V"),
),
psf=gf.MoffatPSF(fwhm_arcsec=1.1, beta=3.0),
sources=[gf.PointSource(x=256, y=256, magnitude=18.0)],
sky=gf.Sky(surface_brightness_mag_arcsec2=21.5),
)
frame = cam.with_config(resolution=(512, 512)).observe(scene, exposure=300.0, seed=1)What's included
Detector physics
The full noise chain for five sensor types: read noise, dark current
(temperature-scaled with a doubling-temperature model), PRNU/DSNU fixed-pattern
noise, shot noise, a unified stochastic EM/avalanche gain stage (EMCCD and eAPD),
clock-induced charge, digitization, bias, and saturation.
Optional realism effects — all off by default — add CTI trailing, charge blooming,
inter-pixel capacitance, kTC/reset noise, multi-amplifier readout offsets,
cosmic-ray tracks, defect maps, and polynomial nonlinearity.
Scene rendering
Render astronomical sources through a PSF and telescope onto a detector with
Camera.observe. Sources include point sources, extended sources (Sérsic profiles
or array cutouts), spatially uniform illumination, and large catalogs. PSF models
cover Gaussian, Moffat, Airy (with central obstruction), elliptical Gaussian, and
user-supplied arrays. The scene layer supports vignetting, radial distortion, and
WCS sky-coordinate projection.
Calibration
combine stacks frames into masters (mean, median, or sigma-clip). calibrate
performs the standard bias/dark/flat reduction so a reduced frame can be compared
directly against Frame.truth — the noise-free ground truth attached to every
frame.
Time series and observations
Camera.observe_series returns an Observation — a structured sequence of frames
with per-frame timestamps, pointing offsets, and per-source truth light curves.
Sources can carry LightCurve brightness variations (box transit, sinusoidal, or
arbitrary function). Pointing models jitter, linear drift, and programmed dither.
Quantitative radiometry
AB-system and Vega-system photometry via Bandpass.ab (SDSS ugriz, Gaia, 2MASS)
and Bandpass.johnson (Johnson UBVRI). Spectral energy distributions with
SED.from_flux_density, real measured transmission curves via
SpectralBandpass.from_file, interstellar extinction (Cardelli–Clayton–Mathis
1989), and thermal background graybody emission for IR scenes. astropy.units
quantities are accepted wherever wavelength or flux density is expected.
Dataset generation and CLI
getframes.dataset.pairs() streams {"raw": ADU array, "truth": electrons array}
pairs reproducibly to disk for training ML pipelines. The getframes CLI
(presets / generate config.toml / dataset config.toml) turns a shareable
TOML file into frames or a dataset without writing Python.
Preset library
Ten built-in camera presets covering the full sensor-type range:
| Preset | Type | Notes |
|---|---|---|
andor_ikon_m934 |
CCD | Deep-cooled back-illuminated scientific CCD |
andor_ixon_ultra_888 |
EMCCD | Single-photon-sensitive |
leonardo_saphira |
eAPD | HgCdTe IR avalanche array |
zwo_asi2600mm |
CMOS | Sony IMX571 cooled CMOS |
hamamatsu_orca_fusion |
sCMOS | Back-thinned, per-pixel read noise |
generic_ccd / generic_cmos / generic_emccd / generic_eapd / generic_scmos |
— | Idealised references for testing |
Validation
tests/test_validation.py asserts the physics against analytic and published
references: AB/Vega zero points, the gain-stage excess noise factor,
CTI/IPC/blooming charge conservation, PSF flux conservation, PTC parameter
recovery, and reduced-frame truth recovery.
Requirements
Python 3.10–3.13. Core dependencies: numpy, scipy, astropy. matplotlib
is available as pip install getframes[examples] for the runnable examples.