Example #1
0
    def __call__(self, results):
        gt_bboxes = results['gt_bboxes']
        if len(gt_bboxes) <= self.n:
            return results

        if 'gt_masks' in results:
            areas = results['gt_masks'].areas
        else:
            areas = bt.bbox_areas(gt_bboxes)

        index = np.argsort(areas)[:self.n]
        results['gt_bboxes'] = gt_bboxes[index]
        if 'gt_labels' in results:
            results['gt_labels'] = results['gt_labels'][index]
        if 'gt_masks' in results:
            results['gt_masks'] = results['gt_masks'][index]

        return results
Example #2
0
    def __call__(self, results):
        for k in ['gt_bboxes', 'gt_masks', 'gt_labels']:
            if k in results:
                num_objs = len(results[k])
                break
        else:
            return results

        ignore = np.zeros((num_objs, ), dtype=np.bool)
        if self.ignore_diff:
            assert 'diffs' in results
            diffs = results['diffs']
            ignore[diffs == 1] = True

        if self.ignore_truncated:
            assert 'trunc' in results
            trunc = results['trunc']
            ignore[trunc == 1] = True

        if self.ignore_size:
            bboxes = results['gt_bboxes']
            wh = bboxes[:, 2:] - bboxes[:, :2]
            ignore[np.min(wh, axis=1) < self.ignore_size] = True

        if self.ignore_real_scales:
            assert len(self.ignore_real_scales) == (len(results['split_sizes']) *
                                                    len(results['split_rates']))
            polys = mask2bbox(results['gt_masks'], 'poly')
            if 'scale_factor' in results:
                scale_factor = np.tile(results['scale_factor'], 2)
                polys = polys / scale_factor
            bbox_scales = np.sqrt(bt.bbox_areas(polys))

            split_sizes=[]
            for rate in results['split_rates']:
                split_sizes += [int(size / rate) for size in results['split_sizes']]
            img_scale = results['img_info']['width']
            scale_ratio = np.array(split_sizes) / img_scale
            inds = np.argmin(abs(np.log(scale_ratio)))
            min_scale, max_scale = self.ignore_real_scales[inds]
            if min_scale is None:
                min_scale = 0
            if max_scale is None:
                max_scale = np.inf
            ignore[bbox_scales < min_scale] = True
            ignore[bbox_scales > max_scale] = True

        if 'gt_bboxes' in results:
            bboxes = results['gt_bboxes']
            gt_bboxes = bboxes[~ignore]
            gt_bboxes_ignore = bboxes[ignore]

            results['gt_bboxes'] = gt_bboxes
            results['gt_bboxes_ignore'] = gt_bboxes_ignore
            if 'gt_bboxes_ignore' not in results['bbox_fields']:
                results['bbox_fields'].append('gt_bboxes_ignore')

        if 'gt_masks' in results:
            gt_inds = np.nonzero(~ignore)[0]
            ignore_inds = np.nonzero(ignore)[0]

            if isinstance(results['gt_masks'], PolygonMasks) \
               and len(gt_inds) == 0:
                height = results['gt_masks'].height
                width = results['gt_masks'].width
                gt_masks = PolygonMasks([], height, width)
            else:
                gt_masks = results['gt_masks'][gt_inds]

            if isinstance(results['gt_masks'], PolygonMasks) \
               and len(ignore_inds) == 0:
                height = results['gt_masks'].height
                width = results['gt_masks'].width
                gt_masks_ignore = PolygonMasks([], height, width)
            else:
                gt_masks_ignore = results['gt_masks'][ignore_inds]

            results['gt_masks'] = gt_masks
            results['gt_masks_ignore'] = gt_masks_ignore
            if 'gt_masks_ignore' not in results['mask_fields']:
                results['mask_fields'].append('gt_masks_ignore')

        if 'gt_labels' in results:
            results['gt_labels'] = results['gt_labels'][~ignore]

        for k in results.get('aligned_fields', []):
            results[k] = results[k][~ignore]

        return results
Example #3
0
def eval_arb_map(det_results,
                 annotations,
                 scale_ranges=None,
                 iou_thr=0.5,
                 use_07_metric=True,
                 dataset=None,
                 logger=None,
                 nproc=4):
    """Evaluate mAP of a dataset.

    Args:
        det_results (list[list]): [[cls1_det, cls2_det, ...], ...].
            The outer list indicates images, and the inner list indicates
            per-class detected bboxes.
        annotations (list[dict]): Ground truth annotations where each item of
            the list indicates an image. Keys of annotations are:

            - `bboxes`: numpy array of shape (n, 4)
            - `labels`: numpy array of shape (n, )
            - `bboxes_ignore` (optional): numpy array of shape (k, 4)
            - `labels_ignore` (optional): numpy array of shape (k, )
        scale_ranges (list[tuple] | None): Range of scales to be evaluated,
            in the format [(min1, max1), (min2, max2), ...]. A range of
            (32, 64) means the area range between (32**2, 64**2).
            Default: None.
        iou_thr (float): IoU threshold to be considered as matched.
            Default: 0.5.
        dataset (list[str] | str | None): Dataset name or dataset classes,
            there are minor differences in metrics for different datsets, e.g.
            "voc07", "imagenet_det", etc. Default: None.
        logger (logging.Logger | str | None): The way to print the mAP
            summary. See `mmdet.utils.print_log()` for details. Default: None.
        nproc (int): Processes used for computing TP and FP.
            Default: 4.

    Returns:
        tuple: (mAP, [dict, dict, ...])
    """
    assert len(det_results) == len(annotations)

    num_imgs = len(det_results)
    num_scales = len(scale_ranges) if scale_ranges is not None else 1
    num_classes = len(det_results[0])  # positive class num
    area_ranges = ([(rg[0]**2, rg[1]**2) for rg in scale_ranges]
                   if scale_ranges is not None else None)

    pool = Pool(nproc)
    eval_results = []
    for i in range(num_classes):
        # get gt and det bboxes of this class
        cls_dets, cls_gts, cls_gts_ignore = get_cls_results(
            det_results, annotations, i)
        # compute tp and fp for each image with multiple processes
        tpfp = pool.starmap(
            tpfp_default,
            zip(cls_dets, cls_gts, cls_gts_ignore,
                [iou_thr for _ in range(num_imgs)],
                [area_ranges for _ in range(num_imgs)]))
        tp, fp = tuple(zip(*tpfp))
        # calculate gt number of each scale
        # ignored gts or gts beyond the specific scale are not counted
        num_gts = np.zeros(num_scales, dtype=int)
        for j, bbox in enumerate(cls_gts):
            if area_ranges is None:
                num_gts[0] += bbox.shape[0]
            else:
                gt_areas = bt.bbox_areas(
                    bbox)  #---------------------------------------
                # gt_areas = (bbox[:, 2] - bbox[:, 0]) * (
                # bbox[:, 3] - bbox[:, 1])
                for k, (min_area, max_area) in enumerate(area_ranges):
                    num_gts[k] += np.sum((gt_areas >= min_area)
                                         & (gt_areas < max_area))
        # sort all det bboxes by score, also sort tp and fp
        cls_dets = np.vstack(cls_dets)
        num_dets = cls_dets.shape[0]
        sort_inds = np.argsort(-cls_dets[:, -1])
        tp = np.hstack(tp)[:, sort_inds]
        fp = np.hstack(fp)[:, sort_inds]
        # calculate recall and precision with tp and fp
        tp = np.cumsum(tp, axis=1)
        fp = np.cumsum(fp, axis=1)
        eps = np.finfo(np.float32).eps
        recalls = tp / np.maximum(num_gts[:, np.newaxis], eps)
        precisions = tp / np.maximum((tp + fp), eps)
        # calculate AP
        if scale_ranges is None:
            recalls = recalls[0, :]
            precisions = precisions[0, :]
            num_gts = num_gts.item()
        mode = 'area' if not use_07_metric else '11points'
        ap = average_precision(recalls, precisions, mode)
        eval_results.append({
            'num_gts': num_gts,
            'num_dets': num_dets,
            'recall': recalls,
            'precision': precisions,
            'ap': ap
        })
    pool.close()
    if scale_ranges is not None:
        # shape (num_classes, num_scales)
        all_ap = np.vstack([cls_result['ap'] for cls_result in eval_results])
        all_num_gts = np.vstack(
            [cls_result['num_gts'] for cls_result in eval_results])
        mean_ap = []
        for i in range(num_scales):
            if np.any(all_num_gts[:, i] > 0):
                mean_ap.append(all_ap[all_num_gts[:, i] > 0, i].mean())
            else:
                mean_ap.append(0.0)
    else:
        aps = []
        for cls_result in eval_results:
            if cls_result['num_gts'] > 0:
                aps.append(cls_result['ap'])
        mean_ap = np.array(aps).mean().item() if aps else 0.0

    print_map_summary(mean_ap,
                      eval_results,
                      dataset,
                      area_ranges,
                      logger=logger)

    return mean_ap, eval_results
