edgefirst-validator 4.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. deepview/modelpack/utils/argmax.py +16 -0
  2. edgefirst/validator/__init__.py +1 -0
  3. edgefirst/validator/__main__.py +375 -0
  4. edgefirst/validator/datasets/__init__.py +118 -0
  5. edgefirst/validator/datasets/cache.py +296 -0
  6. edgefirst/validator/datasets/core.py +250 -0
  7. edgefirst/validator/datasets/darknet.py +446 -0
  8. edgefirst/validator/datasets/database.py +1067 -0
  9. edgefirst/validator/datasets/instance/__init__.py +4 -0
  10. edgefirst/validator/datasets/instance/core.py +222 -0
  11. edgefirst/validator/datasets/instance/detection.py +145 -0
  12. edgefirst/validator/datasets/instance/multitask.py +80 -0
  13. edgefirst/validator/datasets/instance/segmentation.py +120 -0
  14. edgefirst/validator/datasets/utils/fetch.py +682 -0
  15. edgefirst/validator/datasets/utils/readers.py +425 -0
  16. edgefirst/validator/datasets/utils/transformations.py +1695 -0
  17. edgefirst/validator/evaluators/__init__.py +17 -0
  18. edgefirst/validator/evaluators/callbacks/__init__.py +3 -0
  19. edgefirst/validator/evaluators/callbacks/core.py +192 -0
  20. edgefirst/validator/evaluators/callbacks/plots.py +900 -0
  21. edgefirst/validator/evaluators/callbacks/studio.py +234 -0
  22. edgefirst/validator/evaluators/core.py +257 -0
  23. edgefirst/validator/evaluators/detection.py +749 -0
  24. edgefirst/validator/evaluators/multitask.py +270 -0
  25. edgefirst/validator/evaluators/parameters/__init__.py +53 -0
  26. edgefirst/validator/evaluators/parameters/core.py +554 -0
  27. edgefirst/validator/evaluators/parameters/dataset.py +239 -0
  28. edgefirst/validator/evaluators/parameters/model.py +338 -0
  29. edgefirst/validator/evaluators/parameters/validation.py +528 -0
  30. edgefirst/validator/evaluators/segmentation.py +729 -0
  31. edgefirst/validator/evaluators/utils/__init__.py +3 -0
  32. edgefirst/validator/evaluators/utils/classify.py +292 -0
  33. edgefirst/validator/evaluators/utils/match.py +262 -0
  34. edgefirst/validator/evaluators/utils/timer.py +132 -0
  35. edgefirst/validator/metrics/__init__.py +9 -0
  36. edgefirst/validator/metrics/data/__init__.py +7 -0
  37. edgefirst/validator/metrics/data/label.py +668 -0
  38. edgefirst/validator/metrics/data/metrics.py +759 -0
  39. edgefirst/validator/metrics/data/plots.py +476 -0
  40. edgefirst/validator/metrics/data/stats.py +507 -0
  41. edgefirst/validator/metrics/detection.py +595 -0
  42. edgefirst/validator/metrics/segmentation.py +173 -0
  43. edgefirst/validator/metrics/utils/math.py +717 -0
  44. edgefirst/validator/publishers/__init__.py +3 -0
  45. edgefirst/validator/publishers/console.py +147 -0
  46. edgefirst/validator/publishers/studio.py +128 -0
  47. edgefirst/validator/publishers/tensorboard.py +119 -0
  48. edgefirst/validator/publishers/utils/logger.py +111 -0
  49. edgefirst/validator/publishers/utils/table.py +403 -0
  50. edgefirst/validator/runners/__init__.py +8 -0
  51. edgefirst/validator/runners/core.py +727 -0
  52. edgefirst/validator/runners/deepviewrt.py +177 -0
  53. edgefirst/validator/runners/hailo.py +263 -0
  54. edgefirst/validator/runners/keras.py +150 -0
  55. edgefirst/validator/runners/kinara.py +265 -0
  56. edgefirst/validator/runners/offline.py +228 -0
  57. edgefirst/validator/runners/onnx.py +241 -0
  58. edgefirst/validator/runners/processing/decode.py +320 -0
  59. edgefirst/validator/runners/processing/dvapi.py +4192 -0
  60. edgefirst/validator/runners/processing/nms.py +637 -0
  61. edgefirst/validator/runners/processing/outputs.py +507 -0
  62. edgefirst/validator/runners/tensorrt.py +321 -0
  63. edgefirst/validator/runners/tflite.py +221 -0
  64. edgefirst/validator/validate.py +843 -0
  65. edgefirst/validator/visualize/__init__.py +3 -0
  66. edgefirst/validator/visualize/detection.py +623 -0
  67. edgefirst/validator/visualize/segmentation.py +281 -0
  68. edgefirst/validator/visualize/utils/plots.py +635 -0
  69. edgefirst_validator-4.2.1.dist-info/METADATA +111 -0
  70. edgefirst_validator-4.2.1.dist-info/RECORD +73 -0
  71. edgefirst_validator-4.2.1.dist-info/WHEEL +5 -0
  72. edgefirst_validator-4.2.1.dist-info/entry_points.txt +2 -0
  73. edgefirst_validator-4.2.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,637 @@
