matrice 1.0.99269__py3-none-any.whl → 1.0.99270__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.
@@ -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
  # --------------------------------------------------------------------- #
@@ -589,10 +590,10 @@ class ProximityUseCase(BaseProcessor):
589
590
  """Count UNIQUE proximity events between detections in a frame.
590
591
 
591
592
  Rules:
592
- - If canonical track IDs are present, deduplicate by track_id first.
593
- - Otherwise, deduplicate overlapping boxes using IoU to avoid duplicate detections of the same person.
594
- - Count each pair once (i < j) using Euclidean distance between bottom-center points.
595
- - Distance is evaluated in meters when calibration is available. If unavailable, pixel threshold is used as a fallback.
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,101 @@ 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
- iou_duplicate_threshold = getattr(self, "_proximity_iou_duplicate_threshold", 0.5)
607
+ overlap_iou_threshold = getattr(self, "_proximity_iou_duplicate_threshold", 0.5)
607
608
 
608
- # Step 1: Deduplicate detections
609
- unique_detections: List[Dict[str, Any]] = []
610
- seen_track_ids: Set[Any] = set()
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
- track_id = det.get("track_id")
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
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
+ })
617
638
 
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
- continue
652
-
653
- dx = float(p_i[0]) - float(p_j[0])
654
- dy = float(p_i[1]) - float(p_j[1])
655
- pixel_distance = math.hypot(dx, dy)
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
643
+ centroids: Dict[Any, tuple] = {}
644
+ for td in kept:
645
+ x1, y1, x2, y2 = map(float, td["bbox"])
646
+ cx, cy = (x1 + x2) / 2.0, (y1 + y2) / 2.0
647
+ centroids[td.get("track_id")] = (cx, cy)
648
+
649
+ # Build current frame proximity pairs using track IDs only when both exist
650
+ current_pairs: Set[tuple] = set()
651
+ ids_list = [tid for tid in centroids.keys() if tid is not None]
652
+ for i in range(len(ids_list)):
653
+ for j in range(i + 1, len(ids_list)):
654
+ id1, id2 = ids_list[i], ids_list[j]
655
+ cx1, cy1 = centroids[id1]
656
+ cx2, cy2 = centroids[id2]
657
+ pixel_distance = math.hypot(cx1 - cx2, cy1 - cy2)
656
658
 
657
659
  is_close = False
658
660
  if meters_per_pixel:
659
661
  meters_distance = pixel_distance * float(meters_per_pixel)
660
- if meters_distance < float(threshold_meters):
661
- is_close = True
662
+ is_close = meters_distance < float(threshold_meters)
662
663
  else:
663
- if pixel_distance < float(threshold_pixels_fallback):
664
- is_close = True
664
+ is_close = pixel_distance < float(threshold_pixels_fallback)
665
+
666
+ if is_close:
667
+ pair = (id1, id2) if id1 <= id2 else (id2, id1)
668
+ current_pairs.add(pair)
665
669
 
666
- if not is_close:
667
- continue
670
+ # Update global unique proximity pairs
671
+ new_unique_pairs = {frozenset(p) for p in current_pairs} - self._observed_proximity_pairs
672
+ if new_unique_pairs:
673
+ self._total_proximity_count += len(new_unique_pairs)
674
+ self._observed_proximity_pairs.update(new_unique_pairs)
668
675
 
669
- frame_unique_count += 1
676
+ # Store last frame pairs
677
+ self._last_frame_proximity_pairs = current_pairs
670
678
 
671
- # Update global unique proximity pairs only when both have canonical IDs
672
- id_i = det_i.get("track_id")
673
- id_j = det_j.get("track_id")
674
- if id_i is not None and id_j is not None:
675
- pair_key = frozenset({id_i, id_j})
676
- if pair_key not in self._observed_proximity_pairs:
677
- self._observed_proximity_pairs.add(pair_key)
678
- self._total_proximity_count += 1
679
+ # Return count of unique pairs in current frame
680
+ return len(current_pairs)
679
681
 
680
- return frame_unique_count
682
+ def _nms_by_iou(self, detections: List[Dict[str, Any]], iou_threshold: float) -> List[Dict[str, Any]]:
683
+ """Perform simple IoU-based NMS on a list of detections.
684
+
685
+ Each detection is a dict with keys: 'bbox' as [x1,y1,x2,y2], 'confidence' (float), and optional 'track_id'.
686
+ Keeps highest-confidence detections when overlap exceeds threshold.
687
+ """
688
+ if not detections:
689
+ return []
690
+ # Sort by confidence descending
691
+ dets = sorted(detections, key=lambda d: float(d.get("confidence", 1.0)), reverse=True)
692
+ kept: List[Dict[str, Any]] = []
693
+ for det in dets:
694
+ should_keep = True
695
+ for kept_det in kept:
696
+ if self._compute_iou(det["bbox"], kept_det["bbox"]) >= iou_threshold:
697
+ should_keep = False
698
+ break
699
+ if should_keep:
700
+ kept.append(det)
701
+ return kept
681
702
 
682
703
  def _get_meters_per_pixel(self, config: ProximityConfig, stream_info: Optional[Dict[str, Any]] = None) -> Optional[float]:
683
704
  """Compute meters-per-pixel scale using config and optional stream_info.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrice
3
- Version: 1.0.99269
3
+ Version: 1.0.99270
4
4
  Summary: SDK for connecting to matrice.ai services
5
5
  Home-page: https://github.com/matrice-ai/python-sdk
6
6
  Author: Matrice.ai
@@ -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=QyknlaCyvzRg0j9Vndi1prYNW1elz0ARNBimHRyBak4,85207
207
+ matrice/deploy/utils/post_processing/usecases/proximity_detection.py,sha256=EBpu9tR_OOPfES0lM-YzBJN4FMwxc31TRKMS4OSLg4k,86507
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.99269.dist-info/licenses/LICENSE.txt,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
247
- matrice-1.0.99269.dist-info/METADATA,sha256=y9DrDgXE7DfQ8kc1rALCkIrPanFK1zTR3Eoc8DxVVyo,14624
248
- matrice-1.0.99269.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
249
- matrice-1.0.99269.dist-info/top_level.txt,sha256=P97js8ur6o5ClRqMH3Cjoab_NqbJ6sOQ3rJmVzKBvMc,8
250
- matrice-1.0.99269.dist-info/RECORD,,
246
+ matrice-1.0.99270.dist-info/licenses/LICENSE.txt,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
247
+ matrice-1.0.99270.dist-info/METADATA,sha256=oBNUFZl4QKsG87Nbl2m9QwrbUSzHoKLMSEKZ3l9VMQI,14624
248
+ matrice-1.0.99270.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
249
+ matrice-1.0.99270.dist-info/top_level.txt,sha256=P97js8ur6o5ClRqMH3Cjoab_NqbJ6sOQ3rJmVzKBvMc,8
250
+ matrice-1.0.99270.dist-info/RECORD,,