Shahriar-jaman commited on
Commit
8ee7620
·
verified ·
1 Parent(s): cc3de49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -94
app.py CHANGED
@@ -26,7 +26,13 @@ def resize_image(image: Image.Image, max_size: int = 1024) -> Image.Image:
26
  return image
27
 
28
  @app.post("/predict")
29
- async def predict(files: List[UploadFile] = File(...)):
 
 
 
 
 
 
30
  if len(files) > 3:
31
  raise HTTPException(status_code=400, detail="Maximum 3 images allowed.")
32
 
@@ -45,55 +51,59 @@ async def predict(files: List[UploadFile] = File(...)):
45
  image = Image.open(io.BytesIO(image_data)).convert("RGB")
46
  image = resize_image(image, max_size=1024)
47
 
48
- # Generate description
49
- prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, people, and environmental context."
50
- inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
51
- outputs = model.generate(**inputs, max_new_tokens=256)
52
- description = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip()
53
 
54
- # Extract signs/number plates (OCR)
55
- prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
56
- inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
57
- ocr_outputs = model.generate(**inputs_ocr, max_new_tokens=256)
58
- signs_text = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip()
 
59
 
60
- # Detect harmful objects/blood with confidence simulation
61
- prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
62
- inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
63
- detect_outputs = model.generate(**inputs_detect, max_new_tokens=256)
64
- detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
65
- harmful_detected = []
66
- for obj in harmful_objects:
67
- if obj in detected_objects:
68
- confidence = 90 if obj in detected_objects.split() else 60
69
- harmful_detected.append({"object": obj, "confidence": confidence})
70
- harmful_output = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
71
 
72
- # Get image embedding for similarity
73
- inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
74
- with torch.no_grad():
75
- emb = model.vision_model(inputs_emb["pixel_values"]).last_hidden_state.mean(dim=1).cpu().numpy()
76
- image_embeddings.append(emb)
 
 
 
 
 
 
 
77
 
78
- results.append({
79
- "image_id": f"Image_{idx}",
80
- "timestamp": timestamp,
81
- "description": description if description else "No description generated.",
82
- "signs": signs_text if signs_text else "None detected",
83
- "harmful_objects": harmful_output
84
- })
 
85
  except Exception as e:
86
- results.append({
87
  "image_id": f"Image_{idx}",
88
  "timestamp": timestamp,
89
- "description": "Error processing image.",
90
- "signs": "Error",
91
- "harmful_objects": [{"object": "Error", "confidence": 0}],
92
  "error": str(e)
93
- })
 
 
 
 
 
 
 
94
 
95
- # Compute similarity scores (cosine similarity to first image)
96
- if len(image_embeddings) > 1:
97
  base_embedding = image_embeddings[0]
98
  for i in range(1, len(image_embeddings)):
99
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
@@ -104,71 +114,90 @@ async def predict(files: List[UploadFile] = File(...)):
104
  return {"results": results, "analysis_timestamp": timestamp}
105
 
106
  # Gradio interface for Hugging Face Spaces
107
- def gradio_predict(*images):
108
- if len([img for img in images if img is not None]) > 3:
 
 
 
 
 
 
 
 
 
 
 
109
  return "Error: Maximum 3 images allowed."
 
 
 
 
 
 
110
 
111
  results = []
112
  image_embeddings = []
113
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
114
 
115
  for idx, image in enumerate(images, 1):
116
- if image is None:
117
- continue
118
  try:
119
  # Convert Gradio image input to PIL and resize
120
  image = Image.fromarray(image).convert("RGB")
121
  image = resize_image(image, max_size=1024)
122
 
123
- # Generate description
124
- prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, people, and environmental context."
125
- inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
126
- outputs = model.generate(**inputs, max_new_tokens=256)
127
- description = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip()
128
 
129
- # Extract signs/number plates (OCR)
130
- prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
131
- inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
132
- ocr_outputs = model.generate(**inputs_ocr, max_new_tokens=256)
133
- signs_text = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip()
 
134
 
