Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
#
|
| 21 |
-
|
| 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 (
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 (
|
| 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="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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__":
|