lasagna-fields / make_thumbs.py
SharpMeow's picture
Every field: Dataset, Manifest, Preview and Copy URL on every field (public Scrolls 0896f42, #293)
9aab612 verified
Raw History Blame Contribute Delete
2.87 kB
"""Card pictures for the Every field tab: each field's xy preview (cos | normals; preview_xy in fields.json, a
dataset's preview-xy.png or, for villa's fields in the open data, the picture make_previews.py wrote) as a small 2:1
WebP (fitted whole, on black) under assets/thumbs/SCROLL-VOLUME.webp. The guide falls back to the preview itself when
a thumb is missing. Like the rest of assets/, the thumbs live on the Space, not in git.
python3 spaces/lasagna-fields/make_thumbs.py OUT_DIR [PREVIEWS_DIR] (then upload OUT_DIR as assets/thumbs/)
PREVIEWS_DIR, make_previews.py's output, is read before the Space has the open-data previews.
"""
import io
import json
import sys
import urllib.request
from pathlib import Path
HERE = Path(__file__).resolve().parent
W, H = 720, 360
def trim(im):
"""The picture without its title bar and empty margins: the panels are pure black where the field is empty, the
title bar and labels sit on a dark gray canvas around them, so keep the black panels' box, then the content in it."""
import numpy as np
a = np.asarray(im).astype(int)
canvas = a[0, 0].copy()
black = (a.max(axis=2) <= 3)
ys, xs = np.nonzero(black)
if len(ys) and (ys.max() - ys.min()) * (xs.max() - xs.min()) > 0.3 * a.shape[0] * a.shape[1]:
a = a[ys.min():ys.max() + 1, xs.min():xs.max() + 1]
if canvas.max() > 3: # the canvas strip between the panels turns black too
a = a.copy()
a[np.abs(a - canvas).max(axis=2) <= 4] = 0
ys, xs = np.nonzero(a.max(axis=2) > 24)
if len(ys):
pad = 8
a = a[max(0, ys.min() - pad):ys.max() + 1 + pad, max(0, xs.min() - pad):xs.max() + 1 + pad]
from PIL import Image
return Image.fromarray(a.astype("uint8"))
def main(argv=None):
from PIL import Image
args = argv or sys.argv[1:]
out = Path(args[0])
local = Path(args[1]) if len(args) > 1 else None
out.mkdir(parents=True, exist_ok=True)
doc = json.loads((HERE / "fields.json").read_text())
for f in doc["fields"]:
name = f"{f['scroll']}-{f['volume']}"
url = f["preview_xy"]
here = local / url.rsplit("/", 1)[1] if local else None
raw = here.read_bytes() if here and here.is_file() else urllib.request.urlopen(url, timeout=120).read()
im = trim(Image.open(io.BytesIO(raw)).convert("RGB"))
r = min(W / im.width, H / im.height) # contain, centered on black like the panels
im = im.resize((round(im.width * r), round(im.height * r)), Image.LANCZOS)
canvas = Image.new("RGB", (W, H))
canvas.paste(im, ((W - im.width) // 2, (H - im.height) // 2))
canvas.save(out / f"{name}.webp", "WEBP", quality=82, method=6)
print(name, (out / f"{name}.webp").stat().st_size)
return 0
if __name__ == "__main__":
sys.exit(main())