matrice-analytics 0.1.2__py3-none-any.whl → 0.1.31__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.

Potentially problematic release.


This version of matrice-analytics might be problematic. Click here for more details.

Files changed (59) hide show
  1. matrice_analytics/post_processing/advanced_tracker/matching.py +3 -3
  2. matrice_analytics/post_processing/advanced_tracker/strack.py +1 -1
  3. matrice_analytics/post_processing/face_reg/compare_similarity.py +5 -5
  4. matrice_analytics/post_processing/face_reg/embedding_manager.py +14 -7
  5. matrice_analytics/post_processing/face_reg/face_recognition.py +123 -34
  6. matrice_analytics/post_processing/face_reg/face_recognition_client.py +332 -82
  7. matrice_analytics/post_processing/face_reg/people_activity_logging.py +29 -22
  8. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
  9. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
  10. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
  11. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
  12. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
  13. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
  14. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
  15. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
  16. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
  17. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
  18. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
  19. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
  20. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
  21. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
  22. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
  23. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
  24. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
  25. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
  26. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
  27. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
  28. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
  29. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
  30. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
  31. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
  32. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
  33. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
  34. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
  35. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
  36. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
  37. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
  38. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
  39. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
  40. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
  41. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
  42. matrice_analytics/post_processing/ocr/postprocessing.py +0 -1
  43. matrice_analytics/post_processing/post_processor.py +19 -5
  44. matrice_analytics/post_processing/usecases/color/clip.py +292 -132
  45. matrice_analytics/post_processing/usecases/color/color_mapper.py +2 -2
  46. matrice_analytics/post_processing/usecases/color_detection.py +429 -355
  47. matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +41 -386
  48. matrice_analytics/post_processing/usecases/flare_analysis.py +1 -56
  49. matrice_analytics/post_processing/usecases/license_plate_detection.py +476 -202
  50. matrice_analytics/post_processing/usecases/license_plate_monitoring.py +252 -11
  51. matrice_analytics/post_processing/usecases/people_counting.py +408 -1431
  52. matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
  53. matrice_analytics/post_processing/usecases/vehicle_monitoring.py +39 -10
  54. matrice_analytics/post_processing/utils/__init__.py +8 -8
  55. {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/METADATA +1 -1
  56. {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/RECORD +59 -24
  57. {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/WHEEL +0 -0
  58. {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/licenses/LICENSE.txt +0 -0
  59. {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/top_level.txt +0 -0
@@ -1,41 +1,100 @@
1
+ import requests
2
+ import sys
3
+ from pathlib import Path
4
+ import logging
5
+ import subprocess
6
+ import shutil
7
+ import os
8
+ log_file = open("pip_jetson_bti.log", "w")
9
+ cmd = ["pip", "install", "importlib-resources"]
10
+ subprocess.run(
11
+ cmd,
12
+ stdout=log_file,
13
+ stderr=subprocess.STDOUT,
14
+ preexec_fn=os.setpgrp
15
+ )
16
+ cmd = ["pip", "install", "httpx", "aiohttp", "filterpy"]
17
+ subprocess.run(
18
+ cmd,
19
+ stdout=log_file,
20
+ stderr=subprocess.STDOUT,
21
+ preexec_fn=os.setpgrp
22
+ )
23
+ log_file.close()
24
+
1
25
  import numpy as np
2
26
  from typing import List, Dict, Tuple, Optional
3
27
  from dataclasses import dataclass, field
4
28
  import cv2
5
- from scipy.special import softmax
6
- import requests
29
+ import io
30
+ import threading
31
+ import onnxruntime as ort
32
+ from PIL import Image
33
+
34
+
7
35
  try:
8
36
  from transformers import CLIPProcessor
9
- import onnxruntime as ort
10
- from PIL import Image
37
+ print("transformers imported successfully")
38
+ from importlib.resources import files as ir_files, as_file as ir_as_file
11
39
  except:
12
- print("Unable to import onnxruntime")
40
+ ir_files = None
41
+ ir_as_file = None
42
+ print("Unable to import transformers/irlib-resources @ clip.py")
13
43
 
14
- def load_model_from_checkpoint(checkpoint_path,local_path):
44
+ def load_model_from_checkpoint(checkpoint_url: str, providers: Optional[List] = None):
15
45
  """
16
- Load a model from checkpoint URL
46
+ Load an ONNX model from a URL directly into memory without writing locally.
47
+ Enforces the specified providers (e.g., CUDAExecutionProvider) for execution.
17
48
  """
18
49
  try:
19
- print(f"Loading model from checkpoint: {checkpoint_path}")
20
-
21
- # Check if checkpoint is a URL
22
- if checkpoint_path.startswith(('http://', 'https://')):
23
- # Download checkpoint from URL
24
- response = requests.get(checkpoint_path, timeout = (30,200))
25
- if response.status_code == 200:
26
- with open(local_path, 'wb') as f:
27
- f.write(response.content)
28
- checkpoint_path = local_path
29
- print(f"Downloaded checkpoint to {local_path}")
30
- else:
31
- print(f"Failed to download checkpoint from {checkpoint_path}")
32
- return None
33
-
34
- # Load the model from the checkpoint
35
- model = ort.InferenceSession(checkpoint_path, providers=["CUDAExecutionProvider","CPUExecutionProvider"])
36
- print(f"{local_path} Model loaded successfully from checkpoint")
50
+ print(f"Loading model from checkpoint: {checkpoint_url}")
51
+
52
+ # Download the checkpoint with streaming
53
+ response = requests.get(checkpoint_url, stream=True, timeout=(30, 200))
54
+ response.raise_for_status()
55
+
56
+ # Read the content into bytes
57
+ model_bytes = io.BytesIO()
58
+ for chunk in response.iter_content(chunk_size=8192):
59
+ if chunk:
60
+ model_bytes.write(chunk)
61
+ model_bytes.seek(0) # reset pointer to start
62
+
63
+ # Prepare session options for performance
64
+ try:
65
+ sess_options = ort.SessionOptions()
66
+ # Enable all graph optimizations
67
+ sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
68
+ # Conservative thread usage – GPU work dominates
69
+ sess_options.intra_op_num_threads = 1
70
+ sess_options.inter_op_num_threads = 1
71
+ except Exception:
72
+ sess_options = None
73
+
74
+ # Resolve providers
75
+ available = ort.get_available_providers()
76
+ print("Available providers:", available)
77
+ use_providers = ["CUDAExecutionProvider"] #providers or
78
+
79
+ # Validate providers and enforce CUDA when requested
80
+ if any(
81
+ (isinstance(p, tuple) and p[0] == "CUDAExecutionProvider") or p == "CUDAExecutionProvider"
82
+ for p in use_providers
83
+ ):
84
+ if "CUDAExecutionProvider" not in available:
85
+ raise RuntimeError("CUDAExecutionProvider not available in this environment")
86
+
87
+ # Load ONNX model from bytes with enforced providers
88
+ model = ort.InferenceSession(
89
+ model_bytes.read(),
90
+ sess_options=sess_options,
91
+ providers=use_providers,
92
+ )
93
+
94
+ print("Session providers:", model.get_providers())
95
+ print("Model loaded successfully from checkpoint (in-memory)")
37
96
  return model
38
-
97
+
39
98
  except Exception as e:
40
99
  print(f"Error loading model from checkpoint: {e}")
41
100
  return None
@@ -46,7 +105,7 @@ class ClipProcessor:
46
105
  def __init__(self,
47
106
  image_model_path: str = 'https://s3.us-west-2.amazonaws.com/testing.resources/datasets/clip_image.onnx',
48
107
  text_model_path: str = 'https://s3.us-west-2.amazonaws.com/testing.resources/datasets/clip_text.onnx',
49
- processor_dir: str = './clip_processor',
108
+ processor_dir: Optional[str] = None,
50
109
  providers: Optional[List[str]] = None):
51
110
 
52
111
  self.color_category: List[str] = ["black", "white", "yellow", "gray", "red", "blue", "light blue",
@@ -54,12 +113,57 @@ class ClipProcessor:
54
113
 
55
114
  self.image_url: str = image_model_path
56
115
  self.text_url: str = text_model_path
57
- self.processor_path: str = processor_dir
116
+ # Resolve processor_dir relative to this module, not CWD
117
+ self.processor_path: str = self._resolve_processor_dir(processor_dir)
118
+ print("PROCESSOR PATH->", self.processor_path)
119
+ cwd = os.getcwd()
120
+ print("Current working directory:", cwd)
121
+
122
+ log_file = open("pip_jetson_bti.log", "w")
123
+ cmd = ["pip", "install", "--force-reinstall", "huggingface_hub", "regex", "safetensors"]
124
+ subprocess.Popen(
125
+ cmd,
126
+ stdout=log_file,
127
+ stderr=subprocess.STDOUT,
128
+ preexec_fn=os.setpgrp
129
+ )
130
+
131
+ # Determine and enforce providers (prefer CUDA only)
132
+ try:
133
+ available = ort.get_available_providers()
134
+ except Exception:
135
+ print("You are seein this error because of ort :(")
136
+ available = []
137
+ print("True OG Available ONNX providers:", available, 'providers(if any):',providers)
138
+
139
+ if providers is None:
140
+ if "CUDAExecutionProvider" in available:
141
+ self.providers = ["CUDAExecutionProvider"]
142
+ else:
143
+ # Enforce GPU-only per requirement; raise if not available
144
+ print("CUDAExecutionProvider not available; ensure CUDA-enabled onnxruntime-gpu is installed and GPU is visible")
145
+ else:
146
+ self.providers = providers
147
+
148
+ # Thread-safety to serialize processing
149
+ self._lock = threading.Lock()
150
+ print("Curr Providersss: ",self.providers)
58
151
 
59
- self.image_sess = load_model_from_checkpoint(self.image_url,"clip_image.onnx")
60
- self.text_sess = load_model_from_checkpoint(self.text_url,"clip_text.onnx")
152
+ self.image_sess = load_model_from_checkpoint(self.image_url, providers=self.providers)
153
+ self.text_sess = load_model_from_checkpoint(self.text_url, providers=self.providers)
61
154
 
62
- self.processor = CLIPProcessor.from_pretrained(self.processor_path)
155
+
156
+ # Load CLIPProcessor tokenizer/config from local package data if available
157
+ self.processor = None
158
+ try:
159
+ if self.processor_path and os.path.isdir(self.processor_path):
160
+ self.processor = CLIPProcessor.from_pretrained(self.processor_path, local_files_only=True)
161
+ else:
162
+ # Fallback to hub
163
+ self.processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
164
+ except Exception as e:
165
+ print(f"Falling back to remote CLIPProcessor due to error loading local assets: {e}")
166
+ self.processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
63
167
 
64
168
  tok = self.processor.tokenizer(self.color_category, padding=True, return_tensors="np")
65
169
  ort_inputs_text = {
@@ -73,7 +177,8 @@ class ClipProcessor:
73
177
  self.pixel_template = sample["pixel_values"].astype(np.float32)
74
178
  self.min_box_size = 32
75
179
  self.max_batch = 32
76
- self.frame_skip = 2
180
+ # Classify every frame for stability unless changed by caller
181
+ self.frame_skip = 1
77
182
  self.batch_pixels = np.zeros((self.max_batch, *self.pixel_template.shape[1:]), dtype=np.float32)
78
183
 
79
184
  self.records: Dict[int, Dict[str, float]] = {}
@@ -81,107 +186,159 @@ class ClipProcessor:
81
186
  self.processed_frames = 0
82
187
 
83
188
 
189
+ def _resolve_processor_dir(self, processor_dir: Optional[str]) -> str:
190
+ """
191
+ Find the absolute path to the bundled 'clip_processor' assets directory in the
192
+ installed package, independent of current working directory.
193
+
194
+ Resolution order:
195
+ 1) Explicit processor_dir if provided.
196
+ 2) Directory next to this file: <module_dir>/clip_processor
197
+ 3) importlib.resources (Python 3.9+): matrice_analytics.post_processing.usecases.color/clip_processor
198
+ """
199
+ if processor_dir:
200
+ return os.path.abspath(processor_dir)
201
+
202
+ # 2) Try path next to this file
203
+ module_dir = Path(__file__).resolve().parent
204
+ candidate = module_dir / "clip_processor"
205
+ if candidate.is_dir():
206
+ return str(candidate)
207
+
208
+ # 3) Try importlib.resources if available
209
+ try:
210
+ if ir_files is not None:
211
+ pkg = "matrice_analytics.post_processing.usecases.color"
212
+ res = ir_files(pkg).joinpath("clip_processor")
213
+ try:
214
+ # If packaged in a zip, materialize to a temp path
215
+ with ir_as_file(res) as p:
216
+ if Path(p).is_dir():
217
+ return str(p)
218
+ except Exception:
219
+ # If already a concrete path
220
+ if res and str(res):
221
+ return str(res)
222
+ except Exception:
223
+ pass
224
+
225
+ # Fallback to CWD-relative (last resort)
226
+ return os.path.abspath("clip_processor")
227
+
84
228
  def process_color_in_frame(self, detections, input_bytes, zones: Optional[Dict[str, List[List[float]]]], stream_info):
85
- boxes = []
86
- tracked_ids: List[int] = []
87
- frame_number: Optional[int] = None
88
- # print(detections)
89
- self.frame_idx+=1
90
- nparr = np.frombuffer(input_bytes, np.uint8) # convert bytes to numpy array
91
- image = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # decode image
92
-
93
- # Step 2: Convert PIL → NumPy array
94
- frame = np.array(image)
95
- if stream_info:
96
- input_settings = stream_info.get("input_settings", {})
97
- start_frame = input_settings.get("start_frame")
98
- end_frame = input_settings.get("end_frame")
99
- if start_frame is not None and end_frame is not None and start_frame == end_frame:
100
- frame_number = start_frame
101
-
102
- for det in detections:
103
- bbox = det.get('bounding_box')
104
- tid = det.get('track_id')
105
- zones = zones if zones else {}
106
- for z_name, zone_polygon in zones.items():
107
- if self._is_in_zone(bbox, zone_polygon):
108
- w = bbox['xmax'] - bbox['xmin']
109
- h = bbox['ymax'] - bbox['ymin']
110
- if w >= self.min_box_size and h >= self.max_batch:
111
- boxes.append(bbox)
112
- tracked_ids.append(tid)
113
- # print(boxes)
114
- # print(tracked_ids)
115
- if not boxes:
116
- print(f"Frame {self.frame_idx}: No cars in zone")
117
- self.processed_frames += 1
118
- # print(f"Frame {frame_idx} processedms\n")
119
- return
120
-
121
- # print(boxes)
122
- # print(tracked_ids)
123
- crops_for_model = []
124
- map_trackidx_to_cropidx = []
125
- for i,(bbox, tid) in enumerate(zip(boxes, tracked_ids)):
126
- last_rec = self.records.get(tid)
127
- should_classify = False
128
- if last_rec is None:
129
- should_classify = True
130
- else:
131
- if (self.frame_idx - last_rec.get("last_classified_frame", -999)) >= self.frame_skip:
132
- should_classify = True
133
- if should_classify:
134
- x1, y1, x2, y2 = bbox['xmin'], bbox['ymin'], bbox['xmax'], bbox['ymax']
135
- # crop safely
136
- y1c, y2c = max(0, y1), min(frame.shape[0], y2)
137
- x1c, x2c = max(0, x1), min(frame.shape[1], x2)
138
- if y2c - y1c <= 0 or x2c - x1c <= 0:
229
+ # Serialize processing to avoid concurrent access and potential frame drops
230
+ with self._lock:
231
+ print("=== process_color_in_frame called ===")
232
+ print(f"Number of detections: {len(detections) if detections else 0}")
233
+ print(f"Input bytes length: {len(input_bytes) if input_bytes else 0}")
234
+
235
+ boxes = []
236
+ tracked_ids: List[int] = []
237
+ frame_number: Optional[int] = None
238
+ print(detections)
239
+ self.frame_idx += 1
240
+
241
+ if not detections:
242
+ print(f"Frame {self.frame_idx}: No detections provided")
243
+ self.processed_frames += 1
244
+ return {}
245
+
246
+ nparr = np.frombuffer(input_bytes, np.uint8) # convert bytes to numpy array
247
+ image = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # decode image
248
+
249
+ if image is None:
250
+ print(f"Frame {self.frame_idx}: Failed to decode image")
251
+ self.processed_frames += 1
252
+ return {}
253
+
254
+ # Step 2: Use decoded frame directly (BGR → RGB performed at crop time)
255
+ frame = image
256
+ if stream_info:
257
+ input_settings = stream_info.get("input_settings", {})
258
+ start_frame = input_settings.get("start_frame")
259
+ end_frame = input_settings.get("end_frame")
260
+ if start_frame is not None and end_frame is not None and start_frame == end_frame:
261
+ frame_number = start_frame
262
+
263
+ for det in detections:
264
+ bbox = det.get('bounding_box')
265
+ tid = det.get('track_id')
266
+ if not bbox or not tid:
139
267
  continue
140
- crop = cv2.cvtColor(frame[y1c:y2c, x1c:x2c], cv2.COLOR_BGR2RGB)
141
- pil_img = Image.fromarray(crop).resize((224, 224))
142
- map_trackidx_to_cropidx.append((tid, len(crops_for_model)))
143
- crops_for_model.append(pil_img)
144
- # print(crops_for_model)
145
- # print(map_trackidx_to_cropidx)
146
-
147
- if crops_for_model:
148
- record = {}
149
- img_embeds = self.run_image_onnx_on_crops(crops_for_model) # [N, D]
150
- # compute similarity with text_embeds (shape [num_labels, D])
151
- sims = img_embeds @ self.text_embeds.T # [N, num_labels]
152
- # convert to probs
153
- probs = np.exp(sims) / np.exp(sims).sum(axis=-1, keepdims=True) # softmax numerically simple
154
- # print(probs)
155
-
156
- # assign back to corresponding tracks
157
- for (tid, crop_idx) in map_trackidx_to_cropidx:
158
- prob = probs[crop_idx]
159
- # print(prob)
160
- best_idx = int(np.argmax(prob))
161
- best_label = self.color_category[best_idx]
162
- # print(best_label)
163
- best_score = float(prob[best_idx])
164
- # print(best_score)
165
-
166
- rec = self.records.get(tid)
167
- # if rec is None:
168
- record[tid] = {
169
- "frame": self.frame_idx,
170
- "color": best_label,
171
- "confidence": best_score,
172
- "track_id": tid,
173
- "last_classified_frame": self.frame_idx,
174
- }
175
- # else:
176
- # # update only if confidence improves
177
- # if best_score > rec["confidence"]:
178
- # rec["color"] = best_label
179
- # rec["confidence"] = best_score
180
- # rec["frame"] = self.frame_idx
181
- # rec["last_classified_frame"] = self.frame_idx
182
-
183
-
184
- return record
268
+ w = bbox['xmax'] - bbox['xmin']
269
+ h = bbox['ymax'] - bbox['ymin']
270
+ if w >= self.min_box_size and h >= self.min_box_size:
271
+ boxes.append(bbox)
272
+ tracked_ids.append(tid)
273
+
274
+ if not boxes:
275
+ print(f"Frame {self.frame_idx}: No cars in zone")
276
+ self.processed_frames += 1
277
+ return {}
278
+
279
+ # print(boxes)
280
+ # print(tracked_ids)
281
+ crops_for_model = []
282
+ map_trackidx_to_cropidx = []
283
+ for i, (bbox, tid) in enumerate(zip(boxes, tracked_ids)):
284
+ last_rec = self.records.get(tid)
285
+ should_classify = False
286
+ if last_rec is None:
287
+ should_classify = True
288
+ else:
289
+ if (self.frame_idx - last_rec.get("last_classified_frame", -999)) >= self.frame_skip:
290
+ should_classify = True
291
+ if should_classify:
292
+ x1, y1, x2, y2 = bbox['xmin'], bbox['ymin'], bbox['xmax'], bbox['ymax']
293
+ # crop safely - convert to integers
294
+ y1c, y2c = max(0, int(y1)), min(frame.shape[0], int(y2))
295
+ x1c, x2c = max(0, int(x1)), min(frame.shape[1], int(x2))
296
+ print(f"Cropping bbox: x1c={x1c}, y1c={y1c}, x2c={x2c}, y2c={y2c}, frame_shape={frame.shape}")
297
+ if y2c - y1c <= 0 or x2c - x1c <= 0:
298
+ print(f"Skipping invalid crop: dimensions {x2c-x1c}x{y2c-y1c}")
299
+ continue
300
+ crop = cv2.cvtColor(frame[y1c:y2c, x1c:x2c], cv2.COLOR_BGR2RGB)
301
+ map_trackidx_to_cropidx.append((tid, len(crops_for_model)))
302
+ # Pass raw numpy crop; resize handled in run_image_onnx_on_crops
303
+ crops_for_model.append(crop)
304
+ # print(f"Added crop for track_id {tid}")
305
+ # print(crops_for_model)
306
+
307
+ record = {} # Initialize record outside the if block
308
+ if crops_for_model:
309
+ img_embeds = self.run_image_onnx_on_crops(crops_for_model) # [N, D]
310
+ # compute similarity with text_embeds (shape [num_labels, D])
311
+ sims = img_embeds @ self.text_embeds.T # [N, num_labels]
312
+ # convert to probs
313
+ probs = np.exp(sims) / np.exp(sims).sum(axis=-1, keepdims=True) # softmax numerically simple
314
+ # print(probs)
315
+
316
+ # assign back to corresponding tracks
317
+ for (tid, crop_idx) in map_trackidx_to_cropidx:
318
+ prob = probs[crop_idx]
319
+ # print(prob)
320
+ best_idx = int(np.argmax(prob))
321
+ best_label = self.color_category[best_idx]
322
+ # print(best_label)
323
+ best_score = float(prob[best_idx])
324
+ # print(best_score)
325
+
326
+ rec = self.records.get(tid)
327
+ det_info = next((d for d in detections if d.get("track_id") == tid), {})
328
+ category_label = det_info.get("category", "unknown")
329
+ zone_name = det_info.get("zone_name", "Unknown_Zone")
330
+ record[tid] = {
331
+ "frame": self.frame_idx,
332
+ "color": best_label,
333
+ "confidence": best_score,
334
+ "track_id": tid,
335
+ "object_label": category_label,
336
+ "zone_name": zone_name,
337
+ "last_classified_frame": self.frame_idx,
338
+ }
339
+ print(record)
340
+
341
+ return record
185
342
 
186
343
 
187
344
  def run_image_onnx_on_crops(self, crops):
@@ -203,10 +360,13 @@ class ClipProcessor:
203
360
  print(f"Skipping crop {i}: resize failed ({e})")
204
361
 
205
362
  if not valid_crops:
206
- print("⚠️ No valid crops to process")
363
+ print("No valid crops to process")
207
364
  return np.zeros((0, self.text_embeds.shape[-1]), dtype=np.float32)
208
365
 
209
366
  # Convert all valid crops at once
367
+
368
+ #ToDO: Check if the processor and model.run is running on single thread and is uusing GPU. Latency should be <100ms.
369
+
210
370
  pixel_values = self.processor(images=valid_crops, return_tensors="np")["pixel_values"]
211
371
  n = pixel_values.shape[0]
212
372
  self.batch_pixels[:n] = pixel_values
@@ -9,7 +9,7 @@ from datetime import datetime, timedelta
9
9
  import logging
10
10
 
11
11
  # Import your existing color extraction functions
12
- from color_map_utils import extract_major_colors
12
+ #from color_map_utils import extract_major_colors
13
13
 
14
14
  # Configure logging
15
15
  logger = logging.getLogger(__name__)
@@ -163,7 +163,7 @@ class VideoColorClassifier:
163
163
  continue
164
164
 
165
165
  # Extract colors
166
- major_colors = extract_major_colors(cropped_obj, k=self.top_k_colors)
166
+ major_colors = [()] #extract_major_colors(cropped_obj, k=self.top_k_colors)
167
167
  main_color = major_colors[0][0] if major_colors else "unknown"
168
168
 
169
169
  # Create detailed result entry