YOLOV5目标检测记录

本文仅讨论YOLOV5的目标检测使用过程的一些记录,不涉及CUDA的配置,以及一些环境的配置问题,相关问题可以参考我的其他博客。

1、运行官网YOLOV5代码

1、下载源代码

首先肯定找开源代码,目前似乎大部分代码都是在下面的代码修改而来的,我们从git上找到大神的开源代码:

ultralytics/yolov5 at v5.0 (github.com)[0]

这里在旁边的分支这里选择v5.0的分支,全文下载也可以,或者电脑如果有git,也可以下面的命令来获取

git clone xxx

下载页面如图

这个下完只有1.4M,直觉高速我们肯定不对,所以我们需要看看缺了啥,没错还需要把权重下载下来,打开这个sh文件就可以看到了,这里我们可以之间点到这个链接里面去下载

进入后,下拉页面,找到需要的版本下载权重,因为训练和目标检测都需要以下权重。这也可以理解为模型文件,训练好的模型文件。

其实官方模型是一个20类物体的目标见的模型,是比较常见的模型,在data的voc的配置文件里面我们可以看到里面的内容,下面的修改也是基于这个进行的

2、文件夹解析

这里参考了:目标检测—教你利用yolov5训练自己的目标检测模型[0]

下面是一些重要文件夹的说明

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的数据集格式,参考文章如下:

https://blog.csdn.net/didiaopao/article/details/120022845?spm=1001.2014.3001.5501

首先是修改配置文件,需要修改如下

这里再次修改了训练的配置文件。其实就是一些必要的信息和路径。

之后是数据集部分,这里先新建一个文件夹

这里上面的博主提供了将我们的标号的数据集转成voc格式的代码,修改一点参数就可以使用了,可以在这里看到标签种类和一些训练集占比的修改

输出的yolo使用的txt文件

运行后可以看到生成了我们训练需要的文件

源码如下: 代码的来源在这里

https://blog.csdn.net/didiaopao/article/details/120022845?spm=1001.2014.3001.5501

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后取均值。

参考这里:

https://blog.csdn.net/flyfish1986/article/details/118858068

5、使用训练好的模型进行预测

其实这里是去原始检测,只是修改模型文件。

3、获取目标中心坐标

一般用yolo进行目标追踪的话,需要进行查找中心位置,当然这是很传统的方法了,所以下面就看看这种经典的方案

当我们进入这个函数时,我们可以看到相框函数

加一句

print("中心位置的坐标为"+str((c1[0]+c2[0])/2)+','+str((c1[1]+c2[1])/2))

就是这样,运行它看看结果

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2022年5月9日
下一篇 2022年5月9日

相关推荐