coco evaluate

指标含义

参考  https://zhuanlan.zhihu.com/p/60707912
mmap的解释:
给定一组IOU阈值,在每个IOU阈值下面,求所有类别的AP,并将其平均起来,作为这个IOU阈值下的检测性能,称为mAP(比如[email protected]就表示IOU阈值为0.5时的mAP);最后,将所有IOU阈值下的mAP进行平均,就得到了最终的性能评价指标:mmAP。

值得注意的一个细节是:在实际计算时,mmAP并不会把所有检测结果都考虑进来,因为那样总是可以达到Recall=100%,为了更有效地评价检测结果,mmAP只会考虑每张图片的前100个结果。这个数字应该随着数据集变化而改变,比如如果是密集场景数据集,这个数字应该提高。
coco evaluate_第1张图片

关键过程和解析

分别读取检测结果的json文件和gt的json文件,然后进行评估
coco evaluate_第2张图片
(1)COCOeval的初始化

cocoEval = COCOeval(cocoGt, cocoDt, iou_type)
其中cocoGt和cocoDt都是pycocotools.coco.COCO类

        self.cocoGt   = cocoGt              # ground truth COCO API
        self.cocoDt   = cocoDt              # detections COCO API
        self.evalImgs = defaultdict(list)   # per-image per-category evaluation results [KxAxI] elements
        self.eval     = {}                  # accumulated evaluation results
        self._gts = defaultdict(list)       # gt for evaluation
        self._dts = defaultdict(list)       # dt for evaluation
        self.params = Params(iouType=iouType) # parameters
        self._paramsEval = {}               # parameters for evaluation
        self.stats = []                     # result summarization
        self.ious = {}                      # ious between all gts and dts
        if not cocoGt is None:
            self.params.imgIds = sorted(cocoGt.getImgIds())
            self.params.catIds = sorted(cocoGt.getCatIds())

(2)设置cocoEval的相关属性

cocoEval.params.catIds = self.cat_ids
cocoEval.params.imgIds = self.img_ids
cocoEval.params.maxDets = list(proposal_nums)
cocoEval.params.iouThrs = iou_thrs

coco evaluate_第3张图片

(3) evaluate函数
coco evaluate_第4张图片

p = self.params
p.imgIds = list(np.unique(p.imgIds))
p.catIds = list(np.unique(p.catIds))
p.maxDets = sorted(p.maxDets)

#prepare:将gt和dt重新处理为字典。
self._prepare()
	gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds))
	dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds))
	self._gts = defaultdict(list)
	for gt in gts:
		#(image_id,category_id)作为key,value是一个list,包含了该图该类别下的每一个gt
		self._gts[gt['image_id'], gt['category_id']].append(gt)
	dts做相同的处理,得到self._dts

coco evaluate_第5张图片

在这里插入图片描述

#self.ious是一个字典,key为(imgId,catId),value是一个N*M的array,N是该图该类别的gt_bbox,M是该图该类别的det_bbox,array是gt_bbox和det_bbox的IoU
self.ious = {(imgId, catId): computeIoU(imgId, catId) for imgId in p.imgIds for catId in catIds}
#self.evalImgs生成每张图片每个类别在10个不同的IoU的阈值(0.5-0.95)下的匹配情况。
self.evalImgs = [evaluateImg(imgId, catId, areaRng, maxDet)
                 for catId in
                         catIds
                 for areaRng in p.areaRng
                 for imgId in p.imgIds
             ]
 def evaluateImg(self, imgId, catId, aRng, maxDet):
 	

self.evalImgs共imagescateroryarea range条,每一条表示当前图像、当前类别、当前目标范围的各个目标的检测结果。

self.evalImgs[0]中的dtMatches:
共10行,每一行表示在当前IoU阈值下,dt box的匹配情况。因为我们总共有10个阈值(array([0.5 , 0.55, 0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95])),所以结果有10行。
每一行的具体含义,以第一行(1,0,2)为例,表示dt box 1<->gt box1,dt box3<->gt box2。可以参考下面的self._dts和self._gts来理解。
self.evalImgs[0]中的gtMatches:
gtMatches的含义与dtMatches基本一致。
注意:dtIds和gtMatches是针对全部的检测目标而言,而不是针对某张图或某个类别。所以在后面的匹配信息中,dtIds和gtMatches都会很大
coco evaluate_第6张图片
coco evaluate_第7张图片

