← back to Wallco Ai
scripts/composite-on-texture.py
60 lines
#!/usr/bin/env python3
# Composite a single-ink motif onto a real texture background swatch.
# Usage:
# python3 composite-on-texture.py <motif.png> <texture.png> <out.png>
#
# The motif PNG is expected to be:
# - 1024×1024 (Replicate SDXL native)
# - Pale background (cream/white)
# - Dark single-ink figure
#
# We knock out the pale background via luminance threshold, then alpha-blend
# the figure over the texture. Texture is tiled if smaller than the motif.
import sys
from PIL import Image, ImageOps, ImageChops
def composite(motif_path, texture_path, out_path):
motif = Image.open(motif_path).convert('RGB')
texture = Image.open(texture_path).convert('RGB')
# Resize texture to match motif if needed (tile if smaller).
w, h = motif.size
if texture.size != (w, h):
# Tile texture to fill (w,h).
tw, th = texture.size
if tw < w or th < h:
tiled = Image.new('RGB', (w, h))
for y in range(0, h, th):
for x in range(0, w, tw):
tiled.paste(texture, (x, y))
texture = tiled
else:
texture = texture.resize((w, h), Image.LANCZOS)
# Derive an alpha mask from the motif's luminance:
# - dark pixels = figure (high alpha, ~255)
# - bright pixels = background (low alpha, ~0)
gray = motif.convert('L')
# Invert so figure (dark) becomes white, background (bright) becomes dark.
alpha = ImageOps.invert(gray)
# Boost contrast so the figure pops clean.
alpha = ImageOps.autocontrast(alpha, cutoff=5)
# Build figure RGBA with derived alpha.
figure = motif.copy()
figure.putalpha(alpha)
# Texture as base, composite figure over.
base = texture.copy()
base.paste(figure, (0, 0), figure)
base.save(out_path, format='PNG', optimize=True)
print(f"composite written: {out_path} ({w}x{h})")
if __name__ == '__main__':
if len(sys.argv) != 4:
sys.stderr.write("usage: composite-on-texture.py <motif.png> <texture.png> <out.png>\n")
sys.exit(2)
composite(sys.argv[1], sys.argv[2], sys.argv[3])