无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集

前言

避坑autolabelimg,这个自动标注软件,因为他只支持yolov5s的训练模型,而且只支持检测coco数据集中的80个类别。
这个软件我尝试了很久,找了很多方法都不行,遂放弃了,到头来,发现yolov5就自带了保存预测标签的功能。在detect.py的参数里,–save-txt
下边是我目标检测训练的两个小玩具,小王子(prince)和加菲猫(garfield)
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第1张图片
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第2张图片

yolov5官网链接
https://github.com/ultralytics/yolov5

目录

本文流程分为两个主要部分:
1.下载安装yolov5进行推理测试
2.自己标注数据
3.训练自己的专属模型
4.使用自己的模型进行剩余图片的自动标注

1. yolov5 安装测试

把yolov5的环境搭建一下:
我的win11笔记本电脑联想拯救者2018款GPU 1060 , CUDA 10.1 所以安装Pytorch 1.7.1

 conda install pytorch==1.7.1 torchvision==0.8.2 torchaudio==0.7.2 cudatoolkit=10.1 -c pytorch

然后下载官方的代码
https://github.com/ultralytics/yolov5
这里点击code,点击 download zip就下载好了安装包。

然后在终端界面使用cd的方式进入目录该目录

pip install -r requirements.txt  # install

然后去官网下载yolov5的模型进行测试,我这里准备了四个模型来测试,下载到yolov5/pt文件夹中,这个文件夹是我自己新建的
如图所示:
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第3张图片
然后我新建了 demoimg文件夹放测试图片:
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第4张图片
运行代码:

python detect.py --weights .\pt\yolov5m.pt --source .\demoimg\

如果报错找不到pillow库可以参考附录1:
运行结果保存到exp文件夹中了,去查看

(yolov5) PS G:\bsh\yolov5\yolov5-master> python detect.py --weights .\pt\yolov5m.pt --source .\demoimg\
detect: weights=['.\\pt\\yolov5m.pt'], source=.\demoimg\, data=data\coco128.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs\detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=False, vid_stride=1
YOLOv5  2023-7-20 Python-3.8.17 torch-1.7.1 CUDA:0 (NVIDIA GeForce GTX 1060, 6144MiB)

Fusing layers...
YOLOv5m summary: 290 layers, 21172173 parameters, 0 gradients, 48.9 GFLOPs
image 1/2 G:\bsh\yolov5\yolov5-master\demoimg\1.jpg: 448x640 1 zebra, 24.0ms
image 2/2 G:\bsh\yolov5\yolov5-master\demoimg\2.jpg: 576x640 2 persons, 2 cars, 1 frisbee, 28.0ms
Speed: 2.0ms pre-process, 26.0ms inference, 2.0ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs\detect\exp

检测结果还是一如既往的好,标注框要比yolov7自带的粗许多
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第5张图片
然后准备标注数据集。

2. 标注图片

是我自己拍的200张照片,使用手机快门按住不松手,一次拍20张,然后拍10次,样本图片如下图,是两个小玩偶,一个是小王子(Prince),一个是加菲猫(Garfield)
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第6张图片
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第7张图片
手动标注36张图片,然后整合成数据集
参考链接:
http://t.csdn.cn/4fmx4

找到如下文件进行种类的修改:
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第8张图片
我删掉了,然后改为
prince
garfield
在yolov5的数据集文件夹中新建两个文件夹
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第9张图片
运行autolabelimg

python .\labelImg.py

在运行的label软件勾选 view-Auto save mode
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第10张图片

然后这个图是参考链接中的,和autolabelimg差不多,但是一个是open img,一个是open xml,一个意思,格式还是voc格式

无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第11张图片
点击create box进行标注,我大概标注了36张,因为一开始标注了20张训练效果不好
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第12张图片

无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第13张图片

可以点开看看样子,用记事本打开,这是voc格式,待会把他们转换成coco格式
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第14张图片
然后划分划分训练集、验证集、测试集
Annotationimages相同目录放一个split_train_val.py文件
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第15张图片
然后把split_train_val.py文件代码粘贴上:

