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

View file

@ -1,85 +0,0 @@
"""Diagnostic: how good is the coordinate registration, and how many
detections survive removal of the static sky?
astroalign only reports a few dozen matched stars, which is enough to define a
similarity transform but says nothing about how well it holds across a
4788 x 3194 field. This refines each frame's transform by nearest-neighbour
matching every detection against the reference frame's detections and refitting
a full affine, then quotes the residual. It also counts, per frame, how many
detections are left once everything coincident with a master-stack source is
removed - that residual population is the input to the tracklet search.
"""
import os
import numpy as np
from astropy.io import fits
from scipy.spatial import cKDTree
from skimage.transform import AffineTransform
import layout
SRC = layout.SESSION
OUT = layout.SESSION
CACHE = layout.path("_mo_dets.npz")
def load():
z = np.load(CACHE, allow_pickle=True)
meta = {r[0]: r for r in z["meta"]}
dets = {k: z[k] for k in meta}
return meta, dets
def refine(dets, ref_key="Luminance_002", tol=3.0):
"""Refit each frame's transform on all cross-matched detections."""
ref = dets[ref_key]
tree = cKDTree(ref[:, :2])
out = {}
for key, d in dets.items():
src = d[:, 2:4].astype(float) # native coords
cur = d[:, :2].astype(float) # astroalign-transformed
for t in (tol, 1.5):
dist, idx = tree.query(cur, distance_upper_bound=t)
ok = np.isfinite(dist)
if ok.sum() < 50:
break
tf = AffineTransform()
tf.estimate(src[ok], ref[idx[ok], :2].astype(float))
cur = tf(src)
dist, idx = tree.query(cur, distance_upper_bound=1.5)
ok = np.isfinite(dist)
out[key] = (cur, np.median(dist[ok]), np.percentile(dist[ok], 90),
ok.sum())
return out
def main():
meta, dets = load()
lum = [k for k in dets if k.startswith("Luminance")]
ref = refine(dets)
print("registration residual against reference frame (px):")
for k in sorted(dets):
_, med, p90, n = ref[k]
print(f" {k:16s} n={n:5d} median={med:.3f} p90={p90:.3f}")
# Static sky = every source in the deep luminance master.
with fits.open(layout.path("master-Luminance.fit")) as hd:
master = hd[0].data.astype(np.float32)
import sep
bkg = sep.Background(master, bw=64, bh=64, fw=3, fh=3)
m = sep.extract(master - bkg.back(), 2.5, err=bkg.globalrms, minarea=4,
deblend_cont=0.005)
print(f"\nmaster detections (static sky): {len(m)}")
mtree = cKDTree(np.column_stack([m["x"], m["y"]]))
print("\nresiduals after removing anything within 4 px of a master source:")
for k in sorted(lum):
cur = ref[k][0]
d, _ = mtree.query(cur, distance_upper_bound=4.0)
left = ~np.isfinite(d)
print(f" {k:16s} {left.sum():5d} / {len(cur)}")
if __name__ == "__main__":
main()