from __future__ import annotations import argparse import io import json import os import random import zipfile from concurrent.futures import ThreadPoolExecutor import numpy as np import requests from PIL import Image ANN_URL = "http://images.cocodataset.org/annotations/annotations_trainval2014.zip" def csr(img, size): img = img.convert("RGB") w, h = img.size s = min(w, h) l, t = (w - s) // 2, (h - s) // 2 return np.asarray(img.crop((l, t, l + s, t + s)).resize((size, size), Image.BICUBIC), dtype=np.uint8) def fetch_one(item, size): url, cap = item for _ in range(3): try: r = requests.get(url, timeout=15) if r.status_code == 200: return csr(Image.open(io.BytesIO(r.content)), size), cap except Exception: pass return None def main(): ap = argparse.ArgumentParser() ap.add_argument("--work", default="/root/v6cache") ap.add_argument("--n", type=int, default=5000) ap.add_argument("--size", type=int, default=256) ap.add_argument("--seed", type=int, default=0) ap.add_argument("--workers", type=int, default=48) args = ap.parse_args() os.makedirs(args.work, exist_ok=True) out_path = os.path.join(args.work, "eval_256.npz") if os.path.exists(out_path): print(f"[eval-set] already exists at {out_path}", flush=True) return ann_path = os.path.join(args.work, "captions_val2014.json") if not os.path.exists(ann_path): print("[eval-set] downloading annotations", flush=True) z = os.path.join(args.work, "ann.zip") with requests.get(ANN_URL, stream=True, timeout=120) as r: with open(z, "wb") as f: for chunk in r.iter_content(1 << 20): f.write(chunk) with zipfile.ZipFile(z) as zf: with zf.open("annotations/captions_val2014.json") as src, open(ann_path, "wb") as dst: dst.write(src.read()) os.remove(z) ann = json.load(open(ann_path)) url_by_id = {im["id"]: im["coco_url"] for im in ann["images"]} cap_by_id = {} for a in ann["annotations"]: cap_by_id.setdefault(a["image_id"], a["caption"]) items = [(url_by_id[i], cap_by_id[i]) for i in cap_by_id if i in url_by_id] random.Random(args.seed).shuffle(items) print(f"[eval-set] {len(items)} val2014 pairs available, target {args.n}", flush=True) imgs, caps = [], [] pool = ThreadPoolExecutor(max_workers=args.workers) idx, batch = 0, 64 while len(imgs) < args.n and idx < len(items): chunk = items[idx:idx + batch] idx += batch results = [r for r in pool.map(lambda it: fetch_one(it, args.size), chunk) if r is not None] for a, c in results: imgs.append(a); caps.append(c) if idx % (batch * 20) == 0: print(f"[eval-set] {len(imgs)}/{args.n}", flush=True) imgs = np.stack(imgs[:args.n]) caps = np.array(caps[:args.n], dtype=object) np.savez(out_path, images=imgs, captions=caps) print(f"[eval-set] DONE {imgs.shape} -> {out_path}", flush=True) if __name__ == "__main__": main()