【深度学习】【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV5口罩检测
文章目录
前言
Ultralytics YOLO 是一系列基于 YOLO(You Only Look Once)算法的检测、分割、分类、跟踪和姿势估计模型,由 Ultralytics 公司开发和维护,YOLO 算法以其快速和准确的目标检测能力而闻名。从最初的YOLOv1到最新的YOLOv11,每一代版本都在特征提取、边界框预测和优化技术等方面引入了重要的创新。这些改进特别是在骨干网络(backbone)、颈部(neck)和头部(head)组件上的进步,使得YOLO成为实时目标检测领域的领先解决方案。
【YOLO的发展历程参考】,本博文将通过口罩检测项目简要介绍Ultralytics–YOLOv5的使用。【官方源码】
YOLOV5模型运行环境搭建
在win11环境下装anaconda环境,方便搭建专用于YOLOV5模型的虚拟环境。
- 查看主机支持的cuda版本(最高)
# 打开cmd,执行下面的指令查看CUDA版本号 nvidia-smi
- 安装GPU版本的torch【官网】
博主的cuda版本是12.2,博主选的11.8也没问题。
其他cuda版本的torch在【以前版本】找对应的安装命令。 - 博主安装环境参考
# 创建虚拟环境 conda create -n yolov5 python=3.10 # 查看新环境是否安装成功 conda env list # 激活环境 activate yolov5 # cd到合适的位置下载yolov5源码 git clone https://github.com/ultralytics/yolov5 cd yolov5 # 切换到一个特定的v7.0版本 git checkout tags/v7.0 # 安装pytorch和torchvision,否则容易自动安装成CPU版本(不知原因) pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装运行所需的包,修改requirements,删除torch和torchvision部分,增加onnx(博主需要导出onnx) pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 查看所有安装的包 pip list conda list
注意,这里博主的代码是YOLOV5算法的v7.0版本,假如git下载经常失败,也可以直接在【官方源码】直接下载。
YOLO系列的每个大版本算法都是有迭代优化的,不是每次迭代都是一个新的大版本,不要理解错了。
YOLOV5模型运行
数据集准备
-
数据集下载:口罩目标检测数据集WIDER_MASK_VOC2007.zip【百度云下载,提取码:4awg 】,以下是下载的数据集格式
VOC2007 └── Annotations 标签 ├── mask_withoutmask0.xml ├── mask_withoutmask1.xml ├── ... └── JPEGImages 图片 ├── mask_withoutmask0.jpg ├── mask_withoutmask1.jpg ├── ...
-
数据集格式转化:将原始标签的xml格式转化Ultralytics-YOLO的txt格式。
每幅图像对应一个txt文件,如果图像中没有检测对象则不需要txt文件。
每个对象占一行,每一行中包含的内容为:类别(class )、中心X坐标(x_center)、中心Y坐标(y_center)、图像宽度(width)和图像高度(height)。
中心坐标已经宽高都做了归一化处理,从0到 1;类别编号从0开始。这里博主提供了转化的python代码,博主个人推荐在yolov5工程下新建datasetsTool目录,用于放置额外需要的辅助代码,将xml2txt.py代码放置到datasetsTool目录下。
import os import xml.etree.ElementTree as ET def parse_xml(xml_file): # 解析XML文件 tree = ET.parse(xml_file) root = tree.getroot() # 获取图像尺寸 size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) # 初始化结果列表 objects = [] # 遍历所有的object标签 for obj in root.findall('object'): name = obj.find('name').text difficult = int(obj.find('difficult').text) # 只处理name为'without-mask'或者'mask'且difficult为0的对象 if name == 'without-mask' and difficult == 0: bndbox = obj.find('bndbox') xmin = int(bndbox.find('xmin').text) ymin = int(bndbox.find('ymin').text) xmax = int(bndbox.find('xmax').text) ymax = int(bndbox.find('ymax').text) # 计算中心点坐标和宽高 x_center = (xmin + xmax) / 2.0 y_center = (ymin + ymax) / 2.0 box_width = xmax - xmin box_height = ymax - ymin # 归一化处理 x_center /= width y_center /= height box_width /= width box_height /= height # 添加到结果列表 objects.append((0, x_center, y_center, box_width, box_height)) elif name == 'mask'and difficult == 0: bndbox = obj.find('bndbox') xmin = int(bndbox.find('xmin').text) ymin = int(bndbox.find('ymin').text) xmax = int(bndbox.find('xmax').text) ymax = int(bndbox.find('ymax').text) # 计算中心点坐标和宽高 x_center = (xmin + xmax) / 2.0 y_center = (ymin + ymax) / 2.0 box_width = xmax - xmin box_height = ymax - ymin # 归一化处理 x_center /= width y_center /= height box_width /= width box_height /= height # 添加到结果列表 objects.append((1, x_center, y_center, box_width, box_height)) return objects def save_to_txt(objects, txt_file): with open(txt_file, 'w') as f: for obj in objects: line = ' '.join([str(x) for x in obj]) + '\n' f.write(line) def process_directory(input_directory, output_directory): # 确保输出目录存在 if not os.path.exists(output_directory): os.makedirs(output_directory) # 遍历输入目录下的所有XML文件 for filename in os.listdir(input_directory): if filename.endswith('.xml'): xml_file = os.path.join(input_directory, filename) txt_file = os.path.join(output_directory, filename.replace('.xml', '.txt')) # 解析XML并获取所需信息 objects = parse_xml(xml_file) # 将结果保存到TXT文件 save_to_txt(objects, txt_file) if __name__ == "__main__": input_directory = r'Annotations' # 替换为你的XML文件所在目录 output_directory = r'labels' # 建议与Annotations在同一级 ''' eg: input_directory = r'E:\BaiduNetdiskDownload\VOC2007\Annotations' output_directory = r'E:\BaiduNetdiskDownload\VOC2007\labels' ''' process_directory(input_directory, output_directory)
-
数据集组织结构:将原始数据划分成训练集和测试集,博主个人推荐在yolov5工程下新建datasets目录,用于放置所需的数据集,并新建口罩检测数据集maskdetection,将train和val数据集放到maskdetection目录下。
maskdetection └── train ├── images | ├── mask_withoutmask0.jpg | ├── mask_withoutmask2.jpg | ├── ... ├── labels | ├── mask_withoutmask0.txt | ├── mask_withoutmask2.txt | ├── ... └── val ├── images | ├── mask_withoutmask1.jpg | ├── mask_withoutmask3.jpg | ├── ... ├── labels | ├── mask_withoutmask1.txt | ├── mask_withoutmask3.txt | ├── ...
这里博主提供了转化的python代码,将split_train_val.py代码放置到datasetsTool目录下。
import os import shutil import random image_dir = r'JPEGImages' # 替换为你的图像文件所在目录 label_dir = r'labels' # 刚才生成的txt文件 ''' eg: image_dir = r'E:\BaiduNetdiskDownload\VOC2007\JPEGImages' label_dir = r'E:\BaiduNetdiskDownload\VOC2007\labels' ''' train_dir = r'datasets/maskdetection/train' # 口罩数据集的存放位置 val_dir = r'datasets/maskdetection/val' ''' eg: train_dir = r'E:\Ultralytics-YOLO\yolov5\datasets\maskdetection\train' val_dir = r'E:\Ultralytics-YOLO\yolov5\datasets\maskdetection\val' ''' # 创建新的文件夹 os.makedirs(os.path.join(train_dir, 'images'), exist_ok=True) os.makedirs(os.path.join(train_dir, 'labels'), exist_ok=True) os.makedirs(os.path.join(val_dir, 'images'), exist_ok=True) os.makedirs(os.path.join(val_dir, 'labels'), exist_ok=True) # 获取所有图像文件名 image_files = [f for f in os.listdir(image_dir) if f.endswith('.jpg')] # 设置随机种子以保证结果可复现 random.seed(42) # 打乱文件列表 random.shuffle(image_files) # 计算训练集和验证集的数量 split_index = int(0.8 * len(image_files)) train_files = image_files[:split_index] val_files = image_files[split_index:] # 复制训练集文件 for file_name in train_files: image_path = os.path.join(image_dir, file_name) label_path = os.path.join(label_dir, os.path.splitext(file_name)[0] + '.txt') # 复制图像文件 shutil.copy(image_path, os.path.join(train_dir, 'images', file_name)) # 复制标签文件 shutil.copy(label_path, os.path.join(train_dir, 'labels', os.path.splitext(file_name)[0] + '.txt')) # 复制验证集文件 for file_name in val_files: image_path = os.path.join(image_dir, file_name) label_path = os.path.join(label_dir, os.path.splitext(file_name)[0] + '.txt') # 复制图像文件 shutil.copy(image_path, os.path.join(val_dir, 'images', file_name)) # 复制标签文件 shutil.copy(label_path, os.path.join(val_dir, 'labels', os.path.splitext(file_name)[0] + '.txt')) print("数据集划分完成")
-
配置maskdetection.yaml:博主根据data/coco128.yaml文件,在data目录下同样配置了口罩目标检测的数据集配置文件maskdetection.yaml。
path: datasets/maskdetection # 数据集路径 train: train/images # 训练集 val: val/images # 验证集 nc: 2 # 类别数量 names: # 类别名:避免用中文 0: without-mask 1: mask
-
配置yolov5s-mask.yaml:博主根据models/yolov5s.yaml文件,在models目录下同样配置了口罩目标检测的网络配置文件yolov5s-mask.yaml。
# 复制yolov5s.yaml只需修改类别数量 nc: 2 # 类别数量
YOLOV5运行
运行yolov5,建议增加虚拟内存!!!!不然内存不足会导致很多错误!!!
模型训练
train.py配置训练参数:在有标注的者训练集进行模型的训练,并在验证集上评估。
常用参数含义
weights:指定预训练模型的权重文件;
cfg:存储模型结构的配置文件;
data:存储训练、测试数据的配置文件;
batch-size:一次训练的图片数量;
img:输入图片宽高,根据需求和硬件条件修改;
device:模型运行的设备,cuda 0,1,2,3或者cpu.
其他参数在后续讲解具体代码的过程中再去解释。
训练运行以下命令:
python train.py --img 640 --epochs 300 --data data/maskdetection.yaml --batch-size 16 --weights yolov5s.pt --cfg yolov5s-mask.yaml --device 0
weights 参数和 cfg 参数对应的模型有冲突,以 cfg 指定的模型为基准。
可能出现的问题:
1.问题:“_pickle.UnpicklingError: Weights only load failed. This file can still be loaded, to do so you have two options, do those steps only if you trust the source of the checkpoint.”
解决方式:
# 修改前
ckpt = torch.load(weights, map_location='cpu') # load checkpoint to CPU to avoid CUDA memory leak
# 修改后
ckpt = torch.load(weights, map_location='cpu', weights_only=False) # load checkpoint to CPU to avoid CUDA memory leak
2.问题:“AttributeError: ‘FreeTypeFont’ object has no attribute ‘getsize.’”
解决方式:
# 修改前
w, h = self.font.getsize(label) # text width, height
w, h = self.font.getsize(text) # text width, height
# 修改后
w, h = self.font.getbbox(label)[2:4] # text width, height
w, h = self.font.getbbox(text)[2:4] # text width, height
模型验证
val.py参数验证配置:在有标注的者验证集上进行模型效果的评估模型好坏,目标检测中最常使用的评估指标为mAP。
常用参数含义
data:存储训练、测试数据的配置文件;
weights:指定训练好的模型权重文件;
batch-size:一次验证的图片数量;
img:输入图片宽高,根据需求和硬件条件修改;
device:模型运行的设备,cuda 0,1,2,3或者cpu;
augment:额外的数据增强.
其他参数在后续讲解具体代码的过程中再去解释。
验证运行以下命令:
python val.py --img 640 --data data/maskdetection.yaml --batch-size 16 --weights runs/train/exp/weights/best.pt --device 0 --augment
这里博主随便找了一次训练过程中的中间训练权重进行演示,所以精度不是很高。
模型推理
detect.py配置推理参数:在没有标注的数据集上进行推理。
常用参数含义
weights:指定训练好的模型权重文件;
source:测试图片的保存路径;
device:模型运行的设备,cuda 0,1,2,3或者cpu;
–conf-thres:指定置信度阈值;
–iou-thres:非极大值抑制IoU 阈值;
其他参数在后续讲解具体代码的过程中再去解释。
推理运行以下命令:
python detect.py --weights runs/train/exp/weights/best.pt --source data/images --device 0 --conf-thres 0.7 --iou-thres 0.3
在runs/detect/exp获得输出:
部分读者可能需要博文中训练好的权重文件best.pt(此前的人脸检测)【百度云下载,提取码: meqc 】。
导出onnx模型
export.py配置推理参数:
常用参数含义
weights:指定训练好的模型权重文件;
include:导出的模型类型;
其他参数在后续讲解具体代码的过程中再去解释。
导出运行以下命令:
python export.py --weights runs/train/exp/weights/best.pt --include onnx
总结
尽可能简单、详细的介绍了YOLOV5的安装流程以及YOLOV5的使用方法。后续会根据自己学到的知识结合个人理解讲解YOLOV5的原理和代码。