matrice-analytics 0.1.97__py3-none-any.whl → 0.1.124__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/post_processing/__init__.py +22 -0
- matrice_analytics/post_processing/advanced_tracker/config.py +8 -4
- matrice_analytics/post_processing/advanced_tracker/track_class_aggregator.py +128 -0
- matrice_analytics/post_processing/advanced_tracker/tracker.py +22 -1
- matrice_analytics/post_processing/config.py +17 -2
- matrice_analytics/post_processing/core/config.py +107 -1
- matrice_analytics/post_processing/face_reg/face_recognition.py +706 -73
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +25 -14
- matrice_analytics/post_processing/post_processor.py +16 -0
- matrice_analytics/post_processing/usecases/__init__.py +9 -0
- matrice_analytics/post_processing/usecases/crowdflow.py +1088 -0
- matrice_analytics/post_processing/usecases/footfall.py +170 -22
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +57 -38
- matrice_analytics/post_processing/usecases/parking_lot_analytics.py +1137 -0
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +30 -4
- matrice_analytics/post_processing/usecases/vehicle_monitoring_drone_view.py +246 -3
- matrice_analytics/post_processing/usecases/vehicle_monitoring_parking_lot.py +36 -3
- matrice_analytics/post_processing/usecases/vehicle_monitoring_wrong_way.py +1021 -0
- matrice_analytics/post_processing/utils/__init__.py +5 -0
- matrice_analytics/post_processing/utils/agnostic_nms.py +759 -0
- matrice_analytics/post_processing/utils/alert_instance_utils.py +55 -7
- matrice_analytics/post_processing/utils/business_metrics_manager_utils.py +25 -2
- matrice_analytics/post_processing/utils/incident_manager_utils.py +12 -1
- matrice_analytics/post_processing/utils/parking_analytics_tracker.py +359 -0
- matrice_analytics/post_processing/utils/wrong_way_tracker.py +670 -0
- {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/METADATA +1 -1
- {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/RECORD +30 -23
- {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/WHEEL +0 -0
- {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/top_level.txt +0 -0
|
@@ -30,6 +30,10 @@ class VehicleMonitoringConfig(BaseConfig):
|
|
|
30
30
|
smoothing_confidence_range_factor: float = 0.5
|
|
31
31
|
confidence_threshold: float = 0.6
|
|
32
32
|
|
|
33
|
+
# Class Aggregation: Configuration parameters
|
|
34
|
+
enable_class_aggregation: bool = True
|
|
35
|
+
class_aggregation_window_size: int = 30 # 30 frames ≈ 1 second at 30 FPS
|
|
36
|
+
|
|
33
37
|
#JBK_720_GATE POLYGON = [[86, 328], [844, 317], [1277, 520], [1273, 707], [125, 713]]
|
|
34
38
|
zone_config: Optional[Dict[str, List[List[float]]]] = None #field(
|
|
35
39
|
# default_factory=lambda: {
|
|
@@ -107,8 +111,24 @@ class VehicleMonitoringUseCase(BaseProcessor):
|
|
|
107
111
|
def process(self, data: Any, config: ConfigProtocol, context: Optional[ProcessingContext] = None,
|
|
108
112
|
stream_info: Optional[Dict[str, Any]] = None) -> ProcessingResult:
|
|
109
113
|
processing_start = time.time()
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
# Relaxed check: Accept VehicleMonitoringConfig OR any config with matching usecase/category
|
|
115
|
+
# This handles multiprocessing module path mismatches while maintaining type safety
|
|
116
|
+
is_valid_config = (
|
|
117
|
+
isinstance(config, VehicleMonitoringConfig) or
|
|
118
|
+
(hasattr(config, 'usecase') and config.usecase == 'vehicle_monitoring' and
|
|
119
|
+
hasattr(config, 'category') and config.category == 'traffic')
|
|
120
|
+
)
|
|
121
|
+
if not is_valid_config:
|
|
122
|
+
self.logger.error(
|
|
123
|
+
f"Config validation failed in vehicle_monitoring. "
|
|
124
|
+
f"Got type={type(config).__name__}, module={type(config).__module__}, "
|
|
125
|
+
f"usecase={getattr(config, 'usecase', 'N/A')}, category={getattr(config, 'category', 'N/A')}"
|
|
126
|
+
)
|
|
127
|
+
return self.create_error_result(
|
|
128
|
+
f"Invalid config type: expected VehicleMonitoringConfig or config with usecase='vehicle_monitoring', "
|
|
129
|
+
f"got {type(config).__name__} with usecase={getattr(config, 'usecase', 'N/A')}",
|
|
130
|
+
usecase=self.name, category=self.category, context=context
|
|
131
|
+
)
|
|
112
132
|
if context is None:
|
|
113
133
|
context = ProcessingContext()
|
|
114
134
|
|
|
@@ -158,9 +178,15 @@ class VehicleMonitoringUseCase(BaseProcessor):
|
|
|
158
178
|
from ..advanced_tracker import AdvancedTracker
|
|
159
179
|
from ..advanced_tracker.config import TrackerConfig
|
|
160
180
|
if self.tracker is None:
|
|
161
|
-
tracker_config = TrackerConfig(
|
|
181
|
+
tracker_config = TrackerConfig(
|
|
182
|
+
enable_class_aggregation=config.enable_class_aggregation,
|
|
183
|
+
class_aggregation_window_size=config.class_aggregation_window_size
|
|
184
|
+
)
|
|
162
185
|
self.tracker = AdvancedTracker(tracker_config)
|
|
163
|
-
self.logger.info(
|
|
186
|
+
self.logger.info(
|
|
187
|
+
f"Initialized AdvancedTracker for Vehicle Monitoring "
|
|
188
|
+
f"(class_aggregation={config.enable_class_aggregation})"
|
|
189
|
+
)
|
|
164
190
|
processed_data = self.tracker.update(processed_data)
|
|
165
191
|
except Exception as e:
|
|
166
192
|
self.logger.warning(f"AdvancedTracker failed: {e}")
|
|
@@ -19,6 +19,7 @@ from ..utils import (
|
|
|
19
19
|
from dataclasses import dataclass, field
|
|
20
20
|
from ..core.config import BaseConfig, AlertConfig, ZoneConfig
|
|
21
21
|
from ..utils.geometry_utils import get_bbox_center, point_in_polygon, get_bbox_bottom25_center
|
|
22
|
+
from ..utils.agnostic_nms import AgnosticNMS
|
|
22
23
|
|
|
23
24
|
@dataclass
|
|
24
25
|
class VehicleMonitoringDroneViewConfig(BaseConfig):
|
|
@@ -30,6 +31,17 @@ class VehicleMonitoringDroneViewConfig(BaseConfig):
|
|
|
30
31
|
smoothing_confidence_range_factor: float = 0.5
|
|
31
32
|
confidence_threshold: float = 0.6
|
|
32
33
|
|
|
34
|
+
# Agnostic-NMS: Configuration parameters
|
|
35
|
+
enable_nms: bool = True
|
|
36
|
+
nms_iou_threshold: float = 0.45
|
|
37
|
+
nms_class_agnostic: bool = True
|
|
38
|
+
nms_min_box_size: float = 2.0
|
|
39
|
+
nms_use_vectorized: bool = True
|
|
40
|
+
|
|
41
|
+
# Class Aggregation: Configuration parameters
|
|
42
|
+
enable_class_aggregation: bool = True
|
|
43
|
+
class_aggregation_window_size: int = 30 # 30 frames ≈ 1 second at 30 FPS
|
|
44
|
+
|
|
33
45
|
#JBK_720_GATE POLYGON = [[86, 328], [844, 317], [1277, 520], [1273, 707], [125, 713]]
|
|
34
46
|
zone_config: Optional[Dict[str, List[List[float]]]] = None #field(
|
|
35
47
|
# default_factory=lambda: {
|
|
@@ -99,6 +111,9 @@ class VehicleMonitoringDroneViewUseCase(BaseProcessor):
|
|
|
99
111
|
self._zone_current_counts = {} # zone_name -> current count in zone
|
|
100
112
|
self._zone_total_counts = {} # zone_name -> total count that have been in zone
|
|
101
113
|
|
|
114
|
+
# Agnostic-NMS: Initialize reusable NMS module
|
|
115
|
+
self._nms_module = None
|
|
116
|
+
|
|
102
117
|
def process(self, data: Any, config: ConfigProtocol, context: Optional[ProcessingContext] = None,
|
|
103
118
|
stream_info: Optional[Dict[str, Any]] = None) -> ProcessingResult:
|
|
104
119
|
processing_start = time.time()
|
|
@@ -110,18 +125,28 @@ class VehicleMonitoringDroneViewUseCase(BaseProcessor):
|
|
|
110
125
|
# Determine if zones are configured
|
|
111
126
|
has_zones = bool(config.zone_config and config.zone_config.get('zones'))
|
|
112
127
|
|
|
128
|
+
# ===== DEBUG POINT 1: RAW INPUT =====
|
|
129
|
+
self._log_detection_stats(data, "01_RAW_INPUT", show_samples=True)
|
|
130
|
+
|
|
113
131
|
# Normalize typical YOLO outputs (COCO pretrained) to internal schema
|
|
114
132
|
data = self._normalize_yolo_results(data, getattr(config, 'index_to_category', None))
|
|
115
133
|
|
|
134
|
+
# ===== DEBUG POINT 2: AFTER NORMALIZATION =====
|
|
135
|
+
self._log_detection_stats(data, "02_AFTER_NORMALIZATION", show_samples=True)
|
|
136
|
+
|
|
116
137
|
input_format = match_results_structure(data)
|
|
117
138
|
context.input_format = input_format
|
|
118
139
|
context.confidence_threshold = config.confidence_threshold
|
|
119
|
-
|
|
140
|
+
# NOTE : Confidence Threshold overwrite disabled for now
|
|
141
|
+
# config.confidence_threshold = 0.25
|
|
142
|
+
|
|
120
143
|
# param to be updated
|
|
121
144
|
|
|
122
145
|
if config.confidence_threshold is not None:
|
|
123
146
|
processed_data = filter_by_confidence(data, config.confidence_threshold)
|
|
124
147
|
self.logger.debug(f"Applied confidence filtering with threshold {config.confidence_threshold}")
|
|
148
|
+
# ===== DEBUG POINT 3: AFTER CONFIDENCE FILTER =====
|
|
149
|
+
self._log_detection_stats(processed_data, "03_AFTER_CONFIDENCE_FILTER")
|
|
125
150
|
else:
|
|
126
151
|
processed_data = data
|
|
127
152
|
self.logger.debug("Did not apply confidence filtering since no threshold provided")
|
|
@@ -129,11 +154,74 @@ class VehicleMonitoringDroneViewUseCase(BaseProcessor):
|
|
|
129
154
|
if config.index_to_category:
|
|
130
155
|
processed_data = apply_category_mapping(processed_data, config.index_to_category)
|
|
131
156
|
self.logger.debug("Applied category mapping")
|
|
157
|
+
# ===== DEBUG POINT 4: AFTER CATEGORY MAPPING =====
|
|
158
|
+
self._log_detection_stats(processed_data, "04_AFTER_CATEGORY_MAPPING")
|
|
159
|
+
|
|
160
|
+
# Agnostic-NMS: Apply NMS using reusable module with safety
|
|
161
|
+
if getattr(config, 'enable_nms', False):
|
|
162
|
+
pre_nms_count = len(processed_data)
|
|
163
|
+
|
|
164
|
+
# ===== DEBUG POINT 5: BEFORE NMS =====
|
|
165
|
+
self._log_detection_stats(processed_data, "05_BEFORE_NMS", show_samples=True)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Safety: Log pre-NMS state for debugging
|
|
169
|
+
if pre_nms_count > 0:
|
|
170
|
+
sample_det = processed_data[0]
|
|
171
|
+
self.logger.debug(
|
|
172
|
+
f"Pre-NMS sample detection keys: {list(sample_det.keys())}, "
|
|
173
|
+
f"category type: {type(sample_det.get('category')).__name__}, "
|
|
174
|
+
f"confidence type: {type(sample_det.get('confidence')).__name__}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
# Initialize NMS module if needed
|
|
179
|
+
if self._nms_module is None:
|
|
180
|
+
self._nms_module = AgnosticNMS(
|
|
181
|
+
iou_threshold=getattr(config, 'nms_iou_threshold', 0.45),
|
|
182
|
+
min_box_size=getattr(config, 'nms_min_box_size', 2.0),
|
|
183
|
+
use_vectorized=getattr(config, 'nms_use_vectorized', True)
|
|
184
|
+
)
|
|
185
|
+
self.logger.info("AgnosticNMS module initialized")
|
|
186
|
+
|
|
187
|
+
# Apply NMS
|
|
188
|
+
processed_data = self._nms_module.apply(
|
|
189
|
+
processed_data,
|
|
190
|
+
class_agnostic=getattr(config, 'nms_class_agnostic', True),
|
|
191
|
+
target_categories=self.target_categories
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
post_nms_count = len(processed_data)
|
|
195
|
+
suppressed_count = pre_nms_count - post_nms_count
|
|
196
|
+
|
|
197
|
+
# ===== DEBUG POINT 6: AFTER NMS =====
|
|
198
|
+
self._log_detection_stats(processed_data, "06_AFTER_NMS")
|
|
199
|
+
|
|
200
|
+
self.logger.info(
|
|
201
|
+
f"NMS applied successfully: {pre_nms_count} -> {post_nms_count} detections "
|
|
202
|
+
f"({suppressed_count} suppressed, {100 * suppressed_count / max(pre_nms_count, 1):.1f}%)"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
except ValueError as ve:
|
|
206
|
+
# Schema validation error - log detailed diagnostics
|
|
207
|
+
self.logger.error(f"NMS schema validation failed: {ve}")
|
|
208
|
+
self.logger.error("Continuing without NMS. Check logs above for detailed diagnostics.")
|
|
209
|
+
|
|
210
|
+
except Exception as e:
|
|
211
|
+
# Unexpected error - log full details
|
|
212
|
+
import traceback
|
|
213
|
+
self.logger.error(f"NMS failed with unexpected error: {e}")
|
|
214
|
+
self.logger.error(f"Traceback: {traceback.format_exc()}")
|
|
215
|
+
self.logger.error("Continuing without NMS.")
|
|
216
|
+
|
|
132
217
|
|
|
133
218
|
processed_data = [d for d in processed_data if d.get('category') in self.target_categories]
|
|
134
219
|
if config.target_categories:
|
|
135
220
|
processed_data = [d for d in processed_data if d.get('category') in self.target_categories]
|
|
136
221
|
self.logger.debug("Applied category filtering")
|
|
222
|
+
|
|
223
|
+
# ===== DEBUG POINT 7: AFTER TARGET CATEGORY FILTER =====
|
|
224
|
+
self._log_detection_stats(processed_data, "07_AFTER_TARGET_FILTER")
|
|
137
225
|
|
|
138
226
|
|
|
139
227
|
if config.enable_smoothing:
|
|
@@ -149,14 +237,34 @@ class VehicleMonitoringDroneViewUseCase(BaseProcessor):
|
|
|
149
237
|
self.smoothing_tracker = BBoxSmoothingTracker(smoothing_config)
|
|
150
238
|
processed_data = bbox_smoothing(processed_data, self.smoothing_tracker.config, self.smoothing_tracker)
|
|
151
239
|
|
|
240
|
+
# ===== DEBUG POINT 8: AFTER SMOOTHING =====
|
|
241
|
+
self._log_detection_stats(processed_data, "08_AFTER_SMOOTHING")
|
|
242
|
+
|
|
152
243
|
try:
|
|
153
244
|
from ..advanced_tracker import AdvancedTracker
|
|
154
245
|
from ..advanced_tracker.config import TrackerConfig
|
|
155
246
|
if self.tracker is None:
|
|
156
|
-
tracker_config = TrackerConfig(
|
|
247
|
+
tracker_config = TrackerConfig(
|
|
248
|
+
# CLASS AGGREGATION: Map from use case config
|
|
249
|
+
enable_class_aggregation=config.enable_class_aggregation,
|
|
250
|
+
class_aggregation_window_size=config.class_aggregation_window_size
|
|
251
|
+
)
|
|
157
252
|
self.tracker = AdvancedTracker(tracker_config)
|
|
158
253
|
self.logger.info("Initialized AdvancedTracker for Vehicle Monitoring Drone View Use Case")
|
|
254
|
+
|
|
255
|
+
if config.enable_class_aggregation:
|
|
256
|
+
self.logger.info(
|
|
257
|
+
f"AdvancedTracker initialized with class aggregation "
|
|
258
|
+
f"(window_size={config.class_aggregation_window_size})"
|
|
259
|
+
)
|
|
260
|
+
else:
|
|
261
|
+
self.logger.info("AdvancedTracker initialized without class aggregation")
|
|
262
|
+
|
|
159
263
|
processed_data = self.tracker.update(processed_data)
|
|
264
|
+
|
|
265
|
+
# ===== DEBUG POINT 9: AFTER TRACKING =====
|
|
266
|
+
self._log_detection_stats(processed_data, "09_AFTER_TRACKING")
|
|
267
|
+
|
|
160
268
|
except Exception as e:
|
|
161
269
|
self.logger.warning(f"AdvancedTracker failed: {e}")
|
|
162
270
|
|
|
@@ -1004,4 +1112,139 @@ class VehicleMonitoringDroneViewUseCase(BaseProcessor):
|
|
|
1004
1112
|
return self._format_timestamp(self._tracking_start_time)
|
|
1005
1113
|
|
|
1006
1114
|
def _set_tracking_start_time(self) -> None:
|
|
1007
|
-
self._tracking_start_time = time.time()
|
|
1115
|
+
self._tracking_start_time = time.time()
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
def _log_detection_stats(
|
|
1119
|
+
self,
|
|
1120
|
+
data: Any,
|
|
1121
|
+
stage_name: str,
|
|
1122
|
+
show_samples: bool = False
|
|
1123
|
+
) -> None:
|
|
1124
|
+
"""
|
|
1125
|
+
Log detailed detection statistics at any pipeline stage with frame identification.
|
|
1126
|
+
|
|
1127
|
+
Args:
|
|
1128
|
+
data: Detection data (list or dict format)
|
|
1129
|
+
stage_name: Name of the pipeline stage for identification
|
|
1130
|
+
show_samples: If True, show sample detection structure
|
|
1131
|
+
"""
|
|
1132
|
+
if not getattr(self, '_debug_detection_flow', True):
|
|
1133
|
+
return
|
|
1134
|
+
|
|
1135
|
+
# Use existing frame counter
|
|
1136
|
+
frame_id = self._total_frame_counter
|
|
1137
|
+
timestamp_str = datetime.now(timezone.utc).strftime('%H:%M:%S.%f')[:-3]
|
|
1138
|
+
|
|
1139
|
+
# Create unique log identifier for grep
|
|
1140
|
+
log_id = f"FRAME_{frame_id:06d}"
|
|
1141
|
+
|
|
1142
|
+
separator = "=" * 80
|
|
1143
|
+
print(f"\n{separator}")
|
|
1144
|
+
print(f"[DETECTION_STATS] {log_id} | Time: {timestamp_str} | Stage: {stage_name}")
|
|
1145
|
+
print(separator)
|
|
1146
|
+
|
|
1147
|
+
# Handle different data formats
|
|
1148
|
+
detections = []
|
|
1149
|
+
if isinstance(data, list):
|
|
1150
|
+
detections = data
|
|
1151
|
+
elif isinstance(data, dict):
|
|
1152
|
+
# Frame-based format
|
|
1153
|
+
for fid, frame_dets in data.items():
|
|
1154
|
+
if isinstance(frame_dets, list):
|
|
1155
|
+
detections.extend(frame_dets)
|
|
1156
|
+
|
|
1157
|
+
if not detections:
|
|
1158
|
+
print(f" Frame: #{frame_id} | Total Detections: 0")
|
|
1159
|
+
print(separator)
|
|
1160
|
+
return
|
|
1161
|
+
|
|
1162
|
+
# Calculate statistics
|
|
1163
|
+
total_count = len(detections)
|
|
1164
|
+
|
|
1165
|
+
# Count by category
|
|
1166
|
+
category_counts = {}
|
|
1167
|
+
confidence_sum = {}
|
|
1168
|
+
confidence_min = {}
|
|
1169
|
+
confidence_max = {}
|
|
1170
|
+
bbox_format_count = {"x1/y1/x2/y2": 0, "xmin/ymin/xmax/ymax": 0, "other": 0}
|
|
1171
|
+
has_track_id = 0
|
|
1172
|
+
|
|
1173
|
+
for det in detections:
|
|
1174
|
+
if not isinstance(det, dict):
|
|
1175
|
+
continue
|
|
1176
|
+
|
|
1177
|
+
# Category counting
|
|
1178
|
+
cat = det.get('category', 'UNKNOWN')
|
|
1179
|
+
category_counts[cat] = category_counts.get(cat, 0) + 1
|
|
1180
|
+
|
|
1181
|
+
# Confidence stats
|
|
1182
|
+
conf = det.get('confidence', 0.0)
|
|
1183
|
+
if cat not in confidence_sum:
|
|
1184
|
+
confidence_sum[cat] = 0.0
|
|
1185
|
+
confidence_min[cat] = conf
|
|
1186
|
+
confidence_max[cat] = conf
|
|
1187
|
+
confidence_sum[cat] += conf
|
|
1188
|
+
confidence_min[cat] = min(confidence_min[cat], conf)
|
|
1189
|
+
confidence_max[cat] = max(confidence_max[cat], conf)
|
|
1190
|
+
|
|
1191
|
+
# BBox format detection
|
|
1192
|
+
bbox = det.get('bounding_box', det.get('bbox', {}))
|
|
1193
|
+
if isinstance(bbox, dict):
|
|
1194
|
+
if 'x1' in bbox and 'y1' in bbox:
|
|
1195
|
+
bbox_format_count["x1/y1/x2/y2"] += 1
|
|
1196
|
+
elif 'xmin' in bbox and 'ymin' in bbox:
|
|
1197
|
+
bbox_format_count["xmin/ymin/xmax/ymax"] += 1
|
|
1198
|
+
else:
|
|
1199
|
+
bbox_format_count["other"] += 1
|
|
1200
|
+
|
|
1201
|
+
# Track ID presence
|
|
1202
|
+
if det.get('track_id') is not None:
|
|
1203
|
+
has_track_id += 1
|
|
1204
|
+
|
|
1205
|
+
# Print summary
|
|
1206
|
+
print(f" Frame: #{frame_id} | Total Detections: {total_count}")
|
|
1207
|
+
|
|
1208
|
+
if has_track_id > 0:
|
|
1209
|
+
print(f" Detections with Track ID: {has_track_id} ({100*has_track_id/total_count:.1f}%)")
|
|
1210
|
+
|
|
1211
|
+
print(f"\n Category Distribution:")
|
|
1212
|
+
|
|
1213
|
+
# Sort categories by count (descending)
|
|
1214
|
+
sorted_cats = sorted(category_counts.items(), key=lambda x: x[1], reverse=True)
|
|
1215
|
+
|
|
1216
|
+
for cat, count in sorted_cats:
|
|
1217
|
+
percentage = (count / total_count) * 100
|
|
1218
|
+
avg_conf = confidence_sum[cat] / count
|
|
1219
|
+
min_conf = confidence_min[cat]
|
|
1220
|
+
max_conf = confidence_max[cat]
|
|
1221
|
+
|
|
1222
|
+
print(f" [{cat:20s}] Count: {count:4d} ({percentage:5.1f}%) | "
|
|
1223
|
+
f"Conf: avg={avg_conf:.3f}, min={min_conf:.3f}, max={max_conf:.3f}")
|
|
1224
|
+
|
|
1225
|
+
# Print bbox format distribution
|
|
1226
|
+
print(f"\n BBox Format Distribution:")
|
|
1227
|
+
for fmt, count in bbox_format_count.items():
|
|
1228
|
+
if count > 0:
|
|
1229
|
+
percentage = (count / total_count) * 100
|
|
1230
|
+
print(f" {fmt:25s}: {count:4d} ({percentage:5.1f}%)")
|
|
1231
|
+
|
|
1232
|
+
# Show sample detection structure if requested
|
|
1233
|
+
if show_samples and detections:
|
|
1234
|
+
print(f"\n Sample Detection Structure:")
|
|
1235
|
+
sample = detections[0]
|
|
1236
|
+
print(f" Keys: {list(sample.keys())}")
|
|
1237
|
+
print(f" Category: {sample.get('category')} (type: {type(sample.get('category')).__name__})")
|
|
1238
|
+
print(f" Confidence: {sample.get('confidence')} (type: {type(sample.get('confidence')).__name__})")
|
|
1239
|
+
|
|
1240
|
+
if sample.get('track_id') is not None:
|
|
1241
|
+
print(f" Track ID: {sample.get('track_id')} (type: {type(sample.get('track_id')).__name__})")
|
|
1242
|
+
|
|
1243
|
+
bbox = sample.get('bounding_box', sample.get('bbox', {}))
|
|
1244
|
+
if isinstance(bbox, dict):
|
|
1245
|
+
print(f" BBox Keys: {list(bbox.keys())}")
|
|
1246
|
+
if bbox:
|
|
1247
|
+
first_key = list(bbox.keys())[0]
|
|
1248
|
+
print(f" BBox Coord Type: {type(bbox[first_key]).__name__}")
|
|
1249
|
+
|
|
1250
|
+
print(separator)
|
|
@@ -30,6 +30,10 @@ class VehicleMonitoringParkingLotConfig(BaseConfig):
|
|
|
30
30
|
smoothing_confidence_range_factor: float = 0.5
|
|
31
31
|
confidence_threshold: float = 0.6
|
|
32
32
|
|
|
33
|
+
# Class Aggregation: Configuration parameters
|
|
34
|
+
enable_class_aggregation: bool = True
|
|
35
|
+
class_aggregation_window_size: int = 30 # 30 frames ≈ 1 second at 30 FPS
|
|
36
|
+
|
|
33
37
|
#JBK_720_GATE POLYGON = [[86, 328], [844, 317], [1277, 520], [1273, 707], [125, 713]]
|
|
34
38
|
zone_config: Optional[Dict[str, List[List[float]]]] = None #field(
|
|
35
39
|
# default_factory=lambda: {
|
|
@@ -106,8 +110,24 @@ class VehicleMonitoringParkingLotUseCase(BaseProcessor):
|
|
|
106
110
|
def process(self, data: Any, config: ConfigProtocol, context: Optional[ProcessingContext] = None,
|
|
107
111
|
stream_info: Optional[Dict[str, Any]] = None) -> ProcessingResult:
|
|
108
112
|
processing_start = time.time()
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
# Relaxed check: Accept VehicleMonitoringParkingLotConfig OR any config with matching usecase/category
|
|
114
|
+
# This handles multiprocessing module path mismatches while maintaining type safety
|
|
115
|
+
is_valid_config = (
|
|
116
|
+
isinstance(config, VehicleMonitoringParkingLotConfig) or
|
|
117
|
+
(hasattr(config, 'usecase') and config.usecase == 'vehicle_monitoring_parking_lot' and
|
|
118
|
+
hasattr(config, 'category') and config.category == 'traffic')
|
|
119
|
+
)
|
|
120
|
+
if not is_valid_config:
|
|
121
|
+
self.logger.error(
|
|
122
|
+
f"Config validation failed in vehicle_monitoring_parking_lot. "
|
|
123
|
+
f"Got type={type(config).__name__}, module={type(config).__module__}, "
|
|
124
|
+
f"usecase={getattr(config, 'usecase', 'N/A')}, category={getattr(config, 'category', 'N/A')}"
|
|
125
|
+
)
|
|
126
|
+
return self.create_error_result(
|
|
127
|
+
f"Invalid config type: expected VehicleMonitoringParkingLotConfig or config with usecase='vehicle_monitoring_parking_lot', "
|
|
128
|
+
f"got {type(config).__name__} with usecase={getattr(config, 'usecase', 'N/A')}",
|
|
129
|
+
usecase=self.name, category=self.category, context=context
|
|
130
|
+
)
|
|
111
131
|
if context is None:
|
|
112
132
|
context = ProcessingContext()
|
|
113
133
|
|
|
@@ -157,9 +177,22 @@ class VehicleMonitoringParkingLotUseCase(BaseProcessor):
|
|
|
157
177
|
from ..advanced_tracker import AdvancedTracker
|
|
158
178
|
from ..advanced_tracker.config import TrackerConfig
|
|
159
179
|
if self.tracker is None:
|
|
160
|
-
tracker_config = TrackerConfig(
|
|
180
|
+
tracker_config = TrackerConfig(
|
|
181
|
+
# CLASS AGGREGATION: Map from use case config
|
|
182
|
+
enable_class_aggregation=config.enable_class_aggregation,
|
|
183
|
+
class_aggregation_window_size=config.class_aggregation_window_size
|
|
184
|
+
)
|
|
161
185
|
self.tracker = AdvancedTracker(tracker_config)
|
|
162
186
|
self.logger.info("Initialized AdvancedTracker for Vehicle Monitoring Parking Lot use case")
|
|
187
|
+
|
|
188
|
+
if config.enable_class_aggregation:
|
|
189
|
+
self.logger.info(
|
|
190
|
+
f"AdvancedTracker initialized with class aggregation "
|
|
191
|
+
f"(window_size={config.class_aggregation_window_size})"
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
self.logger.info("AdvancedTracker initialized without class aggregation")
|
|
195
|
+
|
|
163
196
|
processed_data = self.tracker.update(processed_data)
|
|
164
197
|
except Exception as e:
|
|
165
198
|
self.logger.warning(f"AdvancedTracker failed: {e}")
|