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,356 @@
1
+ """
2
+ Test utilities and base classes for post processing tests.
3
+
4
+ This module provides base test classes, assertion helpers, performance monitoring,
5
+ and common utilities for testing post processing functionality.
6
+ """
7
+
8
+ import unittest
9
+ import time
10
+ import psutil
11
+ import os
12
+ from typing import Any, Dict, List, Optional, Union
13
+ from dataclasses import dataclass
14
+ import tempfile
15
+ import json
16
+ import yaml
17
+
18
+ # Fix imports for proper module resolution
19
+ import sys
20
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src'))
21
+
22
+ from src.matrice_analytics.post_processing import (
23
+ PostProcessor, ProcessingResult, ProcessingContext, ProcessingStatus,
24
+ PeopleCountingConfig, CustomerServiceConfig, BaseConfig
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class PerformanceMetrics:
30
+ """Container for performance test metrics."""
31
+ execution_time: float
32
+ memory_usage_mb: float
33
+ cpu_usage_percent: float
34
+ peak_memory_mb: float
35
+
36
+ def is_within_limits(self, max_time: float = 5.0, max_memory_mb: float = 500.0) -> bool:
37
+ """Check if metrics are within acceptable limits."""
38
+ return (self.execution_time <= max_time and
39
+ self.peak_memory_mb <= max_memory_mb)
40
+
41
+
42
+ class BasePostProcessingTest(unittest.TestCase):
43
+ """Base test class for all post processing tests."""
44
+
45
+ def setUp(self):
46
+ """Set up test environment."""
47
+ self.processor = PostProcessor()
48
+ self.temp_dir = tempfile.mkdtemp()
49
+ self.start_time = time.time()
50
+ self.process = psutil.Process(os.getpid())
51
+ self.initial_memory = self.process.memory_info().rss / 1024 / 1024 # MB
52
+
53
+ def tearDown(self):
54
+ """Clean up test environment."""
55
+ # Clean up temp files
56
+ import shutil
57
+ if os.path.exists(self.temp_dir):
58
+ shutil.rmtree(self.temp_dir)
59
+
60
+ def measure_performance(self, func, *args, **kwargs) -> tuple[Any, PerformanceMetrics]:
61
+ """Measure performance of a function call."""
62
+ # Record initial state
63
+ start_time = time.time()
64
+ start_memory = self.process.memory_info().rss / 1024 / 1024
65
+ cpu_percent_start = self.process.cpu_percent()
66
+
67
+ # Execute function
68
+ result = func(*args, **kwargs)
69
+
70
+ # Record final state
71
+ end_time = time.time()
72
+ end_memory = self.process.memory_info().rss / 1024 / 1024
73
+ cpu_percent_end = self.process.cpu_percent()
74
+
75
+ # Calculate metrics
76
+ metrics = PerformanceMetrics(
77
+ execution_time=end_time - start_time,
78
+ memory_usage_mb=end_memory - start_memory,
79
+ cpu_usage_percent=(cpu_percent_end + cpu_percent_start) / 2,
80
+ peak_memory_mb=max(start_memory, end_memory)
81
+ )
82
+
83
+ return result, metrics
84
+
85
+ def assert_processing_result(self, result: ProcessingResult,
86
+ expected_status: ProcessingStatus = ProcessingStatus.SUCCESS,
87
+ min_insights: int = 0,
88
+ max_warnings: int = 10,
89
+ required_metrics: List[str] = None):
90
+ """Assert processing result meets expectations."""
91
+ self.assertIsInstance(result, ProcessingResult)
92
+ self.assertEqual(result.status, expected_status)
93
+
94
+ if expected_status == ProcessingStatus.SUCCESS:
95
+ self.assertIsNotNone(result.data)
96
+ self.assertGreaterEqual(len(result.insights), min_insights)
97
+ self.assertLessEqual(len(result.warnings), max_warnings)
98
+
99
+ if required_metrics:
100
+ for metric in required_metrics:
101
+ self.assertIn(metric, result.metrics)
102
+
103
+ elif expected_status == ProcessingStatus.ERROR:
104
+ self.assertIsNotNone(result.error_message)
105
+ self.assertIsNotNone(result.error_type)
106
+
107
+ def assert_config_valid(self, config: BaseConfig):
108
+ """Assert configuration is valid."""
109
+ errors = config.validate()
110
+ self.assertEqual(len(errors), 0, f"Configuration validation failed: {errors}")
111
+
112
+ def create_temp_config_file(self, config: Dict[str, Any], format: str = "json") -> str:
113
+ """Create temporary configuration file."""
114
+ if format == "json":
115
+ filename = os.path.join(self.temp_dir, "test_config.json")
116
+ with open(filename, 'w') as f:
117
+ json.dump(config, f, indent=2)
118
+ elif format == "yaml":
119
+ filename = os.path.join(self.temp_dir, "test_config.yaml")
120
+ with open(filename, 'w') as f:
121
+ yaml.dump(config, f, default_flow_style=False)
122
+ else:
123
+ raise ValueError(f"Unsupported format: {format}")
124
+
125
+ return filename
126
+
127
+ def assert_performance_acceptable(self, metrics: PerformanceMetrics,
128
+ max_time: float = 5.0,
129
+ max_memory_mb: float = 500.0):
130
+ """Assert performance metrics are acceptable."""
131
+ self.assertLessEqual(metrics.execution_time, max_time,
132
+ f"Execution time {metrics.execution_time:.2f}s exceeds limit {max_time}s")
133
+ self.assertLessEqual(metrics.peak_memory_mb, max_memory_mb,
134
+ f"Peak memory {metrics.peak_memory_mb:.2f}MB exceeds limit {max_memory_mb}MB")
135
+
136
+
137
+ class StressTestMixin:
138
+ """Mixin for stress testing functionality."""
139
+
140
+ def run_stress_test(self, test_func, iterations: int = 100,
141
+ max_failures: int = 5) -> Dict[str, Any]:
142
+ """Run stress test with multiple iterations."""
143
+ results = {
144
+ "total_iterations": iterations,
145
+ "successful": 0,
146
+ "failed": 0,
147
+ "errors": [],
148
+ "execution_times": [],
149
+ "memory_usage": []
150
+ }
151
+
152
+ for i in range(iterations):
153
+ try:
154
+ start_time = time.time()
155
+ start_memory = psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024
156
+
157
+ test_func()
158
+
159
+ end_time = time.time()
160
+ end_memory = psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024
161
+
162
+ results["successful"] += 1
163
+ results["execution_times"].append(end_time - start_time)
164
+ results["memory_usage"].append(end_memory - start_memory)
165
+
166
+ except Exception as e:
167
+ results["failed"] += 1
168
+ results["errors"].append(f"Iteration {i}: {str(e)}")
169
+
170
+ if results["failed"] > max_failures:
171
+ break
172
+
173
+ # Calculate statistics
174
+ if results["execution_times"]:
175
+ results["avg_execution_time"] = sum(results["execution_times"]) / len(results["execution_times"])
176
+ results["max_execution_time"] = max(results["execution_times"])
177
+ results["min_execution_time"] = min(results["execution_times"])
178
+
179
+ if results["memory_usage"]:
180
+ results["avg_memory_usage"] = sum(results["memory_usage"]) / len(results["memory_usage"])
181
+ results["max_memory_usage"] = max(results["memory_usage"])
182
+
183
+ return results
184
+
185
+
186
+ class ValidationHelpers:
187
+ """Helper methods for validation testing."""
188
+
189
+ @staticmethod
190
+ def create_invalid_bbox() -> List[float]:
191
+ """Create invalid bounding box for testing."""
192
+ return [100, 100, 50, 50] # x2 < x1, y2 < y1
193
+
194
+ @staticmethod
195
+ def create_invalid_polygon() -> List[List[float]]:
196
+ """Create invalid polygon for testing."""
197
+ return [[0, 0], [100, 0]] # Only 2 points
198
+
199
+ @staticmethod
200
+ def create_invalid_confidence() -> float:
201
+ """Create invalid confidence value for testing."""
202
+ return 1.5 # > 1.0
203
+
204
+ @staticmethod
205
+ def create_empty_detection_results() -> List[Dict[str, Any]]:
206
+ """Create empty detection results for edge case testing."""
207
+ return []
208
+
209
+ @staticmethod
210
+ def create_malformed_detection_results() -> List[Dict[str, Any]]:
211
+ """Create malformed detection results for error testing."""
212
+ return [
213
+ {"bbox": [0, 0, 100]}, # Missing coordinate
214
+ {"confidence": 0.8}, # Missing bbox
215
+ {"bbox": [0, 0, 100, 100], "confidence": "high"}, # Wrong type
216
+ ]
217
+
218
+ @staticmethod
219
+ def create_extreme_values() -> Dict[str, Any]:
220
+ """Create extreme values for boundary testing."""
221
+ return {
222
+ "huge_bbox": [0, 0, 999999, 999999],
223
+ "tiny_bbox": [0, 0, 1, 1],
224
+ "negative_coords": [-100, -100, 100, 100],
225
+ "zero_confidence": 0.0,
226
+ "max_confidence": 1.0,
227
+ "huge_polygon": [[i, i] for i in range(1000)],
228
+ "empty_string": "",
229
+ "none_value": None
230
+ }
231
+
232
+
233
+ class ConcurrencyTestMixin:
234
+ """Mixin for testing concurrent processing."""
235
+
236
+ def run_concurrent_test(self, test_func, num_threads: int = 5,
237
+ iterations_per_thread: int = 10) -> Dict[str, Any]:
238
+ """Run concurrent test with multiple threads."""
239
+ import threading
240
+ import queue
241
+
242
+ results_queue = queue.Queue()
243
+ threads = []
244
+
245
+ def worker():
246
+ thread_results = []
247
+ for _ in range(iterations_per_thread):
248
+ try:
249
+ result = test_func()
250
+ thread_results.append(("success", result))
251
+ except Exception as e:
252
+ thread_results.append(("error", str(e)))
253
+ results_queue.put(thread_results)
254
+
255
+ # Start threads
256
+ for _ in range(num_threads):
257
+ thread = threading.Thread(target=worker)
258
+ threads.append(thread)
259
+ thread.start()
260
+
261
+ # Wait for completion
262
+ for thread in threads:
263
+ thread.join()
264
+
265
+ # Collect results
266
+ all_results = []
267
+ while not results_queue.empty():
268
+ all_results.extend(results_queue.get())
269
+
270
+ # Analyze results
271
+ successful = sum(1 for status, _ in all_results if status == "success")
272
+ failed = sum(1 for status, _ in all_results if status == "error")
273
+ errors = [result for status, result in all_results if status == "error"]
274
+
275
+ return {
276
+ "total_operations": len(all_results),
277
+ "successful": successful,
278
+ "failed": failed,
279
+ "success_rate": successful / len(all_results) if all_results else 0,
280
+ "errors": errors[:10] # Limit error list
281
+ }
282
+
283
+
284
+ class MockDataGenerator:
285
+ """Generate mock data for testing."""
286
+
287
+ @staticmethod
288
+ def create_detection_batch(batch_size: int = 100) -> List[Dict[str, Any]]:
289
+ """Create batch of detection results."""
290
+ import random
291
+
292
+ detections = []
293
+ categories = ["person", "car", "bike", "truck", "bus"]
294
+
295
+ for i in range(batch_size):
296
+ width = random.randint(20, 200)
297
+ height = random.randint(20, 200)
298
+ x1 = random.randint(0, 640 - width)
299
+ y1 = random.randint(0, 480 - height)
300
+
301
+ detections.append({
302
+ "bbox": [x1, y1, x1 + width, y1 + height],
303
+ "confidence": random.uniform(0.3, 0.95),
304
+ "category": random.choice(categories),
305
+ "detection_id": i
306
+ })
307
+
308
+ return detections
309
+
310
+ @staticmethod
311
+ def create_tracking_batch(num_tracks: int = 20, frames: int = 10) -> List[Dict[str, Any]]:
312
+ """Create batch of tracking results."""
313
+ import random
314
+
315
+ tracks = []
316
+ categories = ["person", "car", "bike"]
317
+
318
+ for track_id in range(1, num_tracks + 1):
319
+ for frame in range(1, frames + 1):
320
+ # Simulate movement
321
+ base_x = 100 + track_id * 20
322
+ base_y = 100 + track_id * 15
323
+ x = base_x + frame * random.randint(-5, 5)
324
+ y = base_y + frame * random.randint(-5, 5)
325
+
326
+ tracks.append({
327
+ "track_id": track_id,
328
+ "bbox": [x, y, x + 50, y + 80],
329
+ "confidence": random.uniform(0.5, 0.9),
330
+ "category": random.choice(categories),
331
+ "frame": frame,
332
+ "timestamp": time.time() + frame * 0.033
333
+ })
334
+
335
+ return tracks
336
+
337
+
338
+ def assert_processing_result(result: ProcessingResult,
339
+ expected_status: ProcessingStatus = ProcessingStatus.SUCCESS,
340
+ min_insights: int = 0,
341
+ required_data_keys: List[str] = None):
342
+ """Standalone assertion helper for processing results."""
343
+ assert isinstance(result, ProcessingResult)
344
+ assert result.status == expected_status
345
+
346
+ if expected_status == ProcessingStatus.SUCCESS:
347
+ assert result.data is not None
348
+ assert len(result.insights) >= min_insights
349
+
350
+ if required_data_keys:
351
+ for key in required_data_keys:
352
+ assert key in result.data
353
+
354
+ elif expected_status == ProcessingStatus.ERROR:
355
+ assert result.error_message is not None
356
+ assert result.error_type is not None