# coding:utf-8

import os
import random
import argparse

parser = argparse.ArgumentParser()
#xml文件的地址,根据自己的数据进行修改 xml一般存放在Annotations下
parser.add_argument('--xml_path', default='Annotations', type=str, help='input xml label path')
#数据集的划分,地址选择自己数据下的ImageSets/Main
parser.add_argument('--txt_path', default='ImageSets/Main', type=str, help='output txt label path')
opt = parser.parse_args()

trainval_percent = 1.0  # 训练集和验证集所占比例。 这里没有划分测试集
train_percent = 0.8     # 训练集所占比例,可自己进行调整
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
    os.makedirs(txtsavepath)

num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)

file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')

for i in list_index:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        file_trainval.write(name)
        if i in train:
            file_train.write(name)
        else:
            file_val.write(name)
    else:
        file_test.write(name)

file_trainval.close()
file_train.close()
file_val.close()
file_test.close()

直接运行

python 

会自动生成imagesets文件夹,里边还有个Main文件夹,Main里边有几个txt文件

无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第16张图片
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第17张图片
然后再把数据集的voc格式改为coco格式,在datavivoimg目录下创建程序 text_to_yolo.py 并运行

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd

sets = ['train', 'val', 'test']
classes = ["prince", "garfield"]  # 改成自己的类别
img_path = "D:/me/ultraxxxxxxin/datasets/luosi/"  # 改成自己的路径
print(img_path)


