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.
Files changed (196) hide show
  1. matrice_analytics/__init__.py +28 -0
  2. matrice_analytics/boundary_drawing_internal/README.md +305 -0
  3. matrice_analytics/boundary_drawing_internal/__init__.py +45 -0
  4. matrice_analytics/boundary_drawing_internal/boundary_drawing_internal.py +1207 -0
  5. matrice_analytics/boundary_drawing_internal/boundary_drawing_tool.py +429 -0
  6. matrice_analytics/boundary_drawing_internal/boundary_tool_template.html +1036 -0
  7. matrice_analytics/boundary_drawing_internal/data/.gitignore +12 -0
  8. matrice_analytics/boundary_drawing_internal/example_usage.py +206 -0
  9. matrice_analytics/boundary_drawing_internal/usage/README.md +110 -0
  10. matrice_analytics/boundary_drawing_internal/usage/boundary_drawer_launcher.py +102 -0
  11. matrice_analytics/boundary_drawing_internal/usage/simple_boundary_launcher.py +107 -0
  12. matrice_analytics/post_processing/README.md +455 -0
  13. matrice_analytics/post_processing/__init__.py +732 -0
  14. matrice_analytics/post_processing/advanced_tracker/README.md +650 -0
  15. matrice_analytics/post_processing/advanced_tracker/__init__.py +17 -0
  16. matrice_analytics/post_processing/advanced_tracker/base.py +99 -0
  17. matrice_analytics/post_processing/advanced_tracker/config.py +77 -0
  18. matrice_analytics/post_processing/advanced_tracker/kalman_filter.py +370 -0
  19. matrice_analytics/post_processing/advanced_tracker/matching.py +195 -0
  20. matrice_analytics/post_processing/advanced_tracker/strack.py +230 -0
  21. matrice_analytics/post_processing/advanced_tracker/tracker.py +367 -0
  22. matrice_analytics/post_processing/config.py +146 -0
  23. matrice_analytics/post_processing/core/__init__.py +63 -0
  24. matrice_analytics/post_processing/core/base.py +704 -0
  25. matrice_analytics/post_processing/core/config.py +3291 -0
  26. matrice_analytics/post_processing/core/config_utils.py +925 -0
  27. matrice_analytics/post_processing/face_reg/__init__.py +43 -0
  28. matrice_analytics/post_processing/face_reg/compare_similarity.py +556 -0
  29. matrice_analytics/post_processing/face_reg/embedding_manager.py +950 -0
  30. matrice_analytics/post_processing/face_reg/face_recognition.py +2234 -0
  31. matrice_analytics/post_processing/face_reg/face_recognition_client.py +606 -0
  32. matrice_analytics/post_processing/face_reg/people_activity_logging.py +321 -0
  33. matrice_analytics/post_processing/ocr/__init__.py +0 -0
  34. matrice_analytics/post_processing/ocr/easyocr_extractor.py +250 -0
  35. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
  36. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
  37. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
  38. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
  39. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
  40. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
  41. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
  42. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
  43. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
  44. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
  45. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
  46. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
  47. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
  48. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
  49. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
  50. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
  51. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
  52. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
  53. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
  54. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
  55. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
  56. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
  57. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
  58. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
  59. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
  60. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
  61. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
  62. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
  63. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
  64. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
  65. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
  66. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
  67. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
  68. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
  69. matrice_analytics/post_processing/ocr/postprocessing.py +270 -0
  70. matrice_analytics/post_processing/ocr/preprocessing.py +52 -0
  71. matrice_analytics/post_processing/post_processor.py +1175 -0
  72. matrice_analytics/post_processing/test_cases/__init__.py +1 -0
  73. matrice_analytics/post_processing/test_cases/run_tests.py +143 -0
  74. matrice_analytics/post_processing/test_cases/test_advanced_customer_service.py +841 -0
  75. matrice_analytics/post_processing/test_cases/test_basic_counting_tracking.py +523 -0
  76. matrice_analytics/post_processing/test_cases/test_comprehensive.py +531 -0
  77. matrice_analytics/post_processing/test_cases/test_config.py +852 -0
  78. matrice_analytics/post_processing/test_cases/test_customer_service.py +585 -0
  79. matrice_analytics/post_processing/test_cases/test_data_generators.py +583 -0
  80. matrice_analytics/post_processing/test_cases/test_people_counting.py +510 -0
  81. matrice_analytics/post_processing/test_cases/test_processor.py +524 -0
  82. matrice_analytics/post_processing/test_cases/test_usecases.py +165 -0
  83. matrice_analytics/post_processing/test_cases/test_utilities.py +356 -0
  84. matrice_analytics/post_processing/test_cases/test_utils.py +743 -0
  85. matrice_analytics/post_processing/usecases/Histopathological_Cancer_Detection_img.py +604 -0
  86. matrice_analytics/post_processing/usecases/__init__.py +267 -0
  87. matrice_analytics/post_processing/usecases/abandoned_object_detection.py +797 -0
  88. matrice_analytics/post_processing/usecases/advanced_customer_service.py +1601 -0
  89. matrice_analytics/post_processing/usecases/age_detection.py +842 -0
  90. matrice_analytics/post_processing/usecases/age_gender_detection.py +1085 -0
  91. matrice_analytics/post_processing/usecases/anti_spoofing_detection.py +656 -0
  92. matrice_analytics/post_processing/usecases/assembly_line_detection.py +841 -0
  93. matrice_analytics/post_processing/usecases/banana_defect_detection.py +624 -0
  94. matrice_analytics/post_processing/usecases/basic_counting_tracking.py +667 -0
  95. matrice_analytics/post_processing/usecases/blood_cancer_detection_img.py +881 -0
  96. matrice_analytics/post_processing/usecases/car_damage_detection.py +834 -0
  97. matrice_analytics/post_processing/usecases/car_part_segmentation.py +946 -0
  98. matrice_analytics/post_processing/usecases/car_service.py +1601 -0
  99. matrice_analytics/post_processing/usecases/cardiomegaly_classification.py +864 -0
  100. matrice_analytics/post_processing/usecases/cell_microscopy_segmentation.py +897 -0
  101. matrice_analytics/post_processing/usecases/chicken_pose_detection.py +648 -0
  102. matrice_analytics/post_processing/usecases/child_monitoring.py +814 -0
  103. matrice_analytics/post_processing/usecases/color/clip.py +660 -0
  104. matrice_analytics/post_processing/usecases/color/clip_processor/merges.txt +48895 -0
  105. matrice_analytics/post_processing/usecases/color/clip_processor/preprocessor_config.json +28 -0
  106. matrice_analytics/post_processing/usecases/color/clip_processor/special_tokens_map.json +30 -0
  107. matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer.json +245079 -0
  108. matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer_config.json +32 -0
  109. matrice_analytics/post_processing/usecases/color/clip_processor/vocab.json +1 -0
  110. matrice_analytics/post_processing/usecases/color/color_map_utils.py +70 -0
  111. matrice_analytics/post_processing/usecases/color/color_mapper.py +468 -0
  112. matrice_analytics/post_processing/usecases/color_detection.py +1936 -0
  113. matrice_analytics/post_processing/usecases/color_map_utils.py +70 -0
  114. matrice_analytics/post_processing/usecases/concrete_crack_detection.py +827 -0
  115. matrice_analytics/post_processing/usecases/crop_weed_detection.py +781 -0
  116. matrice_analytics/post_processing/usecases/customer_service.py +1008 -0
  117. matrice_analytics/post_processing/usecases/defect_detection_products.py +936 -0
  118. matrice_analytics/post_processing/usecases/distracted_driver_detection.py +822 -0
  119. matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +585 -0
  120. matrice_analytics/post_processing/usecases/drowsy_driver_detection.py +829 -0
  121. matrice_analytics/post_processing/usecases/dwell_detection.py +829 -0
  122. matrice_analytics/post_processing/usecases/emergency_vehicle_detection.py +827 -0
  123. matrice_analytics/post_processing/usecases/face_emotion.py +813 -0
  124. matrice_analytics/post_processing/usecases/face_recognition.py +827 -0
  125. matrice_analytics/post_processing/usecases/fashion_detection.py +835 -0
  126. matrice_analytics/post_processing/usecases/field_mapping.py +902 -0
  127. matrice_analytics/post_processing/usecases/fire_detection.py +1146 -0
  128. matrice_analytics/post_processing/usecases/flare_analysis.py +836 -0
  129. matrice_analytics/post_processing/usecases/flower_segmentation.py +1006 -0
  130. matrice_analytics/post_processing/usecases/gas_leak_detection.py +837 -0
  131. matrice_analytics/post_processing/usecases/gender_detection.py +832 -0
  132. matrice_analytics/post_processing/usecases/human_activity_recognition.py +871 -0
  133. matrice_analytics/post_processing/usecases/intrusion_detection.py +1672 -0
  134. matrice_analytics/post_processing/usecases/leaf.py +821 -0
  135. matrice_analytics/post_processing/usecases/leaf_disease.py +840 -0
  136. matrice_analytics/post_processing/usecases/leak_detection.py +837 -0
  137. matrice_analytics/post_processing/usecases/license_plate_detection.py +1188 -0
  138. matrice_analytics/post_processing/usecases/license_plate_monitoring.py +1781 -0
  139. matrice_analytics/post_processing/usecases/litter_monitoring.py +717 -0
  140. matrice_analytics/post_processing/usecases/mask_detection.py +869 -0
  141. matrice_analytics/post_processing/usecases/natural_disaster.py +907 -0
  142. matrice_analytics/post_processing/usecases/parking.py +787 -0
  143. matrice_analytics/post_processing/usecases/parking_space_detection.py +822 -0
  144. matrice_analytics/post_processing/usecases/pcb_defect_detection.py +888 -0
  145. matrice_analytics/post_processing/usecases/pedestrian_detection.py +808 -0
  146. matrice_analytics/post_processing/usecases/people_counting.py +706 -0
  147. matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
  148. matrice_analytics/post_processing/usecases/people_tracking.py +1842 -0
  149. matrice_analytics/post_processing/usecases/pipeline_detection.py +605 -0
  150. matrice_analytics/post_processing/usecases/plaque_segmentation_img.py +874 -0
  151. matrice_analytics/post_processing/usecases/pothole_segmentation.py +915 -0
  152. matrice_analytics/post_processing/usecases/ppe_compliance.py +645 -0
  153. matrice_analytics/post_processing/usecases/price_tag_detection.py +822 -0
  154. matrice_analytics/post_processing/usecases/proximity_detection.py +1901 -0
  155. matrice_analytics/post_processing/usecases/road_lane_detection.py +623 -0
  156. matrice_analytics/post_processing/usecases/road_traffic_density.py +832 -0
  157. matrice_analytics/post_processing/usecases/road_view_segmentation.py +915 -0
  158. matrice_analytics/post_processing/usecases/shelf_inventory_detection.py +583 -0
  159. matrice_analytics/post_processing/usecases/shoplifting_detection.py +822 -0
  160. matrice_analytics/post_processing/usecases/shopping_cart_analysis.py +899 -0
  161. matrice_analytics/post_processing/usecases/skin_cancer_classification_img.py +864 -0
  162. matrice_analytics/post_processing/usecases/smoker_detection.py +833 -0
  163. matrice_analytics/post_processing/usecases/solar_panel.py +810 -0
  164. matrice_analytics/post_processing/usecases/suspicious_activity_detection.py +1030 -0
  165. matrice_analytics/post_processing/usecases/template_usecase.py +380 -0
  166. matrice_analytics/post_processing/usecases/theft_detection.py +648 -0
  167. matrice_analytics/post_processing/usecases/traffic_sign_monitoring.py +724 -0
  168. matrice_analytics/post_processing/usecases/underground_pipeline_defect_detection.py +775 -0
  169. matrice_analytics/post_processing/usecases/underwater_pollution_detection.py +842 -0
  170. matrice_analytics/post_processing/usecases/vehicle_monitoring.py +1029 -0
  171. matrice_analytics/post_processing/usecases/warehouse_object_segmentation.py +899 -0
  172. matrice_analytics/post_processing/usecases/waterbody_segmentation.py +923 -0
  173. matrice_analytics/post_processing/usecases/weapon_detection.py +771 -0
  174. matrice_analytics/post_processing/usecases/weld_defect_detection.py +615 -0
  175. matrice_analytics/post_processing/usecases/wildlife_monitoring.py +898 -0
  176. matrice_analytics/post_processing/usecases/windmill_maintenance.py +834 -0
  177. matrice_analytics/post_processing/usecases/wound_segmentation.py +856 -0
  178. matrice_analytics/post_processing/utils/__init__.py +150 -0
  179. matrice_analytics/post_processing/utils/advanced_counting_utils.py +400 -0
  180. matrice_analytics/post_processing/utils/advanced_helper_utils.py +317 -0
  181. matrice_analytics/post_processing/utils/advanced_tracking_utils.py +461 -0
  182. matrice_analytics/post_processing/utils/alerting_utils.py +213 -0
  183. matrice_analytics/post_processing/utils/category_mapping_utils.py +94 -0
  184. matrice_analytics/post_processing/utils/color_utils.py +592 -0
  185. matrice_analytics/post_processing/utils/counting_utils.py +182 -0
  186. matrice_analytics/post_processing/utils/filter_utils.py +261 -0
  187. matrice_analytics/post_processing/utils/format_utils.py +293 -0
  188. matrice_analytics/post_processing/utils/geometry_utils.py +300 -0
  189. matrice_analytics/post_processing/utils/smoothing_utils.py +358 -0
  190. matrice_analytics/post_processing/utils/tracking_utils.py +234 -0
  191. matrice_analytics/py.typed +0 -0
  192. matrice_analytics-0.1.60.dist-info/METADATA +481 -0
  193. matrice_analytics-0.1.60.dist-info/RECORD +196 -0
  194. matrice_analytics-0.1.60.dist-info/WHEEL +5 -0
  195. matrice_analytics-0.1.60.dist-info/licenses/LICENSE.txt +21 -0
  196. matrice_analytics-0.1.60.dist-info/top_level.txt +1 -0
