matrice-analytics 0.1.89__py3-none-any.whl → 0.1.96__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 +8 -2
- matrice_analytics/post_processing/config.py +2 -0
- matrice_analytics/post_processing/core/config.py +40 -3
- matrice_analytics/post_processing/face_reg/face_recognition.py +146 -14
- matrice_analytics/post_processing/face_reg/face_recognition_client.py +116 -4
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +19 -0
- matrice_analytics/post_processing/post_processor.py +4 -0
- matrice_analytics/post_processing/usecases/__init__.py +4 -1
- matrice_analytics/post_processing/usecases/advanced_customer_service.py +5 -2
- matrice_analytics/post_processing/usecases/color_detection.py +1 -0
- matrice_analytics/post_processing/usecases/fire_detection.py +94 -14
- matrice_analytics/post_processing/usecases/footfall.py +750 -0
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +91 -1
- matrice_analytics/post_processing/usecases/people_counting.py +55 -22
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +1 -0
- matrice_analytics/post_processing/usecases/weapon_detection.py +2 -1
- matrice_analytics/post_processing/utils/alert_instance_utils.py +94 -26
- matrice_analytics/post_processing/utils/business_metrics_manager_utils.py +97 -4
- matrice_analytics/post_processing/utils/incident_manager_utils.py +103 -6
- {matrice_analytics-0.1.89.dist-info → matrice_analytics-0.1.96.dist-info}/METADATA +1 -1
- {matrice_analytics-0.1.89.dist-info → matrice_analytics-0.1.96.dist-info}/RECORD +24 -23
- {matrice_analytics-0.1.89.dist-info → matrice_analytics-0.1.96.dist-info}/WHEEL +0 -0
- {matrice_analytics-0.1.89.dist-info → matrice_analytics-0.1.96.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_analytics-0.1.89.dist-info → matrice_analytics-0.1.96.dist-info}/top_level.txt +0 -0
|
@@ -99,6 +99,7 @@ from .usecases import (
|
|
|
99
99
|
UndergroundPipelineDefectUseCase,
|
|
100
100
|
SusActivityUseCase,
|
|
101
101
|
NaturalDisasterUseCase,
|
|
102
|
+
FootFallUseCase,
|
|
102
103
|
# Put all IMAGE based usecases here
|
|
103
104
|
BloodCancerDetectionUseCase,
|
|
104
105
|
SkinCancerClassificationUseCase,
|
|
@@ -569,6 +570,9 @@ class PostProcessor:
|
|
|
569
570
|
registry.register_use_case(
|
|
570
571
|
"environmental", "natural_disaster_detection", NaturalDisasterUseCase
|
|
571
572
|
)
|
|
573
|
+
registry.register_use_case(
|
|
574
|
+
"retail", "footfall", FootFallUseCase
|
|
575
|
+
)
|
|
572
576
|
|
|
573
577
|
# Put all IMAGE based usecases here
|
|
574
578
|
registry.register_use_case(
|
|
@@ -85,6 +85,8 @@ from .pcb_defect_detection import PCBDefectConfig, PCBDefectUseCase
|
|
|
85
85
|
from .underground_pipeline_defect_detection import UndergroundPipelineDefectConfig,UndergroundPipelineDefectUseCase
|
|
86
86
|
from .suspicious_activity_detection import SusActivityConfig, SusActivityUseCase
|
|
87
87
|
from .natural_disaster import NaturalDisasterConfig, NaturalDisasterUseCase
|
|
88
|
+
from .footfall import FootFallConfig, FootFallUseCase
|
|
89
|
+
|
|
88
90
|
|
|
89
91
|
#Put all IMAGE based usecases here
|
|
90
92
|
from .blood_cancer_detection_img import BloodCancerDetectionConfig, BloodCancerDetectionUseCase
|
|
@@ -172,6 +174,7 @@ __all__ = [
|
|
|
172
174
|
'UndergroundPipelineDefectUseCase',
|
|
173
175
|
'SusActivityUseCase',
|
|
174
176
|
'NaturalDisasterUseCase',
|
|
177
|
+
'FootFallUseCase',
|
|
175
178
|
|
|
176
179
|
#Put all IMAGE based usecases here
|
|
177
180
|
'BloodCancerDetectionUseCase',
|
|
@@ -254,7 +257,7 @@ __all__ = [
|
|
|
254
257
|
'PCBDefectConfig',
|
|
255
258
|
'SusActivityConfig',
|
|
256
259
|
'NaturalDisasterConfig',
|
|
257
|
-
|
|
260
|
+
'FootFallConfig',
|
|
258
261
|
#Put all IMAGE based usecase CONFIGS here
|
|
259
262
|
'BloodCancerDetectionConfig',
|
|
260
263
|
'SkinCancerClassificationConfig',
|
|
@@ -74,6 +74,8 @@ class AdvancedCustomerServiceUseCase(BaseProcessor):
|
|
|
74
74
|
"""Initialize advanced customer service use case."""
|
|
75
75
|
super().__init__("advanced_customer_service")
|
|
76
76
|
self.category = "sales"
|
|
77
|
+
self.CASE_TYPE: Optional[str] = 'advanced_customer_service'
|
|
78
|
+
self.CASE_VERSION: Optional[str] = '1.3'
|
|
77
79
|
|
|
78
80
|
# Advanced tracking structures
|
|
79
81
|
self.customer_occupancy = {}
|
|
@@ -496,7 +498,7 @@ class AdvancedCustomerServiceUseCase(BaseProcessor):
|
|
|
496
498
|
# Calculate current_counts (frame-wise counts)
|
|
497
499
|
current_counts = [
|
|
498
500
|
{"category": "staff", "count": staff_analytics.get("active_staff", 0)},
|
|
499
|
-
{"category": "
|
|
501
|
+
{"category": "Active Customers", "count": queue_analytics.get("active_customers", 0)}
|
|
500
502
|
]
|
|
501
503
|
# Detections: include all detections for this frame
|
|
502
504
|
detection_objs = []
|
|
@@ -529,7 +531,8 @@ class AdvancedCustomerServiceUseCase(BaseProcessor):
|
|
|
529
531
|
"alerts": alerts,
|
|
530
532
|
"alert_settings": alert_settings,
|
|
531
533
|
"reset_settings": reset_settings,
|
|
532
|
-
"human_text": human_text
|
|
534
|
+
"human_text": human_text,
|
|
535
|
+
"target_categories": ['Staff', 'Active Customers']
|
|
533
536
|
}
|
|
534
537
|
# Patch: Build real_time_occupancy with correct service_areas info (not just empty lists)
|
|
535
538
|
real_time_occupancy = analytics_results.get("real_time_occupancy", {}).copy()
|
|
@@ -1202,6 +1202,7 @@ class ColorDetectionUseCase(BaseProcessor):
|
|
|
1202
1202
|
detections=detections, human_text=human_text, camera_info=camera_info, alerts=alerts, alert_settings=alert_settings,
|
|
1203
1203
|
reset_settings=reset_settings, start_time=high_precision_start_timestamp ,
|
|
1204
1204
|
reset_time=high_precision_reset_timestamp)
|
|
1205
|
+
tracking_stat['target_categories'] = self.target_categories
|
|
1205
1206
|
|
|
1206
1207
|
tracking_stats.append(tracking_stat)
|
|
1207
1208
|
return tracking_stats
|
|
@@ -107,7 +107,7 @@ class FireSmokeUseCase(BaseProcessor):
|
|
|
107
107
|
|
|
108
108
|
self.smoothing_tracker = None # Required for bbox smoothing
|
|
109
109
|
self._fire_smoke_recent_history = []
|
|
110
|
-
self.target_categories=['fire']
|
|
110
|
+
self.target_categories=['fire', 'smoke'] # Lowercase to match filtering logic at line 276
|
|
111
111
|
|
|
112
112
|
self._ascending_alert_list: List[str] = []
|
|
113
113
|
self.current_incident_end_timestamp: str = "N/A"
|
|
@@ -538,9 +538,21 @@ class FireSmokeUseCase(BaseProcessor):
|
|
|
538
538
|
count_thresholds = {}
|
|
539
539
|
if config.alert_config and hasattr(config.alert_config, "count_thresholds"):
|
|
540
540
|
count_thresholds = config.alert_config.count_thresholds or {}
|
|
541
|
+
|
|
542
|
+
# CRITICAL FIX: Ensure we have at least one category to process
|
|
543
|
+
# If count_thresholds is empty, use detected categories from per_category_count
|
|
544
|
+
# This ensures incidents are always generated when detections exist
|
|
545
|
+
per_category_count = summary.get("per_category_count", {})
|
|
546
|
+
if not count_thresholds and per_category_count:
|
|
547
|
+
# Create thresholds for all detected categories with threshold=0 (always trigger)
|
|
548
|
+
count_thresholds = {cat: 0 for cat in per_category_count.keys()}
|
|
549
|
+
self.logger.debug(f"[INCIDENT] count_thresholds was empty, using detected categories: {count_thresholds}")
|
|
550
|
+
|
|
551
|
+
# Flag to track if we generated any incident
|
|
552
|
+
incident_generated = False
|
|
541
553
|
|
|
542
554
|
for category, threshold in count_thresholds.items():
|
|
543
|
-
if category in
|
|
555
|
+
if category in per_category_count:
|
|
544
556
|
|
|
545
557
|
#count = summary.get("per_category_count", {})[category]
|
|
546
558
|
start_timestamp = self._get_start_timestamp_str(stream_info)
|
|
@@ -643,6 +655,55 @@ class FireSmokeUseCase(BaseProcessor):
|
|
|
643
655
|
event['duration'] = self.get_duration_seconds(start_timestamp, self.current_incident_end_timestamp)
|
|
644
656
|
event['incident_quant'] = intensity_pct
|
|
645
657
|
incidents.append(event)
|
|
658
|
+
incident_generated = True
|
|
659
|
+
|
|
660
|
+
# CRITICAL FALLBACK: If no incident was generated despite having detections,
|
|
661
|
+
# generate a basic incident to ensure the incident manager receives data
|
|
662
|
+
if not incident_generated and total > 0:
|
|
663
|
+
self.logger.warning(f"[INCIDENT] No incident generated despite {total} detections. Generating fallback incident.")
|
|
664
|
+
# Calculate area and intensity for fallback
|
|
665
|
+
for det in detections:
|
|
666
|
+
bbox = det.get("bounding_box") or det.get("bbox")
|
|
667
|
+
if bbox:
|
|
668
|
+
xmin, ymin = bbox.get("xmin"), bbox.get("ymin")
|
|
669
|
+
xmax, ymax = bbox.get("xmax"), bbox.get("ymax")
|
|
670
|
+
if None not in (xmin, ymin, xmax, ymax):
|
|
671
|
+
width, height = xmax - xmin, ymax - ymin
|
|
672
|
+
if width > 0 and height > 0:
|
|
673
|
+
total_area += width * height
|
|
674
|
+
|
|
675
|
+
threshold_area = config.threshold_area or 250200.0
|
|
676
|
+
intensity_pct = min(100.0, (total_area / threshold_area) * 100)
|
|
677
|
+
|
|
678
|
+
# Determine severity level
|
|
679
|
+
if intensity_pct >= 30:
|
|
680
|
+
level = "critical"
|
|
681
|
+
elif intensity_pct >= 13:
|
|
682
|
+
level = "significant"
|
|
683
|
+
elif intensity_pct >= 3:
|
|
684
|
+
level = "medium"
|
|
685
|
+
else:
|
|
686
|
+
level = "low"
|
|
687
|
+
self._ascending_alert_list.append(level)
|
|
688
|
+
|
|
689
|
+
start_timestamp = self._get_start_timestamp_str(stream_info)
|
|
690
|
+
human_text = f"INCIDENTS DETECTED @ {current_timestamp}:\n\tSeverity Level: {(self.CASE_TYPE, level)}"
|
|
691
|
+
|
|
692
|
+
event = self.create_incident(
|
|
693
|
+
incident_id='incident_' + self.CASE_TYPE + '_fallback',
|
|
694
|
+
incident_type=self.CASE_TYPE,
|
|
695
|
+
severity_level=level,
|
|
696
|
+
human_text=human_text,
|
|
697
|
+
camera_info=camera_info,
|
|
698
|
+
alerts=alerts,
|
|
699
|
+
alert_settings=[],
|
|
700
|
+
start_time=start_timestamp,
|
|
701
|
+
end_time='Incident still active',
|
|
702
|
+
level_settings={"low": 3, "medium": 5, "significant": 15, "critical": 30}
|
|
703
|
+
)
|
|
704
|
+
event['incident_quant'] = intensity_pct
|
|
705
|
+
incidents.append(event)
|
|
706
|
+
self.logger.info(f"[INCIDENT] Generated fallback incident with level={level}, intensity={intensity_pct:.2f}%")
|
|
646
707
|
|
|
647
708
|
else:
|
|
648
709
|
#self._ascending_alert_list.append(level)
|
|
@@ -689,20 +750,39 @@ class FireSmokeUseCase(BaseProcessor):
|
|
|
689
750
|
|
|
690
751
|
|
|
691
752
|
# Build total_counts array in expected format
|
|
753
|
+
# ALWAYS populate with all target categories to avoid empty arrays downstream
|
|
692
754
|
total_counts = []
|
|
693
755
|
if total > 0:
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
756
|
+
total_counts.append({
|
|
757
|
+
"category": 'Fire/Smoke',
|
|
758
|
+
"count": 1
|
|
759
|
+
})
|
|
760
|
+
else:
|
|
761
|
+
# When no detections, send count=0 for each category to avoid empty array
|
|
762
|
+
total_counts.append({
|
|
763
|
+
"category": 'Fire',
|
|
764
|
+
"count": 0
|
|
765
|
+
})
|
|
766
|
+
total_counts.append({
|
|
767
|
+
"category": 'Smoke',
|
|
768
|
+
"count": 0
|
|
769
|
+
})
|
|
698
770
|
|
|
699
|
-
# Build current_counts array in expected format
|
|
771
|
+
# Build current_counts array in expected format
|
|
772
|
+
# ALWAYS populate with all target categories to avoid empty arrays downstream
|
|
700
773
|
current_counts = []
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
774
|
+
|
|
775
|
+
# Add Fire entry (count=1 if detected, count=0 if not)
|
|
776
|
+
current_counts.append({
|
|
777
|
+
"category": 'Fire',
|
|
778
|
+
"count": 1 if total_fire > 0 else 0
|
|
779
|
+
})
|
|
780
|
+
|
|
781
|
+
# Add Smoke entry (count=1 if detected, count=0 if not)
|
|
782
|
+
current_counts.append({
|
|
783
|
+
"category": 'Smoke',
|
|
784
|
+
"count": 1 if total_smoke > 0 else 0
|
|
785
|
+
})
|
|
706
786
|
|
|
707
787
|
human_lines = [f"CURRENT FRAME @ {current_timestamp}:"]
|
|
708
788
|
if total_fire > 0:
|
|
@@ -773,8 +853,8 @@ class FireSmokeUseCase(BaseProcessor):
|
|
|
773
853
|
detections=detections, human_text=human_text, camera_info=camera_info, alerts=alerts, alert_settings=alert_settings,
|
|
774
854
|
reset_settings=reset_settings, start_time=high_precision_start_timestamp ,
|
|
775
855
|
reset_time=high_precision_reset_timestamp)
|
|
776
|
-
|
|
777
|
-
|
|
856
|
+
|
|
857
|
+
tracking_stat['target_categories'] = self.target_categories
|
|
778
858
|
tracking_stats.append(tracking_stat)
|
|
779
859
|
|
|
780
860
|
if len(self.id_hit_list)==1:
|