def convert(size, box):
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    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(img_path+'Annotations/%s.xml' % (image_id), encoding='UTF-8')
    out_file = open(img_path'labels/%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
        # 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))
        b1, b2, b3, b4 = b
        # 标注越界修正
        if b2 > w:
            b2 = w
        if b4 > h:
            b4 = h
        b = (b1, b2, b3, b4)
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


wd = getcwd()
for image_set in sets:
    if not os.path.exists(img_path+'labels/'):
        os.makedirs(img_path+'labels/')
    image_ids = open(img_path+'ImageSets/Main/%s.txt' % (image_set)).read().strip().split()

    if not os.path.exists(img_path+'dataSet_path/'):
        os.makedirs(img_path+'dataSet_path/')

    list_file = open('dataSet_path/%s.txt' % (image_set), 'w')
    # 这行路径不需更改,这是相对路径
    for image_id in image_ids:
        list_file.write(img_path+'images/%s.jpg\n' % (image_id))
        convert_annotation(image_id)
    list_file.close()


然后运行:

(yolov5) PS G:\bsh\yolov5\yolov5-master\datavivoimg> python .\text_to_yolo.py
#如果运行成功,会输出如下代码:
G:\bsh\yolov5\yolov5-master\datavivoimg

然后去查看
labels里边的标签和dataset_path里的文件
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第18张图片
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第19张图片
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第20张图片

3. yolov5训练得到模型文件

首先配置自己数据集的yaml文件:
在 yolov5 目录下的 data 文件夹下 新建一个 vivo.yaml文件(可以自定义命名),用记事本打开
内容如下,修改的时候注意空格问题,一定要严格按照格式来修改:

train: G:/bsh/yolov5/yolov5-master/datavivoimg/dataSet_path/train.txt
val: G:/bsh/yolov5/yolov5-master/datavivoimg/dataSet_path/val.txt

# number of classes
nc: 2

# class names
names: ["prince", "garfield"]

然后注意,不需要修改yolov5的配置文件(这里不需要修改,其实上边的vivo.yaml里修改了numclass之后会自动覆盖)

在这里插入图片描述

无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第21张图片

然后进行训练

 python train.py --weights pt/yolov5m.pt  --cfg models/yolov5m.yaml  --data data/vivo.yaml --epoch 200 --batch-size 6 --img 640

按照我的参数,我笔记本的gpu 1060,大概1-2个小时,挺快的。
如果训练报错缺少库要自己改一改,
例如我缺少了git库就去conda install git,
而且pillow库版本9.3过高不匹配,我就修改到pip install pillow==8.0
因为我batchsize=8报错memory异常,所以调小到6,然后开始训练:

训练结果如下:

YOLOv5m summary: 212 layers, 20856975 parameters, 0 gradients, 47.9 GFLOPs
                 Class     Images  Instances          P          R      mAP50   mAP50-95: 100%|██████████| 1/1 [00:00<00:00,  2.40it/s]
                   all          8          8      0.933          1      0.995      0.651
                prince          8          5      0.989          1      0.995      0.687
              garfield          8          3      0.876          1      0.995      0.614
Results saved to runs\train\exp7


可以看到,36张图片,训练了200epoch就达到了99.5%的mAP,效果还是非常好的。

4. 使用detect.py自动标注

通过设置参数,打开detect.py,修改save–text 参数,值为’store_false’
在这里插入图片描述
该参数的意思是是否保存检测后后的结果,如框的位置大小,框的物体类别。注意,store_false才是保存检测的结果
运行语句:

python detect.py --weights .\runs\train\exp7\weights\prigarbest.pt --source .\datavivoimg\images\  

就可以把images文件夹下的所有图像标注出来了。这是运行结束的效果:

image 197/200 G:\bsh\yolov5\yolov5-master\datavivoimg\images\IMG_20230719_230826_Burst17.jpg: 640x480 1 garfield, 34.0ms
image 198/200 G:\bsh\yolov5\yolov5-master\datavivoimg\images\IMG_20230719_230826_Burst18.jpg: 640x480 1 garfield, 34.0ms
image 199/200 G:\bsh\yolov5\yolov5-master\datavivoimg\images\IMG_20230719_230826_Burst19.jpg: 640x480 1 garfield, 34.0ms
image 200/200 G:\bsh\yolov5\yolov5-master\datavivoimg\images\IMG_20230719_230826_Burst20.jpg: 640x480 1 garfield, 34.0ms
Speed: 0.6ms pre-process, 35.1ms inference, 1.9ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs\detect\exp5
200 labels saved to runs\detect\exp5\labels

然后去exp5中查看效果:
所有的图片都被标注了,labels统一存放了,可以把labels复制到dataset中去,进行以后的训练了,全文完结,撒花~
无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第22张图片

无脑008——yolov5目标检测全流程,训练自己的庞大数据集,半自动标注数据集_第23张图片
然后挨着打开标注好的图片对满意的图片进行保留,不想要就按del进行删除,最后使用choose.py进行对应标签的保留,其他的标签删掉

import os

# 指定exp72文件夹路径和labels文件夹路径
imgs_folder_path = 'G:/bsh/yolov8/runs/detect/predict21/'
labels_folder_path = 'G:/bsh/yolov8/runs/detect/predict21/labels'

# 获取exp72文件夹中的所有图片文件名(不含扩展名)
# image_names = [os.path.splitext(file)[0] for file in os.listdir(imgs_folder_path) if file.endswith('.jpg')]
image_names = [os.path.splitext(file)[0] for file in os.listdir(imgs_folder_path) if file.endswith('.bmp')]

# 获取labels文件夹中的所有txt文件名
txt_files = [file for file in os.listdir(labels_folder_path) if file.endswith('.txt')]

# 遍历labels文件夹中的txt文件
for txt_file in txt_files:
    # 提取txt文件名(不含扩展名)
    txt_file_name = os.path.splitext(txt_file)[0]

    # 检查txt文件名是否在图片文件名列表中
    if txt_file_name not in image_names:
        # 不在图片文件名列表中的txt文件,删除
        txt_file_path = os.path.join(labels_folder_path, txt_file)
        os.remove(txt_file_path)

然后把留下的标签复制粘贴到数据集的labels文件夹里,这个时候需要重新分配训练集和验证集,需要运行split_train_val.py,这个时候注意:是把上边的路径修改以下:
意思就是从labels里便读取标签进行重新分配

#parser.add_argument('--xml_path', default='G:/bsh/dataset/luosi/Annotations', type=str, help='input xml label path')
parser.add_argument('--xml_path', default='G:/bsh/dataset/luosi/labels', type=str, help='input xml label path')

然后继续运行text_to_yolo.py,这里其实原作者写的不好,应该先转换格式再分配路径,以后有机会我改改。这里先不管了,继续运行,记得注释掉转换格式的代码(因为已经标注的就是yolo格式的txt,不需要再转换了)
注释后的代码长这样

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd
dataAllPath = 'G:/bsh/dataset/luosi/'
sets = ['train', 'val', 'test']
# classes = ["yuanguojia","naiguojia","huanhuogai","dianchi","shuomingshu","paomoban","xiaohuogai"]  # 改成自己的类别
classes = ["luosi"]  # 改成自己的类别
print(dataAllPath)

# 使用detect.py产生的标签不需要进行转换
# def convert(size, box):
#     dw = 1. / (size[0])
#     dh = 1. / (size[1])
#     x = (box[0] + box[1]) / 2.0 - 1
#     y = (box[2] + box[3]) / 2.0 - 1
#     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(dataAllPath+'Annotations/%s.xml' % (image_id), encoding='UTF-8')
#     out_file = open(dataAllPath+'labels/%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
#         # 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))
#         b1, b2, b3, b4 = b
#         # 标注越界修正
#         if b2 > w:
#             b2 = w
#         if b4 > h:
#             b4 = h
#         b = (b1, b2, b3, b4)
#         bb = convert((w, h), b)
#         out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


wd = getcwd()
for image_set in sets:
    if not os.path.exists(dataAllPath+'labels'):
        os.makedirs(dataAllPath+'labels/')
    image_ids = open(dataAllPath+'ImageSets/Main/%s.txt' % (image_set)).read().strip().split()

    if not os.path.exists(dataAllPath+'dataSet_path/'):
        os.makedirs(dataAllPath+'dataSet_path/')

    list_file = open(dataAllPath+'dataSet_path/%s.txt' % (image_set), 'w')
    # 这行路径不需更改,这是相对路径
    for image_id in image_ids:
        # list_file.write(dataAllPath+'images/%s.jpg\n' % (image_id))
        # list_file.write(dataAllPath+'images/%s.bmp\n' % (image_id))
        list_file.write(dataAllPath+'images/%s.png\n' % (image_id))
        # convert_annotation(image_id)
    list_file.close()

然后就可以继续运行训练了,这个时候就完成了一次完整闭环的训练循环,用少数样本训练小模型,用小模型预测结果自动保存标签,人工筛选满意的结果进行保存,然后汇总到labels一起合并训练,如果不满意可以继续循环训练,直到达到满意的指标
完结撒花~~!

附录 遇到的错误

1.缺少pillow库

具体表现为运行detect.py报错:

(yolov5) PS G:\bsh\yolov5\yolov5-master> python detect.py --weights .\pt\yolov5m.pt --source .\demoimg\
Traceback (most recent call last):
  File "detect.py", line 45, in <module>
    from models.common import DetectMultiBackend
  File "G:\bsh\yolov5\yolov5-master\models\common.py", line 24, in <module>
    from PIL import Image
  File "F:\APP\miniconda\envs\yolov5\lib\site-packages\PIL\Image.py", line 100, in <module>
    from . import _imaging as core
ImportError: DLL load failed while importing _imaging: 找不到指定的模块。

推测原因是pillow库是pip requirements自动安装的最新版本9.3.0,和我的pytorch不兼容,所以降级

pip install pillow==8.0

然后就可以运行了。

2.detect.py适用范围

官网的代码参数可以调用电脑摄像头等数据来源,供大家参考

python detect.py --weights yolov5s.pt --source 0                               # webcam
                                               img.jpg                         # image
                                               vid.mp4                         # video
                                               screen                          # screenshot
                                               path/                           # directory
                                               list.txt                        # list of images
                                               list.streams                    # list of streams
                                               'path/*.jpg'                    # glob
                                               'https://youtu.be/Zgi9g1ksQHc'  # YouTube
                                               'rtsp://example.com/media.mp4'  # RTSP, RTMP, HTTP stream

你可能感兴趣的:(无脑100,python,目标检测)