【Datawhale AI夏令营第五期】 CV方向 Task02学习笔记 精读Baseline 建模方案解读与进阶

【Datawhale AI夏令营第五期】 CV方向 Task02学习笔记 精读Baseline 建模方案解读与进阶

教程:

链接:
https://linklearner.com/activity/16/16/68
传送门
之前我看原画课的时候,造型的部分就跟我们说,让我们日常观察事物的时候把它想象出有个外边框。在画室学画的期间,第一步打型也是先在纸上草拟最上下左右的几个关键点。
在这里插入图片描述
可以理解为分类器只需要输出一个最正确的答案,但是检测器需要同时输出所有合理的答案?
在这里插入图片描述
在这里插入图片描述

物体检测算法主要分为两类:One-Stage(一阶段)和Two-Stage(两阶段)模型。
在这里插入图片描述
一步到位:全盘扫描,简单粗暴速度快。
两步战略:先抓可疑区域,再精细排查,准确精度高。
在这里插入图片描述
具体选择?还是要看模型的应用场景。在这里插入图片描述
Yolo适合要求速度的:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
认同,高频亮眼的是YOLO5,YOLO8。
在这里插入图片描述
一图一个txt文件,一行标注一个物体,包括类别索引和边界框。
YOLO习惯看的数据是
中心坐标+长宽比例
。所以需要对原本坐标在左上角,宽高是长度的数据集做处理。
在这里插入图片描述
在这里插入图片描述
在Baseline中就有类别列表和需要识别的违规行为数量。
在这里插入图片描述

# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../dataset/  # dataset root dir
train: images/train/  # train images (relative to 'path') 128 images
val: images/val/  # train images (relative to 'path') 128 images

# Classes
nc: 2  # number of classes
names: ["0", '1']  # class names

我记得训练模型好像还有个best.pt的说法,是训练完成后模型保存的最佳权重,一般就用这个best.pt来执行后续的工作。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
results和weight应该比较重要,跟混淆矩阵相关的这几个F1,RP啥的图表根据需要看,我记得这几个指标好像别的地方见过有函数可以直接打印输出。
在这里插入图片描述
在这里插入图片描述

from sklearn.metrics import confusion_matrix, classification_report, roc_curve, auc

# 假设 y_true 是真实标签,y_pred 是模型预测的标签
y_true = [0, 1, 0, 1]
y_pred = [0, 1, 1, 0]

# 计算混淆矩阵
conf_matrix = confusion_matrix(y_true, y_pred)

# 打印分类报告,其中包含了精确度、召回率和F1分数
print(classification_report(y_true, y_pred))

# 计算ROC曲线和AUC
fpr, tpr, thresholds = roc_curve(y_true, y_pred)
roc_auc = auc(fpr, tpr)

# 打印AUC值
print("AUC:", roc_auc)

此外,还可以使用 matplotlib 或 seaborn 等可视化库来绘制混淆矩阵和ROC曲线:

import matplotlib.pyplot as plt
import seaborn as sns

# 绘制混淆矩阵热力图
sns.heatmap(conf_matrix, annot=True, fmt='d')
plt.show()

# 绘制ROC曲线
plt.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()

下面这个点是我第一次看,以后多加留心。
在这里插入图片描述
在这里插入图片描述
选择YOLO的原因应该是这个最后的应用场景类似于监控摄像头,不要求很精确,但是必须实时检测,快准狠,反应迅速。在这里插入图片描述
如果毕设答辩选了YOLO视频图像识别啥的,下面教程的这段话应该可以借鉴思路,一下子多了好几行。在这里插入图片描述
顺便,这放一张搞笑梗图:
在这里插入图片描述

Baseline 进阶思路:

1.加数据量。2.换模型。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

原始为
!wget http://mirror.coggle.club/yolo/yolov8n-v8.2.0.pt -O yolov8n.pt

下面任意一个
!wget http://mirror.coggle.club/yolo/yolov8s-v8.2.0.pt -O yolov8s.pt
!wget http://mirror.coggle.club/yolo/yolov8m-v8.2.0.pt -O yolov8m.pt
!wget http://mirror.coggle.club/yolo/yolov8l-v8.2.0.pt -O yolov8l.pt
!wget http://mirror.coggle.club/yolo/yolov8x-v8.2.0.pt -O yolov8x.pt

