matrice-analytics 0.1.60__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.
Files changed (196) hide show
  1. matrice_analytics/__init__.py +28 -0
  2. matrice_analytics/boundary_drawing_internal/README.md +305 -0
  3. matrice_analytics/boundary_drawing_internal/__init__.py +45 -0
  4. matrice_analytics/boundary_drawing_internal/boundary_drawing_internal.py +1207 -0
  5. matrice_analytics/boundary_drawing_internal/boundary_drawing_tool.py +429 -0
  6. matrice_analytics/boundary_drawing_internal/boundary_tool_template.html +1036 -0
  7. matrice_analytics/boundary_drawing_internal/data/.gitignore +12 -0
  8. matrice_analytics/boundary_drawing_internal/example_usage.py +206 -0
  9. matrice_analytics/boundary_drawing_internal/usage/README.md +110 -0
  10. matrice_analytics/boundary_drawing_internal/usage/boundary_drawer_launcher.py +102 -0
  11. matrice_analytics/boundary_drawing_internal/usage/simple_boundary_launcher.py +107 -0
  12. matrice_analytics/post_processing/README.md +455 -0
  13. matrice_analytics/post_processing/__init__.py +732 -0
  14. matrice_analytics/post_processing/advanced_tracker/README.md +650 -0
  15. matrice_analytics/post_processing/advanced_tracker/__init__.py +17 -0
  16. matrice_analytics/post_processing/advanced_tracker/base.py +99 -0
  17. matrice_analytics/post_processing/advanced_tracker/config.py +77 -0
  18. matrice_analytics/post_processing/advanced_tracker/kalman_filter.py +370 -0
  19. matrice_analytics/post_processing/advanced_tracker/matching.py +195 -0
  20. matrice_analytics/post_processing/advanced_tracker/strack.py +230 -0
  21. matrice_analytics/post_processing/advanced_tracker/tracker.py +367 -0
  22. matrice_analytics/post_processing/config.py +146 -0
  23. matrice_analytics/post_processing/core/__init__.py +63 -0
  24. matrice_analytics/post_processing/core/base.py +704 -0
  25. matrice_analytics/post_processing/core/config.py +3291 -0
  26. matrice_analytics/post_processing/core/config_utils.py +925 -0
  27. matrice_analytics/post_processing/face_reg/__init__.py +43 -0
  28. matrice_analytics/post_processing/face_reg/compare_similarity.py +556 -0
  29. matrice_analytics/post_processing/face_reg/embedding_manager.py +950 -0
  30. matrice_analytics/post_processing/face_reg/face_recognition.py +2234 -0
  31. matrice_analytics/post_processing/face_reg/face_recognition_client.py +606 -0
  32. matrice_analytics/post_processing/face_reg/people_activity_logging.py +321 -0
  33. matrice_analytics/post_processing/ocr/__init__.py +0 -0
  34. matrice_analytics/post_processing/ocr/easyocr_extractor.py +250 -0
  35. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
  36. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
  37. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
  38. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
  39. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
  40. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
  41. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
  42. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
  43. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
  44. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
  45. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
  46. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
  47. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
  48. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
  49. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
  50. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
  51. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
  52. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
  53. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
  54. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
  55. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
  56. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
  57. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
  58. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
  59. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
  60. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
  61. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
  62. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
  63. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
  64. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
  65. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
  66. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
  67. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
  68. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
  69. matrice_analytics/post_processing/ocr/postprocessing.py +270 -0
  70. matrice_analytics/post_processing/ocr/preprocessing.py +52 -0
  71. matrice_analytics/post_processing/post_processor.py +1175 -0
  72. matrice_analytics/post_processing/test_cases/__init__.py +1 -0
  73. matrice_analytics/post_processing/test_cases/run_tests.py +143 -0
  74. matrice_analytics/post_processing/test_cases/test_advanced_customer_service.py +841 -0
  75. matrice_analytics/post_processing/test_cases/test_basic_counting_tracking.py +523 -0
  76. matrice_analytics/post_processing/test_cases/test_comprehensive.py +531 -0
  77. matrice_analytics/post_processing/test_cases/test_config.py +852 -0
  78. matrice_analytics/post_processing/test_cases/test_customer_service.py +585 -0
  79. matrice_analytics/post_processing/test_cases/test_data_generators.py +583 -0
  80. matrice_analytics/post_processing/test_cases/test_people_counting.py +510 -0
  81. matrice_analytics/post_processing/test_cases/test_processor.py +524 -0
  82. matrice_analytics/post_processing/test_cases/test_usecases.py +165 -0
  83. matrice_analytics/post_processing/test_cases/test_utilities.py +356 -0
  84. matrice_analytics/post_processing/test_cases/test_utils.py +743 -0
  85. matrice_analytics/post_processing/usecases/Histopathological_Cancer_Detection_img.py +604 -0
  86. matrice_analytics/post_processing/usecases/__init__.py +267 -0
  87. matrice_analytics/post_processing/usecases/abandoned_object_detection.py +797 -0
  88. matrice_analytics/post_processing/usecases/advanced_customer_service.py +1601 -0
  89. matrice_analytics/post_processing/usecases/age_detection.py +842 -0
  90. matrice_analytics/post_processing/usecases/age_gender_detection.py +1085 -0
  91. matrice_analytics/post_processing/usecases/anti_spoofing_detection.py +656 -0
  92. matrice_analytics/post_processing/usecases/assembly_line_detection.py +841 -0
  93. matrice_analytics/post_processing/usecases/banana_defect_detection.py +624 -0
  94. matrice_analytics/post_processing/usecases/basic_counting_tracking.py +667 -0
  95. matrice_analytics/post_processing/usecases/blood_cancer_detection_img.py +881 -0
  96. matrice_analytics/post_processing/usecases/car_damage_detection.py +834 -0
  97. matrice_analytics/post_processing/usecases/car_part_segmentation.py +946 -0
  98. matrice_analytics/post_processing/usecases/car_service.py +1601 -0
  99. matrice_analytics/post_processing/usecases/cardiomegaly_classification.py +864 -0
  100. matrice_analytics/post_processing/usecases/cell_microscopy_segmentation.py +897 -0
  101. matrice_analytics/post_processing/usecases/chicken_pose_detection.py +648 -0
  102. matrice_analytics/post_processing/usecases/child_monitoring.py +814 -0
  103. matrice_analytics/post_processing/usecases/color/clip.py +660 -0
  104. matrice_analytics/post_processing/usecases/color/clip_processor/merges.txt +48895 -0
  105. matrice_analytics/post_processing/usecases/color/clip_processor/preprocessor_config.json +28 -0
  106. matrice_analytics/post_processing/usecases/color/clip_processor/special_tokens_map.json +30 -0
  107. matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer.json +245079 -0
  108. matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer_config.json +32 -0
  109. matrice_analytics/post_processing/usecases/color/clip_processor/vocab.json +1 -0
  110. matrice_analytics/post_processing/usecases/color/color_map_utils.py +70 -0
  111. matrice_analytics/post_processing/usecases/color/color_mapper.py +468 -0
  112. matrice_analytics/post_processing/usecases/color_detection.py +1936 -0
  113. matrice_analytics/post_processing/usecases/color_map_utils.py +70 -0
  114. matrice_analytics/post_processing/usecases/concrete_crack_detection.py +827 -0
  115. matrice_analytics/post_processing/usecases/crop_weed_detection.py +781 -0
  116. matrice_analytics/post_processing/usecases/customer_service.py +1008 -0
  117. matrice_analytics/post_processing/usecases/defect_detection_products.py +936 -0
  118. matrice_analytics/post_processing/usecases/distracted_driver_detection.py +822 -0
  119. matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +585 -0
  120. matrice_analytics/post_processing/usecases/drowsy_driver_detection.py +829 -0
  121. matrice_analytics/post_processing/usecases/dwell_detection.py +829 -0
  122. matrice_analytics/post_processing/usecases/emergency_vehicle_detection.py +827 -0
  123. matrice_analytics/post_processing/usecases/face_emotion.py +813 -0
  124. matrice_analytics/post_processing/usecases/face_recognition.py +827 -0
  125. matrice_analytics/post_processing/usecases/fashion_detection.py +835 -0
  126. matrice_analytics/post_processing/usecases/field_mapping.py +902 -0
  127. matrice_analytics/post_processing/usecases/fire_detection.py +1146 -0
  128. matrice_analytics/post_processing/usecases/flare_analysis.py +836 -0
  129. matrice_analytics/post_processing/usecases/flower_segmentation.py +1006 -0
  130. matrice_analytics/post_processing/usecases/gas_leak_detection.py +837 -0
  131. matrice_analytics/post_processing/usecases/gender_detection.py +832 -0
  132. matrice_analytics/post_processing/usecases/human_activity_recognition.py +871 -0
  133. matrice_analytics/post_processing/usecases/intrusion_detection.py +1672 -0
  134. matrice_analytics/post_processing/usecases/leaf.py +821 -0
  135. matrice_analytics/post_processing/usecases/leaf_disease.py +840 -0
  136. matrice_analytics/post_processing/usecases/leak_detection.py +837 -0
  137. matrice_analytics/post_processing/usecases/license_plate_detection.py +1188 -0
  138. matrice_analytics/post_processing/usecases/license_plate_monitoring.py +1781 -0
  139. matrice_analytics/post_processing/usecases/litter_monitoring.py +717 -0
  140. matrice_analytics/post_processing/usecases/mask_detection.py +869 -0
  141. matrice_analytics/post_processing/usecases/natural_disaster.py +907 -0
  142. matrice_analytics/post_processing/usecases/parking.py +787 -0
  143. matrice_analytics/post_processing/usecases/parking_space_detection.py +822 -0
  144. matrice_analytics/post_processing/usecases/pcb_defect_detection.py +888 -0
  145. matrice_analytics/post_processing/usecases/pedestrian_detection.py +808 -0
  146. matrice_analytics/post_processing/usecases/people_counting.py +706 -0
  147. matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
  148. matrice_analytics/post_processing/usecases/people_tracking.py +1842 -0
  149. matrice_analytics/post_processing/usecases/pipeline_detection.py +605 -0
  150. matrice_analytics/post_processing/usecases/plaque_segmentation_img.py +874 -0
  151. matrice_analytics/post_processing/usecases/pothole_segmentation.py +915 -0
  152. matrice_analytics/post_processing/usecases/ppe_compliance.py +645 -0
  153. matrice_analytics/post_processing/usecases/price_tag_detection.py +822 -0
  154. matrice_analytics/post_processing/usecases/proximity_detection.py +1901 -0
  155. matrice_analytics/post_processing/usecases/road_lane_detection.py +623 -0
  156. matrice_analytics/post_processing/usecases/road_traffic_density.py +832 -0
  157. matrice_analytics/post_processing/usecases/road_view_segmentation.py +915 -0
  158. matrice_analytics/post_processing/usecases/shelf_inventory_detection.py +583 -0
  159. matrice_analytics/post_processing/usecases/shoplifting_detection.py +822 -0
  160. matrice_analytics/post_processing/usecases/shopping_cart_analysis.py +899 -0
  161. matrice_analytics/post_processing/usecases/skin_cancer_classification_img.py +864 -0
  162. matrice_analytics/post_processing/usecases/smoker_detection.py +833 -0
  163. matrice_analytics/post_processing/usecases/solar_panel.py +810 -0
  164. matrice_analytics/post_processing/usecases/suspicious_activity_detection.py +1030 -0
  165. matrice_analytics/post_processing/usecases/template_usecase.py +380 -0
  166. matrice_analytics/post_processing/usecases/theft_detection.py +648 -0
  167. matrice_analytics/post_processing/usecases/traffic_sign_monitoring.py +724 -0
  168. matrice_analytics/post_processing/usecases/underground_pipeline_defect_detection.py +775 -0
  169. matrice_analytics/post_processing/usecases/underwater_pollution_detection.py +842 -0
  170. matrice_analytics/post_processing/usecases/vehicle_monitoring.py +1029 -0
  171. matrice_analytics/post_processing/usecases/warehouse_object_segmentation.py +899 -0
  172. matrice_analytics/post_processing/usecases/waterbody_segmentation.py +923 -0
  173. matrice_analytics/post_processing/usecases/weapon_detection.py +771 -0
  174. matrice_analytics/post_processing/usecases/weld_defect_detection.py +615 -0
  175. matrice_analytics/post_processing/usecases/wildlife_monitoring.py +898 -0
  176. matrice_analytics/post_processing/usecases/windmill_maintenance.py +834 -0
  177. matrice_analytics/post_processing/usecases/wound_segmentation.py +856 -0
  178. matrice_analytics/post_processing/utils/__init__.py +150 -0
  179. matrice_analytics/post_processing/utils/advanced_counting_utils.py +400 -0
  180. matrice_analytics/post_processing/utils/advanced_helper_utils.py +317 -0
  181. matrice_analytics/post_processing/utils/advanced_tracking_utils.py +461 -0
  182. matrice_analytics/post_processing/utils/alerting_utils.py +213 -0
  183. matrice_analytics/post_processing/utils/category_mapping_utils.py +94 -0
  184. matrice_analytics/post_processing/utils/color_utils.py +592 -0
  185. matrice_analytics/post_processing/utils/counting_utils.py +182 -0
  186. matrice_analytics/post_processing/utils/filter_utils.py +261 -0
  187. matrice_analytics/post_processing/utils/format_utils.py +293 -0
  188. matrice_analytics/post_processing/utils/geometry_utils.py +300 -0
  189. matrice_analytics/post_processing/utils/smoothing_utils.py +358 -0
  190. matrice_analytics/post_processing/utils/tracking_utils.py +234 -0
  191. matrice_analytics/py.typed +0 -0
  192. matrice_analytics-0.1.60.dist-info/METADATA +481 -0
  193. matrice_analytics-0.1.60.dist-info/RECORD +196 -0
  194. matrice_analytics-0.1.60.dist-info/WHEEL +5 -0
  195. matrice_analytics-0.1.60.dist-info/licenses/LICENSE.txt +21 -0
  196. matrice_analytics-0.1.60.dist-info/top_level.txt +1 -0
