@numba.jit(nopython=True)
def iou_nd_jit(boxes, query_boxes, add1=True):
    'calculate box iou nd, 2x slower than iou_jit.\n    ----------\n    boxes: (N, ndim * 2) ndarray of float\n    query_boxes: (K, ndim * 2) ndarray of float\n    Returns\n    -------\n    overlaps: (N, K) ndarray of overlap between boxes and query_boxes\n    '
    N = boxes.shape[0]
    K = query_boxes.shape[0]
    ndim = (boxes.shape[1] // 2)
    overlaps = np.zeros((N, K), dtype=boxes.dtype)
    side_lengths = np.zeros((ndim,), dtype=boxes.dtype)
    if add1:
        add1 = 1.0
    else:
        add1 = 0.0
    invalid = False
    for k in range(K):
        qbox_area = ((query_boxes[(k, ndim)] - query_boxes[(k, 0)]) + add1)
        for i in range(1, ndim):
            qbox_area *= ((query_boxes[(k, (ndim + i))] - query_boxes[(k, i)]) + add1)
        for n in range(N):
            invalid = False
            for i in range(ndim):
                side_length = ((min(boxes[(n, (i + ndim))], query_boxes[(k, (i + ndim))]) - max(boxes[(n, i)], query_boxes[(k, i)])) + add1)
                if (side_length <= 0):
                    invalid = True
                    break
                side_lengths[i] = side_length
            if (not invalid):
                box_area = ((boxes[(n, ndim)] - boxes[(n, 0)]) + add1)
                for i in range(1, ndim):
                    box_area *= ((boxes[(n, (ndim + i))] - boxes[(n, i)]) + add1)
                inter = side_lengths[0]
                for i in range(1, ndim):
                    inter *= side_lengths[i]
                ua = float(((box_area + qbox_area) - inter))
                overlaps[(n, k)] = (inter / ua)
    return overlaps
calculate box iou nd, 2x slower than iou_jit.
----------
boxes: (N, ndim * 2) ndarray of float
query_boxes: (K, ndim * 2) ndarray of float
Returns
-------
overlaps: (N, K) ndarray of overlap between boxes and query_boxes
@numba.jit(nopython=True)
def iou_nd_jit(boxes, query_boxes, add1=True):
    'calculate box iou nd, 2x slower than iou_jit.\n    ----------\n    boxes: (N, ndim * 2) ndarray of float\n    query_boxes: (K, ndim * 2) ndarray of float\n    Returns\n    -------\n    overlaps: (N, K) ndarray of overlap between boxes and query_boxes\n    '
    N = boxes.shape[0]
    K = query_boxes.shape[0]
    ndim = (boxes.shape[1] // 2)
    overlaps = np.zeros((N, K), dtype=boxes.dtype)
    side_lengths = np.zeros((ndim,), dtype=boxes.dtype)
    if add1:
        add1 = 1.0
    else:
        add1 = 0.0
    invalid = False
    for k in range(K):
        qbox_area = ((query_boxes[(k, ndim)] - query_boxes[(k, 0)]) + add1)
        for i in range(1, ndim):
            qbox_area *= ((query_boxes[(k, (ndim + i))] - query_boxes[(k, i)]) + add1)
        for n in range(N):
            invalid = False
            for i in range(ndim):
                side_length = ((min(boxes[(n, (i + ndim))], query_boxes[(k, (i + ndim))]) - max(boxes[(n, i)], query_boxes[(k, i)])) + add1)
                if (side_length <= 0):
                    invalid = True
                    break
                side_lengths[i] = side_length
            if (not invalid):
                box_area = ((boxes[(n, ndim)] - boxes[(n, 0)]) + add1)
                for i in range(1, ndim):
                    box_area *= ((boxes[(n, (ndim + i))] - boxes[(n, i)]) + add1)
                inter = side_lengths[0]
                for i in range(1, ndim):
                    inter *= side_lengths[i]
                ua = float(((box_area + qbox_area) - inter))
                overlaps[(n, k)] = (inter / ua)
    return overlaps