matrice 1.0.99127__py3-none-any.whl → 1.0.99128__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.
@@ -24,6 +24,7 @@ APP_NAME_TO_USECASE = {
24
24
  "shopping_cart_analysis": "shopping_cart_analysis",
25
25
  "car_part_segmentation": "car_part_segmentation",
26
26
  "weld_defect_detection" : "weld_defect_detection",
27
+ "fruit_monitoring" : "fruit_monitoring",
27
28
  }
28
29
 
29
30
  APP_NAME_TO_CATEGORY = {
@@ -52,6 +53,7 @@ APP_NAME_TO_CATEGORY = {
52
53
  "shopping_cart_analysis": "retail",
53
54
  "car_part_segmentation": "automobile",
54
55
  "weld_defect_detection" : "weld",
56
+ "fruit_monitoring" : "agriculture",
55
57
  }
56
58
 
57
59
  def get_usecase_from_app_name(app_name: str) -> str:
@@ -1,9 +1,9 @@
1
1
  from typing import Any, Dict, List, Optional
2
- from dataclasses import asdict, dataclass, field
2
+ from dataclasses import asdict
3
3
  import time
4
4
  from datetime import datetime, timezone
5
5
 
6
- from ..core.base import BaseProcessor, ProcessingContext, ProcessingResult, ConfigProtocol, ResultFormat
6
+ from ..core.base import BaseProcessor, ProcessingContext, ProcessingResult, ConfigProtocol
7
7
  from ..utils import (
8
8
  filter_by_confidence,
9
9
  filter_by_categories,
@@ -16,11 +16,13 @@ from ..utils import (
16
16
  BBoxSmoothingConfig,
17
17
  BBoxSmoothingTracker
18
18
  )
19
+ from dataclasses import dataclass, field
19
20
  from ..core.config import BaseConfig, AlertConfig, ZoneConfig
20
21
 
22
+
21
23
  @dataclass
22
24
  class BananaMonitoringConfig(BaseConfig):
23
- """Configuration for Fruit detection use case in fruit monitoring."""
25
+ """Configuration for banana defect detection use case."""
24
26
  enable_smoothing: bool = True
25
27
  smoothing_algorithm: str = "observability"
26
28
  smoothing_window_size: int = 20
@@ -45,139 +47,23 @@ class BananaMonitoringConfig(BaseConfig):
45
47
  }
46
48
  )
47
49
 
