Performs non-maximum suppression in a batched fashion. Each labels value correspond to a category, and NMS will not be applied between elements of different categories. Adapted from https://github.com/MIC-DKFZ/nnDetection/blob/main/nndet/core/boxes/nms.py Args: boxes: boun
(
boxes: NdarrayOrTensor,
scores: NdarrayOrTensor,
labels: NdarrayOrTensor,
nms_thresh: float,
max_proposals: int = -1,
box_overlap_metric: Callable = box_iou,
)
| 1160 | |
| 1161 | |
| 1162 | def batched_nms( |
| 1163 | boxes: NdarrayOrTensor, |
| 1164 | scores: NdarrayOrTensor, |
| 1165 | labels: NdarrayOrTensor, |
| 1166 | nms_thresh: float, |
| 1167 | max_proposals: int = -1, |
| 1168 | box_overlap_metric: Callable = box_iou, |
| 1169 | ) -> NdarrayOrTensor: |
| 1170 | """ |
| 1171 | Performs non-maximum suppression in a batched fashion. |
| 1172 | Each labels value correspond to a category, and NMS will not be applied between elements of different categories. |
| 1173 | |
| 1174 | Adapted from https://github.com/MIC-DKFZ/nnDetection/blob/main/nndet/core/boxes/nms.py |
| 1175 | |
| 1176 | Args: |
| 1177 | boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` |
| 1178 | scores: prediction scores of the boxes, sized (N,). This function keeps boxes with higher scores. |
| 1179 | labels: indices of the categories for each one of the boxes. sized(N,), value range is (0, num_classes) |
| 1180 | nms_thresh: threshold of NMS. Discards all overlapping boxes with box_overlap > nms_thresh. |
| 1181 | max_proposals: maximum number of boxes it keeps. |
| 1182 | If ``max_proposals`` = -1, there is no limit on the number of boxes that are kept. |
| 1183 | box_overlap_metric: the metric to compute overlap between boxes. |
| 1184 | |
| 1185 | Returns: |
| 1186 | Indexes of ``boxes`` that are kept after NMS. |
| 1187 | """ |
| 1188 | # returns empty array if boxes is empty |
| 1189 | if boxes.shape[0] == 0: |
| 1190 | return convert_to_dst_type(src=np.array([]), dst=boxes, dtype=torch.long)[0] |
| 1191 | |
| 1192 | # convert numpy to tensor if needed |
| 1193 | boxes_t, *_ = convert_data_type(boxes, torch.Tensor, dtype=torch.float32) |
| 1194 | scores_t, *_ = convert_to_dst_type(scores, boxes_t) |
| 1195 | labels_t, *_ = convert_to_dst_type(labels, boxes_t, dtype=torch.long) |
| 1196 | |
| 1197 | # strategy: in order to perform NMS independently per class. |
| 1198 | # we add an offset to all the boxes. The offset is dependent |
| 1199 | # only on the class idx, and is large enough so that boxes |
| 1200 | # from different classes do not overlap |
| 1201 | max_coordinate = boxes_t.max() |
| 1202 | offsets = labels_t.to(boxes_t) * (max_coordinate + 1) |
| 1203 | boxes_for_nms = boxes + offsets[:, None] |
| 1204 | keep = non_max_suppression(boxes_for_nms, scores_t, nms_thresh, max_proposals, box_overlap_metric) |
| 1205 | |
| 1206 | # convert tensor back to numpy if needed |
| 1207 | return convert_to_dst_type(src=keep, dst=boxes, dtype=keep.dtype)[0] |
no test coverage detected
searching dependent graphs…