"""Independent pixel-only inverse arm for fragmented_circle_center. The oracle receives one RGB raster. It segments the public dark contour and the four public marker colors, fits a circle to all disconnected contour pixels by least squares, and selects the marker centroid nearest the recovered center. It imports no renderer, latent scene, prompt, generator, verifier, or gold code. """ from __future__ import annotations import math import numpy as np from PIL import Image _DARK = np.array((40.0, 48.0, 58.0), dtype=np.float64) _PALETTE = { "red": np.array((205.0, 67.0, 85.0), dtype=np.float64), "blue": np.array((45.0, 103.0, 210.0), dtype=np.float64), "green": np.array((38.0, 151.0, 105.0), dtype=np.float64), "orange": np.array((232.0, 132.0, 39.0), dtype=np.float64), } def measure_image(image: Image.Image) -> dict: """Recover global circle evidence and the color-bound center decision.""" arr = np.asarray(image.convert("RGB"), dtype=np.float64) if arr.ndim != 3 or arr.shape[2] != 3: raise ValueError("expected an RGB image") dark_distance = np.linalg.norm(arr - _DARK, axis=2) dark_mask = dark_distance < 48.0 ys, xs = np.nonzero(dark_mask) if len(xs) < 700: raise ValueError("abstain: insufficient disconnected contour evidence") # Algebraic fit x^2+y^2 = 2*cx*x + 2*cy*y + c. Sampling all pixels of the # constant-width fragmented stroke is unbiased because arclets span the circle. x = xs.astype(np.float64) + 0.5 y = ys.astype(np.float64) + 0.5 design = np.column_stack((2.0 * x, 2.0 * y, np.ones_like(x))) target = x * x + y * y coeff, _, rank, _ = np.linalg.lstsq(design, target, rcond=None) if rank < 3: raise ValueError("abstain: contour cannot determine a circle") cx, cy, constant = (float(value) for value in coeff) radius_sq = constant + cx * cx + cy * cy if radius_sq <= 0.0: raise ValueError("abstain: invalid fitted radius") radius = math.sqrt(radius_sq) radial = np.hypot(x - cx, y - cy) fit_rms = float(np.sqrt(np.mean((radial - radius) ** 2))) angles = (np.arctan2(y - cy, x - cx) + 2.0 * math.pi) % (2.0 * math.pi) angular_bins = int(len(np.unique(np.floor(angles / (2.0 * math.pi) * 24.0).astype(int)))) if fit_rms > 5.0 or angular_bins < 12: raise ValueError("abstain: weak or non-circular global contour evidence") palette_names = tuple(_PALETTE) palette = np.stack([_PALETTE[name] for name in palette_names], axis=0) color_distances = np.linalg.norm(arr[:, :, None, :] - palette[None, None, :, :], axis=3) nearest = color_distances.argmin(axis=2) nearest_distance = color_distances.min(axis=2) centroids: dict[str, tuple[float, float]] = {} for index, name in enumerate(palette_names): mask = (nearest == index) & (nearest_distance < 58.0) my, mx = np.nonzero(mask) if not 250 <= len(mx) <= 650: raise ValueError(f"abstain: {name} marker is absent or malformed") centroids[name] = (float(mx.mean() + 0.5), float(my.mean() + 0.5)) ranked = sorted( (math.hypot(mx - cx, my - cy), name) for name, (mx, my) in centroids.items() ) runner_up_gap = float(ranked[1][0] - ranked[0][0]) if runner_up_gap < 14.0: raise ValueError("abstain: center candidates are raster-ambiguous") return { "decision": ranked[0][1], "runner_up_gap": runner_up_gap, "circle_center": [cx, cy], "circle_radius": radius, "fit_rms": fit_rms, "angular_bins": angular_bins, "evidence_pixels": int(len(xs)), } def decision_from_image(image: Image.Image) -> str: return str(measure_image(image)["decision"])