hackerloi45 commited on
Commit
7712c9d
Β·
1 Parent(s): 7116b90

fixed eveerything

Browse files
Files changed (1) hide show
  1. app.py +29 -58
app.py CHANGED
@@ -3,7 +3,6 @@ import uuid
3
  import gradio as gr
4
  import numpy as np
5
  from PIL import Image
6
- import qdrant_client
7
  from qdrant_client import QdrantClient
8
  from qdrant_client.http.models import VectorParams, Distance, PointStruct
9
  from sentence_transformers import SentenceTransformer
@@ -16,73 +15,64 @@ os.makedirs(UPLOAD_DIR, exist_ok=True)
16
 
17
  COLLECTION = "lost_and_found"
18
 
19
- # Qdrant client (in-memory for Spaces; replace with actual url/api_key if you use a remote Qdrant)
20
  qclient = QdrantClient(":memory:")
21
 
22
- # SentenceTransformer encoder (CLIP)
23
  encoder = SentenceTransformer("clip-ViT-B-32")
24
 
25
- # Create collection if missing (use the model vector size)
26
- VECTOR_SIZE = encoder.get_sentence_embedding_dimension()
 
 
 
 
 
 
 
27
  if not qclient.collection_exists(COLLECTION):
28
  qclient.create_collection(
29
  collection_name=COLLECTION,
30
- vectors_config=VectorParams(size=VECTOR_SIZE, distance=Distance.COSINE),
31
  )
32
 
33
-
34
  # ===============================
35
  # Encoding function
36
- # (image handling MUST remain unchanged per request)
37
  # ===============================
38
  def encode_data(text=None, image=None):
39
- """
40
- Returns a vector (numpy array) for either a PIL Image, an image path (str),
41
- or text (string). Image-handling kept exactly as requested.
42
- """
43
- # --- IMAGE branch (unchanged) ---
44
  if isinstance(image, Image.Image):
45
- # NOTE: per your instruction, do not modify the image encoding logic
46
  return encoder.encode(image.convert("RGB"))
47
  if isinstance(image, str):
48
  return encoder.encode(Image.open(image).convert("RGB"))
49
-
50
- # --- TEXT branch (safe to adjust) ---
51
  if text:
52
  return encoder.encode([text])[0]
53
-
54
  return None
55
 
56
-
57
  # ===============================
58
- # Add Item (finder uploads a found item)
59
  # ===============================
60
  def add_item(text, image, uploader_name, uploader_phone):
61
  try:
62
  img_path = None
63
  vector = None
64
 
65
- # If image provided (PIL), save and encode by image (image priority)
66
  if isinstance(image, Image.Image):
67
  img_id = str(uuid.uuid4())
68
  img_path = os.path.join(UPLOAD_DIR, f"{img_id}.png")
69
  image.save(img_path)
70
  vector = encode_data(image=image)
71
-
72
- # If no image but text provided -> encode text
73
  elif text:
74
  vector = encode_data(text=text)
75
 
76
  if vector is None:
77
- return "❌ Please provide at least an image or some text."
78
 
79
- # Ensure vector is numpy array
80
  vec = np.asarray(vector, dtype=float)
81
 
82
  payload = {
83
  "text": text or "",
84
- "uploader_name": (uploader_name or "N/A"),
85
- "uploader_phone": (uploader_phone or "N/A"),
86
  "image_path": img_path,
87
  "has_image": bool(img_path),
88
  }
@@ -96,18 +86,14 @@ def add_item(text, image, uploader_name, uploader_phone):
96
  except Exception as e:
97
  return f"❌ Error: {e}"
98
 
99
-
100
  # ===============================
101
- # Search (fixed to handle text OR image OR both)
102
- # - keep image coding intact (per request)
103
- # - if both text+image supplied, average normalized vectors (cross-modal)
104
  # ===============================
105
  def search_items(text, image, max_results, min_score):
106
  try:
107
  text_vec = None
108
  img_vec = None
109
 
110
- # get vectors (do not change image encoding)
111
  if isinstance(image, Image.Image):
112
  img_vec = encode_data(image=image)
113
  img_vec = np.asarray(img_vec, dtype=float)
@@ -115,23 +101,18 @@ def search_items(text, image, max_results, min_score):
115
  text_vec = encode_data(text=text)
116
  text_vec = np.asarray(text_vec, dtype=float)
117
 
118
- # If both provided -> combine (normalize then average)
119
  if img_vec is not None and text_vec is not None:
120
- # normalize
121
- n1 = np.linalg.norm(img_vec) + 1e-12
122
- n2 = np.linalg.norm(text_vec) + 1e-12
123
- v1 = img_vec / n1
124
- v2 = text_vec / n2
125
- qvec = v1 + v2
126
- qvec = qvec / (np.linalg.norm(qvec) + 1e-12)
127
  elif img_vec is not None:
