← back to Wild Wallcoverings
auto-save: 2026-07-22T14:14:43 (1 files) — scripts/measure_scale.py
20e94c4d3472f9f6f22c6c5380a5291581a167ec · 2026-07-22 14:14:49 -0700 · Steve Abrams
Files touched
A scripts/measure_scale.py
Diff
commit 20e94c4d3472f9f6f22c6c5380a5291581a167ec
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Jul 22 14:14:49 2026 -0700
auto-save: 2026-07-22T14:14:43 (1 files) — scripts/measure_scale.py
---
scripts/measure_scale.py | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/scripts/measure_scale.py b/scripts/measure_scale.py
new file mode 100644
index 0000000..b51e3ad
--- /dev/null
+++ b/scripts/measure_scale.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+"""Vision-measure each reference's motif count across the tile width and
+rewrite briefs.json scale specs as countable instructions.
+Cost ~$0.001/ref. Usage: GEMINI_API_KEY=... measure_scale.py
+"""
+import base64, json, os, urllib.request
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+KEY = os.environ["GEMINI_API_KEY"]
+URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={KEY}"
+PROMPT = """This is a repeating wallpaper pattern. Estimate the motif scale. Answer ONLY JSON:
+{"motifs_across": float, // how many primary motifs fit side-by-side across the image WIDTH (e.g. 1.5, 2, 6)
+ "motif_width_fraction": float, // width of one primary motif as fraction of image width (0-1)
+ "coverage": "dense|medium|sparse"}"""
+
+def main():
+ briefs = json.load(open(os.path.join(ROOT, "data", "briefs.json")))
+ for b in briefs:
+ ref = os.path.join(ROOT, "output", "refs", f"ref-{b['id']}.jpg")
+ if not os.path.exists(ref):
+ continue
+ img = base64.b64encode(open(ref, "rb").read()).decode()
+ body = {"contents": [{"parts": [{"text": PROMPT},
+ {"inline_data": {"mime_type": "image/jpeg", "data": img}}]}],
+ "generationConfig": {"responseMimeType": "application/json"}}
+ req = urllib.request.Request(URL, data=json.dumps(body).encode(),
+ headers={"Content-Type": "application/json"})
+ try:
+ v = json.loads(json.load(urllib.request.urlopen(req, timeout=60))
+ ["candidates"][0]["content"]["parts"][0]["text"])
+ b["scale_measured"] = v
+ n = v["motifs_across"]
+ b["scale"] = (f"exactly {n:g} primary motifs across the tile width — each motif "
+ f"spans about {round(100*v['motif_width_fraction'])}% of the tile "
+ f"width; {v['coverage']} packing; match this count precisely")
+ print(f"{b['id']:<20} across={n:g} frac={v['motif_width_fraction']:.2f} {v['coverage']}", flush=True)
+ except Exception as e:
+ print(b["id"], "FAIL", e, flush=True)
+ json.dump(briefs, open(os.path.join(ROOT, "data", "briefs.json"), "w"), indent=1)
+ print("saved")
+
+if __name__ == "__main__":
+ main()
← 6f6649f 150/150 complete: all variants settlement-gated OK, registry
·
back to Wild Wallcoverings
·
measured scale lock: vision-counted motifs-across per origin 813e88a →