YOLOv3 训练自己的数据集

 

关于YOLOv3的介绍不再多言,可以看一下论文。本文旨在尝试使用利用YOLO训练自己的数据集。

一、 环境

建议使用anaconda搭建环境。

建立一个名为yolov3(自定义)的环境,并安装pytorch和pillow。

conda create -n yolov3 python=3.7 pytorch=1.4 pillow

激活yolov3环境。

activate yolov3

安装opencv用pip比较方便。

pip install opencv-python

至此,关键的包就装完了,其余包跑的时候发现错误随便用pip或conda装一下就好。

二、下载并测试YOLO

这里选择github上ultralytics的基于pytorch的实现。找一个路径,

git clone https://github.com/ultralytics/yolov3.git

或者直接到https://github.com/ultralytics/yolov3下载。

其中weights文件在google云盘,不能**的同学可以在在这里下载自己需要的weights文件。下载后放到weights文件夹下

链接:https://pan.baidu.com/s/18Q0T5OSsYt_ubfP0QmzlLg 
提取码:w05u 

有兴趣的同学可以仔细阅读以下README文件。

使用摄像头进行测试:

python detect.py --cfg cfg/yolov3-tiny.cfg --weights weights/yolov3-tiny.weights --source 0

正常情况下你会看到一个被persion框圈起来的自己。

当然你也可以选择不用摄像头,把--source后面的0改成文件(图片or视频)就好,那样你就可以在output文件夹下找到对应的处理完的图像or视频。

三、训练自己的数据集

关于训练自己的数据集也可以看看原文档https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data

1. 收集照片

提供一个自己拍摄照片的脚本:

import cv2
import os

data_path = './data'  # 数据储存的文件夹
dataset_name = 'your_dataset'  # 数据集名称
image_path = data_path+'/'+dataset_name+'/images/'
MAX_image = 1000
video_source = 0

if __name__ == "__main__":
    cap = cv2.VideoCapture(video_source)
    rat, frame = cap.read()
    image_count = 0
    if not os.path.exists(data_path):
        os.mkdir(data_path)
    if not os.path.exists(data_path+'/'+dataset_name):
        os.mkdir(data_path+'/'+dataset_name)
    if not os.path.exists(image_path):
        os.mkdir(image_path)
    while rat:
        rat, frame = cap.read()
        cv2.imshow("create custom dataset", frame)
        keyPress = cv2.waitKey(1) & 0xff
        if keyPress == 27:
            break
        if keyPress == ord('s'):
            full_path = image_path+dataset_name+'{:04.0f}'.format(image_count)+'.jpg'
            cv2.imwrite(full_path, frame)
            print("save "+full_path)
            image_count += 1
            if image_count == MAX_image:
                print(" you have almost saved %d samples!" % MAX_image)
                break
    cv2.destroyAllWindows()
    cap.release()

使用方法:

  • 修改数据集名称
  • 设置预期数据数
  • 设置视频源,0表示摄像头
  • 运行程序后,按s键储存图像,按esc退出
  • 若按帧直接存储,自行修改

2. 标注

可以使用labelimg进行标注,标注方法也很简单,框起来输入label,设置下difficult(遮挡严重就选,否则不用管),保存即可。

如有和我一样的懒人,可以试试用跟踪算法写一个辅助标注的工具。这里给一个参考https://github.com/Carey-cc/Label_image_with_medianflow。但效果并不是特别理想,局限性也很大。要求高的还是建议手动标注,更准确。

3. 生成coco格式数据集

标注完成后,文件结构应该是这样的:

|
|-.github
|-cfg
|-data
  |
  |-your_dataset
    |
    |-images
      |-xxxx.jpg
      |-xxxx.xml
  |-....
|-...

在images文件夹下,搜索xml,选中所有xml文件,然后剪切。

在your_dataset文件夹下建立如下文件结构,并把xml文件粘贴到指定位置

|
|-your_dataset
  |-Annotations
    |-xxxx.xml
  |-images
    |-xxxx.jpg
  |-ImageSets
  |-labels

然后划分数据集:data_split.py

import os
import random

dataset_name = "your_dataset"
trainval_percent = 0.1
train_percent = 0.9

xmlfilepath = './data/'+dataset_name+'/Annotations'
txtsavepath = './data/'+dataset_name+'/ImageSets'
total_xml = os.listdir(xmlfilepath)

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

