[object Object]

← 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

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 →