48
- class BananaMonitoringUseCase(BaseProcessor):
49
- def _get_track_ids_info(self, detections: list) -> Dict[str, Any]:
50
- frame_track_ids = set()
51
- for det in detections:
52
- tid = det.get('track_id')
53
- if tid is not None:
54
- frame_track_ids.add(tid)
55
- total_track_ids = set()
56
- for s in getattr(self, '_fruit_total_track_ids', {}).values():
57
- total_track_ids.update(s)
58
- return {
59
- "total_count": len(total_track_ids),
60
- "current_frame_count": len(frame_track_ids),
61
- "total_unique_track_ids": len(total_track_ids),
62
- "current_frame_track_ids": list(frame_track_ids),
63
- "last_update_time": time.time(),
64
- "total_frames_processed": getattr(self, '_total_frame_counter', 0)
65
- }
66
-
67
- @staticmethod
68
- def _iou(bbox1, bbox2):
69
- x1 = max(bbox1["xmin"], bbox2["xmin"])
70
- y1 = max(bbox1["ymin"], bbox2["ymin"])
71
- x2 = min(bbox1["xmax"], bbox2["xmax"])
72
- y2 = min(bbox1["ymax"], bbox2["ymax"])
73
- inter_w = max(0, x2 - x1)
74
- inter_h = max(0, y2 - y1)
75
- inter_area = inter_w * inter_h
76
- area1 = (bbox1["xmax"] - bbox1["xmin"]) * (bbox1["ymax"] - bbox1["ymin"])
77
- area2 = (bbox2["xmax"] - bbox2["xmin"]) * (bbox2["ymax"] - bbox2["ymin"])
78
- union = area1 + area2 - inter_area
79
- if union == 0:
80
- return 0.0
81
- return inter_area / union
82
-
83
- @staticmethod
84
- def _deduplicate_fruits(detections, iou_thresh=0.7):
85
- filtered = []
86
- used = [False] * len(detections)
87
- for i, det in enumerate(detections):
88
- if used[i]:
89
- continue
90
- group = [i]
91
- for j in range(i+1, len(detections)):
92
- if used[j]:
93
- continue
94
- if det.get("category") == detections[j].get("category"):
95
- bbox1 = det.get("bounding_box")
96
- bbox2 = detections[j].get("bounding_box")
97
- if bbox1 and bbox2:
98
- iou = BananaMonitoringUseCase._iou(bbox1, bbox2)
99
- if iou > iou_thresh:
100
- used[j] = True
101
- group.append(j)
102
- best_idx = max(group, key=lambda idx: detections[idx].get("confidence", 0))
103
- filtered.append(detections[best_idx])
104
- used[best_idx] = True
105
- return filtered
106
-
107
- def _update_fruit_tracking_state(self, detections: list):
108
- if not hasattr(self, "_fruit_total_track_ids"):
109
- self._fruit_total_track_ids = {cat: set() for cat in self.fruit_categories}
110
- self._fruit_current_frame_track_ids = {cat: set() for cat in self.fruit_categories}
111
-
112
- for det in detections:
113
- cat = det.get("category")
114
- raw_track_id = det.get("track_id")
115
- if cat not in self.fruit_categories or raw_track_id is None:
116
- continue
117
- bbox = det.get("bounding_box", det.get("bbox"))
118
- canonical_id = self._merge_or_register_track(raw_track_id, bbox)
119
- det["track_id"] = canonical_id
120
- self._fruit_total_track_ids.setdefault(cat, set()).add(canonical_id)
121
- self._fruit_current_frame_track_ids[cat].add(canonical_id)
122
-
123
- def get_total_fruit_counts(self):
124
- return {cat: len(ids) for cat, ids in getattr(self, '_fruit_total_track_ids', {}).items()}
125
-
126
- def _format_timestamp_for_video(self, timestamp: float) -> str:
127
- hours = int(timestamp // 3600)
128
- minutes = int((timestamp % 3600) // 60)
129
- seconds = timestamp % 60
130
- return f"{hours:02d}:{minutes:02d}:{seconds:06.2f}"
131
-
132
- def _format_timestamp_for_stream(self, timestamp: float) -> str:
133
- dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
134
- return dt.strftime('%Y:%m:%d %H:%M:%S')
135
-
136
- def _get_current_timestamp_str(self, stream_info: Optional[Dict[str, Any]]) -> str:
137
- if not stream_info:
138
- return "00:00:00.00"
139
- if stream_info.get("input_settings", {}).get("stream_type", "video_file") == "video_file":
140
- stream_time_str = stream_info.get("video_timestamp", "")
141
- return stream_time_str[:8]
142
- else:
143
- stream_time_str = stream_info.get("stream_time", "")
144
- if stream_time_str:
145
- try:
146
- timestamp_str = stream_time_str.replace(" UTC", "")
147
- dt = datetime.strptime(timestamp_str, "%Y-%m-%d-%H:%M:%S.%f")
148
- timestamp = dt.replace(tzinfo=timezone.utc).timestamp()
149
- return self._format_timestamp_for_stream(timestamp)
150
- except:
151
- return self._format_timestamp_for_stream(time.time())
152
- else:
153
- return self._format_timestamp_for_stream(time.time())
154
-
155
- def _get_start_timestamp_str(self, stream_info: Optional[Dict[str, Any]]) -> str:
156
- if not stream_info:
157
- return "00:00:00"
158
- is_video_chunk = stream_info.get("input_settings", {}).get("is_video_chunk", False)
159
- if is_video_chunk or stream_info.get("input_settings", {}).get("stream_type", "video_file") == "video_file":
160
- return "00:00:00"
161
- else:
162
- if self._tracking_start_time is None:
163
- stream_time_str = stream_info.get("stream_time", "")
164
- if stream_time_str:
165
- try:
166
- timestamp_str = stream_time_str.replace(" UTC", "")
167
- dt = datetime.strptime(timestamp_str, "%Y-%m-%d-%H:%M:%S.%f")
168
- self._tracking_start_time = dt.replace(tzinfo=timezone.utc).timestamp()
169
- except:
170
- self._tracking_start_time = time.time()
171
- else:
172
- self._tracking_start_time = time.time()
173
- dt = datetime.fromtimestamp(self._tracking_start_time, tz=timezone.utc)
174
- dt = dt.replace(minute=0, second=0, microsecond=0)
175
- return dt.strftime('%Y:%m:%d %H:%M:%S')
176
50
 
51
+ class BananaMonitoringUseCase(BaseProcessor):
52
+ CATEGORY_DISPLAY = {
53
+ "freshripe": "Fresh Ripe",
54
+ "freshunripe": "Fresh Unripe",
55
+ "overripe": "Overripe",
56
+ "ripe": "Ripe",
57
+ "rotten": "Rotten",
58
+ "unripe": "Unripe"
59
+ }
60
+
177
61
  def __init__(self):
178
62
  super().__init__("fruit_monitoring")
179
63
  self.category = "agriculture"
180
- self.fruit_categories = ['freshripe', 'freshunripe', 'overripe', 'ripe', 'rotten', 'unripe']
64
+ self.CASE_TYPE: Optional[str] = 'banana_defect_detection'
65
+ self.CASE_VERSION: Optional[str] = '1.0'
66
+ self.target_categories = ['freshripe', 'freshunripe', 'overripe', 'ripe', 'rotten', 'unripe']
181
67
  self.smoothing_tracker = None
182
68
  self.tracker = None
183
69
  self._total_frame_counter = 0
@@ -187,8 +73,11 @@ class BananaMonitoringUseCase(BaseProcessor):
187
73
  self._canonical_tracks: Dict[Any, Dict[str, Any]] = {}
188
74
  self._track_merge_iou_threshold: float = 0.05
189
75
  self._track_merge_time_window: float = 7.0
76
+ self._ascending_alert_list: List[int] = []
77
+ self.current_incident_end_timestamp: str = "N/A"
190
78
 
191
- def process(self, data: Any, config: ConfigProtocol, context: Optional[ProcessingContext] = None, stream_info: Optional[Dict[str, Any]] = None) -> ProcessingResult:
79
+ def process(self, data: Any, config: ConfigProtocol, context: Optional[ProcessingContext] = None,
80
+ stream_info: Optional[Dict[str, Any]] = None) -> ProcessingResult:
192
81
  start_time = time.time()
193
82
  if not isinstance(config, BananaMonitoringConfig):
194
83
  return self.create_error_result("Invalid config type", usecase=self.name, category=self.category, context=context)
@@ -204,15 +93,15 @@ class BananaMonitoringUseCase(BaseProcessor):
204
93
  self.logger.debug(f"Applied confidence filtering with threshold {config.confidence_threshold}")
205
94
  else:
206
95
  processed_data = data
207
- self.logger.debug("Did not apply confidence filtering with threshold since nothing was provided")
96
+ self.logger.debug("No confidence filtering applied")
208
97
 
209
98
  if config.index_to_category:
210
99
  processed_data = apply_category_mapping(processed_data, config.index_to_category)
211
100
  self.logger.debug("Applied category mapping")
212
101
 
213
102
  if config.target_fruit_categories:
214
- processed_data = [d for d in processed_data if d.get('category') in self.fruit_categories]
215
- self.logger.debug("Applied fruit category filtering")
103
+ processed_data = [d for d in processed_data if d.get('category') in self.target_categories]
104
+ self.logger.debug("Applied category filtering")
216
105
 
217
106
  if config.enable_smoothing:
218
107
  if self.smoothing_tracker is None:
@@ -225,8 +114,7 @@ class BananaMonitoringUseCase(BaseProcessor):
225
114
  enable_smoothing=True
226
115
  )
227
116
  self.smoothing_tracker = BBoxSmoothingTracker(smoothing_config)
228
- smoothed_fruits = bbox_smoothing(processed_data, self.smoothing_tracker.config, self.smoothing_tracker)
229
- processed_data = smoothed_fruits
117
+ processed_data = bbox_smoothing(processed_data, self.smoothing_tracker.config, self.smoothing_tracker)
230
118
 
231
119
  try:
232
120
  from ..advanced_tracker import AdvancedTracker
@@ -234,13 +122,12 @@ class BananaMonitoringUseCase(BaseProcessor):
234
122
  if self.tracker is None:
235
123
  tracker_config = TrackerConfig()
236
124
  self.tracker = AdvancedTracker(tracker_config)
237
- self.logger.info("Initialized AdvancedTracker for Fruit Monitoring and tracking")
125
+ self.logger.info("Initialized AdvancedTracker for Banana Monitoring")
238
126
  processed_data = self.tracker.update(processed_data)
239
127
  except Exception as e:
240
128
  self.logger.warning(f"AdvancedTracker failed: {e}")
241
129
 
242
- processed_data = self._deduplicate_fruits(processed_data, iou_thresh=0.95)
243
- self._update_fruit_tracking_state(processed_data)
130
+ self._update_tracking_state(processed_data)
244
131
  self._total_frame_counter += 1
245
132
 
246
133
  frame_number = None
@@ -253,212 +140,372 @@ class BananaMonitoringUseCase(BaseProcessor):
253
140
 
254
141
  general_counting_summary = calculate_counting_summary(data)
255
142
  counting_summary = self._count_categories(processed_data, config)
256
- total_fruit_counts = self.get_total_fruit_counts()
257
- counting_summary['total_fruit_counts'] = total_fruit_counts
258
- insights = self._generate_insights(counting_summary, config)
259
- alerts = self._check_alerts(counting_summary, config)
143
+ total_counts = self.get_total_counts()
144
+ counting_summary['total_counts'] = total_counts
145
+ alerts = self._check_alerts(counting_summary, frame_number, config)
260
146
  predictions = self._extract_predictions(processed_data)
261
- summary = self._generate_summary(counting_summary, alerts)
262
147
 
263
- events_list = self._generate_events(counting_summary, alerts, config, frame_number, stream_info)
264
- tracking_stats_list = self._generate_tracking_stats(counting_summary, insights, summary, config, frame_number, stream_info)
148
+ incidents_list = self._generate_incidents(counting_summary, alerts, config, frame_number, stream_info)
149
+ tracking_stats_list = self._generate_tracking_stats(counting_summary, alerts, config, frame_number, stream_info)
150
+ business_analytics_list = self._generate_business_analytics(counting_summary, alerts, config, stream_info, is_empty=True)
151
+ summary_list = self._generate_summary(counting_summary, incidents_list, tracking_stats_list, business_analytics_list, alerts)
265
152
 
266
- events = events_list[0] if events_list else {}
153
+ incidents = incidents_list[0] if incidents_list else {}
267
154
  tracking_stats = tracking_stats_list[0] if tracking_stats_list else {}
155
+ business_analytics = business_analytics_list[0] if business_analytics_list else {}
156
+ summary = summary_list[0] if summary_list else {}
157
+ agg_summary = {str(frame_number): {
158
+ "incidents": incidents,
159
+ "tracking_stats": tracking_stats,
160
+ "business_analytics": business_analytics,
161
+ "alerts": alerts,
162
+ "human_text": summary}
163
+ }
268
164
 
269
165
  context.mark_completed()
270
166
  result = self.create_result(
271
- data={
272
- "counting_summary": counting_summary,
273
- "general_counting_summary": general_counting_summary,
274
- "alerts": alerts,
275
- "total_fruits": counting_summary.get("total_count", 0),
276
- "events": events,
277
- "tracking_stats": tracking_stats,
278
- },
167
+ data={"agg_summary": agg_summary},
279
168
  usecase=self.name,
280
169
  category=self.category,
281
170
  context=context
282
171
  )
283
- result.summary = summary
284
- result.insights = insights
285
- result.predictions = predictions
286
172
  return result
287
173
 
288
- def reset_tracker(self) -> None:
289
- if self.tracker is not None:
290
- self.tracker.reset()
291
- self.logger.info("AdvancedTracker reset for new tracking session")
174
+ def _check_alerts(self, summary: dict, frame_number: Any, config: BananaMonitoringConfig) -> List[Dict]:
175
+ def get_trend(data, lookback=900, threshold=0.6):
176
+ window = data[-lookback:] if len(data) >= lookback else data
177
+ if len(window) < 2:
178
+ return True
179
+ increasing = 0
180
+ total = 0
181
+ for i in range(1, len(window)):
182
+ if window[i] >= window[i - 1]:
183
+ increasing += 1
184
+ total += 1
185
+ ratio = increasing / total
186
+ return ratio >= threshold
292
187
 
293
- def reset_fruit_tracking(self) -> None:
294
- self._fruit_total_track_ids = {cat: set() for cat in self.fruit_categories}
295
- self._total_frame_counter = 0
296
- self._global_frame_offset = 0
297
- self._tracking_start_time = None
298
- self._track_aliases.clear()
299
- self._canonical_tracks.clear()
300
- self.logger.info("Fruit Monitoring tracking state reset")
188
+ frame_key = str(frame_number) if frame_number is not None else "current_frame"
189
+ alerts = []
190
+ total_detections = summary.get("total_count", 0)
191
+ total_counts_dict = summary.get("total_counts", {})
192
+ per_category_count = summary.get("per_category_count", {})
301
193
 
302
- def reset_all_tracking(self) -> None:
303
- self.reset_tracker()
304
- self.reset_fruit_tracking()
305
- self.logger.info("All Fruits tracking state reset")
194
+ if not config.alert_config:
195
+ return alerts
306
196
 
307
- def _generate_events(self, counting_summary: Dict, alerts: List, config: BananaMonitoringConfig, frame_number: Optional[int] = None, stream_info: Optional[Dict[str, Any]] = None) -> List[Dict]:
308
- frame_key = str(frame_number) if frame_number is not None else "current_frame"
309
- events = [{frame_key: []}]
310
- frame_events = events[0][frame_key]
311
- total_fruits = counting_summary.get("total_count", 0)
197
+ total = summary.get("total_count", 0)
198
+ if hasattr(config.alert_config, 'count_thresholds') and config.alert_config.count_thresholds:
199
+ for category, threshold in config.alert_config.count_thresholds.items():
200
+ if category == "all" and total > threshold:
201
+ alerts.append({
202
+ "alert_type": getattr(config.alert_config, 'alert_type', ['Default']),
203
+ "alert_id": f"alert_{category}_{frame_key}",
204
+ "incident_category": self.CASE_TYPE,
205
+ "threshold_level": threshold,
206
+ "ascending": get_trend(self._ascending_alert_list, lookback=900, threshold=0.8),
207
+ "settings": {t: v for t, v in zip(getattr(config.alert_config, 'alert_type', ['Default']),
208
+ getattr(config.alert_config, 'alert_value', ['JSON']))}
209
+ })
210
+ elif category in per_category_count:
211
+ count = per_category_count[category]
212
+ if count > threshold:
213
+ alerts.append({
214
+ "alert_type": getattr(config.alert_config, 'alert_type', ['Default']),
215
+ "alert_id": f"alert_{category}_{frame_key}",
216
+ "incident_category": self.CASE_TYPE,
217
+ "threshold_level": threshold,
218
+ "ascending": get_trend(self._ascending_alert_list, lookback=900, threshold=0.8),
219
+ "settings": {t: v for t, v in zip(getattr(config.alert_config, 'alert_type', ['Default']),
220
+ getattr(config.alert_config, 'alert_value', ['JSON']))}
221
+ })
222
+ return alerts
223
+
224
+ def _generate_incidents(self, counting_summary: Dict, alerts: List, config: BananaMonitoringConfig,
225
+ frame_number: Optional[int] = None, stream_info: Optional[Dict[str, Any]] = None) -> List[Dict]:
226
+ incidents = []
227
+ total_detections = counting_summary.get("total_count", 0)
228
+ current_timestamp = self._get_current_timestamp_str(stream_info)
229
+ camera_info = self.get_camera_info_from_stream(stream_info)
230
+
231
+ self._ascending_alert_list = self._ascending_alert_list[-900:] if len(self._ascending_alert_list) > 900 else self._ascending_alert_list
312
232
 
313
- if total_fruits > 0:
314
- level = "info"
233
+ if total_detections > 0:
234
+ level = "low"
315
235
  intensity = 5.0
236
+ start_timestamp = self._get_start_timestamp_str(stream_info)
237
+ if start_timestamp and self.current_incident_end_timestamp == 'N/A':
238
+ self.current_incident_end_timestamp = 'Incident still active'
239
+ elif start_timestamp and self.current_incident_end_timestamp == 'Incident still active':
240
+ if len(self._ascending_alert_list) >= 15 and sum(self._ascending_alert_list[-15:]) / 15 < 1.5:
241
+ self.current_incident_end_timestamp = current_timestamp
242
+ elif self.current_incident_end_timestamp != 'Incident still active' and self.current_incident_end_timestamp != 'N/A':
243
+ self.current_incident_end_timestamp = 'N/A'
244
+
316
245
  if config.alert_config and config.alert_config.count_thresholds:
317
246
  threshold = config.alert_config.count_thresholds.get("all", 15)
318
- intensity = min(10.0, (total_fruits / threshold) * 10)
319
- if intensity >= 7:
247
+ intensity = min(10.0, (total_detections / threshold) * 10)
248
+ if intensity >= 9:
320
249
  level = "critical"
250
+ self._ascending_alert_list.append(3)
251
+ elif intensity >= 7:
252
+ level = "significant"
253
+ self._ascending_alert_list.append(2)
321
254
  elif intensity >= 5:
322
- level = "warning"
255
+ level = "medium"
256
+ self._ascending_alert_list.append(1)
323
257
  else:
324
- level = "info"
258
+ level = "low"
259
+ self._ascending_alert_list.append(0)
325
260
  else:
326
- if total_fruits > 25:
261
+ if total_detections > 30:
327
262
  level = "critical"
263
+ intensity = 10.0
264
+ self._ascending_alert_list.append(3)
265
+ elif total_detections > 25:
266
+ level = "significant"
328
267
  intensity = 9.0
329
- elif total_fruits > 15:
330
- level = "warning"
268
+ self._ascending_alert_list.append(2)
269
+ elif total_detections > 15:
270
+ level = "medium"
331
271
  intensity = 7.0
272
+ self._ascending_alert_list.append(1)
332
273
  else:
333
- level = "info"
334
- intensity = min(10.0, total_fruits / 3.0)
274
+ level = "low"
275
+ intensity = min(10.0, total_detections / 3.0)
276
+ self._ascending_alert_list.append(0)
335
277
 
336
- human_text_lines = ["EVENTS DETECTED:"]
337
- human_text_lines.append(f" - {total_fruits} Fruit(s) detected [INFO]")
278
+ human_text_lines = [f"INCIDENTS DETECTED @ {current_timestamp}:"]
279
+ human_text_lines.append(f"\tSeverity Level: {(self.CASE_TYPE, level)}")
338
280
  human_text = "\n".join(human_text_lines)
339
281
 
340
- event = {
341
- "type": "fruit_monitoring",
342
- "stream_time": datetime.now(timezone.utc).strftime("%Y-%m-%d-%H:%M:%S UTC"),
343
- "level": level,
344
- "intensity": round(intensity, 1),
345
- "config": {
346
- "min_value": 0,
347
- "max_value": 10,
348
- "level_settings": {"info": 2, "warning": 5, "critical": 7}
349
- },
350
- "application_name": "Fruit Monitoring System",
351
- "application_version": "1.2",
352
- "location_info": None,
353
- "human_text": human_text
354
- }
355
- frame_events.append(event)
356
-
357
- for alert in alerts:
358
- total_fruits = counting_summary.get("total_count", 0)
359
- intensity_message = "ALERT: Low fruit density in the scene"
360
- if config.alert_config and config.alert_config.count_thresholds:
361
- threshold = config.alert_config.count_thresholds.get("all", 15)
362
- percentage = (total_fruits / threshold) * 100 if threshold > 0 else 0
363
- if percentage < 20:
364
- intensity_message = "ALERT: Low fruit density in the scene"
365
- elif percentage <= 50:
366
- intensity_message = "ALERT: Moderate fruit density in the scene"
367
- elif percentage <= 70:
368
- intensity_message = "ALERT: High fruit density in the scene"
369
- else:
370
- intensity_message = "ALERT: Severe fruit density in the scene"
371
- else:
372
- if total_fruits > 15:
373
- intensity_message = "ALERT: High fruit density in the scene"
374
- elif total_fruits == 1:
375
- intensity_message = "ALERT: Low fruit density in the scene"
376
- else:
377
- intensity_message = "ALERT: Moderate fruit density in the scene"
378
-
379
- alert_event = {
380
- "type": alert.get("type", "density_alert"),
381
- "stream_time": datetime.now(timezone.utc).strftime("%Y-%m-%d-%H:%M:%S UTC"),
382
- "level": alert.get("severity", "warning"),
383
- "intensity": 8.0,
384
- "config": {
385
- "min_value": 0,
386
- "max_value": 10,
387
- "level_settings": {"info": 2, "warning": 5, "critical": 7}
388
- },
389
- "application_name": "Fruit Density Alert System",
390
- "application_version": "1.2",
391
- "location_info": alert.get("zone"),
392
- "human_text": f"{datetime.now(timezone.utc).strftime('%Y-%m-%d-%H:%M:%S UTC')} : {intensity_message}"
393
- }
394
- frame_events.append(alert_event)
395
-
396
- return events
397
-
398
- def _generate_tracking_stats(
399
- self,
400
- counting_summary: Dict,
401
- insights: List[str],
402
- summary: str,
403
- config: BananaMonitoringConfig,
404
- frame_number: Optional[int] = None,
405
- stream_info: Optional[Dict[str, Any]] = None
406
- ) -> List[Dict]:
407
- frame_key = str(frame_number) if frame_number is not None else "current_frame"
408
- tracking_stats = [{frame_key: []}]
409
- frame_tracking_stats = tracking_stats[0][frame_key]
410
-
411
- total_fruits = counting_summary.get("total_count", 0)
412
- total_fruit_counts = counting_summary.get("total_fruit_counts", {})
413
- cumulative_total = sum(total_fruit_counts.values()) if total_fruit_counts else 0
282
+ alert_settings = []
283
+ if config.alert_config and hasattr(config.alert_config, 'alert_type'):
284
+ alert_settings.append({
285
+ "alert_type": getattr(config.alert_config, 'alert_type', ['Default']),
286
+ "incident_category": self.CASE_TYPE,
287
+ "threshold_level": config.alert_config.count_thresholds if hasattr(config.alert_config, 'count_thresholds') else {},
288
+ "ascending": True,
289
+ "settings": {t: v for t, v in zip(getattr(config.alert_config, 'alert_type', ['Default']),
290
+ getattr(config.alert_config, 'alert_value', ['JSON']))}
291
+ })
292
+
293
+ event = self.create_incident(
294
+ incident_id=self.CASE_TYPE + '_' + str(frame_number),
295
+ incident_type=self.CASE_TYPE,
296
+ severity_level=level,
297
+ human_text=human_text,
298
+ camera_info=camera_info,
299
+ alerts=alerts,
300
+ alert_settings=alert_settings,
301
+ start_time=start_timestamp,
302
+ end_time=self.current_incident_end_timestamp,
303
+ level_settings={"low": 1, "medium": 3, "significant": 4, "critical": 7}
304
+ )
305
+ incidents.append(event)
306
+ else:
307
+ self._ascending_alert_list.append(0)
308
+ incidents.append({})
309
+ return incidents
310
+
311
+ def _generate_tracking_stats(self, counting_summary: Dict, alerts: List, config: BananaMonitoringConfig,
312
+ frame_number: Optional[int] = None, stream_info: Optional[Dict[str, Any]] = None) -> List[Dict]:
313
+ camera_info = self.get_camera_info_from_stream(stream_info)
314
+ tracking_stats = []
315
+ total_detections = counting_summary.get("total_count", 0)
316
+ total_counts_dict = counting_summary.get("total_counts", {})
414
317
  per_category_count = counting_summary.get("per_category_count", {})
415
-
416
- track_ids_info = self._get_track_ids_info(counting_summary.get("detections", []))
417
-
418
- current_timestamp = self._get_current_timestamp_str(stream_info)
419
- start_timestamp = self._get_start_timestamp_str(stream_info)
420
-
421
- human_text_lines = []
422
- human_text_lines.append(f"CURRENT FRAME @ {current_timestamp}:")
423
- if total_fruits > 0:
424
- category_counts = [f"{count} {cat}" for cat, count in per_category_count.items() if count > 0]
425
- if len(category_counts) == 1:
426
- fruits_text = category_counts[0] + " detected"
427
- elif len(category_counts) == 2:
428
- fruits_text = f"{category_counts[0]} and {category_counts[1]} detected"
318
+ current_timestamp = self._get_current_timestamp_str(stream_info, precision=False)
319
+ start_timestamp = self._get_start_timestamp_str(stream_info, precision=False)
320
+ high_precision_start_timestamp = self._get_current_timestamp_str(stream_info, precision=True)
321
+ high_precision_reset_timestamp = self._get_start_timestamp_str(stream_info, precision=True)
322
+
323
+ total_counts = [{"category": cat, "count": count} for cat, count in total_counts_dict.items() if count > 0]
324
+ current_counts = [{"category": cat, "count": count} for cat, count in per_category_count.items() if count > 0 or total_detections > 0]
325
+
326
+ detections = []
327
+ for detection in counting_summary.get("detections", []):
328
+ bbox = detection.get("bounding_box", {})
329
+ category = detection.get("category", "banana")
330
+ if detection.get("masks"):
331
+ segmentation = detection.get("masks", [])
332
+ detection_obj = self.create_detection_object(category, bbox, segmentation=segmentation)
333
+ elif detection.get("segmentation"):
334
+ segmentation = detection.get("segmentation")
335
+ detection_obj = self.create_detection_object(category, bbox, segmentation=segmentation)
336
+ elif detection.get("mask"):
337
+ segmentation = detection.get("mask")
338
+ detection_obj = self.create_detection_object(category, bbox, segmentation=segmentation)
429
339
  else:
430
- fruits_text = f"{', '.join(category_counts[:-1])}, and {category_counts[-1]} detected"
431
- human_text_lines.append(f"\t- {fruits_text}")
340
+ detection_obj = self.create_detection_object(category, bbox)
341
+ detections.append(detection_obj)
342
+
343
+ alert_settings = []
344
+ if config.alert_config and hasattr(config.alert_config, 'alert_type'):
345
+ alert_settings.append({
346
+ "alert_type": getattr(config.alert_config, 'alert_type', ['Default']),
347
+ "incident_category": self.CASE_TYPE,
348
+ "threshold_level": config.alert_config.count_thresholds if hasattr(config.alert_config, 'count_thresholds') else {},
349
+ "ascending": True,
350
+ "settings": {t: v for t, v in zip(getattr(config.alert_config, 'alert_type', ['Default']),
351
+ getattr(config.alert_config, 'alert_value', ['JSON']))}
352
+ })
353
+
354
+ human_text_lines = [f"Tracking Statistics:"]
355
+ human_text_lines.append(f"CURRENT FRAME @ {current_timestamp}")
356
+ for cat, count in per_category_count.items():
357
+ human_text_lines.append(f"\t{cat}: {count}")
358
+ human_text_lines.append(f"TOTAL SINCE {start_timestamp}")
359
+ for cat, count in total_counts_dict.items():
360
+ if count > 0:
361
+ human_text_lines.append(f"\t{cat}: {count}")
362
+ if alerts:
363
+ for alert in alerts:
364
+ human_text_lines.append(f"Alerts: {alert.get('settings', {})} sent @ {current_timestamp}")
432
365
  else:
433
- human_text_lines.append(f"\t- No fruits detected")
366
+ human_text_lines.append("Alerts: None")
367
+ human_text = "\n".join(human_text_lines)
434
368
 
435
- human_text_lines.append("")
436
- human_text_lines.append(f"TOTAL SINCE {start_timestamp}:")
437
- human_text_lines.append(f"\t- Total Fruits Detected: {cumulative_total}")
438
- if total_fruit_counts:
439
- for cat, count in total_fruit_counts.items():
440
- if count > 0:
441
- human_text_lines.append(f"\t- {cat}: {count}")
369
+ reset_settings = [{"interval_type": "daily", "reset_time": {"value": 9, "time_unit": "hour"}}]
370
+ tracking_stat = self.create_tracking_stats(
371
+ total_counts=total_counts,
372
+ current_counts=current_counts,
373
+ detections=detections,
374
+ human_text=human_text,
375
+ camera_info=camera_info,
376
+ alerts=alerts,
377
+ alert_settings=alert_settings,
378
+ reset_settings=reset_settings,
379
+ start_time=high_precision_start_timestamp,
380
+ reset_time=high_precision_reset_timestamp
381
+ )
382
+ tracking_stats.append(tracking_stat)
383
+ return tracking_stats
442
384
 
443
- human_text = "\n".join(human_text_lines)
385
+ def _generate_business_analytics(self, counting_summary: Dict, alerts: Any, config: BananaMonitoringConfig,
386
+ stream_info: Optional[Dict[str, Any]] = None, is_empty=False) -> List[Dict]:
387
+ if is_empty:
388
+ return []
389
+
390
+ def _generate_summary(self, summary: dict, incidents: List, tracking_stats: List, business_analytics: List, alerts: List) -> List[Dict]:
391
+ lines = {}
392
+ lines["Application Name"] = self.CASE_TYPE
393
+ lines["Application Version"] = self.CASE_VERSION
394
+ if len(incidents) > 0:
395
+ lines["Incidents"] = f"\n\t{incidents[0].get('human_text', 'No incidents detected')}\n"
396
+ if len(tracking_stats) > 0:
397
+ lines["Tracking Statistics"] = f"\t{tracking_stats[0].get('human_text', 'No tracking statistics detected')}\n"
398
+ if len(business_analytics) > 0:
399
+ lines["Business Analytics"] = f"\t{business_analytics[0].get('human_text', 'No business analytics detected')}\n"
400
+ if len(incidents) == 0 and len(tracking_stats) == 0 and len(business_analytics) == 0:
401
+ lines["Summary"] = "No Summary Data"
402
+ return [lines]
444
403
 
445
- tracking_stat = {
446
- "type": "fruit_tracking",
447
- "category": "fruit",
448
- "count": total_fruits,
449
- "insights": insights,
450
- "summary": summary,
451
- "timestamp": datetime.now(timezone.utc).strftime('%Y-%m-%d-%H:%M:%S UTC'),
452
- "human_text": human_text,
453
- "track_ids_info": track_ids_info,
454
- "global_frame_offset": getattr(self, '_global_frame_offset', 0),
455
- "local_frame_id": frame_key,
456
- "detections": counting_summary.get("detections", [])
457
-
404
+ def _get_track_ids_info(self, detections: list) -> Dict[str, Any]:
405
+ frame_track_ids = set()
406
+ for det in detections:
407
+ tid = det.get('track_id')
408
+ if tid is not None:
409
+ frame_track_ids.add(tid)
410
+ total_track_ids = set()
411
+ for s in getattr(self, '_per_category_total_track_ids', {}).values():
412
+ total_track_ids.update(s)
413
+ return {
414
+ "total_count": len(total_track_ids),
415
+ "current_frame_count": len(frame_track_ids),
416
+ "total_unique_track_ids": len(total_track_ids),
417
+ "current_frame_track_ids": list(frame_track_ids),
418
+ "last_update_time": time.time(),
419
+ "total_frames_processed": getattr(self, '_total_frame_counter', 0)
458
420
  }
459
421
 
460
- frame_tracking_stats.append(tracking_stat)
461
- return tracking_stats
422
+ def _update_tracking_state(self, detections: list):
423
+ if not hasattr(self, "_per_category_total_track_ids"):
424
+ self._per_category_total_track_ids = {cat: set() for cat in self.target_categories}
425
+ self._current_frame_track_ids = {cat: set() for cat in self.target_categories}
426
+
427
+ for det in detections:
428
+ cat = det.get("category")
429
+ raw_track_id = det.get("track_id")
430
+ if cat not in self.target_categories or raw_track_id is None:
431
+ continue
432
+ bbox = det.get("bounding_box", det.get("bbox"))
433
+ canonical_id = self._merge_or_register_track(raw_track_id, bbox)
434
+ det["track_id"] = canonical_id
435
+ self._per_category_total_track_ids.setdefault(cat, set()).add(canonical_id)
436
+ self._current_frame_track_ids[cat].add(canonical_id)
437
+
438
+ def get_total_counts(self):
439
+ return {cat: len(ids) for cat, ids in getattr(self, '_per_category_total_track_ids', {}).items()}
440
+
441
+ def _format_timestamp_for_stream(self, timestamp: float) -> str:
442
+ dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
443
+ return dt.strftime('%Y:%m:%d %H:%M:%S')
444
+
445
+ def _format_timestamp_for_video(self, timestamp: float) -> str:
446
+ hours = int(timestamp // 3600)
447
+ minutes = int((timestamp % 3600) // 60)
448
+ seconds = round(float(timestamp % 60), 2)
449
+ return f"{hours:02d}:{minutes:02d}:{seconds:.1f}"
450
+
451
+ def _get_current_timestamp_str(self, stream_info: Optional[Dict[str, Any]], precision=False, frame_id: Optional[str] = None) -> str:
452
+ if not stream_info:
453
+ return "00:00:00.00"
454
+ if precision:
455
+ if stream_info.get("input_settings", {}).get("start_frame", "na") != "na":
456
+ if frame_id:
457
+ start_time = int(frame_id) / stream_info.get("input_settings", {}).get("original_fps", 30)
458
+ else:
459
+ start_time = stream_info.get("input_settings", {}).get("start_frame", 30) / stream_info.get("input_settings", {}).get("original_fps", 30)
460
+ stream_time_str = self._format_timestamp_for_video(start_time)
461
+ return stream_time_str
462
+ else:
463
+ return datetime.now(timezone.utc).strftime("%Y-%m-%d-%H:%M:%S.%f UTC")
464
+ if stream_info.get("input_settings", {}).get("start_frame", "na") != "na":
465
+ if frame_id:
466
+ start_time = int(frame_id) / stream_info.get("input_settings", {}).get("original_fps", 30)
467
+ else:
468
+ start_time = stream_info.get("input_settings", {}).get("start_frame", 30) / stream_info.get("input_settings", {}).get("original_fps", 30)
469
+ stream_time_str = self._format_timestamp_for_video(start_time)
470
+ return stream_time_str
471
+ else:
472
+ stream_time_str = stream_info.get("input_settings", {}).get("stream_info", {}).get("stream_time", "")
473
+ if stream_time_str:
474
+ try:
475
+ timestamp_str = stream_time_str.replace(" UTC", "")
476
+ dt = datetime.strptime(timestamp_str, "%Y-%m-%d-%H:%M:%S.%f")
477
+ timestamp = dt.replace(tzinfo=timezone.utc).timestamp()
478
+ return self._format_timestamp_for_stream(timestamp)
479
+ except:
480
+ return self._format_timestamp_for_stream(time.time())
481
+ else:
482
+ return self._format_timestamp_for_stream(time.time())
483
+
484
+ def _get_start_timestamp_str(self, stream_info: Optional[Dict[str, Any]], precision=False) -> str:
485
+ if not stream_info:
486
+ return "00:00:00"
487
+ if precision:
488
+ if stream_info.get("input_settings", {}).get("start_frame", "na") != "na":
489
+ return "00:00:00"
490
+ else:
491
+ return datetime.now(timezone.utc).strftime("%Y-%m-%d-%H:%M:%S.%f UTC")
492
+ if stream_info.get("input_settings", {}).get("start_frame", "na") != "na":
493
+ return "00:00:00"
494
+ else:
495
+ if self._tracking_start_time is None:
496
+ stream_time_str = stream_info.get("input_settings", {}).get("stream_info", {}).get("stream_time", "")
497
+ if stream_time_str:
498
+ try:
499
+ timestamp_str = stream_time_str.replace(" UTC", "")
500
+ dt = datetime.strptime(timestamp_str, "%Y-%m-%d-%H:%M:%S.%f")
501
+ self._tracking_start_time = dt.replace(tzinfo=timezone.utc).timestamp()
502
+ except:
503
+ self._tracking_start_time = time.time()
504
+ else:
505
+ self._tracking_start_time = time.time()
506
+ dt = datetime.fromtimestamp(self._tracking_start_time, tz=timezone.utc)
507
+ dt = dt.replace(minute=0, second=0, microsecond=0)
508
+ return dt.strftime('%Y:%m:%d %H:%M:%S')
462
509
 
463
510
  def _count_categories(self, detections: list, config: BananaMonitoringConfig) -> dict:
464
511
  counts = {}
@@ -480,73 +527,6 @@ class BananaMonitoringUseCase(BaseProcessor):
480
527
  ]
481
528
  }
482
529
 
483
- CATEGORY_DISPLAY = {
484
- "Fresh Ripe": "freshripe",
485
- "Fresh Unripe": "freshunripe",
486
- "Overripe": "overripe",
487
- "Ripe": "ripe",
488
- "Rotten": "rotten",
489
- "Unripe": "unripe"
490
- }
491
-
492
- def _generate_insights(self, summary: dict, config: BananaMonitoringConfig) -> List[str]:
493
- insights = []
494
- per_cat = summary.get("per_category_count", {})
495
- total_fruits = summary.get("total_count", 0)
496
-
497
- if total_fruits == 0:
498
- insights.append("No fruits detected in the scene")
499
- return insights
500
- insights.append(f"EVENT: Detected {total_fruits} fruits in the scene")
501
- intensity_threshold = None
502
- if config.alert_config and config.alert_config.count_thresholds and "all" in config.alert_config.count_thresholds:
503
- intensity_threshold = config.alert_config.count_thresholds["all"]
504
-
505
- if intensity_threshold is not None:
506
- percentage = (total_fruits / intensity_threshold) * 100
507
- if percentage < 20:
508
- insights.append(f"INTENSITY: Low fruit density in the scene ({percentage:.1f}% of capacity)")
509
- elif percentage <= 50:
510
- insights.append(f"INTENSITY: Moderate fruit density in the scene ({percentage:.1f}% of capacity)")
511
- elif percentage <= 70:
512
- insights.append(f"INTENSITY: High fruit density in the scene ({percentage:.1f}% of capacity)")
513
- else:
514
- insights.append(f"INTENSITY: Severe fruit density in the scene ({percentage:.1f}% of capacity)")
515
-
516
- for cat, count in per_cat.items():
517
- display = self.CATEGORY_DISPLAY.get(cat, cat)
518
- insights.append(f"{display}: {count}")
519
- return insights
520
-
521
- def _check_alerts(self, summary: dict, config: BananaMonitoringConfig) -> List[Dict]:
522
- alerts = []
523
- if not config.alert_config:
524
- return alerts
525
- total = summary.get("total_count", 0)
526
- if config.alert_config.count_thresholds:
527
- for category, threshold in config.alert_config.count_thresholds.items():
528
- if category == "all" and total >= threshold:
529
- alerts.append({
530
- "type": "count_threshold",
531
- "severity": "warning",
532
- "message": f"Total fruit count ({total}) exceeds threshold ({threshold})",
533
- "category": category,
534
- "current_count": total,
535
- "threshold": threshold
536
- })
537
- elif category in summary.get("per_category_count", {}):
538
- count = summary.get("per_category_count", {})[category]
539
- if count >= threshold:
540
- alerts.append({
541
- "type": "count_threshold",
542
- "severity": "warning",
543
- "message": f"{category} count ({count}) exceeds threshold ({threshold})",
544
- "category": category,
545
- "current_count": count,
546
- "threshold": threshold
547
- })
548
- return alerts
549
-
550
530
  def _extract_predictions(self, detections: list) -> List[Dict[str, Any]]:
551
531
  return [
552
532
  {
@@ -557,25 +537,6 @@ class BananaMonitoringUseCase(BaseProcessor):
557
537
  for det in detections
558
538
  ]
559
539
 
560
- def _generate_summary(self, summary: dict, alerts: List) -> str:
561
- total = summary.get("total_count", 0)
562
- per_cat = summary.get("per_category_count", {})
563
- cumulative = summary.get("total_fruit_counts", {})
564
- cumulative_total = sum(cumulative.values()) if cumulative else 0
565
- lines = []
566
- if total > 0:
567
- lines.append(f"{total} Fruit(s) detected")
568
- if per_cat:
569
- lines.append("Fruits:")
570
- for cat, count in per_cat.items():
571
- lines.append(f"\t{cat}: {count}")
572
- else:
573
- lines.append("No fruit detected")
574
- lines.append(f"Total fruits detected: {cumulative_total}")
575
- if alerts:
576
- lines.append(f"{len(alerts)} alert(s)")
577
- return "\n".join(lines)
578
-
579
540
  def _compute_iou(self, box1: Any, box2: Any) -> float:
580
541
  def _bbox_to_list(bbox):
581
542
  if bbox is None:
@@ -597,20 +558,25 @@ class BananaMonitoringUseCase(BaseProcessor):
597
558
  return 0.0
598
559
  x1_min, y1_min, x1_max, y1_max = l1
599
560
  x2_min, y2_min, x2_max, y2_max = l2
561
+
600
562
  x1_min, x1_max = min(x1_min, x1_max), max(x1_min, x1_max)
601
563
  y1_min, y1_max = min(y1_min, y1_max), max(y1_min, y1_max)
602
564
  x2_min, x2_max = min(x2_min, x2_max), max(x2_min, x2_max)
603
565
  y2_min, y2_max = min(y2_min, y2_max), max(y2_min, y2_max)
566
+
604
567
  inter_x_min = max(x1_min, x2_min)
605
568
  inter_y_min = max(y1_min, y2_min)
606
569
  inter_x_max = min(x1_max, x2_max)
607
570
  inter_y_max = min(y1_max, y2_max)
571
+
608
572
  inter_w = max(0.0, inter_x_max - inter_x_min)
609
573
  inter_h = max(0.0, inter_y_max - inter_y_min)
610
574
  inter_area = inter_w * inter_h
575
+
611
576
  area1 = (x1_max - x1_min) * (y1_max - y1_min)
612
577
  area2 = (x2_max - x2_min) * (y2_max - y2_min)
613
578
  union_area = area1 + area2 - inter_area
579
+
614
580
  return (inter_area / union_area) if union_area > 0 else 0.0
615
581
 
616
582
  def _merge_or_register_track(self, raw_id: Any, bbox: Any) -> Any:
@@ -625,6 +591,7 @@ class BananaMonitoringUseCase(BaseProcessor):
625
591
  track_info["last_update"] = now
626
592
  track_info["raw_ids"].add(raw_id)
627
593
  return canonical_id
594
+
628
595
  for canonical_id, info in self._canonical_tracks.items():
629
596
  if now - info["last_update"] > self._track_merge_time_window:
630
597
  continue
@@ -635,6 +602,7 @@ class BananaMonitoringUseCase(BaseProcessor):
635
602
  info["last_update"] = now
636
603
  info["raw_ids"].add(raw_id)
637
604
  return canonical_id
605
+
638
606
  canonical_id = raw_id
639
607
  self._track_aliases[raw_id] = canonical_id
640
608
  self._canonical_tracks[canonical_id] = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrice
3
- Version: 1.0.99127
3
+ Version: 1.0.99128
4
4
  Summary: SDK for connecting to matrice.ai services
5
5
  Home-page: https://github.com/matrice-ai/python-sdk
6
6
  Author: Matrice.ai
@@ -127,7 +127,7 @@ matrice/deploy/utils/boundary_drawing_internal/boundary_drawing_internal.py,sha2
127
127
  matrice/deploy/utils/boundary_drawing_internal/boundary_drawing_tool.py,sha256=eY0VQGZ8BfTmR4_ThIAXaumBjh8_c7w69w-d3kta8p0,15421
128
128
  matrice/deploy/utils/boundary_drawing_internal/example_usage.py,sha256=cUBhxxsVdTQWIPvIOjCUGrhqon7ZBr5N6qNewjrTIuk,6434
129
129
  matrice/deploy/utils/post_processing/__init__.py,sha256=QaVlnp8KYxWlFabtX4dcZzGHcNL_Wf1E1usnd67sCKY,22862
130
- matrice/deploy/utils/post_processing/config.py,sha256=YxJZGOXV4qaRADVc--SnHI6UnRTAwFprcQXp9l49bTA,2789
130
+ matrice/deploy/utils/post_processing/config.py,sha256=zSBW2KE2iXyJhhkBXrIy-5eWg9ZqGl9uuWkIvcuovCM,2874
131
131
  matrice/deploy/utils/post_processing/processor.py,sha256=_tRjlZH-HEAwT0bHDCcXkbP6Hmx0m8DD1NMmYh4xMs0,30414
132
132
  matrice/deploy/utils/post_processing/advanced_tracker/__init__.py,sha256=tAPFzI_Yep5TLX60FDwKqBqppc-EbxSr0wNsQ9DGI1o,423
133
133
  matrice/deploy/utils/post_processing/advanced_tracker/base.py,sha256=VqWy4dd5th5LK-JfueTt2_GSEoOi5QQfQxjTNhmQoLc,3580
@@ -157,7 +157,7 @@ matrice/deploy/utils/post_processing/usecases/advanced_customer_service.py,sha25
157
157
  matrice/deploy/utils/post_processing/usecases/age_detection.py,sha256=cgOIx2zqPd_OWQMfTDV_4HSlvXlI8eKcZSEZ85SnZWA,35263
158
158
  matrice/deploy/utils/post_processing/usecases/anti_spoofing_detection.py,sha256=XdtDdXGzZMLQdWcoOoiE5t4LPYHhgOtJ7tZCNlq1A2E,31329
159
159
  matrice/deploy/utils/post_processing/usecases/assembly_line_detection.py,sha256=I_oeuec84KJnGMg_A8Wgs9U6h_IiopkDz9FbM1JG110,40410
160
- matrice/deploy/utils/post_processing/usecases/banana_defect_detection.py,sha256=OgkqYP8-LkCt3bg4x7V0VvlqvSE8I38SeKr_Wtb63B8,29532
160
+ matrice/deploy/utils/post_processing/usecases/banana_defect_detection.py,sha256=uKvB550Xve3iWfdWeOU3VhgORxD-M_UzsQDQKkYcFBc,30338
161
161
  matrice/deploy/utils/post_processing/usecases/basic_counting_tracking.py,sha256=-vr2z0J-qMh5wOoGubqeTWPttJ4NOYtGqKSV-_8PaKw,28311
162
162
  matrice/deploy/utils/post_processing/usecases/blood_cancer_detection_img.py,sha256=Ay_0OC9gM1dTQHWC5wL8XLZjl7ulHzd0zdhNzGH4JKU,42128
163
163
  matrice/deploy/utils/post_processing/usecases/car_damage_detection.py,sha256=WvD7M90DrJ9serjp4mOkZiCDwSXUyJNv6OX1AjlXs6s,34505
@@ -225,8 +225,8 @@ matrice/deployment/camera_manager.py,sha256=ReBZqm1CNXRImKcbcZ4uWAT3TUWkof1D28oB
225
225
  matrice/deployment/deployment.py,sha256=PLIUD-PxTaC2Zxb3Y12wUddsryV-OJetjCjLoSUh7S4,48103
226
226
  matrice/deployment/inference_pipeline.py,sha256=bXLgd29ViA7o0c7YWLFJl1otBUQfTPb61jS6VawQB0Y,37918
227
227
  matrice/deployment/streaming_gateway_manager.py,sha256=w5swGsuFVfZIdOm2ZuBHRHlRdYYJMLopLsf2gb91lQ8,20946
228
- matrice-1.0.99127.dist-info/licenses/LICENSE.txt,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
229
- matrice-1.0.99127.dist-info/METADATA,sha256=IPGAjHmiSN9pexaPdLutvX1BqVvNJ7DRA0FrJKfX_Ec,14624
230
- matrice-1.0.99127.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
231
- matrice-1.0.99127.dist-info/top_level.txt,sha256=P97js8ur6o5ClRqMH3Cjoab_NqbJ6sOQ3rJmVzKBvMc,8
232
- matrice-1.0.99127.dist-info/RECORD,,
228
+ matrice-1.0.99128.dist-info/licenses/LICENSE.txt,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
229
+ matrice-1.0.99128.dist-info/METADATA,sha256=gXAqcRNMLWJ3W-9UIIn1jK-Fmj9OydTdwUCGVbqxjAo,14624
230
+ matrice-1.0.99128.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
231
+ matrice-1.0.99128.dist-info/top_level.txt,sha256=P97js8ur6o5ClRqMH3Cjoab_NqbJ6sOQ3rJmVzKBvMc,8
232
+ matrice-1.0.99128.dist-info/RECORD,,