问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

YOLOv8可视化并统计每张图的TP,FP,FN,检测准确率、误检率、漏检统计

创作时间:
作者:
@小白创作中心

YOLOv8可视化并统计每张图的TP,FP,FN,检测准确率、误检率、漏检统计

引用
CSDN
1.
https://blog.csdn.net/p13672294967/article/details/138569890

YOLOv8是一种流行的目标检测框架,能够快速准确地识别图像中的目标。在进行目标检测时,我们通常会关注三个关键指标:真正例(True Positives,TP)、假正例(False Positives,FP)和假负例(False Negatives,FN)。本文将详细介绍如何使用YOLOv8可视化这些指标,并统计每张图片的检测准确率、误检率和漏检率。

一、相关名词解释

在目标检测中,TP、FP和FN的定义稍微复杂一些,因为目标检测不仅要考虑分类是否正确,还要考虑定位是否准确。以下是这些概念的解释和示例:

  1. 真正例(True Positives,TP):指检测到的目标与实际目标之间的匹配。这意味着检测到的目标在位置和类别上都与实际目标匹配。

  2. 假正例(False Positives,FP):指模型错误地将负例(非目标)样本预测为正例(目标)。在目标检测中,FP 是指检测到的目标与实际无目标区域之间的匹配。

  3. 假负例(False Negatives,FN):指模型未能检测到实际存在的目标。在目标检测中,FN 是指未检测到的实际目标。

举个例子:

假设我们有一张图像,其中包含一只猫和一只狗。我们的目标检测模型会尝试检测图像中的动物,并且根据预测结果计算 TP、FP 和 FN。

  • TP(真正例):如果模型正确地检测到了图像中的猫和狗,并且对它们进行了正确的分类和定位,那么这就是一个 TP。

  • FP(假正例):如果模型在图像中的某些区域错误地检测到了动物(例如,将一只猫误认为狗),或者在图像中检测到了不存在的动物,那么这就是一个 FP。

  • FN(假负例):如果模型未能检测到图像中的某些动物(例如,漏掉了图像中的狗),那么这就是一个 FN。

例如,如果我们的模型在一张图像中正确检测到了一只猫和一只狗,并且没有检测到不存在的动物,那么:

  • TP = 2(图像中的猫狗都被检测出来,并且类别位置正确)
  • FP = 0(模型未将不存在的动物检测为目标)
  • FN = 0(模型未漏掉任何实际存在的目标)

二、新建Python文件

YOLOv8没有单独运行的Python文件,以下是自己新加的detect.py,来获取每张图的TP、FP、FN:

import os
import cv2
import tqdm
import shutil
import numpy as np

def xywh2xyxy(box):
    box[:, 0] = box[:, 0] - box[:, 2] / 2
    box[:, 1] = box[:, 1] - box[:, 3] / 2
    box[:, 2] = box[:, 0] + box[:, 2]
    box[:, 3] = box[:, 1] + box[:, 3]
    return box

def iou(box1, box2):
    x11, y11, x12, y12 = np.split(box1, 4, axis=1)
    x21, y21, x22, y22 = np.split(box2, 4, axis=1)
 
    xa = np.maximum(x11, np.transpose(x21))
    xb = np.minimum(x12, np.transpose(x22))
    ya = np.maximum(y11, np.transpose(y21))
    yb = np.minimum(y12, np.transpose(y22))
 
    area_inter = np.maximum(0, (xb - xa + 1)) * np.maximum(0, (yb - ya + 1))
 
    area_1 = (x12 - x11 + 1) * (y12 - y11 + 1)
    area_2 = (x22 - x21 + 1) * (y22 - y21 + 1)
    area_union = area_1 + np.transpose(area_2) - area_inter
 
    iou = area_inter / area_union
    return iou

def draw_box(img, box, color):
    cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), color, thickness=5)
    return img

