Spaces:
Running
Running
| """ | |
| Offline script — pre-compute CLIP embeddings for the benchmark library. | |
| Usage: | |
| python scripts/embed_benchmarks.py | |
| Expected directory layout: | |
| data/benchmarks/images/ | |
| excellent/ | |
| cards/ | |
| stripe_card_001.png | |
| stripe_card_001.json ← metadata (same stem) | |
| tables/ | |
| dashboards/ | |
| ... | |
| poor/ | |
| (same category structure) | |
| Output: | |
| data/benchmarks/embeddings.npz | |
| embeddings — float32 (N, 512) — one row per image | |
| metadata — object (N,) — serialised JSON strings | |
| Run this whenever images are added or changed. The app reads only the .npz | |
| file at runtime; raw images are never committed (see .gitignore). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| ROOT = Path(__file__).resolve().parents[1] | |
| IMAGES_DIR = ROOT / "data" / "benchmarks" / "images" | |
| OUTPUT = ROOT / "data" / "benchmarks" / "embeddings.npz" | |
| def main() -> None: | |
| try: | |
| from sentence_transformers import SentenceTransformer | |
| except ImportError: | |
| print("ERROR: sentence-transformers is not installed.") | |
| print(" pip install sentence-transformers") | |
| sys.exit(1) | |
| if not IMAGES_DIR.exists(): | |
| print(f"ERROR: {IMAGES_DIR} does not exist.") | |
| print(" Place benchmark images under data/benchmarks/images/") | |
| sys.exit(1) | |
| image_exts = {".png", ".jpg", ".jpeg", ".webp"} | |
| image_paths = [ | |
| p for p in IMAGES_DIR.rglob("*") | |
| if p.suffix.lower() in image_exts | |
| ] | |
| if not image_paths: | |
| print(f"No images found under {IMAGES_DIR}") | |
| sys.exit(1) | |
| print(f"Found {len(image_paths)} images — loading CLIP model…") | |
| model = SentenceTransformer("clip-ViT-B-32") | |
| from PIL import Image | |
| embeddings: list[np.ndarray] = [] | |
| metadata: list[str] = [] | |
| skipped = 0 | |
| for path in sorted(image_paths): | |
| meta_path = path.with_suffix(".json") | |
| if not meta_path.exists(): | |
| print(f" SKIP {path.name} — no matching .json metadata file") | |
| skipped += 1 | |
| continue | |
| with open(meta_path, encoding="utf-8") as f: | |
| meta = json.load(f) | |
| # Infer quality from directory structure if not in JSON | |
| if "quality" not in meta: | |
| parts = path.parts | |
| if "excellent" in parts: | |
| meta["quality"] = "excellent" | |
| elif "poor" in parts: | |
| meta["quality"] = "poor" | |
| try: | |
| img = Image.open(path).convert("RGB") | |
| emb = np.array(model.encode(img), dtype=np.float32) | |
| except Exception as exc: | |
| print(f" SKIP {path.name} — {exc}") | |
| skipped += 1 | |
| continue | |
| embeddings.append(emb) | |
| metadata.append(json.dumps(meta)) | |
| print(f" OK {path.name} ({meta.get('product', '?')} / {meta.get('category', '?')})") | |
| if not embeddings: | |
| print("No embeddings produced — nothing to save.") | |
| sys.exit(1) | |
| OUTPUT.parent.mkdir(parents=True, exist_ok=True) | |
| np.savez( | |
| str(OUTPUT), | |
| embeddings=np.stack(embeddings), | |
| metadata=np.array(metadata, dtype=object), | |
| ) | |
| print(f"\nSaved {len(embeddings)} embeddings → {OUTPUT}") | |
| if skipped: | |
| print(f"Skipped {skipped} files (missing metadata or load error)") | |
| if __name__ == "__main__": | |
| main() | |