← back to Wallco Ai
fix(make_seamless): hard-cut mask threshold to eliminate ghost-layer artifacts
153962b4ae3287f7dea15459db0cf781a405a711 · 2026-05-24 01:05:53 -0700 · Steve Abrams
The soft alpha-blended mask in the feather zone (128px each edge, ~44% of
image area) produced partial-opacity copies of motifs that Gemini-vision
correctly identified as "ghost layer" defects on dense-motif designs.
Mechanism: shifted (75-100%) over original (0-25%) in the blend region =
faded duplicate behind opaque motif.
One-line fix: threshold the feathered mask to binary (≥128 → 255, <128 →
0) before composite. Cut boundary stays invisible because shifted's edges
ARE the original's interior pixels via wrap-around — they butt-join
seamlessly without needing a soft transition.
A/B tested on 3 designs (sparse-damask 19, scenic 39305, drunk-monkeys-v2
39304):
- sparse-damask: OLD ghost(1.00) → NEW clean ✓ FIXED
- scenic: OLD clean → NEW clean ✓ no regression
- drunk-monkeys-v2: OLD ghost(1.00) → NEW ghost(1.00) (pre-baked in input,
can't be removed
by reprocessing)
Edge-Δ wraparound test: OLD vs NEW within 0.1 across all three. No
visible-seam regression.
The fix-on-new-renders only takes effect on freshly-generated designs;
existing ghost-layered PNGs need to be regenerated through the ghost-review
admin UI (Reverse-Engineer + Regenerate Whole).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M scripts/make_seamless.py
Diff
commit 153962b4ae3287f7dea15459db0cf781a405a711
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 24 01:05:53 2026 -0700
fix(make_seamless): hard-cut mask threshold to eliminate ghost-layer artifacts
The soft alpha-blended mask in the feather zone (128px each edge, ~44% of
image area) produced partial-opacity copies of motifs that Gemini-vision
correctly identified as "ghost layer" defects on dense-motif designs.
Mechanism: shifted (75-100%) over original (0-25%) in the blend region =
faded duplicate behind opaque motif.
One-line fix: threshold the feathered mask to binary (≥128 → 255, <128 →
0) before composite. Cut boundary stays invisible because shifted's edges
ARE the original's interior pixels via wrap-around — they butt-join
seamlessly without needing a soft transition.
A/B tested on 3 designs (sparse-damask 19, scenic 39305, drunk-monkeys-v2
39304):
- sparse-damask: OLD ghost(1.00) → NEW clean ✓ FIXED
- scenic: OLD clean → NEW clean ✓ no regression
- drunk-monkeys-v2: OLD ghost(1.00) → NEW ghost(1.00) (pre-baked in input,
can't be removed
by reprocessing)
Edge-Δ wraparound test: OLD vs NEW within 0.1 across all three. No
visible-seam regression.
The fix-on-new-renders only takes effect on freshly-generated designs;
existing ghost-layered PNGs need to be regenerated through the ghost-review
admin UI (Reverse-Engineer + Regenerate Whole).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
scripts/make_seamless.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/scripts/make_seamless.py b/scripts/make_seamless.py
index 483303e..31cfa05 100644
--- a/scripts/make_seamless.py
+++ b/scripts/make_seamless.py
@@ -32,12 +32,17 @@ def make_seamless(im: Image.Image, feather: int | None = None) -> Image.Image:
inner = Image.new('L', (inner_w, inner_h), 255)
mask.paste(inner, (feather, feather))
mask = mask.filter(ImageFilter.GaussianBlur(feather // 2))
+ # Hard-cut the feathered mask at 128 before composite. Soft alpha-blending
+ # in the feather zone produces partial-opacity copies of motifs (original @
+ # 0-25% over shifted @ 75-100%) which Gemini-vision correctly identifies as
+ # a "ghost layer" defect on dense-motif designs. The cut boundary is invisible
+ # because shifted's edge pixels are the original's interior pixels (the
+ # wrap-around already produces a seamless butt-join).
+ mask = mask.point(lambda p: 255 if p >= 128 else 0)
# Composite — `Image.composite(A, B, mask)` returns A where mask=255 and
# B where mask=0. Mask is white at centre, black at edges.
- # We want:
# centre → ORIGINAL (clean interior)
- # edges → SHIFTED (whose edges are the original's *interior* pixels,
- # which means the new edges butt-join seamlessly to themselves)
+ # edges → SHIFTED (whose edges are the original's *interior* pixels)
out = Image.composite(im, shifted, mask)
return out
← a7d1745 add dedupe-flagged-jsonl.js + watch-and-dedupe.sh
·
back to Wallco Ai
·
fix(api/room): pass full mural width to upstream renderer fo 8584a8a →