def get_windows(img_W, img_H, sizes, steps, in_rate_thr=0.6): assert 1 >= in_rate_thr >= 0, 'The `in_rate_thr` should lie in 0~1' windows = [] for size, step in zip(sizes, steps): assert size > step, 'Size should large than step' x_num = 1 if img_W <= size else ceil((img_W - size) / step + 1) x_start = [step * i for i in range(x_num)] if len(x_start) > 1 and x_start[-1] + size > img_W: x_start[-1] = img_W - size y_num = 1 if img_H <= size else ceil((img_H - size) / step + 1) y_start = [step * i for i in range(y_num)] if len(y_start) > 1 and y_start[-1] + size > img_H: y_start[-1] = img_H - size start = np.array(list(product(x_start, y_start)), dtype=np.int64) windows.append(np.concatenate([start, start + size], axis=1)) windows = np.concatenate(windows, axis=0) img_contour = np.array([[0, 0, img_W, img_H]]) win_iofs = bt.bbox_overlaps(windows, img_contour, mode='iof').reshape(-1) if not np.any(win_iofs >= in_rate_thr): win_iofs[abs(win_iofs - win_iofs.max()) < 0.01] = 1 return windows[win_iofs >= in_rate_thr]
def __call__(self, results): results['rotate_after_flip'] = self.rotate_after_flip if 'angle' not in results: results['angle'] = self.get_random_angle(results) if results['angle'] == 0: results['matrix'] = np.eye(3) return results matrix, w, h = self.get_matrix_and_size(results) results['matrix'] = matrix img_bound = np.array([[0, 0, w, 0, w, h, 0, h]]) self.base_rotate(results, matrix, w, h, img_bound) for k in results.get('img_fields', []): if k != 'img': results[k] = cv2.warpAffine(results[k], matrix, (w, h)) for k in results.get('bbox_fields', []): if k == 'gt_bboxes': continue warped_bboxes = bt.warp(results[k], matrix, keep_type=True) if self.keep_shape: iofs = bt.bbox_overlaps(warped_bboxes, img_bound, mode='iof') warped_bboxes = warped_bboxes[iofs[:, 0] > self.keep_iof_thr] results[k] = warped_bboxes for k in results.get('mask_fields', []): if k == 'gt_masks': continue polys = switch_mask_type(results[k], 'polygon') warped_polys = rotate_polygonmask(polys, matrix, w, h) if self.keep_shape: obbs = mask2bbox(warped_polys, 'obb') iofs = bt.bbox_overlaps(obbs, img_bound, mode='iof') index = np.nonzero(iofs[:, 0] > self.keep_iof_thr)[0] warped_polys = warped_polys[index] if isinstance(results[k], BitmapMasks): results[k] = switch_mask_type(warped_polys, 'bitmap') elif isinstance(results[k], PolygonMasks): results[k] = switch_mask_type(warped_polys, 'polygon') else: raise NotImplementedError for k in results.get('seg_fields', []): results[k] = cv2.warpAffine(results[k], matrix, (w, h)) return results
def base_rotate(self, results, matrix, w, h, img_bound): if 'img' in results: img = cv2.warpAffine(results['img'], matrix, (w, h)) results['img'] = img results['img_shape'] = img.shape if 'gt_masks' in results: polygons = switch_mask_type(results['gt_masks'], 'polygon') warped_polygons = rotate_polygonmask(polygons, matrix, w, h) if self.keep_shape: obbs = mask2bbox(warped_polygons, 'obb') iofs = bt.bbox_overlaps(obbs, img_bound, mode='iof') if_inwindow = iofs[:, 0] > self.keep_iof_thr index = np.nonzero(if_inwindow)[0] warped_polygons = warped_polygons[index] if isinstance(results['gt_masks'], BitmapMasks): results['gt_masks'] = switch_mask_type(warped_polygons, 'bitmap') elif isinstance(results['gt_masks'], PolygonMasks): results['gt_masks'] = switch_mask_type(warped_polygons, 'polygon') else: raise NotImplementedError if 'gt_bboxes' in results: results['gt_bboxes'] = mask2bbox(warped_polygons, 'hbb') elif 'gt_bboxes' in results: warped_bboxes = bt.warp(results['gt_bboxes'], matrix, keep_type=True) if self.keep_shape: iofs = bt.bbox_overlaps(warped_bboxes, img_bound, mode='iof') if_inwindow = iofs[:, 0] > self.keep_iof_thr # if ~if_inwindow.any(): # return True warped_bboxes = warped_bboxes[if_inwindow] results['gt_bboxes'] = warped_bboxes if 'gt_labels' in results and self.keep_shape: results['gt_labels'] = results['gt_labels'][if_inwindow] for k in results.get('aligned_fields', []): if self.keep_shape: results[k] = results[k][if_inwindow]
def eval_arb_recalls(gts, proposals, with_scores=True, proposal_nums=None, iou_thrs=0.5, logger=None): """Calculate recalls. Args: gts (list[ndarray]): a list of arrays of shape (n, 4) proposals (list[ndarray]): a list of arrays of shape (k, 4) or (k, 5) proposal_nums (int | Sequence[int]): Top N proposals to be evaluated. iou_thrs (float | Sequence[float]): IoU thresholds. Default: 0.5. logger (logging.Logger | str | None): The way to print the recall summary. See `mmdet.utils.print_log()` for details. Default: None. Returns: ndarray: recalls of different ious and proposal nums """ img_num = len(gts) assert img_num == len(proposals) proposal_nums, iou_thrs = set_recall_param(proposal_nums, iou_thrs) all_ious = [] for i in range(img_num): if proposals[i].ndim == 2 and with_scores: scores = proposals[i][:, -1] sort_idx = np.argsort(scores)[::-1] img_proposal = proposals[i][sort_idx, :] else: img_proposal = proposals[i] if with_scores: img_proposal = img_proposal[:, :-1] prop_num = min(img_proposal.shape[0], proposal_nums[-1]) if gts[i] is None or gts[i].shape[0] == 0: ious = np.zeros((0, img_proposal.shape[0]), dtype=np.float32) else: ious = bt.bbox_overlaps(gts[i], img_proposal[:prop_num]) all_ious.append(ious) all_ious = np.array(all_ious) recalls = _recalls(all_ious, proposal_nums, iou_thrs) print_recall_summary(recalls, proposal_nums, iou_thrs, logger=logger) return recalls
def get_window_obj(info, windows, iof_thr): bboxes = info['ann']['bboxes'] iofs = bt.bbox_overlaps(bboxes, windows, mode='iof') window_anns = [] for i in range(windows.shape[0]): win_iofs = iofs[:, i] pos_inds = np.nonzero(win_iofs >= iof_thr)[0].tolist() win_ann = dict() for k, v in info['ann'].items(): try: win_ann[k] = v[pos_inds] except TypeError: win_ann[k] = [v[i] for i in pos_inds] win_ann['trunc'] = win_iofs[pos_inds] < 1 window_anns.append(win_ann) return window_anns
def __call__(self, bboxes1, bboxes2, mode='iou', is_aligned=False): """Calculate IoU between 2D bboxes Args: bboxes1 (Tensor): bboxes have shape (m, 4) in <x1, y1, x2, y2> format, or shape (m, 5) in <x1, y1, x2, y2, score> format. bboxes2 (Tensor): bboxes have shape (m, 4) in <x1, y1, x2, y2> format, shape (m, 5) in <x1, y1, x2, y2, score> format, or be empty. If is_aligned is ``True``, then m and n must be equal. mode (str): "iou" (intersection over union) or iof (intersection over foreground). Returns: ious(Tensor): shape (m, n) if is_aligned == False else shape (m, 1) """ assert bboxes1.size(-1) in [0, 8, 9] assert bboxes2.size(-1) in [0, 8, 9] if bboxes2.size(-1) == 9: bboxes2 = bboxes2[..., :8] if bboxes1.size(-1) == 9: bboxes1 = bboxes1[..., :8] return bt.bbox_overlaps(bboxes1, bboxes2, mode, is_aligned)
def tpfp_default(det_bboxes, gt_bboxes, gt_bboxes_ignore=None, iou_thr=0.5, area_ranges=None): """Check if detected bboxes are true positive or false positive. Args: det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, of shape (k, 4). Default: None iou_thr (float): IoU threshold to be considered as matched. Default: 0.5. area_ranges (list[tuple] | None): Range of bbox areas to be evaluated, in the format [(min1, max1), (min2, max2), ...]. Default: None. Returns: tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of each array is (num_scales, m). """ # an indicator of ignored gts gt_ignore_inds = np.concatenate((np.zeros(gt_bboxes.shape[0], dtype=np.bool), np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) # stack gt_bboxes and gt_bboxes_ignore for convenience gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) num_dets = det_bboxes.shape[0] num_gts = gt_bboxes.shape[0] if area_ranges is None: area_ranges = [(None, None)] num_scales = len(area_ranges) # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of # a certain scale tp = np.zeros((num_scales, num_dets), dtype=np.float32) fp = np.zeros((num_scales, num_dets), dtype=np.float32) # if there is no gt bboxes in this image, then all det bboxes # within area range are false positives if gt_bboxes.shape[0] == 0: if area_ranges == [(None, None)]: fp[...] = 1 else: det_areas = bt.bbox_areas( det_bboxes[:, :-1] ) #------------------------------------------------- for i, (min_area, max_area) in enumerate(area_ranges): fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 return tp, fp ious = bt.bbox_overlaps(det_bboxes[:, :-1], gt_bboxes) #------------------------------ # for each det, the max iou with all gts ious_max = ious.max(axis=1) # for each det, which gt overlaps most with it ious_argmax = ious.argmax(axis=1) # sort all dets in descending order by scores sort_inds = np.argsort(-det_bboxes[:, -1]) for k, (min_area, max_area) in enumerate(area_ranges): gt_covered = np.zeros(num_gts, dtype=bool) # if no area range is specified, gt_area_ignore is all False if min_area is None: gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) else: gt_areas = bt.bbox_areas( gt_bboxes) #------------------------------------ gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) for i in sort_inds: if ious_max[i] >= iou_thr: matched_gt = ious_argmax[i] if not (gt_ignore_inds[matched_gt] or gt_area_ignore[matched_gt]): if not gt_covered[matched_gt]: gt_covered[matched_gt] = True tp[k, i] = 1 else: fp[k, i] = 1 # otherwise ignore this detected bbox, tp = 0, fp = 0 elif min_area is None: fp[k, i] = 1 else: bbox = det_bboxes[i:i + 1, :-1] area = bt.bbox_areas( bbox) #-------------------------------------------- if area >= min_area and area < max_area: fp[k, i] = 1 return tp, fp