scorevision: push artifact
Browse files
miner.py
CHANGED
|
@@ -149,9 +149,18 @@ class Miner:
|
|
| 149 |
"""
|
| 150 |
|
| 151 |
def __init__(self, path_hf_repo: Path) -> None:
|
| 152 |
-
|
| 153 |
-
#
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
weights_name = os.environ.get("SN44_ONNX_WEIGHTS", _DEFAULT_WEIGHTS)
|
| 157 |
weights_path = path_hf_repo / weights_name
|
|
@@ -306,16 +315,25 @@ class Miner:
|
|
| 306 |
boxes_xywh = preds[:, :4].astype(np.float32)
|
| 307 |
class_scores = preds[:, 4:].astype(np.float32)
|
| 308 |
|
| 309 |
-
# For each detection
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
mask = vehicle_scores >= self.conf_thres
|
| 314 |
if not np.any(mask):
|
| 315 |
return []
|
| 316 |
|
| 317 |
boxes_xywh = boxes_xywh[mask]
|
| 318 |
scores = vehicle_scores[mask]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
boxes = _xywh_to_xyxy(boxes_xywh)
|
| 321 |
|
|
@@ -325,7 +343,9 @@ class Miner:
|
|
| 325 |
boxes /= ratio
|
| 326 |
boxes = _clip_boxes(boxes, orig_size)
|
| 327 |
|
| 328 |
-
boxes, scores = self.
|
|
|
|
|
|
|
| 329 |
if len(boxes) == 0:
|
| 330 |
return []
|
| 331 |
|
|
@@ -333,9 +353,10 @@ class Miner:
|
|
| 333 |
keep = keep[: self.max_det]
|
| 334 |
boxes = boxes[keep]
|
| 335 |
scores = scores[keep]
|
|
|
|
| 336 |
|
| 337 |
out: list[BoundingBox] = []
|
| 338 |
-
for box, conf in zip(boxes, scores):
|
| 339 |
if box[2] <= box[0] or box[3] <= box[1]:
|
| 340 |
continue
|
| 341 |
out.append(
|
|
@@ -344,12 +365,50 @@ class Miner:
|
|
| 344 |
y1=int(math.floor(box[1])),
|
| 345 |
x2=int(math.ceil(box[2])),
|
| 346 |
y2=int(math.ceil(box[3])),
|
| 347 |
-
cls_id=
|
| 348 |
conf=float(conf),
|
| 349 |
)
|
| 350 |
)
|
| 351 |
return out
|
| 352 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
@staticmethod
|
| 354 |
def _coco_classes() -> list[str]:
|
| 355 |
return [
|
|
|
|
| 149 |
"""
|
| 150 |
|
| 151 |
def __init__(self, path_hf_repo: Path) -> None:
|
| 152 |
+
# IMPORTANT: element `manak0/Detect-detect-vehicle` declares:
|
| 153 |
+
# objects = ["bus", "car", "truck", "motorcycle"] (in this order)
|
| 154 |
+
# So the validator maps cls_id 0->"bus", 1->"car", 2->"truck", 3->"motorcycle".
|
| 155 |
+
# We must emit cls_id matching this order.
|
| 156 |
+
self.class_names = ["bus", "car", "truck", "motorcycle"]
|
| 157 |
+
# COCO -> element cls_id:
|
| 158 |
+
# bus (5) -> 0
|
| 159 |
+
# car (2) -> 1
|
| 160 |
+
# truck (7) -> 2
|
| 161 |
+
# motorcycle (3) -> 3
|
| 162 |
+
self.coco_to_element = {5: 0, 2: 1, 7: 2, 3: 3}
|
| 163 |
+
self.vehicle_coco_ids = tuple(self.coco_to_element.keys())
|
| 164 |
|
| 165 |
weights_name = os.environ.get("SN44_ONNX_WEIGHTS", _DEFAULT_WEIGHTS)
|
| 166 |
weights_path = path_hf_repo / weights_name
|
|
|
|
| 315 |
boxes_xywh = preds[:, :4].astype(np.float32)
|
| 316 |
class_scores = preds[:, 4:].astype(np.float32)
|
| 317 |
|
| 318 |
+
# For each detection, determine which COCO vehicle class has the MAX score
|
| 319 |
+
# and retain that as the class id + score.
|
| 320 |
+
vehicle_coco_idx = np.array(self.vehicle_coco_ids, dtype=np.intp)
|
| 321 |
+
vehicle_class_scores = class_scores[:, vehicle_coco_idx] # (N, 4)
|
| 322 |
+
best_in_vehicle = vehicle_class_scores.argmax(axis=1) # (N,) index into vehicle_coco_idx
|
| 323 |
+
vehicle_scores = vehicle_class_scores.max(axis=1) # (N,)
|
| 324 |
+
best_coco_ids = vehicle_coco_idx[best_in_vehicle] # COCO ids per row
|
| 325 |
+
|
| 326 |
mask = vehicle_scores >= self.conf_thres
|
| 327 |
if not np.any(mask):
|
| 328 |
return []
|
| 329 |
|
| 330 |
boxes_xywh = boxes_xywh[mask]
|
| 331 |
scores = vehicle_scores[mask]
|
| 332 |
+
# Map COCO id -> element cls_id (for emission)
|
| 333 |
+
element_cls_ids = np.array(
|
| 334 |
+
[self.coco_to_element[int(c)] for c in best_coco_ids[mask]],
|
| 335 |
+
dtype=np.int32,
|
| 336 |
+
)
|
| 337 |
|
| 338 |
boxes = _xywh_to_xyxy(boxes_xywh)
|
| 339 |
|
|
|
|
| 343 |
boxes /= ratio
|
| 344 |
boxes = _clip_boxes(boxes, orig_size)
|
| 345 |
|
| 346 |
+
boxes, scores, element_cls_ids = self._filter_sane_with_cls(
|
| 347 |
+
boxes, scores, element_cls_ids, orig_size,
|
| 348 |
+
)
|
| 349 |
if len(boxes) == 0:
|
| 350 |
return []
|
| 351 |
|
|
|
|
| 353 |
keep = keep[: self.max_det]
|
| 354 |
boxes = boxes[keep]
|
| 355 |
scores = scores[keep]
|
| 356 |
+
element_cls_ids = element_cls_ids[keep]
|
| 357 |
|
| 358 |
out: list[BoundingBox] = []
|
| 359 |
+
for box, conf, cls_id in zip(boxes, scores, element_cls_ids):
|
| 360 |
if box[2] <= box[0] or box[3] <= box[1]:
|
| 361 |
continue
|
| 362 |
out.append(
|
|
|
|
| 365 |
y1=int(math.floor(box[1])),
|
| 366 |
x2=int(math.ceil(box[2])),
|
| 367 |
y2=int(math.ceil(box[3])),
|
| 368 |
+
cls_id=int(cls_id),
|
| 369 |
conf=float(conf),
|
| 370 |
)
|
| 371 |
)
|
| 372 |
return out
|
| 373 |
|
| 374 |
+
def _filter_sane_with_cls(
|
| 375 |
+
self,
|
| 376 |
+
boxes: np.ndarray,
|
| 377 |
+
scores: np.ndarray,
|
| 378 |
+
cls_ids: np.ndarray,
|
| 379 |
+
orig_size: tuple[int, int],
|
| 380 |
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
| 381 |
+
if len(boxes) == 0:
|
| 382 |
+
return boxes, scores, cls_ids
|
| 383 |
+
orig_w, orig_h = orig_size
|
| 384 |
+
image_area = float(orig_w * orig_h)
|
| 385 |
+
keep: list[int] = []
|
| 386 |
+
for i, box in enumerate(boxes):
|
| 387 |
+
x1, y1, x2, y2 = box.tolist()
|
| 388 |
+
bw = x2 - x1
|
| 389 |
+
bh = y2 - y1
|
| 390 |
+
if bw <= 0 or bh <= 0:
|
| 391 |
+
continue
|
| 392 |
+
if bw < self.min_w or bh < self.min_h:
|
| 393 |
+
continue
|
| 394 |
+
area = bw * bh
|
| 395 |
+
if area < self.min_box_area:
|
| 396 |
+
continue
|
| 397 |
+
if area > self.max_box_area_ratio * image_area:
|
| 398 |
+
continue
|
| 399 |
+
ar = max(bw / max(bh, 1e-6), bh / max(bw, 1e-6))
|
| 400 |
+
if ar > self.max_aspect_ratio:
|
| 401 |
+
continue
|
| 402 |
+
keep.append(i)
|
| 403 |
+
if not keep:
|
| 404 |
+
return (
|
| 405 |
+
np.empty((0, 4), dtype=np.float32),
|
| 406 |
+
np.empty((0,), dtype=np.float32),
|
| 407 |
+
np.empty((0,), dtype=np.int32),
|
| 408 |
+
)
|
| 409 |
+
idx = np.array(keep, dtype=np.intp)
|
| 410 |
+
return boxes[idx], scores[idx], cls_ids[idx]
|
| 411 |
+
|
| 412 |
@staticmethod
|
| 413 |
def _coco_classes() -> list[str]:
|
| 414 |
return [
|