Example #4
0
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
Example #5
0
    def format_results(self,
                       results,
                       with_merge=True,
                       ign_scale_ranges=None,
                       iou_thr=0.5,
                       nproc=4,
                       save_dir=None,
                       **kwargs):
        nproc = min(nproc, os.cpu_count())
        task = self.task
        if mmcv.is_list_of(results, tuple):
            dets, segments = results
            if task == 'Task1':
                dets = _list_mask_2_obb(dets, segments)
        else:
            dets = results

        if not with_merge:
            results = [(data_info['id'], result)
                       for data_info, result in zip(self.data_infos, results)]
            if save_dir is not None:
                id_list, dets_list = zip(*results)
                bt.save_dota_submission(save_dir, id_list, dets_list, task,
                                        self.CLASSES)
            return results

        print('\nMerging patch bboxes into full image!!!')
        start_time = time.time()
        if ign_scale_ranges is not None:
            assert len(ign_scale_ranges) == (len(self.split_info['rates']) *
                                             len(self.split_info['sizes']))
            split_sizes = []
            for rate in self.split_info['rates']:
                split_sizes += [
                    int(size / rate) for size in self.split_info['sizes']
                ]

        collector = defaultdict(list)
        for data_info, result in zip(self.data_infos, dets):
            if ign_scale_ranges is not None:
                img_scale = data_info['width']
                scale_ratio = np.array(split_sizes) / img_scale
                inds = np.argmin(abs(np.log(scale_ratio)))

                min_scale, max_scale = ign_scale_ranges[inds]
                min_scale = 0 if min_scale is None else min_scale
                max_scale = np.inf if max_scale is None else max_scale

            x_start, y_start = data_info['x_start'], data_info['y_start']
            new_result = []
            for i, dets in enumerate(result):
                if ign_scale_ranges is not None:
                    bbox_scales = np.sqrt(bt.bbox_areas(dets[:, :-1]))
                    valid_inds = (bbox_scales > min_scale) & (bbox_scales <
                                                              max_scale)
                    dets = dets[valid_inds]
                bboxes, scores = dets[:, :-1], dets[:, [-1]]
                bboxes = bt.translate(bboxes, x_start, y_start)
                labels = np.zeros((bboxes.shape[0], 1)) + i
                new_result.append(
                    np.concatenate([labels, bboxes, scores], axis=1))

            new_result = np.concatenate(new_result, axis=0)
            collector[data_info['ori_id']].append(new_result)

        merge_func = partial(_merge_func,
                             CLASSES=self.CLASSES,
                             iou_thr=iou_thr,
                             task=task)
        if nproc <= 1:
            print('Single processing')
            merged_results = mmcv.track_iter_progress(
                (map(merge_func, collector.items()), len(collector)))
        else:
            print('Multiple processing')
            merged_results = mmcv.track_parallel_progress(
                merge_func, list(collector.items()), nproc)

        if save_dir is not None:
            id_list, dets_list = zip(*merged_results)
            bt.save_dota_submission(save_dir, id_list, dets_list, task,
                                    self.CLASSES)

        stop_time = time.time()
        print('Used time: %.1f s' % (stop_time - start_time))
        return merged_results