RT-DETR计算COCO指标和TIDE指标,小目标检测必备,更全面的评估和指导模型性能,包含完整步骤和代码
前言
COCO指标
能够直观了解模型在
检测不同大小
、
不同难度
目标时的效果;
TIDE指标
专注于对
检测错误
进行分类和分析,从
不同角度
揭示模型的性能问题,使模型评估更加全面和深入(本文提供了完整的实现代码和配置步骤)。
例如,论文中
COCO
的指标内容展示:
论文中
TIDE
的指标内容展示:
一、基础概念
-
TP(True Positive,真正例) :预测框与真实目标框(Ground - Truth)的交并比(IoU)大于指定阈值(如0.5)的预测框,且同一真实目标框只计算一次TP。
-
FP(False Positive,假正例) :与真实目标框的IoU小于等于指定阈值(如0.5)的预测框,或检测到同一个真实目标框的多余预测框。
-
FN(False Negative,假负例) :未被检测到的真实目标框。
-
Precision(查准率) :计算公式为Precision = TP / (TP + FP) ,表示模型预测的所有目标中,预测正确的比例,用于衡量误检程度。
-
Recall(召回率/查全率) :计算公式为Recall = TP / (TP + FN) ,表示所有真实目标中,模型预测正确的目标比例,用于衡量漏检程度。
-
AP(Average Precision,平均精度) :衡量模型在单个类别上的检测准确率,是Precision - Recall曲线下的面积。在COCO数据集中,AP通常是将IoU阈值从0.5以0.05的步长递增到0.95(即0.5、0.55、0.6、0.65、0.7、0.75、0.8、0.85、0.9、0.95 ),共10个阈值下的AP值取平均。
-
mAP(mean Average Precision,平均精度均值) :所有类别的AP的平均值,用于衡量模型在所有类别上的检测效果。在COCO数据集中,有时不严格区分AP和mAP,所说的AP也可理解为mAP。
-
AR(Average Recall,平均召回率) :对于不同的IoU取最大的召回率再求平均值,反映模型在不同IoU阈值下找到真实目标的能力。
-
mAR(mean Average Recall,平均召回率均值) :所有类别的AR值的平均值,综合评估模型在所有类别上的召回性能。
二、COCO指标
- AP50 :IoU阈值为0.5时的AP。
- AP75 :IoU阈值为0.75时的AP,该阈值对模型定位精度要求更高,能更严格地反映算法框的位置精准程度。
- APsmall :小物体(small - sized objects)的AP,用于评估模型对小物体的检测效果。
- APmedium :中物体(medium - sized objects)的AP。
- APlarge :大物体(large - sized objects)的AP。
主要用于展示模型针对不同大小目标的检测性能。
三、TIDE指标
TIDE指标 的提出: https://arxiv.org/pdf/2008.08115
TIDE指标是用于目标检测分析的指标,将错误类型分为6类,Cls、Loc、Both、Dupe、Bkg、Miss的含义如下:
- Cls(Classification Error,分类错误) :预测框与真实框的最大交并比(IoUᵐᵃˣ)大于等于某一阈值tᶠ ,但预测框属于错误类别。即定位正确(IoU较高),但分类错误的情况。
- Loc(Localization Error,定位误差) :预测框与真实框的最大交并比满足tᵇ≤IoUᵐᵃˣ≤tᶠ ,且预测框属于正确类别。也就是分类正确,但定位不够准确(IoU未达到较高阈值)的情况。
- Both(分类和定位错误) :预测框与真实框的最大交并比满足tᵇ≤IoUᵐᵃˣ≤tᶠ ,且预测框属于错误类别。即同时存在分类错误和定位错误的情况。
- Dupe(Duplicate Detection Error,重复检测错误) :预测框与真实框的最大交并比IoUᵐᵃˣ≥tᶠ ,且预测框属于正确类别,但已经有另一个得分更高的预测框与该真实框匹配。即如果不是因为存在更高得分的检测,当前这个检测本应是正确的。
- Bkg(Background Error,背景误差) :对于所有预测框,其与真实框的最大交并比IoUᵐᵃˣ≤tᵇ 。也就是把背景检测成了前景的误检情况。
- Miss(Missed GT Error,真实框漏检错误) :上述分类或定位错误尚未涵盖的所有未检测到的真实框(即假阴性情况) ,即存在真实目标但模型没有检测到。
主要用于找到模型的性能缺陷,并加以改进,展示前期在分类、定位等方面出现错误的改进效果,并统计了模型在不同阈值情况下的map精度。
四、计算方法
4.1 完整代码
yolo2coco.py:
import os
import cv2
import json
from tqdm import tqdm
from sklearn.model_selection import train_test_split
import argparse
classes = ['iw']
parser = argparse.ArgumentParser()
parser.add_argument('--image_path', default='datasets/images/test',type=str, help="path of images")
parser.add_argument('--label_path', default='datasets/labels/test',type=str, help="path of labels .txt")
parser.add_argument('--save_path', default='data.json', type=str, help="if not split the dataset, give a path to a json file")
arg = parser.parse_args()
def yolo2coco(arg):
print("Loading data from ", arg.image_path, arg.label_path)
assert os.path.exists(arg.image_path)
assert os.path.exists(arg.label_path)
originImagesDir = arg.image_path
originLabelsDir = arg.label_path
# images dir name
indexes = os.listdir(originImagesDir)
dataset = {'categories': [], 'annotations': [], 'images': []}
for i, cls in enumerate(classes, 0):
dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})
ann_id_cnt = 0
for k, index in enumerate(tqdm(indexes)):
txtFile = f'{index[:index.rfind(".")]}.txt'
stem = index[:index.rfind(".")]
try:
im = cv2.imread(os.path.join(originImagesDir, index))
height, width, _ = im.shape
except Exception as e:
print(f'{os.path.join(originImagesDir, index)} read error.\nerror:{e}')
if not os.path.exists(os.path.join(originLabelsDir, txtFile)):
continue
dataset['images'].append({'file_name': index,
'id': stem,
'width': width,
'height': height})
with open(os.path.join(originLabelsDir, txtFile), 'r') as fr:
labelList = fr.readlines()
for label in labelList:
label = label.strip().split()
x = float(label[1])
y = float(label[2])
w = float(label[3])
h = float(label[4])
H, W, _ = im.shape
x1 = (x - w / 2) * W
y1 = (y - h / 2) * H
x2 = (x + w / 2) * W
y2 = (y + h / 2) * H
cls_id = int(label[0])
width = max(0, x2 - x1)
height = max(0, y2 - y1)
dataset['annotations'].append({
'area': width * height,
'bbox': [x1, y1, width, height],
'category_id': cls_id,
'id': ann_id_cnt,
'image_id': stem,
'iscrowd': 0,
'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]]
})
ann_id_cnt += 1
with open(arg.save_path, 'w') as f:
json.dump(dataset, f)
print('Save annotation to {}'.format(arg.save_path))
if __name__ == "__main__":
yolo2coco(arg)
COCO_Metrice.py:
import warnings
warnings.filterwarnings('ignore')
import argparse
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from tidecv import TIDE, datasets
def parse_opt():
parser = argparse.ArgumentParser()
parser.add_argument('--anno_json', type=str, default='data.json', help='label coco json path')
parser.add_argument('--pred_json', type=str, default='runs/val/exp2/predictions.json', help='pred coco json path')
return parser.parse_known_args()[0]
if __name__ == '__main__':
opt = parse_opt()
anno_json = opt.anno_json
pred_json = opt.pred_json
anno = COCO(anno_json) # init annotations api
pred = anno.loadRes(pred_json) # init predictions api
eval = COCOeval(anno, pred, 'bbox')
eval.evaluate()
eval.accumulate()
eval.summarize()
tide = TIDE()
tide.evaluate_range(datasets.COCO(anno_json), datasets.COCOResult(pred_json), mode=TIDE.BOX)
tide.summarize()
tide.plot(out_dir='result')
4.2 配置步骤
①:模型验证,选择想要测试的权重,验证时指定测试集(和后续的coco指标计算时指定的是同一个),设置
save_json=True
,验证完成后会在
run/val/exp
(找到对应的验证结果存放的文件夹即可)中出现一个
predictions.json
②:在根目录新建
yolo2coco.py
,粘贴上方对应的代码,缺少什么库安装什么库
参数配置:
classes
中填写类别名,即自己的data.yaml文件中的类别名,直接复制过来即可,有几个写几个
image_path
为测试集的图片文件夹路径
label_path
为测试集的标签文件夹路径
运行程序,便会在根目录中生成一个
data.json
,即将yolo标签转成了coco标签。
③:在根目录新建
COCO_Metrice.py.py
,粘贴上方对应的代码,缺少什么库安装什么库
参数配置:
anno_json
中指定生成的coco标签路径
pred_json
中指定验证生成的
predictions.json
路径,我这里取出来放在了根目录。
运行。
4.3 计算结果
COCO结果:
loading annotations into memory...
Done (t=0.00s)
creating index...
index created!
Loading and preparing results...
DONE (t=0.37s)
creating index...
index created!
Running per image evaluation...
Evaluate annotation type *bbox*
DONE (t=1.37s).
Accumulating evaluation results...
DONE (t=0.16s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.552
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.899
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.577
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.277
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.563
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.299
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.637
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.694
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.547
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.700
常用的有
AP50:95
、
AP50
、
AP75
、
APs
、
APm
、
APl
。结果中为
-1
的表示在当前情况下没有当前目标或未检测到相应的目标
TIDE结果:
bbox AP @ [50-95]: 55.53
bbox AP @ [50-95]
==================================================================================================
Thresh 50 55 60 65 70 75 80 85 90 95
---------------------------------------------------------------------------------------------------
AP 90.26 87.12 84.52 78.91 66.89 58.06 45.27 28.56 13.38 2.29
==================================================================================================
Main Errors
=============================================================
Type Cls Loc Both Dupe Bkg Miss
-------------------------------------------------------------
dAP 0.00 2.69 0.00 1.73 1.37 0.00
=============================================================
Special Error
=============================
Type FalsePos FalseNeg
-----------------------------
dAP 8.75 0.73
=============================
并在
result/predictions_bbox_summary.png
路径中绘制保存了各个错误的组成图