```python def iou(box0, box1): # box0: [x,y,z,rr], where (x,y,z) is the center of a circle; r is the radius r0 = box0[3] / 2 # half of the radius s0 = box0[:3] - r0 # convert `center` to a `lower-left vertice of a box`; i.e. (x,y,z) -> (x-r, y-r, z-r) e0 = box0[:3] + r0 # convert `center` to a `top-right vertice of a box`; i.e. (x,y,z) -> (x+r, y+r, z+r) r1 = box1[3] / 2 s1 = box1[:3] - r1 e1 = box1[:3] + r1 overlap = [] for i in range(len(s0)): overlap.append(max(0, min(e0[i], e1[i]) - max(s0[i], s1[i]))) # this is like the 2D box-matching case: using min and max operations # to get the size of overlap region. To understand it, see tutorials on 2D IoU calculations intersection = overlap[0] * overlap[1] * overlap[2] union = box0[3] * box0[3] * box0[3] + box1[3] * box1[3] * box1[3] - intersection return intersection / union # return IoU ``` ```python class GetPBB(object): """Class for Getting the Predicted Bounding Boxes? """ def __init__(self, config): self.stride = config['stride'] # if `stride=1` and `anchors=4`, we will have 4 anchors at EVERY voxel locations. self.anchors = np.asarray(config['anchors']) # possible radiuses of the anchors. len(anchors) = number of anchors for each chosen voxel. def __call__(self, output,thresh = -3, ismask=False): # I believe `output` has the shape: [Z, H, W, num_anchors_per_voxel, 5], # where the 5 elements in the last dimension are `confidence`, `(x-xa)/ra`, `(y-ya)/ra`, `(z-za)/ra` and `log(r/ra)`. # If confidence is large, then, it means that we believe there's an object inside the box. stride = self.stride anchors = self.anchors output = np.copy(output) offset = (float(stride) - 1) / 2 output_size = output.shape # A pre-defined anchor position will be at (z,h,w) oz = np.arange(offset, offset + stride * (output_size[0] - 1) + 1, stride) # stores anchor z positions; [num_z,]. Consider a simple case, that offset=0, stride=1; then, we have oz = [0,1,2,...,output_size[0]]. oh = np.arange(offset, offset + stride * (output_size[1] - 1) + 1, stride) # stores anchor h positions; [num_h,] ow = np.arange(offset, offset + stride * (output_size[2] - 1) + 1, stride) # stores anchor w positions; [num_w,] output[:, :, :, :, 1] = oz.reshape((-1, 1, 1, 1)) + output[:, :, :, :, 1] * anchors.reshape((1, 1, 1, -1)) output[:, :, :, :, 2] = oh.reshape((1, -1, 1, 1)) + output[:, :, :, :, 2] * anchors.reshape((1, 1, 1, -1)) output[:, :, :, :, 3] = ow.reshape((1, 1, -1, 1)) + output[:, :, :, :, 3] * anchors.reshape((1, 1, 1, -1)) output[:, :, :, :, 4] = np.exp(output[:, :, :, :, 4]) * anchors.reshape((1, 1, 1, -1)) mask = output[..., 0] > thresh # I think this is for `non-maximum supression`. xx,yy,zz,aa = np.where(mask) # I believe it should be zz,hh,ww,aa; isn't it ???? output = output[xx,yy,zz,aa] # after nms, the output tensor has the shape: [num_anchors, 4], # where the last dimension stores the predicted x,y,z,r. if ismask: return output,[xx,yy,zz,aa] else: return output # [num_anchors, 5], where the last 5 elements are `confidence`, `x`, `y`, `z` and `r`. ```