darknet-yolov3如何训练自己的数据集

4 篇文章 0 订阅
4 篇文章 1 订阅

目录

1、下载darknet代码

1.1 下载代码

1.2 编译代码 

1.2.1 修改Makefile文件

1.2.2 编译

1.2.3 下载权重

2、准备训练数据集

2.1 标注数据

2.2 存放训练集图片和xml文件

2.3 划分数据集

3、生成2007_train.txt和2007_val.txt等以及label文件夹

3.1 拷贝数据集

3.2 生成所需的文件

4、修改data/voc.names

 5、修改cfg/voc.data文件

6、修改cfg/yolov3-voc.py

7、训练

   8、测试

9、批量测试

9.1 准备批量测试的图片

9.2 修改 example/detector.c文件

9.3 执行批量测试的命令

10.补充

10.1 删除2007_train.txt 和 2007_val.txt的文件

参考:


1、下载darknet代码

 darknet yolov3 官网https://pjreddie.com/darknet/yolo/

1.1 下载代码

git clone https://github.com/pjreddie/darknet

1.2 编译代码 

cd darknet

1.2.1 修改Makefile文件

      

1)如果使用GPU,GPU=1;否则使用CPU,CPU=0;

2)如果使用cudnn加速库,CUDNN=1;否则为0;

3)如果使用opencv,将opencv=1;否则,opencv=0;

(注:本机将cudnn设为1时,执行官网的测试demo出现问题,如下所示,自行车没有找到。因此,而设定cudnn=0就可以。)

1.2.2 编译

在darknet下,编译代码make

make

 

1.2.3 下载权重

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

下载权重文件,后面训练时使用。

在darknet目录下新建weights目录,并将darknet53.conv.74移动到weights目录下。

(建议:访问  https://pjreddie.com/darknet/yolo/,测试一下代码是否可行)

 

2、准备训练数据集

按照voc的格式准备数据,如果全部是jpg的格式的图片,直接跳过注意事项即可:

注意:最好统一图片的格式,本文默认图片为jpg的格式;如果是其他格式,使用voc_label.py生成2007_train.txt,包含图片的完整的路径信息,都是以jpg格式生成。这样训练的时候,找不到jpg格式的图片,会造成“Cannot load image”.

情形1)全部图片都为同一格式,比如:png或者bmp时,直接修改 voc_label.py 倒数第3行的后缀名称即可:

情形2)图片中的后缀格式不统一,既有png/jpg/bmp等2种以上图片时,见文章最后补充10。

2.1 标注数据

使用LabelImg软件(可自行百度下载),对训练集图片进行标注,一张图片对应一个xml文件。

2.2 存放训练集图片和xml文件

        新建文件夹 VOCdevkit, 在VOCdevkit目录下新建文件夹VOC2007,在VOC2007目录下新建3个目录,分别为

        Annotations、ImageSets、JPEGImages。最后在ImageSets下新建Main目录。目录结构如下:

        VOCdevkit

         -------------VOC2007

                          ------------Annotations                         (存放标注的xml文件)

                          ------------ImageSets   

                                         ----------------Main              (存放训练集、测试集的文件名的txt文件:train,val,test,trainval)

                          ------------JPEGImages                    (存放训练集的图片)

2.3 划分数据集

将训练集图片进行划分,train、val、test和trainval集。将以下代码保存为train_val_test.py,并将放置在VOC2007目录下执行。

import os
import random
 
# 验证集和测试集占0.2
trainval_percent = 0.2
# 训练集占0.8
train_percent = 0.8
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets/Main'
total_xml = os.listdir(xmlfilepath)
 
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)      # 0.2 * num
tr = int(tv * train_percent)          # 0.2 * 0.8 * num
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)      # trainval:0.2 * num
        if i in train:
            ftest.write(name)      # test: 0.16 * num
        else:
            fval.write(name)       # val: 0.04 * num
    else:
        ftrain.write(name)         # train: 0.8 * num
 
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

