Spaces:
Runtime error
Runtime error
| # Copyright (c) OpenMMLab. All rights reserved. | |
| import torch | |
| from mmcv.ops.nms import batched_nms | |
| from mmdet.core.bbox.iou_calculators import bbox_overlaps | |
| def multiclass_nms(multi_bboxes, | |
| multi_scores, | |
| score_thr, | |
| nms_cfg, | |
| max_num=-1, | |
| score_factors=None, | |
| return_inds=False): | |
| """NMS for multi-class bboxes. | |
| Args: | |
| multi_bboxes (Tensor): shape (n, #class*4) or (n, 4) | |
| multi_scores (Tensor): shape (n, #class), where the last column | |
| contains scores of the background class, but this will be ignored. | |
| score_thr (float): bbox threshold, bboxes with scores lower than it | |
| will not be considered. | |
| nms_cfg (dict): a dict that contains the arguments of nms operations | |
| max_num (int, optional): if there are more than max_num bboxes after | |
| NMS, only top max_num will be kept. Default to -1. | |
| score_factors (Tensor, optional): The factors multiplied to scores | |
| before applying NMS. Default to None. | |
| return_inds (bool, optional): Whether return the indices of kept | |
| bboxes. Default to False. | |
| Returns: | |
| tuple: (dets, labels, indices (optional)), tensors of shape (k, 5), | |
| (k), and (k). Dets are boxes with scores. Labels are 0-based. | |
| """ | |
| num_classes = multi_scores.size(1) - 1 | |
| # exclude background category | |
| if multi_bboxes.shape[1] > 4: | |
| bboxes = multi_bboxes.view(multi_scores.size(0), -1, 4) | |
| else: | |
| bboxes = multi_bboxes[:, None].expand( | |
| multi_scores.size(0), num_classes, 4) | |
| scores = multi_scores[:, :-1] | |
| labels = torch.arange(num_classes, dtype=torch.long, device=scores.device) | |
| labels = labels.view(1, -1).expand_as(scores) | |
| bboxes = bboxes.reshape(-1, 4) | |
| scores = scores.reshape(-1) | |
| labels = labels.reshape(-1) | |
| if not torch.onnx.is_in_onnx_export(): | |
| # NonZero not supported in TensorRT | |
| # remove low scoring boxes | |
| valid_mask = scores > score_thr | |
| # multiply score_factor after threshold to preserve more bboxes, improve | |
| # mAP by 1% for YOLOv3 | |
| if score_factors is not None: | |
| # expand the shape to match original shape of score | |
| score_factors = score_factors.view(-1, 1).expand( | |
| multi_scores.size(0), num_classes) | |
| score_factors = score_factors.reshape(-1) | |
| scores = scores * score_factors | |
| if not torch.onnx.is_in_onnx_export(): | |
| # NonZero not supported in TensorRT | |
| inds = valid_mask.nonzero(as_tuple=False).squeeze(1) | |
| bboxes, scores, labels = bboxes[inds], scores[inds], labels[inds] | |
| else: | |
| # TensorRT NMS plugin has invalid output filled with -1 | |
| # add dummy data to make detection output correct. | |
| bboxes = torch.cat([bboxes, bboxes.new_zeros(1, 4)], dim=0) | |
| scores = torch.cat([scores, scores.new_zeros(1)], dim=0) | |
| labels = torch.cat([labels, labels.new_zeros(1)], dim=0) | |
| if bboxes.numel() == 0: | |
| if torch.onnx.is_in_onnx_export(): | |
| raise RuntimeError('[ONNX Error] Can not record NMS ' | |
| 'as it has not been executed this time') | |
| dets = torch.cat([bboxes, scores[:, None]], -1) | |
| if return_inds: | |
| return dets, labels, inds | |
| else: | |
| return dets, labels | |
| dets, keep = batched_nms(bboxes, scores, labels, nms_cfg) | |
| if max_num > 0: | |
| dets = dets[:max_num] | |
| keep = keep[:max_num] | |
| if return_inds: | |
| return dets, labels[keep], inds[keep] | |
| else: | |
| return dets, labels[keep] | |
| def fast_nms(multi_bboxes, | |
| multi_scores, | |
| multi_coeffs, | |
| score_thr, | |
| iou_thr, | |
| top_k, | |
| max_num=-1): | |
| """Fast NMS in `YOLACT <https://arxiv.org/abs/1904.02689>`_. | |
| Fast NMS allows already-removed detections to suppress other detections so | |
| that every instance can be decided to be kept or discarded in parallel, | |
| which is not possible in traditional NMS. This relaxation allows us to | |
| implement Fast NMS entirely in standard GPU-accelerated matrix operations. | |
| Args: | |
| multi_bboxes (Tensor): shape (n, #class*4) or (n, 4) | |
| multi_scores (Tensor): shape (n, #class+1), where the last column | |
| contains scores of the background class, but this will be ignored. | |
| multi_coeffs (Tensor): shape (n, #class*coeffs_dim). | |
| score_thr (float): bbox threshold, bboxes with scores lower than it | |
| will not be considered. | |
| iou_thr (float): IoU threshold to be considered as conflicted. | |
| top_k (int): if there are more than top_k bboxes before NMS, | |
| only top top_k will be kept. | |
| max_num (int): if there are more than max_num bboxes after NMS, | |
| only top max_num will be kept. If -1, keep all the bboxes. | |
| Default: -1. | |
| Returns: | |
| tuple: (dets, labels, coefficients), tensors of shape (k, 5), (k, 1), | |
| and (k, coeffs_dim). Dets are boxes with scores. | |
| Labels are 0-based. | |
| """ | |
| scores = multi_scores[:, :-1].t() # [#class, n] | |
| scores, idx = scores.sort(1, descending=True) | |
| idx = idx[:, :top_k].contiguous() | |
| scores = scores[:, :top_k] # [#class, topk] | |
| num_classes, num_dets = idx.size() | |
| boxes = multi_bboxes[idx.view(-1), :].view(num_classes, num_dets, 4) | |
| coeffs = multi_coeffs[idx.view(-1), :].view(num_classes, num_dets, -1) | |
| iou = bbox_overlaps(boxes, boxes) # [#class, topk, topk] | |
| iou.triu_(diagonal=1) | |
| iou_max, _ = iou.max(dim=1) | |
| # Now just filter out the ones higher than the threshold | |
| keep = iou_max <= iou_thr | |
| # Second thresholding introduces 0.2 mAP gain at negligible time cost | |
| keep *= scores > score_thr | |
| # Assign each kept detection to its corresponding class | |
| classes = torch.arange( | |
| num_classes, device=boxes.device)[:, None].expand_as(keep) | |
| classes = classes[keep] | |
| boxes = boxes[keep] | |
| coeffs = coeffs[keep] | |
| scores = scores[keep] | |
| # Only keep the top max_num highest scores across all classes | |
| scores, idx = scores.sort(0, descending=True) | |
| if max_num > 0: | |
| idx = idx[:max_num] | |
| scores = scores[:max_num] | |
| classes = classes[idx] | |
| boxes = boxes[idx] | |
| coeffs = coeffs[idx] | |
| cls_dets = torch.cat([boxes, scores[:, None]], dim=1) | |
| return cls_dets, classes, coeffs | |