""" verify_p2_sim.py — synthetic certification for the P2 edge-quality fixes. T7: the dilated occluder ring used to leave 2-5px of ORIGINAL floor around furniture, and the confidence feather made the tile translucent there. Expect: tile reclaimed up to a 1px margin, full opacity at depth edges, feather kept only at the floor↔wall boundary. T8: stair-stepped segmentation boundaries on occluders. Expect: rounded edges, thin components (chair legs) never erased. Runs the REAL functions extracted from app.py against a synthetic room, asserts the geometry, and renders an old-vs-new composite. Usage: python verify_p2_sim.py """ import numpy as np import cv2 from PIL import Image # --- extract the real implementations from app.py ------------------------- src = open("app.py").read() ns = { "np": np, "cv2": cv2, "OCCLUDER_CLASSES": {"occ"}, "REJECT_SURFACE_CLASSES": {"rej"}, "class_ids": lambda names: [10] if "occ" in names else [20], } for fn in ["clean_floor_mask", "build_floor_surface_mask", "build_confidence_map"]: start = src.index(f"def {fn}") end = src.index("\ndef ", start + 10) exec(compile(src[start:end], "app.py", "exec"), ns) # --- synthetic room -------------------------------------------------------- H, W = 600, 800 FLOOR_Y = 320 seg = np.zeros((H, W), np.int32) seg[:FLOOR_Y, :] = 20 # wall # sofa with stair-stepped right edge (4px steps every 6 rows) for y in range(250, 450): step = 4 * ((y // 6) % 2) seg[y, 100:300 + step] = 10 # thin 3px free-standing chair leg seg[350:430, 500:503] = 10 # thin 3px leg ATTACHED to the sofa body (blur erases it, body survives — # must be restored by the removed-chunk guard, not the component guard) seg[450:480, 150:153] = 10 # curtain with zigzag hem for x in range(600, 700): hem = 380 - 10 * ((x // 8) % 2) seg[0:hem, x] = 10 floor_mask = ((seg == 0) & (np.arange(H)[:, None] >= FLOOR_Y)).astype(np.uint8) surface, occ_zone = ns["build_floor_surface_mask"](floor_mask, seg, None, None) conf_new = ns["build_confidence_map"](surface, occ_zone) conf_old = ns["build_confidence_map"](surface, None) occ_raw = (seg == 10).astype(np.uint8) # --- assertions ------------------------------------------------------------ def first_surface_right_of(y, x_edge): row = surface[y, x_edge + 1:] nz = np.flatnonzero(row) return (x_edge + 1 + nz[0]) if len(nz) else None print("== T7: fringe gap + opacity at depth edges ==") for y in (380, 400, 420): edge = 300 + 4 * ((y // 6) % 2) - 1 # sofa's true right edge fx = first_surface_right_of(y, edge) gap = fx - edge - 1 print(f" y={y}: gap={gap}px conf_new={conf_new[y, fx]} conf_old={conf_old[y, fx]}") assert gap <= 3, "fringe gap should be <=3px" assert conf_new[y, fx] == 255, "tile must be opaque at the depth edge" assert conf_old[y, fx] < 200, "old feather should have been translucent here" print("== T7: floor-wall boundary keeps its feather, no gap line ==") col = 50 ys = np.flatnonzero(surface[:, col]) top = ys[0] ramp = [int(conf_new[top + d, col]) for d in range(0, 9)] print(f" top surface row at x={col}: y={top}, conf ramp: {ramp}") assert conf_new[top, col] < 200, "outer boundary must still feather" assert 0 not in ramp, "no untiled gap line inside the wall feather" assert ramp[-1] == 255, "feather must finish within ~8px" assert all(b >= a for a, b in zip(ramp, ramp[1:])), "ramp must be monotonic" print("== T8: free-standing leg survives smoothing, no tile painted on it ==") leg = surface[355:425, 500:503] assert leg.sum() == 0, "tile must not cover the leg" assert occ_zone[390, 501] > 0, "leg must remain in the occluder zone" fx = first_surface_right_of(390, 502) print(f" leg untouched, tile resumes {fx - 503}px right of it") assert fx - 503 <= 3 print("== T8: ATTACHED leg survives via removed-chunk guard ==") att = surface[455:478, 150:153] assert att.sum() == 0, "tile must not cover the attached leg" assert occ_zone[465, 151] > 0, "attached leg must remain in the occluder zone" print(" attached leg untouched") print("== T8: boundary roughness (perimeter ratio, lower = smoother) ==") def perimeter(m): cs, _ = cv2.findContours(m, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) return sum(cv2.arcLength(c, True) for c in cs) # re-run just the T8 blur (without the guards) for the perimeter metric smooth_k = max(13, min(H, W) // 100) | 1 occ_smooth = (cv2.GaussianBlur(occ_raw.astype(np.float32), (smooth_k, smooth_k), 0) >= 0.5).astype(np.uint8) p_raw, p_smooth = perimeter(occ_raw), perimeter(occ_smooth) print(f" k={smooth_k}: perimeter raw={p_raw:.0f} smoothed={p_smooth:.0f} ({p_smooth / p_raw:.2f}x)") assert p_smooth < p_raw * 0.92, "staircase must actually collapse" print("== T8: tile edge beside the sofa is straighter than the raw stairs ==") tile_edge = [np.flatnonzero(surface[y, 250:340])[0] for y in range(370, 430)] amp = max(tile_edge) - min(tile_edge) print(f" tile-edge amplitude over 60 rows: {amp}px (raw stair amplitude: 4px)") assert amp <= 2, "tile edge must be straighter than the raw staircase" print("== T7: furniture feet sit flush (small gap below sofa bottom) ==") below = np.flatnonzero(surface[450:, 200]) gap = below[0] if len(below) else 99 print(f" first tile row below sofa bottom: {gap}px") assert gap <= 3, "feet must sit within 3px of the tile" # --- composite render: old vs new ------------------------------------------ room = np.full((H, W, 3), (150, 110, 70), np.uint8) # warm original floor room[:FLOOR_Y] = (210, 205, 195) # wall room[occ_raw > 0] = (45, 40, 38) # dark furniture yy, xx = np.mgrid[0:H, 0:W] checker = (((yy // 24) + (xx // 24)) % 2).astype(bool) tile = np.where(checker[..., None], (235, 235, 230), (200, 200, 195)).astype(np.uint8) def composite(conf): a = (conf.astype(np.float32) / 255.0)[..., None] out = room.astype(np.float32) * (1 - a) + tile.astype(np.float32) * a return out.astype(np.uint8) old, new = composite(conf_old), composite(conf_new) side = np.hstack([old, new]) Image.fromarray(side).save("verify_out/p2_compare.png") crop = np.hstack([old[330:470, 250:560], new[330:470, 250:560]]) crop = cv2.resize(crop, None, fx=2.5, fy=2.5, interpolation=cv2.INTER_NEAREST) Image.fromarray(crop).save("verify_out/p2_compare_crop.png") print("saved verify_out/p2_compare.png + p2_compare_crop.png (left=old, right=new)") print("ALL P2 CHECKS PASSED")