JPEGImages/Main 生成4个txt文本,train.txt, trainval.txt, val.txt, test.txt。

其中,train:0.8比例

           trainval: 0.2比例

           test: 0.2*0.8=0.16比例

           val: 0.2-0.16=0.04比例

           test和val组成trainval。

        (实际比例可自行修改代码中的参数进行修改,

           train比例:train_percent

           trainval比例: trainval_percent

          但val的比例始终:train_percent * trainval_percent

              test的比例:trainval_percent - val的比例

这些txt的文件中,实际存放了图片的文件名,但不包括后缀名。      

crazing_10是文件名,一行对应一张图片。

3、生成2007_train.txt和2007_val.txt等以及label文件夹

3.1 拷贝数据集

将上一步的制作好的VOCdevkit文件夹拷贝到darknet的目录下。

3.2 生成所需的文件

复制以下代码命名为voc_label.py,移动darknet的目录。(此文件实际为darknet/script/voc_label.py, 最后两行注释掉)

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

sets=[('2007', 'train'), ['2007', 'val']]
# 修改为自己的类名
classes = ["crazing", "inclusion", "patches", "pitted_surface","rolled-in_scale", "scratches"]


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))
    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()

与darknet/script/voc_label.py相比,除了注释掉最后两行以外,还需要根据自己的训练集修改如下两处。 

修改自己的 classes列表:

 

运行代码:

python voc_label.py

此时会生成2个txt文件和一个labels文件夹

        1)2个txt:2007_train.txt, 2007_val.txt 在darknet目录下;

         txt文件中存放图片的完整路径信息

        2)label文件夹在 VOCdevkit/VOC2007目录下;

        生成label文件夹,并在VOCdevkit/VOC2007/labels的文件中,存放图片文件名命名的txt文本。

        文本信息为类别索引和标准化后坐标位置信息

 labels文件夹下存放以图片名命名的txt文本标签信息

 

以crazing_10.txt为例:

第一个字符 0,表示类别信息;后面4位是位置坐标信息

通过解析xml文件获得实际的类名和坐标信息,并将类名用数字表示,同时将坐标信息标准化。

原始坐标信息是左上角和右下角,通过convert转换为中间坐标点信息和宽、高,数值范围在【0-1】之间。

 

4、修改data/voc.names

自己的实际类名信息,一行一个类名

(注意:与voc_label.py中classes顺序保持一致) 

 5、修改cfg/voc.data文件

classes :训练集类别总数

train:训练集路径

valid:验证集路径

names: data/voc.names 文件

backup: 备份文件夹,训练后的权重信息或者断点保存信息在此文件夹下。

(记得:在darknet下创建目录 backup)

6、修改cfg/yolov3-voc.py

共有3处需要修改 ,搜索 “yolo” 主要集中 yolo附近。需要修改 filters 和 classes 的参数。

filters = 3 * (classes数目 + 5)

classes = 自己数据集类别数目

1)第一处:大概在601行

2)第二处:大概在689行 

 3)第三处:大概在769行

如果显存太小,将random=0,关闭多尺度训练。

4) 最后将训练模型开启。

7、训练

 1) 训练

./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg weights/darknet53.conv.74

   (Ctrl + c 中止训练, Ctrl + z 暂停训练, 暂停回来按住fg  ) 

    多GPU训练

./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1,2,3

  2)如果使用ssh连接的服务器的话,可以使用如下命令,关闭ssh服务器会在后头继续运行:

nohup ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg weights/darknet53.conv.74 &

说明:

        1)nohup 指不间断、不挂断地运行,no hang up的缩写。如果运行一个程序,在退出时想继续运行,可以使用nohup。

        2)& 是后台运行的意思,

        3)nohup  命令 &,就是不挂断的后台持续运行程序

        (也可以使用tee进行,但是不能关闭ssh窗口

 ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 2>&1 | tee visualization/train_yolov3.log 

3)训练的次数可以通过 cfg/yolov3-voc.cfg 文件中的max_batches修改,默认是50200。还有一些其他参数可以在此修改。暂时没有深入了解。

 训练结束之后,会生成train.log日志文件,可以查看训练情况。

4)模型的文件保存在backup文件中:

 

5)中断后继续训练

中断后,程序会在backup目录下,自动保存上一次权重信息:yolov3-voc.backup,重新训练时导入即可。

多GPU时加上参数: -gpus 0, 1, 2, 3

 

./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc.backup -gpus 0,1

   8、测试

将cfg/yolov3-voc.cfg中的测试模式打开:

测试:

./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_final.weights 

输入需要测试的图片路径即可

 (backup的权重都可以进行测试)

 

9、批量测试

上述图片只能对图片进行单张测试,现在需要对一个文件夹下的文件进行批量测试。

9.1 准备批量测试的图片

将需要批量测试的图片放到test目录下,将test目录放到darknet目录下;

执行:   (/data/lxh/darknet/test   修改成自己的目录)

