Shahriar-jaman commited on
Commit
57b8903
·
verified ·
1 Parent(s): 69d9c08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -11
app.py CHANGED
@@ -8,6 +8,7 @@ from typing import List
8
  import io
9
  import gradio as gr
10
  from datetime import datetime
 
11
 
12
  # Initialize FastAPI app with increased upload limit (10MB)
13
  app = FastAPI()
@@ -17,25 +18,59 @@ model_id = "HuggingFaceTB/SmolVLM-Instruct"
17
  processor = AutoProcessor.from_pretrained(model_id, token=os.environ.get("HF_TOKEN"), padding=True)
18
  model = AutoModelForVision2Seq.from_pretrained(model_id, token=os.environ.get("HF_TOKEN"))
19
 
20
- # Harmful objects list for detection
21
- harmful_objects = ["knife", "gun", "weapon", "blood", "syringe", "bomb", "blade"]
22
 
23
  # Resize image to max 1024x1024 while preserving aspect ratio
24
  def resize_image(image: Image.Image, max_size: int = 1024) -> Image.Image:
25
  image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
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
 
 
 
 
 
 
 
39
  results = []
40
  image_embeddings = []
41
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -69,7 +104,7 @@ async def predict(
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()
@@ -80,6 +115,27 @@ async def predict(
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)
@@ -100,6 +156,12 @@ async def predict(
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
@@ -120,7 +182,13 @@ def gradio_predict(
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]
@@ -128,12 +196,18 @@ def gradio_predict(
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 = []
@@ -163,7 +237,7 @@ def gradio_predict(
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()
@@ -174,6 +248,27 @@ def gradio_predict(
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)
@@ -194,6 +289,12 @@ def gradio_predict(
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
@@ -216,6 +317,12 @@ def gradio_predict(
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"
@@ -232,11 +339,27 @@ def gradio_predict(
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
@@ -250,11 +373,17 @@ iface = gr.Interface(
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.Textbox(label="Investigation Results", placeholder="Results will appear here..."),
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 displayed in a structured table for investigative analysis."
258
  )
259
 
260
  if __name__ == "__main__":
 
8
  import io
9
  import gradio as gr
10
  from datetime import datetime
11
+ import json
12
 
13
  # Initialize FastAPI app with increased upload limit (10MB)
14
  app = FastAPI()
 
18
  processor = AutoProcessor.from_pretrained(model_id, token=os.environ.get("HF_TOKEN"), padding=True)
19
  model = AutoModelForVision2Seq.from_pretrained(model_id, token=os.environ.get("HF_TOKEN"))
20
 
21
+ # Default harmful objects list
22
+ default_harmful_objects = ["knife", "gun", "weapon", "blood", "syringe", "bomb", "blade"]
23
 
24
  # Resize image to max 1024x1024 while preserving aspect ratio
25
  def resize_image(image: Image.Image, max_size: int = 1024) -> Image.Image:
26
  image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
27
  return image
28
 
29
+ # Format results as a detailed report
30
+ def format_detailed_report(results, timestamp):
31
+ report = f"Investigation Report\nGenerated on: {timestamp}\n\n"
32
+ for result in results:
33
+ report += f"Image ID: {result['image_id']} (Timestamp: {result['timestamp']})\n"
34
+ if "description" in result:
35
+ report += f"Description: {result['description']}\n"
36
+ if "signs" in result:
37
+ report += f"Signs/Number Plates: {result['signs']}\n"
38
+ if "harmful_objects" in result:
39
+ report += f"Harmful Objects: {', '.join([f'{obj['object']} ({obj['confidence']}%)' for obj in result['harmful_objects']])}\n"
40
+ if "similarity_to_image_1" in result:
41
+ report += f"Similarity to Image 1: {result['similarity_to_image_1']:.2f}\n"
42
+ if "faces" in result:
43
+ report += f"Facial Attributes: {result['faces']}\n"
44
+ if "objects" in result:
45
+ report += f"Localized Objects: {', '.join([f'{obj['object']} at {obj['bbox']}' for obj in result['objects']])}\n"
46
+ if "scene_context" in result:
47
+ report += f"Scene Context: {result['scene_context']}\n"
48
+ if "error" in result:
49
+ report += f"Error: {result['error']}\n"
50
+ report += "-" * 50 + "\n"
51
+ return report
52
+
53
  @app.post("/predict")
54
  async def predict(
55
  files: List[UploadFile] = File(...),
56
  description: bool = True,
57
  signs: bool = True,
58
  harmful: bool = True,
59
+ similarity: bool = True,
60
+ faces: bool = False,
61
+ objects: bool = False,
62
+ scene: bool = False,
63
+ custom_harmful: str = ""
64
  ):
65
  if len(files) > 3:
66
  raise HTTPException(status_code=400, detail="Maximum 3 images allowed.")
67
 
68
+ # Process custom harmful objects
69
+ harmful_objects = default_harmful_objects.copy()
70
+ if custom_harmful.strip():
71
+ custom_objects = [obj.strip().lower() for obj in custom_harmful.split(",")]
72
+ harmful_objects.extend(custom_objects)
73
+
74
  results = []
75
  image_embeddings = []
76
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
104
 
105
  # Detect harmful objects/blood with confidence simulation if selected
106
  if harmful:
107
+ prompt_detect = f"<image> Identify any harmful objects ({', '.join(harmful_objects)}) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
108
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
109
  detect_outputs = model.generate(**inputs_detect, max_new_tokens=256)
110
  detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
 
115
  harmful_detected.append({"object": obj, "confidence": confidence})
116
  result["harmful_objects"] = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
117
 
118
+ # Detect facial attributes if selected
119
+ if faces:
120
+ prompt_faces = "<image> Detect faces and estimate attributes such as age range (e.g., child, adult, senior), gender (male, female, unknown), and emotional cues (e.g., neutral, angry, scared)."
121
+ inputs_faces = processor(text=[prompt_faces], images=[image], return_tensors="pt", padding=True)
122
+ faces_outputs = model.generate(**inputs_faces, max_new_tokens=256)
123
+ result["faces"] = processor.decode(faces_outputs[0], skip_special_tokens=True).replace(prompt_faces, "").strip() or "No faces detected"
124
+
125
+ # Localize objects if selected
126
+ if objects:
127
+ prompt_objects = "<image> Identify and localize key objects (e.g., vehicles, weapons, bags) in the image. Provide object names and approximate bounding box coordinates (x_min, y_min, x_max, y_max) in the image."
128
+ inputs_objects = processor(text=[prompt_objects], images=[image], return_tensors="pt", padding=True)
129
+ objects_outputs = model.generate(**inputs_objects, max_new_tokens=256)
130
+ result["objects"] = [{"object": obj.strip(), "bbox": "(unknown)"} for obj in processor.decode(objects_outputs[0], skip_special_tokens=True).replace(prompt_objects, "").split(",") if obj.strip()] or [{"object": "None", "bbox": "N/A"}]
131
+
132
+ # Analyze scene context if selected
133
+ if scene:
134
+ prompt_scene = "<image> Classify the scene type (e.g., indoor, outdoor, urban, rural) and estimate the time of day (e.g., day, night, dusk)."
135
+ inputs_scene = processor(text=[prompt_scene], images=[image], return_tensors="pt", padding=True)
136
+ scene_outputs = model.generate(**inputs_scene, max_new_tokens=256)
137
+ result["scene_context"] = processor.decode(scene_outputs[0], skip_special_tokens=True).replace(prompt_scene, "").strip() or "No context determined"
138
+
139
  # Get image embedding for similarity if selected
140
  if similarity:
141
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
 
156
  error_result["signs"] = "Error"
157
  if harmful:
158
  error_result["harmful_objects"] = [{"object": "Error", "confidence": 0}]
159
+ if faces:
160
+ error_result["faces"] = "Error"
161
+ if objects:
162
+ error_result["objects"] = [{"object": "Error", "bbox": "N/A"}]
163
+ if scene:
164
+ error_result["scene_context"] = "Error"
165
  results.append(error_result)
166
 
167
  # Compute similarity scores if selected
 
182
  signs: bool,
183
  harmful: bool,
184
  similarity: bool,
185
+ faces: bool,
186
+ objects: bool,
187
+ scene: bool,
188
+ combined: bool,
189
+ json_export: bool,
190
+ detailed_report: bool,
191
+ custom_harmful: str
192
  ):
193
  images = [image_1, image_2, image_3]
194
  images = [img for img in images if img is not None]
 
196
  return "Error: At least one image must be uploaded."
197
  if len(images) > 3:
198
  return "Error: Maximum 3 images allowed."
199
+ if not any([description, signs, harmful, similarity, faces, objects, scene, combined]):
200
  return "Error: At least one output option must be selected."
201
 
202
  # If combined is selected, enable all outputs
203
  if combined:
204
+ description = signs = harmful = similarity = faces = objects = scene = True
205
+
206
+ # Process custom harmful objects
207
+ harmful_objects = default_harmful_objects.copy()
208
+ if custom_harmful.strip():
209
+ custom_objects = [obj.strip().lower() for obj in custom_harmful.split(",")]
210
+ harmful_objects.extend(custom_objects)
211
 
212
  results = []
213
  image_embeddings = []
 
237
 
238
  # Detect harmful objects/blood with confidence simulation if selected
239
  if harmful:
240
+ prompt_detect = f"<image> Identify any harmful objects ({', '.join(harmful_objects)}) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
241
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
242
  detect_outputs = model.generate(**inputs_detect, max_new_tokens=256)
243
  detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
 
248
  harmful_detected.append({"object": obj, "confidence": confidence})
249
  result["harmful_objects"] = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
250
 
251
+ # Detect facial attributes if selected
252
+ if faces:
253
+ prompt_faces = "<image> Detect faces and estimate attributes such as age range (e.g., child, adult, senior), gender (male, female, unknown), and emotional cues (e.g., neutral, angry, scared)."
254
+ inputs_faces = processor(text=[prompt_faces], images=[image], return_tensors="pt", padding=True)
255
+ faces_outputs = model.generate(**inputs_faces, max_new_tokens=256)
256
+ result["faces"] = processor.decode(faces_outputs[0], skip_special_tokens=True).replace(prompt_faces, "").strip() or "No faces detected"
257
+
258
+ # Localize objects if selected
259
+ if objects:
260
+ prompt_objects = "<image> Identify and localize key objects (e.g., vehicles, weapons, bags) in the image. Provide object names and approximate bounding box coordinates (x_min, y_min, x_max, y_max) in the image."
261
+ inputs_objects = processor(text=[prompt_objects], images=[image], return_tensors="pt", padding=True)
262
+ objects_outputs = model.generate(**inputs_objects, max_new_tokens=256)
263
+ result["objects"] = [{"object": obj.strip(), "bbox": "(unknown)"} for obj in processor.decode(objects_outputs[0], skip_special_tokens=True).replace(prompt_objects, "").split(",") if obj.strip()] or [{"object": "None", "bbox": "N/A"}]
264
+
265
+ # Analyze scene context if selected
266
+ if scene:
267
+ prompt_scene = "<image> Classify the scene type (e.g., indoor, outdoor, urban, rural) and estimate the time of day (e.g., day, night, dusk)."
268
+ inputs_scene = processor(text=[prompt_scene], images=[image], return_tensors="pt", padding=True)
269
+ scene_outputs = model.generate(**inputs_scene, max_new_tokens=256)
270
+ result["scene_context"] = processor.decode(scene_outputs[0], skip_special_tokens=True).replace(prompt_scene, "").strip() or "No context determined"
271
+
272
  # Get image embedding for similarity if selected
273
  if similarity:
274
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
 
289
  error_result["signs"] = "Error"
290
  if harmful:
291
  error_result["harmful_objects"] = [{"object": "Error", "confidence": 0}]
292
+ if faces:
293
+ error_result["faces"] = "Error"
294
+ if objects:
295
+ error_result["objects"] = [{"object": "Error", "bbox": "N/A"}]
296
+ if scene:
297
+ error_result["scene_context"] = "Error"
298
  results.append(error_result)
299
 
300
  # Compute similarity scores if selected
 
317
  headers.append("Harmful Objects")
318
  if similarity:
319
  headers.append("Similarity to Image 1")
320
+ if faces:
321
+ headers.append("Facial Attributes")
322
+ if objects:
323
+ headers.append("Localized Objects")
324
+ if scene:
325
+ headers.append("Scene Context")
326
 
327
  output += "| " + " | ".join(headers) + " |\n"
328
  output += "| " + " | ".join(["---"] * len(headers)) + " |\n"
 
339
  if similarity:
340
  similarity_val = f"{result.get('similarity_to_image_1', 0):.2f}" if 'similarity_to_image_1' in result else "N/A"
341
  row.append(similarity_val)
342
+ if faces:
343
+ row.append(result.get("faces", "N/A"))
344
+ if objects:
345
+ objects_str = ", ".join([f"{obj['object']} at {obj['bbox']}" for obj in result.get("objects", [{"object": "N/A", "bbox": "N/A"}])])
346
+ row.append(objects_str)
347
+ if scene:
348
+ row.append(result.get("scene_context", "N/A"))
349
 
350
  output += "| " + " | ".join(row) + " |\n"
351
  if "error" in result:
352
  output += f"| **Error** | {result['error']}" + " | " * (len(headers) - 1) + " |\n"
353
 
354
+ # Handle JSON export
355
+ if json_export:
356
+ json_output = {"results": results, "analysis_timestamp": timestamp}
357
+ output += "\n**JSON Export**:\n```json\n" + json.dumps(json_output, indent=2) + "\n```"
358
+
359
+ # Handle detailed report
360
+ if detailed_report:
361
+ output += "\n**Detailed Report**:\n" + format_detailed_report(results, timestamp)
362
+
363
  return output
364
 
365
  # Gradio interface
 
373
  gr.Checkbox(label="Signs/Number Plates", value=True),
374
  gr.Checkbox(label="Harmful Objects/Blood", value=True),
375
  gr.Checkbox(label="Similarity to Image 1", value=True),
376
+ gr.Checkbox(label="Facial Attributes", value=False),
377
+ gr.Checkbox(label="Localized Objects", value=False),
378
+ gr.Checkbox(label="Scene Context", value=False),
379
+ gr.Checkbox(label="Combined (All Outputs)", value=False),
380
+ gr.Checkbox(label="JSON Export", value=False),
381
+ gr.Checkbox(label="Detailed Report", value=False),
382
+ gr.Textbox(label="Custom Harmful Objects (comma-separated, e.g., handgun,crowbar)", placeholder="Enter custom objects to detect")
383
  ],
384
  outputs=gr.Textbox(label="Investigation Results", placeholder="Results will appear here..."),
385
+ title="VisionSage: Image Analysis for Crime Investigation",
386
+ description="Upload up to 3 images (up to 10MB each, any resolution) and select desired outputs for investigative analysis. Features include descriptions, text extraction, harmful object detection, similarity analysis, facial attributes, object localization, and scene context. Export results as JSON or a detailed report."
387
  )
388
 
389
  if __name__ == "__main__":