Spaces:
Running on Zero
Running on Zero
Download slotread/geometry.py from infinitylogesh/GUI-Decisions: direct link, hf CLI and curl.
- Browser
- Download file 2.58 kB
-
https://huggingface.co/spaces/infinitylogesh/GUI-Decisions/resolve/main/slotread/geometry.py
- Command line
-
hf download hf://spaces/infinitylogesh/GUI-Decisions/slotread/geometry.py
-
curl -L -o geometry.py https://huggingface.co/spaces/infinitylogesh/GUI-Decisions/resolve/main/slotread/geometry.py
2.58 kB
| """Boxes and masks: IoU, COCO polygons, and the radial (centre + rays) mask encoding.""" | |
| import math | |
| import numpy as np | |
| from PIL import Image, ImageDraw | |
| def box_iou(a, b): | |
| ix1, iy1, ix2, iy2 = max(a[0], b[0]), max(a[1], b[1]), min(a[2], b[2]), min(a[3], b[3]) | |
| inter = max(0.0, ix2 - ix1) * max(0.0, iy2 - iy1) | |
| union = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter | |
| return inter / union if union > 0 else 0.0 | |
| def poly_mask(polys, W, H): | |
| """COCO polygons (flat lists of x, y) -> boolean mask.""" | |
| im = Image.new("L", (W, H), 0) | |
| d = ImageDraw.Draw(im) | |
| for p in polys: | |
| if len(p) >= 6: | |
| d.polygon([(p[i], p[i + 1]) for i in range(0, len(p), 2)], fill=1) | |
| return np.array(im, dtype=bool) | |
| def mask_iou(a, b): | |
| inter = np.logical_and(a, b).sum() | |
| union = np.logical_or(a, b).sum() | |
| return (inter / union if union else 0.0), int(inter), int(union) | |
| def radial_encode(mask, K): | |
| """Centre: the point inside the mask farthest from its boundary (a centroid can fall outside a | |
| non-convex mask). Ray k at angle 2 pi k / K (0 = right, turning clockwise on screen): the distance to | |
| the farthest mask pixel along it. Returns (cx, cy, d) in pixels.""" | |
| from scipy.ndimage import distance_transform_edt | |
| H, W = mask.shape | |
| dt = distance_transform_edt(mask) | |
| cy, cx = np.unravel_index(int(dt.argmax()), dt.shape) | |
| diag = math.hypot(W, H) | |
| t = np.arange(0, diag, 0.5) | |
| ang = 2 * np.pi * np.arange(K) / K | |
| xs = np.rint(cx + np.outer(np.cos(ang), t)).astype(int) | |
| ys = np.rint(cy + np.outer(np.sin(ang), t)).astype(int) | |
| inb = (xs >= 0) & (xs < W) & (ys >= 0) & (ys < H) | |
| inside = np.zeros_like(inb) | |
| inside[inb] = mask[ys[inb], xs[inb]] | |
| last = np.where(inside.any(1), inside.shape[1] - 1 - np.argmax(inside[:, ::-1], 1), 0) | |
| return float(cx), float(cy), t[last] | |
| def radial_decode(cx, cy, d, W, H): | |
| """Ray endpoints -> polygon -> boolean mask (all in pixels). Returns (mask, polygon points).""" | |
| K = len(d) | |
| ang = 2 * np.pi * np.arange(K) / K | |
| pts = [(cx + float(r) * math.cos(a), cy + float(r) * math.sin(a)) for r, a in zip(d, ang)] | |
| im = Image.new("L", (W, H), 0) | |
| ImageDraw.Draw(im).polygon(pts, fill=1) | |
| return np.array(im, dtype=bool), pts | |
| def grow(b, g, W, H): | |
| """A box (x1, y1, x2, y2, pixels) grown by factor g about its centre, clipped to the image.""" | |
| gx, gy = (b[2] - b[0]) * (g - 1) / 2, (b[3] - b[1]) * (g - 1) / 2 | |
| return [max(0, b[0] - gx), max(0, b[1] - gy), min(W - 1, b[2] + gx), min(H - 1, b[3] + gy)] | |