128
  qvec = img_vec
129
  elif text_vec is not None:
130
  qvec = text_vec
131
  else:
132
- return "❌ Please provide an image or some text to search.", []
133
 
134
- # Run search
135
  hits = qclient.search(
136
  collection_name=COLLECTION,
137
  query_vector=qvec.tolist(),
@@ -144,12 +125,11 @@ def search_items(text, image, max_results, min_score):
144
  return "No matches found.", []
145
 
146
  result_texts = []
147
- gallery_items = [] # list of image paths (or placeholders)
148
 
149
  for h in hits:
150
  payload = h.payload or {}
151
- score = getattr(h, "score", None)
152
- score_str = f"{float(score):.3f}" if score is not None else "N/A"
153
  uploader_name = payload.get("uploader_name", "N/A") or "N/A"
154
  uploader_phone = payload.get("uploader_phone", "N/A") or "N/A"
155
 
@@ -162,18 +142,11 @@ def search_items(text, image, max_results, min_score):
162
  img_path = payload.get("image_path")
163
  if img_path and os.path.exists(img_path):
164
  gallery_items.append(img_path)
165
- else:
166
- # append a small placeholder (you can also skip adding)
167
- # Gradio can display an empty string but better to put a placeholder image path if desired
168
- # We'll skip adding placeholders so gallery only shows real images
169
- pass
170
 
171
  return "\n".join(result_texts), gallery_items
172
-
173
  except Exception as e:
174
  return f"❌ Error: {e}", []
175
 
176
-
177
  # ===============================
178
  # Clear DB
179
  # ===============================
@@ -183,30 +156,28 @@ def clear_database():
183
  qclient.delete_collection(COLLECTION)
184
  qclient.create_collection(
185
  collection_name=COLLECTION,
186
- vectors_config=VectorParams(size=VECTOR_SIZE, distance=Distance.COSINE),
187
  )
188
- # delete uploaded images
189
  for f in os.listdir(UPLOAD_DIR):
190
  try:
191
  os.remove(os.path.join(UPLOAD_DIR, f))
192
- except Exception:
193
  pass
194
  return "πŸ—‘οΈ Database cleared!"
195
  except Exception as e:
196
  return f"❌ Error clearing DB: {e}"
197
 
198
-
199
  # ===============================
200
  # Gradio UI
201
  # ===============================
202
  with gr.Blocks() as demo:
203
- gr.Markdown("## πŸ”Ž Lost & Found β€” Add Found Items (finder) & Search (lost)")
204
 
205
  with gr.Tab("βž• Add Found Item"):
206
  text_in = gr.Textbox(label="Description (optional)")
207
  img_in = gr.Image(type="pil", label="Upload Image (optional)")
208
- uploader_name = gr.Textbox(label="Your name (finder)")
209
- uploader_phone = gr.Textbox(label="Your phone (finder)")
210
  add_btn = gr.Button("Add to database")
211
  add_status = gr.Textbox(label="Status")
212
  add_btn.click(add_item, inputs=[text_in, img_in, uploader_name, uploader_phone], outputs=[add_status])
 
3
  import gradio as gr
4
  import numpy as np
5
  from PIL import Image
 
6
  from qdrant_client import QdrantClient
7
  from qdrant_client.http.models import VectorParams, Distance, PointStruct
8
  from sentence_transformers import SentenceTransformer
 
15
 
16
  COLLECTION = "lost_and_found"
17
 
 
18
  qclient = QdrantClient(":memory:")
19
 
20
+ # Load CLIP model
21
  encoder = SentenceTransformer("clip-ViT-B-32")
22
 
23
+ # Get vector dimension safely
24
+ try:
25
+ VECTOR_SIZE = encoder.get_sentence_embedding_dimension()
26
+ if VECTOR_SIZE is None:
27
+ VECTOR_SIZE = len(encoder.encode(["test"])[0])
28
+ except Exception:
29
+ VECTOR_SIZE = len(encoder.encode(["test"])[0])
30
+
31
+ # Create collection if not exists
32
  if not qclient.collection_exists(COLLECTION):
33
  qclient.create_collection(
34
  collection_name=COLLECTION,
35
+ vectors_config=VectorParams(size=int(VECTOR_SIZE), distance=Distance.COSINE),
36
  )
37
 
 
38
  # ===============================
39
  # Encoding function
 
40
  # ===============================
41
  def encode_data(text=None, image=None):
42
+ """Encode either text or image into embeddings"""
 
 
 
 
43
  if isinstance(image, Image.Image):
 
44
  return encoder.encode(image.convert("RGB"))
45
  if isinstance(image, str):
46
  return encoder.encode(Image.open(image).convert("RGB"))
 
 
47
  if text:
48
  return encoder.encode([text])[0]
 
49
  return None
50
 
 
51
  # ===============================
52
+ # Add Item
53
  # ===============================
54
  def add_item(text, image, uploader_name, uploader_phone):
55
  try:
56
  img_path = None
57
  vector = None
58
 
 
59
  if isinstance(image, Image.Image):
60
  img_id = str(uuid.uuid4())
61
  img_path = os.path.join(UPLOAD_DIR, f"{img_id}.png")
62
  image.save(img_path)
63
  vector = encode_data(image=image)
 
 
64
  elif text:
65
  vector = encode_data(text=text)
66
 
67
  if vector is None:
68
+ return "❌ Please provide at least an image or text."
69
 
 
70
  vec = np.asarray(vector, dtype=float)
71
 
72
  payload = {
73
  "text": text or "",
74
+ "uploader_name": uploader_name or "N/A",
75
+ "uploader_phone": uploader_phone or "N/A",
76
  "image_path": img_path,
77
  "has_image": bool(img_path),
78
  }
 
86
  except Exception as e:
87
  return f"❌ Error: {e}"
88
 
 
89
  # ===============================
90
+ # Search Items
 
 
91
  # ===============================
92
  def search_items(text, image, max_results, min_score):
93
  try:
94
  text_vec = None
95
  img_vec = None
96
 
 
97
  if isinstance(image, Image.Image):
98
  img_vec = encode_data(image=image)
99
  img_vec = np.asarray(img_vec, dtype=float)
 
101
  text_vec = encode_data(text=text)
102
  text_vec = np.asarray(text_vec, dtype=float)
103
 
 
104
  if img_vec is not None and text_vec is not None:
105
+ # Combine both queries
106
+ v1 = img_vec / (np.linalg.norm(img_vec) + 1e-12)
107
+ v2 = text_vec / (np.linalg.norm(text_vec) + 1e-12)
108
+ qvec = (v1 + v2) / 2
 
 
 
109
  elif img_vec is not None:
110
  qvec = img_vec
111
  elif text_vec is not None:
112
  qvec = text_vec
113
  else:
114
+ return "❌ Provide text or image to search.", []
115
 
 
116
  hits = qclient.search(
117
  collection_name=COLLECTION,
118
  query_vector=qvec.tolist(),
 
125
  return "No matches found.", []
126
 
127
  result_texts = []
128
+ gallery_items = []
129
 
130
  for h in hits:
131
  payload = h.payload or {}
132
+ score_str = f"{getattr(h, 'score', 0):.3f}"
 
133
  uploader_name = payload.get("uploader_name", "N/A") or "N/A"
134
  uploader_phone = payload.get("uploader_phone", "N/A") or "N/A"
135
 
 
142
  img_path = payload.get("image_path")
143
  if img_path and os.path.exists(img_path):
144
  gallery_items.append(img_path)
 
 
 
 
 
145
 
146
  return "\n".join(result_texts), gallery_items
 
147
  except Exception as e:
148
  return f"❌ Error: {e}", []
149
 
 
150
  # ===============================
151
  # Clear DB
152
  # ===============================
 
156
  qclient.delete_collection(COLLECTION)
157
  qclient.create_collection(
158
  collection_name=COLLECTION,
159
+ vectors_config=VectorParams(size=int(VECTOR_SIZE), distance=Distance.COSINE),
160
  )
 
161
  for f in os.listdir(UPLOAD_DIR):
162
  try:
163
  os.remove(os.path.join(UPLOAD_DIR, f))
164
+ except:
165
  pass
166
  return "πŸ—‘οΈ Database cleared!"
167
  except Exception as e:
168
  return f"❌ Error clearing DB: {e}"
169
 
 
170
  # ===============================
171
  # Gradio UI
172
  # ===============================
173
  with gr.Blocks() as demo:
174
+ gr.Markdown("## πŸ”Ž Lost & Found")
175
 
176
  with gr.Tab("βž• Add Found Item"):
177
  text_in = gr.Textbox(label="Description (optional)")
178
  img_in = gr.Image(type="pil", label="Upload Image (optional)")
179
+ uploader_name = gr.Textbox(label="Finder's name")
180
+ uploader_phone = gr.Textbox(label="Finder's phone")
181
  add_btn = gr.Button("Add to database")
182
  add_status = gr.Textbox(label="Status")
183
  add_btn.click(add_item, inputs=[text_in, img_in, uploader_name, uploader_phone], outputs=[add_status])