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,1175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main post-processing processor with unified, clean API.
|
|
3
|
+
|
|
4
|
+
This module provides the main PostProcessor class that serves as the entry point
|
|
5
|
+
for all post-processing operations. It manages use cases, configurations, and
|
|
6
|
+
provides both simple and advanced processing interfaces.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, List, Optional, Union
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import logging
|
|
12
|
+
import time
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
import hashlib
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
from .core.base import ProcessingResult, ProcessingContext, ProcessingStatus, registry
|
|
18
|
+
from .core.config import (
|
|
19
|
+
BaseConfig,
|
|
20
|
+
PeopleCountingConfig,
|
|
21
|
+
CustomerServiceConfig,
|
|
22
|
+
IntrusionConfig,
|
|
23
|
+
ProximityConfig,
|
|
24
|
+
config_manager,
|
|
25
|
+
ConfigValidationError,
|
|
26
|
+
PeopleTrackingConfig,
|
|
27
|
+
)
|
|
28
|
+
from .usecases import (
|
|
29
|
+
PeopleCountingUseCase,
|
|
30
|
+
DroneTrafficMonitoringUsecase,
|
|
31
|
+
IntrusionUseCase,
|
|
32
|
+
ProximityUseCase,
|
|
33
|
+
CustomerServiceUseCase,
|
|
34
|
+
AdvancedCustomerServiceUseCase,
|
|
35
|
+
LicensePlateUseCase,
|
|
36
|
+
ColorDetectionUseCase,
|
|
37
|
+
PotholeSegmentationUseCase,
|
|
38
|
+
PPEComplianceUseCase,
|
|
39
|
+
VehicleMonitoringUseCase,
|
|
40
|
+
ShopliftingDetectionUseCase,
|
|
41
|
+
BananaMonitoringUseCase,
|
|
42
|
+
FieldMappingUseCase,
|
|
43
|
+
MaskDetectionUseCase,
|
|
44
|
+
LeafUseCase,
|
|
45
|
+
CarDamageDetectionUseCase,
|
|
46
|
+
LeafDiseaseDetectionUseCase,
|
|
47
|
+
FireSmokeUseCase,
|
|
48
|
+
ShopliftingDetectionConfig,
|
|
49
|
+
FlareAnalysisUseCase,
|
|
50
|
+
WoundSegmentationUseCase,
|
|
51
|
+
ParkingSpaceUseCase,
|
|
52
|
+
ParkingUseCase,
|
|
53
|
+
FaceEmotionUseCase,
|
|
54
|
+
UnderwaterPlasticUseCase,
|
|
55
|
+
PipelineDetectionUseCase,
|
|
56
|
+
PedestrianDetectionUseCase,
|
|
57
|
+
ChickenPoseDetectionUseCase,
|
|
58
|
+
TheftDetectionUseCase,
|
|
59
|
+
TrafficSignMonitoringUseCase,
|
|
60
|
+
AntiSpoofingDetectionUseCase,
|
|
61
|
+
ShelfInventoryUseCase,
|
|
62
|
+
LaneDetectionUseCase,
|
|
63
|
+
LitterDetectionUseCase,
|
|
64
|
+
AbandonedObjectDetectionUseCase,
|
|
65
|
+
LeakDetectionUseCase,
|
|
66
|
+
HumanActivityUseCase,
|
|
67
|
+
GasLeakDetectionUseCase,
|
|
68
|
+
AgeDetectionUseCase,
|
|
69
|
+
WeldDefectUseCase,
|
|
70
|
+
WeaponDetectionUseCase,
|
|
71
|
+
PriceTagUseCase,
|
|
72
|
+
DistractedDriverUseCase,
|
|
73
|
+
EmergencyVehicleUseCase,
|
|
74
|
+
SolarPanelUseCase,
|
|
75
|
+
CropWeedDetectionUseCase,
|
|
76
|
+
ChildMonitoringUseCase,
|
|
77
|
+
GenderDetectionUseCase,
|
|
78
|
+
ConcreteCrackUseCase,
|
|
79
|
+
FashionDetectionUseCase,
|
|
80
|
+
WarehouseObjectUseCase,
|
|
81
|
+
ShoppingCartUseCase,
|
|
82
|
+
BottleDefectUseCase,
|
|
83
|
+
AssemblyLineUseCase,
|
|
84
|
+
CarPartSegmentationUseCase,
|
|
85
|
+
WindmillMaintenanceUseCase,
|
|
86
|
+
FlowerUseCase,
|
|
87
|
+
SmokerDetectionUseCase,
|
|
88
|
+
RoadTrafficUseCase,
|
|
89
|
+
RoadViewSegmentationUseCase,
|
|
90
|
+
# FaceRecognitionUseCase,
|
|
91
|
+
DrowsyDriverUseCase,
|
|
92
|
+
WaterBodyUseCase,
|
|
93
|
+
LicensePlateMonitorUseCase,
|
|
94
|
+
DwellUseCase,
|
|
95
|
+
AgeGenderUseCase,
|
|
96
|
+
PeopleTrackingUseCase,
|
|
97
|
+
WildLifeMonitoringUseCase,
|
|
98
|
+
PCBDefectUseCase,
|
|
99
|
+
UndergroundPipelineDefectUseCase,
|
|
100
|
+
SusActivityUseCase,
|
|
101
|
+
NaturalDisasterUseCase,
|
|
102
|
+
# Put all IMAGE based usecases here
|
|
103
|
+
BloodCancerDetectionUseCase,
|
|
104
|
+
SkinCancerClassificationUseCase,
|
|
105
|
+
PlaqueSegmentationUseCase,
|
|
106
|
+
CardiomegalyUseCase,
|
|
107
|
+
HistopathologicalCancerDetectionUseCase,
|
|
108
|
+
CellMicroscopyUseCase,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Face recognition with embeddings (from face_reg module)
|
|
112
|
+
from .face_reg.face_recognition import FaceRecognitionEmbeddingUseCase
|
|
113
|
+
|
|
114
|
+
from .core.config_utils import create_config_from_template
|
|
115
|
+
from .core.config import BaseConfig, AlertConfig, ZoneConfig, TrackingConfig
|
|
116
|
+
from .config import (
|
|
117
|
+
get_usecase_from_app_name,
|
|
118
|
+
get_category_from_app_name,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
logger = logging.getLogger(__name__)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class PostProcessor:
|
|
125
|
+
"""
|
|
126
|
+
Unified post-processing interface with clean API and comprehensive functionality.
|
|
127
|
+
|
|
128
|
+
This processor provides a simple yet powerful interface for processing model outputs
|
|
129
|
+
with various use cases, centralized configuration management, and comprehensive
|
|
130
|
+
error handling.
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
# Simple usage
|
|
134
|
+
processor = PostProcessor()
|
|
135
|
+
result = processor.process_simple(
|
|
136
|
+
raw_results, "people_counting",
|
|
137
|
+
confidence_threshold=0.6,
|
|
138
|
+
zones={"entrance": [[0, 0], [100, 0], [100, 100], [0, 100]]}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Configuration-based usage
|
|
142
|
+
config = processor.create_config("people_counting", confidence_threshold=0.5)
|
|
143
|
+
result = processor.process(raw_results, config)
|
|
144
|
+
|
|
145
|
+
# File-based configuration
|
|
146
|
+
result = processor.process_from_file(raw_results, "config.json")
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(
|
|
150
|
+
self,
|
|
151
|
+
post_processing_config: Optional[Union[Dict[str, Any], BaseConfig, str]] = None,
|
|
152
|
+
app_name: Optional[str] = None,
|
|
153
|
+
index_to_category: Optional[Dict[int, str]] = None,
|
|
154
|
+
target_categories: Optional[List[str]] = None,
|
|
155
|
+
):
|
|
156
|
+
"""Initialize the PostProcessor with registered use cases."""
|
|
157
|
+
self._statistics = {
|
|
158
|
+
"total_processed": 0,
|
|
159
|
+
"successful": 0,
|
|
160
|
+
"failed": 0,
|
|
161
|
+
"total_processing_time": 0.0,
|
|
162
|
+
}
|
|
163
|
+
self.cache = {}
|
|
164
|
+
self._use_case_cache = {} # Cache for use case instances
|
|
165
|
+
|
|
166
|
+
# Register available use cases
|
|
167
|
+
self._register_use_cases()
|
|
168
|
+
|
|
169
|
+
# Set up default post-processing configuration
|
|
170
|
+
self.post_processing_config = None
|
|
171
|
+
self.app_name = app_name
|
|
172
|
+
self.index_to_category = index_to_category
|
|
173
|
+
self.target_categories = target_categories
|
|
174
|
+
if post_processing_config or self.app_name:
|
|
175
|
+
logging.debug(f"Parsing post-processing config: {post_processing_config}")
|
|
176
|
+
self.post_processing_config = self._parse_post_processing_config(
|
|
177
|
+
post_processing_config, self.app_name
|
|
178
|
+
)
|
|
179
|
+
if self.post_processing_config:
|
|
180
|
+
logging.info(
|
|
181
|
+
f"Successfully parsed post-processing config for usecase: {self.post_processing_config.usecase}"
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
logging.warning("Failed to parse post-processing config")
|
|
185
|
+
else:
|
|
186
|
+
logging.info("No post-processing config provided")
|
|
187
|
+
|
|
188
|
+
def _load_config_from_app_name(self, app_name: str) -> Optional[BaseConfig]:
|
|
189
|
+
"""Load default post-processing configuration based on app name."""
|
|
190
|
+
usecase = get_usecase_from_app_name(app_name)
|
|
191
|
+
category = get_category_from_app_name(app_name)
|
|
192
|
+
if not usecase or not category:
|
|
193
|
+
logging.warning(f"No usecase or category found for app: {app_name}")
|
|
194
|
+
return None
|
|
195
|
+
config = self.create_config(usecase, category)
|
|
196
|
+
return config
|
|
197
|
+
|
|
198
|
+
def _parse_post_processing_config(
|
|
199
|
+
self,
|
|
200
|
+
config: Union[Dict[str, Any], BaseConfig, str],
|
|
201
|
+
app_name: Optional[str] = None,
|
|
202
|
+
) -> Optional[BaseConfig]:
|
|
203
|
+
"""Parse post-processing configuration from various formats."""
|
|
204
|
+
try:
|
|
205
|
+
if not config and not app_name:
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
# Handle app-name based configuration first
|
|
209
|
+
if app_name:
|
|
210
|
+
app_config = self._load_config_from_app_name(app_name)
|
|
211
|
+
if app_config and config and isinstance(config, dict):
|
|
212
|
+
return self._merge_config_into_app_config(app_config, config)
|
|
213
|
+
elif app_config:
|
|
214
|
+
return app_config
|
|
215
|
+
else:
|
|
216
|
+
logging.warning(f"No config found for app: {app_name}")
|
|
217
|
+
|
|
218
|
+
# Handle different config input types
|
|
219
|
+
parsed_config = self._parse_config_by_type(config)
|
|
220
|
+
if parsed_config:
|
|
221
|
+
self._apply_instance_config_overrides(parsed_config)
|
|
222
|
+
|
|
223
|
+
return parsed_config
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logging.error(f"Failed to parse post-processing config: {str(e)}")
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
def _merge_config_into_app_config(
|
|
230
|
+
self, app_config: BaseConfig, config_dict: Dict[str, Any]
|
|
231
|
+
) -> BaseConfig:
|
|
232
|
+
"""Merge provided configuration dictionary into app-based config."""
|
|
233
|
+
logging.debug(f"Merging provided config into app config")
|
|
234
|
+
logging.debug(f"Provided config keys: {list(config_dict.keys())}")
|
|
235
|
+
|
|
236
|
+
for key, value in config_dict.items():
|
|
237
|
+
if value is None:
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
if hasattr(app_config, key):
|
|
241
|
+
self._apply_config_value(app_config, key, value)
|
|
242
|
+
else:
|
|
243
|
+
logging.warning(f"Config key '{key}' not found in app config, skipping")
|
|
244
|
+
|
|
245
|
+
logging.debug(f"Final app config zone_config: {getattr(app_config, 'zone_config', None)}")
|
|
246
|
+
return app_config
|
|
247
|
+
|
|
248
|
+
def _apply_config_value(self, config: BaseConfig, key: str, value: Any) -> None:
|
|
249
|
+
"""Apply a configuration value to a config object, handling nested dicts."""
|
|
250
|
+
if isinstance(value, dict):
|
|
251
|
+
current_value = getattr(config, key)
|
|
252
|
+
try:
|
|
253
|
+
# Try to convert known config dicts to dataclasses
|
|
254
|
+
if key == "alert_config":
|
|
255
|
+
setattr(config, key, AlertConfig(**value))
|
|
256
|
+
elif key == "zone_config":
|
|
257
|
+
setattr(config, key, ZoneConfig(**value))
|
|
258
|
+
elif key == "tracking_config":
|
|
259
|
+
setattr(config, key, TrackingConfig(**value))
|
|
260
|
+
elif isinstance(current_value, dict):
|
|
261
|
+
# Merge dictionaries
|
|
262
|
+
merged_dict = {**(current_value or {}), **value}
|
|
263
|
+
setattr(config, key, merged_dict)
|
|
264
|
+
logging.debug(f"Merged nested dict for {key}: {merged_dict}")
|
|
265
|
+
else:
|
|
266
|
+
setattr(config, key, value)
|
|
267
|
+
except Exception:
|
|
268
|
+
# Fallback to direct assignment
|
|
269
|
+
setattr(config, key, value)
|
|
270
|
+
logging.debug(f"Applied config parameter {key}={value} (fallback)")
|
|
271
|
+
else:
|
|
272
|
+
setattr(config, key, value)
|
|
273
|
+
logging.debug(f"Applied config parameter {key}={value}")
|
|
274
|
+
|
|
275
|
+
def _parse_config_by_type(
|
|
276
|
+
self, config: Union[Dict[str, Any], BaseConfig, str]
|
|
277
|
+
) -> Optional[BaseConfig]:
|
|
278
|
+
"""Parse configuration based on its input type."""
|
|
279
|
+
if isinstance(config, BaseConfig):
|
|
280
|
+
return config
|
|
281
|
+
elif isinstance(config, dict):
|
|
282
|
+
return self._parse_config_dict(config)
|
|
283
|
+
elif isinstance(config, str):
|
|
284
|
+
return create_config_from_template(config)
|
|
285
|
+
else:
|
|
286
|
+
logging.warning(f"Unsupported config type: {type(config)}")
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
def _parse_config_dict(self, config: Dict[str, Any]) -> Optional[BaseConfig]:
|
|
290
|
+
"""Parse configuration from a dictionary."""
|
|
291
|
+
usecase = config.get("usecase")
|
|
292
|
+
if not usecase:
|
|
293
|
+
raise ValueError("Configuration dict must contain 'usecase' key")
|
|
294
|
+
|
|
295
|
+
# Prepare config parameters
|
|
296
|
+
config_params = config.copy()
|
|
297
|
+
config_params.pop("usecase", None)
|
|
298
|
+
config_params.pop("category", None)
|
|
299
|
+
category = config.get("category", "general")
|
|
300
|
+
|
|
301
|
+
# Clean up use-case specific parameters
|
|
302
|
+
self._clean_use_case_specific_params(usecase, config_params)
|
|
303
|
+
|
|
304
|
+
# Normalize nested config objects
|
|
305
|
+
self._normalize_nested_configs(config_params)
|
|
306
|
+
|
|
307
|
+
# Create config using the factory
|
|
308
|
+
return self.create_config(usecase, category, **config_params)
|
|
309
|
+
|
|
310
|
+
def _clean_use_case_specific_params(
|
|
311
|
+
self, usecase: str, config_params: Dict[str, Any]
|
|
312
|
+
) -> None:
|
|
313
|
+
"""Remove parameters that aren't needed for specific use cases."""
|
|
314
|
+
facial_recognition_usecases = {"face_recognition"}
|
|
315
|
+
license_plate_monitoring_usecases = {"license_plate_monitor"}
|
|
316
|
+
|
|
317
|
+
if usecase not in facial_recognition_usecases:
|
|
318
|
+
if "facial_recognition_server_id" in config_params:
|
|
319
|
+
logging.debug(
|
|
320
|
+
f"Removing facial_recognition_server_id from {usecase} config"
|
|
321
|
+
)
|
|
322
|
+
config_params.pop("facial_recognition_server_id", None)
|
|
323
|
+
config_params.pop("deployment_id", None)
|
|
324
|
+
|
|
325
|
+
if usecase not in license_plate_monitoring_usecases:
|
|
326
|
+
if "lpr_server_id" in config_params:
|
|
327
|
+
logging.debug(f"Removing lpr_server_id from {usecase} config")
|
|
328
|
+
config_params.pop("lpr_server_id", None)
|
|
329
|
+
|
|
330
|
+
# Keep session and lpr_server_id only for use cases that need them
|
|
331
|
+
if usecase not in facial_recognition_usecases and usecase not in license_plate_monitoring_usecases:
|
|
332
|
+
if "session" in config_params:
|
|
333
|
+
logging.debug(f"Removing session from {usecase} config")
|
|
334
|
+
config_params.pop("session", None)
|
|
335
|
+
|
|
336
|
+
def _normalize_nested_configs(self, config_params: Dict[str, Any]) -> None:
|
|
337
|
+
"""Convert nested config dictionaries to dataclass instances."""
|
|
338
|
+
config_mappings = {
|
|
339
|
+
"alert_config": AlertConfig,
|
|
340
|
+
"zone_config": ZoneConfig,
|
|
341
|
+
"tracking_config": TrackingConfig,
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
for key, config_class in config_mappings.items():
|
|
345
|
+
if isinstance(config_params.get(key), dict):
|
|
346
|
+
try:
|
|
347
|
+
config_params[key] = config_class(**config_params[key])
|
|
348
|
+
except Exception:
|
|
349
|
+
# Leave as dict; downstream create_config will handle it
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
def _apply_instance_config_overrides(self, config: BaseConfig) -> None:
|
|
353
|
+
"""Apply instance-level configuration overrides."""
|
|
354
|
+
if hasattr(config, "index_to_category"):
|
|
355
|
+
if not config.index_to_category:
|
|
356
|
+
config.index_to_category = self.index_to_category or {}
|
|
357
|
+
else:
|
|
358
|
+
self.index_to_category = config.index_to_category
|
|
359
|
+
|
|
360
|
+
if hasattr(config, "target_categories"):
|
|
361
|
+
if not config.target_categories:
|
|
362
|
+
config.target_categories = self.target_categories
|
|
363
|
+
else:
|
|
364
|
+
self.target_categories = config.target_categories
|
|
365
|
+
|
|
366
|
+
def _register_use_cases(self) -> None:
|
|
367
|
+
"""Register all available use cases."""
|
|
368
|
+
# Register people counting use case
|
|
369
|
+
registry.register_use_case("general", "people_counting", PeopleCountingUseCase)
|
|
370
|
+
|
|
371
|
+
# Register intrusion detection use case
|
|
372
|
+
registry.register_use_case("security", "intrusion_detection", IntrusionUseCase)
|
|
373
|
+
|
|
374
|
+
# Register proximity detection use case
|
|
375
|
+
registry.register_use_case("security", "proximity_detection", ProximityUseCase)
|
|
376
|
+
|
|
377
|
+
# Register customer service use case
|
|
378
|
+
registry.register_use_case("sales", "customer_service", CustomerServiceUseCase)
|
|
379
|
+
|
|
380
|
+
# Register advanced customer service use case
|
|
381
|
+
registry.register_use_case(
|
|
382
|
+
"sales", "advanced_customer_service", AdvancedCustomerServiceUseCase
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Register license plate detection use case
|
|
386
|
+
registry.register_use_case(
|
|
387
|
+
"license_plate", "license_plate_detection", LicensePlateUseCase
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Register color detection use case
|
|
391
|
+
registry.register_use_case(
|
|
392
|
+
"visual_appearance", "color_detection", ColorDetectionUseCase
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# Register video_color_classification as alias for color_detection
|
|
396
|
+
registry.register_use_case(
|
|
397
|
+
"visual_appearance", "video_color_classification", ColorDetectionUseCase
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
# Register PPE compliance use case
|
|
401
|
+
registry.register_use_case(
|
|
402
|
+
"ppe", "ppe_compliance_detection", PPEComplianceUseCase
|
|
403
|
+
)
|
|
404
|
+
registry.register_use_case(
|
|
405
|
+
"infrastructure", "pothole_segmentation", PotholeSegmentationUseCase
|
|
406
|
+
)
|
|
407
|
+
registry.register_use_case(
|
|
408
|
+
"car_damage", "car_damage_detection", CarDamageDetectionUseCase
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
registry.register_use_case(
|
|
412
|
+
"traffic", "vehicle_monitoring", VehicleMonitoringUseCase
|
|
413
|
+
)
|
|
414
|
+
registry.register_use_case(
|
|
415
|
+
"traffic", "fruit_monitoring", BananaMonitoringUseCase
|
|
416
|
+
)
|
|
417
|
+
registry.register_use_case("security", "theft_detection", TheftDetectionUseCase)
|
|
418
|
+
registry.register_use_case(
|
|
419
|
+
"traffic", "traffic_sign_monitoring", TrafficSignMonitoringUseCase
|
|
420
|
+
)
|
|
421
|
+
registry.register_use_case(
|
|
422
|
+
"traffic", "drone_traffic_monitoring", DroneTrafficMonitoringUsecase
|
|
423
|
+
)
|
|
424
|
+
registry.register_use_case(
|
|
425
|
+
"security", "anti_spoofing_detection", AntiSpoofingDetectionUseCase
|
|
426
|
+
)
|
|
427
|
+
registry.register_use_case("retail", "shelf_inventory", ShelfInventoryUseCase)
|
|
428
|
+
registry.register_use_case("traffic", "lane_detection", LaneDetectionUseCase)
|
|
429
|
+
registry.register_use_case(
|
|
430
|
+
"security", "abandoned_object_detection", AbandonedObjectDetectionUseCase
|
|
431
|
+
)
|
|
432
|
+
registry.register_use_case("hazard", "fire_smoke_detection", FireSmokeUseCase)
|
|
433
|
+
registry.register_use_case(
|
|
434
|
+
"flare_detection", "flare_analysis", FlareAnalysisUseCase
|
|
435
|
+
)
|
|
436
|
+
registry.register_use_case("general", "face_emotion", FaceEmotionUseCase)
|
|
437
|
+
registry.register_use_case(
|
|
438
|
+
"parking_space", "parking_space_detection", ParkingSpaceUseCase
|
|
439
|
+
)
|
|
440
|
+
registry.register_use_case(
|
|
441
|
+
"environmental", "underwater_pollution_detection", UnderwaterPlasticUseCase
|
|
442
|
+
)
|
|
443
|
+
registry.register_use_case(
|
|
444
|
+
"pedestrian", "pedestrian_detection", PedestrianDetectionUseCase
|
|
445
|
+
)
|
|
446
|
+
registry.register_use_case("general", "age_detection", AgeDetectionUseCase)
|
|
447
|
+
registry.register_use_case("weld", "weld_defect_detection", WeldDefectUseCase)
|
|
448
|
+
registry.register_use_case("price_tag", "price_tag_detection", PriceTagUseCase)
|
|
449
|
+
registry.register_use_case(
|
|
450
|
+
"mask_detection", "mask_detection", MaskDetectionUseCase
|
|
451
|
+
)
|
|
452
|
+
registry.register_use_case(
|
|
453
|
+
"pipeline_detection", "pipeline_detection", PipelineDetectionUseCase
|
|
454
|
+
)
|
|
455
|
+
registry.register_use_case(
|
|
456
|
+
"automobile", "distracted_driver_detection", DistractedDriverUseCase
|
|
457
|
+
)
|
|
458
|
+
registry.register_use_case(
|
|
459
|
+
"traffic", "emergency_vehicle_detection", EmergencyVehicleUseCase
|
|
460
|
+
)
|
|
461
|
+
registry.register_use_case("energy", "solar_panel", SolarPanelUseCase)
|
|
462
|
+
registry.register_use_case(
|
|
463
|
+
"agriculture", "chicken_pose_detection", ChickenPoseDetectionUseCase
|
|
464
|
+
)
|
|
465
|
+
registry.register_use_case(
|
|
466
|
+
"agriculture", "crop_weed_detection", CropWeedDetectionUseCase
|
|
467
|
+
)
|
|
468
|
+
registry.register_use_case(
|
|
469
|
+
"security", "child_monitoring", ChildMonitoringUseCase
|
|
470
|
+
)
|
|
471
|
+
registry.register_use_case(
|
|
472
|
+
"general", "gender_detection", GenderDetectionUseCase
|
|
473
|
+
)
|
|
474
|
+
registry.register_use_case(
|
|
475
|
+
"security", "weapon_detection", WeaponDetectionUseCase
|
|
476
|
+
)
|
|
477
|
+
registry.register_use_case(
|
|
478
|
+
"general", "concrete_crack_detection", ConcreteCrackUseCase
|
|
479
|
+
)
|
|
480
|
+
registry.register_use_case(
|
|
481
|
+
"retail", "fashion_detection", FashionDetectionUseCase
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
registry.register_use_case(
|
|
485
|
+
"retail", "warehouse_object_segmentation", WarehouseObjectUseCase
|
|
486
|
+
)
|
|
487
|
+
registry.register_use_case(
|
|
488
|
+
"retail", "shopping_cart_analysis", ShoppingCartUseCase
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
registry.register_use_case(
|
|
492
|
+
"security", "shoplifting_detection", ShopliftingDetectionUseCase
|
|
493
|
+
)
|
|
494
|
+
registry.register_use_case(
|
|
495
|
+
"retail", "defect_detection_products", BottleDefectUseCase
|
|
496
|
+
)
|
|
497
|
+
registry.register_use_case(
|
|
498
|
+
"manufacturing", "assembly_line_detection", AssemblyLineUseCase
|
|
499
|
+
)
|
|
500
|
+
registry.register_use_case(
|
|
501
|
+
"automobile", "car_part_segmentation", CarPartSegmentationUseCase
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
registry.register_use_case(
|
|
505
|
+
"manufacturing", "windmill_maintenance", WindmillMaintenanceUseCase
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
registry.register_use_case(
|
|
509
|
+
"infrastructure", "field_mapping", FieldMappingUseCase
|
|
510
|
+
)
|
|
511
|
+
registry.register_use_case(
|
|
512
|
+
"medical", "wound_segmentation", WoundSegmentationUseCase
|
|
513
|
+
)
|
|
514
|
+
registry.register_use_case(
|
|
515
|
+
"agriculture", "leaf_disease_detection", LeafDiseaseDetectionUseCase
|
|
516
|
+
)
|
|
517
|
+
registry.register_use_case("agriculture", "flower_segmentation", FlowerUseCase)
|
|
518
|
+
registry.register_use_case("general", "parking_det", ParkingUseCase)
|
|
519
|
+
registry.register_use_case("agriculture", "leaf_det", LeafUseCase)
|
|
520
|
+
registry.register_use_case(
|
|
521
|
+
"general", "smoker_detection", SmokerDetectionUseCase
|
|
522
|
+
)
|
|
523
|
+
registry.register_use_case(
|
|
524
|
+
"automobile", "road_traffic_density", RoadTrafficUseCase
|
|
525
|
+
)
|
|
526
|
+
registry.register_use_case(
|
|
527
|
+
"automobile", "road_view_segmentation", RoadViewSegmentationUseCase
|
|
528
|
+
)
|
|
529
|
+
# registry.register_use_case("security", "face_recognition", FaceRecognitionUseCase)
|
|
530
|
+
registry.register_use_case(
|
|
531
|
+
"security", "face_recognition", FaceRecognitionEmbeddingUseCase
|
|
532
|
+
)
|
|
533
|
+
registry.register_use_case(
|
|
534
|
+
"automobile", "drowsy_driver_detection", DrowsyDriverUseCase
|
|
535
|
+
)
|
|
536
|
+
registry.register_use_case(
|
|
537
|
+
"agriculture", "waterbody_segmentation", WaterBodyUseCase
|
|
538
|
+
)
|
|
539
|
+
registry.register_use_case(
|
|
540
|
+
"litter_detection", "litter_detection", LitterDetectionUseCase
|
|
541
|
+
)
|
|
542
|
+
registry.register_use_case("oil_gas", "leak_detection", LeakDetectionUseCase)
|
|
543
|
+
registry.register_use_case(
|
|
544
|
+
"general", "human_activity_recognition", HumanActivityUseCase
|
|
545
|
+
)
|
|
546
|
+
registry.register_use_case(
|
|
547
|
+
"oil_gas", "gas_leak_detection", GasLeakDetectionUseCase
|
|
548
|
+
)
|
|
549
|
+
registry.register_use_case(
|
|
550
|
+
"license_plate_monitor", "license_plate_monitor", LicensePlateMonitorUseCase
|
|
551
|
+
)
|
|
552
|
+
registry.register_use_case("general", "dwell", DwellUseCase)
|
|
553
|
+
registry.register_use_case(
|
|
554
|
+
"age_gender_detection", "age_gender_detection", AgeGenderUseCase
|
|
555
|
+
)
|
|
556
|
+
registry.register_use_case("general", "people_tracking", PeopleTrackingUseCase)
|
|
557
|
+
registry.register_use_case(
|
|
558
|
+
"environmental", "wildlife_monitoring", WildLifeMonitoringUseCase
|
|
559
|
+
)
|
|
560
|
+
registry.register_use_case(
|
|
561
|
+
"manufacturing", "pcb_defect_detection", PCBDefectUseCase
|
|
562
|
+
)
|
|
563
|
+
registry.register_use_case(
|
|
564
|
+
"general", "underground_pipeline_defect", UndergroundPipelineDefectUseCase
|
|
565
|
+
)
|
|
566
|
+
registry.register_use_case(
|
|
567
|
+
"security", "suspicious_activity_detection", SusActivityUseCase
|
|
568
|
+
)
|
|
569
|
+
registry.register_use_case(
|
|
570
|
+
"environmental", "natural_disaster_detection", NaturalDisasterUseCase
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
# Put all IMAGE based usecases here
|
|
574
|
+
registry.register_use_case(
|
|
575
|
+
"healthcare", "bloodcancer_img_detection", BloodCancerDetectionUseCase
|
|
576
|
+
)
|
|
577
|
+
registry.register_use_case(
|
|
578
|
+
"healthcare",
|
|
579
|
+
"skincancer_img_classification",
|
|
580
|
+
SkinCancerClassificationUseCase,
|
|
581
|
+
)
|
|
582
|
+
registry.register_use_case(
|
|
583
|
+
"healthcare", "plaque_img_segmentation", PlaqueSegmentationUseCase
|
|
584
|
+
)
|
|
585
|
+
registry.register_use_case(
|
|
586
|
+
"healthcare", "cardiomegaly_classification", CardiomegalyUseCase
|
|
587
|
+
)
|
|
588
|
+
registry.register_use_case(
|
|
589
|
+
"healthcare",
|
|
590
|
+
"histopathological_cancer_detection",
|
|
591
|
+
HistopathologicalCancerDetectionUseCase,
|
|
592
|
+
)
|
|
593
|
+
registry.register_use_case(
|
|
594
|
+
"healthcare", "cell_microscopy_segmentation", CellMicroscopyUseCase
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
logger.debug("Registered use cases with registry")
|
|
598
|
+
|
|
599
|
+
def _generate_cache_key(self, config: BaseConfig, stream_key: Optional[str] = None) -> str:
|
|
600
|
+
"""
|
|
601
|
+
Generate a cache key for use case instances based on config and stream key.
|
|
602
|
+
|
|
603
|
+
Args:
|
|
604
|
+
config: Configuration object
|
|
605
|
+
stream_key: Optional stream key
|
|
606
|
+
|
|
607
|
+
Returns:
|
|
608
|
+
str: Cache key for the use case instance
|
|
609
|
+
"""
|
|
610
|
+
def _make_json_serializable(obj):
|
|
611
|
+
"""Convert objects to JSON-serializable format."""
|
|
612
|
+
if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
|
|
613
|
+
return _make_json_serializable(obj.to_dict())
|
|
614
|
+
elif isinstance(obj, dict):
|
|
615
|
+
return {k: _make_json_serializable(v) for k, v in obj.items()}
|
|
616
|
+
elif isinstance(obj, list):
|
|
617
|
+
return [_make_json_serializable(item) for item in obj]
|
|
618
|
+
elif isinstance(obj, (str, int, float, bool, type(None))):
|
|
619
|
+
return obj
|
|
620
|
+
else:
|
|
621
|
+
return str(obj)
|
|
622
|
+
|
|
623
|
+
# Create a deterministic cache key based on config parameters and stream key
|
|
624
|
+
cache_data = {
|
|
625
|
+
'category': getattr(config, 'category', 'general'),
|
|
626
|
+
'usecase': getattr(config, 'usecase', 'unknown'),
|
|
627
|
+
'stream_key': stream_key or 'default',
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
# Add key configuration parameters that might affect use case behavior
|
|
631
|
+
try:
|
|
632
|
+
config_dict = config.to_dict() if hasattr(config, 'to_dict') else {}
|
|
633
|
+
# Only include parameters that affect use case instantiation/behavior
|
|
634
|
+
relevant_params = ['confidence_threshold', 'zones', 'tracking_config', 'alert_config']
|
|
635
|
+
for param in relevant_params:
|
|
636
|
+
if param in config_dict:
|
|
637
|
+
cache_data[param] = _make_json_serializable(config_dict[param])
|
|
638
|
+
except Exception:
|
|
639
|
+
# Fallback to basic cache key if config serialization fails
|
|
640
|
+
pass
|
|
641
|
+
|
|
642
|
+
# Sort keys for consistent hashing
|
|
643
|
+
config_str = json.dumps(cache_data, sort_keys=True, default=str)
|
|
644
|
+
return hashlib.md5(config_str.encode()).hexdigest()[:16] # Shorter hash for readability
|
|
645
|
+
|
|
646
|
+
async def _get_use_case_instance(
|
|
647
|
+
self, config: BaseConfig, stream_key: Optional[str] = None
|
|
648
|
+
):
|
|
649
|
+
"""
|
|
650
|
+
Get or create a cached use case instance.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
config: Configuration object
|
|
654
|
+
stream_key: Optional stream key
|
|
655
|
+
|
|
656
|
+
Returns:
|
|
657
|
+
Use case instance
|
|
658
|
+
"""
|
|
659
|
+
# Generate cache key
|
|
660
|
+
cache_key = self._generate_cache_key(config, stream_key)
|
|
661
|
+
|
|
662
|
+
# Check if we have a cached instance
|
|
663
|
+
if cache_key in self._use_case_cache:
|
|
664
|
+
logger.debug(f"Using cached use case instance for key: {cache_key}")
|
|
665
|
+
return self._use_case_cache[cache_key]
|
|
666
|
+
|
|
667
|
+
# Get appropriate use case class
|
|
668
|
+
use_case_class = registry.get_use_case(config.category, config.usecase)
|
|
669
|
+
if not use_case_class:
|
|
670
|
+
raise ValueError(f"Use case '{config.category}/{config.usecase}' not found")
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
if use_case_class == FaceRecognitionEmbeddingUseCase:
|
|
674
|
+
use_case = use_case_class(config=config)
|
|
675
|
+
# Await async initialization for face recognition use case
|
|
676
|
+
await use_case.initialize(config)
|
|
677
|
+
else:
|
|
678
|
+
use_case = use_case_class()
|
|
679
|
+
logger.info(f"Created use case instance for: {config.category}/{config.usecase}")
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
# Cache the instance
|
|
683
|
+
self._use_case_cache[cache_key] = use_case
|
|
684
|
+
logger.debug(f"Cached new use case instance for key: {cache_key}")
|
|
685
|
+
|
|
686
|
+
return use_case
|
|
687
|
+
|
|
688
|
+
async def _dispatch_use_case_processing(
|
|
689
|
+
self,
|
|
690
|
+
use_case,
|
|
691
|
+
data: Any,
|
|
692
|
+
config: BaseConfig,
|
|
693
|
+
input_bytes: Optional[bytes],
|
|
694
|
+
context: ProcessingContext,
|
|
695
|
+
stream_info: Optional[Dict[str, Any]]
|
|
696
|
+
) -> ProcessingResult:
|
|
697
|
+
"""
|
|
698
|
+
Dispatch processing to the appropriate use case with correct parameters.
|
|
699
|
+
|
|
700
|
+
This method handles the different method signatures required by different use cases.
|
|
701
|
+
"""
|
|
702
|
+
# Use cases that require input_bytes parameter
|
|
703
|
+
use_cases_with_bytes = {
|
|
704
|
+
ColorDetectionUseCase,
|
|
705
|
+
FlareAnalysisUseCase,
|
|
706
|
+
LicensePlateMonitorUseCase,
|
|
707
|
+
AgeGenderUseCase,
|
|
708
|
+
PeopleTrackingUseCase,
|
|
709
|
+
FaceRecognitionEmbeddingUseCase
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
# Async use cases
|
|
713
|
+
async_use_cases = {
|
|
714
|
+
FaceRecognitionEmbeddingUseCase,
|
|
715
|
+
LicensePlateMonitorUseCase
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
# Determine the appropriate method signature and call
|
|
719
|
+
use_case_type = type(use_case)
|
|
720
|
+
|
|
721
|
+
if use_case_type in async_use_cases:
|
|
722
|
+
# Handle async use cases
|
|
723
|
+
if use_case_type in use_cases_with_bytes:
|
|
724
|
+
result = await use_case.process(data, config, input_bytes, context, stream_info)
|
|
725
|
+
else:
|
|
726
|
+
result = await use_case.process(data, config, context, stream_info)
|
|
727
|
+
else:
|
|
728
|
+
# Handle synchronous use cases
|
|
729
|
+
if use_case_type in use_cases_with_bytes:
|
|
730
|
+
result = use_case.process(data, config, input_bytes, context, stream_info)
|
|
731
|
+
else:
|
|
732
|
+
# Default signature for most use cases
|
|
733
|
+
result = use_case.process(data, config, context, stream_info)
|
|
734
|
+
|
|
735
|
+
return result
|
|
736
|
+
|
|
737
|
+
async def process(
|
|
738
|
+
self,
|
|
739
|
+
data: Any,
|
|
740
|
+
config: Union[BaseConfig, Dict[str, Any], str, Path] = {},
|
|
741
|
+
input_bytes: Optional[bytes] = None,
|
|
742
|
+
stream_key: Optional[str] = "default_stream",
|
|
743
|
+
stream_info: Optional[Dict[str, Any]] = None,
|
|
744
|
+
context: Optional[ProcessingContext] = None,
|
|
745
|
+
custom_post_processing_config: Optional[Union[Dict[str, Any], BaseConfig, str]] = None,
|
|
746
|
+
) -> ProcessingResult:
|
|
747
|
+
"""
|
|
748
|
+
Process data using the specified configuration.
|
|
749
|
+
|
|
750
|
+
Args:
|
|
751
|
+
data: Raw model output (detection, tracking, classification results)
|
|
752
|
+
config: Configuration object, dict, or path to config file
|
|
753
|
+
input_bytes: Optional input bytes for certain use cases
|
|
754
|
+
custom_post_processing_config: Optional custom post processing configuration
|
|
755
|
+
stream_key: Stream key for the inference
|
|
756
|
+
stream_info: Stream info for the inference (optional)
|
|
757
|
+
context: Optional processing context
|
|
758
|
+
custom_post_processing_config: Optional custom post processing configuration
|
|
759
|
+
Returns:
|
|
760
|
+
ProcessingResult: Standardized result object
|
|
761
|
+
"""
|
|
762
|
+
start_time = time.time()
|
|
763
|
+
|
|
764
|
+
try:
|
|
765
|
+
if config:
|
|
766
|
+
try:
|
|
767
|
+
config = self._parse_config(config)
|
|
768
|
+
except Exception as e:
|
|
769
|
+
logger.error(f"Failed to parse config: {e}", exc_info=True)
|
|
770
|
+
raise ValueError(f"Failed to parse config: {e}")
|
|
771
|
+
|
|
772
|
+
parsed_config = config or self.post_processing_config
|
|
773
|
+
|
|
774
|
+
if not parsed_config:
|
|
775
|
+
raise ValueError("No valid configuration found")
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
# Get cached use case instance (await since it's async now)
|
|
779
|
+
use_case = await self._get_use_case_instance(parsed_config, stream_key)
|
|
780
|
+
|
|
781
|
+
# Create context if not provided
|
|
782
|
+
if context is None:
|
|
783
|
+
context = ProcessingContext()
|
|
784
|
+
|
|
785
|
+
# Process with use case using dispatch pattern
|
|
786
|
+
result = await self._dispatch_use_case_processing(
|
|
787
|
+
use_case, data, parsed_config, input_bytes, context, stream_info
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
# Add processing time
|
|
791
|
+
result.processing_time = time.time() - start_time
|
|
792
|
+
|
|
793
|
+
# Update statistics
|
|
794
|
+
self._update_statistics(result)
|
|
795
|
+
|
|
796
|
+
return result
|
|
797
|
+
|
|
798
|
+
except Exception as e:
|
|
799
|
+
processing_time = time.time() - start_time
|
|
800
|
+
logger.error(f"Processing failed: {str(e)}", exc_info=True)
|
|
801
|
+
|
|
802
|
+
error_result = self._create_error_result(
|
|
803
|
+
str(e), type(e).__name__, context=context
|
|
804
|
+
)
|
|
805
|
+
error_result.processing_time = processing_time
|
|
806
|
+
|
|
807
|
+
# Update statistics
|
|
808
|
+
self._update_statistics(error_result)
|
|
809
|
+
|
|
810
|
+
return error_result
|
|
811
|
+
|
|
812
|
+
async def process_simple(
|
|
813
|
+
self,
|
|
814
|
+
data: Any,
|
|
815
|
+
usecase: str,
|
|
816
|
+
category: Optional[str] = None,
|
|
817
|
+
context: Optional[ProcessingContext] = None,
|
|
818
|
+
stream_key: Optional[str] = None,
|
|
819
|
+
stream_info: Optional[Dict[str, Any]] = None,
|
|
820
|
+
**config_params,
|
|
821
|
+
) -> ProcessingResult:
|
|
822
|
+
"""
|
|
823
|
+
Simple processing interface for quick use cases.
|
|
824
|
+
|
|
825
|
+
Args:
|
|
826
|
+
data: Raw model output
|
|
827
|
+
usecase: Use case name ('people_counting', 'customer_service', etc.)
|
|
828
|
+
category: Use case category (auto-detected if not provided)
|
|
829
|
+
context: Optional processing context
|
|
830
|
+
stream_key: Optional stream key for caching
|
|
831
|
+
stream_info: Stream info for the inference (optional)
|
|
832
|
+
**config_params: Configuration parameters
|
|
833
|
+
|
|
834
|
+
Returns:
|
|
835
|
+
ProcessingResult: Standardized result object
|
|
836
|
+
"""
|
|
837
|
+
try:
|
|
838
|
+
# Auto-detect category if not provided
|
|
839
|
+
if category is None:
|
|
840
|
+
if usecase == "people_counting":
|
|
841
|
+
category = "general"
|
|
842
|
+
elif usecase == "customer_service":
|
|
843
|
+
category = "sales"
|
|
844
|
+
elif usecase in ["color_detection", "video_color_classification"]:
|
|
845
|
+
category = "visual_appearance"
|
|
846
|
+
elif usecase == "people_tracking":
|
|
847
|
+
category = "general"
|
|
848
|
+
else:
|
|
849
|
+
category = "general" # Default fallback
|
|
850
|
+
|
|
851
|
+
# Create configuration
|
|
852
|
+
config = self.create_config(usecase, category=category, **config_params)
|
|
853
|
+
return await self.process(
|
|
854
|
+
data,
|
|
855
|
+
config,
|
|
856
|
+
context=context,
|
|
857
|
+
stream_key=stream_key,
|
|
858
|
+
stream_info=stream_info,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
except Exception as e:
|
|
862
|
+
logger.error(f"Simple processing failed: {str(e)}", exc_info=True)
|
|
863
|
+
return self._create_error_result(
|
|
864
|
+
str(e), type(e).__name__, usecase, category or "general", context
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
async def process_from_file(
|
|
868
|
+
self,
|
|
869
|
+
data: Any,
|
|
870
|
+
config_file: Union[str, Path],
|
|
871
|
+
context: Optional[ProcessingContext] = None,
|
|
872
|
+
stream_key: Optional[str] = None,
|
|
873
|
+
stream_info: Optional[Dict[str, Any]] = None,
|
|
874
|
+
) -> ProcessingResult:
|
|
875
|
+
"""
|
|
876
|
+
Process data using configuration from file.
|
|
877
|
+
|
|
878
|
+
Args:
|
|
879
|
+
data: Raw model output
|
|
880
|
+
config_file: Path to configuration file (JSON or YAML)
|
|
881
|
+
context: Optional processing context
|
|
882
|
+
stream_key: Optional stream key for caching
|
|
883
|
+
stream_info: Stream info for the inference (optional)
|
|
884
|
+
Returns:
|
|
885
|
+
ProcessingResult: Standardized result object
|
|
886
|
+
"""
|
|
887
|
+
try:
|
|
888
|
+
config = config_manager.load_from_file(config_file)
|
|
889
|
+
return await self.process(
|
|
890
|
+
data,
|
|
891
|
+
config,
|
|
892
|
+
context=context,
|
|
893
|
+
stream_key=stream_key,
|
|
894
|
+
stream_info=stream_info,
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
except Exception as e:
|
|
898
|
+
logger.error(f"File-based processing failed: {str(e)}", exc_info=True)
|
|
899
|
+
return self._create_error_result(
|
|
900
|
+
f"Failed to process with config file: {str(e)}",
|
|
901
|
+
type(e).__name__,
|
|
902
|
+
context=context,
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
def create_config(
|
|
906
|
+
self, usecase: str, category: str = "general", **kwargs
|
|
907
|
+
) -> BaseConfig:
|
|
908
|
+
"""
|
|
909
|
+
Create a validated configuration object.
|
|
910
|
+
|
|
911
|
+
Args:
|
|
912
|
+
usecase: Use case name
|
|
913
|
+
category: Use case category
|
|
914
|
+
**kwargs: Configuration parameters
|
|
915
|
+
|
|
916
|
+
Returns:
|
|
917
|
+
BaseConfig: Validated configuration object
|
|
918
|
+
"""
|
|
919
|
+
return config_manager.create_config(usecase, category=category, **kwargs)
|
|
920
|
+
|
|
921
|
+
def load_config(self, file_path: Union[str, Path]) -> BaseConfig:
|
|
922
|
+
"""Load configuration from file."""
|
|
923
|
+
return config_manager.load_from_file(file_path)
|
|
924
|
+
|
|
925
|
+
def save_config(
|
|
926
|
+
self, config: BaseConfig, file_path: Union[str, Path], format: str = "json"
|
|
927
|
+
) -> None:
|
|
928
|
+
"""Save configuration to file."""
|
|
929
|
+
config_manager.save_to_file(config, file_path, format)
|
|
930
|
+
|
|
931
|
+
def get_config_template(self, usecase: str) -> Dict[str, Any]:
|
|
932
|
+
"""Get configuration template for a use case."""
|
|
933
|
+
return config_manager.get_config_template(usecase)
|
|
934
|
+
|
|
935
|
+
def list_available_usecases(self) -> Dict[str, List[str]]:
|
|
936
|
+
"""List all available use cases by category."""
|
|
937
|
+
return registry.list_use_cases()
|
|
938
|
+
|
|
939
|
+
def get_supported_usecases(self) -> List[str]:
|
|
940
|
+
"""Get list of supported use case names."""
|
|
941
|
+
return config_manager.list_supported_usecases()
|
|
942
|
+
|
|
943
|
+
def get_use_case_schema(
|
|
944
|
+
self, usecase: str, category: str = "general"
|
|
945
|
+
) -> Dict[str, Any]:
|
|
946
|
+
"""
|
|
947
|
+
Get JSON schema for a use case configuration.
|
|
948
|
+
|
|
949
|
+
Args:
|
|
950
|
+
usecase: Use case name
|
|
951
|
+
category: Use case category
|
|
952
|
+
|
|
953
|
+
Returns:
|
|
954
|
+
Dict[str, Any]: JSON schema for the use case
|
|
955
|
+
"""
|
|
956
|
+
use_case_class = registry.get_use_case(category, usecase)
|
|
957
|
+
if not use_case_class:
|
|
958
|
+
raise ValueError(f"Use case '{category}/{usecase}' not found")
|
|
959
|
+
|
|
960
|
+
use_case = use_case_class()
|
|
961
|
+
return use_case.get_config_schema()
|
|
962
|
+
|
|
963
|
+
def validate_config(self, config: Union[BaseConfig, Dict[str, Any]]) -> List[str]:
|
|
964
|
+
"""
|
|
965
|
+
Validate a configuration object or dictionary.
|
|
966
|
+
|
|
967
|
+
Args:
|
|
968
|
+
config: Configuration to validate
|
|
969
|
+
|
|
970
|
+
Returns:
|
|
971
|
+
List[str]: List of validation errors (empty if valid)
|
|
972
|
+
"""
|
|
973
|
+
try:
|
|
974
|
+
if isinstance(config, dict):
|
|
975
|
+
usecase = config.get("usecase")
|
|
976
|
+
if not usecase:
|
|
977
|
+
return ["Configuration must specify 'usecase'"]
|
|
978
|
+
|
|
979
|
+
category = config.get("category", "general")
|
|
980
|
+
parsed_config = config_manager.create_config(
|
|
981
|
+
usecase, category=category, **config
|
|
982
|
+
)
|
|
983
|
+
return parsed_config.validate()
|
|
984
|
+
elif isinstance(config, BaseConfig):
|
|
985
|
+
return config.validate()
|
|
986
|
+
else:
|
|
987
|
+
return [f"Invalid configuration type: {type(config)}"]
|
|
988
|
+
|
|
989
|
+
except Exception as e:
|
|
990
|
+
return [f"Configuration validation failed: {str(e)}"]
|
|
991
|
+
|
|
992
|
+
def clear_use_case_cache(self) -> None:
|
|
993
|
+
"""Clear the use case instance cache."""
|
|
994
|
+
self._use_case_cache.clear()
|
|
995
|
+
logger.debug("Cleared use case instance cache")
|
|
996
|
+
|
|
997
|
+
def get_cache_stats(self) -> Dict[str, Any]:
|
|
998
|
+
"""
|
|
999
|
+
Get statistics about the use case cache.
|
|
1000
|
+
|
|
1001
|
+
Returns:
|
|
1002
|
+
Dict[str, Any]: Cache statistics
|
|
1003
|
+
"""
|
|
1004
|
+
return {
|
|
1005
|
+
"cached_instances": len(self._use_case_cache),
|
|
1006
|
+
"cache_keys": list(self._use_case_cache.keys()),
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
1010
|
+
"""
|
|
1011
|
+
Get processing statistics.
|
|
1012
|
+
|
|
1013
|
+
Returns:
|
|
1014
|
+
Dict[str, Any]: Processing statistics
|
|
1015
|
+
"""
|
|
1016
|
+
stats = self._statistics.copy()
|
|
1017
|
+
if stats["total_processed"] > 0:
|
|
1018
|
+
stats["success_rate"] = stats["successful"] / stats["total_processed"]
|
|
1019
|
+
stats["failure_rate"] = stats["failed"] / stats["total_processed"]
|
|
1020
|
+
stats["average_processing_time"] = (
|
|
1021
|
+
stats["total_processing_time"] / stats["total_processed"]
|
|
1022
|
+
)
|
|
1023
|
+
else:
|
|
1024
|
+
stats["success_rate"] = 0.0
|
|
1025
|
+
stats["failure_rate"] = 0.0
|
|
1026
|
+
stats["average_processing_time"] = 0.0
|
|
1027
|
+
|
|
1028
|
+
# Add cache statistics
|
|
1029
|
+
stats["cache_stats"] = self.get_cache_stats()
|
|
1030
|
+
|
|
1031
|
+
return stats
|
|
1032
|
+
|
|
1033
|
+
def reset_statistics(self) -> None:
|
|
1034
|
+
"""Reset processing statistics."""
|
|
1035
|
+
self._statistics = {
|
|
1036
|
+
"total_processed": 0,
|
|
1037
|
+
"successful": 0,
|
|
1038
|
+
"failed": 0,
|
|
1039
|
+
"total_processing_time": 0.0,
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
def _parse_config( # TODO: remove all of the kwargs that are not in the use case config
|
|
1043
|
+
self, config: Union[BaseConfig, Dict[str, Any], str, Path]
|
|
1044
|
+
) -> BaseConfig:
|
|
1045
|
+
"""Parse configuration from various input formats."""
|
|
1046
|
+
if isinstance(config, BaseConfig):
|
|
1047
|
+
return config
|
|
1048
|
+
elif isinstance(config, dict):
|
|
1049
|
+
usecase = config.get("usecase")
|
|
1050
|
+
if not usecase:
|
|
1051
|
+
raise ValueError("Configuration dict must contain 'usecase' key")
|
|
1052
|
+
|
|
1053
|
+
category = config.get("category", "general")
|
|
1054
|
+
return config_manager.create_config(usecase, category=category, **config)
|
|
1055
|
+
elif isinstance(config, (str, Path)):
|
|
1056
|
+
return config_manager.load_from_file(config)
|
|
1057
|
+
else:
|
|
1058
|
+
raise ValueError(f"Unsupported config type: {type(config)}")
|
|
1059
|
+
|
|
1060
|
+
def _create_error_result(
|
|
1061
|
+
self,
|
|
1062
|
+
message: str,
|
|
1063
|
+
error_type: str = "ProcessingError",
|
|
1064
|
+
usecase: str = "",
|
|
1065
|
+
category: str = "",
|
|
1066
|
+
context: Optional[ProcessingContext] = None,
|
|
1067
|
+
) -> ProcessingResult:
|
|
1068
|
+
"""Create an error result with structured events."""
|
|
1069
|
+
# Create structured error event
|
|
1070
|
+
error_event = {
|
|
1071
|
+
"type": "processing_error",
|
|
1072
|
+
"stream_time": datetime.now(timezone.utc).strftime("%Y-%m-%d-%H:%M:%S UTC"),
|
|
1073
|
+
"level": "critical",
|
|
1074
|
+
"intensity": 5,
|
|
1075
|
+
"config": {
|
|
1076
|
+
"min_value": 0,
|
|
1077
|
+
"max_value": 10,
|
|
1078
|
+
"level_settings": {"info": 2, "warning": 5, "critical": 7},
|
|
1079
|
+
},
|
|
1080
|
+
"application_name": (
|
|
1081
|
+
f"{usecase.title()} Processing" if usecase else "Post Processing"
|
|
1082
|
+
),
|
|
1083
|
+
"application_version": "1.0",
|
|
1084
|
+
"location_info": None,
|
|
1085
|
+
"human_text": f"Event: Processing Error\nLevel: Critical\nTime: {datetime.now(timezone.utc).strftime('%Y-%m-%d-%H:%M:%S UTC')}\nError: {message}",
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
result = ProcessingResult(
|
|
1089
|
+
data={
|
|
1090
|
+
"events": [error_event],
|
|
1091
|
+
"tracking_stats": [],
|
|
1092
|
+
"error_details": {"message": message, "type": error_type},
|
|
1093
|
+
},
|
|
1094
|
+
status=ProcessingStatus.ERROR,
|
|
1095
|
+
usecase=usecase,
|
|
1096
|
+
category=category,
|
|
1097
|
+
context=context,
|
|
1098
|
+
error_message=message,
|
|
1099
|
+
error_type=error_type,
|
|
1100
|
+
summary=f"Processing failed: {message}",
|
|
1101
|
+
)
|
|
1102
|
+
|
|
1103
|
+
if context:
|
|
1104
|
+
result.processing_time = context.processing_time or 0.0
|
|
1105
|
+
|
|
1106
|
+
return result
|
|
1107
|
+
|
|
1108
|
+
def _update_statistics(self, result: ProcessingResult) -> None:
|
|
1109
|
+
"""Update processing statistics."""
|
|
1110
|
+
self._statistics["total_processed"] += 1
|
|
1111
|
+
self._statistics["total_processing_time"] += result.processing_time
|
|
1112
|
+
|
|
1113
|
+
if result.is_success():
|
|
1114
|
+
self._statistics["successful"] += 1
|
|
1115
|
+
else:
|
|
1116
|
+
self._statistics["failed"] += 1
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
# Convenience functions for backward compatibility and simple usage
|
|
1120
|
+
async def process_simple(
|
|
1121
|
+
data: Any, usecase: str, category: Optional[str] = None, **config
|
|
1122
|
+
) -> ProcessingResult:
|
|
1123
|
+
"""
|
|
1124
|
+
Simple processing function for quick use cases.
|
|
1125
|
+
|
|
1126
|
+
Args:
|
|
1127
|
+
data: Raw model output
|
|
1128
|
+
usecase: Use case name ('people_counting', 'customer_service', etc.)
|
|
1129
|
+
category: Use case category (auto-detected if not provided)
|
|
1130
|
+
**config: Configuration parameters
|
|
1131
|
+
|
|
1132
|
+
Returns:
|
|
1133
|
+
ProcessingResult: Standardized result object
|
|
1134
|
+
"""
|
|
1135
|
+
processor = PostProcessor()
|
|
1136
|
+
return await processor.process_simple(data, usecase, category, **config)
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
def create_config_template(usecase: str) -> Dict[str, Any]:
|
|
1140
|
+
"""
|
|
1141
|
+
Create a configuration template for a use case.
|
|
1142
|
+
|
|
1143
|
+
Args:
|
|
1144
|
+
usecase: Use case name
|
|
1145
|
+
|
|
1146
|
+
Returns:
|
|
1147
|
+
Dict[str, Any]: Configuration template
|
|
1148
|
+
"""
|
|
1149
|
+
processor = PostProcessor()
|
|
1150
|
+
return processor.get_config_template(usecase)
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
def list_available_usecases() -> Dict[str, List[str]]:
|
|
1154
|
+
"""
|
|
1155
|
+
List all available use cases.
|
|
1156
|
+
|
|
1157
|
+
Returns:
|
|
1158
|
+
Dict[str, List[str]]: Available use cases by category
|
|
1159
|
+
"""
|
|
1160
|
+
processor = PostProcessor()
|
|
1161
|
+
return processor.list_available_usecases()
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
def validate_config(config: Union[BaseConfig, Dict[str, Any]]) -> List[str]:
|
|
1165
|
+
"""
|
|
1166
|
+
Validate a configuration.
|
|
1167
|
+
|
|
1168
|
+
Args:
|
|
1169
|
+
config: Configuration to validate
|
|
1170
|
+
|
|
1171
|
+
Returns:
|
|
1172
|
+
List[str]: List of validation errors
|
|
1173
|
+
"""
|
|
1174
|
+
processor = PostProcessor()
|
|
1175
|
+
return processor.validate_config(config)
|