matrice 1.0.99140__py3-none-any.whl → 1.0.99142__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/deploy/utils/post_processing/config.py +4 -0
- matrice/deploy/utils/post_processing/usecases/concrete_crack_detection.py +49 -59
- matrice/deploy/utils/post_processing/usecases/road_lane_detection.py +321 -329
- matrice/deploy/utils/post_processing/usecases/shelf_inventory_detection.py +310 -345
- {matrice-1.0.99140.dist-info → matrice-1.0.99142.dist-info}/METADATA +1 -1
- {matrice-1.0.99140.dist-info → matrice-1.0.99142.dist-info}/RECORD +9 -9
- {matrice-1.0.99140.dist-info → matrice-1.0.99142.dist-info}/WHEEL +0 -0
- {matrice-1.0.99140.dist-info → matrice-1.0.99142.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice-1.0.99140.dist-info → matrice-1.0.99142.dist-info}/top_level.txt +0 -0
@@ -26,6 +26,8 @@ APP_NAME_TO_USECASE = {
|
|
26
26
|
"weld_defect_detection" : "weld_defect_detection",
|
27
27
|
"fruit_monitoring" : "fruit_monitoring",
|
28
28
|
"concrete_crack_detection": "concrete_crack_detection",
|
29
|
+
"lane_detection" : "lane_detection",
|
30
|
+
"shelf_inventory" :"shelf_inventory",
|
29
31
|
}
|
30
32
|
|
31
33
|
APP_NAME_TO_CATEGORY = {
|
@@ -56,6 +58,8 @@ APP_NAME_TO_CATEGORY = {
|
|
56
58
|
"weld_defect_detection" : "weld",
|
57
59
|
"fruit_monitoring" : "agriculture",
|
58
60
|
"concrete_crack_detection": "general",
|
61
|
+
"lane_detection" : "traffic",
|
62
|
+
"shelf_inventory" : "retail",
|
59
63
|
}
|
60
64
|
|
61
65
|
def get_usecase_from_app_name(app_name: str) -> str:
|
@@ -22,7 +22,7 @@ from ..core.config import BaseConfig, AlertConfig, ZoneConfig
|
|
22
22
|
|
23
23
|
@dataclass
|
24
24
|
class ConcreteCrackConfig(BaseConfig):
|
25
|
-
"""Configuration for
|
25
|
+
"""Configuration for Concrete Crack detection use case."""
|
26
26
|
# Smoothing configuration
|
27
27
|
enable_smoothing: bool = True
|
28
28
|
smoothing_algorithm: str = "observability" # "window" or "observability"
|
@@ -31,14 +31,14 @@ class ConcreteCrackConfig(BaseConfig):
|
|
31
31
|
smoothing_confidence_range_factor: float = 0.5
|
32
32
|
|
33
33
|
#confidence thresholds
|
34
|
-
confidence_threshold: float = 0.
|
34
|
+
confidence_threshold: float = 0.6
|
35
35
|
|
36
36
|
usecase_categories: List[str] = field(
|
37
|
-
default_factory=lambda:
|
37
|
+
default_factory=lambda: ["Cracks"]
|
38
38
|
)
|
39
39
|
|
40
40
|
target_categories: List[str] = field(
|
41
|
-
default_factory=lambda: [
|
41
|
+
default_factory=lambda: ["Cracks"]
|
42
42
|
)
|
43
43
|
|
44
44
|
alert_config: Optional[AlertConfig] = None
|
@@ -55,23 +55,23 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
55
55
|
CATEGORY_DISPLAY = {
|
56
56
|
"Cracks": "Cracks",
|
57
57
|
}
|
58
|
-
|
58
|
+
|
59
|
+
|
59
60
|
def __init__(self):
|
60
61
|
super().__init__("concrete_crack_detection")
|
61
62
|
self.category = "general"
|
62
63
|
|
63
64
|
self.CASE_TYPE: Optional[str] = 'concrete_crack_detection'
|
64
|
-
self.CASE_VERSION: Optional[str] = '1.
|
65
|
-
|
65
|
+
self.CASE_VERSION: Optional[str] = '1.2'
|
66
66
|
# List of categories to track
|
67
|
-
self.target_categories =
|
67
|
+
self.target_categories = ["Cracks"]
|
68
|
+
|
68
69
|
|
69
70
|
# Initialize smoothing tracker
|
70
71
|
self.smoothing_tracker = None
|
71
72
|
|
72
73
|
# Initialize advanced tracker (will be created on first use)
|
73
74
|
self.tracker = None
|
74
|
-
|
75
75
|
# Initialize tracking state variables
|
76
76
|
self._total_frame_counter = 0
|
77
77
|
self._global_frame_offset = 0
|
@@ -88,6 +88,7 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
88
88
|
self._ascending_alert_list: List[int] = []
|
89
89
|
self.current_incident_end_timestamp: str = "N/A"
|
90
90
|
|
91
|
+
|
91
92
|
def process(self, data: Any, config: ConfigProtocol, context: Optional[ProcessingContext] = None,
|
92
93
|
stream_info: Optional[Dict[str, Any]] = None) -> ProcessingResult:
|
93
94
|
"""
|
@@ -107,8 +108,6 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
107
108
|
input_format = match_results_structure(data)
|
108
109
|
context.input_format = input_format
|
109
110
|
context.confidence_threshold = config.confidence_threshold
|
110
|
-
|
111
|
-
|
112
111
|
|
113
112
|
if config.confidence_threshold is not None:
|
114
113
|
processed_data = filter_by_confidence(data, config.confidence_threshold)
|
@@ -117,19 +116,15 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
117
116
|
processed_data = data
|
118
117
|
|
119
118
|
self.logger.debug(f"Did not apply confidence filtering with threshold since nothing was provided")
|
120
|
-
|
121
|
-
print(processed_data)
|
119
|
+
|
122
120
|
# Step 2: Apply category mapping if provided
|
123
121
|
if config.index_to_category:
|
124
122
|
processed_data = apply_category_mapping(processed_data, config.index_to_category)
|
125
123
|
self.logger.debug("Applied category mapping")
|
126
|
-
|
127
|
-
print(processed_data)
|
124
|
+
|
128
125
|
if config.target_categories:
|
129
126
|
processed_data = [d for d in processed_data if d.get('category') in self.target_categories]
|
130
127
|
self.logger.debug(f"Applied category filtering")
|
131
|
-
print("---------------------processed_3-----------------------------------")
|
132
|
-
print(processed_data)
|
133
128
|
|
134
129
|
# Apply bbox smoothing if enabled
|
135
130
|
if config.enable_smoothing:
|
@@ -143,11 +138,14 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
143
138
|
enable_smoothing=True
|
144
139
|
)
|
145
140
|
self.smoothing_tracker = BBoxSmoothingTracker(smoothing_config)
|
146
|
-
print("---------------------processed_4-----------------------------------")
|
147
141
|
processed_data = bbox_smoothing(processed_data, self.smoothing_tracker.config, self.smoothing_tracker)
|
148
|
-
|
142
|
+
|
149
143
|
# Advanced tracking (BYTETracker-like)
|
150
144
|
try:
|
145
|
+
from ..advanced_tracker import AdvancedTracker
|
146
|
+
from ..advanced_tracker.config import TrackerConfig
|
147
|
+
|
148
|
+
# Create tracker instance if it doesn't exist (preserves state across frames)
|
151
149
|
if self.tracker is None:
|
152
150
|
# Configure tracker thresholds based on the use-case confidence threshold so that
|
153
151
|
# low-confidence detections (e.g. < 0.7) can still be initialised as tracks when
|
@@ -168,9 +166,10 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
168
166
|
f"low={tracker_config.track_low_thresh}, "
|
169
167
|
f"new={tracker_config.new_track_thresh}"
|
170
168
|
)
|
169
|
+
|
170
|
+
# The tracker expects the data in the same format as input
|
171
|
+
# It will add track_id and frame_id to each detection
|
171
172
|
processed_data = self.tracker.update(processed_data)
|
172
|
-
print("---------------------processed_5-----------------------------------")
|
173
|
-
print(processed_data)
|
174
173
|
|
175
174
|
except Exception as e:
|
176
175
|
# If advanced tracker fails, fallback to unsmoothed detections
|
@@ -198,16 +197,14 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
198
197
|
# Add total unique counts after tracking using only local state
|
199
198
|
total_counts = self.get_total_counts()
|
200
199
|
counting_summary['total_counts'] = total_counts
|
201
|
-
|
202
|
-
print(counting_summary)
|
200
|
+
|
203
201
|
alerts = self._check_alerts(counting_summary, frame_number, config)
|
204
202
|
predictions = self._extract_predictions(processed_data)
|
205
203
|
|
206
204
|
# Step: Generate structured incidents, tracking stats and business analytics with frame-based keys
|
207
205
|
incidents_list = self._generate_incidents(counting_summary, alerts, config, frame_number, stream_info)
|
208
206
|
tracking_stats_list = self._generate_tracking_stats(counting_summary, alerts, config, frame_number, stream_info)
|
209
|
-
|
210
|
-
business_analytics_list = []
|
207
|
+
business_analytics_list = self._generate_business_analytics(counting_summary, alerts, config, stream_info, is_empty=True)
|
211
208
|
summary_list = self._generate_summary(counting_summary, incidents_list, tracking_stats_list, business_analytics_list, alerts)
|
212
209
|
|
213
210
|
# Extract frame-based dictionaries from the lists
|
@@ -222,8 +219,8 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
222
219
|
"alerts": alerts,
|
223
220
|
"human_text": summary}
|
224
221
|
}
|
225
|
-
|
226
|
-
|
222
|
+
|
223
|
+
|
227
224
|
context.mark_completed()
|
228
225
|
|
229
226
|
# Build result object following the new pattern
|
@@ -285,8 +282,8 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
285
282
|
"threshold_level": threshold,
|
286
283
|
"ascending": get_trend(self._ascending_alert_list, lookback=900, threshold=0.8),
|
287
284
|
"settings": {t: v for t, v in zip(getattr(config.alert_config, 'alert_type', ['Default']) if hasattr(config.alert_config, 'alert_type') else ['Default'],
|
288
|
-
|
289
|
-
|
285
|
+
getattr(config.alert_config, 'alert_value', ['JSON']) if hasattr(config.alert_config, 'alert_value') else ['JSON'])
|
286
|
+
}
|
290
287
|
})
|
291
288
|
elif category in summary.get("per_category_count", {}):
|
292
289
|
count = summary.get("per_category_count", {})[category]
|
@@ -298,15 +295,15 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
298
295
|
"threshold_level": threshold,
|
299
296
|
"ascending": get_trend(self._ascending_alert_list, lookback=900, threshold=0.8),
|
300
297
|
"settings": {t: v for t, v in zip(getattr(config.alert_config, 'alert_type', ['Default']) if hasattr(config.alert_config, 'alert_type') else ['Default'],
|
301
|
-
|
302
|
-
|
298
|
+
getattr(config.alert_config, 'alert_value', ['JSON']) if hasattr(config.alert_config, 'alert_value') else ['JSON'])
|
299
|
+
}
|
303
300
|
})
|
304
301
|
else:
|
305
302
|
pass
|
306
303
|
return alerts
|
307
304
|
|
308
305
|
def _generate_incidents(self, counting_summary: Dict, alerts: List, config: ConcreteCrackConfig,
|
309
|
-
|
306
|
+
frame_number: Optional[int] = None, stream_info: Optional[Dict[str, Any]] = None) -> List[
|
310
307
|
Dict]:
|
311
308
|
"""Generate structured incidents for the output format with frame-based keys."""
|
312
309
|
|
@@ -364,12 +361,12 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
364
361
|
intensity = min(10.0, total_detections / 3.0)
|
365
362
|
self._ascending_alert_list.append(0)
|
366
363
|
|
367
|
-
|
364
|
+
# Generate human text in new format
|
368
365
|
human_text_lines = [f"INCIDENTS DETECTED @ {current_timestamp}:"]
|
369
366
|
human_text_lines.append(f"\tSeverity Level: {(self.CASE_TYPE,level)}")
|
370
367
|
human_text = "\n".join(human_text_lines)
|
371
368
|
|
372
|
-
alert_settings
|
369
|
+
alert_settings=[]
|
373
370
|
if config.alert_config and hasattr(config.alert_config, 'alert_type'):
|
374
371
|
alert_settings.append({
|
375
372
|
"alert_type": getattr(config.alert_config, 'alert_type', ['Default']) if hasattr(config.alert_config, 'alert_type') else ['Default'],
|
@@ -382,17 +379,16 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
382
379
|
})
|
383
380
|
|
384
381
|
event= self.create_incident(incident_id=self.CASE_TYPE+'_'+str(frame_number), incident_type=self.CASE_TYPE,
|
385
|
-
|
386
|
-
|
387
|
-
|
382
|
+
severity_level=level, human_text=human_text, camera_info=camera_info, alerts=alerts, alert_settings=alert_settings,
|
383
|
+
start_time=start_timestamp, end_time=self.current_incident_end_timestamp,
|
384
|
+
level_settings= {"low": 1, "medium": 3, "significant":4, "critical": 7})
|
388
385
|
incidents.append(event)
|
389
386
|
|
390
387
|
else:
|
391
388
|
self._ascending_alert_list.append(0)
|
392
389
|
incidents.append({})
|
393
|
-
|
390
|
+
|
394
391
|
return incidents
|
395
|
-
|
396
392
|
def _generate_tracking_stats(
|
397
393
|
self,
|
398
394
|
counting_summary: Dict,
|
@@ -430,8 +426,7 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
430
426
|
"category": cat,
|
431
427
|
"count": count
|
432
428
|
})
|
433
|
-
|
434
|
-
print(total_counts)
|
429
|
+
|
435
430
|
# Build current_counts array in expected format
|
436
431
|
current_counts = []
|
437
432
|
for cat, count in per_category_count.items():
|
@@ -440,8 +435,7 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
440
435
|
"category": cat,
|
441
436
|
"count": count
|
442
437
|
})
|
443
|
-
|
444
|
-
print(current_counts)
|
438
|
+
|
445
439
|
# Prepare detections without confidence scores (as per eg.json)
|
446
440
|
detections = []
|
447
441
|
for detection in counting_summary.get("detections", []):
|
@@ -493,9 +487,7 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
493
487
|
human_text_lines.append("Alerts: None")
|
494
488
|
|
495
489
|
human_text = "\n".join(human_text_lines)
|
496
|
-
|
497
|
-
print(human_text)
|
498
|
-
reset_settings = [
|
490
|
+
reset_settings=[
|
499
491
|
{
|
500
492
|
"interval_type": "daily",
|
501
493
|
"reset_time": {
|
@@ -506,14 +498,14 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
506
498
|
]
|
507
499
|
|
508
500
|
tracking_stat=self.create_tracking_stats(total_counts=total_counts, current_counts=current_counts,
|
509
|
-
|
510
|
-
|
511
|
-
|
501
|
+
detections=detections, human_text=human_text, camera_info=camera_info, alerts=alerts, alert_settings=alert_settings,
|
502
|
+
reset_settings=reset_settings, start_time=high_precision_start_timestamp ,
|
503
|
+
reset_time=high_precision_reset_timestamp)
|
512
504
|
|
513
505
|
tracking_stats.append(tracking_stat)
|
514
506
|
return tracking_stats
|
515
507
|
|
516
|
-
def _generate_business_analytics(self, counting_summary: Dict,
|
508
|
+
def _generate_business_analytics(self, counting_summary: Dict, alerts:Any, config: ConcreteCrackConfig, stream_info: Optional[Dict[str, Any]] = None, is_empty=False) -> List[Dict]:
|
517
509
|
"""Generate standardized business analytics for the agg_summary structure."""
|
518
510
|
if is_empty:
|
519
511
|
return []
|
@@ -596,6 +588,12 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
596
588
|
Return total unique track_id count for each category.
|
597
589
|
"""
|
598
590
|
return {cat: len(ids) for cat, ids in getattr(self, '_per_category_total_track_ids', {}).items()}
|
591
|
+
|
592
|
+
|
593
|
+
def _format_timestamp_for_stream(self, timestamp: float) -> str:
|
594
|
+
"""Format timestamp for streams (YYYY:MM:DD HH:MM:SS format)."""
|
595
|
+
dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
596
|
+
return dt.strftime('%Y:%m:%d %H:%M:%S')
|
599
597
|
|
600
598
|
def _format_timestamp_for_video(self, timestamp: float) -> str:
|
601
599
|
"""Format timestamp for video chunks (HH:MM:SS.ms format)."""
|
@@ -604,11 +602,6 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
604
602
|
seconds = round(float(timestamp % 60),2)
|
605
603
|
return f"{hours:02d}:{minutes:02d}:{seconds:.1f}"
|
606
604
|
|
607
|
-
def _format_timestamp_for_stream(self, timestamp: float) -> str:
|
608
|
-
"""Format timestamp for streams (YYYY:MM:DD HH:MM:SS format)."""
|
609
|
-
dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
610
|
-
return dt.strftime('%Y:%m:%d %H:%M:%S')
|
611
|
-
|
612
605
|
def _get_current_timestamp_str(self, stream_info: Optional[Dict[str, Any]], precision=False, frame_id: Optional[str]=None) -> str:
|
613
606
|
"""Get formatted current timestamp based on stream type."""
|
614
607
|
if not stream_info:
|
@@ -684,6 +677,7 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
684
677
|
dt = dt.replace(minute=0, second=0, microsecond=0)
|
685
678
|
return dt.strftime('%Y:%m:%d %H:%M:%S')
|
686
679
|
|
680
|
+
|
687
681
|
def _count_categories(self, detections: list, config: ConcreteCrackConfig) -> dict:
|
688
682
|
"""
|
689
683
|
Count the number of detections per category and return a summary dict.
|
@@ -695,10 +689,6 @@ class ConcreteCrackUseCase(BaseProcessor):
|
|
695
689
|
cat = det.get('category', 'unknown')
|
696
690
|
counts[cat] = counts.get(cat, 0) + 1
|
697
691
|
# Each detection dict will now include 'track_id' (and possibly 'frame_id')
|
698
|
-
print("---------------------------------COUNTS-------------------------------------")
|
699
|
-
print(counts)
|
700
|
-
print("---------------------------------Detections-------------------------------------")
|
701
|
-
print(detections)
|
702
692
|
return {
|
703
693
|
"total_count": sum(counts.values()),
|
704
694
|
"per_category_count": counts,
|