meaculpitt commited on
Commit
f29cd5b
·
verified ·
1 Parent(s): 3743c5d

v3 weights (yolo11s, 204k merged plate dataset, val mAP50=0.944)

Browse files
Files changed (1) hide show
  1. miner.py +59 -29
miner.py CHANGED
@@ -155,18 +155,26 @@ class Miner:
155
  self._onnx_declared_h = _maybe_int(input_shape[2], None)
156
  self._onnx_declared_w = _maybe_int(input_shape[3], None)
157
 
158
- # Pre-NMS confidence threshold. The reference uses 0.25; we lower
159
- # slightly because validator plates are tiny but not as far as 0.15
160
- # which produces too many decayed-score ghost detections at 1408
161
- # input resolution (verified on starter assets: F1 dropped from
162
- # 0.625 to 0.462 at conf=0.15).
163
- self.conf_threshold = 0.25
 
164
  # Soft-NMS hyperparameters (Gaussian variant).
165
  self.soft_nms_sigma = 0.5
166
- # Final score floor after Soft-NMS decay. At higher input resolution
167
- # the model produces more medium-confidence detections that survive
168
- # decay; we keep this stricter so they don't pollute the output.
169
- self.score_threshold = 0.20
 
 
 
 
 
 
 
170
 
171
  # GPU warmup — force ORT / CUDA / cuDNN kernel compilation and pull
172
  # the 4090 out of low-power idle state so the first real validator
@@ -351,23 +359,12 @@ class Miner:
351
  dets.append((xa, ya, xb, yb, float(confs[i]), int(cls_ids[i])))
352
  return dets
353
 
354
- def _infer_single(self, image_bgr: ndarray) -> list[BoundingBox]:
355
- """Quad-4 (2x2 quadrant) SAHI inference.
356
-
357
- Splits the frame into four overlapping quadrants, each
358
- anisotropically resized to ``(input_h, input_w)`` for ~2x
359
- magnification in both axes. This recovers plates that TB-2
360
- (top/bottom only) missed — especially the 5-7 px plates in
361
- image 6 that need vertical AND horizontal magnification.
362
-
363
- Overlap is ~10% on each axis to avoid seam misses. All tile
364
- detections are merged via Soft-NMS.
365
-
366
- Measured on the 7 starter frames vs TB-2:
367
- mAP@50 0.406 -> 0.489
368
- recall 0.433 -> 0.500
369
- wall p95 55 ms -> 98 ms (budget 10 s)
370
- """
371
  orig_h, orig_w = image_bgr.shape[:2]
372
  OVERLAP_X = 70 # ~10% of 1408/2
373
  OVERLAP_Y = 38 # ~10% of 768/2
@@ -380,10 +377,43 @@ class Miner:
380
  (0, max(0, my - OVERLAP_Y), min(orig_w, mx + OVERLAP_X), orig_h), # BL
381
  (max(0, mx - OVERLAP_X), max(0, my - OVERLAP_Y), orig_w, orig_h), # BR
382
  ]
383
-
384
- all_dets = []
385
  for x0, y0, x1, y1 in tiles:
386
  all_dets.extend(self._infer_tile(image_bgr, x0, y0, x1, y1))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
  dets = self._soft_nms(all_dets)
389
 
 
155
  self._onnx_declared_h = _maybe_int(input_shape[2], None)
156
  self._onnx_declared_w = _maybe_int(input_shape[3], None)
157
 
158
+ # Pre-NMS confidence threshold. Top-of-leaderboard miners (Cargile,
159
+ # alfred8995) run 0.16-0.21 with a LOW post-NMS floor (0.01) and TTA.
160
+ # Bench on 12 archived validator tasks (12-task consensus set) at
161
+ # conf=0.16, score_threshold=0.01 with our v3 ONNX: HIGH recall
162
+ # 57% -> 66% (+3 plates) at the cost of +2 singletons. Net positive
163
+ # given 0.6 weight on map50 vs 0.4 on false_positive in composite.
164
+ self.conf_threshold = 0.16
165
  # Soft-NMS hyperparameters (Gaussian variant).
