| import argparse |
| import json |
| import os |
| import sys |
| from pathlib import Path |
|
|
| from datasets import load_dataset |
| from PIL import Image |
| from tqdm import tqdm |
|
|
|
|
| def normalize_bbox(bbox, width, height): |
| x1, y1, x2, y2 = bbox |
| return [ |
| x1 / width, |
| y1 / height, |
| x2 / width, |
| y2 / height, |
| ] |
|
|
|
|
| def _maybe_resize(img: Image.Image, image_size: int | None) -> Image.Image: |
| if image_size is None: |
| return img |
| im = img.copy() |
| im.thumbnail((image_size, image_size), Image.Resampling.LANCZOS) |
| return im |
|
|
|
|
| def _save_image(img: Image.Image, path: Path, image_size: int | None) -> None: |
| path.parent.mkdir(parents=True, exist_ok=True) |
| out = _maybe_resize(img, image_size) |
| |
| out.save(path, optimize=True) |
|
|
|
|
| def _media_path_for_json(saved: Path, output_json: Path) -> str: |
| """Path of saved file relative to the JSON file's directory.""" |
| out_dir = output_json.parent.resolve() |
| saved_r = saved.resolve() |
| return Path(os.path.relpath(saved_r, out_dir)).as_posix() |
|
|
|
|
| def parse_args(argv: list[str] | None = None) -> argparse.Namespace: |
| here = Path(__file__).resolve().parent |
| p = argparse.ArgumentParser(description=__doc__) |
| p.add_argument( |
| "--output", |
| type=Path, |
| default=here / "genposter_subset.json", |
| help="Output JSON path", |
| ) |
| p.add_argument( |
| "--images-dir", |
| type=Path, |
| default=here / "images", |
| help="Directory for downloaded images (when --download-images)", |
| ) |
| p.add_argument( |
| "--max-samples", |
| type=int, |
| default=5000, |
| help="Max training rows to scan from the streaming dataset", |
| ) |
| p.add_argument( |
| "--download-images", |
| action="store_true", |
| help="Download background and per-layer images (large disk use vs metadata-only)", |
| ) |
| p.add_argument( |
| "--image-size", |
| type=int, |
| default=None, |
| metavar="N", |
| help="If set, fit each image inside N×N (aspect preserved; PIL thumbnail)", |
| ) |
| return p.parse_args(argv) |
|
|
|
|
| def main(argv: list[str] | None = None) -> int: |
| args = parse_args(argv) |
| dataset = load_dataset( |
| "creative-graphic-design/GenPoster100K", split="train", streaming=True |
| ) |
|
|
| processed_data = [] |
| sid: str | int | None = None |
| try: |
| for i, sample in enumerate(tqdm(dataset, total=args.max_samples)): |
| if i >= args.max_samples: |
| break |
| sid = sample.get("id") |
| try: |
| layer_columns = sample["layers"] |
| bboxes = layer_columns["bbox"] |
| n = len(bboxes) |
| if n == 0: |
| continue |
|
|
| width, height = layer_columns["psd_size"][0] |
| labels = layer_columns.get("label", [""] * n) |
| texts = layer_columns.get("text", [""] * n) |
| font_sizes = layer_columns.get("font_size", [0] * n) |
| layer_images = layer_columns.get("layer_image") if args.download_images else None |
|
|
| elements = [] |
| for j in range(n): |
| bbox = bboxes[j] |
| if bbox is None: |
| continue |
| el = { |
| "type": labels[j] if j < len(labels) else "unknown", |
| "text": texts[j] if j < len(texts) else "", |
| "bbox": normalize_bbox(bbox, width, height), |
| "font_size": float(font_sizes[j]) |
| if j < len(font_sizes) and font_sizes[j] is not None |
| else 0, |
| } |
| if args.download_images and layer_images is not None: |
| if j < len(layer_images) and layer_images[j] is not None: |
| layer_name = f"{sid}_layer_{len(elements)}.png" |
| layer_path = args.images_dir / layer_name |
| _save_image(layer_images[j], layer_path, args.image_size) |
| el["layer_image_path"] = _media_path_for_json( |
| layer_path, args.output |
| ) |
| elements.append(el) |
|
|
| if len(elements) == 0: |
| continue |
|
|
| row = { |
| "id": sid, |
| "canvas_size": [width, height], |
| "elements": elements, |
| } |
| if args.download_images: |
| bg = sample.get("background_image") |
| if bg is not None: |
| bg_name = f"{sid}_bg.png" |
| bg_path = args.images_dir / bg_name |
| _save_image(bg, bg_path, args.image_size) |
| row["image_path"] = _media_path_for_json(bg_path, args.output) |
|
|
| processed_data.append(row) |
|
|
| except Exception: |
| continue |
|
|
| except KeyboardInterrupt: |
| print(f"\nInterrupted after {len(processed_data)} samples (last id={sid!r}).", file=sys.stderr) |
|
|
| args.output.parent.mkdir(parents=True, exist_ok=True) |
| with open(args.output, "w", encoding="utf-8") as f: |
| json.dump(processed_data, f) |
|
|
| print(f"Saved {len(processed_data)} samples to {args.output}") |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|