我这就试试多放几个数据喂模型,这是我第一次跑Baseline的结果,泯然众人。
在这里插入图片描述
我给他安排15个视频依葫芦画瓢:
在这里插入图片描述
下载了zip文件以后,解压json这步厚德云很成功,上一期的阿里云实例就不行,代码一点没动过,一样是git命令拉下来的,我不知道为什么。
在这里插入图片描述
结果文件夹还没出,先看看训练的result——????这啥啊??在这里插入图片描述
看这个图倒是感觉检测得还挺准?话说他怎么知道是不是违章停车呢?会不会把正常停车也标出来,本质上是个车就要逮呢??
在这里插入图片描述
一看得分,我靠,效果拔群啊??!
在这里插入图片描述
在这里插入图片描述
不知道是触发了什么神奇开关,有机会的话我再多试试。

应该看不到哪去但是也搬来的教程参考资料:

https://docs.ultralytics.com/datasets/detect/#ultralytics-yolo-format
https://docs.ultralytics.com/guides/yolo-performance-metrics/
https://docs.ultralytics.com/models/yolov8/#performance-metrics

精读Baseline:

把Task01的小试牛刀体验代码跑了,我对这其中的细节很好奇,因为我之前自己跑过别人分享的Yolo5项目,当时感觉是特别大的一个文件夹,里面塞满了需要用到的各类东西,但是这个代码只需要短短几行就可以了。
之前我只听过F1-score,这个跟混淆矩阵有关系,但这个MOTA指标是啥?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下载数据集,并且还指定了下载的名字。
在这里插入图片描述
在这里插入图片描述
我还是第一次注意到read_json这个函数:在这里插入图片描述
读取视频的一帧测试一下:
在这里插入图片描述
我感觉Kimi说的有道理,这个逻辑读了第一帧会break,啥也没读到也是break,那该如何区分是哪种情况呢?起码该有个输出??
在这里插入图片描述
在这里插入图片描述
因为如果不看是否ret确实读到了视频第一帧的话,下一个frame.shape的代码可能就会遇到报错。在这里插入图片描述
在这里插入图片描述
所以为什么返回的帧数还可能是浮点数呢?
在这里插入图片描述
先简单拉个框看看效果:在这里插入图片描述
在这里插入图片描述
既然这段代码实现的功能是在图片上拉个能指定(颜色,边框粗细,大小,四角点)的框,那么市面上那些图像标注工具,是不是核心逻辑就跟这个差不多呢?
在这里插入图片描述
关于市面上免费的图像标注工具,我之前的帖子里面记了几个:
https://blog.csdn.net/bailichen800/article/details/140308080
传送门

表示目录路径的方法:

感觉这个会比较通用,码住。

在这里插入图片描述
在这里插入图片描述

import os

# 获取当前目录的绝对路径,并确保路径以分隔符结尾
dir_path = os.path.join(os.path.abspath('./'), '')

print(dir_path)

在这里插入图片描述

dir_path = os.path.abspath('.')
# 连接目录路径和文件名
file_path = os.path.join(dir_path, 'example.txt')

这个具体情况再慢慢print出路径调整吧,先暂时把大概函数记住。

打开yaml文件并写入:

这段代码本质上是遵循了读写文本文件的基本流程,所以操作大同小异。

# 需要按照你的修改path
with open('yolo-dataset/yolo.yaml', 'w', encoding='utf-8') as up:
    up.write(f'''
path: {dir_path}/yolo-dataset/
train: train/
val: val/

names:
    0: 非机动车违停
    1: 机动车违停
    2: 垃圾桶满溢
    3: 违法经营
''')

在这里插入图片描述
glob模块获取指定名称的标注文件和视频文件,放入列表,并且排序,确保对应关系。在这里插入图片描述
在这里插入图片描述
下面这段代码看着比较复杂:

