scorevision: push artifact
Browse files
miner.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
"""
|
| 2 |
-
Score Vision SN44 β Unified miner v3.
|
| 3 |
-
|
| 4 |
Pose model: YOLOv8n-pose FP16 640 for false-positive filtering + keypoint box refinement.
|
| 5 |
Vehicle weights loaded from secondary HF repo (meaculpitt/ScoreVision-Vehicle).
|
| 6 |
Person weights loaded from primary HF repo (template downloads automatically).
|
|
@@ -29,8 +29,6 @@ Pose model (pose_weights.onnx):
|
|
| 29 |
|
| 30 |
Vehicle + person models run on every image when hint='both'. All detections merged.
|
| 31 |
Vehicle eval uses cls_id 1-3. Person eval uses cls_id 0 only.
|
| 32 |
-
Petrol model runs only when challenge_type_id is unrecognized (not 2 or 12).
|
| 33 |
-
Petrol weights loaded from meaculpitt/ScoreVision-Petrol HF repo.
|
| 34 |
"""
|
| 35 |
|
| 36 |
import os
|
|
@@ -376,20 +374,6 @@ ENABLE_PARALLEL = True
|
|
| 376 |
# ββ Secondary HF repo for vehicle weights βββββββββββββββββββββββββββββββββββ
|
| 377 |
VEHICLE_HF_REPO = "meaculpitt/ScoreVision-Vehicle"
|
| 378 |
|
| 379 |
-
# ββ Petrol config βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 380 |
-
PETROL_HF_REPO = "meaculpitt/ScoreVision-Petrol"
|
| 381 |
-
PETROL_CONF = 0.25 # base conf for hose + pump
|
| 382 |
-
PETROL_CONF_HIGH = 0.35 # stricter conf for price_board + canopy (reduce tile FPs)
|
| 383 |
-
PETROL_NMS_IOU = 0.45
|
| 384 |
-
# SAHI tiling (640/40% = 6 tiles + full + flip = 8 passes, ~5.6s on chute GPU)
|
| 385 |
-
PETROL_TILE_SIZE = 640
|
| 386 |
-
PETROL_TILE_OVERLAP = 0.4
|
| 387 |
-
# Class IDs (petrol model output β independent of person/vehicle cls_ids
|
| 388 |
-
# because element_hint routing ensures only one pipeline runs per challenge)
|
| 389 |
-
PETROL_CLS_HOSE = 0
|
| 390 |
-
PETROL_CLS_PUMP = 1
|
| 391 |
-
PETROL_CLS_PRICEBOARD = 2
|
| 392 |
-
PETROL_CLS_CANOPY = 3
|
| 393 |
|
| 394 |
|
| 395 |
def _wbf_multi(boxes_list, scores_list, labels_list, iou_thr=0.55, skip_thr=0.0001):
|
|
@@ -678,43 +662,6 @@ class Miner:
|
|
| 678 |
self.plate_session = None
|
| 679 |
logger.info("[init] No plate model found, plate confirmation disabled")
|
| 680 |
|
| 681 |
-
# Petrol model β download from dedicated HF repo
|
| 682 |
-
try:
|
| 683 |
-
from huggingface_hub import snapshot_download as _sd
|
| 684 |
-
petrol_path = Path(_sd(PETROL_HF_REPO))
|
| 685 |
-
petrol_weights = str(petrol_path / "weights.onnx")
|
| 686 |
-
logger.info(f"[init] Petrol weights from {PETROL_HF_REPO}")
|
| 687 |
-
except Exception as e:
|
| 688 |
-
logger.warning(f"[init] Petrol secondary repo failed ({e}), trying primary repo")
|
| 689 |
-
petrol_weights = str(path_hf_repo / "weights.onnx")
|
| 690 |
-
if not Path(petrol_weights).exists():
|
| 691 |
-
petrol_weights = None
|
| 692 |
-
logger.warning("[init] No petrol weights found β petrol inference disabled")
|
| 693 |
-
|
| 694 |
-
if petrol_weights and Path(petrol_weights).exists():
|
| 695 |
-
self.petrol_session = ort.InferenceSession(
|
| 696 |
-
petrol_weights,
|
| 697 |
-
providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
|
| 698 |
-
)
|
| 699 |
-
self.petrol_input_name = self.petrol_session.get_inputs()[0].name
|
| 700 |
-
petrol_shape = self.petrol_session.get_inputs()[0].shape
|
| 701 |
-
self.petrol_h = int(petrol_shape[2])
|
| 702 |
-
self.petrol_w = int(petrol_shape[3])
|
| 703 |
-
# Detect output format
|
| 704 |
-
petrol_out_shape = self.petrol_session.get_outputs()[0].shape
|
| 705 |
-
self._petrol_end2end = (
|
| 706 |
-
len(petrol_out_shape) == 3
|
| 707 |
-
and petrol_out_shape[2] == 6
|
| 708 |
-
and (petrol_out_shape[1] or 0) <= 1000
|
| 709 |
-
)
|
| 710 |
-
petrol_actual = self.petrol_session.get_providers()
|
| 711 |
-
logger.warning(f"[init] Petrol session ACTIVE providers: {petrol_actual}")
|
| 712 |
-
if "CUDAExecutionProvider" not in petrol_actual:
|
| 713 |
-
logger.error("[init] β PETROL IS ON CPU β CUDA EP NOT ACTIVE")
|
| 714 |
-
logger.info(f"[init] Petrol model loaded: {self.petrol_h}x{self.petrol_w}, end2end={self._petrol_end2end}")
|
| 715 |
-
else:
|
| 716 |
-
self.petrol_session = None
|
| 717 |
-
self._petrol_end2end = False
|
| 718 |
|
| 719 |
# Pose cache β populated by _pose_filter_refine, read by vehicle parts
|
| 720 |
self._cached_pose_data = None
|
|
@@ -2096,12 +2043,10 @@ class Miner:
|
|
| 2096 |
_CHALLENGE_TYPE_MAP = {2: 'person', 12: 'vehicle'}
|
| 2097 |
|
| 2098 |
def _detect_element_hint(self) -> str:
|
| 2099 |
-
"""Detect whether this request is for person
|
| 2100 |
|
| 2101 |
Reads challenge_type_id from the chute template predict() metadata
|
| 2102 |
-
via stack frame inspection. Returns 'person', 'vehicle',
|
| 2103 |
-
Any unrecognized challenge_type_id routes to petrol (the only other
|
| 2104 |
-
element on this chute).
|
| 2105 |
"""
|
| 2106 |
frame = None
|
| 2107 |
try:
|
|
@@ -2116,7 +2061,7 @@ class Miner:
|
|
| 2116 |
hint = self._CHALLENGE_TYPE_MAP.get(ct_id)
|
| 2117 |
if hint:
|
| 2118 |
return hint
|
| 2119 |
-
return '
|
| 2120 |
except Exception:
|
| 2121 |
pass
|
| 2122 |
finally:
|
|
@@ -2138,9 +2083,6 @@ class Miner:
|
|
| 2138 |
vehicle_boxes = self._infer_vehicle(image_bgr)
|
| 2139 |
return self._vehicle_parts_confirm(vehicle_boxes, [], image_bgr)
|
| 2140 |
|
| 2141 |
-
if element_hint == 'petrol' and self.petrol_session:
|
| 2142 |
-
return self._infer_petrol(image_bgr)
|
| 2143 |
-
|
| 2144 |
# Fallback: run both (original behavior)
|
| 2145 |
if ENABLE_PARALLEL:
|
| 2146 |
veh_future = self._executor.submit(self._infer_vehicle, image_bgr)
|
|
@@ -2157,197 +2099,6 @@ class Miner:
|
|
| 2157 |
|
| 2158 |
return vehicle_boxes + person_boxes
|
| 2159 |
|
| 2160 |
-
# ββ Petrol inference pipeline (SAHI tiling) ββββββββββββββββββββββββββββββ
|
| 2161 |
-
|
| 2162 |
-
def _petrol_letterbox(self, image_bgr):
|
| 2163 |
-
"""Letterbox resize to model input, return blob + undo params."""
|
| 2164 |
-
h, w = image_bgr.shape[:2]
|
| 2165 |
-
scale = min(self.petrol_h / h, self.petrol_w / w)
|
| 2166 |
-
nh, nw = int(h * scale), int(w * scale)
|
| 2167 |
-
resized = cv2.resize(image_bgr, (nw, nh))
|
| 2168 |
-
canvas = np.full((self.petrol_h, self.petrol_w, 3), 114, dtype=np.uint8)
|
| 2169 |
-
dy, dx = (self.petrol_h - nh) // 2, (self.petrol_w - nw) // 2
|
| 2170 |
-
canvas[dy:dy + nh, dx:dx + nw] = resized
|
| 2171 |
-
rgb = cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB)
|
| 2172 |
-
blob = rgb.astype(np.float32) / 255.0
|
| 2173 |
-
blob = np.transpose(blob, (2, 0, 1))[None, ...]
|
| 2174 |
-
return blob, scale, dx, dy
|
| 2175 |
-
|
| 2176 |
-
def _petrol_decode(self, out, orig_h, orig_w, scale, dx, dy):
|
| 2177 |
-
"""Decode end2end [1,N,6] or raw [1,C,N] output with letterbox undo."""
|
| 2178 |
-
pred = out[0]
|
| 2179 |
-
if pred.ndim != 2:
|
| 2180 |
-
return []
|
| 2181 |
-
# End-to-end: [N, 6] = x1,y1,x2,y2,conf,cls
|
| 2182 |
-
if pred.shape[1] == 6:
|
| 2183 |
-
results = []
|
| 2184 |
-
for i in range(pred.shape[0]):
|
| 2185 |
-
conf = float(pred[i, 4])
|
| 2186 |
-
if conf < PETROL_CONF:
|
| 2187 |
-
continue
|
| 2188 |
-
x1 = (float(pred[i, 0]) - dx) / scale
|
| 2189 |
-
y1 = (float(pred[i, 1]) - dy) / scale
|
| 2190 |
-
x2 = (float(pred[i, 2]) - dx) / scale
|
| 2191 |
-
y2 = (float(pred[i, 3]) - dy) / scale
|
| 2192 |
-
cls_id = int(pred[i, 5])
|
| 2193 |
-
x1, y1 = max(0.0, x1), max(0.0, y1)
|
| 2194 |
-
x2, y2 = min(float(orig_w), x2), min(float(orig_h), y2)
|
| 2195 |
-
if x2 > x1 + 2 and y2 > y1 + 2:
|
| 2196 |
-
results.append([x1, y1, x2, y2, conf, cls_id])
|
| 2197 |
-
return results
|
| 2198 |
-
# Raw: [C, N] β transpose to [N, C], decode cx,cy,w,h + class scores
|
| 2199 |
-
if pred.shape[0] < pred.shape[1]:
|
| 2200 |
-
pred = pred.T
|
| 2201 |
-
if pred.shape[1] < 5:
|
| 2202 |
-
return []
|
| 2203 |
-
boxes = pred[:, :4]
|
| 2204 |
-
cls_scores = pred[:, 4:]
|
| 2205 |
-
cls_ids = np.argmax(cls_scores, axis=1)
|
| 2206 |
-
confs = np.max(cls_scores, axis=1)
|
| 2207 |
-
keep = confs >= PETROL_CONF
|
| 2208 |
-
boxes, confs, cls_ids = boxes[keep], confs[keep], cls_ids[keep]
|
| 2209 |
-
results = []
|
| 2210 |
-
for i in range(boxes.shape[0]):
|
| 2211 |
-
cx, cy, bw, bh = boxes[i].tolist()
|
| 2212 |
-
x1 = ((cx - bw / 2.0) - dx) / scale
|
| 2213 |
-
y1 = ((cy - bh / 2.0) - dy) / scale
|
| 2214 |
-
x2 = ((cx + bw / 2.0) - dx) / scale
|
| 2215 |
-
y2 = ((cy + bh / 2.0) - dy) / scale
|
| 2216 |
-
x1, y1 = max(0.0, x1), max(0.0, y1)
|
| 2217 |
-
x2, y2 = min(float(orig_w), x2), min(float(orig_h), y2)
|
| 2218 |
-
if x2 > x1 + 2 and y2 > y1 + 2:
|
| 2219 |
-
results.append([x1, y1, x2, y2, float(confs[i]), int(cls_ids[i])])
|
| 2220 |
-
return results
|
| 2221 |
-
|
| 2222 |
-
@staticmethod
|
| 2223 |
-
def _petrol_nms(dets, iou_thresh=PETROL_NMS_IOU):
|
| 2224 |
-
"""Per-class NMS on list of [x1,y1,x2,y2,conf,cls_id]."""
|
| 2225 |
-
if not dets:
|
| 2226 |
-
return []
|
| 2227 |
-
by_cls = {}
|
| 2228 |
-
for d in dets:
|
| 2229 |
-
by_cls.setdefault(int(d[5]), []).append(d)
|
| 2230 |
-
result = []
|
| 2231 |
-
for cls_dets in by_cls.values():
|
| 2232 |
-
arr = np.array(cls_dets, dtype=np.float32)
|
| 2233 |
-
boxes, scores = arr[:, :4], arr[:, 4]
|
| 2234 |
-
order = scores.argsort()[::-1]
|
| 2235 |
-
kept = []
|
| 2236 |
-
while order.size > 0:
|
| 2237 |
-
i = order[0]
|
| 2238 |
-
kept.append(i)
|
| 2239 |
-
if order.size == 1:
|
| 2240 |
-
break
|
| 2241 |
-
xx1 = np.maximum(boxes[i, 0], boxes[order[1:], 0])
|
| 2242 |
-
yy1 = np.maximum(boxes[i, 1], boxes[order[1:], 1])
|
| 2243 |
-
xx2 = np.minimum(boxes[i, 2], boxes[order[1:], 2])
|
| 2244 |
-
yy2 = np.minimum(boxes[i, 3], boxes[order[1:], 3])
|
| 2245 |
-
inter = np.maximum(0, xx2 - xx1) * np.maximum(0, yy2 - yy1)
|
| 2246 |
-
area_i = (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1])
|
| 2247 |
-
area_r = (boxes[order[1:], 2] - boxes[order[1:], 0]) * (boxes[order[1:], 3] - boxes[order[1:], 1])
|
| 2248 |
-
iou = inter / np.maximum(area_i + area_r - inter, 1e-6)
|
| 2249 |
-
order = order[np.where(iou <= iou_thresh)[0] + 1]
|
| 2250 |
-
for k in kept:
|
| 2251 |
-
result.append(cls_dets[k])
|
| 2252 |
-
return result
|
| 2253 |
-
|
| 2254 |
-
def _petrol_run_on_crop(self, crop_bgr):
|
| 2255 |
-
"""Run petrol model on a single crop, return dets in crop coords."""
|
| 2256 |
-
ch, cw = crop_bgr.shape[:2]
|
| 2257 |
-
blob, scale, dx, dy = self._petrol_letterbox(crop_bgr)
|
| 2258 |
-
out = self.petrol_session.run(None, {self.petrol_input_name: blob})[0]
|
| 2259 |
-
return self._petrol_decode(out, ch, cw, scale, dx, dy)
|
| 2260 |
-
|
| 2261 |
-
@staticmethod
|
| 2262 |
-
def _petrol_tiles(img_h, img_w, tile_size, overlap):
|
| 2263 |
-
"""Generate tile coordinates (y1, x1, y2, x2)."""
|
| 2264 |
-
stride = max(1, int(tile_size * (1.0 - overlap)))
|
| 2265 |
-
tiles = []
|
| 2266 |
-
y = 0
|
| 2267 |
-
while True:
|
| 2268 |
-
x = 0
|
| 2269 |
-
while True:
|
| 2270 |
-
y1, x1 = y, x
|
| 2271 |
-
y2 = min(y + tile_size, img_h)
|
| 2272 |
-
x2 = min(x + tile_size, img_w)
|
| 2273 |
-
# Snap small trailing tiles to image edge
|
| 2274 |
-
if y2 - y1 < tile_size * 0.4:
|
| 2275 |
-
y1 = max(0, img_h - tile_size)
|
| 2276 |
-
y2 = img_h
|
| 2277 |
-
if x2 - x1 < tile_size * 0.4:
|
| 2278 |
-
x1 = max(0, img_w - tile_size)
|
| 2279 |
-
x2 = img_w
|
| 2280 |
-
tiles.append((y1, x1, y2, x2))
|
| 2281 |
-
x += stride
|
| 2282 |
-
if x >= img_w:
|
| 2283 |
-
break
|
| 2284 |
-
y += stride
|
| 2285 |
-
if y >= img_h:
|
| 2286 |
-
break
|
| 2287 |
-
# Deduplicate
|
| 2288 |
-
seen = set()
|
| 2289 |
-
unique = []
|
| 2290 |
-
for t in tiles:
|
| 2291 |
-
if t not in seen:
|
| 2292 |
-
seen.add(t)
|
| 2293 |
-
unique.append(t)
|
| 2294 |
-
return unique
|
| 2295 |
-
|
| 2296 |
-
def _infer_petrol(self, image_bgr: ndarray) -> list[BoundingBox]:
|
| 2297 |
-
"""Petrol inference with SAHI tiling + flip TTA, merged via NMS."""
|
| 2298 |
-
if not hasattr(self, '_petrol_providers_logged'):
|
| 2299 |
-
provs = self.petrol_session.get_providers()
|
| 2300 |
-
logger.warning(f"[petrol] First inference β active providers: {provs}")
|
| 2301 |
-
self._petrol_providers_logged = True
|
| 2302 |
-
|
| 2303 |
-
oh, ow = image_bgr.shape[:2]
|
| 2304 |
-
all_dets = []
|
| 2305 |
-
|
| 2306 |
-
# 1. Full-image pass (letterbox)
|
| 2307 |
-
all_dets.extend(self._petrol_run_on_crop(image_bgr))
|
| 2308 |
-
|
| 2309 |
-
# 2. Full-image flip TTA
|
| 2310 |
-
flipped = cv2.flip(image_bgr, 1)
|
| 2311 |
-
flip_dets = self._petrol_run_on_crop(flipped)
|
| 2312 |
-
for d in flip_dets:
|
| 2313 |
-
d[0], d[2] = ow - d[2], ow - d[0] # mirror x coords
|
| 2314 |
-
all_dets.extend(flip_dets)
|
| 2315 |
-
|
| 2316 |
-
# 3. SAHI tile passes
|
| 2317 |
-
tiles = self._petrol_tiles(oh, ow, PETROL_TILE_SIZE, PETROL_TILE_OVERLAP)
|
| 2318 |
-
for ty1, tx1, ty2, tx2 in tiles:
|
| 2319 |
-
crop = image_bgr[ty1:ty2, tx1:tx2]
|
| 2320 |
-
tile_dets = self._petrol_run_on_crop(crop)
|
| 2321 |
-
for d in tile_dets:
|
| 2322 |
-
d[0] += tx1; d[1] += ty1 # offset to full-image coords
|
| 2323 |
-
d[2] += tx1; d[3] += ty1
|
| 2324 |
-
all_dets.extend(tile_dets)
|
| 2325 |
-
|
| 2326 |
-
# 4. Per-class NMS merge
|
| 2327 |
-
merged = self._petrol_nms(all_dets)
|
| 2328 |
-
|
| 2329 |
-
# 5. Apply per-class confidence thresholds
|
| 2330 |
-
# Hose + pump: PETROL_CONF (0.25) β already filtered in decode
|
| 2331 |
-
# Price board + canopy: PETROL_CONF_HIGH (0.35) β stricter to reduce tile FPs
|
| 2332 |
-
filtered = []
|
| 2333 |
-
for d in merged:
|
| 2334 |
-
cls_id = int(d[5])
|
| 2335 |
-
if cls_id in (PETROL_CLS_PRICEBOARD, PETROL_CLS_CANOPY):
|
| 2336 |
-
if d[4] < PETROL_CONF_HIGH:
|
| 2337 |
-
continue
|
| 2338 |
-
filtered.append(d)
|
| 2339 |
-
|
| 2340 |
-
out_boxes = []
|
| 2341 |
-
for x1, y1, x2, y2, conf, cls_id in filtered:
|
| 2342 |
-
out_boxes.append(BoundingBox(
|
| 2343 |
-
x1=max(0, min(ow, math.floor(x1))),
|
| 2344 |
-
y1=max(0, min(oh, math.floor(y1))),
|
| 2345 |
-
x2=max(0, min(ow, math.ceil(x2))),
|
| 2346 |
-
y2=max(0, min(oh, math.ceil(y2))),
|
| 2347 |
-
cls_id=int(cls_id),
|
| 2348 |
-
conf=max(0.0, min(1.0, conf)),
|
| 2349 |
-
))
|
| 2350 |
-
return out_boxes
|
| 2351 |
|
| 2352 |
# -- Replay buffer -------------------------------------------------------
|
| 2353 |
REPLAY_DIR = Path("/home/miner/replay_buffer")
|
|
|
|
| 1 |
"""
|
| 2 |
+
Score Vision SN44 β Unified miner v3.28 (2026-04-08). R9c vehicle FP16 (mAP50=0.929). Person: TTA consensus.
|
| 3 |
+
Dual-model: vehicle (YOLO11m INT8 1280) + person (YOLO12s FP16 960 TRT).
|
| 4 |
Pose model: YOLOv8n-pose FP16 640 for false-positive filtering + keypoint box refinement.
|
| 5 |
Vehicle weights loaded from secondary HF repo (meaculpitt/ScoreVision-Vehicle).
|
| 6 |
Person weights loaded from primary HF repo (template downloads automatically).
|
|
|
|
| 29 |
|
| 30 |
Vehicle + person models run on every image when hint='both'. All detections merged.
|
| 31 |
Vehicle eval uses cls_id 1-3. Person eval uses cls_id 0 only.
|
|
|
|
|
|
|
| 32 |
"""
|
| 33 |
|
| 34 |
import os
|
|
|
|
| 374 |
# ββ Secondary HF repo for vehicle weights βββββββββββββββββββββββββββββββββββ
|
| 375 |
VEHICLE_HF_REPO = "meaculpitt/ScoreVision-Vehicle"
|
| 376 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
|
| 378 |
|
| 379 |
def _wbf_multi(boxes_list, scores_list, labels_list, iou_thr=0.55, skip_thr=0.0001):
|
|
|
|
| 662 |
self.plate_session = None
|
| 663 |
logger.info("[init] No plate model found, plate confirmation disabled")
|
| 664 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
|
| 666 |
# Pose cache β populated by _pose_filter_refine, read by vehicle parts
|
| 667 |
self._cached_pose_data = None
|
|
|
|
| 2043 |
_CHALLENGE_TYPE_MAP = {2: 'person', 12: 'vehicle'}
|
| 2044 |
|
| 2045 |
def _detect_element_hint(self) -> str:
|
| 2046 |
+
"""Detect whether this request is for person or vehicle.
|
| 2047 |
|
| 2048 |
Reads challenge_type_id from the chute template predict() metadata
|
| 2049 |
+
via stack frame inspection. Returns 'person', 'vehicle', or 'both'.
|
|
|
|
|
|
|
| 2050 |
"""
|
| 2051 |
frame = None
|
| 2052 |
try:
|
|
|
|
| 2061 |
hint = self._CHALLENGE_TYPE_MAP.get(ct_id)
|
| 2062 |
if hint:
|
| 2063 |
return hint
|
| 2064 |
+
return 'both'
|
| 2065 |
except Exception:
|
| 2066 |
pass
|
| 2067 |
finally:
|
|
|
|
| 2083 |
vehicle_boxes = self._infer_vehicle(image_bgr)
|
| 2084 |
return self._vehicle_parts_confirm(vehicle_boxes, [], image_bgr)
|
| 2085 |
|
|
|
|
|
|
|
|
|
|
| 2086 |
# Fallback: run both (original behavior)
|
| 2087 |
if ENABLE_PARALLEL:
|
| 2088 |
veh_future = self._executor.submit(self._infer_vehicle, image_bgr)
|
|
|
|
| 2099 |
|
| 2100 |
return vehicle_boxes + person_boxes
|
| 2101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2102 |
|
| 2103 |
# -- Replay buffer -------------------------------------------------------
|
| 2104 |
REPLAY_DIR = Path("/home/miner/replay_buffer")
|