Download server/place.py from devmandan/qr-scan-difficulty-check: direct link, hf CLI and curl.
- Browser
- Download file 2.38 kB
-
https://huggingface.co/spaces/devmandan/qr-scan-difficulty-check/resolve/main/server/place.py
- Command line
-
hf download hf://spaces/devmandan/qr-scan-difficulty-check/server/place.py
-
curl -L -o place.py https://huggingface.co/spaces/devmandan/qr-scan-difficulty-check/resolve/main/server/place.py
2.38 kB
| """Place a picture on QR Scan Difficulty's scale from the five scanners' reads. | |
| The page's scale comes from a two-parameter item-response fit of all eight scanners at seven frame sizes: each | |
| (scanner, frame size) pair reads a code of difficulty b with chance 1 / (1 + exp(-a (theta - b))). Here the 35 curves | |
| of the five scanners that run on Linux are frozen as fitted (model.json), and a picture's b is the fit's own estimate | |
| for one code from its 35 reads: the most likely b under the fit's prior (normal, mean 0, sd 2.5). The log-posterior | |
| has one peak (its slope falls as b grows), so bisection on the slope finds it; the fit's clipped Newton step can | |
| bounce between two points when the readers are held fixed. A picture every pair read sits with the codes every | |
| scanner read at every size (the left pile); one no pair read sits with the codes no scanner read (the right pile). | |
| Otherwise d = 100 (b - lo) / (hi - lo), clipped to 0..100. | |
| """ | |
| import json | |
| from pathlib import Path | |
| import numpy as np | |
| MODEL = json.loads(Path(__file__).with_name("model.json").read_text()) | |
| PILE_LO, PILE_HI = -6, 106 | |
| def map_b(y: np.ndarray, theta: np.ndarray, a: np.ndarray, prior_b: float) -> float: | |
| """The most likely b given reads y by readers (theta, a): where the log-posterior's slope, the fit's gradient, | |
| is zero.""" | |
| def slope(b: float) -> float: | |
| p = 1.0 / (1.0 + np.exp(-np.clip(a * (theta - b), -30, 30))) | |
| return float(-(a * (y - p)).sum() - b / prior_b ** 2) | |
| lo, hi = -30.0, 30.0 # slope > 0 at lo and < 0 at hi for any reads | |
| for _ in range(80): | |
| mid = (lo + hi) / 2 | |
| if slope(mid) > 0: | |
| lo = mid | |
| else: | |
| hi = mid | |
| return (lo + hi) / 2 | |
| def place(reads: dict[str, list[int]], frames: list[int]) -> dict: | |
| """{"d": difficulty, "pile": "lo" | "hi" | None} from {scanner: [read at each frame size]}.""" | |
| order = [frames.index(f) for f in MODEL["frames"]] | |
| y = np.array([reads[k][i] for k in MODEL["decoders"] for i in order], dtype=float) | |
| if y.all(): | |
| return {"d": PILE_LO, "pile": "lo"} | |
| if not y.any(): | |
| return {"d": PILE_HI, "pile": "hi"} | |
| b = map_b(y, np.array(MODEL["theta"]).ravel(), np.array(MODEL["a"]).ravel(), MODEL["prior_b"]) | |
| d = 100 * (b - MODEL["lo"]) / (MODEL["hi"] - MODEL["lo"]) | |
| return {"d": round(min(100.0, max(0.0, d)), 1), "pile": None} | |