ftrainval = open(txtsavepath+'/trainval.txt', 'w')
ftest = open(txtsavepath+'/test.txt', 'w')
ftrain = open(txtsavepath+'/train.txt', 'w')
fval = open(txtsavepath+'/val.txt', 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftest.write(name)
        else:
            fval.write(name)
    else:
        ftrain.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

coco格式label:voc_label.py

import xml.etree.ElementTree as ET
import os
from os import getcwd

sets = ['train', 'test', 'val']

classes = ["your class"]  # 设置你的类名,要求和标注的时候输入的一样
data_path = './data/your_dataset/'  #设置你的数据集路径


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(data_path+'Annotations/%s.xml' % (image_id))
    out_file = open(data_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'):
        try:
            difficult = obj.find('difficulty').text
        except AttributeError:
            difficult = '0'
        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')


wd = getcwd()
print(wd)
for image_set in sets:
    if not os.path.exists(data_path+'labels'):
        os.makedirs(data_path+'labels')
    image_ids = open(data_path+'ImageSets/%s.txt' % (image_set)).read().strip().split('\n')
    list_file = open(data_path+'%s.txt' % (image_set), 'w')
    for image_id in image_ids:
        list_file.write(data_path+'images/%s.jpg\n' % (image_id))
        print(image_id)
        convert_annotation(image_id)
    list_file.close()

然后新建.names和.data文件

文件内容如下:

your_dataset.names (你标注的类名)

your class

your_dataset.data

classes=1
train=data/your_dataset/train.txt
valid=data/your_dataset/test.txt
names=data/your_dataset/your_dataset.names
backup=backup/
eval=coco

其中classes为类别数

至此,你的数据集文件夹下的文件结构为:

|
|-your_dataset
  |-Annotations
    |-xxxx.xml
  |-images
    |-xxxx.jpg
  |-ImageSets
    |-test.txt
    |-train.txt
    |-trainval.txt
    |-val.txt
  |-labels
    |-xxxx.txt
  |-your_dataset.names
  |-your_dataset.data
  |-text.txt
  |-train.txt
  |-val.txt

 4. 训练

以yolov3-tiny为例,打开cfg/yolov3-tiny.cfg

找到文件中的两处yolo

[convolutional]
size=1
stride=1
pad=1
filters=255  -> 3*(1+5) = 18
activation=linear



[yolo]
mask = 3,4,5
anchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319
classes=80 -> 1
num=6
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

修改其中 [convolutional]里的(只修改每个[yolo]前的这个)filters=3*(class数+5),下面[yolo]里的classes改成class数。

然后开始训练

python train.py --cfg cfg/yolov3-tiny.cfg --weights weights/yolov3-tiny.pt --data data/your_dataset/your_dataset.data

如果想指定epochs或batch-size,加上--epochs xxx --batch-size xxx.

训练默认都是用GPU 的,因此不用管这个,如果你有多块GPU要选择,加上--device xxx。

然后等待......

训练结束后可以看到训练结果:

5. 测试

训练好的模型会保存在weights文件夹下,bets.pt是最佳,last.pt是最后训练的,这里提醒一下,可以利用这个中断和继续训练,即训练的时候加载预训练模型选last.pt.

测试模型和开始的时候差不多,改一下weights文件就好。

python detect.py --cfg cfg/yolov3-tiny.cfg --weights weights/best.weights --source 0

 

总结

yolo的用户体验真的很不错,十分“丝滑”,精度也挺高。

另外,解决有关中文路径的问题:

在utils/datasets.py中,第514行做些修改,否则对中文路径解码会出现错误,提示Image Not Found,如下注释处

    if img is None:  # not cached
        img_path = self.img_files[index]
        # img = cv2.imread(img_path)  # BGR
        img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), -1)  # 解决中文路径问题
        assert img is not None, 'Image Not Found ' + img_path

 

 
在使用YOLOv3训练自己的数据集时,你需要进行以下几个步骤的修改: 1. 数据集标注格式:YOLOv3使用的是Darknet格式的标注文件,每个图像对应一个同名的txt文件,其中包含了物体类别和边界框的位置信息。如果你的数据集使用其他格式,你需要将其转换为Darknet格式。 2. 类别数量:根据你的数据集中物体的类别数量,你需要在模型配置文件中修改类别数目。在YOLOv3中,这个配置文件通常是`yolov3.cfg`或`yolov3-tiny.cfg`。 3. 锚框(anchors)设置:YOLOv3使用了预定义的锚框来提取不同尺度的目标框。如果你的数据集中的对象尺度与预定义锚框不匹配,你需要在模型配置文件中修改锚框的尺寸。这些锚框信息通常在`[yolo]`层中定义。 4. 输入图像尺寸:YOLOv3网络对输入图像的大小有一定要求。默认情况下,YOLOv3要求输入图像的宽高为32的倍数。如果你的图像尺寸不满足这个要求,你需要在模型配置文件中修改对应的参数。 5. 训练参数:根据你的数据集训练需求,你可能需要修改训练参数,如学习率、批次大小、迭代次数等。这些参数通常在训练脚本中进行设置。 除了以上修改,你还需要注意确保数据集的正确性,包括标注准确、类别均衡、图像质量等。此外,为了获得更好的检测性能,你可能需要调整其他超参数,如网络深度、特征图数量等。这些超参数可以在模型配置文件中进行调整。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值