[object Object]

← 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

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 →