Xenova HF Staff commited on
Commit
723a9b2
·
verified ·
1 Parent(s): 47e90a2

sync 2e7068faf55e

Browse files
README.md CHANGED
@@ -1,3 +1,89 @@
1
  ---
 
2
  license: apache-2.0
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ library_name: kernels
3
  license: apache-2.0
4
+ tags:
5
+ - kernel
6
+ - webgpu
7
+ - wgsl
8
  ---
9
+ # ai.onnx.NonMaxSuppression
10
+
11
+ `ai.onnx` · standard ONNX operator · ONNX opset ≥ 11
12
+
13
+ ## Description
14
+
15
+ Filters boxes whose intersection over union (IoU) with a higher-scoring selected box exceeds `iou_threshold`. Processing is independent per batch and class, and returns logical int64 `[batch_index, class_index, box_index]` triples with lossless uint32 WebGPU storage. The int64 selection limit uses a semantics-preserving saturating projection to uint32. Because the result length is data-dependent, callers provide its exact shape. The current backend implements float32 boxes, scores, and thresholds.
16
+
17
+ See the [ONNX `NonMaxSuppression` spec](https://onnx.ai/onnx/operators/onnx__NonMaxSuppression.html) for the reference semantics.
18
+
19
+ ## Inputs
20
+
21
+ | Name | Bind key | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- | --- | --- |
23
+ | `boxes` | `boxes` | `T` | same as logical dtype | `3` | — | Bounding box coordinates with shape `[num_batches, spatial_dimension, 4]`; box format is controlled by `center_point_box`. | required |
24
+ | `scores` | `scores` | `T` | same as logical dtype | `3` | — | Per-class confidence scores with shape `[num_batches, num_classes, spatial_dimension]`. | required |
25
+ | `max_output_boxes_per_class` | `max_output_boxes_per_class` | `M` | `uint32` | `0` | — | Optional logical int64 scalar limiting boxes selected per batch and class. When omitted, the ONNX default is zero and the output is empty. WebGPU maps non-positive values to zero and values above uint32 range to `UINT32_MAX`; this preserves results because no group can select more boxes than its finite input. | optional |
26
+ | `iou_threshold` | `iou_threshold` | `T` | same as logical dtype | `0` | — | Optional scalar IoU threshold in `[0, 1]`; boxes whose IoU is strictly greater are suppressed. Defaults to zero. | optional |
27
+ | `score_threshold` | `score_threshold` | `T` | same as logical dtype | `0` | — | Optional scalar score threshold. When present, only boxes whose score is strictly greater than the threshold are considered; when omitted, scores are not filtered. | optional |
28
+
29
+ ## Outputs
30
+
31
+ | Name | Bind key | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
32
+ | --- | --- | --- | --- | --- | --- | --- | --- |
33
+ | `selected_indices` | `selected_indices` | `I` | `uint32` | `2` | — | Logical int64 selected-box indices with shape `[num_selected_indices, 3]`, each row containing `[batch_index, class_index, box_index]`. WebGPU stores the bounded indices as uint32. | required |
34
+
35
+ ## Attributes
36
+
37
+ Default values (overridable per request):
38
+
39
+ | Attribute | Default | Description |
40
+ | --- | --- | --- |
41
+ | `center_point_box` | `0` | Box coordinate format: 0 for corner format `[y1, x1, y2, x2]`, 1 for center format `[x_center, y_center, width, height]`. |
42
+
43
+ ## Type constraints
44
+
45
+ | Variable | Allowed dtypes |
46
+ | --- | --- |
47
+ | `T` | `float32` |
48
+ | `M` | `int64` |
49
+ | `I` | `int64` |
50
+
51
+ ## Files
52
+
53
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
54
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
55
+ - [`test.json`](build/webgpu/test.json) — correctness cases
56
+ - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
57
+ - [`nms-groups-compact.wgsl.jinja`](build/webgpu/nms-groups-compact.wgsl.jinja)
58
+ - [`nms-groups-select.wgsl.jinja`](build/webgpu/nms-groups-select.wgsl.jinja)
59
+ - [`non-max-suppression.wgsl.jinja`](build/webgpu/non-max-suppression.wgsl.jinja)
60
+
61
+ ## Use with `@huggingface/kernels`
62
+
63
+ The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
64
+
65
+ The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
66
+
67
+ - `selected_indices`
68
+
69
+ Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
70
+
71
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
72
+
73
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
74
+
75
+ ```js
76
+ import { getKernel } from "@huggingface/kernels";
77
+
78
+ const kernel = await getKernel("webgpu-kernels/ai.onnx.NonMaxSuppression", { version: 1 });
79
+ // Explicit destinations request optional results or supply metadata that cannot be inferred.
80
+ const { selected_indices } = await kernel({
81
+ boxes: { data: boxesData, shape: [1, 1, 4] },
82
+ scores: { data: scoresData, shape: [1, 1, 1] },
83
+ max_output_boxes_per_class: { data: max_output_boxes_per_classData, shape: [] },
84
+ iou_threshold: { data: iou_thresholdData, shape: [] },
85
+ score_threshold: { data: score_thresholdData, shape: [] },
86
+ }, {
87
+ outputs: { selected_indices: { shape: [1, 3], dtype: "int64" } },
88
+ });
89
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.NonMaxSuppression",
3
+ "cases": [
4
+ {
5
+ "name": "small_64_boxes",
6
+ "inputs": {
7
+ "boxes": { "dtype": "float32", "shape": [1, 64, 4], "data": { "kind": "constant", "value": 0.0 } },
8
+ "scores": { "dtype": "float32", "shape": [1, 1, 64], "data": { "kind": "constant", "value": 1.0 } },
9
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "constant", "value": 10 } },
10
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.5 } },
11
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.1 } }
12
+ },
13
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [10, 3] } }
14
+ },
15
+ {
16
+ "name": "nms-1class-2048-boxes",
17
+ "preset": "smoke",
18
+ "inputs": {
19
+ "boxes": { "dtype": "float32", "shape": [1, 2048, 4], "data": { "kind": "constant", "value": 0.0 } },
20
+ "scores": { "dtype": "float32", "shape": [1, 1, 2048], "data": { "kind": "constant", "value": 1.0 } },
21
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "constant", "value": 64 } },
22
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.5 } },
23
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.5 } }
24
+ },
25
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [64, 3] } },
26
+ "bench": {
27
+ "primary": true,
28
+ "metrics": [{ "type": "bandwidth", "value": "4 * (numel(shapes.boxes) + numel(shapes.scores))" }]
29
+ }
30
+ },
31
+ {
32
+ "name": "nms-8class-1024-boxes",
33
+ "preset": "smoke",
34
+ "inputs": {
35
+ "boxes": { "dtype": "float32", "shape": [1, 1024, 4], "data": { "kind": "constant", "value": 0.0 } },
36
+ "scores": { "dtype": "float32", "shape": [1, 8, 1024], "data": { "kind": "constant", "value": 1.0 } },
37
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "constant", "value": 32 } },
38
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.5 } },
39
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.5 } }
40
+ },
41
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [256, 3] } },
42
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "4 * (numel(shapes.boxes) + numel(shapes.scores))" }] }
43
+ },
44
+ {
45
+ "name": "nms_single_class_4096_boxes_max_keep_wg_parallel",
46
+ "preset": "stress",
47
+ "attrs": { "center_point_box": 0 },
48
+ "inputs": {
49
+ "boxes": { "dtype": "float32", "shape": [1, 4096, 4], "data": { "kind": "constant", "value": 0.0 } },
50
+ "scores": { "dtype": "float32", "shape": [1, 1, 4096], "data": { "kind": "constant", "value": 1.0 } },
51
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "constant", "value": 4096 } },
52
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.01 } },
53
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.0 } }
54
+ },
55
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [4096, 3] } },
56
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "4 * (numel(shapes.boxes) + numel(shapes.scores))" }] }
57
+ },
58
+ {
59
+ "name": "yolo-80class-8400-boxes-top100",
60
+ "preset": "stress",
61
+ "provenance": {
62
+ "notes": "Worst-case exact-output workload: zero-area boxes never overlap and unit scores pass the threshold, so every class reaches max_output_boxes_per_class."
63
+ },
64
+ "attrs": { "center_point_box": 1 },
65
+ "inputs": {
66
+ "boxes": { "dtype": "float32", "shape": [1, 8400, 4], "data": { "kind": "constant", "value": 0.0 } },
67
+ "scores": { "dtype": "float32", "shape": [1, 80, 8400], "data": { "kind": "constant", "value": 1.0 } },
68
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "constant", "value": 100 } },
69
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.45 } },
70
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "constant", "value": 0.25 } }
71
+ },
72
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [8000, 3] } },
73
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "4 * (numel(shapes.boxes) + numel(shapes.scores))" }] }
74
+ }
75
+ ]
76
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,730 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "ai.onnx",
3
+ "name": "NonMaxSuppression",
4
+ "sinceVersion": 11,
5
+ "description": "Filters boxes whose intersection over union (IoU) with a higher-scoring selected box exceeds `iou_threshold`. Processing is independent per batch and class, and returns logical int64 `[batch_index, class_index, box_index]` triples with lossless uint32 WebGPU storage. The int64 selection limit uses a semantics-preserving saturating projection to uint32. Because the result length is data-dependent, callers provide its exact shape. The current backend implements float32 boxes, scores, and thresholds.",
6
+ "inputs": [
7
+ {
8
+ "role": "boxes",
9
+ "dtype": "T",
10
+ "rank": 3,
11
+ "description": "Bounding box coordinates with shape `[num_batches, spatial_dimension, 4]`; box format is controlled by `center_point_box`."
12
+ },
13
+ {
14
+ "role": "scores",
15
+ "dtype": "T",
16
+ "rank": 3,
17
+ "description": "Per-class confidence scores with shape `[num_batches, num_classes, spatial_dimension]`."
18
+ },
19
+ {
20
+ "role": "max_output_boxes_per_class",
21
+ "dtype": "M",
22
+ "rank": 0,
23
+ "optional": true,
24
+ "description": "Optional logical int64 scalar limiting boxes selected per batch and class. When omitted, the ONNX default is zero and the output is empty. WebGPU maps non-positive values to zero and values above uint32 range to `UINT32_MAX`; this preserves results because no group can select more boxes than its finite input."
25
+ },
26
+ {
27
+ "role": "iou_threshold",
28
+ "dtype": "T",
29
+ "rank": 0,
30
+ "optional": true,
31
+ "description": "Optional scalar IoU threshold in `[0, 1]`; boxes whose IoU is strictly greater are suppressed. Defaults to zero."
32
+ },
33
+ {
34
+ "role": "score_threshold",
35
+ "dtype": "T",
36
+ "rank": 0,
37
+ "optional": true,
38
+ "description": "Optional scalar score threshold. When present, only boxes whose score is strictly greater than the threshold are considered; when omitted, scores are not filtered."
39
+ }
40
+ ],
41
+ "outputs": [
42
+ {
43
+ "role": "selected_indices",
44
+ "dtype": "I",
45
+ "rank": 2,
46
+ "description": "Logical int64 selected-box indices with shape `[num_selected_indices, 3]`, each row containing `[batch_index, class_index, box_index]`. WebGPU stores the bounded indices as uint32."
47
+ }
48
+ ],
49
+ "attributes": { "center_point_box": 0 },
50
+ "attributeDescriptions": {
51
+ "center_point_box": "Box coordinate format: 0 for corner format `[y1, x1, y2, x2]`, 1 for center format `[x_center, y_center, width, height]`."
52
+ },
53
+ "attributeConstraints": { "center_point_box": { "values": [0, 1] } },
54
+ "typeConstraints": { "T": ["float32"], "M": ["int64"], "I": ["int64"] },
55
+ "args": {
56
+ "boxes": { "kind": "tensor", "semantic": "boxes", "role": "input" },
57
+ "scores": { "kind": "tensor", "semantic": "scores", "role": "input" },
58
+ "max_output_boxes_per_class": {
59
+ "kind": "tensor",
60
+ "semantic": "max_output_boxes_per_class",
61
+ "role": "input",
62
+ "dtype": "uint32",
63
+ "narrowing": "saturating",
64
+ "required": false
65
+ },
66
+ "iou_threshold": { "kind": "tensor", "semantic": "iou_threshold", "role": "input", "required": false },
67
+ "score_threshold": { "kind": "tensor", "semantic": "score_threshold", "role": "input", "required": false },
68
+ "selected_indices": { "kind": "tensor", "semantic": "selected_indices", "role": "output", "dtype": "uint32" }
69
+ },
70
+ "tunables": { "WORKGROUP_SIZE": 256 },
71
+ "derive": {
72
+ "storageBufferLimit": "min(device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize)",
73
+ "validLayout": "ranks.boxes == 3 and ranks.scores == 3 and ranks.selected_indices == 2 and dim(shapes.boxes, 0) == dim(shapes.scores, 0) and dim(shapes.boxes, 1) == dim(shapes.scores, 2) and dim(shapes.boxes, 2) == 4 and dim(shapes.selected_indices, 1) == 3 and dim(shapes.selected_indices, 0) <= dim(shapes.boxes, 0) * dim(shapes.scores, 1) * dim(shapes.boxes, 1) and (present.max_output_boxes_per_class or dim(shapes.selected_indices, 0) == 0) and (not present.max_output_boxes_per_class or (ranks.max_output_boxes_per_class == 0 and numel(shapes.max_output_boxes_per_class) == 1)) and (not present.iou_threshold or (ranks.iou_threshold == 0 and numel(shapes.iou_threshold) == 1)) and (not present.score_threshold or (ranks.score_threshold == 0 and numel(shapes.score_threshold) == 1))",
74
+ "numGroups": "dim(shapes.boxes, 0) * dim(shapes.scores, 1)",
75
+ "maxKeep": "max(1, dim(shapes.boxes, 1))",
76
+ "parallelDispatchFits": "numGroups <= device.limits.maxComputeWorkgroupsPerDimension",
77
+ "parallelScratchFits": "numGroups * dim(shapes.boxes, 1) * 4 <= storageBufferLimit and numGroups * maxKeep * 4 <= storageBufferLimit and numGroups * 4 <= storageBufferLimit"
78
+ },
79
+ "constants": { "workgroupSize": "tunables.WORKGROUP_SIZE" },
80
+ "bindingSets": {
81
+ "parallelSelect": [
82
+ {
83
+ "name": "boxes",
84
+ "arg": "boxes",
85
+ "semantic": "boxes",
86
+ "buffer": { "type": "read-only-storage" },
87
+ "elementType": "f32"
88
+ },
89
+ {
90
+ "name": "scores",
91
+ "arg": "scores",
92
+ "semantic": "scores",
93
+ "buffer": { "type": "read-only-storage" },
94
+ "elementType": "f32"
95
+ },
96
+ {
97
+ "name": "max_output_boxes_per_class",
98
+ "arg": "max_output_boxes_per_class",
99
+ "semantic": "max_output_boxes_per_class",
100
+ "buffer": { "type": "read-only-storage" },
101
+ "elementType": "u32",
102
+ "length": 1
103
+ },
104
+ {
105
+ "name": "iou_threshold",
106
+ "arg": "iou_threshold",
107
+ "semantic": "iou_threshold",
108
+ "buffer": { "type": "read-only-storage" },
109
+ "elementType": "f32",
110
+ "length": 1
111
+ },
112
+ {
113
+ "name": "score_threshold",
114
+ "arg": "score_threshold",
115
+ "semantic": "score_threshold",
116
+ "buffer": { "type": "read-only-storage" },
117
+ "elementType": "f32",
118
+ "length": 1
119
+ },
120
+ { "name": "suppressed", "semantic": "suppressed", "buffer": { "type": "storage" }, "elementType": "u32" },
121
+ { "name": "groupSel", "semantic": "groupSel", "buffer": { "type": "storage" }, "elementType": "u32" },
122
+ { "name": "groupCount", "semantic": "groupCount", "buffer": { "type": "storage" }, "elementType": "u32" },
123
+ {
124
+ "name": "params",
125
+ "semantic": "kernel.params",
126
+ "buffer": { "type": "uniform" },
127
+ "struct": {
128
+ "name": "Params",
129
+ "fields": [
130
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
131
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
132
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
133
+ ]
134
+ }
135
+ }
136
+ ],
137
+ "parallelCompact": [
138
+ { "name": "groupSel", "semantic": "groupSel", "buffer": { "type": "read-only-storage" }, "elementType": "u32" },
139
+ {
140
+ "name": "groupCount",
141
+ "semantic": "groupCount",
142
+ "buffer": { "type": "read-only-storage" },
143
+ "elementType": "u32"
144
+ },
145
+ {
146
+ "name": "selected_indices",
147
+ "arg": "selected_indices",
148
+ "semantic": "selected_indices",
149
+ "buffer": { "type": "storage" },
150
+ "elementType": "u32"
151
+ },
152
+ {
153
+ "name": "params",
154
+ "semantic": "kernel.params",
155
+ "buffer": { "type": "uniform" },
156
+ "struct": {
157
+ "name": "Params",
158
+ "fields": [
159
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
160
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
161
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" }
162
+ ]
163
+ }
164
+ }
165
+ ],
166
+ "serialDefaults": [
167
+ {
168
+ "name": "boxes",
169
+ "arg": "boxes",
170
+ "semantic": "boxes",
171
+ "buffer": { "type": "read-only-storage" },
172
+ "elementType": "f32"
173
+ },
174
+ {
175
+ "name": "scores",
176
+ "arg": "scores",
177
+ "semantic": "scores",
178
+ "buffer": { "type": "read-only-storage" },
179
+ "elementType": "f32"
180
+ },
181
+ {
182
+ "name": "selected_indices",
183
+ "arg": "selected_indices",
184
+ "semantic": "selected_indices",
185
+ "buffer": { "type": "storage" },
186
+ "elementType": "u32"
187
+ },
188
+ {
189
+ "name": "params",
190
+ "semantic": "kernel.params",
191
+ "buffer": { "type": "uniform" },
192
+ "struct": {
193
+ "name": "Params",
194
+ "fields": [
195
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
196
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
197
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
198
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
199
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
200
+ ]
201
+ }
202
+ }
203
+ ],
204
+ "serialMax": [
205
+ {
206
+ "name": "boxes",
207
+ "arg": "boxes",
208
+ "semantic": "boxes",
209
+ "buffer": { "type": "read-only-storage" },
210
+ "elementType": "f32"
211
+ },
212
+ {
213
+ "name": "scores",
214
+ "arg": "scores",
215
+ "semantic": "scores",
216
+ "buffer": { "type": "read-only-storage" },
217
+ "elementType": "f32"
218
+ },
219
+ {
220
+ "name": "max_output_boxes_per_class",
221
+ "arg": "max_output_boxes_per_class",
222
+ "semantic": "max_output_boxes_per_class",
223
+ "buffer": { "type": "read-only-storage" },
224
+ "elementType": "u32",
225
+ "length": 1
226
+ },
227
+ {
228
+ "name": "selected_indices",
229
+ "arg": "selected_indices",
230
+ "semantic": "selected_indices",
231
+ "buffer": { "type": "storage" },
232
+ "elementType": "u32"
233
+ },
234
+ {
235
+ "name": "params",
236
+ "semantic": "kernel.params",
237
+ "buffer": { "type": "uniform" },
238
+ "struct": {
239
+ "name": "Params",
240
+ "fields": [
241
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
242
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
243
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
244
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
245
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
246
+ ]
247
+ }
248
+ }
249
+ ],
250
+ "serialIou": [
251
+ {
252
+ "name": "boxes",
253
+ "arg": "boxes",
254
+ "semantic": "boxes",
255
+ "buffer": { "type": "read-only-storage" },
256
+ "elementType": "f32"
257
+ },
258
+ {
259
+ "name": "scores",
260
+ "arg": "scores",
261
+ "semantic": "scores",
262
+ "buffer": { "type": "read-only-storage" },
263
+ "elementType": "f32"
264
+ },
265
+ {
266
+ "name": "iou_threshold",
267
+ "arg": "iou_threshold",
268
+ "semantic": "iou_threshold",
269
+ "buffer": { "type": "read-only-storage" },
270
+ "elementType": "f32",
271
+ "length": 1
272
+ },
273
+ {
274
+ "name": "selected_indices",
275
+ "arg": "selected_indices",
276
+ "semantic": "selected_indices",
277
+ "buffer": { "type": "storage" },
278
+ "elementType": "u32"
279
+ },
280
+ {
281
+ "name": "params",
282
+ "semantic": "kernel.params",
283
+ "buffer": { "type": "uniform" },
284
+ "struct": {
285
+ "name": "Params",
286
+ "fields": [
287
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
288
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
289
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
290
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
291
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
292
+ ]
293
+ }
294
+ }
295
+ ],
296
+ "serialScore": [
297
+ {
298
+ "name": "boxes",
299
+ "arg": "boxes",
300
+ "semantic": "boxes",
301
+ "buffer": { "type": "read-only-storage" },
302
+ "elementType": "f32"
303
+ },
304
+ {
305
+ "name": "scores",
306
+ "arg": "scores",
307
+ "semantic": "scores",
308
+ "buffer": { "type": "read-only-storage" },
309
+ "elementType": "f32"
310
+ },
311
+ {
312
+ "name": "score_threshold",
313
+ "arg": "score_threshold",
314
+ "semantic": "score_threshold",
315
+ "buffer": { "type": "read-only-storage" },
316
+ "elementType": "f32",
317
+ "length": 1
318
+ },
319
+ {
320
+ "name": "selected_indices",
321
+ "arg": "selected_indices",
322
+ "semantic": "selected_indices",
323
+ "buffer": { "type": "storage" },
324
+ "elementType": "u32"
325
+ },
326
+ {
327
+ "name": "params",
328
+ "semantic": "kernel.params",
329
+ "buffer": { "type": "uniform" },
330
+ "struct": {
331
+ "name": "Params",
332
+ "fields": [
333
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
334
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
335
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
336
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
337
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
338
+ ]
339
+ }
340
+ }
341
+ ],
342
+ "serialMaxIou": [
343
+ {
344
+ "name": "boxes",
345
+ "arg": "boxes",
346
+ "semantic": "boxes",
347
+ "buffer": { "type": "read-only-storage" },
348
+ "elementType": "f32"
349
+ },
350
+ {
351
+ "name": "scores",
352
+ "arg": "scores",
353
+ "semantic": "scores",
354
+ "buffer": { "type": "read-only-storage" },
355
+ "elementType": "f32"
356
+ },
357
+ {
358
+ "name": "max_output_boxes_per_class",
359
+ "arg": "max_output_boxes_per_class",
360
+ "semantic": "max_output_boxes_per_class",
361
+ "buffer": { "type": "read-only-storage" },
362
+ "elementType": "u32",
363
+ "length": 1
364
+ },
365
+ {
366
+ "name": "iou_threshold",
367
+ "arg": "iou_threshold",
368
+ "semantic": "iou_threshold",
369
+ "buffer": { "type": "read-only-storage" },
370
+ "elementType": "f32",
371
+ "length": 1
372
+ },
373
+ {
374
+ "name": "selected_indices",
375
+ "arg": "selected_indices",
376
+ "semantic": "selected_indices",
377
+ "buffer": { "type": "storage" },
378
+ "elementType": "u32"
379
+ },
380
+ {
381
+ "name": "params",
382
+ "semantic": "kernel.params",
383
+ "buffer": { "type": "uniform" },
384
+ "struct": {
385
+ "name": "Params",
386
+ "fields": [
387
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
388
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
389
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
390
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
391
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
392
+ ]
393
+ }
394
+ }
395
+ ],
396
+ "serialMaxScore": [
397
+ {
398
+ "name": "boxes",
399
+ "arg": "boxes",
400
+ "semantic": "boxes",
401
+ "buffer": { "type": "read-only-storage" },
402
+ "elementType": "f32"
403
+ },
404
+ {
405
+ "name": "scores",
406
+ "arg": "scores",
407
+ "semantic": "scores",
408
+ "buffer": { "type": "read-only-storage" },
409
+ "elementType": "f32"
410
+ },
411
+ {
412
+ "name": "max_output_boxes_per_class",
413
+ "arg": "max_output_boxes_per_class",
414
+ "semantic": "max_output_boxes_per_class",
415
+ "buffer": { "type": "read-only-storage" },
416
+ "elementType": "u32",
417
+ "length": 1
418
+ },
419
+ {
420
+ "name": "score_threshold",
421
+ "arg": "score_threshold",
422
+ "semantic": "score_threshold",
423
+ "buffer": { "type": "read-only-storage" },
424
+ "elementType": "f32",
425
+ "length": 1
426
+ },
427
+ {
428
+ "name": "selected_indices",
429
+ "arg": "selected_indices",
430
+ "semantic": "selected_indices",
431
+ "buffer": { "type": "storage" },
432
+ "elementType": "u32"
433
+ },
434
+ {
435
+ "name": "params",
436
+ "semantic": "kernel.params",
437
+ "buffer": { "type": "uniform" },
438
+ "struct": {
439
+ "name": "Params",
440
+ "fields": [
441
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
442
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
443
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
444
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
445
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
446
+ ]
447
+ }
448
+ }
449
+ ],
450
+ "serialIouScore": [
451
+ {
452
+ "name": "boxes",
453
+ "arg": "boxes",
454
+ "semantic": "boxes",
455
+ "buffer": { "type": "read-only-storage" },
456
+ "elementType": "f32"
457
+ },
458
+ {
459
+ "name": "scores",
460
+ "arg": "scores",
461
+ "semantic": "scores",
462
+ "buffer": { "type": "read-only-storage" },
463
+ "elementType": "f32"
464
+ },
465
+ {
466
+ "name": "iou_threshold",
467
+ "arg": "iou_threshold",
468
+ "semantic": "iou_threshold",
469
+ "buffer": { "type": "read-only-storage" },
470
+ "elementType": "f32",
471
+ "length": 1
472
+ },
473
+ {
474
+ "name": "score_threshold",
475
+ "arg": "score_threshold",
476
+ "semantic": "score_threshold",
477
+ "buffer": { "type": "read-only-storage" },
478
+ "elementType": "f32",
479
+ "length": 1
480
+ },
481
+ {
482
+ "name": "selected_indices",
483
+ "arg": "selected_indices",
484
+ "semantic": "selected_indices",
485
+ "buffer": { "type": "storage" },
486
+ "elementType": "u32"
487
+ },
488
+ {
489
+ "name": "params",
490
+ "semantic": "kernel.params",
491
+ "buffer": { "type": "uniform" },
492
+ "struct": {
493
+ "name": "Params",
494
+ "fields": [
495
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
496
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
497
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
498
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
499
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
500
+ ]
501
+ }
502
+ }
503
+ ],
504
+ "serialAll": [
505
+ {
506
+ "name": "boxes",
507
+ "arg": "boxes",
508
+ "semantic": "boxes",
509
+ "buffer": { "type": "read-only-storage" },
510
+ "elementType": "f32"
511
+ },
512
+ {
513
+ "name": "scores",
514
+ "arg": "scores",
515
+ "semantic": "scores",
516
+ "buffer": { "type": "read-only-storage" },
517
+ "elementType": "f32"
518
+ },
519
+ {
520
+ "name": "max_output_boxes_per_class",
521
+ "arg": "max_output_boxes_per_class",
522
+ "semantic": "max_output_boxes_per_class",
523
+ "buffer": { "type": "read-only-storage" },
524
+ "elementType": "u32",
525
+ "length": 1
526
+ },
527
+ {
528
+ "name": "iou_threshold",
529
+ "arg": "iou_threshold",
530
+ "semantic": "iou_threshold",
531
+ "buffer": { "type": "read-only-storage" },
532
+ "elementType": "f32",
533
+ "length": 1
534
+ },
535
+ {
536
+ "name": "score_threshold",
537
+ "arg": "score_threshold",
538
+ "semantic": "score_threshold",
539
+ "buffer": { "type": "read-only-storage" },
540
+ "elementType": "f32",
541
+ "length": 1
542
+ },
543
+ {
544
+ "name": "selected_indices",
545
+ "arg": "selected_indices",
546
+ "semantic": "selected_indices",
547
+ "buffer": { "type": "storage" },
548
+ "elementType": "u32"
549
+ },
550
+ {
551
+ "name": "params",
552
+ "semantic": "kernel.params",
553
+ "buffer": { "type": "uniform" },
554
+ "struct": {
555
+ "name": "Params",
556
+ "fields": [
557
+ { "name": "batches", "type": "u32", "value": "dim(shapes.boxes, 0)" },
558
+ { "name": "boxesPerBatch", "type": "u32", "value": "dim(shapes.boxes, 1)" },
559
+ { "name": "classes", "type": "u32", "value": "dim(shapes.scores, 1)" },
560
+ { "name": "capacity", "type": "u32", "value": "dim(shapes.selected_indices, 0)" },
561
+ { "name": "centerPointBox", "type": "u32", "value": "attrs.center_point_box" }
562
+ ]
563
+ }
564
+ }
565
+ ]
566
+ },
567
+ "variants": [
568
+ {
569
+ "id": "groups_parallel",
570
+ "priority": 25,
571
+ "when": ["validLayout", "present.max_output_boxes_per_class", "present.iou_threshold", "present.score_threshold", "dim(shapes.selected_indices, 0) > 0", "dim(shapes.boxes, 1) > 0", "parallelDispatchFits", "parallelScratchFits"],
572
+ "intermediates": [
573
+ { "id": "suppressed", "dtype": "uint32", "shape": "[numGroups * dim(shapes.boxes, 1)]" },
574
+ { "id": "groupSel", "dtype": "uint32", "shape": "[numGroups * maxKeep]" },
575
+ { "id": "groupCount", "dtype": "uint32", "shape": "[numGroups]" }
576
+ ],
577
+ "passes": [
578
+ {
579
+ "id": "select",
580
+ "name": "NonMaxSuppression.GroupSelect",
581
+ "source": {
582
+ "shader": "nms-groups-select.wgsl.jinja",
583
+ "inputs": {
584
+ "maxKeep": "maxKeep",
585
+ "useSubgroups": "device.features.has(\"subgroups\") and device.wgslLanguageFeatures.has(\"subgroup_id\")"
586
+ }
587
+ },
588
+ "subgroupCollectivesWidth": "portable",
589
+ "bindings": "parallelSelect",
590
+ "dispatch": { "x": "numGroups" }
591
+ },
592
+ {
593
+ "id": "compact",
594
+ "name": "NonMaxSuppression.GroupCompact",
595
+ "source": { "shader": "nms-groups-compact.wgsl.jinja", "inputs": { "maxKeep": "maxKeep" } },
596
+ "bindings": "parallelCompact",
597
+ "dispatch": { "x": 1 }
598
+ }
599
+ ]
600
+ },
601
+ {
602
+ "id": "serial_defaults",
603
+ "when": ["validLayout", "not present.max_output_boxes_per_class", "not present.iou_threshold", "not present.score_threshold"],
604
+ "passes": [
605
+ {
606
+ "id": "main",
607
+ "name": "NonMaxSuppression",
608
+ "source": {
609
+ "shader": "non-max-suppression.wgsl.jinja",
610
+ "inputs": { "hasMaxOutput": false, "hasIouThreshold": false, "hasScoreThreshold": false }
611
+ },
612
+ "bindings": "serialDefaults",
613
+ "dispatch": { "x": 1 }
614
+ }
615
+ ]
616
+ },
617
+ {
618
+ "id": "serial_max",
619
+ "when": ["validLayout", "present.max_output_boxes_per_class", "not present.iou_threshold", "not present.score_threshold"],
620
+ "passes": [
621
+ {
622
+ "id": "main",
623
+ "name": "NonMaxSuppression",
624
+ "source": {
625
+ "shader": "non-max-suppression.wgsl.jinja",
626
+ "inputs": { "hasMaxOutput": true, "hasIouThreshold": false, "hasScoreThreshold": false }
627
+ },
628
+ "bindings": "serialMax",
629
+ "dispatch": { "x": 1 }
630
+ }
631
+ ]
632
+ },
633
+ {
634
+ "id": "serial_iou",
635
+ "when": ["validLayout", "not present.max_output_boxes_per_class", "present.iou_threshold", "not present.score_threshold"],
636
+ "passes": [
637
+ {
638
+ "id": "main",
639
+ "name": "NonMaxSuppression",
640
+ "source": {
641
+ "shader": "non-max-suppression.wgsl.jinja",
642
+ "inputs": { "hasMaxOutput": false, "hasIouThreshold": true, "hasScoreThreshold": false }
643
+ },
644
+ "bindings": "serialIou",
645
+ "dispatch": { "x": 1 }
646
+ }
647
+ ]
648
+ },
649
+ {
650
+ "id": "serial_score",
651
+ "when": ["validLayout", "not present.max_output_boxes_per_class", "not present.iou_threshold", "present.score_threshold"],
652
+ "passes": [
653
+ {
654
+ "id": "main",
655
+ "name": "NonMaxSuppression",
656
+ "source": {
657
+ "shader": "non-max-suppression.wgsl.jinja",
658
+ "inputs": { "hasMaxOutput": false, "hasIouThreshold": false, "hasScoreThreshold": true }
659
+ },
660
+ "bindings": "serialScore",
661
+ "dispatch": { "x": 1 }
662
+ }
663
+ ]
664
+ },
665
+ {
666
+ "id": "serial_max_iou",
667
+ "when": ["validLayout", "present.max_output_boxes_per_class", "present.iou_threshold", "not present.score_threshold"],
668
+ "passes": [
669
+ {
670
+ "id": "main",
671
+ "name": "NonMaxSuppression",
672
+ "source": {
673
+ "shader": "non-max-suppression.wgsl.jinja",
674
+ "inputs": { "hasMaxOutput": true, "hasIouThreshold": true, "hasScoreThreshold": false }
675
+ },
676
+ "bindings": "serialMaxIou",
677
+ "dispatch": { "x": 1 }
678
+ }
679
+ ]
680
+ },
681
+ {
682
+ "id": "serial_max_score",
683
+ "when": ["validLayout", "present.max_output_boxes_per_class", "not present.iou_threshold", "present.score_threshold"],
684
+ "passes": [
685
+ {
686
+ "id": "main",
687
+ "name": "NonMaxSuppression",
688
+ "source": {
689
+ "shader": "non-max-suppression.wgsl.jinja",
690
+ "inputs": { "hasMaxOutput": true, "hasIouThreshold": false, "hasScoreThreshold": true }
691
+ },
692
+ "bindings": "serialMaxScore",
693
+ "dispatch": { "x": 1 }
694
+ }
695
+ ]
696
+ },
697
+ {
698
+ "id": "serial_iou_score",
699
+ "when": ["validLayout", "not present.max_output_boxes_per_class", "present.iou_threshold", "present.score_threshold"],
700
+ "passes": [
701
+ {
702
+ "id": "main",
703
+ "name": "NonMaxSuppression",
704
+ "source": {
705
+ "shader": "non-max-suppression.wgsl.jinja",
706
+ "inputs": { "hasMaxOutput": false, "hasIouThreshold": true, "hasScoreThreshold": true }
707
+ },
708
+ "bindings": "serialIouScore",
709
+ "dispatch": { "x": 1 }
710
+ }
711
+ ]
712
+ },
713
+ {
714
+ "id": "serial_all_inputs",
715
+ "when": ["validLayout", "present.max_output_boxes_per_class", "present.iou_threshold", "present.score_threshold"],
716
+ "passes": [
717
+ {
718
+ "id": "main",
719
+ "name": "NonMaxSuppression",
720
+ "source": {
721
+ "shader": "non-max-suppression.wgsl.jinja",
722
+ "inputs": { "hasMaxOutput": true, "hasIouThreshold": true, "hasScoreThreshold": true }
723
+ },
724
+ "bindings": "serialAll",
725
+ "dispatch": { "x": 1 }
726
+ }
727
+ ]
728
+ }
729
+ ]
730
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ai.onnx.NonMaxSuppression",
3
+ "id": "_ai_onnx_nonmaxsuppression_webgpu_d44f6ed",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "QfNxa5siC+dAWXaBHejTokSn3gllrgjOelQ0UNQlxEE=",
11
+ "manifest.json": "vZuoGvM3nYg4zijLh8n62me7YOGuyRElAeZQelssago=",
12
+ "nms-groups-compact.wgsl.jinja": "4z2+LMPnLoFRYfgH4bMN9D9eQw/besrivDzeM81JI5c=",
13
+ "nms-groups-select.wgsl.jinja": "OKea1RdhUe0+cDFuFGIrx32rOxnRbUxgDJA+0FHIHt8=",
14
+ "non-max-suppression.wgsl.jinja": "D4xiNb1cSvy5W1TjKi3jp4zA/H6besnr0zfsh3OAk+Q=",
15
+ "test.json": "ehZ2AurQHFMhpSAmDim7zkCfdJL/IdB6VaCVCakwAMY="
16
+ }
17
+ },
18
+ "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
19
+ "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.NonMaxSuppression" }
20
+ }
build/webgpu/nms-groups-compact.wgsl.jinja ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Pass 2 of group-parallel NonMaxSuppression. One workgroup walks per-group
2
+ // selections in (batch, class)-major order and compacts them into the final
3
+ // [num_selected, 3] tensor. The running offset is the sum of prior group counts,
4
+ // whose exact data-dependent shape is supplied by the caller. Selections within
5
+ // a group are already in score-descending greedy order. Defensive bounds and
6
+ // initialization keep an invalid allocation from producing out-of-bounds writes.
7
+ {{ env.wgsl.resourceDeclarations }}
8
+
9
+ const WG: u32 = {{ workgroupSize }}u;
10
+ const MAX_KEEP: u32 = {{ source.maxKeep }}u;
11
+
12
+ var<workgroup> wgTotal: u32;
13
+
14
+ @compute @workgroup_size(WG, 1, 1)
15
+ fn main(@builtin(local_invocation_id) lid: vec3<u32>) {
16
+ let tid = lid.x;
17
+ let numGroups = params.batches * params.classes;
18
+
19
+ if (tid == 0u) {
20
+ var total = 0u;
21
+ for (var group = 0u; group < numGroups; group = group + 1u) {
22
+ let batch = group / params.classes;
23
+ let class_id = group % params.classes;
24
+ let selBase = group * MAX_KEEP;
25
+ let n = groupCount[group];
26
+ for (var k = 0u; k < n; k = k + 1u) {
27
+ if (total < params.capacity) {
28
+ let box = groupSel[selBase + k];
29
+ let row = total * 3u;
30
+ selected_indices[row] = batch;
31
+ selected_indices[row + 1u] = class_id;
32
+ selected_indices[row + 2u] = box;
33
+ }
34
+ total = total + 1u;
35
+ }
36
+ }
37
+ wgTotal = total;
38
+ }
39
+ let total = workgroupUniformLoad(&wgTotal);
40
+
41
+ // Initialize any defensive tail rows to (0,0,0).
42
+ let written = min(total, params.capacity);
43
+ for (var row = written + tid; row < params.capacity; row = row + WG) {
44
+ let base = row * 3u;
45
+ selected_indices[base] = 0u;
46
+ selected_indices[base + 1u] = 0u;
47
+ selected_indices[base + 2u] = 0u;
48
+ }
49
+ }
build/webgpu/nms-groups-select.wgsl.jinja ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Pass 1 of group-parallel NonMaxSuppression. Independent (batch, class) groups
2
+ // do not interact, so each gets one workgroup. Within a group, the greedy step
3
+ // and each suppression sweep are parallelized across the workgroup. The sweep
4
+ // also finds the next surviving score maximum. Ties choose the lower box index,
5
+ // and the chosen corners are shared for the iteration.
6
+ //
7
+ // Per-group scratch stores sticky suppression flags, score-descending selected
8
+ // indices, and the selection count. Every group reaches its natural limit;
9
+ // pass 2 applies batch/class-major ordering into the exact output allocation.
10
+ {% if source.useSubgroups %}
11
+ enable subgroups;
12
+ {% endif %}
13
+ {{ env.wgsl.resourceDeclarations }}
14
+
15
+ const WG: u32 = {{ workgroupSize }}u;
16
+ const MAX_KEEP: u32 = {{ source.maxKeep }}u;
17
+ const INVALID: u32 = 0xffffffffu;
18
+
19
+ fn box_offset(batch: u32, box: u32) -> u32 {
20
+ return (batch * params.boxesPerBatch + box) * 4u;
21
+ }
22
+
23
+ fn score_at(batch: u32, class_id: u32, box: u32) -> f32 {
24
+ return scores[(batch * params.classes + class_id) * params.boxesPerBatch + box];
25
+ }
26
+
27
+ fn box_corners(batch: u32, box: u32) -> vec4<f32> {
28
+ let offset = box_offset(batch, box);
29
+ let v0 = boxes[offset];
30
+ let v1 = boxes[offset + 1u];
31
+ let v2 = boxes[offset + 2u];
32
+ let v3 = boxes[offset + 3u];
33
+ if (params.centerPointBox != 0u) {
34
+ let x_center = v0;
35
+ let y_center = v1;
36
+ let half_w = v2 * 0.5;
37
+ let half_h = v3 * 0.5;
38
+ return vec4<f32>(y_center - half_h, x_center - half_w, y_center + half_h, x_center + half_w);
39
+ }
40
+ return vec4<f32>(v0, v1, v2, v3);
41
+ }
42
+
43
+ fn iou_with_box(batch: u32, a: u32, b_box: vec4<f32>) -> f32 {
44
+ let a_box = box_corners(batch, a);
45
+ let ay1_raw = a_box.x;
46
+ let ax1_raw = a_box.y;
47
+ let ay2_raw = a_box.z;
48
+ let ax2_raw = a_box.w;
49
+ let by1_raw = b_box.x;
50
+ let bx1_raw = b_box.y;
51
+ let by2_raw = b_box.z;
52
+ let bx2_raw = b_box.w;
53
+ let ay1 = min(ay1_raw, ay2_raw);
54
+ let ax1 = min(ax1_raw, ax2_raw);
55
+ let ay2 = max(ay1_raw, ay2_raw);
56
+ let ax2 = max(ax1_raw, ax2_raw);
57
+ let by1 = min(by1_raw, by2_raw);
58
+ let bx1 = min(bx1_raw, bx2_raw);
59
+ let by2 = max(by1_raw, by2_raw);
60
+ let bx2 = max(bx1_raw, bx2_raw);
61
+ let inter_y1 = max(ay1, by1);
62
+ let inter_x1 = max(ax1, bx1);
63
+ let inter_y2 = min(ay2, by2);
64
+ let inter_x2 = min(ax2, bx2);
65
+ let inter = max(0.0, inter_y2 - inter_y1) * max(0.0, inter_x2 - inter_x1);
66
+ let area_a = max(0.0, ay2 - ay1) * max(0.0, ax2 - ax1);
67
+ let area_b = max(0.0, by2 - by1) * max(0.0, bx2 - bx1);
68
+ let denom = area_a + area_b - inter;
69
+ if (denom <= 0.0) { return 0.0; }
70
+ return inter / denom;
71
+ }
72
+
73
+
74
+ var<workgroup> wgScore: array<f32, WG>;
75
+ var<workgroup> wgBox: array<u32, WG>;
76
+ var<workgroup> wgChosen: u32;
77
+ var<workgroup> wgChosenCorners: vec4<f32>;
78
+
79
+ fn reduce_best(
80
+ tid: u32,
81
+ {% if source.useSubgroups %}
82
+ sg_lane: u32,
83
+ sg_id: u32,
84
+ num_sg: u32,
85
+ sg_size: u32,
86
+ {% endif %}
87
+ local_score: f32,
88
+ local_box: u32
89
+ ) {
90
+ {% if source.useSubgroups %}
91
+ // Reduce within each hardware subgroup, then reduce the subgroup winners.
92
+ // Each lane folds a strided set of winners first, so this is independent of
93
+ // subgroup width and also covers devices where num_sg > sg_size.
94
+ let lane_score = select(-3.4028234663852886e38, local_score, local_box != INVALID);
95
+ let sg_score = subgroupMax(lane_score);
96
+ let lane_box = select(INVALID, local_box, local_box != INVALID && local_score == sg_score);
97
+ let sg_box = subgroupMin(lane_box);
98
+ if (sg_lane == 0u) {
99
+ wgScore[sg_id] = sg_score;
100
+ wgBox[sg_id] = sg_box;
101
+ }
102
+ workgroupBarrier();
103
+
104
+ var block_score = -3.4028234663852886e38;
105
+ var block_box = INVALID;
106
+ for (var i = sg_lane; i < num_sg; i += sg_size) {
107
+ let candidate_score = wgScore[i];
108
+ let candidate_box = wgBox[i];
109
+ if (candidate_box != INVALID &&
110
+ (block_box == INVALID || candidate_score > block_score ||
111
+ (candidate_score == block_score && candidate_box < block_box))) {
112
+ block_score = candidate_score;
113
+ block_box = candidate_box;
114
+ }
115
+ }
116
+ let final_score = subgroupMax(block_score);
117
+ let final_box = subgroupMin(select(INVALID, block_box, block_score == final_score));
118
+ if (tid == 0u) {
119
+ wgChosen = final_box;
120
+ }
121
+ workgroupBarrier();
122
+ {% else %}
123
+ wgScore[tid] = local_score;
124
+ wgBox[tid] = local_box;
125
+ var stride = WG / 2u;
126
+ while (stride > 0u) {
127
+ workgroupBarrier();
128
+ if (tid < stride) {
129
+ let o_score = wgScore[tid + stride];
130
+ let o_box = wgBox[tid + stride];
131
+ let m_score = wgScore[tid];
132
+ let m_box = wgBox[tid];
133
+ if (o_box != INVALID && (m_box == INVALID || o_score > m_score || (o_score == m_score && o_box < m_box))) {
134
+ wgScore[tid] = o_score;
135
+ wgBox[tid] = o_box;
136
+ }
137
+ }
138
+ stride = stride >> 1u;
139
+ }
140
+ workgroupBarrier();
141
+ if (tid == 0u) {
142
+ wgChosen = wgBox[0];
143
+ }
144
+ workgroupBarrier();
145
+ {% endif %}
146
+ }
147
+
148
+ @compute @workgroup_size(WG, 1, 1)
149
+ fn main(
150
+ @builtin(workgroup_id) wid: vec3<u32>,
151
+ @builtin(local_invocation_id) lid: vec3<u32>{% if source.useSubgroups %},
152
+ @builtin(subgroup_invocation_id) sg_lane: u32,
153
+ @builtin(subgroup_id) sg_id: u32,
154
+ @builtin(num_subgroups) num_sg: u32,
155
+ @builtin(subgroup_size) sg_size: u32{% endif %}
156
+ ) {
157
+ let tid = lid.x;
158
+ let group = wid.x;
159
+ // group index decodes to (batch, class) in (batch, class)-major order, exactly
160
+ // matching the serial iteration order.
161
+ let batch = group / params.classes;
162
+ let class_id = group % params.classes;
163
+ let suppBase = group * params.boxesPerBatch;
164
+ let selBase = group * MAX_KEEP;
165
+ let max_per_class = min(max_output_boxes_per_class[0], MAX_KEEP);
166
+ let iou_threshold_value = iou_threshold[0];
167
+ let score_threshold_value = score_threshold[0];
168
+
169
+ var best_box = INVALID;
170
+ var best_score = 0.0;
171
+ for (var box = tid; box < params.boxesPerBatch; box = box + WG) {
172
+ suppressed[suppBase + box] = 0u;
173
+ let score = score_at(batch, class_id, box);
174
+ if (score <= score_threshold_value) { continue; }
175
+ if (best_box == INVALID || score > best_score || (score == best_score && box < best_box)) {
176
+ best_score = score;
177
+ best_box = box;
178
+ }
179
+ }
180
+
181
+ var selected_for_class = 0u;
182
+ reduce_best(tid, {% if source.useSubgroups %}sg_lane, sg_id, num_sg, sg_size, {% endif %}best_score, best_box);
183
+ var chosen = workgroupUniformLoad(&wgChosen);
184
+ while (chosen != INVALID && selected_for_class < max_per_class) {
185
+ if (tid == 0u) {
186
+ groupSel[selBase + selected_for_class] = chosen;
187
+ }
188
+ selected_for_class = selected_for_class + 1u;
189
+ // The last retained box cannot affect the requested output; avoid one
190
+ // complete IoU sweep and reduction when maxKeep is reached.
191
+ if (selected_for_class >= max_per_class) { break; }
192
+ if (tid == 0u) {
193
+ wgChosenCorners = box_corners(batch, chosen);
194
+ }
195
+ let chosen_corners = workgroupUniformLoad(&wgChosenCorners);
196
+
197
+ best_box = INVALID;
198
+ best_score = score_threshold_value;
199
+ for (var box = tid; box < params.boxesPerBatch; box = box + WG) {
200
+ if (box == chosen) {
201
+ suppressed[suppBase + box] = 1u;
202
+ continue;
203
+ }
204
+ if (suppressed[suppBase + box] != 0u) { continue; }
205
+ if (iou_with_box(batch, box, chosen_corners) > iou_threshold_value) {
206
+ suppressed[suppBase + box] = 1u;
207
+ continue;
208
+ }
209
+ let score = score_at(batch, class_id, box);
210
+ if (score <= score_threshold_value) { continue; }
211
+ if (best_box == INVALID || score > best_score || (score == best_score && box < best_box)) {
212
+ best_score = score;
213
+ best_box = box;
214
+ }
215
+ }
216
+ reduce_best(tid, {% if source.useSubgroups %}sg_lane, sg_id, num_sg, sg_size, {% endif %}best_score, best_box);
217
+ chosen = workgroupUniformLoad(&wgChosen);
218
+ }
219
+
220
+ if (tid == 0u) {
221
+ groupCount[group] = selected_for_class;
222
+ }
223
+ }
build/webgpu/non-max-suppression.wgsl.jinja ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {{ env.wgsl.resourceDeclarations }}
2
+
3
+ fn box_offset(batch: u32, box: u32) -> u32 {
4
+ return (batch * params.boxesPerBatch + box) * 4u;
5
+ }
6
+
7
+ fn score_at(batch: u32, class_id: u32, box: u32) -> f32 {
8
+ return scores[(batch * params.classes + class_id) * params.boxesPerBatch + box];
9
+ }
10
+
11
+ fn box_corners(batch: u32, box: u32) -> vec4<f32> {
12
+ let offset = box_offset(batch, box);
13
+ let v0 = boxes[offset];
14
+ let v1 = boxes[offset + 1u];
15
+ let v2 = boxes[offset + 2u];
16
+ let v3 = boxes[offset + 3u];
17
+ if (params.centerPointBox != 0u) {
18
+ let x_center = v0;
19
+ let y_center = v1;
20
+ let half_w = v2 * 0.5;
21
+ let half_h = v3 * 0.5;
22
+ return vec4<f32>(y_center - half_h, x_center - half_w, y_center + half_h, x_center + half_w);
23
+ }
24
+ return vec4<f32>(v0, v1, v2, v3);
25
+ }
26
+
27
+ fn iou_with_box(batch: u32, a: u32, b_box: vec4<f32>) -> f32 {
28
+ let a_box = box_corners(batch, a);
29
+ let ay1_raw = a_box.x;
30
+ let ax1_raw = a_box.y;
31
+ let ay2_raw = a_box.z;
32
+ let ax2_raw = a_box.w;
33
+ let by1_raw = b_box.x;
34
+ let bx1_raw = b_box.y;
35
+ let by2_raw = b_box.z;
36
+ let bx2_raw = b_box.w;
37
+ let ay1 = min(ay1_raw, ay2_raw);
38
+ let ax1 = min(ax1_raw, ax2_raw);
39
+ let ay2 = max(ay1_raw, ay2_raw);
40
+ let ax2 = max(ax1_raw, ax2_raw);
41
+ let by1 = min(by1_raw, by2_raw);
42
+ let bx1 = min(bx1_raw, bx2_raw);
43
+ let by2 = max(by1_raw, by2_raw);
44
+ let bx2 = max(bx1_raw, bx2_raw);
45
+ let inter_y1 = max(ay1, by1);
46
+ let inter_x1 = max(ax1, bx1);
47
+ let inter_y2 = min(ay2, by2);
48
+ let inter_x2 = min(ax2, bx2);
49
+ let inter = max(0.0, inter_y2 - inter_y1) * max(0.0, inter_x2 - inter_x1);
50
+ let area_a = max(0.0, ay2 - ay1) * max(0.0, ax2 - ax1);
51
+ let area_b = max(0.0, by2 - by1) * max(0.0, bx2 - bx1);
52
+ let denom = area_a + area_b - inter;
53
+ if (denom <= 0.0) { return 0.0; }
54
+ return inter / denom;
55
+ }
56
+
57
+ fn iou(batch: u32, a: u32, b: u32) -> f32 {
58
+ return iou_with_box(batch, a, box_corners(batch, b));
59
+ }
60
+
61
+
62
+ fn selected_box(row: u32) -> u32 {
63
+ return selected_indices[row * 3u + 2u];
64
+ }
65
+
66
+ fn write_selected(row: u32, batch: u32, class_id: u32, box: u32) {
67
+ let base = row * 3u;
68
+ selected_indices[base] = batch;
69
+ selected_indices[base + 1u] = class_id;
70
+ selected_indices[base + 2u] = box;
71
+ }
72
+
73
+ @compute @workgroup_size(1)
74
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
75
+ if (gid.x != 0u) { return; }
76
+ {% if source.hasMaxOutput %}
77
+ let max_per_class = max_output_boxes_per_class[0];
78
+ {% else %}
79
+ let max_per_class = 0u;
80
+ {% endif %}
81
+ {% if source.hasIouThreshold %}
82
+ let iou_threshold_value = iou_threshold[0];
83
+ {% else %}
84
+ let iou_threshold_value = 0.0;
85
+ {% endif %}
86
+ var total = 0u;
87
+ for (var batch = 0u; batch < params.batches; batch = batch + 1u) {
88
+ for (var class_id = 0u; class_id < params.classes; class_id = class_id + 1u) {
89
+ let class_start = total;
90
+ var selected_for_class = 0u;
91
+ loop {
92
+ if (selected_for_class >= max_per_class) { break; }
93
+ var best_box = 0xffffffffu;
94
+ var best_score = 0.0;
95
+ for (var box = 0u; box < params.boxesPerBatch; box = box + 1u) {
96
+ let score = score_at(batch, class_id, box);
97
+ {% if source.hasScoreThreshold %}
98
+ if (score <= score_threshold[0]) { continue; }
99
+ {% endif %}
100
+ var suppressed = false;
101
+ for (var prev = 0u; prev < selected_for_class; prev = prev + 1u) {
102
+ let sel = selected_box(class_start + prev);
103
+ // An already-selected box must never be re-selected. For normal boxes
104
+ // iou(box, box) == 1 > threshold handles this, but a zero-area box has
105
+ // iou 0/0 -> 0 (the denom<=0 guard), so it would be re-picked every
106
+ // iteration; skip it explicitly by index (no-op for normal boxes).
107
+ if (box == sel || iou(batch, box, sel) > iou_threshold_value) {
108
+ suppressed = true;
109
+ }
110
+ }
111
+ if (!suppressed && (best_box == 0xffffffffu || score > best_score || (score == best_score && box < best_box))) {
112
+ best_score = score;
113
+ best_box = box;
114
+ }
115
+ }
116
+ if (best_box == 0xffffffffu) { break; }
117
+ write_selected(total, batch, class_id, best_box);
118
+ total = total + 1u;
119
+ selected_for_class = selected_for_class + 1u;
120
+ }
121
+ }
122
+ }
123
+ for (var row = total; row < params.capacity; row = row + 1u) {
124
+ write_selected(row, 0u, 0u, 0u);
125
+ }
126
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,1179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.NonMaxSuppression",
3
+ "fixtureArrays": {
4
+ "nms_six_boxes": [0, 0, 1, 1, 0, 0.1, 1, 1.1, 0, -0.1, 1, 0.9, 0, 10, 1, 11, 0, 10.1, 1, 11.1, 0, 100, 1, 101],
5
+ "identical_boxes_tie_lower_index_ort_input_boxes": [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1],
6
+ "two_batches_one_class_ort_input_boxes": [0, 0, 1, 1, 0, 0.1, 1, 1.1, 0, -0.1, 1, 0.9, 0, 10, 1, 11, 0, 10.1, 1, 11.1, 0, 100, 1, 101, 0, 0, 1, 1, 0, 0.1, 1, 1.1, 0, -0.1, 1, 0.9, 0, 10, 1, 11, 0, 10.1, 1, 11.1, 0, 100, 1, 101],
7
+ "two_batches_two_classes_ort_input_boxes": [0, 0, 0.3, 0.3, 0, 0, 0.4, 0.4, 0, 0, 0.5, 0.5, 0.5, 0.5, 0.9, 0.9, 0.5, 0.5, 1, 1, 0, 0, 0.3, 0.3, 0, 0, 0.4, 0.4, 0.5, 0.5, 0.95, 0.95, 0.5, 0.5, 0.96, 0.96, 0.5, 0.5, 1, 1],
8
+ "two_batches_two_classes_ort_input_scores": [0.1, 0.2, 0.6, 0.3, 0.9, 0.1, 0.2, 0.6, 0.3, 0.9, 0.1, 0.2, 0.6, 0.3, 0.9, 0.1, 0.2, 0.6, 0.3, 0.9],
9
+ "flipped_coordinates_ort_input_boxes": [1, 1, 0, 0, 0, 0.1, 1, 1.1, 0, 0.9, 1, -0.1, 0, 10, 1, 11, 1, 10.1, 0, 11.1, 1, 101, 0, 100],
10
+ "onnx_backend_nonmaxsuppression_limit_output_size_input_boxes": [0, 0, 1, 1, 0, 0.10000000149011612, 1, 1.100000023841858, 0, -0.10000000149011612, 1, 0.8999999761581421, 0, 10, 1, 11, 0, 10.100000381469727, 1, 11.100000381469727, 0, 100, 1, 101]
11
+ },
12
+ "cases": [
13
+ {
14
+ "name": "single_class_suppresses_overlap",
15
+ "inputs": {
16
+ "boxes": {
17
+ "dtype": "float32",
18
+ "shape": [1, 3, 4],
19
+ "data": { "kind": "values", "values": [0.0, 0.0, 10.0, 10.0, 1.0, 1.0, 11.0, 11.0, 20.0, 20.0, 30.0, 30.0] }
20
+ },
21
+ "scores": { "dtype": "float32", "shape": [1, 1, 3], "data": { "kind": "values", "values": [0.9, 0.8, 0.7] } },
22
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
23
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
24
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
25
+ },
26
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } }
27
+ },
28
+ {
29
+ "name": "f32_subnormal_score_above_zero_threshold_gpu_gap",
30
+ "skipGpu": {
31
+ "category": "permanent",
32
+ "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes denormals to zero in the ALU; the subnormal score is flushed when loaded for the > score_threshold comparison and the ordered best-score selection. Permanent FTZ limitation."
33
+ },
34
+ "provenance": {
35
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
36
+ "test": "NonMaxSuppressionOpTest.WithScoreThreshold",
37
+ "notes": "Score threshold filtering is strict; a finite positive subnormal score is greater than a zero threshold and should produce one selected index."
38
+ },
39
+ "inputs": {
40
+ "boxes": {
41
+ "dtype": "float32",
42
+ "shape": [1, 2, 4],
43
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0] }
44
+ },
45
+ "scores": { "dtype": "float32", "shape": [1, 1, 2], "data": { "kind": "values", "values": [0.0, 1e-40] } },
46
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [1] } },
47
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
48
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
49
+ },
50
+ "outputs": {
51
+ "selected_indices": {
52
+ "dtype": "uint32",
53
+ "shape": [1, 3],
54
+ "tolerance": 0,
55
+ "data": { "kind": "values", "values": [0, 0, 1] }
56
+ }
57
+ }
58
+ },
59
+ {
60
+ "name": "two_classes_threshold",
61
+ "inputs": {
62
+ "boxes": {
63
+ "dtype": "float32",
64
+ "shape": [1, 3, 4],
65
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 2.0, 2.0, 4.0, 4.0, 5.0, 5.0] }
66
+ },
67
+ "scores": {
68
+ "dtype": "float32",
69
+ "shape": [1, 2, 3],
70
+ "data": { "kind": "values", "values": [0.4, 0.9, 0.7, 0.6, 0.2, 0.8] }
71
+ },
72
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [1] } },
73
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.3] } },
74
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } }
75
+ },
76
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } }
77
+ },
78
+ {
79
+ "name": "ort_two_classes_full_capacity",
80
+ "provenance": {
81
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
82
+ "test": "NonMaxSuppressionOpTest.TwoClasses",
83
+ "notes": "The fixture supplies the exact data-dependent ONNX output shape."
84
+ },
85
+ "inputs": {
86
+ "boxes": {
87
+ "dtype": "float32",
88
+ "shape": [1, 6, 4],
89
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
90
+ },
91
+ "scores": {
92
+ "dtype": "float32",
93
+ "shape": [1, 2, 6],
94
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
95
+ },
96
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [6] } },
97
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
98
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
99
+ },
100
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [6, 3] } }
101
+ },
102
+ {
103
+ "name": "ort_with_score_threshold",
104
+ "provenance": {
105
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
106
+ "test": "NonMaxSuppressionOpTest.WithScoreThreshold",
107
+ "notes": "The fixture supplies the exact data-dependent ONNX output shape."
108
+ },
109
+ "inputs": {
110
+ "boxes": {
111
+ "dtype": "float32",
112
+ "shape": [1, 6, 4],
113
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
114
+ },
115
+ "scores": {
116
+ "dtype": "float32",
117
+ "shape": [1, 1, 6],
118
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
119
+ },
120
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
121
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
122
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.4] } }
123
+ },
124
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } }
125
+ },
126
+ {
127
+ "name": "single_box_capacity_ort",
128
+ "provenance": {
129
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
130
+ "test": "NonMaxSuppressionOpTest.SelectSingleBox"
131
+ },
132
+ "inputs": {
133
+ "boxes": {
134
+ "dtype": "float32",
135
+ "shape": [1, 1, 4],
136
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0] }
137
+ },
138
+ "scores": { "dtype": "float32", "shape": [1, 1, 1], "data": { "kind": "values", "values": [0.9] } },
139
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
140
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
141
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
142
+ },
143
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [1, 3] } }
144
+ },
145
+ {
146
+ "name": "identical_boxes_tie_lower_index_ort",
147
+ "provenance": {
148
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
149
+ "test": "NonMaxSuppressionOpTest.SelectFromIdenticalBoxes"
150
+ },
151
+ "inputs": {
152
+ "boxes": {
153
+ "dtype": "float32",
154
+ "shape": [1, 10, 4],
155
+ "data": {
156
+ "kind": "values",
157
+ "values": { "$ref": "#/fixtureArrays/identical_boxes_tie_lower_index_ort_input_boxes" }
158
+ }
159
+ },
160
+ "scores": {
161
+ "dtype": "float32",
162
+ "shape": [1, 1, 10],
163
+ "data": { "kind": "values", "values": [0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9] }
164
+ },
165
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
166
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
167
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
168
+ },
169
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [1, 3] } }
170
+ },
171
+ {
172
+ "name": "select_thirty_capacity_ort",
173
+ "provenance": {
174
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
175
+ "test": "NonMaxSuppressionOpTest.SelectThirty"
176
+ },
177
+ "inputs": {
178
+ "boxes": {
179
+ "dtype": "float32",
180
+ "shape": [1, 6, 4],
181
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
182
+ },
183
+ "scores": {
184
+ "dtype": "float32",
185
+ "shape": [1, 1, 6],
186
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
187
+ },
188
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [30] } },
189
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
190
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
191
+ },
192
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } }
193
+ },
194
+ {
195
+ "name": "with_iou_threshold_ort",
196
+ "provenance": {
197
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
198
+ "test": "NonMaxSuppressionOpTest.WithIOUThreshold"
199
+ },
200
+ "inputs": {
201
+ "boxes": {
202
+ "dtype": "float32",
203
+ "shape": [1, 6, 4],
204
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
205
+ },
206
+ "scores": {
207
+ "dtype": "float32",
208
+ "shape": [1, 1, 6],
209
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
210
+ },
211
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
212
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
213
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
214
+ },
215
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } }
216
+ },
217
+ {
218
+ "name": "without_score_threshold_ort",
219
+ "provenance": {
220
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
221
+ "test": "NonMaxSuppressionOpTest.WithoutScoreThreshold"
222
+ },
223
+ "inputs": {
224
+ "boxes": {
225
+ "dtype": "float32",
226
+ "shape": [1, 6, 4],
227
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
228
+ },
229
+ "scores": {
230
+ "dtype": "float32",
231
+ "shape": [1, 1, 6],
232
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
233
+ },
234
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
235
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } }
236
+ },
237
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } }
238
+ },
239
+ {
240
+ "name": "select_two_capacity_ort",
241
+ "provenance": {
242
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
243
+ "test": "NonMaxSuppressionOpTest.SelectTwo"
244
+ },
245
+ "inputs": {
246
+ "boxes": {
247
+ "dtype": "float32",
248
+ "shape": [1, 6, 4],
249
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
250
+ },
251
+ "scores": {
252
+ "dtype": "float32",
253
+ "shape": [1, 1, 6],
254
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
255
+ },
256
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
257
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
258
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
259
+ },
260
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } }
261
+ },
262
+ {
263
+ "name": "two_batches_one_class_ort",
264
+ "provenance": {
265
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
266
+ "test": "NonMaxSuppressionOpTest.TwoBatches_OneClass"
267
+ },
268
+ "inputs": {
269
+ "boxes": {
270
+ "dtype": "float32",
271
+ "shape": [2, 6, 4],
272
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/two_batches_one_class_ort_input_boxes" } }
273
+ },
274
+ "scores": {
275
+ "dtype": "float32",
276
+ "shape": [2, 1, 6],
277
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
278
+ },
279
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
280
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
281
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
282
+ },
283
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [4, 3] } }
284
+ },
285
+ {
286
+ "name": "two_batches_two_classes_ort",
287
+ "provenance": {
288
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
289
+ "test": "NonMaxSuppressionOpTest.TwoBatches_TwoClasses"
290
+ },
291
+ "inputs": {
292
+ "boxes": {
293
+ "dtype": "float32",
294
+ "shape": [2, 5, 4],
295
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/two_batches_two_classes_ort_input_boxes" } }
296
+ },
297
+ "scores": {
298
+ "dtype": "float32",
299
+ "shape": [2, 2, 5],
300
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/two_batches_two_classes_ort_input_scores" } }
301
+ },
302
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
303
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.8] } },
304
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
305
+ },
306
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [8, 3] } }
307
+ },
308
+ {
309
+ "name": "score_threshold_zero_scores_ort",
310
+ "provenance": {
311
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
312
+ "test": "NonMaxSuppressionOpTest.WithScoreThresholdZeroScores"
313
+ },
314
+ "inputs": {
315
+ "boxes": {
316
+ "dtype": "float32",
317
+ "shape": [1, 6, 4],
318
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
319
+ },
320
+ "scores": {
321
+ "dtype": "float32",
322
+ "shape": [1, 1, 6],
323
+ "data": { "kind": "values", "values": [0.1, 0.0, 0.0, 0.3, 0.2, -5.0] }
324
+ },
325
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [6] } },
326
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
327
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-3.0] } }
328
+ },
329
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } }
330
+ },
331
+ {
332
+ "name": "flipped_coordinates_ort",
333
+ "provenance": {
334
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
335
+ "test": "NonMaxSuppressionOpTest.FlippedCoordinates"
336
+ },
337
+ "inputs": {
338
+ "boxes": {
339
+ "dtype": "float32",
340
+ "shape": [1, 6, 4],
341
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/flipped_coordinates_ort_input_boxes" } }
342
+ },
343
+ "scores": {
344
+ "dtype": "float32",
345
+ "shape": [1, 1, 6],
346
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
347
+ },
348
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
349
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
350
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
351
+ },
352
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } }
353
+ },
354
+ {
355
+ "name": "empty_input_capacity_ort",
356
+ "provenance": {
357
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
358
+ "test": "NonMaxSuppressionOpTest.EmptyInput"
359
+ },
360
+ "inputs": {
361
+ "boxes": { "dtype": "float32", "shape": [1, 0, 4], "data": { "kind": "values", "values": [] } },
362
+ "scores": { "dtype": "float32", "shape": [1, 1, 0], "data": { "kind": "values", "values": [] } },
363
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [30] } },
364
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
365
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
366
+ },
367
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [0, 3] } }
368
+ },
369
+ {
370
+ "name": "zero_max_output_per_class_ort",
371
+ "provenance": {
372
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
373
+ "test": "NonMaxSuppressionOpTest.ZeroMaxOutputPerClass"
374
+ },
375
+ "inputs": {
376
+ "boxes": {
377
+ "dtype": "float32",
378
+ "shape": [1, 6, 4],
379
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
380
+ },
381
+ "scores": {
382
+ "dtype": "float32",
383
+ "shape": [1, 1, 6],
384
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
385
+ },
386
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [0] } },
387
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
388
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.4] } }
389
+ },
390
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [0, 3] } }
391
+ },
392
+ {
393
+ "name": "onnx_backend_nonmaxsuppression_center_point_box_format",
394
+ "attrs": { "center_point_box": 1 },
395
+ "inputs": {
396
+ "boxes": {
397
+ "dtype": "float32",
398
+ "shape": [1, 6, 4],
399
+ "data": {
400
+ "kind": "values",
401
+ "values": [0.5, 0.5, 1.0, 1.0, 0.5, 0.6000000238418579, 1.0, 1.0, 0.5, 0.4000000059604645, 1.0, 1.0, 0.5, 10.5, 1.0, 1.0, 0.5, 10.600000381469727, 1.0, 1.0, 0.5, 100.5, 1.0, 1.0]
402
+ }
403
+ },
404
+ "scores": {
405
+ "dtype": "float32",
406
+ "shape": [1, 1, 6],
407
+ "data": {
408
+ "kind": "values",
409
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
410
+ }
411
+ },
412
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
413
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
414
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
415
+ },
416
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } },
417
+ "provenance": {
418
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_center_point_box_format",
419
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
420
+ }
421
+ },
422
+ {
423
+ "name": "onnx_backend_nonmaxsuppression_flipped_coordinates",
424
+ "attrs": { "center_point_box": 0 },
425
+ "inputs": {
426
+ "boxes": {
427
+ "dtype": "float32",
428
+ "shape": [1, 6, 4],
429
+ "data": {
430
+ "kind": "values",
431
+ "values": [1.0, 1.0, 0.0, 0.0, 0.0, 0.10000000149011612, 1.0, 1.100000023841858, 0.0, 0.8999999761581421, 1.0, -0.10000000149011612, 0.0, 10.0, 1.0, 11.0, 1.0, 10.100000381469727, 0.0, 11.100000381469727, 1.0, 101.0, 0.0, 100.0]
432
+ }
433
+ },
434
+ "scores": {
435
+ "dtype": "float32",
436
+ "shape": [1, 1, 6],
437
+ "data": {
438
+ "kind": "values",
439
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
440
+ }
441
+ },
442
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
443
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
444
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
445
+ },
446
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } },
447
+ "provenance": {
448
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_flipped_coordinates",
449
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
450
+ }
451
+ },
452
+ {
453
+ "name": "onnx_backend_nonmaxsuppression_identical_boxes",
454
+ "attrs": { "center_point_box": 0 },
455
+ "inputs": {
456
+ "boxes": {
457
+ "dtype": "float32",
458
+ "shape": [1, 10, 4],
459
+ "data": {
460
+ "kind": "values",
461
+ "values": { "$ref": "#/fixtureArrays/identical_boxes_tie_lower_index_ort_input_boxes" }
462
+ }
463
+ },
464
+ "scores": {
465
+ "dtype": "float32",
466
+ "shape": [1, 1, 10],
467
+ "data": {
468
+ "kind": "values",
469
+ "values": [0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 0.8999999761581421]
470
+ }
471
+ },
472
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
473
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
474
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
475
+ },
476
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [1, 3] } },
477
+ "provenance": {
478
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_identical_boxes",
479
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
480
+ }
481
+ },
482
+ {
483
+ "name": "onnx_backend_nonmaxsuppression_iou_threshold_boundary",
484
+ "attrs": { "center_point_box": 0 },
485
+ "inputs": {
486
+ "boxes": {
487
+ "dtype": "float32",
488
+ "shape": [1, 2, 4],
489
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 0.5, 0.5, 1.5, 1.5] }
490
+ },
491
+ "scores": {
492
+ "dtype": "float32",
493
+ "shape": [1, 1, 2],
494
+ "data": { "kind": "values", "values": [0.8999999761581421, 0.800000011920929] }
495
+ },
496
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
497
+ "iou_threshold": {
498
+ "dtype": "float32",
499
+ "shape": [],
500
+ "data": { "kind": "values", "values": [0.1428571492433548] }
501
+ },
502
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
503
+ },
504
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } },
505
+ "provenance": {
506
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_iou_threshold_boundary",
507
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
508
+ }
509
+ },
510
+ {
511
+ "name": "onnx_backend_nonmaxsuppression_limit_output_size",
512
+ "attrs": { "center_point_box": 0 },
513
+ "inputs": {
514
+ "boxes": {
515
+ "dtype": "float32",
516
+ "shape": [1, 6, 4],
517
+ "data": {
518
+ "kind": "values",
519
+ "values": { "$ref": "#/fixtureArrays/onnx_backend_nonmaxsuppression_limit_output_size_input_boxes" }
520
+ }
521
+ },
522
+ "scores": {
523
+ "dtype": "float32",
524
+ "shape": [1, 1, 6],
525
+ "data": {
526
+ "kind": "values",
527
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
528
+ }
529
+ },
530
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
531
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
532
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
533
+ },
534
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } },
535
+ "provenance": {
536
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_limit_output_size",
537
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
538
+ }
539
+ },
540
+ {
541
+ "name": "onnx_backend_nonmaxsuppression_single_box",
542
+ "attrs": { "center_point_box": 0 },
543
+ "inputs": {
544
+ "boxes": {
545
+ "dtype": "float32",
546
+ "shape": [1, 1, 4],
547
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0] }
548
+ },
549
+ "scores": {
550
+ "dtype": "float32",
551
+ "shape": [1, 1, 1],
552
+ "data": { "kind": "values", "values": [0.8999999761581421] }
553
+ },
554
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
555
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
556
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
557
+ },
558
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [1, 3] } },
559
+ "provenance": {
560
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_single_box",
561
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
562
+ }
563
+ },
564
+ {
565
+ "name": "onnx_backend_nonmaxsuppression_suppress_by_IOU",
566
+ "attrs": { "center_point_box": 0 },
567
+ "inputs": {
568
+ "boxes": {
569
+ "dtype": "float32",
570
+ "shape": [1, 6, 4],
571
+ "data": {
572
+ "kind": "values",
573
+ "values": { "$ref": "#/fixtureArrays/onnx_backend_nonmaxsuppression_limit_output_size_input_boxes" }
574
+ }
575
+ },
576
+ "scores": {
577
+ "dtype": "float32",
578
+ "shape": [1, 1, 6],
579
+ "data": {
580
+ "kind": "values",
581
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
582
+ }
583
+ },
584
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
585
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
586
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
587
+ },
588
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3] } },
589
+ "provenance": {
590
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_suppress_by_IOU",
591
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
592
+ }
593
+ },
594
+ {
595
+ "name": "onnx_backend_nonmaxsuppression_suppress_by_IOU_and_scores",
596
+ "attrs": { "center_point_box": 0 },
597
+ "inputs": {
598
+ "boxes": {
599
+ "dtype": "float32",
600
+ "shape": [1, 6, 4],
601
+ "data": {
602
+ "kind": "values",
603
+ "values": { "$ref": "#/fixtureArrays/onnx_backend_nonmaxsuppression_limit_output_size_input_boxes" }
604
+ }
605
+ },
606
+ "scores": {
607
+ "dtype": "float32",
608
+ "shape": [1, 1, 6],
609
+ "data": {
610
+ "kind": "values",
611
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
612
+ }
613
+ },
614
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
615
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
616
+ "score_threshold": {
617
+ "dtype": "float32",
618
+ "shape": [],
619
+ "data": { "kind": "values", "values": [0.4000000059604645] }
620
+ }
621
+ },
622
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3] } },
623
+ "provenance": {
624
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_suppress_by_IOU_and_scores",
625
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
626
+ }
627
+ },
628
+ {
629
+ "name": "onnx_backend_nonmaxsuppression_two_batches",
630
+ "attrs": { "center_point_box": 0 },
631
+ "inputs": {
632
+ "boxes": {
633
+ "dtype": "float32",
634
+ "shape": [2, 6, 4],
635
+ "data": {
636
+ "kind": "values",
637
+ "values": [0.0, 0.0, 1.0, 1.0, 0.0, 0.10000000149011612, 1.0, 1.100000023841858, 0.0, -0.10000000149011612, 1.0, 0.8999999761581421, 0.0, 10.0, 1.0, 11.0, 0.0, 10.100000381469727, 1.0, 11.100000381469727, 0.0, 100.0, 1.0, 101.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.10000000149011612, 1.0, 1.100000023841858, 0.0, -0.10000000149011612, 1.0, 0.8999999761581421, 0.0, 10.0, 1.0, 11.0, 0.0, 10.100000381469727, 1.0, 11.100000381469727, 0.0, 100.0, 1.0, 101.0]
638
+ }
639
+ },
640
+ "scores": {
641
+ "dtype": "float32",
642
+ "shape": [2, 1, 6],
643
+ "data": {
644
+ "kind": "values",
645
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896, 0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
646
+ }
647
+ },
648
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
649
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
650
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
651
+ },
652
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [4, 3] } },
653
+ "provenance": {
654
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_two_batches",
655
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
656
+ }
657
+ },
658
+ {
659
+ "name": "onnx_backend_nonmaxsuppression_two_classes",
660
+ "attrs": { "center_point_box": 0 },
661
+ "inputs": {
662
+ "boxes": {
663
+ "dtype": "float32",
664
+ "shape": [1, 6, 4],
665
+ "data": {
666
+ "kind": "values",
667
+ "values": { "$ref": "#/fixtureArrays/onnx_backend_nonmaxsuppression_limit_output_size_input_boxes" }
668
+ }
669
+ },
670
+ "scores": {
671
+ "dtype": "float32",
672
+ "shape": [1, 2, 6],
673
+ "data": {
674
+ "kind": "values",
675
+ "values": [0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896, 0.8999999761581421, 0.75, 0.6000000238418579, 0.949999988079071, 0.5, 0.30000001192092896]
676
+ }
677
+ },
678
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
679
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
680
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
681
+ },
682
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [4, 3] } },
683
+ "provenance": {
684
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_nonmaxsuppression_two_classes",
685
+ "notes": "The ONNX optional scalar inputs are supplied as rank-0 tensors, the output uses its exact data-dependent shape, and ONNX int64 indices are represented as uint32 where representable."
686
+ }
687
+ },
688
+ {
689
+ "name": "ort_with_iou_threshold",
690
+ "provenance": {
691
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
692
+ "test": "NonMaxSuppressionOpTest.WithIOUThreshold"
693
+ },
694
+ "attrs": { "center_point_box": 0 },
695
+ "inputs": {
696
+ "boxes": {
697
+ "dtype": "float32",
698
+ "shape": [1, 6, 4],
699
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
700
+ },
701
+ "scores": {
702
+ "dtype": "float32",
703
+ "shape": [1, 1, 6],
704
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
705
+ },
706
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
707
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
708
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
709
+ },
710
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3], "tolerance": 0 } }
711
+ },
712
+ {
713
+ "name": "ort_center_point_box_format",
714
+ "provenance": {
715
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
716
+ "test": "NonMaxSuppressionOpTest.CenterPointBoxFormat"
717
+ },
718
+ "attrs": { "center_point_box": 1 },
719
+ "inputs": {
720
+ "boxes": {
721
+ "dtype": "float32",
722
+ "shape": [1, 6, 4],
723
+ "data": {
724
+ "kind": "values",
725
+ "values": [0.5, 0.5, 1.0, 1.0, 0.5, 0.6, 1.0, 1.0, 0.5, 0.4, 1.0, 1.0, 0.5, 10.5, 1.0, 1.0, 0.5, 10.6, 1.0, 1.0, 0.5, 100.5, 1.0, 1.0]
726
+ }
727
+ },
728
+ "scores": {
729
+ "dtype": "float32",
730
+ "shape": [1, 1, 6],
731
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
732
+ },
733
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
734
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
735
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
736
+ },
737
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3], "tolerance": 0 } }
738
+ },
739
+ {
740
+ "name": "ort_two_batches_one_class",
741
+ "provenance": {
742
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
743
+ "test": "NonMaxSuppressionOpTest.TwoBatches_OneClass"
744
+ },
745
+ "attrs": { "center_point_box": 0 },
746
+ "inputs": {
747
+ "boxes": {
748
+ "dtype": "float32",
749
+ "shape": [2, 6, 4],
750
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/two_batches_one_class_ort_input_boxes" } }
751
+ },
752
+ "scores": {
753
+ "dtype": "float32",
754
+ "shape": [2, 1, 6],
755
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
756
+ },
757
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
758
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
759
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
760
+ },
761
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [4, 3], "tolerance": 0 } }
762
+ },
763
+ {
764
+ "name": "ort_two_batches_two_classes",
765
+ "provenance": {
766
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
767
+ "test": "NonMaxSuppressionOpTest.TwoBatches_TwoClasses"
768
+ },
769
+ "attrs": { "center_point_box": 0 },
770
+ "inputs": {
771
+ "boxes": {
772
+ "dtype": "float32",
773
+ "shape": [2, 5, 4],
774
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/two_batches_two_classes_ort_input_boxes" } }
775
+ },
776
+ "scores": {
777
+ "dtype": "float32",
778
+ "shape": [2, 2, 5],
779
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/two_batches_two_classes_ort_input_scores" } }
780
+ },
781
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
782
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.8] } },
783
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
784
+ },
785
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [8, 3], "tolerance": 0 } }
786
+ },
787
+ {
788
+ "name": "ort_score_threshold_zero_scores",
789
+ "provenance": {
790
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
791
+ "test": "NonMaxSuppressionOpTest.WithScoreThresholdZeroScores"
792
+ },
793
+ "attrs": { "center_point_box": 0 },
794
+ "inputs": {
795
+ "boxes": {
796
+ "dtype": "float32",
797
+ "shape": [1, 6, 4],
798
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
799
+ },
800
+ "scores": {
801
+ "dtype": "float32",
802
+ "shape": [1, 1, 6],
803
+ "data": { "kind": "values", "values": [0.1, 0.0, 0.0, 0.3, 0.2, -5.0] }
804
+ },
805
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [6] } },
806
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
807
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-3.0] } }
808
+ },
809
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3], "tolerance": 0 } }
810
+ },
811
+ {
812
+ "name": "ort_flipped_coordinates",
813
+ "provenance": {
814
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
815
+ "test": "NonMaxSuppressionOpTest.FlippedCoordinates"
816
+ },
817
+ "attrs": { "center_point_box": 0 },
818
+ "inputs": {
819
+ "boxes": {
820
+ "dtype": "float32",
821
+ "shape": [1, 6, 4],
822
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/flipped_coordinates_ort_input_boxes" } }
823
+ },
824
+ "scores": {
825
+ "dtype": "float32",
826
+ "shape": [1, 1, 6],
827
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
828
+ },
829
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
830
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
831
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
832
+ },
833
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3], "tolerance": 0 } }
834
+ },
835
+ {
836
+ "name": "ort_zero_max_output_per_class",
837
+ "provenance": {
838
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
839
+ "test": "NonMaxSuppressionOpTest.ZeroMaxOutputPerClass"
840
+ },
841
+ "attrs": { "center_point_box": 0 },
842
+ "inputs": {
843
+ "boxes": {
844
+ "dtype": "float32",
845
+ "shape": [1, 6, 4],
846
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
847
+ },
848
+ "scores": {
849
+ "dtype": "float32",
850
+ "shape": [1, 1, 6],
851
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
852
+ },
853
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [0] } },
854
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
855
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.4] } }
856
+ },
857
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [0, 3], "tolerance": 0 } }
858
+ },
859
+ {
860
+ "name": "groups_parallel_single_class_512",
861
+ "provenance": {
862
+ "notes": "512 boxes on an integer grid (13-value cycle gives 13 repeating geometries -> heavy score/index tie-breaking). Grid coordinates keep IoU ratios far from the threshold so float comparisons are unambiguous. Exercises the group-parallel fused suppression/next-argmax path."
863
+ },
864
+ "inputs": {
865
+ "boxes": {
866
+ "dtype": "float32",
867
+ "shape": [1, 512, 4],
868
+ "data": { "kind": "cycle", "values": [0.0, 0.0, 4.0, 4.0, 1.0, 1.0, 5.0, 5.0, 2.0, 2.0, 6.0, 6.0, 3.0] }
869
+ },
870
+ "scores": {
871
+ "dtype": "float32",
872
+ "shape": [1, 1, 512],
873
+ "data": {
874
+ "kind": "cycle",
875
+ "values": [0.9, 0.1, 0.55, 0.7, 0.3, 0.85, 0.2, 0.65, 0.4, 0.75, 0.05, 0.95, 0.5, 0.6, 0.15, 0.8, 0.35]
876
+ }
877
+ },
878
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [16] } },
879
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
880
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.25] } }
881
+ },
882
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [4, 3], "tolerance": 0 } }
883
+ },
884
+ {
885
+ "name": "groups_parallel_two_batches_two_classes",
886
+ "provenance": {
887
+ "notes": "2 batches x 2 classes x 320 boxes; 19-value box cycle yields varied grid geometries. Per-group suppression state must remain isolated across batches and classes."
888
+ },
889
+ "inputs": {
890
+ "boxes": {
891
+ "dtype": "float32",
892
+ "shape": [2, 320, 4],
893
+ "data": {
894
+ "kind": "cycle",
895
+ "values": [0.0, 0.0, 2.0, 2.0, 1.0, 0.0, 3.0, 2.0, 2.0, 0.0, 4.0, 2.0, 0.0, 1.0, 2.0, 3.0, 1.0, 1.0, 3.0]
896
+ }
897
+ },
898
+ "scores": {
899
+ "dtype": "float32",
900
+ "shape": [2, 2, 320],
901
+ "data": {
902
+ "kind": "cycle",
903
+ "values": [0.31, 0.97, 0.12, 0.58, 0.74, 0.05, 0.66, 0.83, 0.22, 0.49, 0.91, 0.38, 0.7, 0.15, 0.62, 0.88, 0.27, 0.53, 0.79, 0.08, 0.44, 0.95, 0.35]
904
+ }
905
+ },
906
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [8] } },
907
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.4] } },
908
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.3] } }
909
+ },
910
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [30, 3], "tolerance": 0 } }
911
+ },
912
+ {
913
+ "name": "groups_parallel_center_point_box_256",
914
+ "provenance": {
915
+ "notes": "center_point_box=1 decode through the group-parallel kernel; 17-value positive cycle keeps widths/heights valid and corners on a half-integer grid."
916
+ },
917
+ "attrs": { "center_point_box": 1 },
918
+ "inputs": {
919
+ "boxes": {
920
+ "dtype": "float32",
921
+ "shape": [1, 256, 4],
922
+ "data": {
923
+ "kind": "cycle",
924
+ "values": [2.0, 2.0, 2.0, 2.0, 5.0, 3.0, 4.0, 2.0, 3.0, 6.0, 2.0, 4.0, 7.0, 7.0, 6.0, 6.0, 1.0]
925
+ }
926
+ },
927
+ "scores": {
928
+ "dtype": "float32",
929
+ "shape": [1, 1, 256],
930
+ "data": { "kind": "cycle", "values": [0.45, 0.9, 0.1, 0.7, 0.3, 0.85, 0.55, 0.25, 0.65, 0.4, 0.75] }
931
+ },
932
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [10] } },
933
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
934
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.2] } }
935
+ },
936
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [10, 3], "tolerance": 0 } }
937
+ },
938
+ {
939
+ "name": "ort_big_int_max_output_boxes_per_class",
940
+ "provenance": {
941
+ "source": "onnxruntime/test/providers/cpu/object_detection/non_max_suppression_test.cc",
942
+ "test": "NonMaxSuppressionOpTest.BigIntMaxOutputBoxesPerClass",
943
+ "notes": "ORT source coverage uses INT64_MAX; this C-long-safe large positive scalar has the same no-practical-limit behavior while the fixture supplies the exact result shape."
944
+ },
945
+ "inputs": {
946
+ "boxes": {
947
+ "dtype": "float32",
948
+ "shape": [1, 6, 4],
949
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/nms_six_boxes" } }
950
+ },
951
+ "scores": {
952
+ "dtype": "float32",
953
+ "shape": [1, 1, 6],
954
+ "data": { "kind": "values", "values": [0.9, 0.75, 0.6, 0.95, 0.5, 0.3] }
955
+ },
956
+ "max_output_boxes_per_class": {
957
+ "dtype": "uint32",
958
+ "shape": [],
959
+ "data": { "kind": "values", "values": [2147483647] }
960
+ },
961
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
962
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.4] } }
963
+ },
964
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [2, 3], "tolerance": 0 } }
965
+ },
966
+ {
967
+ "name": "score_exactly_at_threshold_excluded",
968
+ "attrs": { "center_point_box": 0 },
969
+ "inputs": {
970
+ "boxes": {
971
+ "dtype": "float32",
972
+ "shape": [1, 3, 4],
973
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 5.0, 5.0, 6.0, 6.0, 10.0, 10.0, 11.0, 11.0] }
974
+ },
975
+ "scores": { "dtype": "float32", "shape": [1, 1, 3], "data": { "kind": "values", "values": [0.8, 0.5, 0.3] } },
976
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
977
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
978
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } }
979
+ },
980
+ "outputs": {
981
+ "selected_indices": {
982
+ "dtype": "uint32",
983
+ "shape": [1, 3],
984
+ "tolerance": 0,
985
+ "data": { "kind": "values", "values": [0, 0, 0] }
986
+ }
987
+ }
988
+ },
989
+ {
990
+ "name": "iou_exactly_at_threshold_kept",
991
+ "attrs": { "center_point_box": 0 },
992
+ "inputs": {
993
+ "boxes": {
994
+ "dtype": "float32",
995
+ "shape": [1, 2, 4],
996
+ "data": { "kind": "values", "values": [0.0, 0.0, 5.0, 4.0, 3.0, 0.0, 8.0, 4.0] }
997
+ },
998
+ "scores": { "dtype": "float32", "shape": [1, 1, 2], "data": { "kind": "values", "values": [0.9, 0.8] } },
999
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
1000
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.25] } },
1001
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
1002
+ },
1003
+ "outputs": {
1004
+ "selected_indices": {
1005
+ "dtype": "uint32",
1006
+ "shape": [2, 3],
1007
+ "tolerance": 0,
1008
+ "data": { "kind": "values", "values": [0, 0, 0, 0, 0, 1] }
1009
+ }
1010
+ }
1011
+ },
1012
+ {
1013
+ "name": "groups_parallel_multi_class_suppression_isolation",
1014
+ "attrs": { "center_point_box": 0 },
1015
+ "inputs": {
1016
+ "boxes": {
1017
+ "dtype": "float32",
1018
+ "shape": [1, 4, 4],
1019
+ "data": {
1020
+ "kind": "values",
1021
+ "values": [0.0, 0.0, 2.0, 2.0, 0.5, 0.5, 2.5, 2.5, 10.0, 10.0, 12.0, 12.0, 20.0, 20.0, 22.0, 22.0]
1022
+ }
1023
+ },
1024
+ "scores": {
1025
+ "dtype": "float32",
1026
+ "shape": [1, 2, 4],
1027
+ "data": { "kind": "values", "values": [0.9, 0.8, 0.7, 0.6, 0.2, 0.95, 0.85, 0.15] }
1028
+ },
1029
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
1030
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
1031
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.1] } }
1032
+ },
1033
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [6, 3] } }
1034
+ },
1035
+ {
1036
+ "name": "zero_area_boxes_never_suppress_each_other",
1037
+ "attrs": { "center_point_box": 0 },
1038
+ "inputs": {
1039
+ "boxes": {
1040
+ "dtype": "float32",
1041
+ "shape": [1, 3, 4],
1042
+ "data": { "kind": "values", "values": [5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 3.0, 3.0, 3.0, 3.0] }
1043
+ },
1044
+ "scores": { "dtype": "float32", "shape": [1, 1, 3], "data": { "kind": "values", "values": [0.9, 0.8, 0.7] } },
1045
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [4] } },
1046
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
1047
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
1048
+ },
1049
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [3, 3], "tolerance": 0 } },
1050
+ "provenance": {
1051
+ "notes": "Reference-generated regression for zero-area boxes: explicit selected-box tracking prevents a zero self-IoU from selecting the same box repeatedly."
1052
+ }
1053
+ },
1054
+ {
1055
+ "name": "all_optional_inputs_omitted_defaults_to_empty",
1056
+ "provenance": {
1057
+ "source": "ONNX NonMaxSuppression-11",
1058
+ "notes": "Covers all three optional scalar inputs omitted: max_output_boxes_per_class defaults to zero, so the exact result is empty even though scores include negative and positive values."
1059
+ },
1060
+ "inputs": {
1061
+ "boxes": {
1062
+ "dtype": "float32",
1063
+ "shape": [1, 2, 4],
1064
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0] }
1065
+ },
1066
+ "scores": { "dtype": "float32", "shape": [1, 1, 2], "data": { "kind": "values", "values": [-1.0, 1.0] } }
1067
+ },
1068
+ "outputs": {
1069
+ "selected_indices": {
1070
+ "dtype": "uint32",
1071
+ "shape": [0, 3],
1072
+ "tolerance": 0,
1073
+ "data": { "kind": "values", "values": [] }
1074
+ }
1075
+ }
1076
+ },
1077
+ {
1078
+ "name": "only_max_output_input_present",
1079
+ "provenance": {
1080
+ "source": "ONNX NonMaxSuppression-11",
1081
+ "notes": "With score_threshold omitted, negative scores remain eligible; with iou_threshold omitted, its zero default keeps disjoint boxes."
1082
+ },
1083
+ "inputs": {
1084
+ "boxes": {
1085
+ "dtype": "float32",
1086
+ "shape": [1, 2, 4],
1087
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0] }
1088
+ },
1089
+ "scores": { "dtype": "float32", "shape": [1, 1, 2], "data": { "kind": "values", "values": [-1.0, 1.0] } },
1090
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } }
1091
+ },
1092
+ "outputs": {
1093
+ "selected_indices": {
1094
+ "dtype": "uint32",
1095
+ "shape": [2, 3],
1096
+ "tolerance": 0,
1097
+ "data": { "kind": "values", "values": [0, 0, 1, 0, 0, 0] }
1098
+ }
1099
+ }
1100
+ },
1101
+ {
1102
+ "name": "only_iou_threshold_input_present",
1103
+ "provenance": {
1104
+ "source": "ONNX NonMaxSuppression-11",
1105
+ "notes": "An IOU input does not change the omitted max_output_boxes_per_class default of zero."
1106
+ },
1107
+ "inputs": {
1108
+ "boxes": {
1109
+ "dtype": "float32",
1110
+ "shape": [1, 1, 4],
1111
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0] }
1112
+ },
1113
+ "scores": { "dtype": "float32", "shape": [1, 1, 1], "data": { "kind": "values", "values": [1.0] } },
1114
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } }
1115
+ },
1116
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [0, 3], "tolerance": 0 } }
1117
+ },
1118
+ {
1119
+ "name": "only_score_threshold_input_present",
1120
+ "provenance": {
1121
+ "source": "ONNX NonMaxSuppression-11",
1122
+ "notes": "A score input does not change the omitted max_output_boxes_per_class default of zero."
1123
+ },
1124
+ "inputs": {
1125
+ "boxes": {
1126
+ "dtype": "float32",
1127
+ "shape": [1, 1, 4],
1128
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0] }
1129
+ },
1130
+ "scores": { "dtype": "float32", "shape": [1, 1, 1], "data": { "kind": "values", "values": [1.0] } },
1131
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } }
1132
+ },
1133
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [0, 3], "tolerance": 0 } }
1134
+ },
1135
+ {
1136
+ "name": "max_and_score_inputs_iou_default",
1137
+ "provenance": {
1138
+ "source": "ONNX NonMaxSuppression-11",
1139
+ "notes": "Covers the optional-input hole before score_threshold: iou_threshold is omitted and defaults to zero."
1140
+ },
1141
+ "inputs": {
1142
+ "boxes": {
1143
+ "dtype": "float32",
1144
+ "shape": [1, 2, 4],
1145
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0] }
1146
+ },
1147
+ "scores": { "dtype": "float32", "shape": [1, 1, 2], "data": { "kind": "values", "values": [-1.0, 1.0] } },
1148
+ "max_output_boxes_per_class": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [2] } },
1149
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
1150
+ },
1151
+ "outputs": {
1152
+ "selected_indices": {
1153
+ "dtype": "uint32",
1154
+ "shape": [1, 3],
1155
+ "tolerance": 0,
1156
+ "data": { "kind": "values", "values": [0, 0, 1] }
1157
+ }
1158
+ }
1159
+ },
1160
+ {
1161
+ "name": "iou_and_score_inputs_max_default",
1162
+ "provenance": {
1163
+ "source": "ONNX NonMaxSuppression-11",
1164
+ "notes": "Covers both threshold inputs with max_output_boxes_per_class omitted; its zero default still produces an empty result."
1165
+ },
1166
+ "inputs": {
1167
+ "boxes": {
1168
+ "dtype": "float32",
1169
+ "shape": [1, 1, 4],
1170
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 1.0] }
1171
+ },
1172
+ "scores": { "dtype": "float32", "shape": [1, 1, 1], "data": { "kind": "values", "values": [1.0] } },
1173
+ "iou_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } },
1174
+ "score_threshold": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.5] } }
1175
+ },
1176
+ "outputs": { "selected_indices": { "dtype": "uint32", "shape": [0, 3], "tolerance": 0 } }
1177
+ }
1178
+ ]
1179
+ }