searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

yolov5s mAP评价全过程详解

2023-05-22 10:01:51
97
0

首先介绍一下coco数据集

首先使用代码读取coco2017验证集的标注文件-instances_val2017.json
with open(annotate_in_file) as f: # annotate_in_file为coco数据标注文件-instances_val2017.json
        dataset = json.load(f)​
读取结果如下:
标注文件一共分为5大类,info里面包含着数据集的各种描述信息;licenses为许可信息;images是所有图片的信息,images是一个list,每个元素对应一张图片的信息,list的下标没有任何实际意义;annotations也是一个list,每个元素对应一个框,list的下标也无实际意义。
 
每张图片的信息包括图片名和图片的尺寸、图片的id
 
每个边界框信息:包含边界框的坐标【边界框左上角横坐标x,边界框左上角纵坐标y,边界框的宽,边界框的高】;边界框的面积;边界框的类别id;边界框的id。
 
类别一共包含80个类别,但是类别的id不是从0-79的,而是从1-90中的80个数作为类别id,yolov5s预测的类别id是从0-79的,在做map验证时需要将预测类别id进行转化。
 
yolov5s前处理
 
为了方便多batch推理,以及评测各厂家的GPU性能,我们习惯将图片统一到固定尺寸,yolov5s的官方map结果使用的是640×640的图片大小。
yolov5s的官方map表现为map.50=56.8,其他参数为--conf 0.001 --iou 0.65。
 
coco数据集中有的图片尺寸大于640×640,有的图片小于640×640,需要采取一个统一的规则将所有图片的尺寸统一到640×640
1.首先将图片的最长边max_L变成640,记录r=640/max_L
2.接着将图片的高h和宽w缩放为:new_h = int(h*r) , new_w = int(w*r) ,使用cv2.resize函数将图像缩放到新尺寸
3.经过上一步,长边已经变成640,剩下的短边将其填充到640,在短边的两侧均与的填充像素,使得短边长度变成640,短边两侧填充的距离计算方法如下:d= (640 - min_L)/ 2。使用cv2.copyMakeBorder为图像填充像素
4.记录图像的原始尺寸(h0,w0), 图像长边缩放到640时,图像的尺寸(h,w),图像被填充到640×640时,图像周围的填充距离(dw,dh)
5.图片需要从 HWC 到 CHW, 从 BGR 到 RGB, 使用如下代码实现:img.transpose((2, 0, 1))[::-1]  
6.图片还需要归一化。im /= 255
 
yolov5s后处理
 
图片经过模型推理会得到shapes为(1,25200,85)的矩阵,1为图片的batch,25200为边界框的个数,85为边界框的中心点坐标(x,y),(宽w、高h),80个类别的概率分数。
1.首先将图片预测得到框进行筛选,选择目标置信度大于0.001的框
2.将80个类别概率都乘上目标置信度,得到每个类别的综合置信度
3.将边界框的坐标从(center_x, center_y, width, height) to (x1, y1, x2, y2),即左上角的坐标和右下角的坐标
4.进一步筛选综合置信度大于0.001的框和类别。即使同一个框对应不同的类别,这也相当于两个框。
5.将筛选后的框的坐标,框的类别对应的综合置信度,框的类别的索引,进行拼接
6.将得到的矩阵按照综合置信度进行降序排序得到新的矩阵
7.将新矩阵的边界框坐标分别加上一个偏移量,即当前边界框对应的类别索引扩大7680倍的值
8.最后将所有的框送入torchvision.ops.nms,并设定iou_thres=0.65,函数返回剩余框的索引。在这些索引中,选择前300个框作为最后的预测框
9.最终我们得到一个矩阵,第一个维度小于300,代表预测到边界框的个数,第二个维度为6,包含边界框的坐标(x1, y1, x2, y2),边界框的类别综合置信度,边界框的类别索引
 
yolov5s 评价结果验证
 
首先官方的mAP值,是将预测结果重新缩放到原图上,再与coco val2017的标注文件做对比计算得到的。
 
我们需要将预测框映射回原图,然后将预测框结果保存为与标注文件一致的json格式
 
1.模型推理+后处理,得到的是640×640的预测框坐标,我们在前处理中有记录图像的原始尺寸(h0,w0), 图像resize之后的尺寸(h,w),图像填充到640需要填充的距离(dw,dh)。首先需要将预测框的坐标减去填充的距离,得到resize后图像上框的坐标。接着将横坐标除以 w/w0,纵坐标除以h/h0(其中w/w0=h/h0,具体请看前处理)。最后我们得到原图上预测得到的边界框的坐标。但是标注文件里的坐标为边界框左上角的坐标,边界框的宽和高,还需要将预测框的坐标转为左上角的坐标和框的宽和高。为了避免异常,还需要将框的坐标约束在原始图片的尺寸内
2.将每一个框的结果保存为json格式,用于与标注文件做对比计算。保存格式如下所示:
jdict.append({
            'image_id': image_id,
            'category_id': class_map[int(p[5])],
            'bbox': [round(x, 3) for x in b],
            'score': round(p[4], 5)})
 
image_id为图像的文件名对应的整数,类别id需要将0-79的id转为1-90,bbox的坐标就是原图上预测框的左上角坐标和边界框的宽和高,score就是预测框的综合置信度。
将所有框的结果都添加到jdict这个字典当中。
 
3.最后使用pycocotool这个工具包计算coco评价指标,特别注意:需要指明eval.params.imgIds,即图片的id,实际上就是图片名的整数。
        anno_json = str('instances_val2017.json')  # annotations
        pred_json = str("predictions.json")  # predictions
        LOGGER.info(f'\nEvaluating pycocotools mAP... saving {pred_json}...')
        with open(pred_json, 'w') as f:
            json.dump(jdict, f)

        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
            check_requirements('pycocotools')
            from pycocotools.coco import COCO
            from pycocotools.cocoeval import COCOeval

            anno = COCO(anno_json)  # init annotations api
            pred = anno.loadRes(pred_json)  # init predictions api
            eval = COCOeval(anno, pred, 'bbox')
            if is_coco:
                eval.params.imgIds = [int(Path(x).stem) for x in dataloader.dataset.im_files]  # image IDs to evaluate
            eval.evaluate()
            eval.accumulate()
            eval.summarize()
            map, map50 = eval.stats[:2]  # update results (mAP@0.5:0.95, mAP@0.5)
 
0条评论
0 / 1000
张****佳
4文章数
1粉丝数
张****佳
4 文章 | 1 粉丝