使用YOLOv3 训练行人检测模型

北城余情 提交于 2019-11-28 05:20:51

使用YOLO进行目标检测在保证速度的前提下能有较好的准确性,这篇文章是在yolov3的代码基础上进行修改为行人检测,由原来的20个分类改为识别person,使用tiny-yolo的进行训练和检测,最后能达到还不错的效果。

1.数据集制作

收集图片并标注,这是一件工作量较大的事情,所以我们直接使用了PASCAL VOC2007的数据集,抽取其中带有行人的图片。

具体过程如下:

1.1 创建相应的文件夹

  • 在yolo代码里的scripts/目录下创建VOC_person文件夹,在其文件夹下创建Annotations、ImageSets、JPEGImages文件夹,在ImageSets文件夹下建立Main文件夹。这里顺便说下这三个文件夹的作用:

Annotations -- Annotations文件夹中存放的是xml格式的标签文件,每一个xml文件都对应于JPEGImages文件夹中的一张图片;

ImageSets -- ImageSets存放的是每一种类型的challenge对应的图像数据。在ImageSets下有Main文件夹,存放的是图像物体识别的数据,总共分为20类;

JPEGImages -- JPEGImages文件夹中包含了PASCAL VOC所提供的所有的图片信息,包括了训练图片和测试图片。

1.2 拷贝相应的文件

  • 提取VOC2007/ImageSets/Main/文件夹里的person_test.txt,person_trainval.txt,person_train.txt  person_val.txt 这四个文件到VOC_person/ImageSets/Main/目录下;
  • 拷贝VOC2007/Annotations/ 目录下的所有的文件到VOC_person/Annotations/ 目录下;
  • 拷贝VOC2007/JPEGImages/ 目录下的所有的文件到VOC_person/JPEGImages/ 目录下;

1.3 生成训练集,测试集和开发集文件

  • 我们需要生成VOC_person/ImageSets/Main/目录下的test.txt,train.txt,trainval.txt,val.txt 这四个文件。

train.txt是训练集,test.txt是测试集,val.txt是验证集,trainval.txt是训练和验证集。VOC2007中,trainval大概是整个数据集的50%,test也大概是整个数据集的50%;train大概是trainval的50%,val大概是trainval的50%,当数据集较小时,可减少val和test的数量。

使用Matlab生成这四个txt文件,make_train_data.m 代码如下:

%注意修改下面四个值  
xmlfilepath='E:\Image Sets\VOC_person\Annotations';  
txtsavepath='E:\Image Sets\VOC_person\ImageSets\Main\';  
trainval_percent=0.8;%trainval占整个数据集的百分比,剩下部分就是test所占百分比  
train_percent=0.8;%train占trainval的百分比,剩下部分就是val所占百分比  

xmlfile=dir(xmlfilepath);  
numOfxml=length(xmlfile)-2;%减去.和..  总的数据集大小  

trainval=sort(randperm(numOfxml,floor(numOfxml*trainval_percent)));  
test=sort(setdiff(1:numOfxml,trainval));  

trainvalsize=length(trainval);%trainval的大小  
train=sort(trainval(randperm(trainvalsize,floor(trainvalsize*train_percent))));  
val=sort(setdiff(trainval,train));  

ftrainval=fopen([txtsavepath 'trainval.txt'],'w');  
ftest=fopen([txtsavepath 'test.txt'],'w');  
ftrain=fopen([txtsavepath 'train.txt'],'w');  
fval=fopen([txtsavepath 'val.txt'],'w');  

for i=1:numOfxml  
    if ismember(i,trainval)  
        fprintf(ftrainval,'%s\n',xmlfile(i+2).name(1:end-4));  
        if ismember(i,train)  
            fprintf(ftrain,'%s\n',xmlfile(i+2).name(1:end-4));  
        else  
            fprintf(fval,'%s\n',xmlfile(i+2).name(1:end-4));  
        end  
    else  
        fprintf(ftest,'%s\n',xmlfile(i+2).name(1:end-4));  
    end  
end  
fclose(ftrainval);  
fclose(ftrain);  
fclose(fval);  
fclose(ftest);
  • 将数据转换成YOLO训练所需的标注各色,并生成训练/验证/测试图片路径文件

1)打开yolo代码下的scripts目录,拷贝voc_label.py文件并重命名为person_voc_label.py 文件:

cp voc_label.py person_voc_label.py

2)修改person_voc_label.py文件:

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join

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

classes = ["person"]


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('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/Annotations/%s.xml'%(image_id))
    out_file = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/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
        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 image_set in sets:
    if not os.path.exists('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/'):
        os.makedirs('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/')
    image_ids = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
    list_file = open('person_%s.txt'%(image_set), 'w')
    for image_id in image_ids:
        list_file.write('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/JPEGImages/%s.jpg\n'%(image_id))
        convert_annotation(image_id)
    list_file.close()

os.system("cat person_train.txt person_val.txt > my_person_train.txt")

3)执行person_voc_label.py脚本。

发现在scripts/VOC_person目录下生成label文件夹,里面有每张图片的标注文件(文件内容形如0 0.488888888889 0.289256198347 0.977777777778 0.429752066116),其中第一个数字表示类别标签(0表示person,也即前面写的classes的顺序),后面四个数为包围框信息。

4)生成训练文件

在上一步执行了person_voc_label.py脚本后,会在 scripts下多了person_train.txt、person_val.txt和person_test.txt这三个文件,这三个文件是数据集中图片的路径。由于yolo训练只需要一个my_person_train.txt文件,文件中包含所有你想要训练的图片的路径。因此,我们还要生成my_person_train.txt文件。

cat person_train.txt person_val.txt > my_person_train.txt

到这里数据集制作算是制作完成了。

 

2.修改代码训练

(1)在data目录下新建文件my_person_train.names,内容为:

person

(2)在cfg目录下新建 my_person_train.data文件,内容为:

classes= 1
train  = /home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/my_person_train.txt
valid  = /home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt
names = data/my_person_train.names
backup = backup

(3)修改examples/yolo.c文件:

  • 检测目标类别改成自己的数据集类别:
char *voc_names[] = {"person"};
  • train_yolo函数:
char *train_images = "/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/my_person_train.txt";
char *backup_directory = "/home/liguiyuan/deep_learning/project/darknet/backup/";

train_images应该指向我们刚得到的my_person_train.txt;backup_directory指向的路径是训练过程中生成的weights文件保存的路径(可以在darknet-master下新建文件夹backup然后指向它)。这两个路径按自己系统修改即可。

  • validate_yolo函数:
    char *base = "results/comp4_det_test_";
    //list *plist = get_paths("data/voc.2007.test");
    list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
    //list *plist = get_paths("data/voc.2012.test");
  • validate_yolo_recall函数:
    char *base = "results/comp4_det_test_";
    list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
  • test_yolo函数:
//draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, 0, voc_names, alphabet, 20);
draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, 0, voc_names, alphabet, 1);

类别数改为1类。

  • run_yolo函数:
//demo(cfg, weights, thresh, cam_index, filename, voc_names, 20, frame_skip, prefix, avg, .5, 0,0,0,0);
demo(cfg, weights, thresh, cam_index, filename, voc_names, 1, frame_skip, prefix, avg, .5, 0,0,0,0);

类别数改为1类。

(4)修改examples/detector.c文件

  • validate_detector_recall函数:
list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
  • run_detector函数:
//int classes = option_find_int(options, "classes", 20);
int classes = option_find_int(options, "classes", 1);

类别数改为1类

(5)修改cfg/yolov3-voc_person.cfg文件:

复制cfg/yolov3-voc.cfg文件并命名为yolov3-voc_person.cfg,如果想用其他模型修改类似。

【yolo】层中 classes 把20改成1,因为我们检测的类别只有penson,有3个地方要改;

【yolo】层的上一层【convolutional】,即网络中的最后一层卷积,的filters改为18,也是3处地方要修改。计算方法是这样:1*1*[3*(4+1+1)]=18。[num*(coords+1+classes) ,num为3,因为yolov3预测3个boxes(yolov1为1个,yolov2为5个),coords为4个坐标(预测boundingbox的 x,y,w,h), 1表示置信度,识别1个类别。N为卷积核的size。

[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear

[yolo]
mask = 3,4,5
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=1
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1

(6)其他的一些参数可以按自己需求修改,比如batch, subdivisions等,都是在这个cfg文件里面修改。我这里把batch改为32,要不然使用GTX1060显卡训练时会出现CUDA Error: out of memory。subdivision:这个参数意思是,让你的每一个batch不是一下子都丢到网络里,而是分成多次丢到GPU内存中,例如2,就是分成2次读入。

[net]
# Testing
# batch=1
# subdivisions=1
# Training
 batch=32
 subdivisions=16
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1

learning_rate=0.001
burn_in=1000
max_batches = 50200
policy=steps
steps=40000,45000
scales=.1,.1

 

3.训练模型

下载预训练darknet53.conv.74的weights权重文件,下载地址为:

wget https://pjreddie.com/media/files/darknet53.conv.74

开始训练模型:

./darknet detector train cfg/my_person_train.data cfg/yolov3-voc_person.cfg darknet53.conv.74

看到开始训练了:

训练中断后从断点开始训练模型:

./darknet detector train cfg/my_person_train.data cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup

使用GTX1060的GPU,经过30多个小时的训练,训练了10000批次,在person_backup目录下看到 yolov3-voc_person_10000.weights等训练好的模型。这里附上我训练的模型链接: https://pan.baidu.com/s/1sfeEqdR-K-9q4NZFD5FdmQ   提取码: 8n8g 

 

4.检测模型的结果

修改cfg/yolov3-voc_person.cfg 文件,把Training的batch, subdivisions屏蔽掉,使用Testing的batch, subdivisions 都为1

# Training
#batch=32
#subdivisions=2
# Testing
batch=1
subdivisions=1

检测图片:

./darknet detect cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup data/person.jpg

摄像头实时检测:

./darknet detector demo cfg/my_person_train.data cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup

效果图如下:

文章到这里完成了整个训练的过程。

注:转载请注明出处!


文章里部分教程参考:https://blog.csdn.net/yudiemiaomiao/article/details/71635257

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!