|
| 1 | +import numpy as np |
| 2 | + |
| 3 | + |
| 4 | +def box_iou_calc(boxes1, boxes2): |
| 5 | + # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py |
| 6 | + """ |
| 7 | + Return intersection-over-union (Jaccard index) of boxes. |
| 8 | + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. |
| 9 | + Arguments: |
| 10 | + boxes1 (Array[N, 4]) |
| 11 | + boxes2 (Array[M, 4]) |
| 12 | + Returns: |
| 13 | + iou (Array[N, M]): the NxM matrix containing the pairwise |
| 14 | + IoU values for every element in boxes1 and boxes2 |
| 15 | +
|
| 16 | + This implementation is taken from the above link and changed so that it only uses numpy.. |
| 17 | + """ |
| 18 | + |
| 19 | + def box_area(box): |
| 20 | + # box = 4xn |
| 21 | + return (box[2] - box[0]) * (box[3] - box[1]) |
| 22 | + |
| 23 | + |
| 24 | + area1 = box_area(boxes1.T) |
| 25 | + area2 = box_area(boxes2.T) |
| 26 | + |
| 27 | + lt = np.maximum(boxes1[:, None, :2], boxes2[:, :2]) # [N,M,2] |
| 28 | + rb = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:]) # [N,M,2] |
| 29 | + |
| 30 | + inter = np.prod(np.clip(rb - lt, a_min = 0, a_max = None), 2) |
| 31 | + return inter / (area1[:, None] + area2 - inter) # iou = inter / (area1 + area2 - inter) |
| 32 | + |
| 33 | + |
| 34 | +class ConfusionMatrix: |
| 35 | + def __init__(self, num_classes, CONF_THRESHOLD = 0.3, IOU_THRESHOLD = 0.5): |
| 36 | + self.matrix = np.zeros((num_classes + 1, num_classes + 1)) |
| 37 | + self.num_classes = num_classes |
| 38 | + self.CONF_THRESHOLD = CONF_THRESHOLD |
| 39 | + self.IOU_THRESHOLD = IOU_THRESHOLD |
| 40 | + |
| 41 | + def process_batch(self, detections, labels): |
| 42 | + ''' |
| 43 | + Return intersection-over-union (Jaccard index) of boxes. |
| 44 | + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. |
| 45 | + Arguments: |
| 46 | + detections (Array[N, 6]), x1, y1, x2, y2, conf, class |
| 47 | + labels (Array[M, 5]), x1, y1, x2, y2, class |
| 48 | + Returns: |
| 49 | + None, updates confusion matrix accordingly |
| 50 | + ''' |
| 51 | + detections = detections[detections[:, 4] > self.CONF_THRESHOLD] |
| 52 | + gt_classes = labels[:, 0].astype(np.int16) |
| 53 | + detection_classes = detections[:, 5].astype(np.int16) |
| 54 | + |
| 55 | + all_ious = box_iou_calc(labels[:, 1:], detections[:, :4]) |
| 56 | + want_idx = np.where(all_ious > self.IOU_THRESHOLD) |
| 57 | + |
| 58 | + all_matches = [] |
| 59 | + for i in range(want_idx[0].shape[0]): |
| 60 | + all_matches.append([want_idx[0][i], want_idx[1][i], all_ious[want_idx[0][i], want_idx[1][i]]]) |
| 61 | + |
| 62 | + all_matches = np.array(all_matches) |
| 63 | + if all_matches.shape[0] > 0: # if there is match |
| 64 | + all_matches = all_matches[all_matches[:, 2].argsort()[::-1]] |
| 65 | + |
| 66 | + all_matches = all_matches[np.unique(all_matches[:, 1], return_index = True)[1]] |
| 67 | + |
| 68 | + all_matches = all_matches[all_matches[:, 2].argsort()[::-1]] |
| 69 | + |
| 70 | + all_matches = all_matches[np.unique(all_matches[:, 0], return_index = True)[1]] |
| 71 | + |
| 72 | + |
| 73 | + for i, label in enumerate(labels): |
| 74 | + if all_matches.shape[0] > 0 and all_matches[all_matches[:, 0] == i].shape[0] == 1: |
| 75 | + gt_class = gt_classes[i] |
| 76 | + detection_class = detection_classes[int(all_matches[all_matches[:, 0] == i, 1][0])] |
| 77 | + self.matrix[(gt_class), detection_class] += 1 |
| 78 | + else: |
| 79 | + gt_class = gt_classes[i] |
| 80 | + self.matrix[(gt_class), self.num_classes] += 1 |
| 81 | + |
| 82 | + for i, detection in enumerate(detections): |
| 83 | + if all_matches.shape[0] and all_matches[all_matches[:, 1] == i].shape[0] == 1: |
| 84 | + detection_class = detection_classes[i] |
| 85 | + self.matrix[self.num_classes ,detection_class] += 1 |
| 86 | + |
| 87 | + def return_matrix(self): |
| 88 | + return self.matrix |
| 89 | + |
| 90 | + def print_matrix(self): |
| 91 | + for i in range(self.num_classes + 1): |
| 92 | + print(' '.join(map(str, self.matrix[i]))) |
| 93 | + |
0 commit comments