← back to Quadrille Showroom
scripts/img_tools.py
67 lines
#!/usr/bin/env python3
"""img_tools.py — local ($0) image helpers for the Quadrille showroom gen pipeline.
python3 img_tools.py score <url|path> -> prints LR seam score (float)
python3 img_tools.py mirror <url|path> <out.png> -> writes a 2x2 mirror book-match
tile (guaranteed seamless,
preserves the real design),
prints "<before> <after>"
The mirror book-match is a legitimate wallcovering technique: reflecting the swatch
makes every tile edge match its neighbour exactly, so the genuine China Seas pattern
tiles cleanly on a wall WITHOUT any AI re-generation (which would misrepresent the
real product). $0 — no API, ~150ms/image.
"""
import sys, urllib.request, tempfile
import numpy as np
from PIL import Image
STRIP = 3
THRESHOLD = 30
def _resolve(src):
if src.startswith('http'):
tmp = tempfile.NamedTemporaryFile(suffix='.img', delete=False)
urllib.request.urlretrieve(src, tmp.name)
return tmp.name
return src
def lr_score(img):
w, h = img.size
sw, sh = min(w, 400), min(h, 800)
d = np.array(img.resize((sw, sh)).convert('RGB')).astype(int)
left = d[:, :STRIP, :]
right = d[:, sw - STRIP:, :][:, ::-1, :] # mirror so col0<->last
return float(np.abs(left - right).sum() / (sh * STRIP) / 3)
def score(src):
print(round(lr_score(Image.open(_resolve(src))), 1))
def mirror(src, out):
im = Image.open(_resolve(src)).convert('RGB')
before = round(lr_score(im), 1)
w, h = im.size
fh = im.transpose(Image.FLIP_LEFT_RIGHT)
fv = im.transpose(Image.FLIP_TOP_BOTTOM)
fb = fh.transpose(Image.FLIP_TOP_BOTTOM)
tile = Image.new('RGB', (w * 2, h * 2))
tile.paste(im, (0, 0)); tile.paste(fh, (w, 0))
tile.paste(fv, (0, h)); tile.paste(fb, (w, h))
tile.save(out, 'PNG')
after = round(lr_score(tile), 1)
print(f"{before} {after}")
if __name__ == '__main__':
cmd = sys.argv[1] if len(sys.argv) > 1 else ''
if cmd == 'score' and len(sys.argv) == 3:
score(sys.argv[2])
elif cmd == 'mirror' and len(sys.argv) == 4:
mirror(sys.argv[2], sys.argv[3])
else:
print(__doc__); sys.exit(2)