YOLO的评测指标和COCO是两套体系,因此需要手动计算。思路是使用pycocotools,
COCOeval(coco_gt, coco_dt, eval_type)
获取YOLO评测结果,predication.json
results = model.val(data='data.yaml',save_json=True) # 确保 data.yaml 路径正确
结果保存在runs/segment/val/predictions.json
ID对齐
这是很重要的一步,COCO.json的id会从1开始重新设置,对应的id会有一个file_name链接指定的图像;而YOLO生成的predication.json以image_id来标识每一张相关图像,会使用图像的名字来作为值。这就会导致两个id对应不上,出现以下错误。
AssertionError: Results do not correspond to current coco set
prediction.json
coco.json
我这里是对齐了的,大家可以检查一下自己是否对齐,这里对齐之后一般就可以出结果了。
category_id对齐
category_id如果没对齐最后输出的结果会全是0,这个时候虽然图像对齐了,但是输出的类别没对上,所以结果是0,因为YOLO和COCO的下标起始不一样,YOLO从1开始,COCO从0开始。
coco.json
prediction.json
转换代码
因此,只要确保这两个对齐,就可以输出最后的COCO评价指标结果。
ID对齐,这里提供最简单的一个思路是把原始的YOLO标签和图像名称按照coco.json里的id重新命名复制一份,因为我的数据集小,我就直接这么做了。
import json
import shutil
import os
coco_json_path = './COCO/train.json'
with open(coco_json_path, 'r', encoding='utf-8') as f:
coco_data = json.load(f)
images = coco_data["images"]
imgs = []
ori_image = './YOLO/images/train'
ori_txt = './YOLO/labels/train'
des_img = './YOLO-Align/images/train'
des_label = './YOLO-Align/labels/train'
for i,img in enumerate(images):
try:
imgs.append([img['file_name'], img['id']])
end = img['file_name'][-4:]
new_name = str(img['id']) + end
des_path = os.path.join(des_img,new_name)
ori_path = os.path.join(ori_image,img['file_name'])
shutil.copy(ori_path,des_path)
new_name_txt = str(img['id']) + '.txt'
des_path = os.path.join(des_label,new_name_txt)
ori_path = os.path.join(ori_txt,img['file_name'][:-4] + '.txt')
shutil.copy(ori_path,des_path)
except:
print("not exist")
category_id对齐,直接修改predication.json里的类别id,这里就比较简单了,我只有一个类,就直接把1改为0,有多类的可以读取predication.json,找到category_id然后+1。
还有一种方法是 ultralytics/models/yolo/detect/val.py
设置self.is_coco = True
,但是我试过了没用,大家可以试试。
最后还有一种最麻烦的是文件数量可能没对齐,这就要大家自己去写代码查找了,可以通过下面这份代码检查对应id下的图像 掩码是否对应。
import numpy as np
import matplotlib.pyplot as plt
from pycocotools.coco import COCO
from pycocotools import mask as maskUtils
def save_binary_mask(anno_json, img_id, save_path, pred_json=None):
"""
根据输入的图片 id,从 COCO 标注或预测结果中加载所有 segmentation 标注,
生成合并后的二值掩码,并保存到指定文件路径(save_path)。
参数:
anno_json: COCO 标注文件的路径(字符串)。
img_id: 指定图片的 id(整数或可转换为整数)。
save_path: 保存二值掩码图片的完整路径(字符串),例如 "binary_mask_1.png"
pred_json: 可选参数,预测结果的 JSON 文件路径(字符串)。若提供则加载预测结果,
否则加载原始标注。
"""
# 加载 COCO 标注数据
coco_gt = COCO(str(anno_json))
if pred_json is not None:
# 如果提供了预测结果 JSON,则加载预测结果
coco = coco_gt.loadRes(str(pred_json))
else:
coco = coco_gt
# 获取指定图像的信息
img_infos = coco.loadImgs(img_id)
if len(img_infos) == 0:
print(f"找不到图像 id {img_id} 对应的图像信息")
return
img_info = img_infos[0]
height, width = img_info['height'], img_info['width']
print(f"处理图像:ID={img_info['id']}, 文件名={img_info['file_name']}, 尺寸={width}x{height}")
# 初始化一个全零的二值掩码,大小与图像相同
binary_mask = np.zeros((height, width), dtype=np.uint8)
# 获取该图像的所有标注
ann_ids = coco.getAnnIds(imgIds=img_id)
anns = coco.loadAnns(ann_ids)
for ann in anns:
seg = ann.get("segmentation", None)
if seg is None:
continue
# 判断 segmentation 格式
if isinstance(seg, list):
# 多边形格式:将多边形转换为 RLE,再解码成二值掩码
rles = maskUtils.frPyObjects(seg, height, width)
rle = maskUtils.merge(rles)
m = maskUtils.decode(rle)
elif isinstance(seg, dict):
# RLE 格式:直接解码
m = maskUtils.decode(seg)
else:
continue
# 合并当前掩码到二值掩码中(像素值大于0视为1)
binary_mask = np.maximum(binary_mask, m)
# 保存二值掩码到指定路径,cmap 设置为灰度图
plt.imsave(save_path, binary_mask, cmap='gray')
print(f"二值掩码已保存到: {save_path}")
# 示例调用:
if __name__ == "__main__":
# 修改为你的 COCO 标注文件路径
anno_json_path = "./COCO/val.json"
pred_json_path = './predictions.json'
# 指定需要查看的图片 id
image_id = 2
# 指定保存的二值掩码文件路径
save_path_gt = f"binary_mask_{image_id}_gt.png"
save_path_pd = f"binary_mask_{image_id}_pd.png"
# 使用真值(标注)生成二值掩码
save_binary_mask(anno_json_path, image_id, save_path_gt)
# 使用预测结果生成二值掩码
save_binary_mask(anno_json_path, image_id, save_path_pd, pred_json=pred_json_path)
结果
这里我只放了部分可以对比的结果,可以看到YOLO的结果起始要高一点,不过我看大家说高个2-3点也是正常的
#seg
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.415
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.861
mAP50 mAP50-95)
0.878 0.443
#box
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.616
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.881
mAP50 mAP50-95)
0.887 0.628