文章目录
- 1、运行官网YOLOV5代码
- 1、下载源代码
- 2、文件夹解析
- 3、开始测试
- 1、图片测试
- 2、视频测试
- 3、摄像头实时测试
- 2、训练自己的神经网络模型
- 1、数据集制作
- 2、划分数据集
- 3、开始训练
- 4、训练参数
- 5、使用训练好的模型进行预测
- 3、获取目标中心坐标
本文仅讨论YOLOV5的目标检测使用过程的一些记录,不涉及CUDA的配置,以及一些环境的配置问题,相关问题可以参考我的其他博客。
1、运行官网YOLOV5代码
1、下载源代码
首先肯定找开源代码,目前似乎大部分代码都是在下面的代码修改而来的,我们从git上找到大神的开源代码:
ultralytics/yolov5 at v5.0 (github.com)
这里在旁边的分支这里选择v5.0的分支,全文下载也可以,或者电脑如果有git,也可以下面的命令来获取
git clone xxx
下载页如图所示
这个下完只有1.4M,直觉高速我们肯定不对,所以我们需要看看缺了啥,没错还需要把权重下载下来,打开这个sh文件就可以看到了,这里我们可以之间点到这个链接里面去下载
进去之后页面下拉,找到需要的版本下载权重,因为下面的无论是训练还是进行目标检测都需要这个权重,这个也可以理解为就是一个模型文件,一个训练好的模型文件
其实官方模型是一个20类物体的目标见的模型,是比较常见的模型,在data的voc的配置文件里面我们可以看到里面的内容,下面的修改也是基于这个进行的
2、文件夹解析
这里参考了:目标检测—教你利用yolov5训练自己的目标检测模型
下面对一些重要的文件夹做下说明
data文件夹:
- 主要是存放一些超参数的配置文件,就是yaml文件,就是一些数据集的配置文件。
- 官方提供测试的图片,当然自己的测试图片也可以使用的。
- 如果是训练自己的数据集的话,那么就需要修改其中的yaml文件。
这里其实其他文件没什么用。.sh是用来下载的,所以干脆就都删掉,只留下一个
models文件夹:
- 里面主要是一些网络构建的配置文件和函数,其中包含了该项目的四个不同的版本,分别为是s、m、l、x。
- 因此就是前面变了这里也要做相应的改变
utils文件夹:
- 存放的是工具类的函数,里面有loss函数,metrics函数,plots函数等等。
weights文件夹:
- 放置训练好的权重参数,这里默认也是一个sh文件,就是下载链接,如果网速不行就先下载好吧,这样就不用下载了
下面是一些重要的py文件的介绍
- detect.py:利用训练好的权重参数进行目标检测,可以进行图像、视频和摄像头的检测。
- train.py:训练自己的数据集的函数。
- test.py:测试训练的结果的函数。
3、开始测试
官方文件下载之后就可以直接进行测试的,这里主要是图片,视频还有摄像头部分的测试
1、图片测试
这里修改下预设的参数即可,就是jpg文件
然后将我们之前准备好的权重文件放到同一路径下,这里选了什么权重路径上就写什么,下面的测试都是基于这个权重来进行的
图片用的是官方的图片,运行之后会run这个文件下面显示出相关的结果
结果如下,还是比较准确的
2、视频测试
视频的部分这个就是将图片修改为对应的视频即可
这里我用了一张道路行车的视频,运行的结果如下
3、摄像头实时测试
实时检测这里需要我们将路径修改为自己的摄像头号码,比如电脑默认的摄像头就是0,这里我用的外接的摄像头,就是1了
但是经过实测好像运行不起来,因为我是去年就跑过这个,所以我之前也下过相应的权重文件,然后这次写这篇文章也下了一次权重文件,我发现两个权重文件报错不太一样,具体请看下面的描述:
第一是使用最新的权重存在的问题,报错信息如下,据说是新的加上了这个SPPF的模块,所以需要到源文件里面加上这个部分
这里其他博主的建议是加上这个,就是缺失的SPPF加上,因为这个权重其实是yolov5-6.0版本的权重了,yolov5更新的很快,据官方描述使用这个模块检测速度提升了很多,但是咱这里用不到,所以就试试吧
import warnings
class SPPF(nn.Module):
# Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
super().__init__()
c_ = c1 // 2 # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_ * 4, c2, 1, 1)
self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
def forward(self, x):
x = self.cv1(x)
with warnings.catch_warnings():
warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
y1 = self.m(x)
y2 = self.m(y1)
return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1))
但是修改后就会报这个错误,又要去改其他的,干脆就不用这个6.0版本的权重了,直接换到下面的5.0版本的,就是下面的一种方案了
第二是旧的模型会出现的问题,会报这样一个错误,但是还是可以运行,不过只有一张图显示出来,后面的就卡住了,网上说原因是数据集制作的问题,就是数据集里面有的数据集是空的,没标注就会出现这样的问题
网上提出的解决办法是需要对数据集重新处理,就是标注的数据集出现了没有标注框的情况,需要把这个部分给他清理掉,但是这样就需要重新训练了,太蛮烦了,这里我觉得不如直接就给他处理掉,把这一行代码注释掉即可
注释掉就可以进行实时的摄像头检测了
2、训练自己的神经网络模型
1、数据集制作
制作软件:labellmg,这里这个软件觉得比较重要的就是快捷键,快捷键很重要啊,一定要看一看,不然标到怀疑人生,其他的就没什么重要的了,软件基本就是导入图片,然后画框,会导出一个xml文件,这个xml文件就是框的一些信息,位置大小之类的,这样就可以给后面的数据集训练使用了。
快捷键如下所示:
- A:切换到上一张图片
- D:切换到下一张图片
- W:调出标注十字架
- del :删除标注框框
- Ctrl+u:选择标注的图片文件夹
- Ctrl+r:选择标注好的label标签存在的文件夹
我们经常从网上获取一些目标检测的数据集资源标签的格式都是VOC(xml格式)的,而yolov5训练所需要的文件格式是yolo(txt格式)的,这里就需要对xml格式的标签文件转换为txt文件。同时训练自己的yolov5检测模型的时候,数据集需要划分为训练集和验证集。这里提供了一份代码将xml格式的标注文件转换为txt格式的标注文件,并按比例划分为训练集和验证集。先上代码再讲解代码的注意事项。
2、划分数据集
这里采用的voc2007的数据集格式,参考文章如下:
首先是修改配置文件,需要修改的如下所示
这里再次修改训练用的配置文件,其实都是一些必要的信息还有路径什么的
之后就是数据集部分了,这里先进行新建文件夹
这里上面的博主提供了将我们的标号的数据集转成voc格式的代码,修改一点参数就可以使用了,可以在这里看到标签种类和一些训练集占比的修改
输出的yolo使用的txt文件
运行之后可以看到生成了我们训练需要的文件
源代码如下:本处代码来源
import xml.etree.ElementTree as ET
import os
import random
from shutil import copyfile
classes = ["hat", "person"]
TRAIN_RATIO = 80
def clear_hidden_files(path):
dir_list = os.listdir(path)
for i in dir_list:
abspath = os.path.join(os.path.abspath(path), i)
if os.path.isfile(abspath):
if i.startswith("._"):
os.remove(abspath)
else:
clear_hidden_files(abspath)
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def convert_annotation(image_id):
in_file = open('VOCdevkit/VOC2007/Annotations/%s.xml' % image_id)
out_file = open('VOCdevkit/VOC2007/YOLOLabels/%s.txt' % image_id, 'w')
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
in_file.close()
out_file.close()
wd = os.getcwd()
wd = os.getcwd()
data_base_dir = os.path.join(wd, "VOCdevkit/")
if not os.path.isdir(data_base_dir):
os.mkdir(data_base_dir)
work_sapce_dir = os.path.join(data_base_dir, "VOC2007/")
if not os.path.isdir(work_sapce_dir):
os.mkdir(work_sapce_dir)
annotation_dir = os.path.join(work_sapce_dir, "Annotations/")
if not os.path.isdir(annotation_dir):
os.mkdir(annotation_dir)
clear_hidden_files(annotation_dir)
image_dir = os.path.join(work_sapce_dir, "JPEGImages/")
if not os.path.isdir(image_dir):
os.mkdir(image_dir)
clear_hidden_files(image_dir)
yolo_labels_dir = os.path.join(work_sapce_dir, "YOLOLabels/")
if not os.path.isdir(yolo_labels_dir):
os.mkdir(yolo_labels_dir)
clear_hidden_files(yolo_labels_dir)
yolov5_images_dir = os.path.join(data_base_dir, "images/")
if not os.path.isdir(yolov5_images_dir):
os.mkdir(yolov5_images_dir)
clear_hidden_files(yolov5_images_dir)
yolov5_labels_dir = os.path.join(data_base_dir, "labels/")
if not os.path.isdir(yolov5_labels_dir):
os.mkdir(yolov5_labels_dir)
clear_hidden_files(yolov5_labels_dir)
yolov5_images_train_dir = os.path.join(yolov5_images_dir, "train/")
if not os.path.isdir(yolov5_images_train_dir):
os.mkdir(yolov5_images_train_dir)
clear_hidden_files(yolov5_images_train_dir)
yolov5_images_test_dir = os.path.join(yolov5_images_dir, "val/")
if not os.path.isdir(yolov5_images_test_dir):
os.mkdir(yolov5_images_test_dir)
clear_hidden_files(yolov5_images_test_dir)
yolov5_labels_train_dir = os.path.join(yolov5_labels_dir, "train/")
if not os.path.isdir(yolov5_labels_train_dir):
os.mkdir(yolov5_labels_train_dir)
clear_hidden_files(yolov5_labels_train_dir)
yolov5_labels_test_dir = os.path.join(yolov5_labels_dir, "val/")
if not os.path.isdir(yolov5_labels_test_dir):
os.mkdir(yolov5_labels_test_dir)
clear_hidden_files(yolov5_labels_test_dir)
train_file = open(os.path.join(wd, "yolov5_train.txt"), 'w')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'w')
train_file.close()
test_file.close()
train_file = open(os.path.join(wd, "yolov5_train.txt"), 'a')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'a')
list_imgs = os.listdir(image_dir) # list image files
prob = random.randint(1, 100)
print("Probability: %d" % prob)
for i in range(0, len(list_imgs)):
path = os.path.join(image_dir, list_imgs[i])
if os.path.isfile(path):
image_path = image_dir + list_imgs[i]
voc_path = list_imgs[i]
(nameWithoutExtention, extention) = os.path.splitext(os.path.basename(image_path))
(voc_nameWithoutExtention, voc_extention) = os.path.splitext(os.path.basename(voc_path))
annotation_name = nameWithoutExtention + '.xml'
annotation_path = os.path.join(annotation_dir, annotation_name)
label_name = nameWithoutExtention + '.txt'
label_path = os.path.join(yolo_labels_dir, label_name)
prob = random.randint(1, 100)
print("Probability: %d" % prob)
if (prob < TRAIN_RATIO): # train dataset
if os.path.exists(annotation_path):
train_file.write(image_path + '\n')
convert_annotation(nameWithoutExtention) # convert label
copyfile(image_path, yolov5_images_train_dir + voc_path)
copyfile(label_path, yolov5_labels_train_dir + label_name)
else: # test dataset
if os.path.exists(annotation_path):
test_file.write(image_path + '\n')
convert_annotation(nameWithoutExtention) # convert label
copyfile(image_path, yolov5_images_test_dir + voc_path)
copyfile(label_path, yolov5_labels_test_dir + label_name)
train_file.close()
test_file.close()
运行就可以将我们标好的数据转成我们需要的格式了!
3、开始训练
训练就进入到train.py这里了,还是一样的,先进行参数修改
下面还有一些比较重要的参数吧,可以参考的看一看(根据实际情况进行修改)
之后就可以运行代码了,可以使用tensorboard 实时查看训练过程,输入下面的命令(当然这个要提前安装好)
tensorboard --logdir=runs/train
点开下面的网址就可以查看了
主要是map,准确率和召回率
4、训练参数
训练完成后会输出一个文件夹,可以在文件夹里面看一些训练的参数,文件夹打开如下所示
我个人的话主要看这个图吧
这部分参数的含义如下所示:
- 边框检测的损失,越小越准确
- Objectness:推测为目标检测loss均值,越小目标检测越准
- Classification:推测为分类loss均值,越小分类越准
- Precision:准确率(找对的/找到的)
- Recall:召回率(找对的/该找对的)
- mAP@0.5 & mAP@0.5:0.95:AP是用Precision和Recall作为两轴作图后围成的面积,m表示平均,@后面的数表示判定iou为正负样本的阈值,@0.5:0.95表示阈值取0.5:0.05:0.95后取均值。
本处参考:
5、使用训练好的模型进行预测
这里其实就是跑到原来那块检测那里了,就是修改下模型文件就行了
3、获取目标中心坐标
一般用yolo进行目标追踪的话,需要进行查找中心位置,当然这是很传统的方法了,所以下面就看看这种经典的方案
我们进入这个函数就能看到画框函数
在里面加上一句
print("中心位置的坐标为"+str((c1[0]+c2[0])/2)+','+str((c1[1]+c2[1])/2))
这样就可以了,运行即可看到结果