"""#98 pixel proof: locate the FLASHING lamp by temporal variance. A flashing lamp alternates between two appearances, so across frames of an otherwise-static cockpit it is the pixels that CHANGE. Comparing a leaking run against an undamaged control run localises the flash without anyone having to guess panel coordinates first. """ import glob, sys import numpy as np from PIL import Image # The 2D cockpit panels only -- excludes the 3D viewport so world/particle # motion cannot be mistaken for a lamp. (Viewport is ~x 280-1170, y 225-660.) PANEL_LEFT = (slice(0, 300), slice(0, 330)) # (rows, cols) -- the coolant panel def frames(prefix, take=40): # the last frame is often truncated (the process is killed mid-write), and a # corrupt frame must not be mistaken for change -- skip anything unreadable fs = sorted(glob.glob("lamp_%s_*.png" % prefix)) out = [] for f in reversed(fs): if len(out) >= take: break try: out.append(np.asarray(Image.open(f).convert("L"), dtype=np.float32)) except Exception: continue if not out: raise SystemExit("no readable frames for %s" % prefix) return np.stack(out) def report(prefix): a = frames(prefix) print("%-6s %d frames, %s" % (prefix, a.shape[0], a.shape[1:])) sd = a.std(axis=0) # per-pixel temporal std-dev panel = sd[PANEL_LEFT] print(" coolant-panel std: mean=%.3f max=%.1f pixels>10: %d" % (panel.mean(), panel.max(), int((panel > 10).sum()))) if panel.max() > 10: ys, xs = np.where(panel > 10) print(" hotspot bbox rows %d-%d, cols %d-%d (centroid %d,%d)" % (ys.min(), ys.max(), xs.min(), xs.max(), int(ys.mean()), int(xs.mean()))) return sd print("=" * 64) leak = report("leak") print() ctl = report("ctl") print("\n" + "=" * 64) pl, pc = leak[PANEL_LEFT], ctl[PANEL_LEFT] print("VERDICT") print(" leaking run : %d panel pixels flashing (std>10)" % int((pl > 10).sum())) print(" control run : %d panel pixels flashing (std>10)" % int((pc > 10).sum())) if (pl > 10).sum() > 0 and (pc > 10).sum() == 0: print(" -> a lamp flashes ONLY when a condenser is leaking. PASS") elif (pl > 10).sum() == 0: print(" -> nothing flashes on the leaking run. FAIL") else: print(" -> both runs show panel motion; inspect the bboxes above")