Download core/contact.py from FineEnvs/DesignGym: direct link, hf CLI and curl.
- Browser
- Download file 2.26 kB
-
https://huggingface.co/spaces/FineEnvs/DesignGym/resolve/main/core/contact.py
- Command line
-
hf download hf://spaces/FineEnvs/DesignGym/core/contact.py
-
curl -L -o contact.py https://huggingface.co/spaces/FineEnvs/DesignGym/resolve/main/core/contact.py
2.26 kB
| """Contact sheet: every asset of a task in one labelled image. | |
| A vision model on the tools surface cannot open files, so it needs to *see* | |
| which asset is which before it can place `a3` on the canvas. One image with a | |
| labelled tile per asset is far cheaper than one tool call per asset, and the | |
| checkerboard shows which assets are cut-outs and which are opaque photos. | |
| """ | |
| from __future__ import annotations | |
| import io | |
| from pathlib import Path | |
| from PIL import Image, ImageDraw, ImageFont | |
| TILE = 180 | |
| LABEL = 26 | |
| PAD = 10 | |
| def _checker(size: tuple[int, int], cell: int = 10) -> Image.Image: | |
| img = Image.new("RGB", size, (255, 255, 255)) | |
| draw = ImageDraw.Draw(img) | |
| for y in range(0, size[1], cell): | |
| for x in range(0, size[0], cell): | |
| if (x // cell + y // cell) % 2: | |
| draw.rectangle([x, y, x + cell - 1, y + cell - 1], fill=(228, 228, 228)) | |
| return img | |
| def contact_sheet(assets: list[dict], paths: dict[str, Path], font_path: Path | None = None, | |
| cols: int = 4) -> bytes: | |
| n = len(assets) | |
| cols = max(1, min(cols, n)) | |
| rows = (n + cols - 1) // cols | |
| width = cols * (TILE + PAD) + PAD | |
| height = rows * (TILE + LABEL + PAD) + PAD | |
| sheet = Image.new("RGB", (width, max(height, TILE)), (246, 246, 248)) | |
| draw = ImageDraw.Draw(sheet) | |
| try: | |
| font = ImageFont.truetype(str(font_path), 16) if font_path else ImageFont.load_default(16) | |
| except Exception: | |
| font = ImageFont.load_default() | |
| for i, asset in enumerate(assets): | |
| r, c = divmod(i, cols) | |
| x0 = PAD + c * (TILE + PAD) | |
| y0 = PAD + r * (TILE + LABEL + PAD) | |
| tile = _checker((TILE, TILE)) | |
| with Image.open(paths[asset["id"]]) as im: | |
| im = im.convert("RGBA") | |
| im.thumbnail((TILE - 8, TILE - 8)) | |
| tile.paste(im, ((TILE - im.width) // 2, (TILE - im.height) // 2), im) | |
| sheet.paste(tile, (x0, y0)) | |
| draw.rectangle([x0, y0, x0 + TILE - 1, y0 + TILE - 1], outline=(200, 200, 205)) | |
| label = f"{asset['id']} {asset['kind']} {asset['aspect']:.2f}:1" | |
| draw.text((x0 + 2, y0 + TILE + 4), label, fill=(30, 30, 35), font=font) | |
| buf = io.BytesIO() | |
| sheet.save(buf, "PNG", optimize=True) | |
| return buf.getvalue() | |