← back to Wallco Ai
fuzzy-seam scanner: detect pil-heal blur-band 'fuzzy cross' the edges-agent misses
5d63f4bc556aba0820bbf059fd96bce1dce806af · 2026-05-27 07:56:26 -0700 · Steve Abrams
The 2026-05-26 pil-mid-heal/pil-edge-heal passes smeared a blurred strip across
the H/V midlines, producing a visible fuzzy T/+ at tile center. edges-agent
measures discontinuity (a hard jump) so a smooth blur band PASSES it. This
detector keys on the actual physics: a thin blurred line at the exact midline,
flanked by sharp content on both sides, isolated (not periodic motif gaps).
Validated 6/6 known-bad FAIL, 0/120 random round-1 false-positives.
Files touched
A scripts/fuzzy_seam_scan.py
Diff
commit 5d63f4bc556aba0820bbf059fd96bce1dce806af
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed May 27 07:56:26 2026 -0700
fuzzy-seam scanner: detect pil-heal blur-band 'fuzzy cross' the edges-agent misses
The 2026-05-26 pil-mid-heal/pil-edge-heal passes smeared a blurred strip across
the H/V midlines, producing a visible fuzzy T/+ at tile center. edges-agent
measures discontinuity (a hard jump) so a smooth blur band PASSES it. This
detector keys on the actual physics: a thin blurred line at the exact midline,
flanked by sharp content on both sides, isolated (not periodic motif gaps).
Validated 6/6 known-bad FAIL, 0/120 random round-1 false-positives.
---
scripts/fuzzy_seam_scan.py | 218 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 218 insertions(+)
diff --git a/scripts/fuzzy_seam_scan.py b/scripts/fuzzy_seam_scan.py
new file mode 100644
index 0000000..b823925
--- /dev/null
+++ b/scripts/fuzzy_seam_scan.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python3
+"""fuzzy-seam scanner — detects the pil-mid-heal / pil-edge-heal "fuzzy cross"
+defect: the heal pass blends a BLURRED strip across the horizontal and/or
+vertical midline, producing a visible smeared T/+ at the center of the tile.
+
+Why a new detector: the edges-agent measures pixel DISCONTINUITY (a hard ΔE
+jump) and the scratch intermediate-color scanner needs a near-perfect 2-tone
+rest. A blur band is *smooth* (low ΔE) and shows up on any palette, so both
+miss it (confirmed: 53100/53191/50099 score loc≈1 on the old scanner).
+
+Signal: the heal band has LOWER local sharpness (mean squared Laplacian) than
+the regions flanking it. We build per-row and per-col sharpness profiles, look
+for a localized VALLEY at the center (where pil splits the 2x2 grid / wraps),
+and score band_sharpness / flank_sharpness. Ratio << 1 => blurred seam.
+
+READ-ONLY. Never writes PG, never modifies a PNG. $0, local numpy+PIL.
+"""
+import argparse, json, os, subprocess, sys, time
+import numpy as np
+from PIL import Image
+
+GEN_DIR = "/Users/stevestudio2/Projects/wallco-ai/data/generated"
+
+# --- tunables (validated against the 2026-05-26 heal cohort) ---
+# The heal seam is a THIN blurred line at the EXACT geometric midline, flanked
+# by NORMAL-sharpness content on BOTH sides. Natural smooth regions are either
+# off-center or wide (one flank is also smooth) — that's how we tell them apart.
+CORE_FRAC = 0.013 # half-width of the seam CORE band (~13px @1024 => 26px core)
+NF_LO_FRAC = 0.018 # near-flank starts just outside the core
+NF_HI_FRAC = 0.060 # near-flank ends here (~18..62px out each side)
+SEARCH_PX = 9 # seam sits at center ± this (pil splits at H/2,W/2)
+FAIL_RATIO = 0.45 # core < 45% of the WEAKER flank => seam
+WARN_RATIO = 0.62
+FLANK_MIN = 0.50 # both flanks must be >= this * global-median sharpness (genuinely sharp)
+THIN_RATIO = 0.60 # core must be < 60% of the inner shoulder => razor-thin line, not a wide smooth zone
+DOWN_MAX = 1024 # cap working size for speed
+
+
+def _sharpness_profiles(path):
+ im = Image.open(path).convert("L")
+ if max(im.size) > DOWN_MAX:
+ im.thumbnail((DOWN_MAX, DOWN_MAX))
+ a = np.asarray(im, dtype=np.float32)
+ H, W = a.shape
+ # 4-neighbour Laplacian on the interior
+ lap = np.zeros_like(a)
+ lap[1:-1, 1:-1] = (
+ -4.0 * a[1:-1, 1:-1]
+ + a[:-2, 1:-1] + a[2:, 1:-1]
+ + a[1:-1, :-2] + a[1:-1, 2:]
+ )
+ sq = lap * lap
+ Rh = sq.mean(axis=1) # sharpness per row -> finds horizontal seam (band of rows)
+ Rv = sq.mean(axis=0) # sharpness per col -> finds vertical seam
+ return Rh, Rv, H, W
+
+
+MAX_REGIONS = 2 # >2 valley regions on an axis => periodic motif gaps, not a seam
+
+
+def _axis(prof):
+ """Analyze one sharpness profile for a heal seam at the geometric midline.
+
+ A heal seam is a THIN blurred line at center, flanked by SHARP content on
+ both sides, and ISOLATED (a periodic motif pattern produces many such
+ valleys at the motif period — that's a pattern gap, not a defect).
+
+ Returns dict(ratio, flank_norm, pos, regions, center_valley)."""
+ n = len(prof)
+ c = n // 2
+ pos_v = prof[prof > 0]
+ gmed = float(np.median(pos_v)) if pos_v.size else 1.0
+ gmed = gmed if gmed > 1e-6 else 1.0
+ core = max(3, int(round(n * CORE_FRAC)))
+ nflo = max(core + 1, int(round(n * NF_LO_FRAC)))
+ nfhi = max(nflo + 4, int(round(n * NF_HI_FRAC)))
+
+ shoulder = max(core + 2, int(round(core * 2.2))) # immediately outside the core
+
+ def valley_ratio(pos):
+ core_s = prof[pos - core:pos + core].mean()
+ left = np.median(prof[pos - nfhi:pos - nflo])
+ right = np.median(prof[pos + nflo:pos + nfhi])
+ flank = min(left, right) # far flanks: both sides must be sharp
+ # inner shoulders: sharpness must RECOVER right outside the core (thin line,
+ # not a wide smooth zone). A wide smooth region keeps the shoulder low too.
+ sl = np.median(prof[pos - shoulder:pos - core])
+ sr = np.median(prof[pos + core:pos + shoulder])
+ shoulder_min = min(sl, sr)
+ if flank <= 1e-6 or shoulder_min <= 1e-6:
+ return None, 0.0, 9.99
+ return core_s / flank, flank / gmed, core_s / shoulder_min
+
+ # strongest seam evidence within the center search window
+ best = (9.99, 0.0, 9.99, c)
+ for pos in range(c - SEARCH_PX, c + SEARCH_PX + 1):
+ if pos - nfhi < 0 or pos + nfhi > n:
+ continue
+ r, fn, thin = valley_ratio(pos)
+ if r is not None and r < best[0]:
+ best = (r, fn, thin, pos)
+ ratio, flank_norm, thin, pos = best
+
+ # count valley regions across the whole axis (periodicity guard)
+ hits = []
+ for p in range(nfhi, n - nfhi, 2):
+ r, fn, th = valley_ratio(p)
+ if r is not None and fn >= FLANK_MIN and r <= FAIL_RATIO and th <= THIN_RATIO:
+ hits.append(p)
+ regions, last = 0, -10 ** 9
+ for p in hits:
+ if p - last > 2 * core + 6:
+ regions += 1
+ last = p
+ return {"ratio": round(float(ratio), 3), "flank_norm": round(float(flank_norm), 2),
+ "thin": round(float(thin), 2), "pos": int(pos), "regions": int(regions)}
+
+
+def _verdict(ax):
+ if ax["regions"] > MAX_REGIONS: # periodic motif gaps, not a seam
+ return "PASS"
+ if ax["flank_norm"] < FLANK_MIN: # flanks not sharp -> wide/natural smooth zone
+ return "PASS"
+ if ax["thin"] > THIN_RATIO: # not a razor-thin line -> wide smooth zone
+ return "PASS"
+ if ax["ratio"] <= FAIL_RATIO:
+ return "FAIL"
+ if ax["ratio"] <= WARN_RATIO:
+ return "WARN"
+ return "PASS"
+
+
+def scan(path):
+ Rh, Rv, H, W = _sharpness_profiles(path)
+ h, v = _axis(Rh), _axis(Rv) # horizontal seam (rows) / vertical seam (cols)
+ vh, vv = _verdict(h), _verdict(v)
+ order = {"FAIL": 2, "WARN": 1, "PASS": 0}
+ verdict = vh if order[vh] >= order[vv] else vv
+ return {
+ "h_ratio": h["ratio"], "h_flank": h["flank_norm"], "h_regions": h["regions"], "h_verdict": vh,
+ "v_ratio": v["ratio"], "v_flank": v["flank_norm"], "v_regions": v["regions"], "v_verdict": vv,
+ "worst": min(h["ratio"], v["ratio"]), "verdict": verdict,
+ "axis": ("both" if vh == "FAIL" and vv == "FAIL" else ("h" if order[vh] >= order[vv] else "v")),
+ }
+
+
+def _resolve(local_path):
+ if local_path and os.path.exists(local_path):
+ return local_path
+ if local_path:
+ alt = os.path.join(GEN_DIR, os.path.basename(local_path))
+ if os.path.exists(alt):
+ return alt
+ return None
+
+
+def _cohort_rows():
+ sql = ("SELECT id, generator, COALESCE(local_path,'') FROM spoon_all_designs "
+ "WHERE is_published=true AND generator IN ('pil-mid-heal','pil-edge-heal') "
+ "ORDER BY id;")
+ out = subprocess.run(["psql", "dw_unified", "-tAc", sql],
+ capture_output=True, text=True).stdout.strip().splitlines()
+ rows = []
+ for ln in out:
+ if ln.count("|") >= 2:
+ rid, gen, lp = ln.split("|", 2)
+ rows.append((int(rid), gen, lp))
+ return rows
+
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument("--path")
+ ap.add_argument("--id", type=int)
+ ap.add_argument("--cohort", action="store_true", help="scan all published heal designs")
+ ap.add_argument("--shard", type=int, default=0)
+ ap.add_argument("--shards", type=int, default=1)
+ ap.add_argument("--out", default="/Users/stevestudio2/Projects/wallco-ai/data/fuzzy-seam-scan.json")
+ ap.add_argument("--json", action="store_true")
+ args = ap.parse_args()
+
+ if args.path:
+ print(json.dumps(scan(args.path), indent=2)); return
+ if args.id:
+ sql = f"SELECT COALESCE(local_path,'') FROM spoon_all_designs WHERE id={args.id};"
+ lp = subprocess.run(["psql","dw_unified","-tAc",sql],capture_output=True,text=True).stdout.strip()
+ p = _resolve(lp)
+ if not p: print(json.dumps({"error":"png missing","id":args.id})); return
+ r = scan(p); r["id"]=args.id
+ print(json.dumps(r, indent=2)); return
+
+ if args.cohort:
+ rows = _cohort_rows()
+ rows = [r for i, r in enumerate(rows) if i % args.shards == args.shard]
+ results, missing, t0 = [], 0, time.time()
+ for i, (rid, gen, lp) in enumerate(rows):
+ p = _resolve(lp)
+ if not p:
+ missing += 1; continue
+ try:
+ r = scan(p); r["id"]=rid; r["generator"]=gen; r["file"]=os.path.basename(p)
+ results.append(r)
+ except Exception as e:
+ missing += 1
+ if (i+1) % 250 == 0:
+ print(f" ...{i+1}/{len(rows)} ({time.time()-t0:.0f}s)", flush=True, file=sys.stderr)
+ out = args.out if args.shards == 1 else args.out.replace(".json", f".shard{args.shard}.json")
+ json.dump({"results":results,"missing":missing}, open(out,"w"))
+ fails = sum(1 for r in results if r["verdict"]=="FAIL")
+ warns = sum(1 for r in results if r["verdict"]=="WARN")
+ print(f"shard {args.shard}/{args.shards}: scanned={len(results)} FAIL={fails} WARN={warns} missing={missing} -> {out}", flush=True)
+ return
+
+ ap.print_help()
+
+
+if __name__ == "__main__":
+ main()
← 306c86f task#25: chinoiserie tile-class SAMPLE on ComfyUI (Mac2, cir
·
back to Wallco Ai
·
Fix fuzzy edges + blurred center-cross in all 3 seam healers fdd35e6 →