for anno_path, video_path in zip(train_annos[:5], train_videos[:5]): #使用 zip 函数将 train_annos 和 train_videos 列表的前5个元素配对。这意味着代码将同时处理每个标注文件和对应的视频文件
    print(video_path) 
    anno_df = pd.read_json(anno_path) #使用 pandas 的 read_json 函数读取标注文件(.json 格式),并将结果存储在 anno_df DataFrame 中
    cap = cv2.VideoCapture(video_path) #使用 cv2.VideoCapture 打开视频文件
    frame_idx = 0  
    while True:
        ret, frame = cap.read() #使用 cap.read() 在一个循环中逐帧读取视频。如果读取失败(ret 为 False),则跳出循环
        if not ret:
            break
 
        img_height, img_width = frame.shape[:2] #从当前帧中获取高度和宽度
        
        frame_anno = anno_df[anno_df['frame_id'] == frame_idx] #使用 anno_df 中的 frame_id 筛选出当前帧的标注信息
        cv2.imwrite('./yolo-dataset/train/' + anno_path.split('/')[-1][:-5] + '_' + str(frame_idx) + '.jpg', frame) #将当前帧保存为JPEG图像,文件名基于标注文件名和帧索引

        if len(frame_anno) != 0:  #如果存在当前帧的标注信息,使用 with open 打开一个文本文件进行写入。文本文件名基于标注文件名和帧索引
            with open('./yolo-dataset/train/' + anno_path.split('/')[-1][:-5] + '_' + str(frame_idx) + '.txt', 'w') as up:
                for category, bbox in zip(frame_anno['category'].values, frame_anno['bbox'].values):
                    category_idx = category_labels.index(category)
                    #对于每个标注,将原始的边界框坐标(bbox)转换为 YOLO 格式的相对坐标和尺寸。YOLO 格式使用中心点坐标和宽度、高度来表示边界框。
                    x_min, y_min, x_max, y_max = bbox
                    x_center = (x_min + x_max) / 2 / img_width
                    y_center = (y_min + y_max) / 2 / img_height
                    width = (x_max - x_min) / img_width
                    height = (y_max - y_min) / img_height

                    if x_center > 1:#如果 x_center 大于1(这通常不应该发生,因为坐标应该在0到1之间),打印出边界框坐标。
                        print(bbox)
                    up.write(f'{category_idx} {x_center} {y_center} {width} {height}\n') #将转换后的标注信息写入文本文件。每个标注一行,包含类别索引和转换后的坐标、宽度、高度。
        
        frame_idx += 1

在这里插入图片描述
但我感觉有点抽象,他这一顿操作到底在干啥??
原来是在适应Yolo的需求,尤其是这个矩形框的表示形式。在这里插入图片描述
下载文件并保存到指定目录:注意这个-p和-o的参数。

!mkdir -p ~/.config/Ultralytics/
!wget http://mirror.coggle.club/yolo/Arial.ttf -O ~/.config/Ultralytics/Arial.ttf

在这里插入图片描述

使用 Ultralytics 的 YOLO(You Only Look Once)目标检测模型进行训练的:

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0" #指定了 CUDA 应该使用的第一个 GPU 设备。在这个例子中,它被设置为 "0",意味着使用默认的 GPU 设备

import warnings
warnings.filterwarnings('ignore') #忽略所有的警告信息,以避免在训练过程中输出不必要的警告


from ultralytics import YOLO #从 Ultralytics 库导入 YOLO 类
model = YOLO("yolov8n.pt") #创建了一个 YOLO 模型实例,使用预训练的权重文件 yolov8n.pt
results = model.train(data="yolo-dataset/yolo.yaml", epochs=2, imgsz=1080, batch=16) #启动了模型的训练过程

注意指定默认GPU设备、忽略所有的警告信息的方法,以后其他代码可以复用:
在这里插入图片描述

使用 Ultralytics 的 YOLO 模型对视频文件进行目标检测,并将检测结果保存为 JSON 格式的文件:

from ultralytics import YOLO
model = YOLO("runs/detect/train/weights/best.pt")
import glob

for path in glob.glob('测试集/*.mp4'):
    submit_json = []
    results = model(path, conf=0.05, imgsz=1080,  verbose=False)
    for idx, result in enumerate(results):
        boxes = result.boxes  # Boxes object for bounding box outputs
        masks = result.masks  # Masks object for segmentation masks outputs
        keypoints = result.keypoints  # Keypoints object for pose outputs
        probs = result.probs  # Probs object for classification outputs
        obb = result.obb  # Oriented boxes object for OBB outputs

        if len(boxes.cls) == 0:
            continue
        
        xywh = boxes.xyxy.data.cpu().numpy().round()
        cls = boxes.cls.data.cpu().numpy().round()
        conf = boxes.conf.data.cpu().numpy()
        for i, (ci, xy, confi) in enumerate(zip(cls, xywh, conf)):
            submit_json.append(
                {
                    'frame_id': idx,
                    'event_id': i+1,
                    'category': category_labels[int(ci)],
                    'bbox': list([int(x) for x in xy]),
                    "confidence": float(confi)
                }
            )

    with open('./result/' + path.split('/')[-1][:-4] + '.json', 'w', encoding='utf-8') as up:
        json.dump(submit_json, up, indent=4, ensure_ascii=False)