1
+ from typing import Tuple
2
+
3
+ import numpy as np
4
+
5
+
6
+ def nms(
7
+ boxes: np.ndarray,
8
+ scores: np.ndarray,
9
+ masks: np.ndarray = None,
10
+ iou_threshold: float = 0.70,
11
+ score_threshold: float = 0.001,
12
+ max_detections: int = 300,
13
+ class_agnostic: bool = False,
14
+ clip_boxes: bool = False,
15
+ nms_type: str = "tensorflow"
16
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
17
+ """
18
+ Deploy the NMS algorithm on object detection outputs. The NMS algorithm
19
+ can be specified using "tensorflow" by default. Otherwise, "numpy"
20
+ or "torch" are possible options.
21
+
22
+ Parameters
23
+ ----------
24
+ boxes: np.ndarray
25
+ Normalized input boxes to the NMS with shape (n, 4)
26
+ in [xmin, ymin, xmax, ymax] format.
27
+ scores: np.ndarray
28
+ Input scores to the NMS (n, ).
29
+ masks: np.ndarray
30
+ (Optional) Instance segmentation masks to also
31
+ to also be filtered during NMS.
32
+ iou_threshold: float
33
+ This is the IoU threshold for the NMS. Higher values
34
+ are less strict in filtering overlapping detections.
35
+ score_threshold: float
36
+ The confidence score threshold for the NMS. Filters to accept
37
+ more confident detections based on this threshold.
38
+ max_detections: int
39
+ The maximum number of boxes to be selected by NMS per class.
40
+ class_agnostic: bool
41
+ Run class-agnostic NMS. Default includes class.
42
+ clip_boxes: bool
43
+ If set to True, boxes will be clipped between 0 and 1. If False,
44
+ the coordinates are kept as it is.
45
+ nms_type: str
46
+ By default the Tensorflow NMS algorithm is deployed by
47
+ specifying "tensorflow" in this parameter. Otherwise, "numpy"
48
+ or "torch" are possible options.
49
+
50
+ Returns
51
+ -------
52
+ boxes : np.ndarray
53
+ This contains only the valid bounding boxes post NMS.
54
+ classes : np.ndarray
55
+ This contains only the valid classes post NMS.
56
+ scores : np.ndarray
57
+ This contains only the valid scores post NMS.
58
+ masks: np.ndarray
59
+ This contains only the valid instance segmentation masks
60
+ post NMS if it exists. Otherwise, None is returned.
61
+
62
+ Raises
63
+ ------
64
+ ImportError
65
+ Depending on the type of NMS specified, the TensorFlow or
66
+ PyTorch libraries are needed to use the NMS.
67
+ """
68
+ if nms_type == "tensorflow":
69
+ return tensorflow_combined_nms(
70
+ boxes=boxes,
71
+ scores=scores,
72
+ masks=masks,
73
+ iou_threshold=iou_threshold,
74
+ score_threshold=score_threshold,
75
+ max_detections=max_detections,
76
+ class_agnostic=class_agnostic,
77
+ clip_boxes=clip_boxes
78
+ )
79
+ else:
80
+ # Reshape boxes and scores and compute classes.
81
+ scores = np.reshape(scores, (boxes.shape[0], -1))
82
+ boxes = np.reshape(boxes, (-1, 4))
83
+ classes = np.argmax(scores, axis=1).astype(np.int32)
84
+
85
+ # Prefilter boxes and scores by minimum score
86
+ max_scores = np.max(scores, axis=1)
87
+ mask = max_scores >= score_threshold
88
+
89
+ # Prefilter the boxes, scores and classes IDs.
90
+ scores = max_scores[mask]
91
+ boxes = boxes[mask]
92
+ classes = classes[mask]
93
+ if masks is not None:
94
+ masks = masks[mask]
95
+
96
+ if nms_type == "torch":
97
+ keep = torch_nms(
98
+ boxes=boxes,
99
+ scores=scores,
100
+ iou_threshold=iou_threshold,
101
+ max_detections=max_detections
102
+ )
103
+ else:
104
+ keep = numpy_nms(
105
+ boxes=boxes,
106
+ scores=scores,
107
+ iou_threshold=iou_threshold,
108
+ max_detections=max_detections
109
+ )
110
+
111
+ # Filter boxes, scores, and classes.
112
+ if len(keep):
113
+ boxes = np.reshape(boxes[keep], (-1, 4))
114
+ scores = np.reshape(scores[keep], (boxes.shape[0],))
115
+ classes = np.reshape(classes[keep], (boxes.shape[0],))
116
+ if masks is not None:
117
+ masks = masks[keep]
118
+
119
+ return boxes, classes, scores, masks
120
+
121
+
122
+ def tensorflow_combined_nms(
123
+ boxes: np.ndarray,
124
+ scores: np.ndarray,
125
+ masks: np.ndarray = None,
126
+ iou_threshold: float = 0.001,
127
+ score_threshold: float = 0.70,
128
+ max_detections: int = 300,
129
+ class_agnostic: bool = False,
130
+ clip_boxes: bool = False
131
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
132
+ """
133
+ Return output of the TensorFlow NMS.
134
+ https://www.tensorflow.org/api_docs/python/tf/image/combined_non_max_suppression
135
+ By default, class-aware NMS is deployed. However, class-agnostic NMS
136
+ can be specified.
137
+
138
+ Parameters
139
+ ----------
140
+ boxes: np.ndarray
141
+ Normalized input boxes to the NMS with shape (n, 4)
142
+ in [xmin, ymin, xmax, ymax] format.
143
+ scores: np.ndarray
144
+ Input scores to the NMS (n, ).
145
+ masks: np.ndarray
146
+ (Optional) Instance segmentation masks to also
147
+ to also be filtered during NMS.
148
+ iou_threshold: float
149
+ This is the IoU threshold for the NMS. Higher values
150
+ are less strict in filtering overlapping detections.
151
+ score_threshold: float
152
+ The confidence score threshold for the NMS. Filters to accept
153
+ more confident detections based on this threshold.
154
+ max_detections: int
155
+ The maximum number of boxes to be selected by NMS per class.
156
+ class_agnostic: bool
157
+ Run class-agnostic NMS. Default includes class.
158
+ clip_boxes: bool
159
+ If set to True, boxes will be clipped between 0 and 1. If False,
160
+ the coordinates are kept as it is.
161
+
162
+ Returns
163
+ -------
164
+ boxes : np.ndarray
165
+ This contains only the valid bounding boxes post NMS.
166
+ classes : np.ndarray
167
+ This contains only the valid classes post NMS.
168
+ scores : np.ndarray
169
+ This contains only the valid scores post NMS.
170
+ masks: np.ndarray
171
+ This contains only the valid instance segmentation masks
172
+ post NMS if it exists. Otherwise, None is returned.
173
+
174
+ Raises
175
+ ------
176
+ ImportError
177
+ Raised if TensorFlow is not installed in the system
178
+ which is needed to run the NMS.
179
+ """
180
+
181
+ try:
182
+ import tensorflow as tf # type: ignore
183
+ except ImportError:
184
+ raise ImportError("TensorFlow is needed to use `tensorflow_nms`.")
185
+
186
+ if class_agnostic:
187
+ # Sort boxes by score.
188
+ boxes = tf.reshape(boxes, [-1, 4])
189
+ N = boxes.shape[0]
190
+ scores = tf.reshape(scores, [N, -1])
191
+ nms_scores = tf.reduce_max(scores, axis=1)
192
+ classes = tf.argmax(scores, axis=1)
193
+
194
+ keep = tensorflow_nms(
195
+ boxes=boxes,
196
+ scores=nms_scores,
197
+ iou_threshold=iou_threshold,
198
+ score_threshold=score_threshold,
199
+ max_detections=max_detections
200
+ )
201
+ boxes = tf.gather(boxes, keep).numpy()
202
+ scores = tf.gather(nms_scores, keep).numpy()
203
+ classes = tf.gather(classes, keep).numpy().astype(np.int32)
204
+ if masks is not None:
205
+ masks = tf.gather(masks, keep).numpy()
206
+
207
+ else:
208
+ N = boxes.shape[0]
209
+ boxes = np.reshape(boxes, (1, N, 1, 4))
210
+ scores = np.expand_dims(scores, 0)
211
+
212
+ # Get maximum class score per anchor.
213
+ per_anchor_scores = tf.reduce_max(scores, axis=-1) # shape (1, 8400)
214
+ per_anchor_scores = per_anchor_scores[0].numpy()
215
+
216
+ boxes, scores, classes, valid_boxes = \
217
+ tf.image.combined_non_max_suppression(
218
+ boxes=boxes,
219
+ scores=scores,
220
+ max_output_size_per_class=max_detections,
221
+ max_total_size=max_detections,
222
+ iou_threshold=iou_threshold,
223
+ score_threshold=score_threshold,
224
+ clip_boxes=clip_boxes
225
+ )
226
+ valid_boxes = valid_boxes.numpy()[0]
227
+ boxes = boxes.numpy()[0]
228
+ classes = classes.numpy()[0]
229
+ scores = scores.numpy()[0]
230
+
231
+ boxes = boxes[:valid_boxes]
232
+ scores = scores[:valid_boxes]
233
+ classes = classes[:valid_boxes].astype(np.int32)
234
+
235
+ if masks is not None:
236
+ # Keep top-k scores (e.g., the top 100 used in NMS).
237
+ sorted_indices = np.argsort(per_anchor_scores)[::-1]
238
+ top_indices = sorted_indices[:valid_boxes]
239
+ masks = masks[top_indices]
240
+
241
+ return boxes, classes, scores, masks
242
+
243
+
244
+ def tensorflow_nms(
245
+ boxes: np.ndarray,
246
+ scores: np.ndarray,
247
+ iou_threshold: float = 0.70,
248
+ score_threshold: float = 0.001,
249
+ max_detections: int = 300
250
+ ):
251
+ """
252
+ Return output from single class TensorFlow NMS.
253
+ https://www.tensorflow.org/api_docs/python/tf/image/non_max_suppression
254
+
255
+ Parameters
256
+ ----------
257
+ boxes: np.ndarray
258
+ Normalized input boxes to the NMS with shape (n, 4)
259
+ in [xmin, ymin, xmax, ymax] format.
260
+ scores: np.ndarray
261
+ Input scores to the NMS (n, ).
262
+ iou_threshold: float
263
+ This is the IoU threshold for the NMS. Higher values
264
+ are less strict in filtering overlapping detections.
265
+ score_threshold: float
266
+ The confidence score threshold for the NMS. Filters to accept
267
+ more confident detections based on this threshold.
268
+ max_detections: int
269
+ The maximum number of boxes to be selected by NMS per class.
270
+
271
+ Returns
272
+ -------
273
+ Tensor
274
+ This contains the indices of the boxes to keep.
275
+
276
+ Raises
277
+ ------
278
+ ImportError
279
+ Raised if TensorFlow is not installed in the system
280
+ which is needed to run the NMS.
281
+ """
282
+ try:
283
+ import tensorflow as tf # type: ignore
284
+ except ImportError:
285
+ raise ImportError("TensorFlow is needed to use `tensorflow_nms`.")
286
+
287
+ return tf.image.non_max_suppression(
288
+ boxes=boxes,
289
+ scores=scores,
290
+ max_output_size=max_detections,
291
+ iou_threshold=iou_threshold,
292
+ score_threshold=score_threshold
293
+ )
294
+
295
+
296
+ def torch_nms(
297
+ boxes: np.ndarray,
298
+ scores: np.ndarray,
299
+ iou_threshold: float,
300
+ max_detections: int = 300
301
+ ):
302
+ """
303
+ Return output from single class torchvision NMS.
304
+ https://docs.pytorch.org/vision/0.9/ops.html#torchvision.ops.nms
305
+
306
+ Parameters
307
+ ----------
308
+ boxes: np.ndarray
309
+ Normalized input boxes to the NMS with shape (n, 4)
310
+ in [xmin, ymin, xmax, ymax] format.
311
+ scores: np.ndarray
312
+ Input scores to the NMS (n, ).
313
+ iou_threshold: float
314
+ This is the IoU threshold for the NMS. Higher values
315
+ are less strict in filtering overlapping detections.
316
+ max_detections: int
317
+ The maximum number of boxes to be selected by NMS per class.
318
+
319
+ Returns
320
+ -------
321
+ torch.Tensor
322
+ This contains the indices of the boxes to keep.
323
+
324
+ Raises
325
+ ------
326
+ ImportError
327
+ Raised if PyTorch NMS is not installed.
328
+ """
329
+ try:
330
+ import torch # type: ignore
331
+ import torchvision # type: ignore
332
+ except ImportError:
333
+ raise ImportError(
334
+ "Torch and Torchvision is needed to use `torch_nms`.")
335
+
336
+ i = torchvision.ops.nms(torch.tensor(
337
+ boxes), torch.tensor(scores), iou_threshold)
338
+
339
+ if i.shape[0] > max_detections: # limit detections
340
+ i = i[:max_detections] # This limits detections.
341
+ return i
342
+
343
+
344
+ def numpy_nms(
345
+ boxes: np.ndarray,
346
+ scores: np.ndarray,
347
+ iou_threshold: float = 0.70,
348
+ max_detections: int = 300,
349
+ eps: float = 1e-7
350
+ ) -> np.ndarray:
351
+ """
352
+ Single class NMS implemented in NumPy.
353
+ Method taken from:: https://github.com/Megvii-BaseDetection/YOLOX/blob/main/yolox/utils/demo_utils.py#L57
354
+ Original source from:: https://github.com/amusi/Non-Maximum-Suppression/blob/master/nms.py
355
+
356
+ Parameters
357
+ ----------
358
+ boxes: np.ndarray
359
+ Normalized input boxes to the NMS with shape (n, 4)
360
+ in [xmin, ymin, xmax, ymax] format.
361
+ scores: np.ndarray
362
+ Input scores to the NMS (n, ).
363
+ iou_threshold: float
364
+ This is the IoU threshold for the NMS. Higher values
365
+ are less strict in filtering overlapping detections.
366
+ max_detections: int
367
+ The maximum number of boxes to be selected by NMS per class.
368
+ eps: float
369
+ Scalar to avoid division by zeros.
370
+
371
+ Returns
372
+ -------
373
+ np.ndarray
374
+ This contains the indices of the boxes to keep.
375
+ """
376
+ if len(boxes) == 0:
377
+ return np.array([], dtype=np.int32)
378
+
379
+ x1 = boxes[:, 0]
380
+ y1 = boxes[:, 1]
381
+ x2 = boxes[:, 2]
382
+ y2 = boxes[:, 3]
383
+
384
+ # Calculate areas (remove the +1 for normalized coordinates)
385
+ areas = (x2 - x1) * (y2 - y1)
386
+
387
+ # Sort by scores in descending order
388
+ order = scores.argsort()[::-1]
389
+
390
+ keep = []
391
+ while order.size > 0:
392
+ i = order[0]
393
+ keep.append(i)
394
+
395
+ if len(keep) >= max_detections:
396
+ break
397
+
398
+ # Calculate intersection coordinates
399
+ xx1 = np.maximum(x1[i], x1[order[1:]])
400
+ yy1 = np.maximum(y1[i], y1[order[1:]])
401
+ xx2 = np.minimum(x2[i], x2[order[1:]])
402
+ yy2 = np.minimum(y2[i], y2[order[1:]])
403
+
404
+ # Calculate intersection area (remove +1 for normalized coords)
405
+ w = np.maximum(0.0, xx2 - xx1)
406
+ h = np.maximum(0.0, yy2 - yy1)
407
+ inter = w * h
408
+
409
+ # Calculate IoU
410
+ union = areas[i] + areas[order[1:]] - inter
411
+ iou = inter / (union + eps)
412
+
413
+ # Keep boxes with IoU less than threshold
414
+ inds = np.where(iou <= iou_threshold)[0]
415
+ order = order[inds + 1]
416
+
417
+ return np.array(keep, dtype=np.int32)
418
+
419
+
420
+ def multiclass_nms_class_aware(
421
+ boxes: np.ndarray,
422
+ scores: np.ndarray,
423
+ iou_threshold: float = 0.45,
424
+ score_threshold: float = 0.10,
425
+ max_detections: int = 300,
426
+ nms_type: str = "numpy"
427
+ ) -> np.ndarray:
428
+ """
429
+ This is the YOLOx Multiclass NMS implemented in NumPy. Class-aware version.
430
+ Source:: https://github.com/Megvii-BaseDetection/YOLOX/blob/main/yolox/utils/demo_utils.py#L96
431
+
432
+ Parameters
433
+ ----------
434
+ boxes: np.ndarray
435
+ Normalized input boxes to the NMS with shape (n, 4)
436
+ in [xmin, ymin, xmax, ymax] format.
437
+ scores: np.ndarray
438
+ Input scores to the NMS (n, ).
439
+ iou_threshold: float
440
+ This is the IoU threshold for the NMS. Higher values
441
+ are less strict in filtering overlapping detections.
442
+ score_threshold: float
443
+ The confidence score threshold for the NMS. Filters to accept
444
+ more confident detections based on this threshold.
445
+ max_detections: int
446
+ The maximum number of boxes to be selected by NMS per class.
447
+ nms_type: str
448
+ Specify the type of NMS algorithm. By default, using a simple case
449
+ using NumPy. Otherwise, other options include 'tensorflow' and 'torch'.
450
+
451
+ Returns
452
+ -------
453
+ np.ndarray
454
+ Post-NMS detections (number of detections, 6) which contains
455
+ (xyxy, score, class) a total of 6 columns.
456
+ """
457
+ final_dets = []
458
+ num_classes = scores.shape[1]
459
+ for cls_ind in range(num_classes):
460
+ cls_scores = scores[:, cls_ind]
461
+ valid_score_mask = cls_scores > score_threshold
462
+ if valid_score_mask.sum() == 0:
463
+ continue
464
+ else:
465
+ valid_scores = cls_scores[valid_score_mask]
466
+ valid_boxes = boxes[valid_score_mask]
467
+
468
+ if nms_type == "numpy":
469
+ keep = numpy_nms(
470
+ boxes=valid_boxes,
471
+ scores=valid_scores,
472
+ iou_threshold=iou_threshold,
473
+ max_detections=max_detections
474
+ )
475
+ elif nms_type == "tensorflow":
476
+ keep = tensorflow_nms(
477
+ boxes=valid_boxes,
478
+ scores=valid_scores,
479
+ iou_threshold=iou_threshold,
480
+ score_threshold=score_threshold,
481
+ max_detections=max_detections
482
+ )
483
+ elif nms_type == "torch":
484
+ keep = torch_nms(
485
+ boxes=valid_boxes,
486
+ scores=valid_scores,
487
+ iou_threshold=iou_threshold,
488
+ max_detections=max_detections
489
+ )
490
+ else:
491
+ raise TypeError(
492
+ "Unrecognized NMS type '{}' provided.".format(nms_type))
493
+
494
+ if len(keep) > 0:
495
+ cls_inds = np.ones((len(keep), 1)) * cls_ind
496
+ dets = np.concatenate(
497
+ [valid_boxes[keep], valid_scores[keep, None], cls_inds], 1
498
+ )
499
+ final_dets.append(dets)
500
+ if len(final_dets) == 0:
501
+ return None
502
+ return np.concatenate(final_dets, 0)
503
+
504
+
505
+ def multiclass_nms_class_agnostic(
506
+ boxes: np.ndarray,
507
+ scores: np.ndarray,
508
+ iou_threshold: float = 0.45,
509
+ score_threshold: float = 0.10,
510
+ max_detections: int = 300,
511
+ nms_type: str = "numpy"
512
+ ) -> np.ndarray:
513
+ """
514
+ This is the YOLOx Multiclass NMS implemented in NumpPy. Class-agnostic version.
515
+ Source:: https://github.com/Megvii-BaseDetection/YOLOX/blob/main/yolox/utils/demo_utils.py#L120.
516
+
517
+ Parameters
518
+ ----------
519
+ boxes: np.ndarray
520
+ Normalized input boxes to the NMS with shape (n, 4)
521
+ in [xmin, ymin, xmax, ymax] format.
522
+ scores: np.ndarray
523
+ Input scores to the NMS (n, ).
524
+ iou_threshold: float
525
+ This is the IoU threshold for the NMS. Higher values
526
+ are less strict in filtering overlapping detections.
527
+ score_threshold: float
528
+ The confidence score threshold for the NMS. Filters to accept
529
+ more confident detections based on this threshold.
530
+ max_detections: int
531
+ The maximum number of boxes to be selected by NMS per class.
532
+ nms_type: str
533
+ Specify the type of NMS algorithm. By default, using a simple case
534
+ using NumPy. Otherwise, other options include 'tensorflow' and 'torch'.
535
+
536
+ Returns
537
+ -------
538
+ np.ndarray
539
+ Post-NMS detections (number of detections, 6) which contains
540
+ (xyxy, score, class) a total of 6 columns.
541
+ """
542
+ cls_inds = scores.argmax(1)
543
+ cls_scores = scores[np.arange(len(cls_inds)), cls_inds]
544
+
545
+ valid_score_mask = cls_scores > score_threshold
546
+ if valid_score_mask.sum() == 0:
547
+ return None
548
+ valid_scores = cls_scores[valid_score_mask]
549
+ valid_boxes = boxes[valid_score_mask]
550
+ valid_cls_inds = cls_inds[valid_score_mask]
551
+
552
+ if nms_type == "numpy":
553
+ keep = numpy_nms(
554
+ boxes=valid_boxes,
555
+ scores=valid_scores,
556
+ iou_threshold=iou_threshold,
557
+ max_detections=max_detections
558
+ )
559
+ elif nms_type == "tensorflow":
560
+ keep = tensorflow_nms(
561
+ boxes=valid_boxes,
562
+ scores=valid_scores,
563
+ iou_threshold=iou_threshold,
564
+ score_threshold=score_threshold,
565
+ max_detections=max_detections
566
+ )
567
+ elif nms_type == "torch":
568
+ keep = torch_nms(
569
+ boxes=valid_boxes,
570
+ scores=valid_scores,
571
+ iou_threshold=iou_threshold,
572
+ max_detections=max_detections
573
+ )
574
+ else:
575
+ raise TypeError(
576
+ "Unrecognized NMS type '{}' provided.".format(nms_type))
577
+
578
+ if len(keep) > 0:
579
+ dets = np.concatenate(
580
+ [valid_boxes[keep], valid_scores[keep, None],
581
+ valid_cls_inds[keep, None]], 1
582
+ )
583
+ return dets
584
+
585
+
586
+ def multiclass_nms(
587
+ boxes: np.ndarray,
588
+ scores: np.ndarray,
589
+ iou_threshold: float = 0.45,
590
+ score_threshold: float = 0.10,
591
+ max_detections: int = 300,
592
+ class_agnostic: bool = True,
593
+ nms_type: str = "numpy"
594
+ ) -> np.ndarray:
595
+ """
596
+ This is the YOLOx Multiclass NMS implemented in NumPy.
597
+ Source:: https://github.com/Megvii-BaseDetection/YOLOX/blob/main/yolox/utils/demo_utils.py#L87
598
+
599
+ Parameters
600
+ ----------
601
+ boxes: np.ndarray
602
+ Normalized input boxes to the NMS with shape (n, 4)
603
+ in [xmin, ymin, xmax, ymax] format.
604
+ scores: np.ndarray
605
+ Input scores to the NMS (n, ).
606
+ iou_threshold: float
607
+ This is the IoU threshold for the NMS. Higher values
608
+ are less strict in filtering overlapping detections.
609
+ score_threshold: float
610
+ The confidence score threshold for the NMS. Filters to accept
611
+ more confident detections based on this threshold.
612
+ max_detections: int
613
+ The maximum number of boxes to be selected by NMS per class.
614
+ class_agnostic: bool
615
+ Run class-agnostic NMS. Default includes class.
616
+ nms_type: str
617
+ Specify the type of NMS algorithm. By default, using a simple case
618
+ using NumPy. Otherwise, other options include 'tensorflow' and 'torch'.
619
+
620
+ Returns
621
+ -------
622
+ np.ndarray
623
+ Post-NMS detections (number of detections, 6) which contains
624
+ (xyxy, score, class) a total of 6 columns.
625
+ """
626
+ if class_agnostic:
627
+ nms_method = multiclass_nms_class_agnostic
628
+ else:
629
+ nms_method = multiclass_nms_class_aware
630
+ return nms_method(
631
+ boxes=boxes,
632
+ scores=scores,
633
+ iou_threshold=iou_threshold,
634
+ score_threshold=score_threshold,
635
+ max_detections=max_detections,
636
+ nms_type=nms_type
637
+ )