135
- # Detect harmful objects/blood with confidence simulation
136
- prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
137
- inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
138
- detect_outputs = model.generate(**inputs_detect, max_new_tokens=256)
139
- detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
140
- harmful_detected = []
141
- for obj in harmful_objects:
142
- if obj in detected_objects:
143
- confidence = 90 if obj in detected_objects.split() else 60
144
- harmful_detected.append({"object": obj, "confidence": confidence})
145
- harmful_output = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
146
 
147
- # Get image embedding for similarity
148
- inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
149
- with torch.no_grad():
150
- emb = model.vision_model(inputs_emb["pixel_values"]).last_hidden_state.mean(dim=1).cpu().numpy()
151
- image_embeddings.append(emb)
 
 
 
 
 
 
 
152
 
153
- results.append({
154
- "image_id": f"Image_{idx}",
155
- "timestamp": timestamp,
156
- "description": description if description else "No description generated.",
157
- "signs": signs_text if signs_text else "None detected",
158
- "harmful_objects": harmful_output
159
- })
 
160
  except Exception as e:
161
- results.append({
162
  "image_id": f"Image_{idx}",
163
  "timestamp": timestamp,
164
- "description": "Error processing image.",
165
- "signs": "Error",
166
- "harmful_objects": [{"object": "Error", "confidence": 0}],
167
  "error": str(e)
168
- })
 
 
 
 
 
 
 
169
 
170
- # Compute similarity scores
171
- if len(image_embeddings) > 1:
172
  base_embedding = image_embeddings[0]
173
  for i in range(1, len(image_embeddings)):
174
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
@@ -178,25 +207,56 @@ def gradio_predict(*images):
178
 
179
  # Format output for Gradio as tabulated markdown
180
  output = f"**Analysis Timestamp**: {timestamp}\n\n"
181
- output += "| Image ID | Description | Signs/Number Plates | Harmful Objects | Similarity to Image 1 |\n"
182
- output += "|----------|-------------|---------------------|-----------------|-----------------------|\n"
 
 
 
 
 
 
 
 
 
 
 
183
  for result in results:
184
- harmful_str = ", ".join([f"{obj['object']} ({obj['confidence']}%)" for obj in result['harmful_objects']])
185
- similarity = f"{result['similarity_to_image_1']:.2f}" if 'similarity_to_image_1' in result else "N/A"
186
- output += f"| {result['image_id']} ({result['timestamp']}) | {result['description']} | {result['signs']} | {harmful_str} | {similarity} |\n"
 
 
 
 
 
 
 
 
 
 
187
  if "error" in result:
188
- output += f"| **Error** | {result['error']} | - | - | - |\n"
 
189
  return output
190
 
191
  # Gradio interface
192
  iface = gr.Interface(
193
  fn=gradio_predict,
194
- inputs=[gr.Image(label=f"Upload Image {i+1} (up to 10MB)") for i in range(3)], # Allow up to 3 images
 
 
 
 
 
 
 
 
 
195
  outputs=gr.Markdown(label="Investigation Results"),
196
  title="VisionSage: Image Analysis for Investigation",
197
- description="Upload up to 3 images (up to 10MB each, any resolution) to get detailed descriptions, extract signs/number plates, detect harmful objects/blood with confidence scores, and compute similarity to the first image. Results are formatted for investigative analysis."
198
  )
199
 
200
  if __name__ == "__main__":
201
-
202
  iface.launch(server_name="0.0.0.0", server_port=7860)
 
26
  return image
27
 
28
  @app.post("/predict")
29
+ async def predict(
30
+ files: List[UploadFile] = File(...),
31
+ description: bool = True,
32
+ signs: bool = True,
33
+ harmful: bool = True,
34
+ similarity: bool = True
35
+ ):
36
  if len(files) > 3:
37
  raise HTTPException(status_code=400, detail="Maximum 3 images allowed.")
38
 
 
51
  image = Image.open(io.BytesIO(image_data)).convert("RGB")
52
  image = resize_image(image, max_size=1024)
53
 
54
+ result = {"image_id": f"Image_{idx}", "timestamp": timestamp}
 
 
 
 
55
 
56
+ # Generate description if selected
57
+ if description:
58
+ prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, people, and environmental context."
59
+ inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
60
+ outputs = model.generate(**inputs, max_new_tokens=256)
61
+ result["description"] = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip() or "No description generated."
62
 
