Skip to content

Reference for ultralytics/solutions/similarity_search.py

Note

This file is available at https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/similarity_search.py. If you spot a problem please help fix it by contributing a Pull Request 🛠️. Thank you 🙏!


ultralytics.solutions.similarity_search.VisualAISearch

VisualAISearch(**kwargs)

Bases: BaseSolution

VisualAISearch leverages OpenCLIP to generate high-quality image and text embeddings, aligning them in a shared semantic space. It then uses FAISS to perform fast and scalable similarity-based retrieval, allowing users to search large collections of images using natural language queries with high accuracy and speed.

Attributes:

Name Type Description
data str

Directory containing images.

device str

Computation device, e.g., 'cpu' or 'cuda'.

Source code in ultralytics/solutions/similarity_search.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def __init__(self, **kwargs):
    """Initializes the VisualAISearch class with the FAISS index file and CLIP model."""
    super().__init__(**kwargs)
    check_requirements(["git+https://github.com/ultralytics/CLIP.git", "faiss-cpu"])
    import clip
    import faiss

    self.faiss = faiss
    self.clip = clip

    self.faiss_index = "faiss.index"
    self.data_path_npy = "paths.npy"
    self.model_name = "ViT-B/32"
    self.data_dir = Path(self.CFG["data"])
    self.device = select_device(self.CFG["device"])

    if not self.data_dir.exists():
        from ultralytics.utils import ASSETS_URL

        self.LOGGER.warning(f"{self.data_dir} not found. Downloading images.zip from {ASSETS_URL}/images.zip")
        from ultralytics.utils.downloads import safe_download

        safe_download(url=f"{ASSETS_URL}/images.zip", unzip=True, retry=3)
        self.data_dir = Path("images")

    self.model, self.preprocess = clip.load(self.model_name, device=self.device)

    self.index = None
    self.image_paths = []

    self.load_or_build_index()

__call__

__call__(query)

Direct call for search function.

Source code in ultralytics/solutions/similarity_search.py
130
131
132
def __call__(self, query):
    """Direct call for search function."""
    return self.search(query)

extract_image_feature

extract_image_feature(path)

Extract CLIP image embedding.

Source code in ultralytics/solutions/similarity_search.py
61
62
63
64
65
66
def extract_image_feature(self, path):
    """Extract CLIP image embedding."""
    image = Image.open(path)
    tensor = self.preprocess(image).unsqueeze(0).to(self.device)
    with torch.no_grad():
        return self.model.encode_image(tensor).cpu().numpy()

extract_text_feature

extract_text_feature(text)

Extract CLIP text embedding.

Source code in ultralytics/solutions/similarity_search.py
68
69
70
71
72
def extract_text_feature(self, text):
    """Extract CLIP text embedding."""
    tokens = self.clip.tokenize([text]).to(self.device)
    with torch.no_grad():
        return self.model.encode_text(tokens).cpu().numpy()

load_or_build_index

load_or_build_index()

Loads FAISS index or builds a new one from image features.

Source code in ultralytics/solutions/similarity_search.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def load_or_build_index(self):
    """Loads FAISS index or builds a new one from image features."""
    # Check if the FAISS index and corresponding image paths already exist
    if Path(self.faiss_index).exists() and Path(self.data_path_npy).exists():
        self.LOGGER.info("Loading existing FAISS index...")
        self.index = self.faiss.read_index(self.faiss_index)  # Load the FAISS index from disk
        self.image_paths = np.load(self.data_path_npy)  # Load the saved image path list
        return  # Exit the function as the index is successfully loaded

    # If the index doesn't exist, start building it from scratch
    self.LOGGER.info("Building FAISS index from images...")
    vectors = []  # List to store feature vectors of images

    # Iterate over all image files in the data directory
    for file in self.data_dir.iterdir():
        # Skip files that are not valid image formats
        if file.suffix.lower().lstrip(".") not in IMG_FORMATS:
            continue
        try:
            # Extract feature vector for the image and add to the list
            vectors.append(self.extract_image_feature(file))
            self.image_paths.append(file.name)  # Store the corresponding image name
        except Exception as e:
            self.LOGGER.warning(f"Skipping {file.name}: {e}")

    # If no vectors were successfully created, raise an error
    if not vectors:
        raise RuntimeError("No image embeddings could be generated.")

    vectors = np.vstack(vectors).astype("float32")  # Stack all vectors into a NumPy array and convert to float32
    self.faiss.normalize_L2(vectors)  # Normalize vectors to unit length for cosine similarity

    self.index = self.faiss.IndexFlatIP(vectors.shape[1])  # Create a new FAISS index using inner product
    self.index.add(vectors)  # Add the normalized vectors to the FAISS index
    self.faiss.write_index(self.index, self.faiss_index)  # Save the newly built FAISS index to disk
    np.save(self.data_path_npy, np.array(self.image_paths))  # Save the list of image paths to disk

    self.LOGGER.info(f"Indexed {len(self.image_paths)} images.")

search

search(query, k=30, similarity_thresh=0.1)

Returns top-k semantically similar images to the given query.

Source code in ultralytics/solutions/similarity_search.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def search(self, query, k=30, similarity_thresh=0.1):
    """Returns top-k semantically similar images to the given query."""
    text_feat = self.extract_text_feature(query).astype("float32")
    self.faiss.normalize_L2(text_feat)

    D, index = self.index.search(text_feat, k)
    results = [
        (self.image_paths[i], float(D[0][idx])) for idx, i in enumerate(index[0]) if D[0][idx] >= similarity_thresh
    ]
    results.sort(key=lambda x: x[1], reverse=True)

    self.LOGGER.info("\nRanked Results:")
    for name, score in results:
        self.LOGGER.info(f"  - {name} | Similarity: {score:.4f}")

    return [r[0] for r in results]





ultralytics.solutions.similarity_search.SearchApp

SearchApp(data='images', device=None)

A Flask-based web interface powers the semantic image search experience, enabling users to input natural language queries and instantly view the most relevant images retrieved from the indexed database—all through a clean, responsive, and easily customizable frontend.

Parameters:

Name Type Description Default
data str

Path to images to index and search.

'images'
device str

Device to run inference on (e.g. 'cpu', 'cuda').

None
Source code in ultralytics/solutions/similarity_search.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def __init__(self, data="images", device=None):
    """Initialization of the VisualAISearch class for performing semantic image search."""
    check_requirements("flask")
    from flask import Flask, render_template, request

    self.render_template = render_template
    self.request = request
    self.searcher = VisualAISearch(data=data, device=device)
    self.app = Flask(
        __name__,
        template_folder="templates",
        static_folder=Path(data).resolve(),  # Absolute path to serve images
        static_url_path="/images",  # URL prefix for images
    )
    self.app.add_url_rule("/", view_func=self.index, methods=["GET", "POST"])

index

index()

Function to process the user query and display output.

Source code in ultralytics/solutions/similarity_search.py
162
163
164
165
166
167
168
def index(self):
    """Function to process the user query and display output."""
    results = []
    if self.request.method == "POST":
        query = self.request.form.get("query", "").strip()
        results = self.searcher(query)
    return self.render_template("similarity-search.html", results=results)

run

run(debug=False)

Runs the Flask web app.

Source code in ultralytics/solutions/similarity_search.py
170
171
172
def run(self, debug=False):
    """Runs the Flask web app."""
    self.app.run(debug=debug)





📅 Created 8 days ago ✏️ Updated 8 days ago

OSZAR »