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.
- matrice_analytics/__init__.py +28 -0
- matrice_analytics/boundary_drawing_internal/README.md +305 -0
- matrice_analytics/boundary_drawing_internal/__init__.py +45 -0
- matrice_analytics/boundary_drawing_internal/boundary_drawing_internal.py +1207 -0
- matrice_analytics/boundary_drawing_internal/boundary_drawing_tool.py +429 -0
- matrice_analytics/boundary_drawing_internal/boundary_tool_template.html +1036 -0
- matrice_analytics/boundary_drawing_internal/data/.gitignore +12 -0
- matrice_analytics/boundary_drawing_internal/example_usage.py +206 -0
- matrice_analytics/boundary_drawing_internal/usage/README.md +110 -0
- matrice_analytics/boundary_drawing_internal/usage/boundary_drawer_launcher.py +102 -0
- matrice_analytics/boundary_drawing_internal/usage/simple_boundary_launcher.py +107 -0
- matrice_analytics/post_processing/README.md +455 -0
- matrice_analytics/post_processing/__init__.py +732 -0
- matrice_analytics/post_processing/advanced_tracker/README.md +650 -0
- matrice_analytics/post_processing/advanced_tracker/__init__.py +17 -0
- matrice_analytics/post_processing/advanced_tracker/base.py +99 -0
- matrice_analytics/post_processing/advanced_tracker/config.py +77 -0
- matrice_analytics/post_processing/advanced_tracker/kalman_filter.py +370 -0
- matrice_analytics/post_processing/advanced_tracker/matching.py +195 -0
- matrice_analytics/post_processing/advanced_tracker/strack.py +230 -0
- matrice_analytics/post_processing/advanced_tracker/tracker.py +367 -0
- matrice_analytics/post_processing/config.py +146 -0
- matrice_analytics/post_processing/core/__init__.py +63 -0
- matrice_analytics/post_processing/core/base.py +704 -0
- matrice_analytics/post_processing/core/config.py +3291 -0
- matrice_analytics/post_processing/core/config_utils.py +925 -0
- matrice_analytics/post_processing/face_reg/__init__.py +43 -0
- matrice_analytics/post_processing/face_reg/compare_similarity.py +556 -0
- matrice_analytics/post_processing/face_reg/embedding_manager.py +950 -0
- matrice_analytics/post_processing/face_reg/face_recognition.py +2234 -0
- matrice_analytics/post_processing/face_reg/face_recognition_client.py +606 -0
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +321 -0
- matrice_analytics/post_processing/ocr/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/easyocr_extractor.py +250 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
- matrice_analytics/post_processing/ocr/postprocessing.py +270 -0
- matrice_analytics/post_processing/ocr/preprocessing.py +52 -0
- matrice_analytics/post_processing/post_processor.py +1175 -0
- matrice_analytics/post_processing/test_cases/__init__.py +1 -0
- matrice_analytics/post_processing/test_cases/run_tests.py +143 -0
- matrice_analytics/post_processing/test_cases/test_advanced_customer_service.py +841 -0
- matrice_analytics/post_processing/test_cases/test_basic_counting_tracking.py +523 -0
- matrice_analytics/post_processing/test_cases/test_comprehensive.py +531 -0
- matrice_analytics/post_processing/test_cases/test_config.py +852 -0
- matrice_analytics/post_processing/test_cases/test_customer_service.py +585 -0
- matrice_analytics/post_processing/test_cases/test_data_generators.py +583 -0
- matrice_analytics/post_processing/test_cases/test_people_counting.py +510 -0
- matrice_analytics/post_processing/test_cases/test_processor.py +524 -0
- matrice_analytics/post_processing/test_cases/test_usecases.py +165 -0
- matrice_analytics/post_processing/test_cases/test_utilities.py +356 -0
- matrice_analytics/post_processing/test_cases/test_utils.py +743 -0
- matrice_analytics/post_processing/usecases/Histopathological_Cancer_Detection_img.py +604 -0
- matrice_analytics/post_processing/usecases/__init__.py +267 -0
- matrice_analytics/post_processing/usecases/abandoned_object_detection.py +797 -0
- matrice_analytics/post_processing/usecases/advanced_customer_service.py +1601 -0
- matrice_analytics/post_processing/usecases/age_detection.py +842 -0
- matrice_analytics/post_processing/usecases/age_gender_detection.py +1085 -0
- matrice_analytics/post_processing/usecases/anti_spoofing_detection.py +656 -0
- matrice_analytics/post_processing/usecases/assembly_line_detection.py +841 -0
- matrice_analytics/post_processing/usecases/banana_defect_detection.py +624 -0
- matrice_analytics/post_processing/usecases/basic_counting_tracking.py +667 -0
- matrice_analytics/post_processing/usecases/blood_cancer_detection_img.py +881 -0
- matrice_analytics/post_processing/usecases/car_damage_detection.py +834 -0
- matrice_analytics/post_processing/usecases/car_part_segmentation.py +946 -0
- matrice_analytics/post_processing/usecases/car_service.py +1601 -0
- matrice_analytics/post_processing/usecases/cardiomegaly_classification.py +864 -0
- matrice_analytics/post_processing/usecases/cell_microscopy_segmentation.py +897 -0
- matrice_analytics/post_processing/usecases/chicken_pose_detection.py +648 -0
- matrice_analytics/post_processing/usecases/child_monitoring.py +814 -0
- matrice_analytics/post_processing/usecases/color/clip.py +660 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/merges.txt +48895 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/preprocessor_config.json +28 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/special_tokens_map.json +30 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer.json +245079 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer_config.json +32 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/vocab.json +1 -0
- matrice_analytics/post_processing/usecases/color/color_map_utils.py +70 -0
- matrice_analytics/post_processing/usecases/color/color_mapper.py +468 -0
- matrice_analytics/post_processing/usecases/color_detection.py +1936 -0
- matrice_analytics/post_processing/usecases/color_map_utils.py +70 -0
- matrice_analytics/post_processing/usecases/concrete_crack_detection.py +827 -0
- matrice_analytics/post_processing/usecases/crop_weed_detection.py +781 -0
- matrice_analytics/post_processing/usecases/customer_service.py +1008 -0
- matrice_analytics/post_processing/usecases/defect_detection_products.py +936 -0
- matrice_analytics/post_processing/usecases/distracted_driver_detection.py +822 -0
- matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +585 -0
- matrice_analytics/post_processing/usecases/drowsy_driver_detection.py +829 -0
- matrice_analytics/post_processing/usecases/dwell_detection.py +829 -0
- matrice_analytics/post_processing/usecases/emergency_vehicle_detection.py +827 -0
- matrice_analytics/post_processing/usecases/face_emotion.py +813 -0
- matrice_analytics/post_processing/usecases/face_recognition.py +827 -0
- matrice_analytics/post_processing/usecases/fashion_detection.py +835 -0
- matrice_analytics/post_processing/usecases/field_mapping.py +902 -0
- matrice_analytics/post_processing/usecases/fire_detection.py +1146 -0
- matrice_analytics/post_processing/usecases/flare_analysis.py +836 -0
- matrice_analytics/post_processing/usecases/flower_segmentation.py +1006 -0
- matrice_analytics/post_processing/usecases/gas_leak_detection.py +837 -0
- matrice_analytics/post_processing/usecases/gender_detection.py +832 -0
- matrice_analytics/post_processing/usecases/human_activity_recognition.py +871 -0
- matrice_analytics/post_processing/usecases/intrusion_detection.py +1672 -0
- matrice_analytics/post_processing/usecases/leaf.py +821 -0
- matrice_analytics/post_processing/usecases/leaf_disease.py +840 -0
- matrice_analytics/post_processing/usecases/leak_detection.py +837 -0
- matrice_analytics/post_processing/usecases/license_plate_detection.py +1188 -0
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +1781 -0
- matrice_analytics/post_processing/usecases/litter_monitoring.py +717 -0
- matrice_analytics/post_processing/usecases/mask_detection.py +869 -0
- matrice_analytics/post_processing/usecases/natural_disaster.py +907 -0
- matrice_analytics/post_processing/usecases/parking.py +787 -0
- matrice_analytics/post_processing/usecases/parking_space_detection.py +822 -0
- matrice_analytics/post_processing/usecases/pcb_defect_detection.py +888 -0
- matrice_analytics/post_processing/usecases/pedestrian_detection.py +808 -0
- matrice_analytics/post_processing/usecases/people_counting.py +706 -0
- matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
- matrice_analytics/post_processing/usecases/people_tracking.py +1842 -0
- matrice_analytics/post_processing/usecases/pipeline_detection.py +605 -0
- matrice_analytics/post_processing/usecases/plaque_segmentation_img.py +874 -0
- matrice_analytics/post_processing/usecases/pothole_segmentation.py +915 -0
- matrice_analytics/post_processing/usecases/ppe_compliance.py +645 -0
- matrice_analytics/post_processing/usecases/price_tag_detection.py +822 -0
- matrice_analytics/post_processing/usecases/proximity_detection.py +1901 -0
- matrice_analytics/post_processing/usecases/road_lane_detection.py +623 -0
- matrice_analytics/post_processing/usecases/road_traffic_density.py +832 -0
- matrice_analytics/post_processing/usecases/road_view_segmentation.py +915 -0
- matrice_analytics/post_processing/usecases/shelf_inventory_detection.py +583 -0
- matrice_analytics/post_processing/usecases/shoplifting_detection.py +822 -0
- matrice_analytics/post_processing/usecases/shopping_cart_analysis.py +899 -0
- matrice_analytics/post_processing/usecases/skin_cancer_classification_img.py +864 -0
- matrice_analytics/post_processing/usecases/smoker_detection.py +833 -0
- matrice_analytics/post_processing/usecases/solar_panel.py +810 -0
- matrice_analytics/post_processing/usecases/suspicious_activity_detection.py +1030 -0
- matrice_analytics/post_processing/usecases/template_usecase.py +380 -0
- matrice_analytics/post_processing/usecases/theft_detection.py +648 -0
- matrice_analytics/post_processing/usecases/traffic_sign_monitoring.py +724 -0
- matrice_analytics/post_processing/usecases/underground_pipeline_defect_detection.py +775 -0
- matrice_analytics/post_processing/usecases/underwater_pollution_detection.py +842 -0
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +1029 -0
- matrice_analytics/post_processing/usecases/warehouse_object_segmentation.py +899 -0
- matrice_analytics/post_processing/usecases/waterbody_segmentation.py +923 -0
- matrice_analytics/post_processing/usecases/weapon_detection.py +771 -0
- matrice_analytics/post_processing/usecases/weld_defect_detection.py +615 -0
- matrice_analytics/post_processing/usecases/wildlife_monitoring.py +898 -0
- matrice_analytics/post_processing/usecases/windmill_maintenance.py +834 -0
- matrice_analytics/post_processing/usecases/wound_segmentation.py +856 -0
- matrice_analytics/post_processing/utils/__init__.py +150 -0
- matrice_analytics/post_processing/utils/advanced_counting_utils.py +400 -0
- matrice_analytics/post_processing/utils/advanced_helper_utils.py +317 -0
- matrice_analytics/post_processing/utils/advanced_tracking_utils.py +461 -0
- matrice_analytics/post_processing/utils/alerting_utils.py +213 -0
- matrice_analytics/post_processing/utils/category_mapping_utils.py +94 -0
- matrice_analytics/post_processing/utils/color_utils.py +592 -0
- matrice_analytics/post_processing/utils/counting_utils.py +182 -0
- matrice_analytics/post_processing/utils/filter_utils.py +261 -0
- matrice_analytics/post_processing/utils/format_utils.py +293 -0
- matrice_analytics/post_processing/utils/geometry_utils.py +300 -0
- matrice_analytics/post_processing/utils/smoothing_utils.py +358 -0
- matrice_analytics/post_processing/utils/tracking_utils.py +234 -0
- matrice_analytics/py.typed +0 -0
- matrice_analytics-0.1.60.dist-info/METADATA +481 -0
- matrice_analytics-0.1.60.dist-info/RECORD +196 -0
- matrice_analytics-0.1.60.dist-info/WHEEL +5 -0
- matrice_analytics-0.1.60.dist-info/licenses/LICENSE.txt +21 -0
- 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)
|