Spaces:
Paused
Paused
Download src/search.py from Vittav/ImageSearcherVitor: direct link, hf CLI and curl.
- Browser
- Download file 1.6 kB
-
https://huggingface.co/spaces/Vittav/ImageSearcherVitor/resolve/main/src/search.py
- Command line
-
hf download hf://spaces/Vittav/ImageSearcherVitor/src/search.py
-
curl -L -o search.py https://huggingface.co/spaces/Vittav/ImageSearcherVitor/resolve/main/src/search.py
1.6 kB
| import json | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| import open_clip | |
| import torch | |
| # --- Settings (must match build_index.py) --- | |
| EMB_DIR = Path("embeddings") | |
| MODEL_NAME = "ViT-B-32" | |
| PRETRAINED = "laion2b_s34b_b79k" | |
| # --- Load everything once, when the module is imported --- | |
| model, _, _ = open_clip.create_model_and_transforms(MODEL_NAME, pretrained=PRETRAINED) | |
| model.eval() | |
| tokenizer = open_clip.get_tokenizer(MODEL_NAME) | |
| embeddings = np.load(EMB_DIR / "image_embeddings.npy") # (1000, 512) | |
| filenames = json.loads((EMB_DIR / "filenames.json").read_text()) # 1000 names | |
| def embed_text(query: str) -> np.ndarray: | |
| """Turn a text query into a normalized 512-number vector.""" | |
| tokens = tokenizer([query]) # batch of 1 query | |
| with torch.no_grad(): | |
| vec = model.encode_text(tokens) # (1, 512) | |
| vec = vec / vec.norm(dim=-1, keepdim=True) | |
| return vec[0].cpu().numpy() # (512,) | |
| def search(query: str, k: int = 5) -> list[tuple[str, float]]: | |
| """Return the top-k (filename, score) pairs, best first.""" | |
| q = embed_text(query) | |
| scores = embeddings @ q # (1000, 512) @ (512,) -> (1000,) | |
| top = np.argsort(scores)[::-1][:k] # positions of the k highest scores | |
| return [(filenames[i], float(scores[i])) for i in top] | |
| if __name__ == "__main__": | |
| query = " ".join(sys.argv[1:]) or "a dog running on the beach" | |
| for rank, (name, score) in enumerate(search(query, k=5), start=1): | |
| print(f"{rank}. {name} score={score:.3f}") |