63
+ # Extract signs/number plates (OCR) if selected
64
+ if signs:
65
+ prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
66
+ inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
67
+ ocr_outputs = model.generate(**inputs_ocr, max_new_tokens=256)
68
+ result["signs"] = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip() or "None detected"
 
 
 
 
 
69
 
70
+ # Detect harmful objects/blood with confidence simulation if selected
71
+ if harmful:
72
+ prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
73
+ inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
74
+ detect_outputs = model.generate(**inputs_detect, max_new_tokens=256)
75
+ detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
76
+ harmful_detected = []
77
+ for obj in harmful_objects:
78
+ if obj in detected_objects:
79
+ confidence = 90 if obj in detected_objects.split() else 60
80
+ harmful_detected.append({"object": obj, "confidence": confidence})
81
+ result["harmful_objects"] = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
82
 
83
+ # Get image embedding for similarity if selected
84
+ if similarity:
85
+ inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
86
+ with torch.no_grad():
87
+ emb = model.vision_model(inputs_emb["pixel_values"]).last_hidden_state.mean(dim=1).cpu().numpy()
88
+ image_embeddings.append(emb)
89
+
90
+ results.append(result)
91
  except Exception as e:
92
+ error_result = {
93
  "image_id": f"Image_{idx}",
94
  "timestamp": timestamp,
 
 
 
95
  "error": str(e)
96
+ }
97
+ if description:
98
+ error_result["description"] = "Error processing image."
99
+ if signs:
100
+ error_result["signs"] = "Error"
101
+ if harmful:
102
+ error_result["harmful_objects"] = [{"object": "Error", "confidence": 0}]
103
+ results.append(error_result)
104
 
105
+ # Compute similarity scores if selected
106
+ if similarity and len(image_embeddings) > 1:
107
  base_embedding = image_embeddings[0]
108
  for i in range(1, len(image_embeddings)):
109
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
 
114
  return {"results": results, "analysis_timestamp": timestamp}
115
 
116
  # Gradio interface for Hugging Face Spaces
117
+ def gradio_predict(
118
+ image_1, image_2, image_3,
119
+ description: bool,
120
+ signs: bool,
121
+ harmful: bool,
122
+ similarity: bool,
123
+ combined: bool
124
+ ):
125
+ images = [image_1, image_2, image_3]
126
+ images = [img for img in images if img is not None]
127
+ if not images:
128
+ return "Error: At least one image must be uploaded."
129
+ if len(images) > 3:
130
  return "Error: Maximum 3 images allowed."
131
+ if not any([description, signs, harmful, similarity, combined]):
132
+ return "Error: At least one output option must be selected."
133
+
134
+ # If combined is selected, enable all outputs
135
+ if combined:
136
+ description = signs = harmful = similarity = True
137
 
138
  results = []
139
  image_embeddings = []
140
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
141
 
142
  for idx, image in enumerate(images, 1):
 
 
143
  try:
144
  # Convert Gradio image input to PIL and resize
145
  image = Image.fromarray(image).convert("RGB")
146
  image = resize_image(image, max_size=1024)
147
 
148
+ result = {"image_id": f"Image_{idx}", "timestamp": timestamp}
 
 
 
 
149
 
150
+ # Generate description if selected
151
+ if description:
152
+ prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, people, and environmental context."
153
+ inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
154
+ outputs = model.generate(**inputs, max_new_tokens=256)
155
+ result["description"] = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip() or "No description generated."
156
 
157
+ # Extract signs/number plates (OCR) if selected
158
+ if signs:
159
+ prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
160
+ inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
161
+ ocr_outputs = model.generate(**inputs_ocr, max_new_tokens=256)
162
+ result["signs"] = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip() or "None detected"
 
 
 
 
 
163
 
164
+ # Detect harmful objects/blood with confidence simulation if selected
165
+ if harmful:
166
+ prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
167
+ inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
168
+ detect_outputs = model.generate(**inputs_detect, max_new_tokens=256)
169
+ detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
170
+ harmful_detected = []
171
+ for obj in harmful_objects:
172
+ if obj in detected_objects:
173
+ confidence = 90 if obj in detected_objects.split() else 60
174
+ harmful_detected.append({"object": obj, "confidence": confidence})
175
+ result["harmful_objects"] = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
176
 
