Spaces:
Sleeping
Sleeping
| """N3 — shade map must drop the old floor's repeating tile wash (ghost | |
| diagonal banding, room 4) while keeping real lighting: gradients and contact | |
| shadows. Runs the REAL build_shade_map from app.py on a synthetic floor whose | |
| components are known exactly, and compares against the pre-N3 pipeline. | |
| Floor luminance = gradient * shadow * periodic tile wash * grout lines. | |
| - wash leakage : correlation of the decoded shade with the wash component | |
| -> must drop >= 60% vs the pre-N3 pipeline | |
| - shadow keep : correlation with the shadow component | |
| -> must stay >= 75% of the pre-N3 pipeline's | |
| - gradient keep : decoded left/right brightness ratio within 15% of truth | |
| """ | |
| import cv2 | |
| import numpy as np | |
| # --- extract the real implementations from app.py --------------------------- | |
| src = open("app.py").read() | |
| ns = {"np": np, "cv2": cv2} | |
| for fn in ["_adaptive_shade_range", "_encode_shade", "_dominant_period", | |
| "_suppress_periodic_shading", "build_shade_map"]: | |
| start = src.index(f"def {fn}") | |
| end = src.index("\ndef ", start + 10) | |
| # build_shade_map is followed by another def inside the same block scan | |
| exec(compile(src[start:end], "app.py", "exec"), ns) | |
| build_shade_map = ns["build_shade_map"] | |
| def build_shade_map_pre_n3(img_np, surface_mask): | |
| """The pre-N3 pipeline (median + Gaussian only), for the baseline.""" | |
| mask = surface_mask.astype(np.uint8) | |
| luminance = (img_np[:, :, 0].astype(np.float32) * 0.299 | |
| + img_np[:, :, 1].astype(np.float32) * 0.587 | |
| + img_np[:, :, 2].astype(np.float32) * 0.114) | |
| h, w = mask.shape[:2] | |
| median_lum = float(np.median(luminance[mask > 0])) | |
| filled = luminance.copy() | |
| filled[mask == 0] = median_lum | |
| med_k = max(9, int(min(h, w) / 40)) | 1 | |
| filled = cv2.medianBlur(np.clip(filled, 0, 255).astype(np.uint8), med_k).astype(np.float32) | |
| sigma = max(8.0, min(h, w) / 28.0) | |
| smooth = cv2.GaussianBlur(filled, (0, 0), sigmaX=sigma, sigmaY=sigma) | |
| relative = smooth / median_lum | |
| relative[mask == 0] = 1.0 | |
| lo, hi = ns["_adaptive_shade_range"](relative, mask) | |
| return ns["_encode_shade"](relative, lo, hi), (lo, hi) | |
| def decode(shade, rng): | |
| lo, hi = rng | |
| return lo + shade.astype(np.float32) / 255.0 * (hi - lo) | |
| def masked_corr(a, b, m): | |
| av = a[m] - a[m].mean() | |
| bv = b[m] - b[m].mean() | |
| den = np.sqrt((av ** 2).sum() * (bv ** 2).sum()) + 1e-9 | |
| return float((av * bv).sum() / den) | |
| def main(): | |
| H, W = 700, 900 | |
| yy, xx = np.mgrid[0:H, 0:W].astype(np.float32) | |
| # known components | |
| gradient = 0.85 + 0.5 * (xx / W) # window on the right | |
| shadow = 1.0 - 0.35 * np.exp(-(((xx - 250) / 90) ** 2 + ((yy - 420) / 60) ** 2)) | |
| period = 200 | |
| wash = 1.0 + 0.16 * np.sign(np.sin(2 * np.pi * (xx + yy) / period) | |
| * np.sin(2 * np.pi * (xx - yy) / period)) | |
| wash = cv2.GaussianBlur(wash, (0, 0), 9) # soft tile shading | |
| grout = np.where((np.mod(xx + yy, period) < 6) | (np.mod(xx - yy, period) < 6), 0.75, 1.0) | |
| lum = 150.0 * gradient * shadow * wash * grout | |
| img = np.repeat(np.clip(lum, 0, 255)[..., None], 3, axis=2).astype(np.uint8) | |
| mask = np.ones((H, W), np.uint8) | |
| mask[: H // 6] = 0 # a wall strip, exercises inpaint | |
| ok = True | |
| res = {} | |
| for name, fn in [("pre-N3", build_shade_map_pre_n3), ("N3", build_shade_map)]: | |
| shade, rng = fn(img, mask) | |
| rel = decode(shade, rng) | |
| m = mask.astype(bool) | |
| wash_c = masked_corr(rel, wash, m) | |
| shadow_c = masked_corr(rel, shadow, m) | |
| left = rel[m & (xx < W * 0.25)].mean() | |
| right = rel[m & (xx > W * 0.75)].mean() | |
| grad_ratio = left / right | |
| true_ratio = gradient[m & (xx < W * 0.25)].mean() / gradient[m & (xx > W * 0.75)].mean() | |
| res[name] = (wash_c, shadow_c, grad_ratio) | |
| print(f"[{name:6s}] wash-corr={wash_c:.3f} shadow-corr={shadow_c:.3f} " | |
| f"gradient L/R={grad_ratio:.3f} (truth {true_ratio:.3f})") | |
| wash_drop = 1 - abs(res["N3"][0]) / max(abs(res["pre-N3"][0]), 1e-6) | |
| shadow_keep = abs(res["N3"][1]) / max(abs(res["pre-N3"][1]), 1e-6) | |
| print(f"wash leakage drop = {wash_drop * 100:.0f}% (need >= 60%)") | |
| print(f"shadow retention = {shadow_keep * 100:.0f}% (need >= 75%)") | |
| if wash_drop < 0.60: | |
| print(" !! periodic wash still leaking"); ok = False | |
| if shadow_keep < 0.75: | |
| print(" !! real shadow lost"); ok = False | |
| true_ratio = gradient[mask.astype(bool) & (xx < W * 0.25)].mean() / \ | |
| gradient[mask.astype(bool) & (xx > W * 0.75)].mean() | |
| if abs(res["N3"][2] - true_ratio) > 0.15 * true_ratio: | |
| print(" !! lighting gradient distorted"); ok = False | |
| # period detector sanity on aperiodic field: pure shadow must NOT register | |
| aper = cv2.GaussianBlur((shadow * 40).astype(np.float32), (0, 0), 3) | |
| aper_hp = aper - cv2.GaussianBlur(aper, (0, 0), 50) | |
| if ns["_dominant_period"](aper_hp, 1) or ns["_dominant_period"](aper_hp, 0): | |
| print(" !! aperiodic shadow field misdetected as periodic"); ok = False | |
| print("\n" + ("ALL N3 CHECKS PASSED" if ok else "N3 CHECKS FAILED")) | |
| return 0 if ok else 1 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |