yolo 学习系列(二):训练自己的数据集

网络结构:yolov2-tiny-voc(以voc数据集为基础的yolov2的轻量型网络tiny)
数据集:VOC2007和VOC2012
采用该网络的原因:tiny的训练时间远远小于完全版本的yolov2,有助于达到目标同时节约时间。

1、图片预处理
1.1 统一大小
import cv2
import os


fullfilename=[]
filepath = "F:/PycharmProjects/image"   # 不能包含中文路径
filepath1 = "F:/PycharmProjects/resize"
for filename in os.listdir(filepath):
    print(filename)
    print(os.path.join(filepath, filename))
    filelist = os.path.join(filepath, filename)
    fullfilename.append(filelist)
i = 1
for imagename in fullfilename:
    img = cv2.imread(imagename)
    img = cv2.resize(img, (416, 416))   # 该句报错,路径中包含中文
    resizename = str(i)+'.jpg'          # 命名形式为1,2,3... 需重新命名位000001,000002,000003...
    isExists = os.path.exists(filepath1)
    if not isExists:
        os.makedirs(filepath1)
        print('mkdir resizename accomploshed')
    savename = filepath1+'/'+resizename
    cv2.imwrite(savename, img)
    print('{} is resized'.format(savename))
    i = i+1
1.2 重命名

图片重命名,并将图片名称写入 train.txt 文本内

# -*- coding:utf8 -*-
#!/usr/bin/python3.6
import os


class BatchRename():
    def __init__(self):
        self.path = 'F:/PycharmProjects/resize'

    def rename(self):
        f = open(r'F:/PycharmProjects/resize/train.txt', 'a')
        filelist = os.listdir(self.path)
        total_num = len(filelist)
        i = 1

        for item in filelist:
            if item.endswith('.jpg'):
                src = os.path.join(os.path.abspath(self.path), item)
                str1 = str(i)
                dst = os.path.join(os.path.abspath(self.path), str1.zfill(6) + '.jpg')
                try:
                    os.rename(src, dst)
                    print('converting %s to %s ...' % (src, dst))

                    # 写入 txt 文本中的名称形式,前面加上绝对路径
                    f.write('/home/chris/darknet/trainData/haishen/VOC2007/JPEGImages/' + str1.zfill(6) + '.jpg' + '\n')
                    i = i + 1
                except:
                    continue
        print('total %d to rename & converted %d jpgs' % (total_num, i))


if __name__ == '__main__':
    demo = BatchRename()
    demo.rename()
2、创建VOC数据集

参考这里还有这里

2.1 新建文件夹

这里面用到的文件夹是Annotation、ImageSets和JPEGImages。

  • Annotation中主要存放xml文件,每一个xml对应一张图像,并且每个xml中存放的是标记的各个目标的位置和类别信息,命名通常与对应的原始图像一样;
  • ImageSets我们只需要用到Main文件夹,这里面存放的是一些文本文件,通常为train.txt、test.txt等,该文本文件里面的内容是需要用来训练或测试的图像的名字(无后缀无路径);
  • JPEGImages文件夹中放我们已按统一规则命名好的原始图像
--VOC2007
    --Annotations
    --ImageSets
      --Main
      --Layout
      --Segmentation
    --JPEGImages
    --SegmentationClass
    --SegmentationObject
  • 新建文件夹VOC2007(通常命名为这个,也可以用其他命名,但一定是名字+年份,例如MYDATA2016,无论叫什么后面都需要改相关代码匹配这里,本例中以VOC2007为例)
  • 在VOC2007文件夹下新建三个文件夹Annotation、ImageSets和JPEGImages,并把准备好的自己的原始图像放在JPEGImages文件夹下
  • 在ImageSets文件夹中,新建三个空文件夹Layout、Main、Segmentation,然后把写了训练或测试的图像的名字的文本拷到Main文件夹下,按目的命名,我这里所有图像用来训练,故而Main文件夹下只有train.txt文件。上面说的小代码运行后会生成该文件,把它拷进去即可。
2.2 标注图像目标区域——labelImg
2.2.1 labelImg安装

下载labelImg: 直接下载或克隆下载

git clone https://github.com/tzutalin/labelImg.git

解压后放在 home 路径下

sudo apt-get install pyqt4-dev-tools # 安装PyQt4
sudo pip install lxml # 安装lxml,如果报错,可以试试下面语句
sudo apt-get install python-lxml

进入 LabelImg 目录后使用 make 编译(源码安装方式,一定要会)

cd labelImg
make all

测试使用:在 labelImg 目录下使用终端执行

python labelImg.py 
#或./labelImg.py
2.2.2 labelImg使用

快捷键

  • Ctrl + u 加载目录中的所有图像,鼠标点击Open dir同功能
  • Ctrl + r 更改默认注释目标目录(xml文件保存的地址)
  • Ctrl + s 保存
  • Ctrl + d 复制当前标签和矩形框
  • d 下一张图片
  • a 上一张图片
  • Ctrl++ 放大
  • Ctrl-- 缩小
  • ↑→↓← 键盘箭头移动选定的矩形框

(1)源码文件夹下,修改data / predefined_classes.txt文件,根据自己的需求修改默认类别,这里改成sea
(2)点击 “Open Dir” 打开至图片文件夹位置,即 JPEGImages 下,点击 Nest Image可切换下张图片
(3)打开 labelImg 使用 Ctrl + r 更改 xml 文件的默认保存目录,修改至上面介绍的Annotation下
接下来右键 Create RectBox 画框,画完后点击 Save 保存,这时在Annotation目录下就会产生一个xml文件了,它的画风是这样的:
文件中记录了图片的大小、通道数、类别、图框位置等信息

<?xml version="1.0" ?>
<annotation>
	<folder>JPEGImages</folder>
	<filename>00000</filename>
	<path>/home/kinglch/VOC2007/JPEGImages/00000.jpg</path>
	<source>
		<database>Unknown</database>
	</source>
	<size>
		<width>704</width>
		<height>576</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>person</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>73</xmin>
			<ymin>139</ymin>
			<xmax>142</xmax>
			<ymax>247</ymax>
		</bndbox>
	</object>
	<object>
		<name>person</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>180</xmin>
			<ymin>65</ymin>
			<xmax>209</xmax>
			<ymax>151</ymax>
		</bndbox>
	</object>

</annotation>

最后,生成训练的txt文件,运行以下Python脚本,在 Main 目录下产生 trainval.txt、train.txt、test.txt、val.txt文本。

  • train.txt 是用来训练的图片文件的文件名列表
  • val.txt是用来验证的图片文件的文件名列表
  • trianval.txt是用来训练和验证的图片文件的文件名列表
  • test.txt 是用来测试的图片文件的文件名列表

train.txt + val.txt = trianval.txt
trianval.txt + test.txt = 整个数据集

import os
import random

trainval_percent = 0.66
train_percent = 0.95
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
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('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')

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

ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
2.3 训练前的准备

前提:yolo已经安装好
在darknet-master/scripts文件夹中新建文件夹 VOCdevkit
然后将整个 VOC2007 文件夹都拷到 VOCdevkit 文件夹下

2.3.1 voc_label.py生成训练文件

首先需要修改 voc_label.py 中的代码,这里主要修改数据集名,以及类别信息,我的是VOC2007,并且所有样本用来训练,并且只检测sea,故只有一类目标,修改如下:

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
# 注释掉默认的
# sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
# classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]

sets=[ ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
classes = [ "sea"]


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(year, image_id):
    in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))  #(如果使用的不是VOC而是自设置数据集名字,则这里需要修改)
    out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, 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')

wd = getcwd()

for year, image_set in sets:
    if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
        os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
    image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
    list_file = open('%s_%s.txt'%(year, image_set), 'w')
    for image_id in image_ids:
        list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
        convert_annotation(year, image_id)
    list_file.close()

修改好后在该目录下运行以下命令:‘’

python voc_label.py

之后在文件夹scripts\VOCdevkit\VOC2007下生成了文件夹 lable,该文件夹下的画风是这样的

yolov3训练自己的数据集pytorch yolov2训练自己的数据_python


同时在 scripts\ 下应该也生成了 train_2007.txt 这个文件,里面包含了所有训练样本的绝对路径。

2.3.2 修改配置文件