ls -R  /data/lxh/darknet/test/* > test.txt

其中test.txt,存放测试集的图片的绝对路径。

9.2 修改 example/detector.c文件

1) 用下面代码替换 void test_detector(), 有3处需要修改成自己的路径。

void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen)
{
    list *options = read_data_cfg(datacfg);
    char *name_list = option_find_str(options, "names", "data/names.list");
    char **names = get_labels(name_list);
 
    image **alphabet = load_alphabet();
    network *net = load_network(cfgfile, weightfile, 0);
    set_batch_network(net, 1);
    srand(2222222);
    double time;
    char buff[256];
    char *input = buff;
    float nms=.45;
    int i=0;
    while(1){
        if(filename){
            strncpy(input, filename, 256);
            image im = load_image_color(input,0,0);
            image sized = letterbox_image(im, net->w, net->h);
        //image sized = resize_image(im, net->w, net->h);
        //image sized2 = resize_max(im, net->w);
        //image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
        //resize_network(net, sized.w, sized.h);
            layer l = net->layers[net->n-1];
 
 
            float *X = sized.data;
            time=what_time_is_it_now();
            network_predict(net, X);
            printf("%s: Predicted in %f seconds.\n", input, what_time_is_it_now()-time);
            int nboxes = 0;
            detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
            //printf("%d\n", nboxes);
            //if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
            if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
                draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
                free_detections(dets, nboxes);
            if(outfile)
             {
                save_image(im, outfile);
             }
            else{
                save_image(im, "predictions");
#ifdef OPENCV
                cvNamedWindow("predictions", CV_WINDOW_NORMAL); 
                if(fullscreen){
                cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
                }
                show_image(im, "predictions");
                cvWaitKey(0);
                cvDestroyAllWindows();
#endif
            }
            free_image(im);
            free_image(sized);
            if (filename) break;
         } 
        else {
            printf("Enter Image Path: ");
            fflush(stdout);
            input = fgets(input, 256, stdin);
            if(!input) return;
            strtok(input, "\n");
   
            list *plist = get_paths(input);
            char **paths = (char **)list_to_array(plist);
             printf("Start Testing!\n");
            int m = plist->size;
            if(access("/data/lxh/darknet/data/out",0)==-1)//"/data/lxh/darknet/data"修改成自己的路径
            {
              if (mkdir("/data/lxh/darknet/data/out",0777))//"/data/lxh/darknet/data"修改成自己的路径
               {
                 printf("creat file bag failed!!!");
               }
            }
            for(i = 0; i < m; ++i){
             char *path = paths[i];
             image im = load_image_color(path,0,0);
             image sized = letterbox_image(im, net->w, net->h);
        //image sized = resize_image(im, net->w, net->h);
        //image sized2 = resize_max(im, net->w);
        //image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
        //resize_network(net, sized.w, sized.h);
        layer l = net->layers[net->n-1];
 
 
        float *X = sized.data;
        time=what_time_is_it_now();
        network_predict(net, X);
        printf("Try Very Hard:");
        printf("%s: Predicted in %f seconds.\n", path, what_time_is_it_now()-time);
        int nboxes = 0;
        detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
        //printf("%d\n", nboxes);
        //if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
        if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
        draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
        free_detections(dets, nboxes);
        if(outfile){
            save_image(im, outfile);
        }
        else{
             
             char b[2048];
            sprintf(b,"/data/lxh/darknet/data/out/%s",GetFilename(path)); //"/data/lxh/darknet/data"修改成自己的路径
            
            save_image(im, b);
            printf("save %s successfully!\n",GetFilename(path));
#ifdef OPENCV
            cvNamedWindow("predictions", CV_WINDOW_NORMAL); 
            if(fullscreen){
                cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
            }
            show_image(im, "predictions");
            cvWaitKey(0);
            cvDestroyAllWindows();
#endif
        }
 
        free_image(im);
        free_image(sized);
        if (filename) break;
        }
      }
    }
}

2)在detector.c的前面添加如下函数*GetFilename(char *p),注意注释内容

(这是识别之后保存文件名的长度,如果你的文件名大于6个字符,根据具体需要改)

#include "darknet.h"
#include <sys/stat.h>
#include<stdio.h>
#include<time.h>
#include<sys/types.h>
static int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90};
 
char *GetFilename(char *p)
{ 
    static char name[20]={""};
    char *q = strrchr(p,'/') + 1;
    strncpy(name,q,6);//注意后面的6,如果你的测试集的图片的名字字符(不包括后缀)是其他长度,请改为你需要的长度(官方的默认的长度是6)
    return name;
}

 3) 编译

make

9.3 执行批量测试的命令

./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_final.weights

1)执行如下所示: 

2)输入批量测试的绝对路径信息文本: /data/lxh/darknet/test.txt         

      (也可以使用 cfg/voc.data的文件里的valid后面的路径)

最后会在darknet/data/out/目录下生成批量测试图片。

10.补充

补充的问题针对第2步提到的情形2)图片格式不统一问题

10.1 删除2007_train.txt 和 2007_val.txt的文件

首先按照上述步骤,运行到3.2,修改3.2生成的2007_train.txt  和 2007_val.txt的文件

10.2 运行rename_picpath.py

重新生成txt的文件,后续接着第4步运行即可。

python rename_picpath.py

rename_picpath.py代码如下:

# rename_picpath.py

import os
'''
处理图片后缀不是jpg,导致训练过程中 Cannot load image的问题
'''
# 保存训练图片的完整的路径列表信息
train_file = open('2007_train.txt', "w")
trainval_file = open("2007_trainval.txt", "w")

wd = os.getcwd()

# 训练集
train_list = open("VOCdevkit/VOC2007/ImageSets/Main/train.txt").read().strip().split()
for file in os.listdir("VOCdevkit/VOC2007/JPEGImages/"):
    if file.split('.')[0] in train_list:
        train_file.write("{}/VOCdevkit/VOC2007/JPEGImages/{}\n".format(wd, file))

# 验证集
trainval_list = open("VOCdevkit/VOC2007/ImageSets/Main/trainval.txt").read().strip().split()
for file2 in os.listdir("VOCdevkit/VOC2007/JPEGImages"):
    if file2.split('.')[0] in trainval_list:
        trainval_file.write("{}/VOCdevkit/VOC2007/JPEGImages/{}\n".format(wd, file2))

train_file.close()
trainval_file.close()

注意:此处生成的txt文本信息,会在第5步中使用。

参考:

目标检测:YOLOv3: 训练自己的数据

YOLOv3批量测试图片并保存在自定义文件夹下

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值