scorevision: push artifact
Browse files
miner.py
CHANGED
|
@@ -98,7 +98,7 @@ class Miner:
|
|
| 98 |
|
| 99 |
# Tuning matched to alfred's deployed model — bias toward precision to dodge
|
| 100 |
# the false_positive pillar penalty (validator weights FP heavily on this element).
|
| 101 |
-
self.conf_thres = 0.
|
| 102 |
self.iou_thres = 0.4
|
| 103 |
self.cross_iou_thresh = 0.7
|
| 104 |
self.max_det = 100
|
|
@@ -337,9 +337,14 @@ class Miner:
|
|
| 337 |
ratio: float,
|
| 338 |
pad: tuple[float, float],
|
| 339 |
orig_size: tuple[int, int],
|
|
|
|
|
|
|
| 340 |
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
| 341 |
"""Decode end2end NMS output and return (boxes, scores, cls_ids)
|
| 342 |
-
in original image coordinates, after conf-threshold + remap + letterbox-reverse + sanity.
|
|
|
|
|
|
|
|
|
|
| 343 |
if preds.ndim == 3 and preds.shape[0] == 1:
|
| 344 |
preds = preds[0]
|
| 345 |
if preds.ndim != 2 or preds.shape[1] < 6:
|
|
@@ -349,14 +354,15 @@ class Miner:
|
|
| 349 |
scores = preds[:, 4].astype(np.float32)
|
| 350 |
cls_ids = preds[:, 5].astype(np.int32)
|
| 351 |
|
| 352 |
-
valid = (cls_ids >= 0) & (cls_ids < len(self.cls_remap))
|
| 353 |
boxes, scores, cls_ids = boxes[valid], scores[valid], cls_ids[valid]
|
| 354 |
cls_ids = self.cls_remap[cls_ids]
|
| 355 |
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
|
|
|
| 360 |
if len(boxes) == 0:
|
| 361 |
return (
|
| 362 |
np.empty((0, 4), dtype=np.float32),
|
|
@@ -381,11 +387,29 @@ class Miner:
|
|
| 381 |
out = self.session.run(self.output_names, {self.input_name: x})[0]
|
| 382 |
return self._decode_raw_dets(out, ratio, pad, orig_size)
|
| 383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
def _predict_single(self, image: np.ndarray) -> list[BoundingBox]:
|
| 385 |
-
boxes, scores, cls_ids = self.
|
| 386 |
-
if len(boxes)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
return []
|
| 388 |
-
|
|
|
|
| 389 |
|
| 390 |
def _predict_tta(self, image: np.ndarray) -> list[BoundingBox]:
|
| 391 |
"""Hflip TTA: merge primary + flipped via per-class hard-NMS,
|
|
|
|
| 98 |
|
| 99 |
# Tuning matched to alfred's deployed model — bias toward precision to dodge
|
| 100 |
# the false_positive pillar penalty (validator weights FP heavily on this element).
|
| 101 |
+
self.conf_thres = 0.40
|
| 102 |
self.iou_thres = 0.4
|
| 103 |
self.cross_iou_thresh = 0.7
|
| 104 |
self.max_det = 100
|
|
|
|
| 337 |
ratio: float,
|
| 338 |
pad: tuple[float, float],
|
| 339 |
orig_size: tuple[int, int],
|
| 340 |
+
*,
|
| 341 |
+
apply_conf_thresh: bool = True,
|
| 342 |
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
| 343 |
"""Decode end2end NMS output and return (boxes, scores, cls_ids)
|
| 344 |
+
in original image coordinates, after conf-threshold + remap + letterbox-reverse + sanity.
|
| 345 |
+
|
| 346 |
+
When apply_conf_thresh=False, the conf-threshold filter is skipped (used for
|
| 347 |
+
the no-detection fallback path: take the single top-conf raw box)."""
|
| 348 |
if preds.ndim == 3 and preds.shape[0] == 1:
|
| 349 |
preds = preds[0]
|
| 350 |
if preds.ndim != 2 or preds.shape[1] < 6:
|
|
|
|
| 354 |
scores = preds[:, 4].astype(np.float32)
|
| 355 |
cls_ids = preds[:, 5].astype(np.int32)
|
| 356 |
|
| 357 |
+
valid = (cls_ids >= 0) & (cls_ids < len(self.cls_remap)) & (scores > 0)
|
| 358 |
boxes, scores, cls_ids = boxes[valid], scores[valid], cls_ids[valid]
|
| 359 |
cls_ids = self.cls_remap[cls_ids]
|
| 360 |
|
| 361 |
+
if apply_conf_thresh:
|
| 362 |
+
keep = scores >= self.conf_thres
|
| 363 |
+
boxes = boxes[keep]
|
| 364 |
+
scores = scores[keep]
|
| 365 |
+
cls_ids = cls_ids[keep]
|
| 366 |
if len(boxes) == 0:
|
| 367 |
return (
|
| 368 |
np.empty((0, 4), dtype=np.float32),
|
|
|
|
| 387 |
out = self.session.run(self.output_names, {self.input_name: x})[0]
|
| 388 |
return self._decode_raw_dets(out, ratio, pad, orig_size)
|
| 389 |
|
| 390 |
+
def _forward_with_fallback(
|
| 391 |
+
self, image: np.ndarray
|
| 392 |
+
) -> tuple[
|
| 393 |
+
tuple[np.ndarray, np.ndarray, np.ndarray],
|
| 394 |
+
tuple[np.ndarray, np.ndarray, np.ndarray],
|
| 395 |
+
]:
|
| 396 |
+
"""Run ONNX once, decode twice: (filtered @ conf_thres, all-survived sanity)."""
|
| 397 |
+
x, ratio, pad, orig_size = self._preprocess(image)
|
| 398 |
+
out = self.session.run(self.output_names, {self.input_name: x})[0]
|
| 399 |
+
primary = self._decode_raw_dets(out, ratio, pad, orig_size, apply_conf_thresh=True)
|
| 400 |
+
fallback = self._decode_raw_dets(out, ratio, pad, orig_size, apply_conf_thresh=False)
|
| 401 |
+
return primary, fallback
|
| 402 |
+
|
| 403 |
def _predict_single(self, image: np.ndarray) -> list[BoundingBox]:
|
| 404 |
+
(boxes, scores, cls_ids), (fb_b, fb_s, fb_c) = self._forward_with_fallback(image)
|
| 405 |
+
if len(boxes) > 0:
|
| 406 |
+
return self._build_results(boxes, scores, cls_ids)
|
| 407 |
+
# FALLBACK: nothing passed conf_thres — return single top-conf box
|
| 408 |
+
# (any class, any conf > 0) so the validator's mAP isn't a hard zero.
|
| 409 |
+
if len(fb_b) == 0:
|
| 410 |
return []
|
| 411 |
+
i = int(np.argmax(fb_s))
|
| 412 |
+
return self._build_results(fb_b[i:i + 1], fb_s[i:i + 1], fb_c[i:i + 1])
|
| 413 |
|
| 414 |
def _predict_tta(self, image: np.ndarray) -> list[BoundingBox]:
|
| 415 |
"""Hflip TTA: merge primary + flipped via per-class hard-NMS,
|