|

AI Project: Build a Reverse Image Search Engine (CLIP + FAISS)

3D isometric illustration of a target photo being converted into a vector laser by a CLIP prism to instantly retrieve similar images from a massive FAISS index grid.

We’ve used Text Embeddings to search documents. Now, let’s use Image Embeddings to search photos. we’ll walk through how you can perform a Reverse Image Search using Python and image embeddings.

We will use CLIP (Contrastive Language-Image Pre-Training). CLIP understands images and text in the same vector space. This allows us to find images that are semantically similar to a query image.

⚡ Quick Fix: Image Similarity Search with CLIP and FAISS

CLIPModel.get_image_features() converts images into 512-dimensional vectors. Add them to a faiss.IndexFlatIP index, then search with a query image embedding to retrieve the closest visual matches by cosine similarity.

from transformers import CLIPProcessor, CLIPModel
import faiss, torch

model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")

inputs = processor(images=images, return_tensors="pt", padding=True)
with torch.no_grad():
    emb = model.get_image_features(**inputs)
emb = emb / emb.norm(p=2, dim=-1, keepdim=True)
emb = emb.numpy().astype("float32")

index = faiss.IndexFlatIP(512)
index.add(emb)
D, I = index.search(query_emb, k=3)

The full two-script walkthrough below covers the indexer, the query flow, and normalization requirements.

Step 1: Installation

pip install transformers torch pillow faiss-cpu

Step 2: The Indexer (Scanning Your Images)

First, we need to look at a folder of images, convert them to numbers (vectors), and save them in a FAISS index.

import os
from PIL import Image
from transformers import CLIPProcessor, CLIPModel
import faiss
import torch
import numpy as np

# 1. Load CLIP
model_id = "openai/clip-vit-base-patch32"
model = CLIPModel.from_pretrained(model_id)
processor = CLIPProcessor.from_pretrained(model_id)

# 2. Load Images from a folder
image_folder = "./my_photos"
image_files = [f for f in os.listdir(image_folder) if f.endswith(".jpg")]
images = [Image.open(os.path.join(image_folder, f)) for f in image_files]

# 3. Generate Embeddings
print(f"Embedding {len(images)} images...")
inputs = processor(images=images, return_tensors="pt", padding=True)
with torch.no_grad():
    outputs = model.get_image_features(**inputs)

# Normalize vectors for Cosine Similarity
embeddings = outputs / outputs.norm(p=2, dim=-1, keepdim=True)
embeddings = embeddings.numpy().astype('float32')

# 4. Create FAISS Index
index = faiss.IndexFlatIP(512) # 512 is the dimension of CLIP-base
index.add(embeddings)
print("Index created!")

Step 3: The Search (Querying)

Now, let’s take a new image and find the closest match in our folder.

# Load a query image
query_image = Image.open("query_cat.jpg")

# Embed the query
inputs = processor(images=query_image, return_tensors="pt")
with torch.no_grad():
    query_emb = model.get_image_features(**inputs)
    query_emb = query_emb / query_emb.norm(p=2, dim=-1, keepdim=True)
    query_emb = query_emb.numpy().astype('float32')

# Search FAISS
D, I = index.search(query_emb, k=3) # Find top 3 matches

print("--- Top Matches ---")
for idx in I[0]:
    print(f"Match: {image_files[idx]}")

The script will output the filenames of the images that look most like your query image!


Python Image Similarity Search with CLIP and FAISS: The Production-Grade Pattern

CLIP’s vector space makes image-to-image similarity search viable without any labeled training data on your side. Two details determine whether this holds up at scale: normalization and index choice. Normalize every embedding with / emb.norm(p=2, dim=-1, keepdim=True) before calling index.add() — skipping this step turns cosine similarity into a raw dot product and corrupts your rankings. IndexFlatIP (inner product) is the correct index type for normalized vectors; swapping in IndexFlatL2 after normalization produces equivalent results but adds unnecessary computation. For folders beyond a few thousand images, switch to faiss.IndexIVFFlat with a trained coarse quantizer — it trades a small accuracy margin for search times that stay constant regardless of index size.

Key Takeaways

  • Use Image Embeddings and CLIP to find semantically similar images to a query image.
  • Start by installing the required tools, then create a FAISS index by converting images to vectors.
  • The script will return filenames of images that closely match the query image.
  • Ensure to normalise embeddings before adding to the index to maintain accurate cosine similarity rankings.
  • For large folders, use faiss.IndexIVFFlat for efficient searches without compromising accuracy.

Python Pro Hub readers also found these helpful:

Similar Posts

Leave a Reply