if __name__ == '__main__':
    postfixes = ['jpg', 'JPG']  # 定义要处理的后缀列表
    img_path = '/home/wuyapeng/yolov8/data/images'
    label_path = '/home/wuyapeng/yolov8/data/labels'
    predict_path = 'runs/detect/predict20/labels'
    save_path = 'vis'
    classes = ['car', 'person']
    detect_color, missing_color, error_color  = (0, 255, 0), (0, 0, 255), (0, 255, 0)#(b,g,r)
    iou_threshold = 0.45
    
    if os.path.exists(save_path):
        shutil.rmtree(save_path)
    os.makedirs(save_path, exist_ok=True)
    all_right_num, all_missing_num, all_error_num = 0, 0, 0
    with open('result.txt', 'w') as f_w:
        for filename in tqdm.tqdm(os.listdir(img_path)):
            postfix = filename.split('.')[-1]  # 获取文件名后缀
            if postfix.lower() in postfixes:  # 判断后缀是否在指定列表中
                image = cv2.imread(os.path.join(img_path, filename))
                if image is None:
                    print(f'image:{os.path.join(img_path, filename)} not found.', file=f_w)
                    continue
                h, w = image.shape[:2]
                path = filename[:-4]  # 去除文件后缀
                try:
                    with open(f'{predict_path}/{path}.txt') as f:
                        pred = np.array(list(map(lambda x:np.array(x.strip().split(), dtype=np.float32), f.readlines())))
                        pred[:, 1:5] = xywh2xyxy(pred[:, 1:5])
                        pred[:, [1, 3]] *= w
                        pred[:, [2, 4]] *= h
                        pred = list(pred)
                except:
                    pred = []
                try:
                    with open(f'{label_path}/{path}.txt') as f:
                        label = np.array(list(map(lambda x:np.array(x.strip().split(), dtype=np.float32), f.readlines())))
                        label[:, 1:] = xywh2xyxy(label[:, 1:])
                        label[:, [1, 3]] *= w
                        label[:, [2, 4]] *= h
                except:
                    print(f'label path:{label_path}/{path}.txt (not found or no target).', file=f_w)
                right_num, missing_num, error_num = 0, 0, 0
                label_id, pred_id = list(range(label.shape[0])), [] if len(pred) == 0 else list(range(len(pred)))
                for i in range(label.shape[0]):
                    if len(pred) == 0: break
                    ious = iou(label[i:i+1, 1:], np.array(pred)[:, 1:5])[0]
                    ious_argsort = ious.argsort()[::-1]
                    missing = True
                    for j in ious_argsort:
                        if ious[j] < iou_threshold: break
                        if label[i, 0] == pred[j][0]:
                            image = draw_box(image, pred[j][1:5], detect_color)
                            pred.pop(j)
                            missing = False
                            right_num += 1
                            break
                    if missing:
                        image = draw_box(image, label[i][1:5], missing_color)
                        missing_num += 1
                if len(pred):
                    for j in range(len(pred)):
                        image = draw_box(image, pred[j][1:5], error_color)
                        error_num += 1
                all_right_num, all_missing_num, all_error_num = all_right_num + right_num, all_missing_num + missing_num, all_error_num + error_num
                cv2.imwrite(f'{save_path}/{path}.{postfix}', image)
                print(f'name:{path} right:{right_num} missing:{missing_num} error:{error_num}', file=f_w)
        print(f'all_result: right:{all_right_num} missing:{all_missing_num} error:{all_error_num}', file=f_w)

注意:detect.py需要修改的地方

  • postfixes = ['jpg', 'JPG']:定义要处理的后缀列表,这个需要根据检测图片的格式进行修改。
  • img_path = '/home/wuyapeng/yolov8/data/images':这是需要检测的图片路径。
  • label_path = '/home/wuyapeng/yolov8/data/labels':这是需要检测的图片标签的路径。
  • predict_path = 'runs/detect/predict20/labels':这个是需要用你自己训练好的模型/官方预训练模型去检测你的图片img_path,会生成一个检测后的标签txt文件,注意save_txt需要设置成True。
  • save_path = 'vis':这是保存路径。
  • classes = ['one', 'two']:这是自己的类别,可以根据实际情况进行修改。

三、具体步骤(以下是我用YOLOv8运行的案例)

首先运行YOLOv8的命令来生成模型pt文件检测的图片标签txt文件(注意save_txt=True必须加上去):

yolo task=detect mode=predict model=/home/wuyapeng/yolov8/yuanv8-2cls.pt source=/home/wuyapeng/yolov8/data/images conf=0.45 save_txt=True device=0 --line_width=5

然后会生成一个检测后的图片和标签,将生成后的标签labels的路径添加到detect.py文件中。

最后直接运行:

python detect.py

会生成一个vis文件夹(可视化正确检测、漏检和误检的检测图)和result.txt文件(会记录每张图片的正确检测、漏检、误检的数量,及总量)。

(注意opencv画图是BGR,所以TP=right=绿色,FN=miss=红色(漏检),FP=error=蓝色(误检))

检测效果展示


© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号