177
+ # Get image embedding for similarity if selected
178
+ if similarity:
179
+ inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
180
+ with torch.no_grad():
181
+ emb = model.vision_model(inputs_emb["pixel_values"]).last_hidden_state.mean(dim=1).cpu().numpy()
182
+ image_embeddings.append(emb)
183
+
184
+ results.append(result)
185
  except Exception as e:
186
+ error_result = {
187
  "image_id": f"Image_{idx}",
188
  "timestamp": timestamp,
 
 
 
189
  "error": str(e)
190
+ }
191
+ if description:
192
+ error_result["description"] = "Error processing image."
193
+ if signs:
194
+ error_result["signs"] = "Error"
195
+ if harmful:
196
+ error_result["harmful_objects"] = [{"object": "Error", "confidence": 0}]
197
+ results.append(error_result)
198
 
199
+ # Compute similarity scores if selected
200
+ if similarity and len(image_embeddings) > 1:
201
  base_embedding = image_embeddings[0]
202
  for i in range(1, len(image_embeddings)):
203
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
 
207
 
208
  # Format output for Gradio as tabulated markdown
209
  output = f"**Analysis Timestamp**: {timestamp}\n\n"
210
+ headers = ["Image ID"]
211
+ if description:
212
+ headers.append("Description")
213
+ if signs:
214
+ headers.append("Signs/Number Plates")
215
+ if harmful:
216
+ headers.append("Harmful Objects")
217
+ if similarity:
218
+ headers.append("Similarity to Image 1")
219
+
220
+ output += "| " + " | ".join(headers) + " |\n"
221
+ output += "| " + " | ".join(["---"] * len(headers)) + " |\n"
222
+
223
  for result in results:
224
+ row = [f"{result['image_id']} ({result['timestamp']})"]
225
+ if description:
226
+ row.append(result.get("description", "N/A"))
227
+ if signs:
228
+ row.append(result.get("signs", "N/A"))
229
+ if harmful:
230
+ harmful_str = ", ".join([f"{obj['object']} ({obj['confidence']}%)" for obj in result.get("harmful_objects", [{"object": "N/A", "confidence": 0}])])
231
+ row.append(harmful_str)
232
+ if similarity:
233
+ similarity_val = f"{result.get('similarity_to_image_1', 0):.2f}" if 'similarity_to_image_1' in result else "N/A"
234
+ row.append(similarity_val)
235
+
236
+ output += "| " + " | ".join(row) + " |\n"
237
  if "error" in result:
238
+ output += f"| **Error** | {result['error']}" + " | " * (len(headers) - 1) + " |\n"
239
+
240
  return output
241
 
242
  # Gradio interface
243
  iface = gr.Interface(
244
  fn=gradio_predict,
245
+ inputs=[
246
+ gr.Image(label="Upload Image 1 (up to 10MB)"),
247
+ gr.Image(label="Upload Image 2 (up to 10MB)"),
248
+ gr.Image(label="Upload Image 3 (up to 10MB)"),
249
+ gr.Checkbox(label="Description", value=True),
250
+ gr.Checkbox(label="Signs/Number Plates", value=True),
251
+ gr.Checkbox(label="Harmful Objects/Blood", value=True),
252
+ gr.Checkbox(label="Similarity to Image 1", value=True),
253
+ gr.Checkbox(label="Combined (All Outputs)", value=False)
254
+ ],
255
  outputs=gr.Markdown(label="Investigation Results"),
256
  title="VisionSage: Image Analysis for Investigation",
257
+ description="Upload up to 3 images (up to 10MB each, any resolution) and select desired outputs: Description, Signs/Number Plates, Harmful Objects/Blood, Similarity, or Combined. Results are formatted for investigative analysis."
258
  )
259
 
260
  if __name__ == "__main__":
261
+ # Launch Gradio interface for Hugging Face Spaces
262
  iface.launch(server_name="0.0.0.0", server_port=7860)