'Function to perform non-maximal supression.
Arguments:
detections (Tensor): detection boxes (box_count, 5), each row has
(x1, y1, x2, y2, score). Assume the boxes have already
been sorted based on score in descending order
threshold (float): box overlap threshold, boxes with smaller overlaps will be kept
normalized (bool): whether box coordinates are normalized to image dimensions.
This affects whether we use a +1 offset to compute box sizes.
Outputs:
keep_ind (list): list of indices'

def nms(self, detections, threshold, normalized=False):

    def div_ceil(N, thread):
        return int(((N / thread) + ((N % thread) > 0)))
    box_count = detections.shape[0]
    threadsPerBlock = 32
    col_blocks = div_ceil(box_count, threadsPerBlock)
    assert (detections.shape == (box_count, 5))
    mask_size = (box_count * col_blocks)
    output_mask = self.zeros(mask_size, dtype=np.uint32)
    params = [(col_blocks, col_blocks, 1), (threadsPerBlock, 1, 1), self.stream, box_count, threshold, detections.gpudata, output_mask.gpudata, normalized]
    kernel = nms._get_nms_kernel()
    kernel.prepared_async_call(*params)
    mask_cpu = output_mask.get().ravel()
    scores = detections[:, (-1)].get()
    num_to_keep = 0
    keep = np.zeros(box_count, dtype=np.int32)
    remv = np.zeros(col_blocks, dtype=np.uint32)
    for i in range(box_count):
        nblock = int((i / threadsPerBlock))
        inblock = int((i % threadsPerBlock))
        if (((remv[nblock] & (1 << inblock)) == 0) and (scores[i] != 0)):
            keep[num_to_keep] = i
            num_to_keep += 1
            for j in range(nblock, col_blocks):
                remv[j] |= mask_cpu[((i * col_blocks) + j)]
    return keep[:num_to_keep].tolist()