在这里插入图片描述
首先看第一个问题,这个“最小置信度阈值”是什么呢?
也就是说,给模型对自己的答案定一个信心标准,达到这个标准的才计数考核模型的预测水平。如果模型都是瞎猜给的答案,直接当无效结果考虑。
我感觉就像有些问卷调查里面的“弱智问题”的作用,故意问“1+1=?”,“C选项是什么颜色”这种很简单的,就是要看这份问卷有没有被认真作答。如果这些选项都选错得话说明是瞎填的,可以直接作废了。
在这里插入图片描述
这几个提取的信息里边谁是啥得看一眼,以后应该用得多:在这里插入图片描述

    for idx, result in enumerate(results): #从结果中提取边界框、分割掩码、关键点、概率和旋转边界框不同类型的检测输出。,,,,
        boxes = result.boxes  #boxes 通常包含边界框信息
        masks = result.masks  # masks 包含实例分割的掩码
        keypoints = result.keypoints  # keypoints 包含关键点检测结果
        probs = result.probs  # probs 包含每个检测到的对象的类别概率
        obb = result.obb  #obb 包含旋转边界框(Oriented Bounding Box)信息

在这里插入图片描述

from ultralytics import YOLO
model = YOLO("runs/detect/train/weights/best.pt") #创建一个 YOLO 模型实例,并加载预训练的权重文件 best.pt
import glob

for path in glob.glob('测试集/*.mp4'): #用于文件名模式匹配,获取符合特定规则的文件列表 获取测试集目录下所有 .mp4 视频文件的路径,并逐个进行处理
    submit_json = [] #存储所有检测结果
    results = model(path, conf=0.05, imgsz=1080,  verbose=False) #使用模型对每个视频文件进行检测,设置最小置信度阈值 conf=0.05,图像尺寸 imgsz=1080,以及 verbose=False 表示不打印详细输出
    for idx, result in enumerate(results): #从结果中提取边界框、分割掩码、关键点、概率和旋转边界框不同类型的检测输出。,,,,
        boxes = result.boxes  #boxes 通常包含边界框信息
        masks = result.masks  # masks 包含实例分割的掩码
        keypoints = result.keypoints  # keypoints 包含关键点检测结果
        probs = result.probs  # probs 包含每个检测到的对象的类别概率
        obb = result.obb  #obb 包含旋转边界框(Oriented Bounding Box)信息

        if len(boxes.cls) == 0:  #如果当前帧没有检测到任何对象(len(boxes.cls) == 0),则跳过当前帧
            continue
        
        xywh = boxes.xyxy.data.cpu().numpy().round() #从 boxes.xyxy 提取的边界框坐标,表示为 (x, y, w, h) 格式,其中 x, y 是边界框左上角的坐标,w, h 是边界框的宽度和高度
        cls = boxes.cls.data.cpu().numpy().round()  #cls 是从 boxes.cls 提取的类别索引
        conf = boxes.conf.data.cpu().numpy() #conf 是从 boxes.conf 提取的置信度。
        for i, (ci, xy, confi) in enumerate(zip(cls, xywh, conf)):
            submit_json.append(  #为每个检测到的对象构建一个字典,包含帧 ID、事件 ID、类别、边界框坐标和置信度。
                {
                    'frame_id': idx,
                    'event_id': i+1,
                    'category': category_labels[int(ci)],
                    'bbox': list([int(x) for x in xy]),
                    "confidence": float(confi)
                }
            )

    with open('./result/' + path.split('/')[-1][:-4] + '.json', 'w', encoding='utf-8') as up:
        json.dump(submit_json, up, indent=4, ensure_ascii=False) #将 submit_json 列表写入到结果目录下,文件名为原始视频文件名加上 .json 后缀。

在这里插入图片描述
ci等于0,1,2,3时,在之前这个定义这个列表里面找对应的违规行为类别,把汉字代替数字存进JSON文件里。在这里插入图片描述

        xywh = boxes.xyxy.data.cpu().numpy().round() #从 boxes.xyxy 提取的边界框坐标,表示为 (x, y, w, h) 格式,其中 x, y 是边界框左上角的坐标,w, h 是边界框的宽度和高度
        cls = boxes.cls.data.cpu().numpy().round()  #cls 是从 boxes.cls 提取的类别索引
        conf = boxes.conf.data.cpu().numpy() #conf 是从 boxes.conf 提取的置信度

输出result:

!\rm result/.ipynb_checkpoints/ -rf
!\rm result.zip
!zip -r result.zip result/

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值