ink-lens / gate.py
SharpMeow's picture
Ink Lens 0.1.0
c368438 verified
Raw History Blame Contribute Delete
6.51 kB
"""Ink Lens: the control-set threshold and the region status (numpy only).
The rule, frozen in docs/prereg/2026-10-20-ink-lens-gate.md before any map of it existed:
- F and R are the mean ink probabilities (0 to 1) of the region with its layers as stored and reversed, each averaged
over the same depth windows and readers.
- Papyrus: pixels whose middle CT layer is above the region's 5th percentile of CT and above 0 (so air and the
masked outside of a segment do not lower the threshold).
- T is the 99th percentile of R over the papyrus pixels: the level that only 1 % of the control's own pixels pass.
- Kept ink: papyrus pixels with F > T. Excess: the fraction of papyrus pixels with F > T, divided by 0.01.
- Status: ink when excess >= 3, weak when 1.5 <= excess < 3, none below 1.5, too_small under 100,000 papyrus pixels
(about 3 mm square at 9.6 um), unreadable when a map is not finite, constant, or identical to its control.
A region with no text, or where the model reads the reversed volume as well as the stored one, has an excess near 1
and the status none: the check can fail. Everything it keeps is model output, not a reading.
"""
from __future__ import annotations
RULE_ID = "ink-lens-gate-v1"
CT_PERCENTILE = 5.0
CONTROL_PERCENTILE = 99.0
BASE_RATE = 0.01
INK_EXCESS = 3.0
WEAK_EXCESS = 1.5
MIN_PAPYRUS = 100_000
STATUSES = ("ink", "weak", "none", "too_small", "unreadable")
STATUS_TEXT = {
"ink": "ink above the control: at least 3 times as many pixels pass the control's 99th percentile as the control",
"weak": "some ink above the control: 1.5 to 3 times the control's share; look before trusting it",
"none": "no ink above the control: the stored order passes the control's 99th percentile no more than 1.5 times "
"as often as the control itself",
"too_small": "too little papyrus in the region to judge (under 100,000 pixels, about 3 mm square)",
"unreadable": "the maps cannot be judged (not finite, constant, or the same as the control)",
}
class GateError(ValueError):
"""Maps the gate cannot take at all (wrong shapes); the message says why."""
def papyrus(ct_middle, percentile=CT_PERCENTILE):
"""Boolean mask of papyrus pixels in a region: middle CT layer above its own percentile and above 0."""
import numpy as np
ct = np.asarray(ct_middle)
if ct.ndim != 2:
raise GateError(f"the CT layer is 2-D, not {ct.shape}")
nz = ct[ct > 0]
if nz.size == 0:
return np.zeros(ct.shape, bool)
floor = max(float(np.percentile(ct, percentile)), 0.0)
return ct > floor
def _unreadable(F, R, mask):
import numpy as np
reasons = []
for name, m in (("stored-order map", F), ("control map", R)):
if not np.all(np.isfinite(m)):
reasons.append(f"the {name} has values that are not finite")
elif mask.any() and float(m[mask].max()) - float(m[mask].min()) < 1e-6:
reasons.append(f"the {name} is constant on the papyrus")
if not reasons and mask.any() and np.array_equal(F[mask], R[mask]):
reasons.append("the stored-order map is identical to its control (a broken run, or the control was not run)")
return reasons
def decide(F, R, ct_middle, min_papyrus=MIN_PAPYRUS):
"""The gate on one region: threshold, excess, status and counts, as a JSON-able dict."""
import numpy as np
F = np.asarray(F, dtype=np.float32)
R = np.asarray(R, dtype=np.float32)
if F.shape != R.shape or F.ndim != 2:
raise GateError(f"the map {F.shape} and its control {R.shape} must be 2-D and the same shape")
mask = papyrus(ct_middle)
if mask.shape != F.shape:
raise GateError(f"the CT layer {mask.shape} and the maps {F.shape} differ in shape")
n = int(mask.sum())
out = {"rule": RULE_ID, "papyrus_px": n, "region_px": int(mask.size), "threshold": None, "excess": None,
"forward_above": None, "control_above": None, "status": None}
reasons = _unreadable(F, R, mask)
if reasons:
out.update(status="unreadable", reasons=reasons)
elif n < min_papyrus:
out.update(status="too_small")
else:
T = float(np.percentile(R[mask], CONTROL_PERCENTILE))
f_above = float((F[mask] > T).mean())
r_above = float((R[mask] > T).mean())
excess = f_above / BASE_RATE
status = "ink" if excess >= INK_EXCESS else "weak" if excess >= WEAK_EXCESS else "none"
out.update(threshold=round(T, 6), excess=round(excess, 4), forward_above=round(f_above, 6),
control_above=round(r_above, 6), status=status)
out["status_text"] = STATUS_TEXT[out["status"]]
out["note"] = "Model output, not a reading."
return out
def kept(F, decision, ct_middle):
"""F where it passes the control's threshold on papyrus, else 0 (float32). Nothing passes unless the status is
ink or weak."""
import numpy as np
F = np.asarray(F, dtype=np.float32)
out = np.zeros_like(F)
if decision.get("status") not in ("ink", "weak"):
return out
m = papyrus(ct_middle) & (F > float(decision["threshold"]))
out[m] = F[m]
return out
def to_u8(kept_map):
"""The kept map as uint8 for an overlay: 0 where nothing is kept (transparent in VC3D), else 1 to 255."""
import numpy as np
k = np.asarray(kept_map, dtype=np.float32)
out = np.zeros(k.shape, np.uint8)
on = k > 0
out[on] = np.clip(np.rint(k[on] * 254) + 1, 1, 255).astype(np.uint8)
return out
def label_shares(kept_map, ink, supervised, inner=64):
"""The preregistered label check: the share of labeled ink pixels kept and the share of labeled background
pixels kept, inside the supervision mask with an inner edge left out."""
import numpy as np
k = np.asarray(kept_map) > 0
m = np.asarray(supervised, bool).copy()
if inner:
m[:inner] = m[-inner:] = False
m[:, :inner] = m[:, -inner:] = False
ink = np.asarray(ink, bool)
ink_px, bg_px = int((m & ink).sum()), int((m & ~ink).sum())
ink_share = float((k & m & ink).sum()) / ink_px if ink_px else None
bg_share = float((k & m & ~ink).sum()) / bg_px if bg_px else None
passes = None if ink_share is None or bg_share is None else ink_share > bg_share
return {"ink_px": ink_px, "background_px": bg_px, "ink_kept_share": None if ink_share is None else round(ink_share, 5),
"background_kept_share": None if bg_share is None else round(bg_share, 5), "ink_share_larger": passes,
"inner": inner}