Shahriar-jaman commited on
Commit
2607de9
·
verified ·
1 Parent(s): 9c2292d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -57
app.py CHANGED
@@ -65,7 +65,7 @@ def validate_custom_harmful(custom_harmful: str) -> List[str]:
65
  def calculate_threat_score(harmful_objects: List[dict], custom_weights: dict) -> float:
66
  score = 0.0
67
  for obj in harmful_objects:
68
- if obj["object"] != "None" and obj["object"] != "Error":
69
  weight = custom_weights.get(obj["object"], 1.0)
70
  score += obj["confidence"] / 100 * weight
71
  return min(score, 100.0) # Cap at 100
@@ -115,63 +115,68 @@ def cancel_analysis():
115
  cancel_flag = True
116
  return "Analysis cancellation requested. Please wait for the current operation to stop."
117
 
118
- # Format results as a detailed report
119
- def format_detailed_report(results, timestamp, keyword: str = ""):
 
120
  filtered_results = keyword_search(results, keyword)
121
- report = f"# Investigation Report\n**Generated on**: {timestamp}\n\n## Key Findings\n"
122
- harmful_found = any(len(result.get("harmful_objects", [])) > 0 and result["harmful_objects"][0]["object"] != "None" for result in filtered_results)
123
- high_similarity = any(result.get("similarity_to_image_1", 0) > 0.8 for result in filtered_results if "similarity_to_image_1" in result)
124
- faces_detected = any(result.get("faces", "No faces detected") != "No faces detected" for result in filtered_results)
125
- threats_detected = any(result.get("threat_score", 0) > 50 for result in filtered_results)
126
- if harmful_found:
127
- report += "- Harmful objects detected in one or more images.\n"
128
- if high_similarity:
129
- report += "- High similarity (>0.8) detected between images.\n"
130
- if faces_detected:
131
- report += "- Faces detected with identifiable attributes.\n"
132
- if threats_detected:
133
- report += "- High threat scores (>50) detected in one or more images.\n"
134
- if not (harmful_found or high_similarity or faces_detected or threats_detected):
135
- report += "- No critical findings detected.\n"
136
-
137
- report += "\n## Analysis Details\n"
 
138
  for result in filtered_results:
139
- report += f"### Image ID: {result['image_id']}\n"
 
 
140
  if "metadata" in result:
141
- report += f"- **Metadata**: Timestamp: {result['metadata']['timestamp']}, GPS: {result['metadata']['gps']['latitude']}, {result['metadata']['gps']['longitude']}, Camera: {result['metadata']['camera']}\n"
 
142
  if "description" in result:
143
- report += f"- **Description**: {result['description']}\n"
144
  if "signs" in result:
145
- report += f"- **Signs**: {result['signs']}\n"
146
  if "harmful_objects" in result:
147
  harmful_str = ", ".join([f"{obj['object']} ({obj['confidence']}%)" for obj in result['harmful_objects']])
148
- report += f"- **Harmful Objects**: {harmful_str}\n"
149
  if "similarity_to_image_1" in result:
150
- report += f"- **Similarity**: {result['similarity_to_image_1']:.2f}\n"
151
  if "faces" in result:
152
- report += f"- **Faces**: {result['faces']}\n"
153
  if "objects" in result:
154
  objects_str = ", ".join([f"{obj['object']} at {obj['bbox']}" for obj in result['objects']])
155
- report += f"- **Objects**: {objects_str}\n"
156
  if "clothing" in result:
157
- report += f"- **Clothing/Colors**: {result['clothing']}\n"
158
  if "scene_context" in result:
159
- report += f"- **Scene**: {result['scene_context']}\n"
160
  if "activity" in result:
161
- report += f"- **Activity**: {result['activity']}\n"
162
  if "threat_score" in result:
163
- report += f"- **Threat Score**: {result['threat_score']:.1f}/100\n"
164
  if "annotation" in result:
165
- report += f"- **Annotation**: {result['annotation']}\n"
166
  if "flag" in result and result["flag"]:
167
- report += f"- **Flagged**: Yes\n"
168
  if "comments" in result:
169
- report += f"- **Investigator Comments**: {result['comments']}\n"
170
  if "error" in result:
171
- report += f"- **Error**: {result['error']}\n"
172
- report += "\n"
173
 
174
- return report
175
 