@@ -0,0 +1,293 @@
1
+ """
2
+ Format conversion utilities for post-processing operations.
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+ from ..core.base import ResultFormat
7
+
8
+
9
+ def match_results_structure(results):
10
+ """
11
+ Match the results structure to the expected structure based on actual output formats.
12
+
13
+ Based on eg_output.json:
14
+ - Classification: {"category": str, "confidence": float}
15
+ - Detection: [{"bounding_box": {...}, "category": str, "confidence": float}, ...]
16
+ - Instance Segmentation: Same as detection but with "masks" field
17
+ - Object Tracking: {"frame_id": [{"track_id": int, "category": str, "confidence": float, "bounding_box": {...}}, ...]}
18
+ - Activity Recognition: {"frame_id": [{"category": str, "confidence": float, "bounding_box": {...}}, ...]} (no track_id)
19
+
20
+ Args:
21
+ results: Raw model output to analyze
22
+
23
+ Returns:
24
+ ResultFormat: Detected format type
25
+ """
26
+ if isinstance(results, list):
27
+ # Array format - detection, instance segmentation, or face recognition
28
+ if len(results) > 0 and isinstance(results[0], dict):
29
+ if results[0].get("masks"):
30
+ return ResultFormat.INSTANCE_SEGMENTATION
31
+ elif results[0].get("embedding") or results[0].get("landmarks"):
32
+ return ResultFormat.FACE_RECOGNITION
33
+ elif "bounding_box" in results[0] and "category" in results[0] and "confidence" in results[0]:
34
+ return ResultFormat.DETECTION
35
+ return ResultFormat.DETECTION # Default for list format
36
+
37
+ elif isinstance(results, dict):
38
+ # Check if it's a simple classification result
39
+ if "category" in results and "confidence" in results and len(results) == 2:
40
+ return ResultFormat.CLASSIFICATION
41
+
42
+ # Check if it's frame-based (tracking or activity recognition)
43
+ # Keys should be frame numbers or frame identifiers
44
+ frame_keys = list(results.keys())
45
+ if frame_keys and all(isinstance(k, (str, int)) for k in frame_keys):
46
+ # Check the first frame's content to determine type
47
+ first_frame_data = list(results.values())[0]
48
+ if isinstance(first_frame_data, list) and len(first_frame_data) > 0:
49
+ first_detection = first_frame_data[0]
50
+ if isinstance(first_detection, dict):
51
+ # Check for face recognition format first (has embedding or landmarks)
52
+ if first_detection.get("embedding") or first_detection.get("landmarks"):
53
+ return ResultFormat.FACE_RECOGNITION
54
+ # Check if it has track_id (object tracking) or not (activity recognition)
55
+ elif "track_id" in first_detection:
56
+ return ResultFormat.OBJECT_TRACKING
57
+ elif "category" in first_detection and "confidence" in first_detection:
58
+ return ResultFormat.ACTIVITY_RECOGNITION
59
+
60
+ # If we can't determine the type, check for typical classification structure
61
+ if "category" in results and "confidence" in results:
62
+ return ResultFormat.CLASSIFICATION
63
+
64
+ return ResultFormat.UNKNOWN
65
+
66
+
67
+ def convert_to_coco_format(results: Any) -> List[Dict]:
68
+ """
69
+ Convert results to COCO format.
70
+
71
+ Args:
72
+ results: Input results in any supported format
73
+
74
+ Returns:
75
+ List[Dict]: Results in COCO format
76
+ """
77
+ if isinstance(results, list):
78
+ # Already in detection format, convert to COCO
79
+ coco_results = []
80
+ for i, detection in enumerate(results):
81
+ bbox = detection.get("bounding_box", detection.get("bbox", {}))
82
+
83
+ # Convert to COCO bbox format [x, y, width, height]
84
+ if "xmin" in bbox:
85
+ coco_bbox = [
86
+ bbox["xmin"],
87
+ bbox["ymin"],
88
+ bbox["xmax"] - bbox["xmin"],
89
+ bbox["ymax"] - bbox["ymin"]
90
+ ]
91
+ elif "x1" in bbox:
92
+ coco_bbox = [
93
+ bbox["x1"],
94
+ bbox["y1"],
95
+ bbox["x2"] - bbox["x1"],
96
+ bbox["y2"] - bbox["y1"]
97
+ ]
98
+ else:
99
+ # Assume generic format
100
+ values = list(bbox.values())
101
+ coco_bbox = [values[0], values[1], values[2] - values[0], values[3] - values[1]]
102
+
103
+ coco_result = {
104
+ "id": i,
105
+ "category_id": detection.get("category_id", 0),
106
+ "category": detection.get("category", "unknown"),
107
+ "bbox": coco_bbox,
108
+ "score": detection.get("confidence", 0.0),
109
+ "area": coco_bbox[2] * coco_bbox[3]
110
+ }
111
+
112
+ if "masks" in detection:
113
+ coco_result["segmentation"] = detection["masks"]
114
+
115
+ # Add face recognition specific fields if present
116
+ if "embedding" in detection:
117
+ coco_result["embedding"] = detection["embedding"]
118
+ if "landmarks" in detection:
119
+ coco_result["landmarks"] = detection["landmarks"]
120
+
121
+ coco_results.append(coco_result)
122
+
123
+ return coco_results
124
+
125
+ elif isinstance(results, dict):
126
+ # Handle frame-based results
127
+ coco_results = []
128
+ result_id = 0
129
+
130
+ for frame_id, detections in results.items():
131
+ if isinstance(detections, list):
132
+ for detection in detections:
133
+ bbox = detection.get("bounding_box", detection.get("bbox", {}))
134
+
135
+ # Convert to COCO bbox format
136
+ if "xmin" in bbox:
137
+ coco_bbox = [
138
+ bbox["xmin"],
139
+ bbox["ymin"],
140
+ bbox["xmax"] - bbox["xmin"],
141
+ bbox["ymax"] - bbox["ymin"]
142
+ ]
143
+ else:
144
+ values = list(bbox.values())
145
+ coco_bbox = [values[0], values[1], values[2] - values[0], values[3] - values[1]]
146
+
147
+ coco_result = {
148
+ "id": result_id,
149
+ "frame_id": frame_id,
150
+ "category_id": detection.get("category_id", 0),
151
+ "category": detection.get("category", "unknown"),
152
+ "bbox": coco_bbox,
153
+ "score": detection.get("confidence", 0.0),
154
+ "area": coco_bbox[2] * coco_bbox[3]
155
+ }
156
+
157
+ if "track_id" in detection:
158
+ coco_result["track_id"] = detection["track_id"]
159
+
160
+ # Add face recognition specific fields if present
161
+ if "embedding" in detection:
162
+ coco_result["embedding"] = detection["embedding"]
163
+ if "landmarks" in detection:
164
+ coco_result["landmarks"] = detection["landmarks"]
165
+
166
+ coco_results.append(coco_result)
167
+ result_id += 1
168
+
169
+ return coco_results
170
+
171
+ return []
172
+
173
+
174
+ def convert_to_yolo_format(results: Any) -> List[List[float]]:
175
+ """
176
+ Convert results to YOLO format (normalized coordinates).
177
+
178
+ Args:
179
+ results: Input results in any supported format
180
+
181
+ Returns:
182
+ List[List[float]]: Results in YOLO format [class_id, x_center, y_center, width, height, confidence]
183
+ """
184
+ yolo_results = []
185
+
186
+ if isinstance(results, list):
187
+ for detection in results:
188
+ bbox = detection.get("bounding_box", detection.get("bbox", {}))
189
+
190
+ # Convert to normalized center coordinates
191
+ if "xmin" in bbox:
192
+ x_center = (bbox["xmin"] + bbox["xmax"]) / 2
193
+ y_center = (bbox["ymin"] + bbox["ymax"]) / 2
194
+ width = bbox["xmax"] - bbox["xmin"]
195
+ height = bbox["ymax"] - bbox["ymin"]
196
+ else:
197
+ values = list(bbox.values())
198
+ x_center = (values[0] + values[2]) / 2
199
+ y_center = (values[1] + values[3]) / 2
200
+ width = values[2] - values[0]
201
+ height = values[3] - values[1]
202
+
203
+ yolo_result = [
204
+ detection.get("category_id", 0),
205
+ x_center,
206
+ y_center,
207
+ width,
208
+ height,
209
+ detection.get("confidence", 0.0)
210
+ ]
211
+ yolo_results.append(yolo_result)
212
+
213
+ return yolo_results
214
+
215
+
216
+ def convert_to_tracking_format(detections: List[Dict], frame_id: str = "0") -> Dict:
217
+ """
218
+ Convert detection format to tracking format.
219
+
220
+ Args:
221
+ detections: List of detection dictionaries
222
+ frame_id: Frame identifier
223
+
224
+ Returns:
225
+ Dict: Results in tracking format
226
+ """
227
+ tracking_results = {frame_id: []}
228
+
229
+ for detection in detections:
230
+ tracking_detection = {
231
+ "track_id": detection.get("track_id", 0),
232
+ "category": detection.get("category", "unknown"),
233
+ "confidence": detection.get("confidence", 0.0),
234
+ "bounding_box": detection.get("bounding_box", detection.get("bbox", {}))
235
+ }
236
+
237
+ # Add face recognition specific fields if present
238
+ if "embedding" in detection:
239
+ tracking_detection["embedding"] = detection["embedding"]
240
+ if "landmarks" in detection:
241
+ tracking_detection["landmarks"] = detection["landmarks"]
242
+
243
+ tracking_results[frame_id].append(tracking_detection)
244
+
245
+ return tracking_results
246
+
247
+
248
+ def convert_detection_to_tracking_format(detections: List[Dict], frame_id: str = "0") -> Dict:
249
+ """
250
+ Convert detection format to tracking format.
251
+
252
+ Args:
253
+ detections: List of detection dictionaries
254
+ frame_id: Frame identifier
255
+
256
+ Returns:
257
+ Dict: Results in tracking format
258
+ """
259
+ return convert_to_tracking_format(detections, frame_id)
260
+
261
+
262
+ def convert_tracking_to_detection_format(tracking_results: Dict) -> List[Dict]:
263
+ """
264
+ Convert tracking format to detection format.
265
+
266
+ Args:
267
+ tracking_results: Tracking results dictionary
268
+
269
+ Returns:
270
+ List[Dict]: Results in detection format
271
+ """
272
+ detections = []
273
+
274
+ for frame_id, frame_detections in tracking_results.items():
275
+ if isinstance(frame_detections, list):
276
+ for detection in frame_detections:
277
+ detection_item = {
278
+ "category": detection.get("category", "unknown"),
279
+ "confidence": detection.get("confidence", 0.0),
280
+ "bounding_box": detection.get("bounding_box", detection.get("bbox", {}))
281
+ }
282
+ if "track_id" in detection:
283
+ detection_item["track_id"] = detection["track_id"]
284
+
285
+ # Add face recognition specific fields if present
286
+ if "embedding" in detection:
287
+ detection_item["embedding"] = detection["embedding"]
288
+ if "landmarks" in detection:
289
+ detection_item["landmarks"] = detection["landmarks"]
290
+
291
+ detections.append(detection_item)
292
+
293
+ return detections
@@ -0,0 +1,300 @@
1
+ """
2
+ Geometry utility functions for post-processing operations.
3
+ """
4
+
5
+ import math
6
+ from typing import List, Dict, Tuple, Union
7
+
8
+
9
+ def point_in_polygon(point: Tuple[float, float], polygon: List[Tuple[float, float]]) -> bool:
10
+ """
11
+ Check if point is inside polygon using ray casting algorithm.
12
+
13
+ Args:
14
+ point: (x, y) coordinate tuple
15
+ polygon: List of (x, y) coordinate tuples defining the polygon
16
+
17
+ Returns:
18
+ bool: True if point is inside polygon
19
+ """
20
+ x, y = point
21
+ n = len(polygon)
22
+ inside = False
23
+
24
+ p1x, p1y = polygon[0]
25
+ for i in range(1, n + 1):
26
+ p2x, p2y = polygon[i % n]
27
+ if y > min(p1y, p2y):
28
+ if y <= max(p1y, p2y):
29
+ if x <= max(p1x, p2x):
30
+ if p1y != p2y:
31
+ xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
32
+ if p1x == p2x or x <= xinters:
33
+ inside = not inside
34
+ p1x, p1y = p2x, p2y
35
+
36
+ return inside
37
+
38
+
39
+ def get_bbox_center(bbox: Union[Dict[str, float], List[float]]) -> Tuple[float, float]:
40
+ """
41
+ Get center point of bounding box.
42
+
43
+ Args:
44
+ bbox: Bounding box dict with coordinates or list [x1, y1, x2, y2]
45
+
46
+ Returns:
47
+ Tuple[float, float]: (x, y) center coordinates
48
+ """
49
+ if isinstance(bbox, list):
50
+ # Handle list format [x1, y1, x2, y2]
51
+ if len(bbox) >= 4:
52
+ return ((bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2)
53
+ return (0, 0)
54
+
55
+ elif isinstance(bbox, dict):
56
+ # Handle dict formats
57
+ if "xmin" in bbox and "xmax" in bbox and "ymin" in bbox and "ymax" in bbox:
58
+ return ((bbox["xmin"] + bbox["xmax"]) / 2, (bbox["ymin"] + bbox["ymax"]) / 2)
59
+ elif "x1" in bbox and "x2" in bbox and "y1" in bbox and "y2" in bbox:
60
+ return ((bbox["x1"] + bbox["x2"]) / 2, (bbox["y1"] + bbox["y2"]) / 2)
61
+ else:
62
+ # Handle different bbox formats
63
+ keys = list(bbox.keys())
64
+ if len(keys) >= 4:
65
+ values = list(bbox.values())
66
+ return ((values[0] + values[2]) / 2, (values[1] + values[3]) / 2)
67
+
68
+ return (0, 0)
69
+
70
+ def get_bbox_bottom25_center(bbox: Union[Dict[str, float], List[float]]) -> Tuple[float, float]:
71
+ """
72
+ Get bottom 25% center point of bounding box.
73
+
74
+ Args:
75
+ bbox: Bounding box dict with coordinates or list [x1, y1, x2, y2]
76
+
77
+ Returns:
78
+ Tuple[float, float]: (x, y) coordinates at bottom 25% height from center X
79
+ """
80
+ if isinstance(bbox, list):
81
+ # Handle list format [x1, y1, x2, y2]
82
+ if len(bbox) >= 4:
83
+ x_center = (bbox[0] + bbox[2]) / 2
84
+ height = bbox[3] - bbox[1]
85
+ y_target = bbox[3] - 0.25 * height
86
+ return (x_center, y_target)
87
+ return (0, 0)
88
+
89
+ elif isinstance(bbox, dict):
90
+ # Handle dict formats
91
+ if "xmin" in bbox and "xmax" in bbox and "ymin" in bbox and "ymax" in bbox:
92
+ x_center = (bbox["xmin"] + bbox["xmax"]) / 2
93
+ height = bbox["ymax"] - bbox["ymin"]
94
+ y_target = bbox["ymax"] - 0.25 * height
95
+ return (x_center, y_target)
96
+ elif "x1" in bbox and "x2" in bbox and "y1" in bbox and "y2" in bbox:
97
+ x_center = (bbox["x1"] + bbox["x2"]) / 2
98
+ height = bbox["y2"] - bbox["y1"]
99
+ y_target = bbox["y2"] - 0.25 * height
100
+ return (x_center, y_target)
101
+ else:
102
+ # Handle different bbox formats
103
+ keys = list(bbox.keys())
104
+ if len(keys) >= 4:
105
+ values = list(bbox.values())
106
+ x_center = (values[0] + values[2]) / 2
107
+ height = values[3] - values[1]
108
+ y_target = values[3] - 0.25 * height
109
+ return (x_center, y_target)
110
+
111
+ return (0, 0)
112
+
113
+ def calculate_distance(point1: Tuple[float, float], point2: Tuple[float, float]) -> float:
114
+ """
115
+ Calculate Euclidean distance between two points.
116
+
117
+ Args:
118
+ point1: First point (x, y)
119
+ point2: Second point (x, y)
120
+
121
+ Returns:
122
+ float: Euclidean distance
123
+ """
124
+ return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)
125
+
126
+
127
+ def calculate_bbox_overlap(bbox1: Dict[str, float], bbox2: Dict[str, float]) -> float:
128
+ """
129
+ Calculate IoU (Intersection over Union) between two bounding boxes.
130
+
131
+ Args:
132
+ bbox1: First bounding box
133
+ bbox2: Second bounding box
134
+
135
+ Returns:
136
+ float: IoU value between 0 and 1
137
+ """
138
+ return calculate_iou(bbox1, bbox2)
139
+
140
+
141
+ def calculate_iou(bbox1: Dict[str, float], bbox2: Dict[str, float]) -> float:
142
+ """
143
+ Calculate IoU (Intersection over Union) between two bounding boxes.
144
+
145
+ Args:
146
+ bbox1: First bounding box
147
+ bbox2: Second bounding box
148
+
149
+ Returns:
150
+ float: IoU value between 0 and 1
151
+ """
152
+ # Normalize bbox format
153
+ def normalize_bbox_coords(bbox):
154
+ if "xmin" in bbox:
155
+ return [bbox["xmin"], bbox["ymin"], bbox["xmax"], bbox["ymax"]]
156
+ elif "x1" in bbox:
157
+ return [bbox["x1"], bbox["y1"], bbox["x2"], bbox["y2"]]
158
+ else:
159
+ values = list(bbox.values())
160
+ return values[:4]
161
+
162
+ box1 = normalize_bbox_coords(bbox1)
163
+ box2 = normalize_bbox_coords(bbox2)
164
+
165
+ # Calculate intersection
166
+ x1 = max(box1[0], box2[0])
167
+ y1 = max(box1[1], box2[1])
168
+ x2 = min(box1[2], box2[2])
169
+ y2 = min(box1[3], box2[3])
170
+
171
+ if x2 < x1 or y2 < y1:
172
+ return 0.0
173
+
174
+ intersection = (x2 - x1) * (y2 - y1)
175
+
176
+ # Calculate union
177
+ area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
178
+ area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
179
+ union = area1 + area2 - intersection
180
+
181
+ return intersection / union if union > 0 else 0.0
182
+
183
+
184
+ def get_bbox_area(bbox: Dict[str, float]) -> float:
185
+ """
186
+ Calculate area of bounding box.
187
+
188
+ Args:
189
+ bbox: Bounding box dict
190
+
191
+ Returns:
192
+ float: Area of the bounding box
193
+ """
194
+ if "xmin" in bbox and "xmax" in bbox and "ymin" in bbox and "ymax" in bbox:
195
+ return (bbox["xmax"] - bbox["xmin"]) * (bbox["ymax"] - bbox["ymin"])
196
+ elif "x1" in bbox and "x2" in bbox and "y1" in bbox and "y2" in bbox:
197
+ return (bbox["x2"] - bbox["x1"]) * (bbox["y2"] - bbox["y1"])
198
+ else:
199
+ values = list(bbox.values())
200
+ if len(values) >= 4:
201
+ return (values[2] - values[0]) * (values[3] - values[1])
202
+ return 0.0
203
+
204
+
205
+ def normalize_bbox(bbox: Dict[str, float], image_width: float, image_height: float) -> Dict[str, float]:
206
+ """
207
+ Normalize bounding box coordinates to [0, 1] range.
208
+
209
+ Args:
210
+ bbox: Bounding box dict
211
+ image_width: Image width
212
+ image_height: Image height
213
+
214
+ Returns:
215
+ Dict[str, float]: Normalized bounding box
216
+ """
217
+ if "xmin" in bbox:
218
+ return {
219
+ "xmin": bbox["xmin"] / image_width,
220
+ "ymin": bbox["ymin"] / image_height,
221
+ "xmax": bbox["xmax"] / image_width,
222
+ "ymax": bbox["ymax"] / image_height
223
+ }
224
+ elif "x1" in bbox:
225
+ return {
226
+ "x1": bbox["x1"] / image_width,
227
+ "y1": bbox["y1"] / image_height,
228
+ "x2": bbox["x2"] / image_width,
229
+ "y2": bbox["y2"] / image_height
230
+ }
231
+ else:
232
+ # Handle generic format
233
+ keys = list(bbox.keys())
234
+ values = list(bbox.values())
235
+ normalized_values = [
236
+ values[0] / image_width,
237
+ values[1] / image_height,
238
+ values[2] / image_width,
239
+ values[3] / image_height
240
+ ]
241
+ return dict(zip(keys, normalized_values))
242
+
243
+
244
+ def denormalize_bbox(bbox: Dict[str, float], image_width: float, image_height: float) -> Dict[str, float]:
245
+ """
246
+ Denormalize bounding box coordinates from [0, 1] range to pixel coordinates.
247
+
248
+ Args:
249
+ bbox: Normalized bounding box dict
250
+ image_width: Image width
251
+ image_height: Image height
252
+
253
+ Returns:
254
+ Dict[str, float]: Denormalized bounding box
255
+ """
256
+ if "xmin" in bbox:
257
+ return {
258
+ "xmin": bbox["xmin"] * image_width,
259
+ "ymin": bbox["ymin"] * image_height,
260
+ "xmax": bbox["xmax"] * image_width,
261
+ "ymax": bbox["ymax"] * image_height
262
+ }
263
+ elif "x1" in bbox:
264
+ return {
265
+ "x1": bbox["x1"] * image_width,
266
+ "y1": bbox["y1"] * image_height,
267
+ "x2": bbox["x2"] * image_width,
268
+ "y2": bbox["y2"] * image_height
269
+ }
270
+ else:
271
+ # Handle generic format
272
+ keys = list(bbox.keys())
273
+ values = list(bbox.values())
274
+ denormalized_values = [
275
+ values[0] * image_width,
276
+ values[1] * image_height,
277
+ values[2] * image_width,
278
+ values[3] * image_height
279
+ ]
280
+ return dict(zip(keys, denormalized_values))
281
+
282
+
283
+ def line_segments_intersect(p1: Tuple[float, float], p2: Tuple[float, float],
284
+ p3: Tuple[float, float], p4: Tuple[float, float]) -> bool:
285
+ """
286
+ Check if two line segments intersect.
287
+
288
+ Args:
289
+ p1: First point of first line segment
290
+ p2: Second point of first line segment
291
+ p3: First point of second line segment
292
+ p4: Second point of second line segment
293
+
294
+ Returns:
295
+ bool: True if line segments intersect
296
+ """
297
+ def ccw(A, B, C):
298
+ return (C[1] - A[1]) * (B[0] - A[0]) > (B[1] - A[1]) * (C[0] - A[0])
299
+
300
+ return ccw(p1, p3, p4) != ccw(p2, p3, p4) and ccw(p1, p2, p3) != ccw(p1, p2, p4)