coolroman commited on
Commit
4f4d648
·
verified ·
1 Parent(s): 9461fa4

scorevision: push artifact

Browse files
Files changed (1) hide show
  1. miner.py +157 -12
miner.py CHANGED
@@ -33,19 +33,41 @@ class Miner:
33
  class_names = ["balaclava", "hoodie", "glove", "bat", "spray paint", "graffiti"]
34
  input_size = 1280
35
  iou_thres = 0.30
36
- cross_iou_thresh = 0.70
37
  max_aspect_ratio = 10.0
38
  max_det = 150
39
- # tuned on consensus-GT, healthy distribution: [balaclava, hoodie, glove, bat, spray, graffiti]
40
  _conf_thres_array = np.array(
41
  [0.30, 0.70, 0.70, 0.50, 0.50, 0.40], dtype=np.float32
42
  )
43
- # per-class rescue gap: if a class has no box above threshold, admit top-1
44
- # if its score >= (threshold - bonus). Inspired by new-vmodel0's _bonus_array.
45
  _bonus_array = np.array(
46
  [0.20, 0.25, 0.20, 0.30, 0.20, 0.15], dtype=np.float32
47
  )
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  def __init__(self, path_hf_repo: Path) -> None:
50
  model_path = path_hf_repo / "weights.onnx"
51
  print("ORT version:", ort.__version__)
@@ -234,6 +256,127 @@ class Miner:
234
  boxes = self._clip(boxes, orig_size)
235
  return boxes, scores, cls_ids
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  def _predict_single(self, image):
238
  x, ratio, pad, orig_size = self._preprocess(image)
239
  out = self.session.run(self.output_names, {self.input_name: x})[0]
@@ -264,15 +407,17 @@ class Miner:
264
  if len(scores) > self.max_det:
265
  top = np.argsort(-scores)[:self.max_det]
266
  boxes, scores, cls_ids = boxes[top], scores[top], cls_ids[top]
267
- return [
268
- BoundingBox(
269
- x1=int(math.floor(b[0])), y1=int(math.floor(b[1])),
270
- x2=int(math.ceil(b[2])), y2=int(math.ceil(b[3])),
 
 
 
 
271
  cls_id=int(c), conf=float(s),
272
- )
273
- for b, s, c in zip(boxes, scores, cls_ids)
274
- if b[2] > b[0] and b[3] > b[1]
275
- ]
276
 
277
  def predict_batch(self, batch_images, offset, n_keypoints):
278
  results = []
 
33
  class_names = ["balaclava", "hoodie", "glove", "bat", "spray paint", "graffiti"]
34
  input_size = 1280
35
  iou_thres = 0.30
36
+ cross_iou_thresh = 0.80
37
  max_aspect_ratio = 10.0
38
  max_det = 150
 
39
  _conf_thres_array = np.array(
40
  [0.30, 0.70, 0.70, 0.50, 0.50, 0.40], dtype=np.float32
41
  )
 
 
42
  _bonus_array = np.array(
43
  [0.20, 0.25, 0.20, 0.30, 0.20, 0.15], dtype=np.float32
44
  )
45
 
46
+ _glove_cls_id = 2
47
+ _glove_spurious_max_conf = 0.04
48
+ _glove_spurious_aspect = 2.5
49
+ _glove_crowd_min_count = 5
50
+ _glove_crowd_max_conf_trigger = 0.6
51
+ _glove_crowd_keep_min_conf = 0.27
52
+ _glove_split_low_max = 0.26
53
+ _glove_split_low_min_count = 4
54
+ _glove_split_high_min_count = 2
55
+ _glove_pair_low_max = 0.1
56
+ _glove_pair_high_min = 0.4
57
+ _glove_pair_max_center_x_dist = 0.028
58
+ _glove_reject_r_min = 100
59
+ _glove_reject_r_max = 125
60
+ _glove_reject_g_min = 88
61
+ _glove_reject_g_max = 115
62
+ _glove_reject_b_min = 68
63
+ _glove_reject_b_max = 88
64
+ _glove_reject_min_pixel_frac = 0.04
65
+ _glove_reject_p90_percentile = 90.0
66
+
67
+ _spray_paint_cls_id = 4
68
+ _spray_orphan_max_conf = 0.3
69
+ _spray_glove_min_overlap = 0.3
70
+
71
  def __init__(self, path_hf_repo: Path) -> None:
