def match_targets_to_proposals(self, proposal, target): match_quality_matrix = boxlist_iou_3d(target, proposal, aug_thickness=self.aug_thickness, criterion=-1, flag='roi_label_generation') matched_idxs = self.proposal_matcher(match_quality_matrix, yaw_diff=None, flag='ROI') # Fast RCNN only need "labels" field for selecting the targets target = target.copy_with_fields("labels") # get the targets corresponding GT for each proposal # NB: need to clamp the indices because we can have a single # GT in the image, and matched_idxs can be -2, which goes # out of bounds matched_targets = target[matched_idxs.clamp(min=0)] matched_targets.add_field("matched_idxs", matched_idxs) if CHECK_IOU: num_gt = len(target) if not torch.all( matched_idxs[-num_gt:].cpu() == torch.arange(num_gt)): ious = match_quality_matrix[:, -num_gt:].diag() err_inds = torch.nonzero( torch.abs(ious - 1) > 1e-5).view(-1) - len(ious) print(f"IOU error: \n{ious}") err_targets = target[err_inds] ious__ = boxlist_iou_3d(err_targets, err_targets, 0) print(err_targets.bbox3d) import pdb pdb.set_trace() # XXX BREAKPOINT assert False pass return matched_targets
def match_targets_to_proposals(self, proposal, target): match_quality_matrix = boxlist_iou_3d(target, proposal) matched_idxs = self.proposal_matcher(match_quality_matrix) # Mask RCNN needs "labels" and "masks "fields for creating the targets target = target.copy_with_fields(["labels", "masks"]) # get the targets corresponding GT for each proposal # NB: need to clamp the indices because we can have a single # GT in the image, and matched_idxs can be -2, which goes # out of bounds matched_targets = target[matched_idxs.clamp(min=0)] matched_targets.add_field("matched_idxs", matched_idxs) return matched_targets
def match_targets_to_anchors(self, anchor, target): from utils3d.geometric_torch import angle_dif if target.bbox3d.shape[0] == 0: matched_idxs = torch.ones([anchor.bbox3d.shape[0]], dtype=torch.int64, device=anchor.bbox3d.device) * (-1) matched_targets = anchor else: match_quality_matrix = boxlist_iou_3d( target, anchor, aug_thickness=self.aug_thickness, criterion=2, flag='rpn_label_generation') yaw_diff = angle_dif(anchor.bbox3d[:, -1].view(1, -1), target.bbox3d[:, -1].view(-1, 1), 0) yaw_diff = torch.abs(yaw_diff) cendis = anchor.bbox3d[:, 0:3].view( 1, -1, 3) - target.bbox3d[:, 0:3].view(-1, 1, 3) cendis = cendis.norm(dim=2) matched_idxs = self.proposal_matcher(match_quality_matrix, yaw_diff=yaw_diff, flag='RPN', cendis=cendis) #anchor.show__together(target, 200) # RPN doesn't need any fields from target # for creating the labels, so clear them all target = target.copy() # get the targets corresponding GT for each anchor # NB: need to clamp the indices because we can have a single # GT in the image, and matched_idxs can be -2, which goes # out of bounds matched_targets = target[matched_idxs.clamp(min=0)] matched_targets.add_field("matched_idxs", matched_idxs) if SHOW_POS_NEG_ANCHORS: self.matched_idxs = matched_idxs self.matched_iou = match_quality_matrix.max(0)[0] if SHOW_IGNORED_ANCHOR: sampled_ign_inds = torch.nonzero(matched_idxs == -2).squeeze(1) anchors_ign = anchor[sampled_ign_inds] ignored_iou = match_quality_matrix.max(0)[0][sampled_ign_inds] print(f'\n ignore {len(anchors_ign)} anchors') print(f'\n ignore ious:\n{ignored_iou}') anchors_ign.show__together(target) #for i in range(0, len(anchors_ign), 3): # ids_ign = [j for j in range(i, i+3)] # print(f'iou: {ignored_iou[ids_ign]}') # anchors_ign[ids_ign].show__together(target) pass if CHECK_MATCHER and target.bbox3d.shape[0] > 0: check_matcher(target, anchor, match_quality_matrix, matched_idxs, self.dset_metas) if SHOW_POS_ANCHOR_IOU_SAME_LOC: num_gt = target.bbox3d.shape[0] for j in range(num_gt): sampled_pos_inds = torch.nonzero(matched_idxs == j).squeeze(1) inds_same_loc = anchor.same_loc_anchors(sampled_pos_inds) matched_idxs_same_loc = matched_idxs[inds_same_loc] # all the ious for gt box j iou_j = match_quality_matrix[j][sampled_pos_inds] anchors_pos_j = anchor[sampled_pos_inds] print( f'\n{iou_j.shape[0]} anchor matched as positive. All anchor centroids are shown.' ) print(f'ious:{iou_j}\n') anchors_pos_j.show__together(target[j], points=anchor.bbox3d[:, 0:3]) for i in range(iou_j.shape[0]): print(f'\n{i}th pos anchor for gt box j\n iou: {iou_j[i]}') #anchors_pos_j[i].show__together(target[j]) ious_same_loc = match_quality_matrix[j][inds_same_loc[i]] yaw_diff_same_loc = yaw_diff[j, inds_same_loc[i]] print( f'\nall same loc anchors \nious:{ious_same_loc}\nmatched_idxs:{matched_idxs_same_loc[i]}\nyaw_diff:{yaw_diff_same_loc}' ) print(f'-1:low, -2:between') anchors_same_loc_i = anchor[inds_same_loc[i]] anchors_same_loc_i.show__together(target[j]) import pdb pdb.set_trace() # XXX BREAKPOINT pass pass return matched_targets
def calc_detection_suncg_prec_rec(gt_boxlists, pred_boxlists, iou_thresh, dset_metas, eval_aug_thickness): """Calculate precision and recall based on evaluation code of PASCAL VOC. This function calculates precision and recall of predicted bounding boxes obtained from a dataset which has :math:`N` images. The code is based on the evaluation code used in PASCAL VOC Challenge. """ n_pos = defaultdict(int) score = defaultdict(list) # The pred having maximum iou with a gt is matched with the gt. # If multiple preds share same maximum iou gt, the one with highest score is # selected. NOTICE HERE, not the one with highest iou! Because, in test, # only score is available. match = defaultdict(list) # 1:true, 0:false, -1:ignore predious = defaultdict(list) pred_for_each_gt = defaultdict(list) batch_size = len(gt_boxlists) bi = -1 for gt_boxlist, pred_boxlist in zip(gt_boxlists, pred_boxlists): bi += 1 pred_bbox = pred_boxlist.bbox3d.numpy() pred_label = pred_boxlist.get_field("labels").numpy() pred_score = pred_boxlist.get_field("scores").numpy() gt_bbox = gt_boxlist.bbox3d.numpy() gt_label = gt_boxlist.get_field("labels").numpy() for l in np.unique(np.concatenate((pred_label, gt_label)).astype(int)): obj_name = dset_metas.label_2_class[l] pred_mask_l = pred_label == l pred_ids_l = np.where(pred_mask_l)[0] pred_bbox_l = pred_bbox[pred_mask_l] pred_score_l = pred_score[pred_mask_l] # sort by score order = pred_score_l.argsort()[::-1] pred_bbox_l = pred_bbox_l[order] pred_score_l = pred_score_l[order] gt_mask_l = gt_label == l # Extract gt only of current class, thus gt_index is the index # inside of one signle class gts, not of all gts # TAG: GT_MASK gt_bbox_l = gt_bbox[gt_mask_l] n_pos[l] += gt_bbox_l.shape[0] score[l].extend(pred_score_l) if len(pred_bbox_l) == 0: continue if len(gt_bbox_l) == 0: match[l].extend((0, ) * pred_bbox_l.shape[0]) predious[l].extend((0, ) * pred_bbox_l.shape[0]) continue pred_bbox_l = pred_bbox_l.copy() gt_bbox_l = gt_bbox_l.copy() iou = boxlist_iou_3d(BoxList3D(gt_bbox_l, gt_boxlist.size3d, gt_boxlist.mode, None, gt_boxlist.constants), BoxList3D(pred_bbox_l, pred_boxlist.size3d, pred_boxlist.mode, None, pred_boxlist.constants), aug_thickness=eval_aug_thickness, criterion=-1, flag='eval').numpy() # [gt_nm,pred_num] gt_index = iou.argmax(axis=0) # the gt index for each predicion # set -1 if there is no matching ground truth gt_index[iou.max(axis=0) < iou_thresh] = -1 pred_for_each_gt_l = defaultdict(list) neg_count = 0 for pi in range(gt_index.shape[0]): pis = { 'pred_idx': pred_ids_l[pi], 'iou': iou[gt_index[pi], pi], 'score': score[l][pi] } gt_idx = gt_index[pi] if gt_idx < 0: neg_count += 1 gt_idx -= (gt_idx == -1) * neg_count pred_for_each_gt_l[gt_idx].append(pis) if obj_name not in pred_for_each_gt: for iii in range(batch_size): pred_for_each_gt[obj_name].append(defaultdict(list)) pred_for_each_gt[obj_name][bi] = pred_for_each_gt_l predious[l].extend(iou.max(0)) selec = np.zeros(gt_bbox_l.shape[0], dtype=bool) for gt_idx in gt_index: if gt_idx >= 0: if not selec[gt_idx]: # gt_index is already sorted by scores, # thus the first pred match a gt box is set 1 match[l].append(1) else: match[l].append(0) selec[gt_idx] = True else: match[l].append(0) del iou pass n_fg_class = max(n_pos.keys()) + 1 prec = [None] * n_fg_class rec = [None] * n_fg_class scores = [None] * n_fg_class pred_ious = [None] * n_fg_class for l in n_pos.keys(): score_l = np.array(score[l]) if score_l.shape[0] == 0: continue match_l = np.array(match[l], dtype=np.int8) order = score_l.argsort()[::-1] scores[l] = score_l[order] match_l = match_l[order] predious[l] = np.array(predious[l], dtype=np.float) if not predious[l].shape[0] == order.shape[0]: import pdb pdb.set_trace() # XXX BREAKPOINT pass pred_ious[l] = predious[l][order] tp = np.cumsum(match_l == 1) fp = np.cumsum(match_l == 0) # If an element of fp + tp is 0, # the corresponding element of prec[l] is nan. prec[l] = tp / (fp + tp) # If n_pos[l] is 0, rec[l] is None. #if n_pos[l] > 0: rec[l] = tp / n_pos[l] #plt.plot(rec[1], label='rec') #plt.plot(prec[1], label='prec') #plt.plot(scores[1], label='score') #plt.legend() #plt.show() return prec, rec, pred_for_each_gt, scores, pred_ious
def calc_detection_suncg_prec_rec(gt_boxlists, pred_boxlists, iou_thresh=0.5): """Calculate precision and recall based on evaluation code of PASCAL VOC. This function calculates precision and recall of predicted bounding boxes obtained from a dataset which has :math:`N` images. The code is based on the evaluation code used in PASCAL VOC Challenge. """ n_pos = defaultdict(int) score = defaultdict(list) match = defaultdict(list) for gt_boxlist, pred_boxlist in zip(gt_boxlists, pred_boxlists): pred_bbox = pred_boxlist.bbox.numpy() pred_label = pred_boxlist.get_field("labels").numpy() pred_score = pred_boxlist.get_field("scores").numpy() gt_bbox = gt_boxlist.bbox.numpy() gt_label = gt_boxlist.get_field("labels").numpy() gt_difficult = gt_boxlist.get_field("difficult").numpy() for l in np.unique(np.concatenate((pred_label, gt_label)).astype(int)): pred_mask_l = pred_label == l pred_bbox_l = pred_bbox[pred_mask_l] pred_score_l = pred_score[pred_mask_l] # sort by score order = pred_score_l.argsort()[::-1] pred_bbox_l = pred_bbox_l[order] pred_score_l = pred_score_l[order] gt_mask_l = gt_label == l gt_bbox_l = gt_bbox[gt_mask_l] gt_difficult_l = gt_difficult[gt_mask_l] n_pos[l] += np.logical_not(gt_difficult_l).sum() score[l].extend(pred_score_l) if len(pred_bbox_l) == 0: continue if len(gt_bbox_l) == 0: match[l].extend((0, ) * pred_bbox_l.shape[0]) continue # VOC evaluation follows integer typed bounding boxes. pred_bbox_l = pred_bbox_l.copy() pred_bbox_l[:, 2:] += 1 gt_bbox_l = gt_bbox_l.copy() gt_bbox_l[:, 2:] += 1 iou = boxlist_iou_3d( BoxList3D(pred_bbox_l, gt_boxlist.size), BoxList3D(gt_bbox_l, gt_boxlist.size), ).numpy() gt_index = iou.argmax(axis=1) # set -1 if there is no matching ground truth gt_index[iou.max(axis=1) < iou_thresh] = -1 del iou selec = np.zeros(gt_bbox_l.shape[0], dtype=bool) for gt_idx in gt_index: if gt_idx >= 0: if gt_difficult_l[gt_idx]: match[l].append(-1) else: if not selec[gt_idx]: match[l].append(1) else: match[l].append(0) selec[gt_idx] = True else: match[l].append(0) n_fg_class = max(n_pos.keys()) + 1 prec = [None] * n_fg_class rec = [None] * n_fg_class for l in n_pos.keys(): score_l = np.array(score[l]) match_l = np.array(match[l], dtype=np.int8) order = score_l.argsort()[::-1] match_l = match_l[order] tp = np.cumsum(match_l == 1) fp = np.cumsum(match_l == 0) # If an element of fp + tp is 0, # the corresponding element of prec[l] is nan. prec[l] = tp / (fp + tp) # If n_pos[l] is 0, rec[l] is None. if n_pos[l] > 0: rec[l] = tp / n_pos[l] return prec, rec