Round 7: zero-text frames, escaped/curly titles, plan-driven banner crops
Deploy / build (push) Successful in 19s

The consultant's review of the shipped set found two defects, both ours, and both fixed
at the source rather than in the output:

1. The banner/thumb crop re-framing detected only the glowing core of the object. Column
   brightness measures 2-6 levels above the background on dark glass, so it boxed 8 % of
   the frame for an object group spanning 35 %+. The detector is now a background-plane
   deviation plus the largest connected components (`plan_banner_crops.py`, detector in
   `probe_band_crop.py`), 0 % false-positive on the frame's own negative space, stable
   across thresholds. The crop plan is frozen as `banner-plan.json` and consumed by the
   renderer; a thin subject is centred at 53.5 % of the banner width with the shortfall
   filled by the art's own edge pixels, one row at a time (a single averaged fill left a
   visible tonal step, tried and rejected). Measured on the delivered 72: 0 clipped,
   silhouette median 47.0 %, centre median 53.5 % (`verify_banner_crops.py`).

2. `art-meta.json` pinned two slugs to the hand-accepted sample frames — the very frames
   that carried the HUD readouts — which also kept them out of the crop plan. Pins removed.

Prompt side: 9 frames still printed glyphs at the consultant's bar (>=12 px and >=2
characters). Every offending noun is measured and now asserted against: a literal `marked
at 28` printed "28"; `storefront kiosk` printed "PRINATA"; `voucher card` printed
"Voucher"; `market booths` printed "10MARKET"; `glass button` printed "OC"; `gateway pylon`
printed "GATELY". 50 forbidden words, 0 hits across 72 captions, and all 9 frames
re-rendered clean.

Also: YAML escapes are honoured in the frontmatter reader and straight quotes become
curly ones (the ZH cards shipped a literal backslash), the evidence line is capped at 65
characters, and the light theme gives dark artwork a hairline border plus a soft shadow
(consultant option A) so it reads as a dark display case instead of a black slab.

OCR gate at the consultant's bar: 72/72 frames clean (16 carry sub-threshold single
glyphs, exempt as geometry noise).
This commit is contained in:
hoelee committed 2026-10-19 11:02:28 +08:00
1 parent 576a43a034
commit f391682fc3
513 files changed
+2223 -174

No files matched your search

+18 -1
View File
@@ -11,6 +11,11 @@ This copies the authoring state into the repo so git alone is enough:
scripts/og-gen/metaphors.json slug -> the unique metaphor (72/72 distinct)
scripts/og-gen/captions/<slug>.json the exact structured caption sent to the model
scripts/og-gen/prompts_catalog.json same shape as before, refreshed from the above
scripts/og-gen/banner-plan.json the banner/thumb crop window per slug, computed by
`C:/AI/ideogram4/plan_banner_crops.py` (detector in
`probe_band_crop.py`). `render_from_art.mjs` reads
this by default; without it a slug falls back to a
weaker on-the-fly heuristic.
Usage:
python scripts/og-gen/sync_prompts_catalog.py # uses C:/AI/ideogram4
@@ -66,6 +71,13 @@ def main() -> None:
if missing_evidence:
problems.append(f"{len(missing_evidence)} posts have no evidence line in art-meta.json")
plan_file = tool / "out" / "banner-plan.json"
plan = json.loads(plan_file.read_text(encoding="utf-8")) if plan_file.exists() else {"frames": {}}
plan_missing = sorted(set(metaphors) - set(plan.get("frames", {})))
if plan_missing:
problems.append(f"{len(plan_missing)} slugs have no banner crop plan "
f"(first: {plan_missing[:4]}) — run plan_banner_crops.py")
print(f"posts {len(posts)} · metaphors {len(metaphors)} · distinct {len(set(metaphors.values()))} · catalog {len(catalog)}")
print("problems:", problems or "none")
if problems:
@@ -95,7 +107,12 @@ def main() -> None:
entry["caption"] = f"captions/{slug}.json"
out.append(entry)
(OG / "prompts_catalog.json").write_text(json.dumps(out, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
print(f"wrote metaphors.json, captions/ ({len(metaphors)} files), prompts_catalog.json")
(OG / "banner-plan.json").write_text(json.dumps(plan, indent=1, ensure_ascii=False) + "\n", encoding="utf-8")
fr = [f["silhouette"] for f in plan.get("frames", {}).values()]
if fr:
print(f"banner plan: {len(fr)} frames, silhouette "
f"{min(fr)*100:.0f}-{max(fr)*100:.0f}% of the banner width")
print(f"wrote metaphors.json, captions/ ({len(metaphors)} files), prompts_catalog.json, banner-plan.json")
if __name__ == "__main__":