PrashanthB461 commited on
Commit
5a3efbb
Β·
verified Β·
1 Parent(s): b898890

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +272 -438
app.py CHANGED
@@ -95,8 +95,6 @@ def initialize_model():
95
  try:
96
  logger.info("Initializing YOLOv8 model...")
97
  yolo_model = YOLO(MODEL_PATH)
98
- # Set model to evaluation mode for better accuracy
99
- yolo_model.model.eval()
100
  logger.info("YOLOv8 model loaded successfully")
101
  return True
102
  except Exception as e:
@@ -315,14 +313,14 @@ def generate_and_upload_report_to_salesforce(sf, violations, record_ids):
315
  logger.error(f"Error in Salesforce PDF report generation/upload: {e}", exc_info=True)
316
  return None, None
317
 
318
- # --- Enhanced Safety Violation Detector Class with Precise Visualization ---
319
  class SafetyViolationDetector:
320
  def __init__(self):
321
- # Enhanced detection thresholds for better accuracy
322
- self.helmet_threshold = 0.65 # Lowered for better sensitivity
323
- self.person_threshold = 0.55 # Lowered for better sensitivity
324
- self.unsafe_distance = 60 # Increased for better safety margin
325
- self.violation_cooldown = 15 # Reduced for faster response
326
 
327
  # Unauthorized zones (x1, y1, x2, y2)
328
  self.unauthorized_zones = [
@@ -335,27 +333,17 @@ class SafetyViolationDetector:
335
  self.person_tracker = {}
336
  self.person_positions_history = {}
337
  self.next_person_id = 1
338
- self.max_tracking_distance = 100
339
 
340
  self.session_violations = {}
341
 
342
- # Enhanced visualization colors
343
- self.colors = {
344
- 'no_helmet': (0, 0, 255), # Red for critical
345
- 'unauthorized': (255, 0, 255), # Magenta for high
346
- 'unsafe_distance': (0, 165, 255), # Orange for moderate
347
- 'safe': (0, 255, 0), # Green for safe
348
- 'person': (255, 255, 0), # Cyan for person detection
349
- 'helmet': (0, 255, 255) # Yellow for helmet detection
350
- }
351
-
352
  def reset_session(self):
353
  self.session_violations = {}
354
  self.active_violations = {}
355
  self.person_tracker = {}
356
  self.person_positions_history = {}
357
  self.next_person_id = 1
358
- logger.info("Session violation tracking reset for new analysis")
359
 
360
  def has_reported_violation(self, person_id, violation_type):
361
  if person_id not in self.session_violations:
@@ -430,7 +418,6 @@ class SafetyViolationDetector:
430
  persons = []
431
  helmets = []
432
 
433
- # Enhanced detection with confidence filtering
434
  for box, conf, cls_id in zip(boxes, confidences, class_ids):
435
  class_name = class_names[cls_id]
436
  if class_name == "person" and conf >= self.person_threshold:
@@ -441,17 +428,12 @@ class SafetyViolationDetector:
441
  'center': ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2),
442
  'id': person_id
443
  })
444
- # Draw person detection box
445
- self._draw_enhanced_person_box(frame, box, person_id, conf)
446
-
447
- elif class_name in ["hard hat", "helmet", "safety helmet"] and conf >= self.helmet_threshold:
448
  helmets.append({
449
  'box': box,
450
  'confidence': conf,
451
  'area': (box[2] - box[0]) * (box[3] - box[1])
452
  })
453
- # Draw helmet detection box
454
- self._draw_helmet_box(frame, box, conf)
455
 
456
  current_person_ids = set()
457
  for person in persons:
@@ -472,7 +454,6 @@ class SafetyViolationDetector:
472
  if len(self.person_tracker[person_id]['positions']) > 10:
473
  self.person_tracker[person_id]['positions'].pop(0)
474
 
475
- # Check violations with enhanced visualization
476
  for person in persons:
477
  person_id = person['id']
478
 
@@ -490,40 +471,9 @@ class SafetyViolationDetector:
490
  self._cleanup_violations(current_time)
491
  self._cleanup_inactive_persons(current_person_ids, current_time)
492
 
493
- logger.info(f"Enhanced violation detection time: {time.time() - start_time:.2f}s")
494
  return violations
495
 
496
- def _draw_enhanced_person_box(self, frame, box, person_id, confidence):
497
- """Draw enhanced person detection box with ID and confidence"""
498
- x1, y1, x2, y2 = map(int, box)
499
-
500
- # Draw main bounding box
501
- cv2.rectangle(frame, (x1, y1), (x2, y2), self.colors['person'], 2)
502
-
503
- # Draw ID and confidence label
504
- label = f"Person {person_id:03d} ({confidence:.2f})"
505
- label_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)[0]
506
-
507
- # Background for label
508
- cv2.rectangle(frame, (x1, y1 - label_size[1] - 10),
509
- (x1 + label_size[0] + 10, y1), self.colors['person'], -1)
510
-
511
- # Label text
512
- cv2.putText(frame, label, (x1 + 5, y1 - 5),
513
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2)
514
-
515
- def _draw_helmet_box(self, frame, box, confidence):
516
- """Draw helmet detection box"""
517
- x1, y1, x2, y2 = map(int, box)
518
-
519
- # Draw helmet bounding box
520
- cv2.rectangle(frame, (x1, y1), (x2, y2), self.colors['helmet'], 2)
521
-
522
- # Draw helmet label
523
- label = f"Helmet ({confidence:.2f})"
524
- cv2.putText(frame, label, (x1, y1 - 10),
525
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, self.colors['helmet'], 2)
526
-
527
  def _check_helmet_violation(self, person, helmets, frame, current_time):
528
  person_id = person['id']
529
  person_box = person['box']
@@ -532,29 +482,17 @@ class SafetyViolationDetector:
532
  if self.has_reported_violation(person_id, violation_type):
533
  return None
534
 
535
- # Enhanced head region calculation (upper 30% of person box)
536
- person_height = person_box[3] - person_box[1]
537
  head_region = [
538
- person_box[0] - 10, # Slightly expand horizontally
539
- person_box[1],
540
- person_box[2] + 10, # Slightly expand horizontally
541
- person_box[1] + (person_height * 0.35) # Upper 35% for head
542
  ]
543
 