(3) accumulate函数
coco evaluate_第8张图片

    def accumulate(self, p = None):
        '''
        Accumulate per image evaluation results and store the result in self.eval
        :param p: input params for evaluation
        :return: None
        '''
        print('Accumulating evaluation results...')
        tic = time.time()
        if not self.evalImgs:
            print('Please run evaluate() first')
        # allows input customized parameters
        if p is None:
            p = self.params
        p.catIds = p.catIds if p.useCats == 1 else [-1]
        T           = len(p.iouThrs)#T=10,iouThrs为array([0.5 , 0.55, 0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95])
        R           = len(p.recThrs)# R01,p.recThrs为0-10.001为间隔
        K           = len(p.catIds) if p.useCats else 1#K是类别数,K=4
        A           = len(p.areaRng)#A=4,p.areaRng[[0, 10000000000.0], [0, 1024], [1024, 9216], [9216, 10000000000.0]]
        M           = len(p.maxDets)#M=3,maxDets为[100, 300, 1000]
        precision   = -np.ones((T,R,K,A,M)) # shape为(10101443-1 for the precision of absent categories
        recall      = -np.ones((T,K,A,M))#shape为(443)
        scores      = -np.ones((T,R,K,A,M))# shape为(10101443# create dictionary for future indexing
        _pe = self._paramsEval
        catIds = _pe.catIds if _pe.useCats else [-1]
        setK = set(catIds)#{0, 1, 2, 3}
        setA = set(map(tuple, _pe.areaRng))#{(0, 10000000000.0), (1024, 9216), (0, 1024), (9216, 10000000000.0)}
        setM = set(_pe.maxDets)#{1000, 100, 300}
        setI = set(_pe.imgIds)#所有的图像
        # get inds to evaluate
        k_list = [n for n, k in enumerate(p.catIds)  if k in setK]#[0, 1, 2, 3]
        m_list = [m for n, m in enumerate(p.maxDets) if m in setM]#[100, 300, 1000]
        a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA]#[0, 1, 2, 3]
        i_list = [n for n, i in enumerate(p.imgIds)  if i in setI]
        I0 = len(_pe.imgIds)#1468
        A0 = len(_pe.areaRng)#4
        # retrieve E at each category, area range, and max number of detections
        #T、R、K、A、M分别代表(IoU的threshold的个数,0.5-0.9510个),(recall的阈值个数,从0.0-1.0共分了100组),(类别数,每个类别分别统计),(area的个数,目前共all,small,middle,big4个值),(MaxDet的个数,共10030010003种选项)
        

输出结果为
precision,shape为T R K A M,以precision[0,0,0,0,0],其含义为(IoU阈值选为0.5时),(只挑选det score大于某个阈值的det_box,使得good_det_box/all_gt_box的值小于0.01,但是大于0.00),(针对某个类别),(针对某个区域大小),(在指定的MaxDet)下的precision,即满足上述条件的det_box/all_det_box. 当R的阈值很小时,我们对recall的要求低,因此我们尽量挑选出质量高的det_box,所以precision大。
scores, shape为T R K A M.以scores[0,0,0,0,0]为例,其含义为(IoU阈值选为0.5时),(只挑选det score大于某个阈值的det_box,使得good_det_box/all_gt_box的值小于0.01,但是大于0.00),(针对某个类别),(针对某个区域大小),(在指定的MaxDet)下的score, 当R的阈值较小时,要求我们选出来高质量的det_box, score的值也比较大。
recall,shape为TKAM,表示recall[0,0,0,0]表示(IoU阈值选为0.5时),(该类别),(该区域大小下),(MaxDet为指定值)时的recall值,即所有的det_box中的good_det_box/all_gt_box
实际使用时:
(1)AP,mAP,或mmAP评估更科学,但是不太直观。针对某一具体问题,我们需要知道某一类别在指定IoU阈值,指定Det_box的分数阈值时,该类别的TP,FP,FN。目前的COCO eval并没有给出这些,需要我们去修改pycocotools/cocoeval.py,保存gt的个数、TP的个数、FP的个数。

gt_all[k,a,m] = npig
tp_all[t,k,a,m] = tp[-1]
fp_all[t,k,a,m] = fp[-1]

修改coco的evaluate函数,保存tp,fp,gt

IoU_pr_p,k_pr_p,area_pr_p,maxdet_pr_p = pr_params
tp_pr_p = cocoEval.eval['tp_all'][IoU_pr_p,k_pr_p,area_pr_p,maxdet_pr_p]
fp_pr_p = cocoEval.eval['fp_all'][IoU_pr_p,k_pr_p,area_pr_p,maxdet_pr_p]
gt_pr_p = cocoEval.eval['gt_all'][k_pr_p,area_pr_p,maxdet_pr_p]
with open(osp.join(out_dir,'tp_fp_fn.csv'),'w') as t_f_file:
	t_f_file.write(f'tp:{tp_pr_p}  fp:{fp_pr_p}  gt:{gt_pr_p} \r\n')

mmdetection中调用过程

#构造iou_thrs array([0.5 , 0.55, 0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95]) 
iou_thrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)


result_files, tmp_dir = self.format_results(results, jsonfile_prefix)
#我们的metrics只有1,[bbox]
for metric in metrics:
	#将检测结果转为指定的格式<pycocotools.coco.COCO object at 0x7f464c2c4e10> 
	cocoDt = cocoGt.loadRes(result_files[metric])
	#cocoGt,cocoDt分别表示gt和检测结果,iou_type为'bbox'
	cocoEval = COCOeval(cocoGt, cocoDt, iou_type) 
	#cocoEval.params的属性catIds、imgIds(各个图片的image_id)maxDets(用于计算recall,比如recall@100,recall@300,recall@1000,默认值[1003001000])
	cocoEval.params
	#
	cocoEval.evaluate()
	cocoEval.accumulate()
	cocoEval.summarize()

coco中PR曲线

https://zhuanlan.zhihu.com/p/60707912

coco evaluate_第9张图片

其他的语句就是一些plt.相关的用来画图的语句了。

plt.grid是用来在图中加网格。

plt.legend是用来在图中添加注释。

你可能感兴趣的:(python)