Move the processing code under pipeline/

Preparing to merge this repository into a combined astrophotography
repo. session-scripts/ becomes pipeline/ because the scripts import
layout.py from their own directory and must stay together, and because
'pipeline' says what it is rather than how it came about. observing/
stays at the top level: observing plans are not processing code.
This commit is contained in:
laurence 2026-07-21 17:13:54 +01:00
parent 653ca103cd
commit c6299f41ab
53 changed files with 0 additions and 0 deletions

70
pipeline/analyse.py Normal file
View file

@ -0,0 +1,70 @@
"""Pass 1: measure every calibrated frame and cache its star list.
Frames are 4788x3194 float32 (61 MB each) and the machine has little free RAM,
so each frame is opened, measured and released one at a time. The star lists are
cached to an npz because both the registration pass and the plate solve need
them, and re-detecting costs more than re-reading a small array.
Recorded per frame: sky background and its rms, the number of detections, and a
median FWHM derived from sep's half-flux radius. The FWHM is the seeing metric
used later to pick the registration reference and to weight the stack.
"""
import glob
import os
import re
import numpy as np
import sep
from astropy.io import fits
import layout
SRC = layout.SESSION
CACHE = layout.path("_stars.npz")
os.makedirs(os.path.dirname(CACHE), exist_ok=True)
NAME_RE = re.compile(r"-(Luminance|Red|Green|Blue)-BIN2-W-300-(\d+)\.fit$")
def frame_list():
out = []
for path in sorted(glob.glob(layout.path("calibrated-*.fit"))):
m = NAME_RE.search(path)
if m:
out.append((path, m.group(1), int(m.group(2))))
return out
if __name__ == "__main__":
frames = frame_list()
print(f"{len(frames)} calibrated frames")
store, meta = {}, []
for path, filt, idx in frames:
with fits.open(path, memmap=False) as hd:
data = hd[0].data.astype(np.float32)
bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3)
back_med = float(np.median(bkg.back()))
rms = float(bkg.globalrms)
sub = data - bkg.back()
objs = sep.extract(sub, 5.0, err=rms, minarea=9, deblend_cont=0.005)
objs = objs[(objs["flag"] == 0) & (objs["npix"] > 12) &
(objs["npix"] < 2000)]
objs = objs[np.argsort(objs["flux"])[::-1][:400]]
rad, _ = sep.flux_radius(sub, objs["x"], objs["y"], 6.0 * objs["a"],
0.5, normflux=objs["flux"])
fwhm = float(np.median(rad) * 2.0)
key = f"{filt}_{idx:03d}"
store[key + "_xy"] = np.column_stack([objs["x"], objs["y"]])
store[key + "_flux"] = objs["flux"]
meta.append((key, os.path.basename(path), filt, idx, len(objs), fwhm,
back_med, rms))
print(f"{key:16s} stars={len(objs):4d} fwhm={fwhm:5.2f}px "
f"bg={back_med:8.1f} rms={rms:6.1f}")
del data, sub, bkg, objs
np.savez_compressed(
CACHE,
meta=np.array(meta, dtype=object),
**store,
)
print("cached ->", CACHE)