matrice 1.0.99269__py3-none-any.whl → 1.0.99271__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matrice/deploy/utils/post_processing/usecases/proximity_detection.py +144 -115
- {matrice-1.0.99269.dist-info → matrice-1.0.99271.dist-info}/METADATA +1 -1
- {matrice-1.0.99269.dist-info → matrice-1.0.99271.dist-info}/RECORD +6 -6
- {matrice-1.0.99269.dist-info → matrice-1.0.99271.dist-info}/WHEEL +0 -0
- {matrice-1.0.99269.dist-info → matrice-1.0.99271.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice-1.0.99269.dist-info → matrice-1.0.99271.dist-info}/top_level.txt +0 -0
@@ -66,6 +66,7 @@ class ProximityUseCase(BaseProcessor):
|
|
66
66
|
# Proximity counting
|
67
67
|
self._total_proximity_count = 0 # Total proximity events across all calls
|
68
68
|
self._observed_proximity_pairs: Set[frozenset] = set() # Unique canonical ID pairs seen across frames
|
69
|
+
self._last_frame_proximity_pairs: Set[tuple] = set() # Pairs detected in the most recent frame (track-id based)
|
69
70
|
|
70
71
|
|
71
72
|
# --------------------------------------------------------------------- #
|
@@ -535,44 +536,44 @@ class ProximityUseCase(BaseProcessor):
|
|
535
536
|
}
|
536
537
|
})
|
537
538
|
if zone_analysis:
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
human_text_lines.append(f"\t- People Detected: {total_people}")
|
560
|
-
human_text_lines.append("")
|
561
|
-
human_text_lines.append(f"TOTAL SINCE @ {start_timestamp}:")
|
539
|
+
human_text_lines = []
|
540
|
+
current_timestamp = self._get_current_timestamp_str(stream_info, frame_id=frame_id)
|
541
|
+
start_timestamp = self._get_start_timestamp_str(stream_info)
|
542
|
+
human_text_lines.append(f"CURRENT FRAME @ {current_timestamp}:")
|
543
|
+
|
544
|
+
def robust_zone_total(zone_count):
|
545
|
+
if isinstance(zone_count, dict):
|
546
|
+
total = 0
|
547
|
+
for v in zone_count.values():
|
548
|
+
if isinstance(v, int):
|
549
|
+
total += v
|
550
|
+
elif isinstance(v, list) and total == 0:
|
551
|
+
total += len(v)
|
552
|
+
return total
|
553
|
+
elif isinstance(zone_count, list):
|
554
|
+
return len(zone_count)
|
555
|
+
elif isinstance(zone_count, int):
|
556
|
+
return zone_count
|
557
|
+
else:
|
558
|
+
return 0
|
562
559
|
|
563
|
-
|
564
|
-
|
565
|
-
human_text_lines.append(f"
|
560
|
+
human_text_lines.append(f"\t- People Detected: {total_people}")
|
561
|
+
human_text_lines.append("")
|
562
|
+
human_text_lines.append(f"TOTAL SINCE @ {start_timestamp}:")
|
563
|
+
|
564
|
+
for zone_name, zone_count in zone_analysis.items():
|
565
|
+
zone_total = robust_zone_total(zone_count)
|
566
|
+
human_text_lines.append(f"\t- Zone name: {zone_name}")
|
566
567
|
human_text_lines.append(f"\t\t- Total count in zone: {zone_total}")
|
567
568
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
569
|
+
if total_unique_count > 0:
|
570
|
+
human_text_lines.append(f"\t- Total unique people in the scene: {total_unique_count}")
|
571
|
+
if alerts:
|
572
|
+
for alert in alerts:
|
573
|
+
human_text_lines.append(f"Alerts: {alert.get('settings', {})} sent @ {current_timestamp}")
|
574
|
+
else:
|
575
|
+
human_text_lines.append("Alerts: None")
|
576
|
+
human_text = "\n".join(human_text_lines)
|
576
577
|
else:
|
577
578
|
human_text = self._generate_human_text_for_tracking(total_people, detections, total_unique_count, config, frame_id, alerts, stream_info)
|
578
579
|
|
@@ -589,10 +590,10 @@ class ProximityUseCase(BaseProcessor):
|
|
589
590
|
"""Count UNIQUE proximity events between detections in a frame.
|
590
591
|
|
591
592
|
Rules:
|
592
|
-
-
|
593
|
-
-
|
594
|
-
- Count each pair once (i < j) using Euclidean distance between
|
595
|
-
- Distance is evaluated in meters when calibration is available
|
593
|
+
- Use IoU-NMS to deduplicate overlapping boxes (highest confidence kept).
|
594
|
+
- Use track IDs when available to build stable (id1,id2) pairs.
|
595
|
+
- Count each pair once (i < j) using Euclidean distance between box centers.
|
596
|
+
- Distance is evaluated in meters when calibration is available; otherwise, fallback to pixel threshold.
|
596
597
|
- Maintain a running set of unique canonical-ID pairs across frames to compute total unique proximity events.
|
597
598
|
"""
|
598
599
|
if not detections:
|
@@ -603,81 +604,113 @@ class ProximityUseCase(BaseProcessor):
|
|
603
604
|
threshold_meters = getattr(config, "proximity_threshold_meters", 1.0)
|
604
605
|
threshold_pixels_fallback = getattr(config, "proximity_threshold_pixels", 400.0)
|
605
606
|
|
606
|
-
|
607
|
+
overlap_iou_threshold = getattr(self, "_proximity_iou_duplicate_threshold", 0.5)
|
607
608
|
|
608
|
-
#
|
609
|
-
|
610
|
-
|
609
|
+
# Helper: convert bbox to xyxy list
|
610
|
+
def _to_xyxy(bbox: Any) -> List[float]:
|
611
|
+
if isinstance(bbox, list):
|
612
|
+
if len(bbox) >= 4:
|
613
|
+
return [float(bbox[0]), float(bbox[1]), float(bbox[2]), float(bbox[3])]
|
614
|
+
return []
|
615
|
+
if isinstance(bbox, dict):
|
616
|
+
if all(k in bbox for k in ("xmin", "ymin", "xmax", "ymax")):
|
617
|
+
return [float(bbox["xmin"]), float(bbox["ymin"]), float(bbox["xmax"]), float(bbox["ymax"])]
|
618
|
+
if all(k in bbox for k in ("x1", "y1", "x2", "y2")):
|
619
|
+
return [float(bbox["x1"]), float(bbox["y1"]), float(bbox["x2"]), float(bbox["y2"])]
|
620
|
+
# Fallback: take first four values
|
621
|
+
vals = list(bbox.values())
|
622
|
+
if len(vals) >= 4:
|
623
|
+
return [float(vals[0]), float(vals[1]), float(vals[2]), float(vals[3])]
|
624
|
+
return []
|
625
|
+
return []
|
611
626
|
|
627
|
+
# Prepare tracked detections (track_id, bbox_xyxy, conf)
|
628
|
+
tracked_detections: List[Dict[str, Any]] = []
|
612
629
|
for det in detections:
|
613
|
-
|
614
|
-
bbox = det.get("bounding_box", det.get("bbox", {}))
|
630
|
+
bbox = _to_xyxy(det.get("bounding_box", det.get("bbox", {})))
|
615
631
|
if not bbox:
|
616
|
-
continue
|
617
|
-
|
618
|
-
# Prefer canonical track IDs if present (set earlier in _update_tracking_state)
|
619
|
-
if track_id is not None:
|
620
|
-
if track_id in seen_track_ids:
|
621
|
-
continue
|
622
|
-
seen_track_ids.add(track_id)
|
623
|
-
unique_detections.append(det)
|
624
|
-
continue
|
625
|
-
|
626
|
-
# Fallback: IoU-based dedup for pure detection outputs without track IDs
|
627
|
-
is_duplicate = False
|
628
|
-
for kept in unique_detections:
|
629
|
-
kept_bbox = kept.get("bounding_box", kept.get("bbox", {}))
|
630
|
-
if self._compute_iou(bbox, kept_bbox) >= iou_duplicate_threshold:
|
631
|
-
is_duplicate = True
|
632
|
-
break
|
633
|
-
if not is_duplicate:
|
634
|
-
unique_detections.append(det)
|
635
|
-
|
636
|
-
# Step 2: Compute proximity pairs uniquely (i < j)
|
637
|
-
frame_unique_count = 0
|
638
|
-
|
639
|
-
for i in range(len(unique_detections)):
|
640
|
-
det_i = unique_detections[i]
|
641
|
-
bbox_i = det_i.get("bounding_box", det_i.get("bbox", {}))
|
642
|
-
p_i = get_bbox_bottom25_center(bbox_i) or get_bbox_center(bbox_i)
|
643
|
-
if not p_i:
|
644
|
-
continue
|
645
|
-
|
646
|
-
for j in range(i + 1, len(unique_detections)):
|
647
|
-
det_j = unique_detections[j]
|
648
|
-
bbox_j = det_j.get("bounding_box", det_j.get("bbox", {}))
|
649
|
-
p_j = get_bbox_bottom25_center(bbox_j) or get_bbox_center(bbox_j)
|
650
|
-
if not p_j:
|
651
632
|
continue
|
633
|
+
tracked_detections.append({
|
634
|
+
"track_id": det.get("track_id"),
|
635
|
+
"bbox": bbox,
|
636
|
+
"confidence": float(det.get("confidence", 1.0))
|
637
|
+
})
|
652
638
|
|
653
|
-
|
654
|
-
|
655
|
-
|
639
|
+
# IoU-NMS to remove overlapping boxes, keep highest confidence
|
640
|
+
kept: List[Dict[str, Any]] = self._nms_by_iou(tracked_detections, overlap_iou_threshold)
|
641
|
+
|
642
|
+
# Compute centroids and keep alignment arrays for IDs
|
643
|
+
centroids: List[tuple] = []
|
644
|
+
track_ids: List[Any] = []
|
645
|
+
for td in kept:
|
646
|
+
x1, y1, x2, y2 = map(float, td["bbox"])
|
647
|
+
# Use box center (matching your reference snippet); switch to bottom-center if needed
|
648
|
+
cx, cy = (x1 + x2) / 2.0, (y1 + y2) / 2.0
|
649
|
+
centroids.append((cx, cy))
|
650
|
+
track_ids.append(td.get("track_id"))
|
651
|
+
|
652
|
+
n = len(centroids)
|
653
|
+
current_pairs_by_ids: Set[tuple] = set()
|
654
|
+
current_pairs_all: Set[tuple] = set()
|
655
|
+
|
656
|
+
# Build current frame proximity pairs for all detections (even without IDs)
|
657
|
+
for i in range(n):
|
658
|
+
cx1, cy1 = centroids[i]
|
659
|
+
for j in range(i + 1, n):
|
660
|
+
cx2, cy2 = centroids[j]
|
661
|
+
pixel_distance = math.hypot(cx1 - cx2, cy1 - cy2)
|
656
662
|
|
657
|
-
is_close = False
|
658
663
|
if meters_per_pixel:
|
659
664
|
meters_distance = pixel_distance * float(meters_per_pixel)
|
660
|
-
|
661
|
-
is_close = True
|
665
|
+
is_close = meters_distance < float(threshold_meters)
|
662
666
|
else:
|
663
|
-
|
664
|
-
is_close = True
|
667
|
+
is_close = pixel_distance < float(threshold_pixels_fallback)
|
665
668
|
|
666
669
|
if not is_close:
|
667
670
|
continue
|
668
671
|
|
669
|
-
|
672
|
+
# For per-frame count, include every close pair
|
673
|
+
current_pairs_all.add((i, j))
|
670
674
|
|
671
|
-
#
|
672
|
-
id_i =
|
673
|
-
id_j =
|
675
|
+
# For global unique, require both IDs
|
676
|
+
id_i = track_ids[i]
|
677
|
+
id_j = track_ids[j]
|
674
678
|
if id_i is not None and id_j is not None:
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
+
pair_ids = (id_i, id_j) if id_i <= id_j else (id_j, id_i)
|
680
|
+
current_pairs_by_ids.add(pair_ids)
|
681
|
+
|
682
|
+
# Update global unique proximity pairs using ID pairs only
|
683
|
+
new_unique_pairs = {frozenset(p) for p in current_pairs_by_ids} - self._observed_proximity_pairs
|
684
|
+
if new_unique_pairs:
|
685
|
+
self._total_proximity_count += len(new_unique_pairs)
|
686
|
+
self._observed_proximity_pairs.update(new_unique_pairs)
|
687
|
+
|
688
|
+
# Store last frame pairs (ID pairs if available, else index pairs as fallback)
|
689
|
+
self._last_frame_proximity_pairs = current_pairs_by_ids if current_pairs_by_ids else current_pairs_all
|
679
690
|
|
680
|
-
|
691
|
+
# Return count of pairs detected in the current frame
|
692
|
+
return len(current_pairs_by_ids) if current_pairs_by_ids else len(current_pairs_all)
|
693
|
+
|
694
|
+
def _nms_by_iou(self, detections: List[Dict[str, Any]], iou_threshold: float) -> List[Dict[str, Any]]:
|
695
|
+
"""Perform simple IoU-based NMS on a list of detections.
|
696
|
+
|
697
|
+
Each detection is a dict with keys: 'bbox' as [x1,y1,x2,y2], 'confidence' (float), and optional 'track_id'.
|
698
|
+
Keeps highest-confidence detections when overlap exceeds threshold.
|
699
|
+
"""
|
700
|
+
if not detections:
|
701
|
+
return []
|
702
|
+
# Sort by confidence descending
|
703
|
+
dets = sorted(detections, key=lambda d: float(d.get("confidence", 1.0)), reverse=True)
|
704
|
+
kept: List[Dict[str, Any]] = []
|
705
|
+
for det in dets:
|
706
|
+
should_keep = True
|
707
|
+
for kept_det in kept:
|
708
|
+
if self._compute_iou(det["bbox"], kept_det["bbox"]) >= iou_threshold:
|
709
|
+
should_keep = False
|
710
|
+
break
|
711
|
+
if should_keep:
|
712
|
+
kept.append(det)
|
713
|
+
return kept
|
681
714
|
|
682
715
|
def _get_meters_per_pixel(self, config: ProximityConfig, stream_info: Optional[Dict[str, Any]] = None) -> Optional[float]:
|
683
716
|
"""Compute meters-per-pixel scale using config and optional stream_info.
|
@@ -720,12 +753,12 @@ class ProximityUseCase(BaseProcessor):
|
|
720
753
|
return None
|
721
754
|
|
722
755
|
def _generate_human_text_for_tracking(
|
723
|
-
self,
|
724
|
-
total_people: int,
|
725
|
-
detections,
|
726
|
-
total_unique_count: int,
|
727
|
-
config: ProximityConfig,
|
728
|
-
frame_id: str,
|
756
|
+
self,
|
757
|
+
total_people: int,
|
758
|
+
detections,
|
759
|
+
total_unique_count: int,
|
760
|
+
config: ProximityConfig,
|
761
|
+
frame_id: str,
|
729
762
|
alerts: Any = None,
|
730
763
|
stream_info: Optional[Dict[str, Any]] = None) -> str:
|
731
764
|
"""Generate human-readable text for tracking stats in old format."""
|
@@ -744,12 +777,8 @@ class ProximityUseCase(BaseProcessor):
|
|
744
777
|
human_text_lines.append("\t- No Proximity Events Detected")
|
745
778
|
|
746
779
|
human_text_lines.append("")
|
747
|
-
|
748
|
-
|
749
|
-
# If tracking IDs are available, _total_proximity_count already includes this frame's new pairs
|
750
|
-
# Otherwise, fall back to showing the current frame's count
|
751
|
-
total_unique = self._total_proximity_count if self._total_proximity_count > 0 else proximity_count
|
752
|
-
human_text_lines.append(f"\t- Total Proximity Count: {total_unique}")
|
780
|
+
human_text_lines.append(f"TOTAL SINCE @ {start_timestamp}:")
|
781
|
+
human_text_lines.append(f"\t- Total Proximity Count: {self._total_proximity_count}")
|
753
782
|
|
754
783
|
if alerts:
|
755
784
|
for alert in alerts:
|
@@ -1384,12 +1413,12 @@ class ProximityUseCase(BaseProcessor):
|
|
1384
1413
|
return datetime.now(timezone.utc).strftime("%Y-%m-%d-%H:%M:%S.%f UTC")
|
1385
1414
|
|
1386
1415
|
if stream_info.get("input_settings", {}).get("start_frame", "na") != "na":
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1416
|
+
if frame_id:
|
1417
|
+
start_time = int(frame_id)/stream_info.get("input_settings", {}).get("original_fps", 30)
|
1418
|
+
else:
|
1419
|
+
start_time = stream_info.get("input_settings", {}).get("start_frame", 30)/stream_info.get("input_settings", {}).get("original_fps", 30)
|
1420
|
+
stream_time_str = self._format_timestamp_for_video(start_time)
|
1421
|
+
return stream_time_str
|
1393
1422
|
else:
|
1394
1423
|
# For streams, use stream_time from stream_info
|
1395
1424
|
stream_time_str = stream_info.get("input_settings", {}).get("stream_info", {}).get("stream_time", "")
|
@@ -204,7 +204,7 @@ matrice/deploy/utils/post_processing/usecases/plaque_segmentation_img.py,sha256=
|
|
204
204
|
matrice/deploy/utils/post_processing/usecases/pothole_segmentation.py,sha256=jXTb8ZqInp5xJ-O3Zp3zQBiryFVD0-WBbhW6Kux_NDo,44905
|
205
205
|
matrice/deploy/utils/post_processing/usecases/ppe_compliance.py,sha256=G9P9j9E9nfNJInHJxmK1Lb4daFBlG5hq0aqotTLvFFE,30146
|
206
206
|
matrice/deploy/utils/post_processing/usecases/price_tag_detection.py,sha256=09Tp6MGAHh95s-NSAp-4WC9iCc20sajWApuUBAvgXiQ,39880
|
207
|
-
matrice/deploy/utils/post_processing/usecases/proximity_detection.py,sha256=
|
207
|
+
matrice/deploy/utils/post_processing/usecases/proximity_detection.py,sha256=tbERNzbuczj1GSLwApxt6_T3sYodFh8roThrPhj-OUI,87022
|
208
208
|
matrice/deploy/utils/post_processing/usecases/road_lane_detection.py,sha256=V_KxwBtAHSNkyoH8sXw-U-P3J8ToXtX3ncc69gn6Tds,31591
|
209
209
|
matrice/deploy/utils/post_processing/usecases/road_traffic_density.py,sha256=YiHQ0kKhXglagHPvygywxMqZAw8s0WharrBQqLQj2q4,40311
|
210
210
|
matrice/deploy/utils/post_processing/usecases/road_view_segmentation.py,sha256=BcBbOOg5622KuvzKrzs9cJW1wkRoIIcOab0N7BONQKQ,44986
|
@@ -243,8 +243,8 @@ matrice/deployment/camera_manager.py,sha256=e1Lc81RJP5wUWRdTgHO6tMWF9BkBdHOSVyx3
|
|
243
243
|
matrice/deployment/deployment.py,sha256=HFt151eWq6iqIAMsQvurpV2WNxW6Cx_gIUVfnVy5SWE,48093
|
244
244
|
matrice/deployment/inference_pipeline.py,sha256=6b4Mm3-qt-Zy0BeiJfFQdImOn3FzdNCY-7ET7Rp8PMk,37911
|
245
245
|
matrice/deployment/streaming_gateway_manager.py,sha256=ifYGl3g25wyU39HwhPQyI2OgF3M6oIqKMWt8RXtMxY8,21401
|
246
|
-
matrice-1.0.
|
247
|
-
matrice-1.0.
|
248
|
-
matrice-1.0.
|
249
|
-
matrice-1.0.
|
250
|
-
matrice-1.0.
|
246
|
+
matrice-1.0.99271.dist-info/licenses/LICENSE.txt,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
|
247
|
+
matrice-1.0.99271.dist-info/METADATA,sha256=KGoqAjTGhMEnZY1dF18S3l9f_ZK1Vn7EICyPlTbDkcg,14624
|
248
|
+
matrice-1.0.99271.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
249
|
+
matrice-1.0.99271.dist-info/top_level.txt,sha256=P97js8ur6o5ClRqMH3Cjoab_NqbJ6sOQ3rJmVzKBvMc,8
|
250
|
+
matrice-1.0.99271.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|