176
  @app.post("/predict")
177
  async def predict(
@@ -417,11 +422,19 @@ def gradio_predict(
417
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
418
  errors = []
419
 
 
 
 
 
 
 
 
 
420
  for idx, image in enumerate(images, 1):
421
  if cancel_flag:
422
  output = f"**Analysis Cancelled at**: {timestamp}\n"
423
  if results:
424
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
425
  return output
426
  try:
427
  # Convert Gradio image input to PIL and resize
@@ -438,7 +451,7 @@ def gradio_predict(
438
  if cancel_flag:
439
  output = f"**Analysis Cancelled at**: {timestamp}\n"
440
  if results:
441
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
442
  return output
443
  result["metadata"] = extract_metadata(image_data.getvalue())
444
 
@@ -446,7 +459,7 @@ def gradio_predict(
446
  if cancel_flag:
447
  output = f"**Analysis Cancelled at**: {timestamp}\n"
448
  if results:
449
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
450
  return output
451
  prompt_desc = "<image> Provide a " + ("brief description of the image." if description_level == "basic" else "detailed description of the image, including objects, colors, people, and environmental context.")
452
  inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
@@ -457,7 +470,7 @@ def gradio_predict(
457
  if cancel_flag:
458
  output = f"**Analysis Cancelled at**: {timestamp}\n"
459
  if results:
460
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
461
  return output
462
  prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
463
  inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
@@ -468,7 +481,7 @@ def gradio_predict(
468
  if cancel_flag:
469
  output = f"**Analysis Cancelled at**: {timestamp}\n"
470
  if results:
471
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
472
  return output
473
  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."
474
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
@@ -480,23 +493,27 @@ def gradio_predict(
480
  confidence = 90 if obj in detected_objects.split() else 60
481
  harmful_detected.append({"object": obj, "confidence": confidence})
482
  result["harmful_objects"] = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
 
 
483
 
484
  if faces:
485
  if cancel_flag:
486
  output = f"**Analysis Cancelled at**: {timestamp}\n"
487
  if results:
488
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
489
  return output
490
  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)."
491
  inputs_faces = processor(text=[prompt_faces], images=[image], return_tensors="pt", padding=True)
492
  faces_outputs = model.generate(**inputs_faces, max_new_tokens=256)
493
  result["faces"] = processor.decode(faces_outputs[0], skip_special_tokens=True).replace(prompt_faces, "").strip() or "No faces detected"
 
 
494
 
495
  if objects:
496
  if cancel_flag:
497
  output = f"**Analysis Cancelled at**: {timestamp}\n"
498
  if results:
499
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
500
  return output
501
  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."
502
  inputs_objects = processor(text=[prompt_objects], images=[image], return_tensors="pt", padding=True)
@@ -507,7 +524,7 @@ def gradio_predict(
507
  if cancel_flag:
508
  output = f"**Analysis Cancelled at**: {timestamp}\n"
509
  if results:
510
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
511
  return output
512
  prompt_activity = "<image> Describe any activities or actions occurring in the image, such as walking, running, or driving."
513
  inputs_activity = processor(text=[prompt_activity], images=[image], return_tensors="pt", padding=True)
@@ -519,7 +536,7 @@ def gradio_predict(
519
  if cancel_flag:
520
  output = f"**Analysis Cancelled at**: {timestamp}\n"
521
  if results:
522
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
523
  return output
524
  prompt_clothing = "<image> Identify clothing items and their colors worn by people in the image."
525
  inputs_clothing = processor(text=[prompt_clothing], images=[image], return_tensors="pt", padding=True)
@@ -530,7 +547,7 @@ def gradio_predict(
530
  if cancel_flag:
531
  output = f"**Analysis Cancelled at**: {timestamp}\n"
532
  if results:
533
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
534
  return output
535
  prompt_scene = "<image> Classify the scene type (e.g., indoor, outdoor, urban, rural) and estimate the time of day (e.g., day, night, dusk)."
536
  inputs_scene = processor(text=[prompt_scene], images=[image], return_tensors="pt", padding=True)
@@ -541,9 +558,11 @@ def gradio_predict(
541
  if cancel_flag:
542
  output = f"**Analysis Cancelled at**: {timestamp}\n"
543
  if results:
544
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
545
  return output
546
  result["threat_score"] = calculate_threat_score(result.get("harmful_objects", []), weights)
 
 
547
 
548
  # User Options & Controls
549
  if annotation.strip():
@@ -557,7 +576,7 @@ def gradio_predict(
557
  if cancel_flag:
558
  output = f"**Analysis Cancelled at**: {timestamp}\n"
559
  if results:
560
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
561
  return output
562
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
563
  with torch.no_grad():
@@ -597,12 +616,14 @@ def gradio_predict(
597
  if cancel_flag:
598
  output = f"**Analysis Cancelled at**: {timestamp}\n"
599
  if results:
600
- output += "\n**Partial Results**:\n" + format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
601
  return output
602
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
603
  np.linalg.norm(base_embedding) * np.linalg.norm(image_embeddings[i])
604
  )
605
  results[i]["similarity_to_image_1"] = float(sim[0][0])
 
 
606
 
607
  # Filter by attributes
608
  if filter_attributes.strip():
@@ -621,14 +642,14 @@ def gradio_predict(
621
  case_output = manage_case_folder(case_folder, results, case_action)
622
 
623
  # Format output
624
- output = format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
625
 
626
  # Reset cancellation flag
627
  cancel_flag = False
628
  return output
629
 
630
  # Helper function to format results
631
- def format_results(results, timestamp, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors):
632
  output = f"**Analysis Timestamp**: {timestamp}\n\n"
633
  headers = ["Image ID"]
634
  if metadata:
@@ -715,7 +736,7 @@ def format_results(results, timestamp, keyword_search, json_export, detailed_rep
715
 
716
  # Handle detailed report
717
  if detailed_report:
718
- output += "\n**Detailed Report**:\n" + format_detailed_report(results, timestamp, keyword_search)
719
 
720
  # Append case folder output
721
  if case_folder.strip() and case_output:
@@ -729,9 +750,9 @@ with gr.Blocks() as iface:
729
  gr.Markdown("Upload up to 3 images (up to 10MB each, any resolution) ")
730
 
731
  with gr.Row():
732
- image_1 = gr.Image(label="Upload Image 1 ")
733
- image_2 = gr.Image(label="Upload Image 2 ")
734
- image_3 = gr.Image(label="Upload Image 3 ")
735
 
736
  with gr.Row():
737
  with gr.Column():
 
65
  def calculate_threat_score(harmful_objects: List[dict], custom_weights: dict) -> float:
66
  score = 0.0
67
  for obj in harmful_objects:
68
+ if obj["object"] not in ["None", "Error"]:
69
  weight = custom_weights.get(obj["object"], 1.0)
70
  score += obj["confidence"] / 100 * weight
71
  return min(score, 100.0) # Cap at 100
 
115
  cancel_flag = True
116
  return "Analysis cancellation requested. Please wait for the current operation to stop."
117
 
118
+ # Format results as a detailed report (optimized)
119
+ def format_detailed_report(results, timestamp, findings, keyword: str = ""):
120
+ global cancel_flag
121
  filtered_results = keyword_search(results, keyword)
122
+ report_lines = [f"# Investigation Report\n**Generated on**: {timestamp}\n\n## Key Findings\n"]
123
+
124
+ # Add key findings from precomputed dictionary
125
+ if findings["harmful_found"]:
126
+ report_lines.append("- Harmful objects detected in one or more images.\n")
127
+ if findings["high_similarity"]:
128
+ report_lines.append("- High similarity (>0.8) detected between images.\n")
129
+ if findings["faces_detected"]:
130
+ report_lines.append("- Faces detected with identifiable attributes.\n")
131
+ if findings["threats_detected"]:
132
+ report_lines.append("- High threat scores (>50) detected in one or more images.\n")
133
+ if not any(findings.values()):
134
+ report_lines.append("- No critical findings detected.\n")
135
+
136
+ if cancel_flag:
137
+ return "".join(report_lines) + "\n**Report Generation Cancelled**"
138
+
139
+ report_lines.append("\n## Analysis Details\n")
140
  for result in filtered_results:
141
+ if cancel_flag:
142
+ return "".join(report_lines) + "\n**Report Generation Cancelled**"
143
+ report_lines.append(f"### Image ID: {result['image_id']}\n")
144
  if "metadata" in result:
145
+ meta = result["metadata"]
146
+ report_lines.append(f"- **Metadata**: Timestamp: {meta.get('timestamp', 'N/A')}, GPS: {meta.get('gps', {}).get('latitude', 'N/A')}, {meta.get('gps', {}).get('longitude', 'N/A')}, Camera: {meta.get('camera', 'N/A')}\n")
147
  if "description" in result:
148
+ report_lines.append(f"- **Description**: {result['description']}\n")
149
  if "signs" in result:
150
+ report_lines.append(f"- **Signs**: {result['signs']}\n")
151
  if "harmful_objects" in result:
152
  harmful_str = ", ".join([f"{obj['object']} ({obj['confidence']}%)" for obj in result['harmful_objects']])
153
+ report_lines.append(f"- **Harmful Objects**: {harmful_str}\n")
154
  if "similarity_to_image_1" in result:
155
+ report_lines.append(f"- **Similarity**: {result['similarity_to_image_1']:.2f}\n")
156
  if "faces" in result:
157
+ report_lines.append(f"- **Faces**: {result['faces']}\n")
158
  if "objects" in result:
159
  objects_str = ", ".join([f"{obj['object']} at {obj['bbox']}" for obj in result['objects']])
160
+ report_lines.append(f"- **Objects**: {objects_str}\n")
161
  if "clothing" in result:
162
+ report_lines.append(f"- **Clothing/Colors**: {result['clothing']}\n")
163
  if "scene_context" in result:
164
+ report_lines.append(f"- **Scene**: {result['scene_context']}\n")
165
  if "activity" in result:
166
+ report_lines.append(f"- **Activity**: {result['activity']}\n")
167
  if "threat_score" in result:
168
+ report_lines.append(f"- **Threat Score**: {result['threat_score']:.1f}/100\n")
169
  if "annotation" in result:
170
+ report_lines.append(f"- **Annotation**: {result['annotation']}\n")
171
  if "flag" in result and result["flag"]:
172
+ report_lines.append(f"- **Flagged**: Yes\n")
173
  if "comments" in result:
174
+ report_lines.append(f"- **Investigator Comments**: {result['comments']}\n")
175
  if "error" in result:
176
+ report_lines.append(f"- **Error**: {result['error']}\n")
177
+ report_lines.append("\n")
178
 
179
+ return "".join(report_lines)
180
 
181
  @app.post("/predict")
182
  async def predict(
 
422
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
423
  errors = []
424
 
425
+ # Precompute key findings
426
+ findings = {
427
+ "harmful_found": False,
428
+ "high_similarity": False,
429
+ "faces_detected": False,
430
+ "threats_detected": False
431
+ }
432
+
433
  for idx, image in enumerate(images, 1):
434
  if cancel_flag:
435
  output = f"**Analysis Cancelled at**: {timestamp}\n"
436
  if results:
437
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
438
  return output
439
  try:
440
  # Convert Gradio image input to PIL and resize
 
451
  if cancel_flag:
452
  output = f"**Analysis Cancelled at**: {timestamp}\n"
453
  if results:
454
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
455
  return output
456
  result["metadata"] = extract_metadata(image_data.getvalue())
457
 
 
459
  if cancel_flag:
460
  output = f"**Analysis Cancelled at**: {timestamp}\n"
461
  if results:
462
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
463
  return output
464
  prompt_desc = "<image> Provide a " + ("brief description of the image." if description_level == "basic" else "detailed description of the image, including objects, colors, people, and environmental context.")
465
  inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
 
470
  if cancel_flag:
471
  output = f"**Analysis Cancelled at**: {timestamp}\n"
472
  if results:
473
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
474
  return output
475
  prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
476
  inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
 
481
  if cancel_flag:
482
  output = f"**Analysis Cancelled at**: {timestamp}\n"
483
  if results:
484
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
485
  return output
486
  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."
487
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
 
493
  confidence = 90 if obj in detected_objects.split() else 60
494
  harmful_detected.append({"object": obj, "confidence": confidence})
495
  result["harmful_objects"] = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
496
+ if harmful_detected and result["harmful_objects"][0]["object"] != "None":
497
+ findings["harmful_found"] = True
498
 
499
  if faces:
500
  if cancel_flag:
501
  output = f"**Analysis Cancelled at**: {timestamp}\n"
502
  if results:
503
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
504
  return output
505
  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)."
506
  inputs_faces = processor(text=[prompt_faces], images=[image], return_tensors="pt", padding=True)
507
  faces_outputs = model.generate(**inputs_faces, max_new_tokens=256)
508
  result["faces"] = processor.decode(faces_outputs[0], skip_special_tokens=True).replace(prompt_faces, "").strip() or "No faces detected"
509
+ if result["faces"] != "No faces detected":
510
+ findings["faces_detected"] = True
511
 
512
  if objects:
513
  if cancel_flag:
514
  output = f"**Analysis Cancelled at**: {timestamp}\n"
515
  if results:
516
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
517
  return output
518
  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."
519
  inputs_objects = processor(text=[prompt_objects], images=[image], return_tensors="pt", padding=True)
 
524
  if cancel_flag:
525
  output = f"**Analysis Cancelled at**: {timestamp}\n"
526
  if results:
527
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
528
  return output
529
  prompt_activity = "<image> Describe any activities or actions occurring in the image, such as walking, running, or driving."
530
  inputs_activity = processor(text=[prompt_activity], images=[image], return_tensors="pt", padding=True)
 
536
  if cancel_flag:
537
  output = f"**Analysis Cancelled at**: {timestamp}\n"
538
  if results:
539
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
540
  return output
541
  prompt_clothing = "<image> Identify clothing items and their colors worn by people in the image."
542
  inputs_clothing = processor(text=[prompt_clothing], images=[image], return_tensors="pt", padding=True)
 
547
  if cancel_flag:
548
  output = f"**Analysis Cancelled at**: {timestamp}\n"
549
  if results:
550
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
551
  return output
552
  prompt_scene = "<image> Classify the scene type (e.g., indoor, outdoor, urban, rural) and estimate the time of day (e.g., day, night, dusk)."
553
  inputs_scene = processor(text=[prompt_scene], images=[image], return_tensors="pt", padding=True)
 
558
  if cancel_flag:
559
  output = f"**Analysis Cancelled at**: {timestamp}\n"
560
  if results:
561
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
562
  return output
563
  result["threat_score"] = calculate_threat_score(result.get("harmful_objects", []), weights)
564
+ if result["threat_score"] > 50:
565
+ findings["threats_detected"] = True
566
 
567
  # User Options & Controls
568
  if annotation.strip():
 
576
  if cancel_flag:
577
  output = f"**Analysis Cancelled at**: {timestamp}\n"
578
  if results:
579
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
580
  return output
581
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
582
  with torch.no_grad():
 
616
  if cancel_flag:
617
  output = f"**Analysis Cancelled at**: {timestamp}\n"
618
  if results:
619
+ output += "\n**Partial Results**:\n" + format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
620
  return output
621
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
622
  np.linalg.norm(base_embedding) * np.linalg.norm(image_embeddings[i])
623
  )
624
  results[i]["similarity_to_image_1"] = float(sim[0][0])
625
+ if results[i]["similarity_to_image_1"] > 0.8:
626
+ findings["high_similarity"] = True
627
 
628
  # Filter by attributes
629
  if filter_attributes.strip():
 
642
  case_output = manage_case_folder(case_folder, results, case_action)
643
 
644
  # Format output
645
+ output = format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors)
646
 
647
  # Reset cancellation flag
648
  cancel_flag = False
649
  return output
650
 
651
  # Helper function to format results
652
+ def format_results(results, timestamp, findings, keyword_search, json_export, detailed_report, case_folder, case_action, filter_attributes, annotation, flag_images, comments, errors):
653
  output = f"**Analysis Timestamp**: {timestamp}\n\n"
654
  headers = ["Image ID"]
655
  if metadata:
 
736
 
737
  # Handle detailed report
738
  if detailed_report:
739
+ output += "\n**Detailed Report**:\n" + format_detailed_report(results, timestamp, findings, keyword_search)
740
 
741
  # Append case folder output
742
  if case_folder.strip() and case_output:
 
750
  gr.Markdown("Upload up to 3 images (up to 10MB each, any resolution) ")
751
 
752
  with gr.Row():
753
+ image_1 = gr.Image(label="Upload Image 1 (up to 10MB)")
754
+ image_2 = gr.Image(label="Upload Image 2 (up to 10MB)")
755
+ image_3 = gr.Image(label="Upload Image 3 (up to 10MB)")
756
 
757
  with gr.Row():
758
  with gr.Column():