Taps the last big unused seam in the OSM data: 140 highway=traffic_signals per CBD level become pole + mast arm + two lantern heads with emissive lenses. Timber power poles with catenary-sagging wires run down one side of every residential street. Billboards now cycle three fictional ad designs. Cloudflare's free tier (10k neurons/day) ran out mid-session, so flux_texture.sh now falls back to the local MODELBEAST farm automatically -- transparent, same output contract. tools/crop_poster.py trims generated billboards to the poster face by saturation, since FLUX renders them in situ with sky and support pole however the prompt is worded. Fixed: signal lenses were built as 0.04 m beams and beam() bails under 0.05 m, so they silently never existed -- caught by inspecting the exported GLB, not the screenshot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Trim a generated billboard down to just its poster face.
|
|
|
|
Usage: crop_poster.py <image.jpg> [more.jpg ...]
|
|
|
|
FLUX renders a billboard *in situ* -- sky, support pole, sometimes a wall --
|
|
however hard the prompt asks for the poster alone. Mapped onto a flat panel
|
|
that bakes sky into the sign. The poster is always the saturated part and the
|
|
surroundings are always washed out, so trim rows/columns whose mean saturation
|
|
falls below a fraction of the image's peak. Survives regeneration, unlike a
|
|
hand-measured crop box.
|
|
"""
|
|
import sys
|
|
from PIL import Image
|
|
|
|
FLOOR = 0.42 # keep bands at >=42% of peak row/col saturation
|
|
MIN_KEEP = 0.35 # never crop away more than 65% of a dimension
|
|
|
|
|
|
def band_keep(profile):
|
|
peak = max(profile) or 1
|
|
thresh = peak * FLOOR
|
|
idx = [i for i, v in enumerate(profile) if v >= thresh]
|
|
if not idx:
|
|
return 0, len(profile)
|
|
lo, hi = idx[0], idx[-1] + 1
|
|
if (hi - lo) < len(profile) * MIN_KEEP: # suspiciously tight -> leave it alone
|
|
return 0, len(profile)
|
|
return lo, hi
|
|
|
|
|
|
def crop(path):
|
|
im = Image.open(path).convert("RGB")
|
|
sat = im.convert("HSV").split()[1]
|
|
w, h = im.size
|
|
px = sat.load()
|
|
cols = [sum(px[x, y] for y in range(0, h, 4)) for x in range(w)]
|
|
rows = [sum(px[x, y] for x in range(0, w, 4)) for y in range(h)]
|
|
x0, x1 = band_keep(cols)
|
|
y0, y1 = band_keep(rows)
|
|
out = im.crop((x0, y0, x1, y1)).resize((w, h), Image.LANCZOS)
|
|
out.save(path, quality=90)
|
|
print("%s: kept x %d-%d, y %d-%d of %dx%d" % (path.split("/")[-1], x0, x1, y0, y1, w, h))
|
|
|
|
|
|
for p in sys.argv[1:]:
|
|
crop(p)
|