166
  self.soft_nms_sigma = 0.5
167
+ # Final score floor after Soft-NMS decay. Was 0.20 raised threshold
168
+ # killed decayed real plates (e.g. plate adjacent to a higher-conf
169
+ # detection gets decayed below 0.20 and dropped). Matches competitor
170
+ # 0.01 floor; Soft-NMS still prevents wild duplicates via decay.
171
+ self.score_threshold = 0.01
172
+
173
+ # Horizontal-flip TTA. Doubles inference cost (~101ms -> ~200ms at
174
+ # batch=1) but we have ~10s budget per-frame, massive headroom. Both
175
+ # top miners (Cargile, alfred8995) use TTA — the extra view helps
176
+ # catch plates the model is directionally biased against.
177
+ self.use_tta = True
178
 
179
  # GPU warmup — force ORT / CUDA / cuDNN kernel compilation and pull
180
  # the 4090 out of low-power idle state so the first real validator
 
359
  dets.append((xa, ya, xb, yb, float(confs[i]), int(cls_ids[i])))
360
  return dets
361
 
362
+ def _quad4_raw_dets(
363
+ self,
364
+ image_bgr: ndarray,
365
+ ) -> list[tuple[float, float, float, float, float, int]]:
366
+ """Run the quad-4 tile pipeline and return RAW (pre-Soft-NMS)
367
+ detections in original-image coordinates."""
 
 
 
 
 
 
 
 
 
 
 
368
  orig_h, orig_w = image_bgr.shape[:2]
369
  OVERLAP_X = 70 # ~10% of 1408/2
370
  OVERLAP_Y = 38 # ~10% of 768/2
 
377
  (0, max(0, my - OVERLAP_Y), min(orig_w, mx + OVERLAP_X), orig_h), # BL
378
  (max(0, mx - OVERLAP_X), max(0, my - OVERLAP_Y), orig_w, orig_h), # BR
379
  ]
380
+ all_dets: list[tuple[float, float, float, float, float, int]] = []
 
381
  for x0, y0, x1, y1 in tiles:
382
  all_dets.extend(self._infer_tile(image_bgr, x0, y0, x1, y1))
383
+ return all_dets
384
+
385
+ def _infer_single(self, image_bgr: ndarray) -> list[BoundingBox]:
386
+ """Quad-4 (2x2 quadrant) SAHI inference with optional horizontal-flip TTA.
387
+
388
+ Splits the frame into four overlapping quadrants, each
389
+ anisotropically resized to ``(input_h, input_w)`` for ~2x
390
+ magnification in both axes. Overlap is ~10% on each axis.
391
+ All tile detections are merged via Soft-NMS.
392
+
393
+ With ``self.use_tta=True``: additionally runs the same quad-4 pass
394
+ on a horizontally flipped copy and un-flips the x-coordinates back
395
+ into original space. Soft-NMS then merges across both views,
396
+ preferring the higher-confidence one for any paired detection.
397
+
398
+ Measured (quad-4 without TTA) on 7 starter frames vs TB-2:
399
+ mAP@50 0.406 -> 0.489
400
+ recall 0.433 -> 0.500
401
+ wall p95 55 ms -> 98 ms
402
+
403
+ TTA roughly doubles inference cost (budget: 10 s).
404
+ """
405
+ orig_h, orig_w = image_bgr.shape[:2]
406
+
407
+ all_dets = self._quad4_raw_dets(image_bgr)
408
+
409
+ if self.use_tta:
410
+ flipped = cv2.flip(image_bgr, 1) # horizontal flip (mirror)
411
+ flip_dets = self._quad4_raw_dets(flipped)
412
+ # Un-flip x-coordinates: x_orig = W - x_flipped
413
+ for x1f, y1, x2f, y2, conf, cls_id in flip_dets:
414
+ all_dets.append(
415
+ (orig_w - x2f, y1, orig_w - x1f, y2, conf, cls_id)
416
+ )
417
 
418
  dets = self._soft_nms(all_dets)
419