@@ -0,0 +1,523 @@
1
+ """
2
+ Tests for BasicCountingTrackingUseCase.
3
+
4
+ This module tests the basic counting and tracking functionality including
5
+ line crossing detection, zone-based counting, and tracking integration.
6
+ """
7
+
8
+ import unittest
9
+ import time
10
+ from typing import Dict, List, Any
11
+ import tempfile
12
+ import os
13
+
14
+ # Fix imports for proper module resolution
15
+ import sys
16
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src'))
17
+
18
+ from src.matrice_analytics.post_processing import (
19
+ PostProcessor, ProcessingResult, ProcessingStatus, ProcessingContext,
20
+ BasicCountingTrackingUseCase, BaseConfig
21
+ )
22
+ from src.matrice_analytics.post_processing.usecases.basic_counting_tracking import BasicCountingTrackingUseCase, BasicCountingTrackingConfig
23
+ from src.matrice_analytics.post_processing.core.config import TrackingConfig, AlertConfig
24
+
25
+ from .test_utilities import BasePostProcessingTest, StressTestMixin, ConcurrencyTestMixin
26
+ from .test_data_generators import (
27
+ create_detection_results, create_tracking_results, create_zone_polygons,
28
+ create_line_crossing_data, create_basic_counting_tracking_scenarios,
29
+ create_edge_case_data, create_performance_test_data
30
+ )
31
+
32
+
33
+ class TestBasicCountingTrackingUseCase(BasePostProcessingTest, StressTestMixin):
34
+ """Test BasicCountingTrackingUseCase functionality."""
35
+
36
+ def setUp(self):
37
+ """Set up test environment."""
38
+ super().setUp()
39
+ self.use_case = BasicCountingTrackingUseCase()
40
+ self.basic_config = BasicCountingTrackingConfig(
41
+ confidence_threshold=0.5,
42
+ enable_tracking=True
43
+ )
44
+
45
+ def test_use_case_initialization(self):
46
+ """Test use case initialization."""
47
+ self.assertEqual(self.use_case.name, "basic_counting_tracking")
48
+ self.assertEqual(self.use_case.category, "general")
49
+ self.assertIsNotNone(self.use_case.get_config_schema())
50
+
51
+ def test_config_schema_validation(self):
52
+ """Test configuration schema validation."""
53
+ schema = self.use_case.get_config_schema()
54
+
55
+ # Check required schema properties
56
+ self.assertIn("type", schema)
57
+ self.assertEqual(schema["type"], "object")
58
+ self.assertIn("properties", schema)
59
+
60
+ # Check key configuration properties
61
+ properties = schema["properties"]
62
+ self.assertIn("confidence_threshold", properties)
63
+ self.assertIn("enable_tracking", properties)
64
+ self.assertIn("zones", properties)
65
+ self.assertIn("lines", properties)
66
+
67
+ def test_default_config_creation(self):
68
+ """Test default configuration creation."""
69
+ config = self.use_case.create_default_config()
70
+
71
+ self.assertIsInstance(config, BasicCountingTrackingConfig)
72
+ self.assertEqual(config.category, "general")
73
+ self.assertEqual(config.usecase, "basic_counting_tracking")
74
+ self.assertIsNotNone(config.confidence_threshold)
75
+
76
+ # Validate config
77
+ errors = config.validate()
78
+ self.assertEqual(len(errors), 0, f"Default config validation failed: {errors}")
79
+
80
+ def test_config_with_overrides(self):
81
+ """Test configuration creation with overrides."""
82
+ config = self.use_case.create_default_config(
83
+ confidence_threshold=0.7,
84
+ enable_tracking=True,
85
+ zones={"entrance": [[0, 0], [100, 0], [100, 100], [0, 100]]}
86
+ )
87
+
88
+ self.assertEqual(config.confidence_threshold, 0.7)
89
+ self.assertTrue(config.enable_tracking)
90
+ self.assertIn("entrance", config.zones)
91
+
92
+ def test_basic_detection_processing(self):
93
+ """Test basic detection processing."""
94
+ # Create test data
95
+ detections = create_detection_results(
96
+ num_detections=15,
97
+ categories=["person", "car"],
98
+ confidence_range=(0.6, 0.9)
99
+ )
100
+
101
+ # Process with use case
102
+ result = self.use_case.process(detections, self.basic_config)
103
+
104
+ # Validate result
105
+ self.assert_processing_result(
106
+ result,
107
+ expected_status=ProcessingStatus.SUCCESS,
108
+ min_insights=1,
109
+ required_metrics=["total_count", "category_counts"]
110
+ )
111
+
112
+ # Check specific metrics
113
+ self.assertIn("total_count", result.metrics)
114
+ self.assertIn("category_counts", result.metrics)
115
+ self.assertGreater(result.metrics["total_count"], 0)
116
+
117
+ def test_tracking_processing(self):
118
+ """Test tracking data processing."""
119
+ # Create tracking data
120
+ tracks = create_tracking_results(
121
+ num_tracks=8,
122
+ frames=15,
123
+ categories=["person", "vehicle"]
124
+ )
125
+
126
+ # Create config with tracking enabled
127
+ config = self.use_case.create_default_config(
128
+ confidence_threshold=0.5,
129
+ enable_tracking=True
130
+ )
131
+
132
+ # Process
133
+ result = self.use_case.process(tracks, config)
134
+
135
+ # Validate result
136
+ self.assert_processing_result(result, min_insights=1)
137
+
138
+ # Check tracking-specific metrics
139
+ self.assertIn("unique_tracks", result.metrics)
140
+ self.assertIn("track_duration_stats", result.metrics)
141
+ self.assertGreater(result.metrics["unique_tracks"], 0)
142
+
143
+ def test_zone_based_counting(self):
144
+ """Test zone-based counting functionality."""
145
+ # Create zones
146
+ zones = create_zone_polygons(["entrance", "lobby", "exit"])
147
+
148
+ # Create detection data
149
+ detections = create_detection_results(num_detections=20)
150
+
151
+ # Create config with zones
152
+ config = self.use_case.create_default_config(
153
+ confidence_threshold=0.5,
154
+ zones=zones
155
+ )
156
+
157
+ # Process
158
+ result = self.use_case.process(detections, config)
159
+
160
+ # Validate result
161
+ self.assert_processing_result(result, min_insights=1)
162
+
163
+ # Check zone analysis
164
+ self.assertIn("zone_analysis", result.metrics)
165
+ zone_analysis = result.metrics["zone_analysis"]
166
+
167
+ for zone_name in zones.keys():
168
+ self.assertIn(zone_name, zone_analysis)
169
+
170
+ def test_line_crossing_detection(self):
171
+ """Test line crossing detection."""
172
+ # Define crossing lines
173
+ lines = {
174
+ "entrance_line": [[100, 200], [200, 200]],
175
+ "exit_line": [[400, 200], [500, 200]]
176
+ }
177
+
178
+ # Create crossing data
179
+ crossing_data = create_line_crossing_data(lines, num_tracks=6, frames=25)
180
+
181
+ # Create config with lines
182
+ config = self.use_case.create_default_config(
183
+ confidence_threshold=0.5,
184
+ enable_tracking=True,
185
+ lines=lines
186
+ )
187
+
188
+ # Process
189
+ result = self.use_case.process(crossing_data, config)
190
+
191
+ # Validate result
192
+ self.assert_processing_result(result, min_insights=1)
193
+
194
+ # Check line crossing analysis
195
+ self.assertIn("line_crossings", result.metrics)
196
+ line_crossings = result.metrics["line_crossings"]
197
+
198
+ for line_name in lines.keys():
199
+ self.assertIn(line_name, line_crossings)
200
+
201
+ def test_combined_zones_and_lines(self):
202
+ """Test processing with both zones and lines."""
203
+ # Create zones and lines
204
+ zones = create_zone_polygons(["zone_a", "zone_b"])
205
+ lines = {"crossing_line": [[200, 100], [200, 300]]}
206
+
207
+ # Create tracking data
208
+ tracks = create_tracking_results(num_tracks=10, frames=20)
209
+
210
+ # Create comprehensive config
211
+ config = self.use_case.create_default_config(
212
+ confidence_threshold=0.5,
213
+ enable_tracking=True,
214
+ zones=zones,
215
+ lines=lines
216
+ )
217
+
218
+ # Process
219
+ result = self.use_case.process(tracks, config)
220
+
221
+ # Validate result
222
+ self.assert_processing_result(result, min_insights=2)
223
+
224
+ # Check both zone and line analysis
225
+ self.assertIn("zone_analysis", result.metrics)
226
+ self.assertIn("line_crossings", result.metrics)
227
+
228
+ def test_confidence_filtering(self):
229
+ """Test confidence threshold filtering."""
230
+ # Create data with varying confidence levels
231
+ low_conf_detections = create_detection_results(
232
+ num_detections=10,
233
+ confidence_range=(0.2, 0.4)
234
+ )
235
+ high_conf_detections = create_detection_results(
236
+ num_detections=10,
237
+ confidence_range=(0.7, 0.9)
238
+ )
239
+
240
+ all_detections = low_conf_detections + high_conf_detections
241
+
242
+ # Test with high threshold
243
+ high_threshold_config = self.use_case.create_default_config(
244
+ confidence_threshold=0.6
245
+ )
246
+
247
+ result = self.use_case.process(all_detections, high_threshold_config)
248
+
249
+ # Should filter out low confidence detections
250
+ self.assert_processing_result(result)
251
+ filtered_count = result.metrics["total_count"]
252
+
253
+ # Test with low threshold
254
+ low_threshold_config = self.use_case.create_default_config(
255
+ confidence_threshold=0.1
256
+ )
257
+
258
+ result = self.use_case.process(all_detections, low_threshold_config)
259
+ unfiltered_count = result.metrics["total_count"]
260
+
261
+ # High threshold should result in fewer detections
262
+ self.assertLessEqual(filtered_count, unfiltered_count)
263
+
264
+ def test_empty_data_handling(self):
265
+ """Test handling of empty input data."""
266
+ empty_data = []
267
+
268
+ result = self.use_case.process(empty_data, self.basic_config)
269
+
270
+ self.assert_processing_result(result)
271
+ self.assertEqual(result.metrics["total_count"], 0)
272
+ self.assertIn("No objects detected", result.insights)
273
+
274
+ def test_malformed_data_handling(self):
275
+ """Test handling of malformed input data."""
276
+ edge_cases = create_edge_case_data()
277
+ malformed_data = edge_cases["malformed_data"]
278
+
279
+ # Should handle gracefully without crashing
280
+ result = self.use_case.process(malformed_data, self.basic_config)
281
+
282
+ # May succeed with warnings or fail gracefully
283
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING, ProcessingStatus.ERROR])
284
+
285
+ if result.status == ProcessingStatus.SUCCESS:
286
+ self.assertGreaterEqual(len(result.warnings), 1)
287
+
288
+ def test_invalid_configuration(self):
289
+ """Test handling of invalid configuration."""
290
+ # Create invalid config
291
+ invalid_config = BasicCountingTrackingConfig(
292
+ confidence_threshold=1.5, # Invalid: > 1.0
293
+ enable_tracking=True
294
+ )
295
+
296
+ # Validation should fail
297
+ errors = invalid_config.validate()
298
+ self.assertGreater(len(errors), 0)
299
+
300
+ # Processing should handle invalid config gracefully
301
+ detections = create_detection_results(5)
302
+ result = self.use_case.process(detections, invalid_config)
303
+
304
+ # Should return error or warning
305
+ self.assertIn(result.status, [ProcessingStatus.ERROR, ProcessingStatus.WARNING])
306
+
307
+ def test_large_dataset_processing(self):
308
+ """Test processing of large datasets."""
309
+ # Create large dataset
310
+ large_data = create_performance_test_data("large")
311
+ detections = large_data["detection_data"]
312
+
313
+ # Measure performance
314
+ result, metrics = self.measure_performance(
315
+ self.use_case.process,
316
+ detections,
317
+ self.basic_config
318
+ )
319
+
320
+ # Validate result
321
+ self.assert_processing_result(result)
322
+
323
+ # Check performance is acceptable
324
+ self.assert_performance_acceptable(metrics, max_time=10.0, max_memory_mb=1000.0)
325
+
326
+ def test_stress_testing(self):
327
+ """Test stress testing with multiple iterations."""
328
+ def test_iteration():
329
+ detections = create_detection_results(50)
330
+ result = self.use_case.process(detections, self.basic_config)
331
+ self.assert_processing_result(result)
332
+ return result
333
+
334
+ stress_results = self.run_stress_test(test_iteration, iterations=50, max_failures=5)
335
+
336
+ # Check stress test results
337
+ self.assertGreaterEqual(stress_results["successful"], 45) # 90% success rate
338
+ self.assertLessEqual(stress_results["failed"], 5)
339
+
340
+ if stress_results["execution_times"]:
341
+ self.assertLess(stress_results["avg_execution_time"], 2.0)
342
+
343
+ def test_scenarios_from_data_generator(self):
344
+ """Test predefined scenarios from data generator."""
345
+ scenarios = create_basic_counting_tracking_scenarios()
346
+
347
+ for scenario in scenarios:
348
+ with self.subTest(scenario=scenario["name"]):
349
+ # Create config from scenario
350
+ config_dict = scenario["config"]
351
+ config = self.use_case.create_default_config(**config_dict)
352
+
353
+ # Process scenario data
354
+ result = self.use_case.process(scenario["data"], config)
355
+
356
+ # Validate result
357
+ self.assert_processing_result(result, min_insights=1)
358
+
359
+ # Check scenario-specific expectations
360
+ if scenario["name"] == "line_crossing":
361
+ self.assertIn("line_crossings", result.metrics)
362
+ elif scenario["name"] == "zone_tracking":
363
+ self.assertIn("zone_analysis", result.metrics)
364
+
365
+ def test_processing_context_usage(self):
366
+ """Test processing with custom context."""
367
+ detections = create_detection_results(10)
368
+
369
+ # Create custom context
370
+ context = ProcessingContext(
371
+ confidence_threshold=0.6,
372
+ enable_tracking=True,
373
+ enable_counting=True,
374
+ metadata={"test_id": "context_test"}
375
+ )
376
+
377
+ result = self.use_case.process(detections, self.basic_config, context)
378
+
379
+ self.assert_processing_result(result)
380
+ self.assertIsNotNone(result.context)
381
+ self.assertEqual(result.context.metadata["test_id"], "context_test")
382
+
383
+ def test_metrics_completeness(self):
384
+ """Test that all expected metrics are present."""
385
+ # Create comprehensive test data
386
+ tracks = create_tracking_results(num_tracks=5, frames=10)
387
+ zones = create_zone_polygons(["test_zone"])
388
+
389
+ config = self.use_case.create_default_config(
390
+ confidence_threshold=0.5,
391
+ enable_tracking=True,
392
+ zones=zones
393
+ )
394
+
395
+ result = self.use_case.process(tracks, config)
396
+
397
+ # Check expected metrics
398
+ expected_metrics = [
399
+ "total_count",
400
+ "category_counts",
401
+ "unique_tracks",
402
+ "zone_analysis",
403
+ "processing_info"
404
+ ]
405
+
406
+ for metric in expected_metrics:
407
+ self.assertIn(metric, result.metrics, f"Missing expected metric: {metric}")
408
+
409
+ def test_insights_generation(self):
410
+ """Test insight generation."""
411
+ detections = create_detection_results(
412
+ num_detections=25,
413
+ categories=["person", "vehicle"]
414
+ )
415
+
416
+ result = self.use_case.process(detections, self.basic_config)
417
+
418
+ self.assert_processing_result(result, min_insights=1)
419
+
420
+ # Check that insights are meaningful
421
+ insights_text = " ".join(result.insights).lower()
422
+ self.assertTrue(
423
+ any(keyword in insights_text for keyword in ["detected", "counted", "objects", "categories"]),
424
+ "Insights should contain meaningful information"
425
+ )
426
+
427
+ def test_summary_generation(self):
428
+ """Test summary generation."""
429
+ detections = create_detection_results(15)
430
+ result = self.use_case.process(detections, self.basic_config)
431
+
432
+ self.assert_processing_result(result)
433
+ self.assertIsNotNone(result.summary)
434
+ self.assertGreater(len(result.summary), 0)
435
+
436
+ # Summary should contain key information
437
+ summary_lower = result.summary.lower()
438
+ self.assertTrue(
439
+ any(keyword in summary_lower for keyword in ["detected", "counted", "objects"]),
440
+ f"Summary should contain meaningful information: {result.summary}"
441
+ )
442
+
443
+
444
+ class TestBasicCountingTrackingIntegration(BasePostProcessingTest, ConcurrencyTestMixin):
445
+ """Integration tests for BasicCountingTrackingUseCase."""
446
+
447
+ def setUp(self):
448
+ """Set up test environment."""
449
+ super().setUp()
450
+ self.use_case = BasicCountingTrackingUseCase()
451
+
452
+ def test_processor_integration(self):
453
+ """Test integration with PostProcessor."""
454
+ detections = create_detection_results(10)
455
+
456
+ # Test through PostProcessor
457
+ result = self.processor.process_simple(
458
+ detections,
459
+ "basic_counting_tracking",
460
+ confidence_threshold=0.5,
461
+ enable_tracking=True
462
+ )
463
+
464
+ self.assert_processing_result(result)
465
+
466
+ def test_concurrent_processing(self):
467
+ """Test concurrent processing."""
468
+ def process_data():
469
+ detections = create_detection_results(20)
470
+ config = self.use_case.create_default_config(confidence_threshold=0.5)
471
+ return self.use_case.process(detections, config)
472
+
473
+ concurrent_results = self.run_concurrent_test(
474
+ process_data,
475
+ num_threads=3,
476
+ iterations_per_thread=5
477
+ )
478
+
479
+ # Check concurrent processing results
480
+ self.assertGreaterEqual(concurrent_results["success_rate"], 0.9)
481
+ self.assertEqual(concurrent_results["failed"], 0)
482
+
483
+ def test_configuration_file_integration(self):
484
+ """Test integration with configuration files."""
485
+ # Create config dict
486
+ config_dict = {
487
+ "category": "general",
488
+ "usecase": "basic_counting_tracking",
489
+ "confidence_threshold": 0.6,
490
+ "enable_tracking": True,
491
+ "zones": create_zone_polygons(["test_zone"])
492
+ }
493
+
494
+ # Create temporary config file
495
+ config_file = self.create_temp_config_file(config_dict, "json")
496
+
497
+ # Test processing from file
498
+ detections = create_detection_results(15)
499
+ result = self.processor.process_from_file(detections, config_file)
500
+
501
+ self.assert_processing_result(result)
502
+
503
+ def test_memory_stability(self):
504
+ """Test memory stability over multiple processing cycles."""
505
+ initial_memory = self.process.memory_info().rss / 1024 / 1024
506
+
507
+ # Process multiple batches
508
+ for i in range(10):
509
+ detections = create_detection_results(100)
510
+ config = self.use_case.create_default_config(confidence_threshold=0.5)
511
+ result = self.use_case.process(detections, config)
512
+ self.assert_processing_result(result)
513
+
514
+ final_memory = self.process.memory_info().rss / 1024 / 1024
515
+ memory_increase = final_memory - initial_memory
516
+
517
+ # Memory increase should be reasonable (< 100MB)
518
+ self.assertLess(memory_increase, 100.0,
519
+ f"Memory increased by {memory_increase:.2f}MB, possible memory leak")
520
+
521
+
522
+ if __name__ == "__main__":
523
+ unittest.main()