We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Question about 3d NMS
As I can see [iou3d_utils](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/ops/iou3d/iou3d_utils.py) is based on [iou3d_nms_utils](https://github.com/open-mmlab/OpenPCDet/blob/master/pcdet/ops/iou3d_nms/iou3d_nms_utils.py) of `OpenPCDet`. The implementation in `OpenPCDet` supports fair `iou` and `nms` with full 3d parametrization: `[x, y, z, dx, dy, dz, heading]`. However the implementation in `mmdetection3d` supports only `[x1, y1, x2, y2, ry]`. This design choice brings a couple of disadvantages. For example, for `VoteNet` on `SUNRGBD` we first predict boxes with angles and then [apply](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/models/dense_heads/vote_head.py#L627) `nms` to the aligned boxes without angle.

So, my question is, why not to use `nms` from `OpenPCDet` instead of applying lifehacks with `aligned_nms` or `bev_nms`?
Thanks in advance.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mmdet3d/core/post_processing/box3d_nms.py`
Content:
```
1 import numba
2 import numpy as np
3 import torch
4 
5 from mmdet3d.ops.iou3d.iou3d_utils import nms_gpu, nms_normal_gpu
6 
7 
8 def box3d_multiclass_nms(mlvl_bboxes,
9                          mlvl_bboxes_for_nms,
10                          mlvl_scores,
11                          score_thr,
12                          max_num,
13                          cfg,
14                          mlvl_dir_scores=None):
15     """Multi-class nms for 3D boxes.
16 
17     Args:
18         mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M).
19             M is the dimensions of boxes.
20         mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape
21             (N, 4). N is the number of boxes.
22         mlvl_scores (torch.Tensor): Multi-level boxes with shape
23             (N, ). N is the number of boxes.
24         score_thr (float): Score thredhold to filter boxes with low
25             confidence.
26         max_num (int): Maximum number of boxes will be kept.
27         cfg (dict): Configuration dict of NMS.
28         mlvl_dir_scores (torch.Tensor, optional): Multi-level scores
29             of direction classifier. Defaults to None.
30 
31     Returns:
32         tuple[torch.Tensor]: Return results after nms, including 3D \
33             bounding boxes, scores, labels and direction scores.
34     """
35     # do multi class nms
36     # the fg class id range: [0, num_classes-1]
37     num_classes = mlvl_scores.shape[1] - 1
38     bboxes = []
39     scores = []
40     labels = []
41     dir_scores = []
42     for i in range(0, num_classes):
43         # get bboxes and scores of this class
44         cls_inds = mlvl_scores[:, i] > score_thr
45         if not cls_inds.any():
46             continue
47 
48         _scores = mlvl_scores[cls_inds, i]
49         _bboxes_for_nms = mlvl_bboxes_for_nms[cls_inds, :]
50 
51         if cfg.use_rotate_nms:
52             nms_func = nms_gpu
53         else:
54             nms_func = nms_normal_gpu
55 
56         selected = nms_func(_bboxes_for_nms, _scores, cfg.nms_thr)
57         _mlvl_bboxes = mlvl_bboxes[cls_inds, :]
58         bboxes.append(_mlvl_bboxes[selected])
59         scores.append(_scores[selected])
60         cls_label = mlvl_bboxes.new_full((len(selected), ),
61                                          i,
62                                          dtype=torch.long)
63         labels.append(cls_label)
64 
65         if mlvl_dir_scores is not None:
66             _mlvl_dir_scores = mlvl_dir_scores[cls_inds]
67             dir_scores.append(_mlvl_dir_scores[selected])
68 
69     if bboxes:
70         bboxes = torch.cat(bboxes, dim=0)
71         scores = torch.cat(scores, dim=0)
72         labels = torch.cat(labels, dim=0)
73         if mlvl_dir_scores is not None:
74             dir_scores = torch.cat(dir_scores, dim=0)
75         if bboxes.shape[0] > max_num:
76             _, inds = scores.sort(descending=True)
77             inds = inds[:max_num]
78             bboxes = bboxes[inds, :]
79             labels = labels[inds]
80             scores = scores[inds]
81             if mlvl_dir_scores is not None:
82                 dir_scores = dir_scores[inds]
83     else:
84         bboxes = mlvl_scores.new_zeros((0, mlvl_bboxes.size(-1)))
85         scores = mlvl_scores.new_zeros((0, ))
86         labels = mlvl_scores.new_zeros((0, ), dtype=torch.long)
87         dir_scores = mlvl_scores.new_zeros((0, ))
88     return bboxes, scores, labels, dir_scores
89 
90 
91 def aligned_3d_nms(boxes, scores, classes, thresh):
92     """3d nms for aligned boxes.
93 
94     Args:
95         boxes (torch.Tensor): Aligned box with shape [n, 6].
96         scores (torch.Tensor): Scores of each box.
97         classes (torch.Tensor): Class of each box.
98         thresh (float): Iou threshold for nms.
99 
100     Returns:
101         torch.Tensor: Indices of selected boxes.
102     """
103     x1 = boxes[:, 0]
104     y1 = boxes[:, 1]
105     z1 = boxes[:, 2]
106     x2 = boxes[:, 3]
107     y2 = boxes[:, 4]
108     z2 = boxes[:, 5]
109     area = (x2 - x1) * (y2 - y1) * (z2 - z1)
110     zero = boxes.new_zeros(1, )
111 
112     score_sorted = torch.argsort(scores)
113     pick = []
114     while (score_sorted.shape[0] != 0):
115         last = score_sorted.shape[0]
116         i = score_sorted[-1]
117         pick.append(i)
118 
119         xx1 = torch.max(x1[i], x1[score_sorted[:last - 1]])
120         yy1 = torch.max(y1[i], y1[score_sorted[:last - 1]])
121         zz1 = torch.max(z1[i], z1[score_sorted[:last - 1]])
122         xx2 = torch.min(x2[i], x2[score_sorted[:last - 1]])
123         yy2 = torch.min(y2[i], y2[score_sorted[:last - 1]])
124         zz2 = torch.min(z2[i], z2[score_sorted[:last - 1]])
125         classes1 = classes[i]
126         classes2 = classes[score_sorted[:last - 1]]
127         inter_l = torch.max(zero, xx2 - xx1)
128         inter_w = torch.max(zero, yy2 - yy1)
129         inter_h = torch.max(zero, zz2 - zz1)
130 
131         inter = inter_l * inter_w * inter_h
132         iou = inter / (area[i] + area[score_sorted[:last - 1]] - inter)
133         iou = iou * (classes1 == classes2).float()
134         score_sorted = score_sorted[torch.nonzero(
135             iou <= thresh, as_tuple=False).flatten()]
136 
137     indices = boxes.new_tensor(pick, dtype=torch.long)
138     return indices
139 
140 
141 @numba.jit(nopython=True)
142 def circle_nms(dets, thresh, post_max_size=83):
143     """Circular NMS.
144 
145     An object is only counted as positive if no other center
146     with a higher confidence exists within a radius r using a
147     bird-eye view distance metric.
148 
149     Args:
150         dets (torch.Tensor): Detection results with the shape of [N, 3].
151         thresh (float): Value of threshold.
152         post_max_size (int): Max number of prediction to be kept. Defaults
153             to 83
154 
155     Returns:
156         torch.Tensor: Indexes of the detections to be kept.
157     """
158     x1 = dets[:, 0]
159     y1 = dets[:, 1]
160     scores = dets[:, 2]
161     order = scores.argsort()[::-1].astype(np.int32)  # highest->lowest
162     ndets = dets.shape[0]
163     suppressed = np.zeros((ndets), dtype=np.int32)
164     keep = []
165     for _i in range(ndets):
166         i = order[_i]  # start with highest score box
167         if suppressed[
168                 i] == 1:  # if any box have enough iou with this, remove it
169             continue
170         keep.append(i)
171         for _j in range(_i + 1, ndets):
172             j = order[_j]
173             if suppressed[j] == 1:
174                 continue
175             # calculate center distance between i and j box
176             dist = (x1[i] - x1[j])**2 + (y1[i] - y1[j])**2
177 
178             # ovr = inter / areas[j]
179             if dist <= thresh:
180                 suppressed[j] = 1
181     return keep[:post_max_size]
182
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mmdet3d/core/post_processing/box3d_nms.py b/mmdet3d/core/post_processing/box3d_nms.py
--- a/mmdet3d/core/post_processing/box3d_nms.py
+++ b/mmdet3d/core/post_processing/box3d_nms.py
@@ -18,9 +18,9 @@
         mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M).
             M is the dimensions of boxes.
         mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape
-            (N, 4). N is the number of boxes.
+            (N, 5) ([x1, y1, x2, y2, ry]). N is the number of boxes.
         mlvl_scores (torch.Tensor): Multi-level boxes with shape
-            (N, ). N is the number of boxes.
+            (N, C + 1). N is the number of boxes. C is the number of classes.
         score_thr (float): Score thredhold to filter boxes with low
             confidence.
         max_num (int): Maximum number of boxes will be kept.

{"golden_diff": "diff --git a/mmdet3d/core/post_processing/box3d_nms.py b/mmdet3d/core/post_processing/box3d_nms.py\n--- a/mmdet3d/core/post_processing/box3d_nms.py\n+++ b/mmdet3d/core/post_processing/box3d_nms.py\n@@ -18,9 +18,9 @@\n         mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M).\n             M is the dimensions of boxes.\n         mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape\n-            (N, 4). N is the number of boxes.\n+            (N, 5) ([x1, y1, x2, y2, ry]). N is the number of boxes.\n         mlvl_scores (torch.Tensor): Multi-level boxes with shape\n-            (N, ). N is the number of boxes.\n+            (N, C + 1). N is the number of boxes. C is the number of classes.\n         score_thr (float): Score thredhold to filter boxes with low\n             confidence.\n         max_num (int): Maximum number of boxes will be kept.\n", "issue": "Question about 3d NMS\nAs I can see [iou3d_utils](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/ops/iou3d/iou3d_utils.py) is based on [iou3d_nms_utils](https://github.com/open-mmlab/OpenPCDet/blob/master/pcdet/ops/iou3d_nms/iou3d_nms_utils.py) of `OpenPCDet`. The implementation in `OpenPCDet` supports fair `iou` and `nms` with full 3d parametrization: `[x, y, z, dx, dy, dz, heading]`. However the implementation in `mmdetection3d` supports only `[x1, y1, x2, y2, ry]`. This design choice brings a couple of disadvantages. For example, for `VoteNet` on `SUNRGBD` we first predict boxes with angles and then [apply](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/models/dense_heads/vote_head.py#L627) `nms` to the aligned boxes without angle.\r\n\r\nSo, my question is, why not to use `nms` from `OpenPCDet` instead of applying lifehacks with `aligned_nms` or `bev_nms`?\r\nThanks in advance.\n", "before_files": [{"content": "import numba\nimport numpy as np\nimport torch\n\nfrom mmdet3d.ops.iou3d.iou3d_utils import nms_gpu, nms_normal_gpu\n\n\ndef box3d_multiclass_nms(mlvl_bboxes,\n                         mlvl_bboxes_for_nms,\n                         mlvl_scores,\n                         score_thr,\n                         max_num,\n                         cfg,\n                         mlvl_dir_scores=None):\n    \"\"\"Multi-class nms for 3D boxes.\n\n    Args:\n        mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M).\n            M is the dimensions of boxes.\n        mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape\n            (N, 4). N is the number of boxes.\n        mlvl_scores (torch.Tensor): Multi-level boxes with shape\n            (N, ). N is the number of boxes.\n        score_thr (float): Score thredhold to filter boxes with low\n            confidence.\n        max_num (int): Maximum number of boxes will be kept.\n        cfg (dict): Configuration dict of NMS.\n        mlvl_dir_scores (torch.Tensor, optional): Multi-level scores\n            of direction classifier. Defaults to None.\n\n    Returns:\n        tuple[torch.Tensor]: Return results after nms, including 3D \\\n            bounding boxes, scores, labels and direction scores.\n    \"\"\"\n    # do multi class nms\n    # the fg class id range: [0, num_classes-1]\n    num_classes = mlvl_scores.shape[1] - 1\n    bboxes = []\n    scores = []\n    labels = []\n    dir_scores = []\n    for i in range(0, num_classes):\n        # get bboxes and scores of this class\n        cls_inds = mlvl_scores[:, i] > score_thr\n        if not cls_inds.any():\n            continue\n\n        _scores = mlvl_scores[cls_inds, i]\n        _bboxes_for_nms = mlvl_bboxes_for_nms[cls_inds, :]\n\n        if cfg.use_rotate_nms:\n            nms_func = nms_gpu\n        else:\n            nms_func = nms_normal_gpu\n\n        selected = nms_func(_bboxes_for_nms, _scores, cfg.nms_thr)\n        _mlvl_bboxes = mlvl_bboxes[cls_inds, :]\n        bboxes.append(_mlvl_bboxes[selected])\n        scores.append(_scores[selected])\n        cls_label = mlvl_bboxes.new_full((len(selected), ),\n                                         i,\n                                         dtype=torch.long)\n        labels.append(cls_label)\n\n        if mlvl_dir_scores is not None:\n            _mlvl_dir_scores = mlvl_dir_scores[cls_inds]\n            dir_scores.append(_mlvl_dir_scores[selected])\n\n    if bboxes:\n        bboxes = torch.cat(bboxes, dim=0)\n        scores = torch.cat(scores, dim=0)\n        labels = torch.cat(labels, dim=0)\n        if mlvl_dir_scores is not None:\n            dir_scores = torch.cat(dir_scores, dim=0)\n        if bboxes.shape[0] > max_num:\n            _, inds = scores.sort(descending=True)\n            inds = inds[:max_num]\n            bboxes = bboxes[inds, :]\n            labels = labels[inds]\n            scores = scores[inds]\n            if mlvl_dir_scores is not None:\n                dir_scores = dir_scores[inds]\n    else:\n        bboxes = mlvl_scores.new_zeros((0, mlvl_bboxes.size(-1)))\n        scores = mlvl_scores.new_zeros((0, ))\n        labels = mlvl_scores.new_zeros((0, ), dtype=torch.long)\n        dir_scores = mlvl_scores.new_zeros((0, ))\n    return bboxes, scores, labels, dir_scores\n\n\ndef aligned_3d_nms(boxes, scores, classes, thresh):\n    \"\"\"3d nms for aligned boxes.\n\n    Args:\n        boxes (torch.Tensor): Aligned box with shape [n, 6].\n        scores (torch.Tensor): Scores of each box.\n        classes (torch.Tensor): Class of each box.\n        thresh (float): Iou threshold for nms.\n\n    Returns:\n        torch.Tensor: Indices of selected boxes.\n    \"\"\"\n    x1 = boxes[:, 0]\n    y1 = boxes[:, 1]\n    z1 = boxes[:, 2]\n    x2 = boxes[:, 3]\n    y2 = boxes[:, 4]\n    z2 = boxes[:, 5]\n    area = (x2 - x1) * (y2 - y1) * (z2 - z1)\n    zero = boxes.new_zeros(1, )\n\n    score_sorted = torch.argsort(scores)\n    pick = []\n    while (score_sorted.shape[0] != 0):\n        last = score_sorted.shape[0]\n        i = score_sorted[-1]\n        pick.append(i)\n\n        xx1 = torch.max(x1[i], x1[score_sorted[:last - 1]])\n        yy1 = torch.max(y1[i], y1[score_sorted[:last - 1]])\n        zz1 = torch.max(z1[i], z1[score_sorted[:last - 1]])\n        xx2 = torch.min(x2[i], x2[score_sorted[:last - 1]])\n        yy2 = torch.min(y2[i], y2[score_sorted[:last - 1]])\n        zz2 = torch.min(z2[i], z2[score_sorted[:last - 1]])\n        classes1 = classes[i]\n        classes2 = classes[score_sorted[:last - 1]]\n        inter_l = torch.max(zero, xx2 - xx1)\n        inter_w = torch.max(zero, yy2 - yy1)\n        inter_h = torch.max(zero, zz2 - zz1)\n\n        inter = inter_l * inter_w * inter_h\n        iou = inter / (area[i] + area[score_sorted[:last - 1]] - inter)\n        iou = iou * (classes1 == classes2).float()\n        score_sorted = score_sorted[torch.nonzero(\n            iou <= thresh, as_tuple=False).flatten()]\n\n    indices = boxes.new_tensor(pick, dtype=torch.long)\n    return indices\n\n\n@numba.jit(nopython=True)\ndef circle_nms(dets, thresh, post_max_size=83):\n    \"\"\"Circular NMS.\n\n    An object is only counted as positive if no other center\n    with a higher confidence exists within a radius r using a\n    bird-eye view distance metric.\n\n    Args:\n        dets (torch.Tensor): Detection results with the shape of [N, 3].\n        thresh (float): Value of threshold.\n        post_max_size (int): Max number of prediction to be kept. Defaults\n            to 83\n\n    Returns:\n        torch.Tensor: Indexes of the detections to be kept.\n    \"\"\"\n    x1 = dets[:, 0]\n    y1 = dets[:, 1]\n    scores = dets[:, 2]\n    order = scores.argsort()[::-1].astype(np.int32)  # highest->lowest\n    ndets = dets.shape[0]\n    suppressed = np.zeros((ndets), dtype=np.int32)\n    keep = []\n    for _i in range(ndets):\n        i = order[_i]  # start with highest score box\n        if suppressed[\n                i] == 1:  # if any box have enough iou with this, remove it\n            continue\n        keep.append(i)\n        for _j in range(_i + 1, ndets):\n            j = order[_j]\n            if suppressed[j] == 1:\n                continue\n            # calculate center distance between i and j box\n            dist = (x1[i] - x1[j])**2 + (y1[i] - y1[j])**2\n\n            # ovr = inter / areas[j]\n            if dist <= thresh:\n                suppressed[j] = 1\n    return keep[:post_max_size]\n", "path": "mmdet3d/core/post_processing/box3d_nms.py"}], "after_files": [{"content": "import numba\nimport numpy as np\nimport torch\n\nfrom mmdet3d.ops.iou3d.iou3d_utils import nms_gpu, nms_normal_gpu\n\n\ndef box3d_multiclass_nms(mlvl_bboxes,\n                         mlvl_bboxes_for_nms,\n                         mlvl_scores,\n                         score_thr,\n                         max_num,\n                         cfg,\n                         mlvl_dir_scores=None):\n    \"\"\"Multi-class nms for 3D boxes.\n\n    Args:\n        mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M).\n            M is the dimensions of boxes.\n        mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape\n            (N, 5) ([x1, y1, x2, y2, ry]). N is the number of boxes.\n        mlvl_scores (torch.Tensor): Multi-level boxes with shape\n            (N, C + 1). N is the number of boxes. C is the number of classes.\n        score_thr (float): Score thredhold to filter boxes with low\n            confidence.\n        max_num (int): Maximum number of boxes will be kept.\n        cfg (dict): Configuration dict of NMS.\n        mlvl_dir_scores (torch.Tensor, optional): Multi-level scores\n            of direction classifier. Defaults to None.\n\n    Returns:\n        tuple[torch.Tensor]: Return results after nms, including 3D \\\n            bounding boxes, scores, labels and direction scores.\n    \"\"\"\n    # do multi class nms\n    # the fg class id range: [0, num_classes-1]\n    num_classes = mlvl_scores.shape[1] - 1\n    bboxes = []\n    scores = []\n    labels = []\n    dir_scores = []\n    for i in range(0, num_classes):\n        # get bboxes and scores of this class\n        cls_inds = mlvl_scores[:, i] > score_thr\n        if not cls_inds.any():\n            continue\n\n        _scores = mlvl_scores[cls_inds, i]\n        _bboxes_for_nms = mlvl_bboxes_for_nms[cls_inds, :]\n\n        if cfg.use_rotate_nms:\n            nms_func = nms_gpu\n        else:\n            nms_func = nms_normal_gpu\n\n        selected = nms_func(_bboxes_for_nms, _scores, cfg.nms_thr)\n        _mlvl_bboxes = mlvl_bboxes[cls_inds, :]\n        bboxes.append(_mlvl_bboxes[selected])\n        scores.append(_scores[selected])\n        cls_label = mlvl_bboxes.new_full((len(selected), ),\n                                         i,\n                                         dtype=torch.long)\n        labels.append(cls_label)\n\n        if mlvl_dir_scores is not None:\n            _mlvl_dir_scores = mlvl_dir_scores[cls_inds]\n            dir_scores.append(_mlvl_dir_scores[selected])\n\n    if bboxes:\n        bboxes = torch.cat(bboxes, dim=0)\n        scores = torch.cat(scores, dim=0)\n        labels = torch.cat(labels, dim=0)\n        if mlvl_dir_scores is not None:\n            dir_scores = torch.cat(dir_scores, dim=0)\n        if bboxes.shape[0] > max_num:\n            _, inds = scores.sort(descending=True)\n            inds = inds[:max_num]\n            bboxes = bboxes[inds, :]\n            labels = labels[inds]\n            scores = scores[inds]\n            if mlvl_dir_scores is not None:\n                dir_scores = dir_scores[inds]\n    else:\n        bboxes = mlvl_scores.new_zeros((0, mlvl_bboxes.size(-1)))\n        scores = mlvl_scores.new_zeros((0, ))\n        labels = mlvl_scores.new_zeros((0, ), dtype=torch.long)\n        dir_scores = mlvl_scores.new_zeros((0, ))\n    return bboxes, scores, labels, dir_scores\n\n\ndef aligned_3d_nms(boxes, scores, classes, thresh):\n    \"\"\"3d nms for aligned boxes.\n\n    Args:\n        boxes (torch.Tensor): Aligned box with shape [n, 6].\n        scores (torch.Tensor): Scores of each box.\n        classes (torch.Tensor): Class of each box.\n        thresh (float): Iou threshold for nms.\n\n    Returns:\n        torch.Tensor: Indices of selected boxes.\n    \"\"\"\n    x1 = boxes[:, 0]\n    y1 = boxes[:, 1]\n    z1 = boxes[:, 2]\n    x2 = boxes[:, 3]\n    y2 = boxes[:, 4]\n    z2 = boxes[:, 5]\n    area = (x2 - x1) * (y2 - y1) * (z2 - z1)\n    zero = boxes.new_zeros(1, )\n\n    score_sorted = torch.argsort(scores)\n    pick = []\n    while (score_sorted.shape[0] != 0):\n        last = score_sorted.shape[0]\n        i = score_sorted[-1]\n        pick.append(i)\n\n        xx1 = torch.max(x1[i], x1[score_sorted[:last - 1]])\n        yy1 = torch.max(y1[i], y1[score_sorted[:last - 1]])\n        zz1 = torch.max(z1[i], z1[score_sorted[:last - 1]])\n        xx2 = torch.min(x2[i], x2[score_sorted[:last - 1]])\n        yy2 = torch.min(y2[i], y2[score_sorted[:last - 1]])\n        zz2 = torch.min(z2[i], z2[score_sorted[:last - 1]])\n        classes1 = classes[i]\n        classes2 = classes[score_sorted[:last - 1]]\n        inter_l = torch.max(zero, xx2 - xx1)\n        inter_w = torch.max(zero, yy2 - yy1)\n        inter_h = torch.max(zero, zz2 - zz1)\n\n        inter = inter_l * inter_w * inter_h\n        iou = inter / (area[i] + area[score_sorted[:last - 1]] - inter)\n        iou = iou * (classes1 == classes2).float()\n        score_sorted = score_sorted[torch.nonzero(\n            iou <= thresh, as_tuple=False).flatten()]\n\n    indices = boxes.new_tensor(pick, dtype=torch.long)\n    return indices\n\n\n@numba.jit(nopython=True)\ndef circle_nms(dets, thresh, post_max_size=83):\n    \"\"\"Circular NMS.\n\n    An object is only counted as positive if no other center\n    with a higher confidence exists within a radius r using a\n    bird-eye view distance metric.\n\n    Args:\n        dets (torch.Tensor): Detection results with the shape of [N, 3].\n        thresh (float): Value of threshold.\n        post_max_size (int): Max number of prediction to be kept. Defaults\n            to 83\n\n    Returns:\n        torch.Tensor: Indexes of the detections to be kept.\n    \"\"\"\n    x1 = dets[:, 0]\n    y1 = dets[:, 1]\n    scores = dets[:, 2]\n    order = scores.argsort()[::-1].astype(np.int32)  # highest->lowest\n    ndets = dets.shape[0]\n    suppressed = np.zeros((ndets), dtype=np.int32)\n    keep = []\n    for _i in range(ndets):\n        i = order[_i]  # start with highest score box\n        if suppressed[\n                i] == 1:  # if any box have enough iou with this, remove it\n            continue\n        keep.append(i)\n        for _j in range(_i + 1, ndets):\n            j = order[_j]\n            if suppressed[j] == 1:\n                continue\n            # calculate center distance between i and j box\n            dist = (x1[i] - x1[j])**2 + (y1[i] - y1[j])**2\n\n            # ovr = inter / areas[j]\n            if dist <= thresh:\n                suppressed[j] = 1\n    return keep[:post_max_size]\n", "path": "mmdet3d/core/post_processing/box3d_nms.py"}]}