File size: 4,328 Bytes
bdec3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# --------------------------------------------------------
# X-Decoder -- Generalized Decoding for Pixel, Image, and Language
# Copyright (c) 2022 Microsoft
# Licensed under The MIT License [see LICENSE for details]
# Modified by Xueyan Zou ([email protected])
# --------------------------------------------------------
import logging
import torch
from torchvision.ops import box_iou

from detectron2.structures import BoxMode
from detectron2.data import MetadataCatalog
from detectron2.utils.comm import all_gather, is_main_process, synchronize
from detectron2.evaluation.evaluator import DatasetEvaluator


class GroundingEvaluator(DatasetEvaluator):
    """
    Evaluate grounding segmentation metrics.
    """

    def __init__(
        self,
        dataset_name,
        compute_box=False,
        distributed=True,
    ):
        self._logger = logging.getLogger(__name__)
        self._dataset_name = dataset_name
        self._distributed = distributed
        self._cpu_device = torch.device("cpu")
        self._compute_box = compute_box
        meta = MetadataCatalog.get(dataset_name)

    def reset(self):
        self.cum_I = 0
        self.cum_U = 0
        self.mIoU = 0
        self.eval_seg_iou_list = [.5, .6, .7, .8, .9]
        self.seg_correct = torch.zeros(len(self.eval_seg_iou_list), device=self._cpu_device)
        self.seg_total = 0
        if self._compute_box:
            self.mIoU_box = 0
            self.seg_correct_box = torch.zeros(len(self.eval_seg_iou_list), device=self._cpu_device)

    @staticmethod
    def computeIoU(pred_seg, gd_seg):
        I = (pred_seg & gd_seg)
        U = (pred_seg | gd_seg)
        return I, U

    def process(self, inputs, outputs):
        for input, output in zip(inputs, outputs):
            pred = output['grounding_mask'].sigmoid() > 0.5
            gt = input['groundings']['masks'].bool()
            bsi = len(pred)
            I, U = self.computeIoU(pred, gt)
            self.cum_I += I.sum().cpu()
            self.cum_U += U.sum().cpu()
            IoU = I.reshape(bsi,-1).sum(-1)*1.0 / (U.reshape(bsi,-1).sum(-1) + 1e-6)
            self.mIoU += IoU.sum().cpu()

            if self._compute_box:
                pred_box = BoxMode.convert(output['grounding_box'], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS)
                gt_box = BoxMode.convert(input['groundings']['boxes'], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS).cpu()
                IoU_box = box_iou(pred_box, gt_box).diagonal()
                self.mIoU_box += IoU_box.sum()

            for idx in range(len(self.eval_seg_iou_list)):
                eval_seg_iou = self.eval_seg_iou_list[idx]
                self.seg_correct[idx] += (IoU >= eval_seg_iou).sum().cpu()
                if self._compute_box:
                    self.seg_correct_box[idx] += (IoU_box >= eval_seg_iou).sum().cpu()
            self.seg_total += bsi

    def evaluate(self):
        if self._distributed:
            synchronize()
            self.cum_I = torch.stack(all_gather(self.cum_I)).sum()
            self.cum_U = torch.stack(all_gather(self.cum_U)).sum()
            self.mIoU = torch.stack(all_gather(self.mIoU)).sum()
            self.seg_correct = torch.stack(all_gather(self.seg_correct)).sum(0)
            self.seg_total = sum(all_gather(self.seg_total))

            if self._compute_box:
                self.mIoU_box = torch.stack(all_gather(self.mIoU_box)).sum()
                self.seg_correct_box = torch.stack(all_gather(self.seg_correct_box)).sum(0)
            if not is_main_process():
                return

        results = {}
        for idx in range(len(self.eval_seg_iou_list)):
            result_str = 'precision@{}'.format(self.eval_seg_iou_list[idx])
            results[result_str] = (self.seg_correct[idx]*100 / self.seg_total).item()
        results['cIoU'] = (self.cum_I*100./self.cum_U).item()
        results['mIoU'] = (self.mIoU*100./self.seg_total).item()

        if self._compute_box:
            for idx in range(len(self.eval_seg_iou_list)):
                result_str = 'precisionB@{}'.format(self.eval_seg_iou_list[idx])
                results[result_str] = (self.seg_correct_box[idx]*100 / self.seg_total).item()
            results['mBIoU'] = (self.mIoU_box*100./self.seg_total).item()

        self._logger.info(results)
        return {'grounding': results}