544
- # Draw head region for visualization
545
- hx1, hy1, hx2, hy2 = map(int, head_region)
546
- cv2.rectangle(frame, (hx1, hy1), (hx2, hy2), (255, 255, 255), 1)
547
- cv2.putText(frame, "Head Region", (hx1, hy1 - 5),
548
- cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
549
-
550
  has_helmet = False
551
- best_helmet_iou = 0
552
-
553
  for helmet in helmets:
554
- iou = self._iou(helmet['box'], head_region)
555
- if iou > 0.05: # Lowered threshold for better detection
556
  has_helmet = True
557
- best_helmet_iou = max(best_helmet_iou, iou)
558
  break
559
 
560
  self.person_tracker[person_id]['helmet_status'] = has_helmet
@@ -583,8 +521,7 @@ class SafetyViolationDetector:
583
  self.person_tracker[person_id]['violations']['no_helmet']['count'] += 1
584
  self.person_tracker[person_id]['violations']['no_helmet']['last_time'] = current_time
585
 
586
- # Enhanced violation annotation - focus on head area
587
- self._annotate_helmet_violation(frame, person_box, head_region, person_id)
588
  logger.info(f"NEW VIOLATION: No helmet detected for person {person_id}")
589
 
590
  return {
@@ -597,51 +534,8 @@ class SafetyViolationDetector:
597
  else:
598
  self.active_violations[violation_key]['last_detected'] = current_time
599
  self.active_violations[violation_key]['count'] += 1
600
- else:
601
- # Draw safe helmet status
602
- px1, py1, px2, py2 = map(int, person_box)
603
- cv2.putText(frame, "βœ“ HELMET OK", (px1, py2 + 20),
604
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, self.colors['safe'], 2)
605
-
606
  return None
607
 
608
- def _annotate_helmet_violation(self, frame, person_box, head_region, person_id):
609
- """Enhanced helmet violation annotation focusing on head area"""
610
- px1, py1, px2, py2 = map(int, person_box)
611
- hx1, hy1, hx2, hy2 = map(int, head_region)
612
-
613
- # Draw critical violation box around person
614
- cv2.rectangle(frame, (px1, py1), (px2, py2), self.colors['no_helmet'], 3)
615
-
616
- # Highlight head region with pulsing effect
617
- cv2.rectangle(frame, (hx1, hy1), (hx2, hy2), self.colors['no_helmet'], 4)
618
-
619
- # Draw warning symbol at head center
620
- head_center_x = (hx1 + hx2) // 2
621
- head_center_y = (hy1 + hy2) // 2
622
-
623
- # Warning triangle
624
- triangle_pts = np.array([
625
- [head_center_x, hy1 + 5],
626
- [head_center_x - 15, hy2 - 5],
627
- [head_center_x + 15, hy2 - 5]
628
- ], np.int32)
629
- cv2.fillPoly(frame, [triangle_pts], (0, 255, 255))
630
- cv2.putText(frame, "!", (head_center_x - 5, head_center_y + 5),
631
- cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2)
632
-
633
- # Critical violation label
634
- label = f"CRITICAL: NO HELMET - ID:{person_id:03d}"
635
- label_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)[0]
636
-
637
- # Background for label
638
- cv2.rectangle(frame, (px1, py1 - label_size[1] - 15),
639
- (px1 + label_size[0] + 10, py1 - 5), self.colors['no_helmet'], -1)
640
-
641
- # Label text
642
- cv2.putText(frame, label, (px1 + 5, py1 - 10),
643
- cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
644
-
645
  def _check_unauthorized_area(self, person, frame, current_time):
646
  person_id = person['id']
647
  violation_type = 'unauthorized_area'
@@ -652,16 +546,10 @@ class SafetyViolationDetector:
652
  x1, y1, x2, y2 = person['box']
653
  person_center = ((x1 + x2) / 2, (y1 + y2) / 2)
654
 
655
- for zone_idx, zone in enumerate(self.unauthorized_zones):
656
  zx1, zy1, zx2, zy2 = zone
657
-
658
- # Draw unauthorized zone
659
- cv2.rectangle(frame, (zx1, zy1), (zx2, zy2), self.colors['unauthorized'], 2)
660
- cv2.putText(frame, f"RESTRICTED ZONE {zone_idx + 1}", (zx1, zy1 - 10),
661
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, self.colors['unauthorized'], 2)
662
-
663
  if (zx1 <= person_center[0] <= zx2 and zy1 <= person_center[1] <= zy2):
664
- violation_key = f"unauthorized_area_{person_id}_{zone_idx}"
665
 
666
  if (violation_key not in self.active_violations or
667
  current_time - self.active_violations[violation_key]['last_detected'] > self.violation_cooldown):
@@ -685,8 +573,8 @@ class SafetyViolationDetector:
685
  self.person_tracker[person_id]['violations']['unauthorized_area']['count'] += 1
686
  self.person_tracker[person_id]['violations']['unauthorized_area']['last_time'] = current_time
687
 
688
- # Enhanced unauthorized area annotation
689
- self._annotate_unauthorized_violation(frame, person['box'], zone, person_id, zone_idx)
690
  logger.info(f"NEW VIOLATION: Unauthorized area detected for person {person_id}")
691
 
692
  return {
@@ -702,34 +590,6 @@ class SafetyViolationDetector:
702
  self.active_violations[violation_key]['count'] += 1
703
  return None
704
 
705
- def _annotate_unauthorized_violation(self, frame, person_box, zone, person_id, zone_idx):
706
- """Enhanced unauthorized area violation annotation"""
707
- px1, py1, px2, py2 = map(int, person_box)
708
- zx1, zy1, zx2, zy2 = zone
709
-
710
- # Draw thick violation box around person
711
- cv2.rectangle(frame, (px1, py1), (px2, py2), self.colors['unauthorized'], 4)
712
-
713
- # Highlight the restricted zone
714
- cv2.rectangle(frame, (zx1, zy1), (zx2, zy2), self.colors['unauthorized'], 3)
715
-
716
- # Draw line from person to zone center
717
- person_center = ((px1 + px2) // 2, (py1 + py2) // 2)
718
- zone_center = ((zx1 + zx2) // 2, (zy1 + zy2) // 2)
719
- cv2.line(frame, person_center, zone_center, self.colors['unauthorized'], 3)
720
-
721
- # Warning label
722
- label = f"HIGH: UNAUTHORIZED AREA {zone_idx + 1} - ID:{person_id:03d}"
723
- label_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)[0]
724
-
725
- # Background for label
726
- cv2.rectangle(frame, (px1, py1 - label_size[1] - 15),
727
- (px1 + label_size[0] + 10, py1 - 5), self.colors['unauthorized'], -1)
728
-
729
- # Label text
730
- cv2.putText(frame, label, (px1 + 5, py1 - 10),
731
- cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
732
-
733
  def _check_distance_violations(self, persons, frame, current_time):
734
  violations = []
735
  if len(persons) < 2:
@@ -775,9 +635,8 @@ class SafetyViolationDetector:
775
  self.person_tracker[pid]['violations']['unsafe_distance']['count'] += 1
776
  self.person_tracker[pid]['violations']['unsafe_distance']['last_time'] = current_time
777
 
778
- # Enhanced distance violation annotation
779
- self._annotate_distance_violation(frame, persons[i]['box'], persons[j]['box'],
780
- person1_id, person2_id, dist)
781
  logger.info(f"NEW VIOLATION: Unsafe distance detected between persons {person1_id} and {person2_id}")
782
 
783
  violations.append({
@@ -795,57 +654,6 @@ class SafetyViolationDetector:
795
  self.active_violations[violation_key]['count'] += 1
796
  return violations
797
 
798
- def _annotate_distance_violation(self, frame, box1, box2, id1, id2, dist):
799
- """Enhanced distance violation annotation with precise visualization"""
800
- x1, y1, x2, y2 = map(int, box1)
801
- x3, y3, x4, y4 = map(int, box2)
802
-
803
- # Draw violation boxes around both persons
804
- cv2.rectangle(frame, (x1, y1), (x2, y2), self.colors['unsafe_distance'], 3)
805
- cv2.rectangle(frame, (x3, y3), (x4, y4), self.colors['unsafe_distance'], 3)
806
-
807
- # Calculate centers
808
- center1 = ((x1 + x2) // 2, (y1 + y2) // 2)
809
- center2 = ((x3 + x4) // 2, (y3 + y4) // 2)
810
-
811
- # Draw distance line with measurement
812
- cv2.line(frame, center1, center2, self.colors['unsafe_distance'], 4)
813
-
814
- # Draw circles at person centers
815
- cv2.circle(frame, center1, 8, self.colors['unsafe_distance'], -1)
816
- cv2.circle(frame, center2, 8, self.colors['unsafe_distance'], -1)
817
-
818
- # Distance measurement
819
- mid_point = ((center1[0] + center2[0]) // 2, (center1[1] + center2[1]) // 2)
820
- distance_text = f"{dist:.1f}px"
821
-
822
- # Background for distance text
823
- text_size = cv2.getTextSize(distance_text, cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2)[0]
824
- cv2.rectangle(frame, (mid_point[0] - text_size[0]//2 - 5, mid_point[1] - text_size[1] - 5),
825
- (mid_point[0] + text_size[0]//2 + 5, mid_point[1] + 5),
826
- self.colors['unsafe_distance'], -1)
827
-
828
- cv2.putText(frame, distance_text, (mid_point[0] - text_size[0]//2, mid_point[1]),
829
- cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
830
-
831
- # Warning labels for both persons
832
- label1 = f"MODERATE: UNSAFE DISTANCE - ID:{id1:03d}"
833
- label2 = f"MODERATE: UNSAFE DISTANCE - ID:{id2:03d}"
834
-
835
- # Label for person 1
836
- label_size1 = cv2.getTextSize(label1, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)[0]
837
- cv2.rectangle(frame, (x1, y1 - label_size1[1] - 15),
838
- (x1 + label_size1[0] + 10, y1 - 5), self.colors['unsafe_distance'], -1)
839
- cv2.putText(frame, label1, (x1 + 5, y1 - 10),
840
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
841
-
842
- # Label for person 2
843
- label_size2 = cv2.getTextSize(label2, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)[0]
844
- cv2.rectangle(frame, (x3, y3 - label_size2[1] - 15),
845
- (x3 + label_size2[0] + 10, y3 - 5), self.colors['unsafe_distance'], -1)
846
- cv2.putText(frame, label2, (x3 + 5, y3 - 10),
847
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
848
-
849
  def _cleanup_violations(self, current_time):
850
  expired_violations = [
851
  k for k, v in self.active_violations.items()
@@ -866,6 +674,25 @@ class SafetyViolationDetector:
866
  if pid in self.person_positions_history:
867
  del self.person_positions_history[pid]
868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
  def _iou(self, box1, box2):
870
  x1 = max(box1[0], box2[0])
871
  y1 = max(box1[1], box2[1])
@@ -901,37 +728,16 @@ class SafetyViolationDetector:
901
 
902
  return summary
903
 
904
- # --- Enhanced Frame Processing Functions ---
905
  def preprocess_frame(frame):
906
- """Enhanced frame preprocessing for better detection accuracy"""
907
  try:
908
- # Apply noise reduction
909
- frame = cv2.bilateralFilter(frame, 9, 75, 75)
910
-
911
- # Enhance contrast and brightness
912
- frame = cv2.convertScaleAbs(frame, alpha=1.3, beta=25)
913
-
914
- # Apply histogram equalization for better lighting
915
- lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
916
- lab[:,:,0] = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)).apply(lab[:,:,0])
917
- frame = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
918
-
919
- # Convert to RGB for YOLO
920
  img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
921
-
922
- # Resize while maintaining aspect ratio
923
- height, width = img.shape[:2]
924
- if width > 640 or height > 640:
925
- scale = min(640/width, 640/height)
926
- new_width = int(width * scale)
927
- new_height = int(height * scale)
928
- img_resized = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_LANCZOS4)
929
- else:
930
- img_resized = img
931
-
932
  return img_resized
933
  except Exception as e:
934
- logger.error(f"Enhanced frame preprocessing error: {e}")
935
  raise
936
 
937
  def capture_rtsp_frames(rtsp_url, max_frames=None):
@@ -958,18 +764,21 @@ def capture_rtsp_frames(rtsp_url, max_frames=None):
958
  finally:
959
  cv2.destroyAllWindows()
960
 
961
- # --- Enhanced Image Processing Function ---
962
  async def process_image(image_path, progress=gr.Progress()):
963
- """Enhanced image processing with precise violation detection and visualization"""
964
  try:
 
 
 
965
  current_run_violations = []
966
  new_sf_record_ids = []
967
  violation_payloads = []
968
  tracker = SafetyViolationDetector()
969
-
970
  tracker.reset_session()
971
  logger.info("Starting new image analysis session")
972
-
973
  # Get Salesforce connection
974
  sf = None
975
  if SALESFORCE_ENABLED:
@@ -977,38 +786,37 @@ async def process_image(image_path, progress=gr.Progress()):
977
  sf = get_salesforce_connection()
978
  except Exception as e:
979
  logger.error(f"Could not connect to Salesforce: {e}")
980
-
981
- progress(0.1, desc="Loading and preprocessing image...")
982
-
983
- # Load and preprocess image
984
  frame = cv2.imread(image_path)
985
  if frame is None:
986
- raise ValueError(f"Could not load image: {image_path}")
987
-
988
- original_frame = frame.copy()
 
 
 
 
989
  processed_frame = preprocess_frame(frame)
990
-
991
- progress(0.3, desc="Running AI detection...")
992
-
993
- # Run YOLO detection with enhanced parameters
994
- results = yolo_model.predict(
995
- processed_frame,
996
- conf=0.4, # Lower confidence for better detection
997
- iou=0.5, # Adjusted IoU threshold
998
- verbose=False,
999
- save=False
1000
- )
1001
-
1002
- progress(0.6, desc="Analyzing violations...")
1003
-
1004
- # Detect violations with enhanced visualization
1005
  violations = tracker.detect_violations(results, frame)
 
 
1006
  timestamp = datetime.now(IST).isoformat()
1007
-
1008
- progress(0.8, desc="Processing results...")
1009
-
1010
- # Process violations
1011
  for violation in violations:
 
1012
  snapshot_url = save_snapshot(frame, save_to_disk=False)
1013
  worker_id = f"WORKER{violation.get('person_id', 'UNKNOWN')}"
1014
  if violation['type'] == 'unsafe_distance':
@@ -1022,81 +830,158 @@ async def process_image(image_path, progress=gr.Progress()):
1022
  'site_id': 'SITE001',
1023
  'camera_id': 'CAM001',
1024
  'worker_id': worker_id,
1025
- 'frame_number': 1
1026
  }
1027
-
1028
  if violation['type'] == 'unsafe_distance':
1029
  violation_data['distance'] = f"{violation['distance']:.1f}px"
1030
-
1031
  current_run_violations.append(violation_data)
1032
  log_violation(violation_data)
1033
  send_alert(violation_data)
1034
-
 
1035
  if sf:
1036
  payload, error = create_salesforce_violation_record(sf, violation_data)
1037
  if payload:
1038
  violation_payloads.append(payload)
1039
  else:
1040
- logger.error(f"Salesforce push failed for violation: {error}")
1041
-
1042
- # Create Salesforce records
 
 
1043
  if sf and violation_payloads:
1044
  try:
1045
  results = sf.bulk.Safety_Violation_Log__c.insert(violation_payloads)
1046
  new_sf_record_ids = [result['id'] for result in results if result.get('success')]
1047
  logger.info(f"Created {len(new_sf_record_ids)} Salesforce records")
 
 
 
1048
  except Exception as e:
1049
- logger.error(f"Failed to create Salesforce records: {e}")
1050
-
1051
  progress(0.9, desc="Generating report...")
1052
-
1053
- # Generate PDF report
1054
  pdf_temp_path = None
1055
  if sf and new_sf_record_ids and current_run_violations:
1056
- pdf_temp_path, pdf_sf_url = generate_and_upload_report_to_salesforce(sf, current_run_violations, new_sf_record_ids)
1057
-
1058
- progress(1.0, desc="Analysis complete!")
1059
-
 
 
 
 
 
 
 
1060
  session_summary = tracker.get_session_summary()
1061
- logger.info(f"Image analysis complete. Session summary: {session_summary}")
1062
-
 
1063
  # Generate status message
1064
- if current_run_violations:
1065
  status_message = f"""βœ… IMAGE ANALYSIS COMPLETED
1066
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1067
  πŸ“Š RESULTS:
1068
- β€’ Image processed successfully
1069
- β€’ Enhanced detection algorithms applied
1070
- β€’ Precise violation visualization enabled
1071
-
1072
- πŸ‘₯ PERSONS DETECTED: {session_summary['total_persons']}
1073
- 🚨 UNIQUE VIOLATIONS: {len(current_run_violations)}
1074
- πŸ” VIOLATION TYPES: {', '.join(session_summary['violations_by_type'].keys()) if session_summary['violations_by_type'] else 'None'}
1075
-
1076
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1077
- Each violation precisely marked with enhanced visualization"""
1078
  else:
1079
  status_message = f"""βœ… IMAGE ANALYSIS COMPLETED
1080
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1081
  πŸ“Š RESULTS:
1082
- β€’ Image processed successfully
1083
- β€’ Enhanced detection algorithms applied
1084
- β€’ Precise violation visualization enabled
1085
-
1086
- πŸ‘₯ PERSONS DETECTED: {session_summary['total_persons']}
1087
  βœ… NO VIOLATIONS DETECTED
1088
-
1089
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1090
  All safety protocols followed"""
1091
-
1092
- return [frame], status_message, pdf_temp_path, format_violations_as_text(current_run_violations)
1093
-
 
 
 
 
 
1094
  except Exception as e:
1095
- logger.error(f"Enhanced image processing error: {e}")
1096
  error_message = f"Image processing failed: {str(e)}"
1097
  return None, error_message, None, format_violations_as_text([])
1098
 
1099
- # --- Enhanced Video Processing Functions ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1100
  async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1101
  global processing_active
1102
  processing_active = True
@@ -1109,7 +994,7 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1109
  tracker = SafetyViolationDetector()
1110
 
1111
  tracker.reset_session()
1112
- logger.info("Starting new enhanced video analysis session")
1113
 
1114
  cap = cv2.VideoCapture(video_path)
1115
  if not cap.isOpened():
@@ -1118,7 +1003,7 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1118
  return None, error_message, None, format_violations_as_text([])
1119
 
1120
  frames = []
1121
- max_display_frames = 15 # Increased for better visualization
1122
  frame_count = 0
1123
  processed_frames = 0
1124
  violation_count = 0
@@ -1135,7 +1020,7 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1135
  total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
1136
  duration = total_frames / fps if fps > 0 else 0
1137
 
1138
- progress(0, desc="Analyzing video with enhanced detection...")
1139
 
1140
  while cap.isOpened() and processing_active:
1141
  ret, frame = cap.read()
@@ -1150,19 +1035,10 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1150
  timestamp = datetime.now(IST).isoformat()
1151
 
1152
  progress_percent = min(100, (frame_count / total_frames) * 100)
1153
- progress(progress_percent / 100, desc=f"Enhanced processing frame {frame_count}/{total_frames}")
1154
 
1155
- # Enhanced preprocessing
1156
  processed_frame = preprocess_frame(frame)
1157
-
1158
- # Enhanced YOLO detection
1159
- results = yolo_model.predict(
1160
- processed_frame,
1161
- conf=0.4, # Lower confidence for better detection
1162
- iou=0.5, # Adjusted IoU threshold
1163
- verbose=False,
1164
- save=False
1165
- )
1166
 
1167
  violations = tracker.detect_violations(results, frame)
1168
 
@@ -1229,13 +1105,16 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1229
  pdf_temp_path, pdf_sf_url = generate_and_upload_report_to_salesforce(sf, current_run_violations, new_sf_record_ids)
1230
  if not pdf_temp_path:
1231
  logger.error("Failed to generate and upload Salesforce report.")
 
 
 
1232
  elif not current_run_violations:
1233
  logger.info("No violations detected, skipping report generation.")
1234
  else:
1235
  logger.warning("Salesforce not configured or no violations recorded. Skipping Salesforce report upload.")
1236
 
1237
  session_summary = tracker.get_session_summary()
1238
- logger.info(f"Enhanced video analysis complete. Session summary: {session_summary}")
1239
  logger.info(f"Total processing time: {processing_time:.2f}s")
1240
 
1241
  status_message = generate_status_message(
@@ -1251,7 +1130,7 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1251
 
1252
  return frames, status_message, pdf_temp_path, format_violations_as_text(current_run_violations)
1253
  except Exception as e:
1254
- logger.error(f"Enhanced video processing error: {e}", exc_info=True)
1255
  error_message = f"Video processing failed: {str(e)}"
1256
  return None, error_message, None, format_violations_as_text([])
1257
  finally:
@@ -1259,31 +1138,6 @@ async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
1259
  cv2.destroyAllWindows()
1260
  logger.info(f"Total processing time: {time.time() - start_total:.2f}s")
1261
 
1262
- # --- Smart Media Processing Function ---
1263
- async def process_media(media_file, frame_skip=5, progress=gr.Progress()):
1264
- """Smart media processor that handles both images and videos with enhanced detection"""
1265
- if media_file is None:
1266
- return None, "No file uploaded", None, format_violations_as_text([])
1267
-
1268
- file_path = media_file.name
1269
- file_extension = os.path.splitext(file_path)[1].lower()
1270
-
1271
- # Image file extensions
1272
- image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp']
1273
- # Video file extensions
1274
- video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm']
1275
-
1276
- if file_extension in image_extensions:
1277
- logger.info(f"Processing image file: {file_path}")
1278
- return await process_image(file_path, progress)
1279
- elif file_extension in video_extensions:
1280
- logger.info(f"Processing video file: {file_path}")
1281
- return await process_video(file_path, frame_skip, progress)
1282
- else:
1283
- error_msg = f"Unsupported file format: {file_extension}. Supported formats: {image_extensions + video_extensions}"
1284
- logger.error(error_msg)
1285
- return None, error_msg, None, format_violations_as_text([])
1286
-
1287
  # --- RTSP Processing ---
1288
  async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=gr.Progress()):
1289
  global processing_active
@@ -1300,7 +1154,7 @@ async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=
1300
  tracker = SafetyViolationDetector()
1301
 
1302
  tracker.reset_session()
1303
- logger.info("Starting new enhanced RTSP stream analysis session")
1304
 
1305
  # Get Salesforce connection once at the beginning
1306
  sf = None
@@ -1311,7 +1165,7 @@ async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=
1311
  logger.error(f"Could not connect to Salesforce at start: {e}")
1312
 
1313
  frames = []
1314
- max_display_frames = 15 # Increased for better visualization
1315
  violation_count = 0
1316
 
1317
  progress(0, desc="Connecting to RTSP stream...")
@@ -1324,19 +1178,10 @@ async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=
1324
  continue
1325
 
1326
  progress_percent = min(100, (fc / (max_frames if max_frames else 100)) * 100)
1327
- progress(progress_percent / 100, desc=f"Enhanced processing frame {fc}")
1328
 
1329
- # Enhanced preprocessing
1330
  processed_frame = preprocess_frame(frame)
1331
-
1332
- # Enhanced YOLO detection
1333
- results = yolo_model.predict(
1334
- processed_frame,
1335
- conf=0.4, # Lower confidence for better detection
1336
- iou=0.5, # Adjusted IoU threshold
1337
- verbose=False,
1338
- save=False
1339
- )
1340
 
1341
  violations = tracker.detect_violations(results, frame)
1342
 
@@ -1398,6 +1243,9 @@ async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=
1398
  pdf_temp_path, pdf_sf_url = generate_and_upload_report_to_salesforce(sf, current_run_violations, new_sf_record_ids)
1399
  if not pdf_temp_path:
1400
  logger.error("Failed to generate and upload Salesforce report.")
 
 
 
1401
  elif not current_run_violations:
1402
  logger.info("No violations detected, skipping report generation.")
1403
  else:
@@ -1407,14 +1255,14 @@ async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=
1407
  return "Processing cancelled.", frames, format_violations_as_text(current_run_violations), generate_heatmap(current_run_violations, generate=False), pdf_temp_path
1408
 
1409
  session_summary = tracker.get_session_summary()
1410
- logger.info(f"Enhanced RTSP analysis complete. Session summary: {session_summary}")
1411
  logger.info(f"Total processing time: {time.time() - start_total:.2f}s")
1412
 
1413
- status_message = f"Enhanced processing completed: {len(frames)} frames with {violation_count} unique violations. Persons tracked: {session_summary['total_persons']}"
1414
 
1415
  return status_message, frames, format_violations_as_text(current_run_violations), generate_heatmap(current_run_violations, generate=False), pdf_temp_path
1416
  except Exception as e:
1417
- logger.error(f"Enhanced RTSP processing error: {e}", exc_info=True)
1418
  error_message = f"RTSP processing failed: {str(e)}"
1419
  return error_message, None, format_violations_as_text([]), None, None
1420
  finally:
@@ -1425,15 +1273,13 @@ async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=
1425
  # --- Other Functions ---
1426
  def generate_status_message(has_violations, total_frames, processed_frames, duration,
1427
  violation_count, processing_time, actual_fps, session_summary=None):
1428
- base_message = f"""βœ… ENHANCED ANALYSIS COMPLETED
1429
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1430
  πŸ“Š RESULTS:
1431
  β€’ Frames: {total_frames} (Processed: {processed_frames})
1432
  β€’ Duration: {duration:.2f}s
1433
  β€’ Processing Time: {processing_time:.2f}s
1434
- β€’ FPS: {actual_fps:.1f}
1435
- β€’ Enhanced detection algorithms applied
1436
- β€’ Precise violation visualization enabled"""
1437
 
1438
  if session_summary:
1439
  base_message += f"""
@@ -1444,7 +1290,7 @@ def generate_status_message(has_violations, total_frames, processed_frames, dura
1444
  return f"""{base_message}
1445
  🚨 UNIQUE VIOLATIONS: {violation_count}
1446
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1447
- Each violation precisely marked with enhanced visualization"""
1448
  else:
1449
  return f"""{base_message}
1450
  βœ… NO VIOLATIONS DETECTED
@@ -1486,34 +1332,32 @@ def send_alert(violation):
1486
 
1487
  def format_violations_as_text(violations):
1488
  if not violations:
1489
- return """πŸ” ENHANCED SAFETY MONITORING STATUS
1490
 
1491
  βœ… NO VIOLATIONS DETECTED
1492
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1493
 
1494
  πŸ“Š Current Status: ALL CLEAR
1495
  πŸ• Last Updated: """ + datetime.now(IST).strftime('%Y-%m-%d %H:%M:%S IST') + """
1496
- 🎯 Detection Accuracy: >95% confidence (Enhanced)
1497
- ⚑ Response Time: <3 seconds
1498
- πŸ” Visualization: Precise violation marking enabled
1499
 
1500
- The enhanced system is actively monitoring for:
1501
- β€’ No Helmet violations (Head region focus)
1502
- β€’ Unsafe Distance violations (Precise measurement)
1503
- β€’ Unauthorized Area violations (Zone highlighting)
1504
 
1505
  All safety protocols are currently being followed."""
1506
 
1507
- text = f"""🚨 ENHANCED SAFETY VIOLATION ALERTS
1508
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1509
 
1510
  πŸ“Š UNIQUE VIOLATIONS DETECTED: {len(violations)}
1511
- πŸ” Enhanced Detection: Precise violation visualization enabled
1512
  Note: Each violation type reported only once per person
1513
 
1514
  """
1515
  for i, violation in enumerate(violations, 1):
1516
- severity_emoji = "πŸ”΄" if violation['severity'] == 'Critical' else "🟑" if violation['severity'] == 'High' else "🟠"
1517
  text += f"""
1518
  β”Œβ”€ ALERT #{i:02d} ─ {severity_emoji} {violation['violation_type'].upper()}
1519
  β”‚
@@ -1522,24 +1366,21 @@ Note: Each violation type reported only once per person
1522
  β”œβ”€ πŸ“ Location: Site {violation['site_id']} | Camera {violation['camera_id']}
1523
  β”œβ”€ πŸ‘· Worker: {violation.get('worker_id', 'UNKNOWN')}
1524
  β”œβ”€ πŸ“Έ Evidence: {violation['snapshot_url']}
1525
- β”œβ”€ 🎯 Enhanced: Precise violation marking applied
1526
  β”‚
1527
  └─────────────────────────────────────────────��───\n"""
1528
 
1529
  text += f"""
1530
 
1531
- πŸ“ˆ ENHANCED SUMMARY STATISTICS:
1532
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1533
  β€’ Total Violations: {len(violations)}
1534
  β€’ Critical: {sum(1 for v in violations if v['severity'] == 'Critical')}
1535
- β€’ High: {sum(1 for v in violations if v['severity'] == 'High')}
1536
  β€’ Moderate: {sum(1 for v in violations if v['severity'] == 'Moderate')}
1537
  β€’ Last Alert: {violations[-1]['timestamp'] if violations else 'N/A'}
1538
 
1539
- πŸ”„ System Status: ENHANCED MONITORING ACTIVE
1540
- ⚑ Response Time: <3 seconds
1541
- 🎯 Detection Accuracy: >95% confidence
1542
- πŸ” Visualization: Precise violation marking enabled"""
1543
  return text
1544
 
1545
  def generate_heatmap(violations, generate=True):
@@ -1552,7 +1393,7 @@ def generate_heatmap(violations, generate=True):
1552
 
1553
  plt.figure(figsize=(12, 8))
1554
  sns.heatmap(heatmap_data, cmap='YlOrRd', annot=True, fmt='d')
1555
- plt.title("Enhanced Violation Analysis - Temporal Distribution")
1556
  plt.xlabel("Violation Type")
1557
  plt.ylabel("Hour of Day")
1558
 
@@ -1962,9 +1803,9 @@ enhanced_custom_css = """
1962
  }
1963
  """
1964
 
1965
- # --- Enhanced Gradio Interface ---
1966
  with gr.Blocks(
1967
- title="Enhanced Safety Violation Detection using CCTV + AI",
1968
  css=enhanced_custom_css,
1969
  theme=gr.themes.Soft(
1970
  primary_hue="blue",
@@ -2071,18 +1912,18 @@ with gr.Blocks(
2071
  # Professional Header
2072
  gr.HTML("""
2073
  <div class="main-header">
2074
- <h1 class="header-title">πŸ” Enhanced Safety Violation Detection using CCTV + AI</h1>
2075
- <p class="header-subtitle">Advanced Multi-Person Tracking with Precise Violation Visualization - Enhanced Detection Accuracy >95%</p>
2076
  </div>
2077
  """)
2078
 
2079
  # Smart Media Analysis Section
2080
- gr.HTML('<div class="section-header">πŸ“· Enhanced Smart Media Analysis</div>')
2081
  with gr.Row():
2082
  with gr.Column(scale=1):
2083
  with gr.Group(elem_classes=["professional-card"]):
2084
  media_input = gr.File(
2085
- label="πŸ“€ Upload Image or Video for Enhanced Safety Analysis",
2086
  file_types=["image", "video"],
2087
  elem_classes=["image-component"],
2088
  height=200
@@ -2092,55 +1933,55 @@ with gr.Blocks(
2092
  maximum=10,
2093
  step=1,
2094
  value=5,
2095
- label="🎯 Frame Skip (Higher = Faster Processing, Lower = More Accurate)"
2096
  )
2097
  with gr.Row():
2098
  media_button = gr.Button(
2099
- "πŸ” Analyze with Enhanced Detection",
2100
  variant="primary",
2101
  elem_classes=["btn-primary"],
2102
  size="lg"
2103
  )
2104
 
2105
  # Analysis Results Section
2106
- gr.HTML('<div class="section-header">πŸ“Š Enhanced Analysis Results & Precise Violation Visualization</div>')
2107
  with gr.Row():
2108
  with gr.Column(scale=1):
2109
  with gr.Group(elem_classes=["professional-card"]):
2110
  media_output = gr.Gallery(
2111
- label="πŸ–ΌοΈ Enhanced Media with Precise Violation Detection",
2112
  elem_classes=["gallery-component"],
2113
- height=280
2114
  )
2115
  with gr.Column(scale=1):
2116
  with gr.Group(elem_classes=["professional-card"]):
2117
  media_status = gr.Textbox(
2118
- label="πŸ“‹ Enhanced Analysis Status",
2119
  elem_classes=["status-display"],
2120
- lines=8,
2121
- max_lines=10,
2122
- value="πŸ“Š Enhanced Detection System Ready\n\nUpload an image/video and click 'Analyze with Enhanced Detection' to begin advanced safety violation detection with precise visualization.",
2123
  interactive=False
2124
  )
2125
  pdf_output = gr.File(
2126
- label="πŸ“₯ Download Enhanced Professional Report",
2127
  elem_classes=["file-component"]
2128
  )
2129
 
2130
  # Violation Details Section
2131
- gr.HTML('<div class="section-header">🚨 Real-time Enhanced Violation Monitoring</div>')
2132
  with gr.Group(elem_classes=["professional-card", "alert-panel"]):
2133
  violation_log = gr.Textbox(
2134
- label="🚨 Real-time Enhanced Violation Details",
2135
  elem_classes=["status-display"],
2136
- lines=12,
2137
- max_lines=15,
2138
  value=format_violations_as_text(recent_violations),
2139
  interactive=False
2140
  )
2141
 
2142
  # Live Stream Processing Section
2143
- gr.HTML('<div class="section-header">πŸ“Ή Enhanced Live Stream Monitoring</div>')
2144
  with gr.Row():
2145
  with gr.Column(scale=2):
2146
  with gr.Group(elem_classes=["professional-card"]):
@@ -2150,16 +1991,9 @@ with gr.Blocks(
2150
  value=RTSP_URL_DEFAULT,
2151
  interactive=True
2152
  )
2153
- rtsp_frame_skip = gr.Slider(
2154
- minimum=1,
2155
- maximum=10,
2156
- step=1,
2157
- value=5,
2158
- label="🎯 RTSP Frame Skip (Higher = Faster, Lower = More Accurate)"
2159
- )
2160
  with gr.Row():
2161
  rtsp_button = gr.Button(
2162
- "πŸ“‘ Start Enhanced Live Monitoring",
2163
  variant="primary",
2164
  elem_classes=["btn-primary"],
2165
  size="lg"
@@ -2171,73 +2005,73 @@ with gr.Blocks(
2171
  size="lg"
2172
  )
2173
  rtsp_status = gr.Textbox(
2174
- label="πŸ“Ί Enhanced Live Stream Processing Status",
2175
  elem_classes=["status-display"],
2176
  lines=6,
2177
  max_lines=8,
2178
- value="πŸ“Ί Enhanced RTSP Stream Processor Ready\n\nEnter an RTSP URL and click 'Start Enhanced Live Monitoring' to begin real-time monitoring with precise violation visualization.",
2179
  interactive=False
2180
  )
2181
  with gr.Column(scale=3):
2182
  with gr.Group(elem_classes=["professional-card"]):
2183
  rtsp_output = gr.Gallery(
2184
- label="🎬 Enhanced Live Stream Frames & Precise Detection Results",
2185
  elem_classes=["gallery-component"],
2186
- height=380,
2187
  columns=3,
2188
  rows=2,
2189
  object_fit="cover"
2190
  )
2191
 
2192
  # Live Violation Log Section
2193
- gr.HTML('<div class="section-header">πŸ“Š Enhanced Live Violation Analytics</div>')
2194
  with gr.Row():
2195
  with gr.Column(scale=1):
2196
  with gr.Group(elem_classes=["professional-card", "alert-panel"]):
2197
  rtsp_violation_log = gr.Textbox(
2198
- label="🚨 Enhanced Live Violation Log",
2199
  elem_classes=["status-display"],
2200
- lines=10,
2201
- max_lines=12,
2202
  interactive=False
2203
  )
2204
  with gr.Column(scale=1):
2205
  with gr.Group(elem_classes=["professional-card", "analytics-panel"]):
2206
  heatmap_output = gr.Image(
2207
- label="πŸ”₯ Enhanced Violation Heatmap - Temporal Analysis",
2208
  elem_classes=["image-component"],
2209
- height=340
2210
  )
2211
  rtsp_pdf_output = gr.File(
2212
- label="πŸ“₯ Download Enhanced RTSP Report",
2213
  elem_classes=["file-component"]
2214
  )
2215
 
2216
  # Professional Footer
2217
  gr.HTML("""
2218
  <div class="footer-info">
2219
- <h3>πŸ›‘οΈ Enhanced Safety Violation Detection using CCTV + AI</h3>
2220
  <div class="feature-grid">
2221
  <div class="feature-item">
2222
- <strong>🎯 Enhanced Detection</strong><br>
2223
- Advanced YOLOv8 AI with >95% accuracy
2224
  </div>
2225
  <div class="feature-item">
2226
  <strong>⚑ Ultra-fast Response</strong><br>
2227
- Alert generation in <3 seconds
2228
  </div>
2229
  <div class="feature-item">
2230
- <strong>πŸ” Precise Visualization</strong><br>
2231
- Exact violation location marking
2232
  </div>
2233
  <div class="feature-item">
2234
  <strong>πŸ“± Responsive Design</strong><br>
2235
- Optimized for all devices
2236
  </div>
2237
  </div>
2238
  <div style="margin-top: 0.8rem; padding-top: 0.8rem; border-top: 0.5px solid rgba(255,255,255,0.2);">
2239
  <p style="margin: 0; font-size: 0.8rem; opacity: 0.7;">
2240
- Enhanced Safety Violation Detection using CCTV + AI Β© 2025 - Precise Violation Visualization Enabled
2241
  </p>
2242
  </div>
2243
  </div>
@@ -2252,7 +2086,7 @@ with gr.Blocks(
2252
 
2253
  rtsp_button.click(
2254
  fn=process_rtsp_stream,
2255
- inputs=[rtsp_url_input, gr.Number(value=None, visible=False), rtsp_frame_skip],
2256
  outputs=[rtsp_status, rtsp_output, rtsp_violation_log, heatmap_output, rtsp_pdf_output]
2257
  )
2258
  rtsp_cancel_btn.click(cancel_processing, outputs=[rtsp_status])
 
95
  try:
96
  logger.info("Initializing YOLOv8 model...")
97
  yolo_model = YOLO(MODEL_PATH)
 
 
98
  logger.info("YOLOv8 model loaded successfully")
99
  return True
100
  except Exception as e:
 
313
  logger.error(f"Error in Salesforce PDF report generation/upload: {e}", exc_info=True)
314
  return None, None
315
 
316
+ # --- Enhanced Safety Violation Detector Class with Group Detection ---
317
  class SafetyViolationDetector:
318
  def __init__(self):
319
+ # Detection thresholds (fine-tuned for better accuracy)
320
+ self.helmet_threshold = 0.75
321
+ self.person_threshold = 0.60
322
+ self.unsafe_distance = 50 # pixels
323
+ self.violation_cooldown = 20 # seconds
324
 
325
  # Unauthorized zones (x1, y1, x2, y2)
326
  self.unauthorized_zones = [
 
333
  self.person_tracker = {}
334
  self.person_positions_history = {}
335
  self.next_person_id = 1
336
+ self.max_tracking_distance = 120
337
 
338
  self.session_violations = {}
339
 
 
 
 
 
 
 
 
 
 
 
340
  def reset_session(self):
341
  self.session_violations = {}
342
  self.active_violations = {}
343
  self.person_tracker = {}
344
  self.person_positions_history = {}
345
  self.next_person_id = 1
346
+ logger.info("Session violation tracking reset for new video")
347
 
348
  def has_reported_violation(self, person_id, violation_type):
349
  if person_id not in self.session_violations:
 
418
  persons = []
419
  helmets = []
420
 
 
421
  for box, conf, cls_id in zip(boxes, confidences, class_ids):
422
  class_name = class_names[cls_id]
423
  if class_name == "person" and conf >= self.person_threshold:
 
428
  'center': ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2),
429
  'id': person_id
430
  })
431
+ elif class_name == "hard hat" and conf >= self.helmet_threshold:
 
 
 
432
  helmets.append({
433
  'box': box,
434
  'confidence': conf,
435
  'area': (box[2] - box[0]) * (box[3] - box[1])
436
  })
 
 
437
 
438
  current_person_ids = set()
439
  for person in persons:
 
454
  if len(self.person_tracker[person_id]['positions']) > 10:
455
  self.person_tracker[person_id]['positions'].pop(0)
456
 
 
457
  for person in persons:
458
  person_id = person['id']
459
 
 
471
  self._cleanup_violations(current_time)
472
  self._cleanup_inactive_persons(current_person_ids, current_time)
473
 
474
+ logger.info(f"Violation detection time: {time.time() - start_time:.2f}s")
475
  return violations
476
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  def _check_helmet_violation(self, person, helmets, frame, current_time):
478
  person_id = person['id']
479
  person_box = person['box']
 
482
  if self.has_reported_violation(person_id, violation_type):
483
  return None
484
 
 
 
485
  head_region = [
486
+ person_box[0],
487
+ max(person_box[1], person_box[1] + (person_box[3] - person_box[1]) * 0.3),
488
+ person_box[2],
489
+ person_box[1] + (person_box[3] - person_box[1]) * 0.3
490
  ]
491
 
 
 
 
 
 
 
492
  has_helmet = False
 
 
493
  for helmet in helmets:
494
+ if self._iou(helmet['box'], head_region) > 0.1:
 
495
  has_helmet = True
 
496
  break
497
 
498
  self.person_tracker[person_id]['helmet_status'] = has_helmet
 
521
  self.person_tracker[person_id]['violations']['no_helmet']['count'] += 1
522
  self.person_tracker[person_id]['violations']['no_helmet']['last_time'] = current_time
523
 
524
+ self._annotate_frame(frame, person_box, person_id, "NO HELMET", (0, 0, 255))
 
525
  logger.info(f"NEW VIOLATION: No helmet detected for person {person_id}")
526
 
527
  return {
 
534
  else:
535
  self.active_violations[violation_key]['last_detected'] = current_time
536
  self.active_violations[violation_key]['count'] += 1
 
 
 
 
 
 
537
  return None
538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  def _check_unauthorized_area(self, person, frame, current_time):
540
  person_id = person['id']
541
  violation_type = 'unauthorized_area'
 
546
  x1, y1, x2, y2 = person['box']
547
  person_center = ((x1 + x2) / 2, (y1 + y2) / 2)
548
 
549
+ for zone in self.unauthorized_zones:
550
  zx1, zy1, zx2, zy2 = zone
 
 
 
 
 
 
551
  if (zx1 <= person_center[0] <= zx2 and zy1 <= person_center[1] <= zy2):
552
+ violation_key = f"unauthorized_area_{person_id}_{zx1}_{zy1}"
553
 
554
  if (violation_key not in self.active_violations or
555
  current_time - self.active_violations[violation_key]['last_detected'] > self.violation_cooldown):
 
573
  self.person_tracker[person_id]['violations']['unauthorized_area']['count'] += 1
574
  self.person_tracker[person_id]['violations']['unauthorized_area']['last_time'] = current_time
575
 
576
+ cv2.rectangle(frame, (zx1, zy1), (zx2, zy2), (255, 0, 255), 2)
577
+ self._annotate_frame(frame, person['box'], person_id, "UNAUTHORIZED", (255, 0, 255))
578
  logger.info(f"NEW VIOLATION: Unauthorized area detected for person {person_id}")
579
 
580
  return {
 
590
  self.active_violations[violation_key]['count'] += 1
591
  return None
592
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  def _check_distance_violations(self, persons, frame, current_time):
594
  violations = []
595
  if len(persons) < 2:
 
635
  self.person_tracker[pid]['violations']['unsafe_distance']['count'] += 1
636
  self.person_tracker[pid]['violations']['unsafe_distance']['last_time'] = current_time
637
 
638
+ self._annotate_distance(frame, persons[i]['box'], persons[j]['box'],
639
+ person1_id, person2_id, dist)
 
640
  logger.info(f"NEW VIOLATION: Unsafe distance detected between persons {person1_id} and {person2_id}")
641
 
642
  violations.append({
 
654
  self.active_violations[violation_key]['count'] += 1
655
  return violations
656
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
  def _cleanup_violations(self, current_time):
658
  expired_violations = [
659
  k for k, v in self.active_violations.items()
 
674
  if pid in self.person_positions_history:
675
  del self.person_positions_history[pid]
676
 
677
+ def _annotate_frame(self, frame, box, person_id, violation_type, color):
678
+ x1, y1, x2, y2 = map(int, box)
679
+ cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
680
+ label = f"ID:{person_id:03d} {violation_type}"
681
+ cv2.putText(frame, label, (x1, y1 - 10),
682
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
683
+
684
+ def _annotate_distance(self, frame, box1, box2, id1, id2, dist):
685
+ x1, y1, x2, y2 = map(int, box1)
686
+ x3, y3, x4, y4 = map(int, box2)
687
+ cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 165, 255), 2)
688
+ cv2.rectangle(frame, (x3, y3), (x4, y4), (0, 165, 255), 2)
689
+ center1 = ((x1 + x2) // 2, (y1 + y2) // 2)
690
+ center2 = ((x3 + x4) // 2, (y3 + y4) // 2)
691
+ cv2.line(frame, center1, center2, (0, 165, 255), 2)
692
+ mid_point = ((center1[0] + center2[0]) // 2, (center1[1] + center2[1]) // 2)
693
+ cv2.putText(frame, f"{dist:.1f}px", mid_point,
694
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 165, 255), 2)
695
+
696
  def _iou(self, box1, box2):
697
  x1 = max(box1[0], box2[0])
698
  y1 = max(box1[1], box2[1])
 
728
 
729
  return summary
730
 
731
+ # --- Frame Processing Functions ---
732
  def preprocess_frame(frame):
 
733
  try:
734
+ # Enhance image for better detection
735
+ frame = cv2.convertScaleAbs(frame, alpha=1.2, beta=20) # Increase contrast
 
 
 
 
 
 
 
 
 
 
736
  img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
737
+ img_resized = cv2.resize(img, (320, 320)) # Reduced resolution
 
 
 
 
 
 
 
 
 
 
738
  return img_resized
739
  except Exception as e:
740
+ logger.error(f"Frame preprocessing error: {e}")
741
  raise
742
 
743
  def capture_rtsp_frames(rtsp_url, max_frames=None):
 
764
  finally:
765
  cv2.destroyAllWindows()
766
 
767
+ # --- Image Processing Function ---
768
  async def process_image(image_path, progress=gr.Progress()):
769
+ """Process a single image for safety violations"""
770
  try:
771
+ logger.info(f"Starting image analysis: {image_path}")
772
+ start_time = time.time()
773
+
774
  current_run_violations = []
775
  new_sf_record_ids = []
776
  violation_payloads = []
777
  tracker = SafetyViolationDetector()
778
+
779
  tracker.reset_session()
780
  logger.info("Starting new image analysis session")
781
+
782
  # Get Salesforce connection
783
  sf = None
784
  if SALESFORCE_ENABLED:
 
786
  sf = get_salesforce_connection()
787
  except Exception as e:
788
  logger.error(f"Could not connect to Salesforce: {e}")
789
+
790
+ progress(0.1, desc="Loading image...")
791
+
792
+ # Load image
793
  frame = cv2.imread(image_path)
794
  if frame is None:
795
+ error_msg = f"Failed to load image: {image_path}"
796
+ logger.error(error_msg)
797
+ return None, error_msg, None, format_violations_as_text([])
798
+
799
+ progress(0.3, desc="Preprocessing image...")
800
+
801
+ # Preprocess image
802
  processed_frame = preprocess_frame(frame)
803
+
804
+ progress(0.5, desc="Running AI detection...")
805
+
806
+ # Run YOLO detection
807
+ results = yolo_model.predict(processed_frame)
808
+
809
+ progress(0.7, desc="Analyzing violations...")
810
+
811
+ # Detect violations
 
 
 
 
 
 
812
  violations = tracker.detect_violations(results, frame)
813
+
814
+ violation_count = 0
815
  timestamp = datetime.now(IST).isoformat()
816
+
817
+ # Process each violation
 
 
818
  for violation in violations:
819
+ violation_count += 1
820
  snapshot_url = save_snapshot(frame, save_to_disk=False)
821
  worker_id = f"WORKER{violation.get('person_id', 'UNKNOWN')}"
822
  if violation['type'] == 'unsafe_distance':
 
830
  'site_id': 'SITE001',
831
  'camera_id': 'CAM001',
832
  'worker_id': worker_id,
833
+ 'frame_number': 1 # Single image
834
  }
835
+
836
  if violation['type'] == 'unsafe_distance':
837
  violation_data['distance'] = f"{violation['distance']:.1f}px"
838
+
839
  current_run_violations.append(violation_data)
840
  log_violation(violation_data)
841
  send_alert(violation_data)
842
+
843
+ # Prepare Salesforce record
844
  if sf:
845
  payload, error = create_salesforce_violation_record(sf, violation_data)
846
  if payload:
847
  violation_payloads.append(payload)
848
  else:
849
+ logger.error(f"Salesforce payload creation failed: {error}")
850
+
851
+ progress(0.8, desc="Creating Salesforce records...")
852
+
853
+ # Create Salesforce records in bulk
854
  if sf and violation_payloads:
855
  try:
856
  results = sf.bulk.Safety_Violation_Log__c.insert(violation_payloads)
857
  new_sf_record_ids = [result['id'] for result in results if result.get('success')]
858
  logger.info(f"Created {len(new_sf_record_ids)} Salesforce records")
859
+ for result in results:
860
+ if not result.get('success'):
861
+ logger.error(f"Failed to create record: {result.get('errors')}")
862
  except Exception as e:
863
+ logger.error(f"Failed to create bulk Salesforce records: {e}")
864
+
865
  progress(0.9, desc="Generating report...")
866
+
867
+ # Generate PDF report if violations found
868
  pdf_temp_path = None
869
  if sf and new_sf_record_ids and current_run_violations:
870
+ logger.info("Generating and uploading PDF report to Salesforce...")
871
+ pdf_temp_path, pdf_sf_url = generate_and_upload_report_to_salesforce(
872
+ sf, current_run_violations, new_sf_record_ids
873
+ )
874
+ if not pdf_temp_path:
875
+ logger.error("Failed to generate Salesforce report")
876
+ elif current_run_violations and not sf:
877
+ # Generate local PDF if no Salesforce
878
+ pdf_temp_path = generate_local_pdf_report(current_run_violations)
879
+
880
+ processing_time = time.time() - start_time
881
  session_summary = tracker.get_session_summary()
882
+
883
+ progress(1.0, desc="Analysis complete!")
884
+
885
  # Generate status message
886
+ if violation_count > 0:
887
  status_message = f"""βœ… IMAGE ANALYSIS COMPLETED
888
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
889
  πŸ“Š RESULTS:
890
+ β€’ Processing Time: {processing_time:.2f}s
891
+ β€’ Image: {os.path.basename(image_path)}
892
+ πŸ‘₯ UNIQUE PERSONS TRACKED: {session_summary['total_persons']}
893
+ πŸ” VIOLATION TYPES: {', '.join(session_summary['violations_by_type'].keys())}
894
+ 🚨 UNIQUE VIOLATIONS: {violation_count}
 
 
 
895
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
896
+ Each violation reported only once per person"""
897
  else:
898
  status_message = f"""βœ… IMAGE ANALYSIS COMPLETED
899
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
900
  πŸ“Š RESULTS:
901
+ β€’ Processing Time: {processing_time:.2f}s
902
+ β€’ Image: {os.path.basename(image_path)}
903
+ πŸ‘₯ UNIQUE PERSONS TRACKED: {session_summary['total_persons']}
 
 
904
  βœ… NO VIOLATIONS DETECTED
 
905
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
906
  All safety protocols followed"""
907
+
908
+ logger.info(f"Image analysis complete. Processing time: {processing_time:.2f}s")
909
+
910
+ # Return annotated frame if violations found
911
+ output_frames = [frame] if violations else None
912
+
913
+ return output_frames, status_message, pdf_temp_path, format_violations_as_text(current_run_violations)
914
+
915
  except Exception as e:
916
+ logger.error(f"Image processing error: {e}", exc_info=True)
917
  error_message = f"Image processing failed: {str(e)}"
918
  return None, error_message, None, format_violations_as_text([])
919
 
920
+ def generate_local_pdf_report(violations):
921
+ """Generate a local PDF report when Salesforce is not available"""
922
+ try:
923
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf', prefix='safety_report_')
924
+ c = canvas.Canvas(temp_file.name, pagesize=letter)
925
+ c.setFont("Helvetica-Bold", 16)
926
+ c.drawString(100, 750, "Safety Violation Report")
927
+ c.setFont("Helvetica", 12)
928
+ c.drawString(100, 730, f"Generated: {datetime.now(IST).strftime('%Y-%m-%d %H:%M:%S IST')}")
929
+ c.setFont("Helvetica", 10)
930
+ c.drawString(100, 710, "Note: Each violation type reported only once per person")
931
+
932
+ y = 680
933
+ for i, violation in enumerate(violations, 1):
934
+ c.setFont("Helvetica-Bold", 12)
935
+ c.drawString(100, y, f"Violation #{i}: {violation['violation_type']}")
936
+ y -= 20
937
+ c.setFont("Helvetica", 10)
938
+ c.drawString(120, y, f"Severity: {violation['severity']}")
939
+ y -= 15
940
+ c.drawString(120, y, f"Time: {violation['timestamp']}")
941
+ y -= 15
942
+ c.drawString(120, y, f"Worker: {violation.get('worker_id', 'UNKNOWN')}")
943
+ y -= 15
944
+ if 'distance' in violation:
945
+ c.drawString(120, y, f"Distance: {violation['distance']}")
946
+ y -= 15
947
+ y -= 20
948
+ if y < 50:
949
+ c.showPage()
950
+ y = 750
951
+
952
+ c.save()
953
+ temp_file.close()
954
+ return temp_file.name
955
+ except Exception as e:
956
+ logger.error(f"Local PDF generation error: {e}")
957
+ return None
958
+
959
+ # --- Media Processing Handler ---
960
+ async def process_media(media_file, frame_skip=5, progress=gr.Progress()):
961
+ """Handle both image and video processing"""
962
+ if media_file is None:
963
+ return None, "No file uploaded", None, format_violations_as_text([])
964
+
965
+ file_path = media_file.name
966
+ file_extension = os.path.splitext(file_path)[1].lower()
967
+
968
+ # Image extensions
969
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.webp'}
970
+ # Video extensions
971
+ video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'}
972
+
973
+ if file_extension in image_extensions:
974
+ logger.info(f"Processing image: {file_path}")
975
+ return await process_image(file_path, progress)
976
+ elif file_extension in video_extensions:
977
+ logger.info(f"Processing video: {file_path}")
978
+ return await process_video(file_path, frame_skip, progress)
979
+ else:
980
+ error_msg = f"Unsupported file format: {file_extension}. Please upload an image or video file."
981
+ logger.error(error_msg)
982
+ return None, error_msg, None, format_violations_as_text([])
983
+
984
+ # --- Video Processing Functions ---
985
  async def process_video(video_path, frame_skip=5, progress=gr.Progress()):
986
  global processing_active
987
  processing_active = True
 
994
  tracker = SafetyViolationDetector()
995
 
996
  tracker.reset_session()
997
+ logger.info("Starting new video analysis session")
998
 
999
  cap = cv2.VideoCapture(video_path)
1000
  if not cap.isOpened():
 
1003
  return None, error_message, None, format_violations_as_text([])
1004
 
1005
  frames = []
1006
+ max_display_frames = 10
1007
  frame_count = 0
1008
  processed_frames = 0
1009
  violation_count = 0
 
1020
  total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
1021
  duration = total_frames / fps if fps > 0 else 0
1022
 
1023
+ progress(0, desc="Analyzing video...")
1024
 
1025
  while cap.isOpened() and processing_active:
1026
  ret, frame = cap.read()
 
1035
  timestamp = datetime.now(IST).isoformat()
1036
 
1037
  progress_percent = min(100, (frame_count / total_frames) * 100)
1038
+ progress(progress_percent / 100, desc=f"Processing frame {frame_count}/{total_frames}")
1039
 
 
1040
  processed_frame = preprocess_frame(frame)
1041
+ results = yolo_model.predict(processed_frame)
 
 
 
 
 
 
 
 
1042
 
1043
  violations = tracker.detect_violations(results, frame)
1044
 
 
1105
  pdf_temp_path, pdf_sf_url = generate_and_upload_report_to_salesforce(sf, current_run_violations, new_sf_record_ids)
1106
  if not pdf_temp_path:
1107
  logger.error("Failed to generate and upload Salesforce report.")
1108
+ elif current_run_violations and not sf:
1109
+ # Generate local PDF if no Salesforce
1110
+ pdf_temp_path = generate_local_pdf_report(current_run_violations)
1111
  elif not current_run_violations:
1112
  logger.info("No violations detected, skipping report generation.")
1113
  else:
1114
  logger.warning("Salesforce not configured or no violations recorded. Skipping Salesforce report upload.")
1115
 
1116
  session_summary = tracker.get_session_summary()
1117
+ logger.info(f"Video analysis complete. Session summary: {session_summary}")
1118
  logger.info(f"Total processing time: {processing_time:.2f}s")
1119
 
1120
  status_message = generate_status_message(
 
1130
 
1131
  return frames, status_message, pdf_temp_path, format_violations_as_text(current_run_violations)
1132
  except Exception as e:
1133
+ logger.error(f"Video processing error: {e}", exc_info=True)
1134
  error_message = f"Video processing failed: {str(e)}"
1135
  return None, error_message, None, format_violations_as_text([])
1136
  finally:
 
1138
  cv2.destroyAllWindows()
1139
  logger.info(f"Total processing time: {time.time() - start_total:.2f}s")
1140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1141
  # --- RTSP Processing ---
1142
  async def process_rtsp_stream(rtsp_url, max_frames=None, frame_skip=5, progress=gr.Progress()):
1143
  global processing_active
 
1154
  tracker = SafetyViolationDetector()
1155
 
1156
  tracker.reset_session()
1157
+ logger.info("Starting new RTSP stream analysis session")
1158
 
1159
  # Get Salesforce connection once at the beginning
1160
  sf = None
 
1165
  logger.error(f"Could not connect to Salesforce at start: {e}")
1166
 
1167
  frames = []
1168
+ max_display_frames = 10
1169
  violation_count = 0
1170
 
1171
  progress(0, desc="Connecting to RTSP stream...")
 
1178
  continue
1179
 
1180
  progress_percent = min(100, (fc / (max_frames if max_frames else 100)) * 100)
1181
+ progress(progress_percent / 100, desc=f"Processing frame {fc}")
1182
 
 
1183
  processed_frame = preprocess_frame(frame)
1184
+ results = yolo_model.predict(processed_frame)
 
 
 
 
 
 
 
 
1185
 
1186
  violations = tracker.detect_violations(results, frame)
1187
 
 
1243
  pdf_temp_path, pdf_sf_url = generate_and_upload_report_to_salesforce(sf, current_run_violations, new_sf_record_ids)
1244
  if not pdf_temp_path:
1245
  logger.error("Failed to generate and upload Salesforce report.")
1246
+ elif current_run_violations and not sf:
1247
+ # Generate local PDF if no Salesforce
1248
+ pdf_temp_path = generate_local_pdf_report(current_run_violations)
1249
  elif not current_run_violations:
1250
  logger.info("No violations detected, skipping report generation.")
1251
  else:
 
1255
  return "Processing cancelled.", frames, format_violations_as_text(current_run_violations), generate_heatmap(current_run_violations, generate=False), pdf_temp_path
1256
 
1257
  session_summary = tracker.get_session_summary()
1258
+ logger.info(f"RTSP analysis complete. Session summary: {session_summary}")
1259
  logger.info(f"Total processing time: {time.time() - start_total:.2f}s")
1260
 
1261
+ status_message = f"Processed {len(frames)} frames with {violation_count} unique violations. Persons tracked: {session_summary['total_persons']}"
1262
 
1263
  return status_message, frames, format_violations_as_text(current_run_violations), generate_heatmap(current_run_violations, generate=False), pdf_temp_path
1264
  except Exception as e:
1265
+ logger.error(f"RTSP processing error: {e}", exc_info=True)
1266
  error_message = f"RTSP processing failed: {str(e)}"
1267
  return error_message, None, format_violations_as_text([]), None, None
1268
  finally:
 
1273
  # --- Other Functions ---
1274
  def generate_status_message(has_violations, total_frames, processed_frames, duration,
1275
  violation_count, processing_time, actual_fps, session_summary=None):
1276
+ base_message = f"""βœ… ANALYSIS COMPLETED
1277
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1278
  πŸ“Š RESULTS:
1279
  β€’ Frames: {total_frames} (Processed: {processed_frames})
1280
  β€’ Duration: {duration:.2f}s
1281
  β€’ Processing Time: {processing_time:.2f}s
1282
+ β€’ FPS: {actual_fps:.1f}"""
 
 
1283
 
1284
  if session_summary:
1285
  base_message += f"""
 
1290
  return f"""{base_message}
1291
  🚨 UNIQUE VIOLATIONS: {violation_count}
1292
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1293
+ Each violation reported only once per person"""
1294
  else:
1295
  return f"""{base_message}
1296
  βœ… NO VIOLATIONS DETECTED
 
1332
 
1333
  def format_violations_as_text(violations):
1334
  if not violations:
1335
+ return """πŸ” SAFETY MONITORING STATUS
1336
 
1337
  βœ… NO VIOLATIONS DETECTED
1338
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1339
 
1340
  πŸ“Š Current Status: ALL CLEAR
1341
  πŸ• Last Updated: """ + datetime.now(IST).strftime('%Y-%m-%d %H:%M:%S IST') + """
1342
+ 🎯 Detection Accuracy: >90% confidence
1343
+ ⚑ Response Time: <5 seconds
 
1344
 
1345
+ The system is actively monitoring for:
1346
+ β€’ No Helmet violations
1347
+ β€’ Unsafe Distance violations
1348
+ β€’ Unauthorized Area violations
1349
 
1350
  All safety protocols are currently being followed."""
1351
 
1352
+ text = f"""🚨 SAFETY VIOLATION ALERTS
1353
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1354
 
1355
  πŸ“Š UNIQUE VIOLATIONS DETECTED: {len(violations)}
 
1356
  Note: Each violation type reported only once per person
1357
 
1358
  """
1359
  for i, violation in enumerate(violations, 1):
1360
+ severity_emoji = "πŸ”΄" if violation['severity'] == 'Critical' else "🟑"
1361
  text += f"""
1362
  β”Œβ”€ ALERT #{i:02d} ─ {severity_emoji} {violation['violation_type'].upper()}
1363
  β”‚
 
1366
  β”œβ”€ πŸ“ Location: Site {violation['site_id']} | Camera {violation['camera_id']}
1367
  β”œβ”€ πŸ‘· Worker: {violation.get('worker_id', 'UNKNOWN')}
1368
  β”œβ”€ πŸ“Έ Evidence: {violation['snapshot_url']}
 
1369
  β”‚
1370
  └─────────────────────────────────────────────��───\n"""
1371
 
1372
  text += f"""
1373
 
1374
+ πŸ“ˆ SUMMARY STATISTICS:
1375
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1376
  β€’ Total Violations: {len(violations)}
1377
  β€’ Critical: {sum(1 for v in violations if v['severity'] == 'Critical')}
 
1378
  β€’ Moderate: {sum(1 for v in violations if v['severity'] == 'Moderate')}
1379
  β€’ Last Alert: {violations[-1]['timestamp'] if violations else 'N/A'}
1380
 
1381
+ πŸ”„ System Status: ACTIVELY MONITORING
1382
+ ⚑ Response Time: <5 seconds
1383
+ 🎯 Detection Accuracy: >90% confidence"""
 
1384
  return text
1385
 
1386
  def generate_heatmap(violations, generate=True):
 
1393
 
1394
  plt.figure(figsize=(12, 8))
1395
  sns.heatmap(heatmap_data, cmap='YlOrRd', annot=True, fmt='d')
1396
+ plt.title("Unique Violations by Hour")
1397
  plt.xlabel("Violation Type")
1398
  plt.ylabel("Hour of Day")
1399
 
 
1803
  }
1804
  """
1805
 
1806
+ # --- Gradio Interface ---
1807
  with gr.Blocks(
1808
+ title="Dynamic Safety Violation Detection using CCTV + AI",
1809
  css=enhanced_custom_css,
1810
  theme=gr.themes.Soft(
1811
  primary_hue="blue",
 
1912
  # Professional Header
1913
  gr.HTML("""
1914
  <div class="main-header">
1915
+ <h1 class="header-title">πŸ” Dynamic Safety Violation Detection using CCTV + AI</h1>
1916
+ <p class="header-subtitle">Enhanced Multi-Person Tracking with Image & Video Analysis - Each violation type detected only once per person</p>
1917
  </div>
1918
  """)
1919
 
1920
  # Smart Media Analysis Section
1921
+ gr.HTML('<div class="section-header">πŸ“· Smart Media Analysis (Images & Videos)</div>')
1922
  with gr.Row():
1923
  with gr.Column(scale=1):
1924
  with gr.Group(elem_classes=["professional-card"]):
1925
  media_input = gr.File(
1926
+ label="πŸ“€ Upload Image or Video for Safety Analysis",
1927
  file_types=["image", "video"],
1928
  elem_classes=["image-component"],
1929
  height=200
 
1933
  maximum=10,
1934
  step=1,
1935
  value=5,
1936
+ label="Frame Skip (Higher = Faster Processing, Videos Only)"
1937
  )
1938
  with gr.Row():
1939
  media_button = gr.Button(
1940
+ "πŸ” Analyze Media",
1941
  variant="primary",
1942
  elem_classes=["btn-primary"],
1943
  size="lg"
1944
  )
1945
 
1946
  # Analysis Results Section
1947
+ gr.HTML('<div class="section-header">πŸ“Š Analysis Results & Violation Details</div>')
1948
  with gr.Row():
1949
  with gr.Column(scale=1):
1950
  with gr.Group(elem_classes=["professional-card"]):
1951
  media_output = gr.Gallery(
1952
+ label="πŸ–ΌοΈ Processed Media with Detection Results",
1953
  elem_classes=["gallery-component"],
1954
+ height=260
1955
  )
1956
  with gr.Column(scale=1):
1957
  with gr.Group(elem_classes=["professional-card"]):
1958
  media_status = gr.Textbox(
1959
+ label="πŸ“‹ Analysis Status",
1960
  elem_classes=["status-display"],
1961
+ lines=7,
1962
+ max_lines=9,
1963
+ value="πŸ“Š Awaiting Media Analysis\n\nUpload an image or video and click 'Analyze Media' to begin safety violation detection.\n\nβ€’ Images: Instant analysis\nβ€’ Videos: Frame-by-frame processing",
1964
  interactive=False
1965
  )
1966
  pdf_output = gr.File(
1967
+ label="πŸ“₯ Download Professional Report",
1968
  elem_classes=["file-component"]
1969
  )
1970
 
1971
  # Violation Details Section
1972
+ gr.HTML('<div class="section-header">🚨 Real-time Violation Monitoring</div>')
1973
  with gr.Group(elem_classes=["professional-card", "alert-panel"]):
1974
  violation_log = gr.Textbox(
1975
+ label="🚨 Real-time Violation Details",
1976
  elem_classes=["status-display"],
1977
+ lines=10,
1978
+ max_lines=12,
1979
  value=format_violations_as_text(recent_violations),
1980
  interactive=False
1981
  )
1982
 
1983
  # Live Stream Processing Section
1984
+ gr.HTML('<div class="section-header">πŸ“Ή Live Stream Monitoring</div>')
1985
  with gr.Row():
1986
  with gr.Column(scale=2):
1987
  with gr.Group(elem_classes=["professional-card"]):
 
1991
  value=RTSP_URL_DEFAULT,
1992
  interactive=True
1993
  )
 
 
 
 
 
 
 
1994
  with gr.Row():
1995
  rtsp_button = gr.Button(
1996
+ "πŸ“‘ Start Live Monitoring",
1997
  variant="primary",
1998
  elem_classes=["btn-primary"],
1999
  size="lg"
 
2005
  size="lg"
2006
  )
2007
  rtsp_status = gr.Textbox(
2008
+ label="πŸ“Ί Live Stream Processing Status",
2009
  elem_classes=["status-display"],
2010
  lines=6,
2011
  max_lines=8,
2012
+ value="πŸ“Ί RTSP Stream Processor Ready\n\nEnter an RTSP URL and click 'Start Live Monitoring' to begin real-time monitoring.",
2013
  interactive=False
2014
  )
2015
  with gr.Column(scale=3):
2016
  with gr.Group(elem_classes=["professional-card"]):
2017
  rtsp_output = gr.Gallery(
2018
+ label="🎬 Live Stream Frames & Detection Results",
2019
  elem_classes=["gallery-component"],
2020
+ height=360,
2021
  columns=3,
2022
  rows=2,
2023
  object_fit="cover"
2024
  )
2025
 
2026
  # Live Violation Log Section
2027
+ gr.HTML('<div class="section-header">πŸ“Š Live Violation Analytics</div>')
2028
  with gr.Row():
2029
  with gr.Column(scale=1):
2030
  with gr.Group(elem_classes=["professional-card", "alert-panel"]):
2031
  rtsp_violation_log = gr.Textbox(
2032
+ label="🚨 Live Violation Log",
2033
  elem_classes=["status-display"],
2034
+ lines=8,
2035
+ max_lines=10,
2036
  interactive=False
2037
  )
2038
  with gr.Column(scale=1):
2039
  with gr.Group(elem_classes=["professional-card", "analytics-panel"]):
2040
  heatmap_output = gr.Image(
2041
+ label="πŸ”₯ Violation Heatmap - Temporal Analysis",
2042
  elem_classes=["image-component"],
2043
+ height=320
2044
  )
2045
  rtsp_pdf_output = gr.File(
2046
+ label="πŸ“₯ Download RTSP Report",
2047
  elem_classes=["file-component"]
2048
  )
2049
 
2050
  # Professional Footer
2051
  gr.HTML("""
2052
  <div class="footer-info">
2053
+ <h3>πŸ›‘οΈ Dynamic Safety Violation Detection using CCTV + AI</h3>
2054
  <div class="feature-grid">
2055
  <div class="feature-item">
2056
+ <strong>🎯 Real-time Detection</strong><br>
2057
+ Advanced YOLOv8 AI with >90% accuracy
2058
  </div>
2059
  <div class="feature-item">
2060
  <strong>⚑ Ultra-fast Response</strong><br>
2061
+ Alert generation in <5 seconds
2062
  </div>
2063
  <div class="feature-item">
2064
+ <strong>πŸ“Έ Image & Video Support</strong><br>
2065
+ Process both static images and video files
2066
  </div>
2067
  <div class="feature-item">
2068
  <strong>πŸ“± Responsive Design</strong><br>
2069
+ Optimized for desktop, tablet & mobile
2070
  </div>
2071
  </div>
2072
  <div style="margin-top: 0.8rem; padding-top: 0.8rem; border-top: 0.5px solid rgba(255,255,255,0.2);">
2073
  <p style="margin: 0; font-size: 0.8rem; opacity: 0.7;">
2074
+ Dynamic Safety Violation Detection using CCTV + AI Β© 2025
2075
  </p>
2076
  </div>
2077
  </div>
 
2086
 
2087
  rtsp_button.click(
2088
  fn=process_rtsp_stream,
2089
+ inputs=[rtsp_url_input],
2090
  outputs=[rtsp_status, rtsp_output, rtsp_violation_log, heatmap_output, rtsp_pdf_output]
2091
  )
2092
  rtsp_cancel_btn.click(cancel_processing, outputs=[rtsp_status])