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.
- matrice_analytics/post_processing/advanced_tracker/matching.py +3 -3
- matrice_analytics/post_processing/advanced_tracker/strack.py +1 -1
- matrice_analytics/post_processing/face_reg/compare_similarity.py +5 -5
- matrice_analytics/post_processing/face_reg/embedding_manager.py +14 -7
- matrice_analytics/post_processing/face_reg/face_recognition.py +123 -34
- matrice_analytics/post_processing/face_reg/face_recognition_client.py +332 -82
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +29 -22
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
- matrice_analytics/post_processing/ocr/postprocessing.py +0 -1
- matrice_analytics/post_processing/post_processor.py +19 -5
- matrice_analytics/post_processing/usecases/color/clip.py +292 -132
- matrice_analytics/post_processing/usecases/color/color_mapper.py +2 -2
- matrice_analytics/post_processing/usecases/color_detection.py +429 -355
- matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +41 -386
- matrice_analytics/post_processing/usecases/flare_analysis.py +1 -56
- matrice_analytics/post_processing/usecases/license_plate_detection.py +476 -202
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +252 -11
- matrice_analytics/post_processing/usecases/people_counting.py +408 -1431
- matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +39 -10
- matrice_analytics/post_processing/utils/__init__.py +8 -8
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/METADATA +1 -1
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/RECORD +59 -24
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/WHEEL +0 -0
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/licenses/LICENSE.txt +0 -0
- {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
|
-
|
|
6
|
-
import
|
|
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
|
-
|
|
10
|
-
from
|
|
37
|
+
print("transformers imported successfully")
|
|
38
|
+
from importlib.resources import files as ir_files, as_file as ir_as_file
|
|
11
39
|
except:
|
|
12
|
-
|
|
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(
|
|
44
|
+
def load_model_from_checkpoint(checkpoint_url: str, providers: Optional[List] = None):
|
|
15
45
|
"""
|
|
16
|
-
Load
|
|
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: {
|
|
20
|
-
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
60
|
-
self.text_sess = load_model_from_checkpoint(self.text_url,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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("
|
|
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
|