修改源文件前最好进行备份或注释掉默认语句,不要在原语句上修改。

做好了上述准备,就可以根据不同的网络设置(cfg文件)来训练了。在文件夹cfg中有很多cfg文件,应该跟caffe中的prototxt文件是一个意思。这里以tiny-yolo-voc.cfg为例,该网络是yolo-voc的简版,相对速度会快些。主要修改参数如下

.  
.  
.  
[convolutional]  
size=1  
stride=1  
pad=1  
filters=30  //修改最后一层卷积层核参数个数,
			//计算公式是依旧自己数据的类别数filter=num×(classes + coords + 1)=5×(1+4+1)=30  
			// YOLOv3版本中 filter=3×(classes + coords + 1)=3×(1+4+1)=18
activation=linear  

[region]  
anchors = 1.08,1.19,  3.42,4.41,  6.63,11.38,  9.42,5.11,  16.62,10.52  
bias_match=1  
classes=1  //类别数,本例为1类  
coords=4  
num=5  
softmax=1  
jitter=.2  
rescore=1  

object_scale=5  
noobject_scale=1  
class_scale=1  
coord_scale=1  

absolute=1  
thresh = .6  
random=1

修改好了cfg文件之后,就需要修改两个文件。
(1)data文件下的voc.names
打开voc.names文件可以看到有20类的名称,根据自己的实际应用修改,此处为sea
(2)cfg文件夹中的voc.data

classes= 1  //类别数  
    train  = /home/chris/darknet/scripts/2007_train.txt  //训练样本的绝对路径文件
    //valid  = /home/pjreddie/data/voc/2007_test.txt  //测试样本的绝对路径  文件
    names = data/voc.names  //上一步修改的voc.names文件  
    backup = /home/xiao_run/darknet-master/results/  //训练后生成的权重存放位置
3、开始训练

上面完成了就可以命令训练了,可以在官网上找到一些预训练的模型作为参数初始值,也可以直接训练,训练命令为

./darknet detector train ./cfg/voc.data cfg/yolov2-tiny-voc.cfg

如果用官网的预训练模型darknet.conv.weights做初始化,则训练命令为

./darknet detector train ./cfg/voc.data cfg/yolov2-tiny-voc.cfg darknet19_448.conv.23

训练过程中会根据迭代次数保存训练的权重模型,然后就可以拿来测试了,测试的命令:

./darknet detector test cfg/voc.data cfg/yolov2-tiny-voc.cfg results/yolov2-tiny-voc_900.weights data/000100.jpg
4、Windows下训练自己的数据集
4.1 准备数据集

同linux下的操作过程相同,即创建 VOCdevkit 文件夹
将该文件夹放在 F:\yolo-install\darknet-master\build\darknet\x64\data\voc路径下,并切换到该路径下,运行以下命令生成txt文件。

python voc_label.py

注意:Windows下VS25015编译安装好的 F:\yolo-install\darknet-master\build\darknet\x64\ 的路径相当于Ubuntu下 home/chris/darknet-master的路径, 因此在Windows下要切换到 F:\yolo-install\darknet-master\build\darknet\x64\ 内输入命令
训练:darknet.exe detector train data/voc.data cfg/yolov2-tiny-voc.cfg
测试:darknet.exe detector test data/voc.data cfg/yolov2-tiny-voc.cfg backup/yolov2-tiny-voc_6000.weights data/person.jpg

4.2 数据集标签与路径生成

修改 F:\yolo-install\darknet-master\build\darknet\x64\data 路径下的voc.data和voc.names文件

  • voc.data设置训练集路径和权重保存位置
  • voc.names设置名称

修改后的 voc.data 内容为

classes= 1
train  = data/voc/2007_train.txt   
valid  = data/voc/2007_val.txt
#difficult = data/difficult_2007_test.txt
names = data/voc.names
backup = backup/
4.3 修改网络配置文件并开始训练

网络配置文件的修改内容与 linux 下的操作相同
打开cmd窗口 切换至 F:\yolo-install\darknet-master\build\darknet\x64\ 路径 运行一下命令即可训练

darknet.exe detector train data/voc.data cfg/yolov2-tiny-voc.cfg