72
  model_path = path_hf_repo / "weights.onnx"
73
  print("ORT version:", ort.__version__)
 
256
  boxes = self._clip(boxes, orig_size)
257
  return boxes, scores, cls_ids
258
 
259
+ @classmethod
260
+ def _is_spurious_glove(cls, cls_id, x1, y1, x2, y2, conf):
261
+ if cls_id != cls._glove_cls_id: return False
262
+ w, h = x2 - x1, y2 - y1
263
+ if w <= 0: return False
264
+ return h > cls._glove_spurious_aspect * w and conf < cls._glove_spurious_max_conf
265
+
266
+ @staticmethod
267
+ def _box_intersection_area(a, b):
268
+ ix1, iy1 = max(a.x1, b.x1), max(a.y1, b.y1)
269
+ ix2, iy2 = min(a.x2, b.x2), min(a.y2, b.y2)
270
+ return max(0.0, ix2 - ix1) * max(0.0, iy2 - iy1)
271
+
272
+ @classmethod
273
+ def _spray_overlaps_glove(cls, spray, glove):
274
+ inter = cls._box_intersection_area(spray, glove)
275
+ area = max(0.0, float(spray.x2 - spray.x1) * float(spray.y2 - spray.y1))
276
+ if area <= 0.0: return False
277
+ return inter / area >= cls._spray_glove_min_overlap
278
+
279
+ @classmethod
280
+ def _is_glove_rejection_color(cls, cls_id, image, x1, y1, x2, y2):
281
+ if cls_id != cls._glove_cls_id or image is None: return False
282
+ H, W = image.shape[:2]
283
+ xi1 = max(0, int(math.floor(x1)))
284
+ yi1 = max(0, int(math.floor(y1)))
285
+ xi2 = min(W, int(math.ceil(x2)))
286
+ yi2 = min(H, int(math.ceil(y2)))
287
+ if xi2 <= xi1 or yi2 <= yi1: return False
288
+ crop = image[yi1:yi2, xi1:xi2]
289
+ if crop.size == 0: return False
290
+ b = crop[:, :, 0].astype(np.float32)
291
+ g = crop[:, :, 1].astype(np.float32)
292
+ r = crop[:, :, 2].astype(np.float32)
293
+ in_range = ((r >= cls._glove_reject_r_min) & (r <= cls._glove_reject_r_max)
294
+ & (g >= cls._glove_reject_g_min) & (g <= cls._glove_reject_g_max)
295
+ & (b >= cls._glove_reject_b_min) & (b <= cls._glove_reject_b_max))
296
+ if float(in_range.mean()) >= cls._glove_reject_min_pixel_frac:
297
+ return True
298
+ p90_r = float(np.percentile(r, cls._glove_reject_p90_percentile))
299
+ p90_g = float(np.percentile(g, cls._glove_reject_p90_percentile))
300
+ p90_b = float(np.percentile(b, cls._glove_reject_p90_percentile))
301
+ return (cls._glove_reject_r_min <= p90_r <= cls._glove_reject_r_max
302
+ and cls._glove_reject_g_min <= p90_g <= cls._glove_reject_g_max
303
+ and cls._glove_reject_b_min <= p90_b <= cls._glove_reject_b_max)
304
+
305
+ @classmethod
306
+ def _filter_glove_by_color(cls, results, image):
307
+ if image is None: return results
308
+ return [b for b in results
309
+ if not cls._is_glove_rejection_color(b.cls_id, image, b.x1, b.y1, b.x2, b.y2)]
310
+
311
+ @staticmethod
312
+ def _glove_cx(b): return (b.x1 + b.x2) / 2.0
313
+
314
+ @classmethod
315
+ def _filter_paired_low_glove(cls, results, image_width):
316
+ if image_width <= 0: return results
317
+ gloves = [b for b in results if b.cls_id == cls._glove_cls_id]
318
+ if len(gloves) < 2: return results
319
+ remove = set()
320
+ for i, a in enumerate(gloves):
321
+ for b in gloves[i+1:]:
322
+ if a.conf < cls._glove_pair_low_max and b.conf > cls._glove_pair_high_min:
323
+ low, high = a, b
324
+ elif b.conf < cls._glove_pair_low_max and a.conf > cls._glove_pair_high_min:
325
+ low, high = b, a
326
+ else:
327
+ continue
328
+ cx_dist = abs(cls._glove_cx(low) - cls._glove_cx(high)) / float(image_width)
329
+ if cx_dist < cls._glove_pair_max_center_x_dist:
330
+ remove.add(id(low))
331
+ return [b for b in results if id(b) not in remove]
332
+
333
+ @classmethod
334
+ def _filter_weak_glove_with_strong_others(cls, results):
335
+ others = [b for b in results if b.cls_id != cls._glove_cls_id]
336
+ if not others: return results
337
+ if not all(b.conf > cls._glove_pair_high_min for b in others): return results
338
+ return [b for b in results
339
+ if b.cls_id != cls._glove_cls_id or b.conf >= cls._glove_pair_low_max]
340
+
341
+ @classmethod
342
+ def _filter_split_glove_confidence(cls, results):
343
+ gloves = [b for b in results if b.cls_id == cls._glove_cls_id]
344
+ low = [b for b in gloves if b.conf < cls._glove_split_low_max]
345
+ high = [b for b in gloves if b.conf > cls._glove_split_low_max]
346
+ if len(low) <= cls._glove_split_low_min_count: return results
347
+ if len(high) < cls._glove_split_high_min_count: return results
348
+ best_low = max(low, key=lambda b: b.conf)
349
+ others = [b for b in results if b.cls_id != cls._glove_cls_id]
350
+ return others + high + [best_low]
351
+
352
+ @classmethod
353
+ def _filter_crowded_gloves(cls, results):
354
+ gloves = [b for b in results if b.cls_id == cls._glove_cls_id]
355
+ if len(gloves) <= cls._glove_crowd_min_count: return results
356
+ if max(b.conf for b in gloves) <= cls._glove_crowd_max_conf_trigger: return results
357
+ return [b for b in results
358
+ if b.cls_id != cls._glove_cls_id or b.conf > cls._glove_crowd_keep_min_conf]
359
+
360
+ @classmethod
361
+ def _filter_orphan_low_conf_spray(cls, results):
362
+ gloves = [b for b in results if b.cls_id == cls._glove_cls_id]
363
+ kept = []
364
+ for box in results:
365
+ if box.cls_id != cls._spray_paint_cls_id or box.conf >= cls._spray_orphan_max_conf:
366
+ kept.append(box); continue
367
+ if any(cls._spray_overlaps_glove(box, g) for g in gloves):
368
+ kept.append(box)
369
+ return kept
370
+
371
+ @classmethod
372
+ def _apply_rules(cls, results, image_width, image=None):
373
+ results = cls._filter_glove_by_color(results, image)
374
+ results = cls._filter_paired_low_glove(results, image_width)
375
+ results = cls._filter_weak_glove_with_strong_others(results)
376
+ results = cls._filter_split_glove_confidence(results)
377
+ results = cls._filter_crowded_gloves(results)
378
+ return cls._filter_orphan_low_conf_spray(results)
379
+
380
  def _predict_single(self, image):
381
  x, ratio, pad, orig_size = self._preprocess(image)
382
  out = self.session.run(self.output_names, {self.input_name: x})[0]
 
407
  if len(scores) > self.max_det:
408
  top = np.argsort(-scores)[:self.max_det]
409
  boxes, scores, cls_ids = boxes[top], scores[top], cls_ids[top]
410
+ results = []
411
+ for b, s, c in zip(boxes, scores, cls_ids):
412
+ if b[2] <= b[0] or b[3] <= b[1]: continue
413
+ x1, y1, x2, y2 = float(b[0]), float(b[1]), float(b[2]), float(b[3])
414
+ if self._is_spurious_glove(int(c), x1, y1, x2, y2, float(s)): continue
415
+ results.append(BoundingBox(
416
+ x1=int(math.floor(x1)), y1=int(math.floor(y1)),
417
+ x2=int(math.ceil(x2)), y2=int(math.ceil(y2)),
418
  cls_id=int(c), conf=float(s),
419
+ ))
420
+ return self._apply_rules(results, image.shape[1], image=image)
 
 
421
 
422
  def predict_batch(self, batch_images, offset, n_keypoints):
423
  results = []