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.
Files changed (30) hide show
  1. matrice_analytics/post_processing/__init__.py +22 -0
  2. matrice_analytics/post_processing/advanced_tracker/config.py +8 -4
  3. matrice_analytics/post_processing/advanced_tracker/track_class_aggregator.py +128 -0
  4. matrice_analytics/post_processing/advanced_tracker/tracker.py +22 -1
  5. matrice_analytics/post_processing/config.py +17 -2
  6. matrice_analytics/post_processing/core/config.py +107 -1
  7. matrice_analytics/post_processing/face_reg/face_recognition.py +706 -73
  8. matrice_analytics/post_processing/face_reg/people_activity_logging.py +25 -14
  9. matrice_analytics/post_processing/post_processor.py +16 -0
  10. matrice_analytics/post_processing/usecases/__init__.py +9 -0
  11. matrice_analytics/post_processing/usecases/crowdflow.py +1088 -0
  12. matrice_analytics/post_processing/usecases/footfall.py +170 -22
  13. matrice_analytics/post_processing/usecases/license_plate_monitoring.py +57 -38
  14. matrice_analytics/post_processing/usecases/parking_lot_analytics.py +1137 -0
  15. matrice_analytics/post_processing/usecases/vehicle_monitoring.py +30 -4
  16. matrice_analytics/post_processing/usecases/vehicle_monitoring_drone_view.py +246 -3
  17. matrice_analytics/post_processing/usecases/vehicle_monitoring_parking_lot.py +36 -3
  18. matrice_analytics/post_processing/usecases/vehicle_monitoring_wrong_way.py +1021 -0
  19. matrice_analytics/post_processing/utils/__init__.py +5 -0
  20. matrice_analytics/post_processing/utils/agnostic_nms.py +759 -0
  21. matrice_analytics/post_processing/utils/alert_instance_utils.py +55 -7
  22. matrice_analytics/post_processing/utils/business_metrics_manager_utils.py +25 -2
  23. matrice_analytics/post_processing/utils/incident_manager_utils.py +12 -1
  24. matrice_analytics/post_processing/utils/parking_analytics_tracker.py +359 -0
  25. matrice_analytics/post_processing/utils/wrong_way_tracker.py +670 -0
  26. {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/METADATA +1 -1
  27. {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/RECORD +30 -23
  28. {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/WHEEL +0 -0
  29. {matrice_analytics-0.1.97.dist-info → matrice_analytics-0.1.124.dist-info}/licenses/LICENSE.txt +0 -0
  30. {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
- if not isinstance(config, VehicleMonitoringConfig):
111
- return self.create_error_result("Invalid config type", usecase=self.name, category=self.category, context=context)
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("Initialized AdvancedTracker for Vehicle Monitoring")
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
- config.confidence_threshold = 0.25
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
- if not isinstance(config, VehicleMonitoringParkingLotConfig):
110
- return self.create_error_result("Invalid config type", usecase=self.name, category=self.category, context=context)
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}")