ankitkr1499 commited on
Commit
481d782
·
verified ·
1 Parent(s): b971a08

Update image_search.py

Browse files
Files changed (1) hide show
  1. image_search.py +151 -108
image_search.py CHANGED
@@ -1,108 +1,151 @@
1
- import sys
2
- import platform
3
-
4
- # Only patch if not running on Windows (e.g., Streamlit Cloud uses Linux)
5
- if platform.system() != "Windows":
6
- try:
7
- import pysqlite3
8
- sys.modules["sqlite3"] = pysqlite3
9
- except ImportError:
10
- raise ImportError("pysqlite3-binary is not installed. Add it to requirements.txt for deployment.")
11
-
12
- import torch
13
- import os
14
- from transformers import CLIPProcessor, CLIPModel
15
- from PIL import Image
16
- import chromadb
17
- from chromadb.utils.embedding_functions import EmbeddingFunction
18
- from tqdm import tqdm
19
- import subprocess
20
- from download import download_flickr8k_dataset
21
-
22
- class CLIPEmbeddingFunction(EmbeddingFunction):
23
- def __init__(self):
24
- self.device = "cuda" if torch.cuda.is_available() else "cpu"
25
- self.model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32").to(self.device).eval()
26
- self.processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
27
-
28
- def embed_image(self, image):
29
- # If `image` is a string, treat it as a file path and open it
30
- if isinstance(image, str):
31
- try:
32
- image = Image.open(image).convert("RGB")
33
- except Exception as e:
34
- raise ValueError(f"Could not open image at {image}. Error: {e}")
35
-
36
- # Process the image through CLIP
37
- inputs = self.processor(images=image, return_tensors="pt").to(self.device)
38
- with torch.no_grad():
39
- image_features = self.model.get_image_features(**inputs)
40
- return image_features.cpu().numpy()[0].tolist()
41
-
42
- def embed_text(self, text):
43
- inputs = self.processor(text=text, return_tensors="pt", padding=True, truncation=True).to(self.device)
44
- with torch.no_grad():
45
- text_features = self.model.get_text_features(**inputs)
46
- return text_features.cpu().numpy()[0].tolist()
47
-
48
- def embed_combined(self, text, image):
49
- text_emb = torch.tensor(self.embed_text(text))
50
- image_emb = torch.tensor(self.embed_image(image))
51
- avg_emb = (text_emb + image_emb) / 2
52
- return avg_emb.numpy().tolist()
53
-
54
- # Initialize CLIP embedder
55
- clip_embedder = CLIPEmbeddingFunction()
56
-
57
- def train_rag_on_images(image_folder):
58
-
59
- # Check if image folder exists; if not, run downloader
60
- if not os.path.exists(image_folder):
61
- print(f"{image_folder} not found. Downloading dataset...")
62
- download_flickr8k_dataset()
63
-
64
- """Embeds and stores image features in ChromaDB."""
65
- client = chromadb.PersistentClient(path="./chroma_db")
66
- collection = client.get_or_create_collection(name="image_search")
67
-
68
- image_paths = []
69
- for root, dirs, files in os.walk(image_folder):
70
- for file in files:
71
- if file.lower().endswith((".jpg", ".png", ".jpeg")):
72
- image_paths.append(os.path.join(root, file))
73
- image_paths=image_paths[:4000] # Limit to 4000 images for faster training
74
- print(f"Embedding {len(image_paths)} images...")
75
-
76
- for idx, path in enumerate(tqdm(image_paths)):
77
- embedding = clip_embedder.embed_image(path)
78
- collection.add(
79
- documents=[path],
80
- embeddings=[embedding],
81
- ids=[f"img_{idx}"]
82
- )
83
- print("Training complete. Image embeddings stored in ChromaDB.")
84
-
85
- def query_similar_images(prompt=None, image=None, k=5):
86
- """Returns k similar images using text, image, or both."""
87
- client = chromadb.PersistentClient(path="./chroma_db")
88
- collection = client.get_collection("image_search")
89
-
90
- if prompt and image:
91
- query_vector = clip_embedder.embed_combined(prompt, image)
92
- elif image:
93
- query_vector = clip_embedder.embed_image(image)
94
- elif prompt:
95
- query_vector = clip_embedder.embed_text(prompt)
96
- else:
97
- raise ValueError("Please provide at least a prompt or an image path.")
98
-
99
- results = collection.query(query_embeddings=[query_vector], n_results=k)
100
- image_paths = results['documents'][0]
101
-
102
- # Check if any result path is missing
103
- if any(not os.path.exists(path) for path in image_paths):
104
- print("Some image files are missing. Re-downloading dataset...")
105
- download_flickr8k_dataset()
106
-
107
- return image_paths
108
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import platform
3
+ import os
4
+ import torch
5
+ import pickle
6
+ import numpy as np
7
+ from PIL import Image
8
+ from tqdm import tqdm
9
+
10
+ # Patch sqlite for Streamlit Cloud
11
+ if platform.system() != "Windows":
12
+ try:
13
+ import pysqlite3
14
+ sys.modules["sqlite3"] = pysqlite3
15
+ except ImportError:
16
+ raise ImportError("pysqlite3-binary is not installed. Add it to requirements.txt for deployment.")
17
+
18
+ # Huggingface Transformers
19
+ # from transformers import CLIPProcessor, CLIPModel
20
+
21
+ # ChromaDB
22
+ import chromadb
23
+ from chromadb.utils.embedding_functions import EmbeddingFunction
24
+
25
+ # Dataset fallback (optional)
26
+ from download import download_flickr8k_dataset
27
+
28
+
29
+ # -------------------------
30
+ # CLIP Embedding Function
31
+ # -------------------------
32
+ class CLIPEmbeddingFunction(EmbeddingFunction):
33
+ def __init__(self):
34
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
35
+ self.model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32").to(self.device).eval()
36
+ self.processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
37
+
38
+ def embed_image(self, image):
39
+ if isinstance(image, str):
40
+ try:
41
+ image = Image.open(image).convert("RGB")
42
+ except Exception as e:
43
+ raise ValueError(f"Could not open image at {image}. Error: {e}")
44
+ inputs = self.processor(images=image, return_tensors="pt").to(self.device)
45
+ with torch.no_grad():
46
+ image_features = self.model.get_image_features(**inputs)
47
+ return image_features.cpu().numpy()[0].tolist()
48
+
49
+ def embed_text(self, text):
50
+ inputs = self.processor(text=text, return_tensors="pt", padding=True, truncation=True).to(self.device)
51
+ with torch.no_grad():
52
+ text_features = self.model.get_text_features(**inputs)
53
+ return text_features.cpu().numpy()[0].tolist()
54
+
55
+ def embed_combined(self, text, image):
56
+ text_emb = torch.tensor(self.embed_text(text))
57
+ image_emb = torch.tensor(self.embed_image(image))
58
+ avg_emb = (text_emb + image_emb) / 2
59
+ return avg_emb.numpy().tolist()
60
+
61
+
62
+ # -------------------------
63
+ # Load CLIP + Custom Model
64
+ # -------------------------
65
+ clip_embedder = CLIPEmbeddingFunction()
66
+
67
+ # Load custom-trained model files
68
+ with open("nn_model.pkl", "rb") as f:
69
+ custom_nn_model = pickle.load(f)
70
+
71
+ custom_image_embeddings = np.load("image_embeddings.npy")
72
+ with open("image_paths.pkl", "rb") as f:
73
+ custom_image_paths = pickle.load(f)
74
+
75
+
76
+ # -------------------------
77
+ # ChromaDB Training Function
78
+ # -------------------------
79
+ def train_rag_on_images(image_folder):
80
+ """Embeds and stores image features in ChromaDB using CLIP."""
81
+ if not os.path.exists(image_folder):
82
+ print(f"{image_folder} not found. Downloading dataset...")
83
+ download_flickr8k_dataset()
84
+
85
+ client = chromadb.PersistentClient(path="./chroma_db")
86
+ collection = client.get_or_create_collection(name="image_search")
87
+
88
+ image_paths = []
89
+ for root, _, files in os.walk(image_folder):
90
+ for file in files:
91
+ if file.lower().endswith((".jpg", ".jpeg", ".png")):
92
+ image_paths.append(os.path.join(root, file))
93
+
94
+ image_paths = image_paths[:4000]
95
+ print(f"Embedding {len(image_paths)} images...")
96
+
97
+ for idx, path in enumerate(tqdm(image_paths)):
98
+ embedding = clip_embedder.embed_image(path)
99
+ collection.add(
100
+ documents=[path],
101
+ embeddings=[embedding],
102
+ ids=[f"img_{idx}"]
103
+ )
104
+
105
+ print("Training complete. Embeddings stored in ChromaDB.")
106
+
107
+
108
+ # -------------------------
109
+ # Query from Custom Model
110
+ # -------------------------
111
+ def query_custom_model(prompt=None, image=None, k=3):
112
+ """Returns top-k similar images from your custom-trained model."""
113
+ if prompt and image:
114
+ query_vector = clip_embedder.embed_combined(prompt, image)
115
+ elif image:
116
+ query_vector = clip_embedder.embed_image(image)
117
+ elif prompt:
118
+ query_vector = clip_embedder.embed_text(prompt)
119
+ else:
120
+ raise ValueError("Please provide at least a prompt or an image.")
121
+
122
+ distances, indices = custom_nn_model.kneighbors([query_vector], n_neighbors=k)
123
+ return [custom_image_paths[i] for i in indices[0]]
124
+
125
+
126
+ # -------------------------
127
+ # Query from CLIP + ChromaDB
128
+ # -------------------------
129
+ def query_similar_images(prompt=None, image=None, k=5, show_custom=True):
130
+ """Returns k similar images using CLIP + ChromaDB, and optionally top-3 from custom model."""
131
+ client = chromadb.PersistentClient(path="./chroma_db")
132
+ collection = client.get_collection("image_search")
133
+
134
+ if prompt and image:
135
+ query_vector = clip_embedder.embed_combined(prompt, image)
136
+ elif image:
137
+ query_vector = clip_embedder.embed_image(image)
138
+ elif prompt:
139
+ query_vector = clip_embedder.embed_text(prompt)
140
+ else:
141
+ raise ValueError("Please provide at least a prompt or an image.")
142
+
143
+ results = collection.query(query_embeddings=[query_vector], n_results=k)
144
+ clip_image_paths = results['documents'][0]
145
+
146
+ if any(not os.path.exists(path) for path in clip_image_paths):
147
+ print("Some image files are missing. Re-downloading dataset...")
148
+ download_flickr8k_dataset()
149
+
150
+ custom_results = query_custom_model(prompt, image) if show_custom else []
151
+ return clip_image_paths, custom_results