1. 程式人生 > >Yolo V2訓練自己資料集

Yolo V2訓練自己資料集

在參照了很多其他的部落格,成功訓練了自己的資料集,這裡記錄一下防止忘記便於以後總結學習。

VOC資料集準備

準備資料

首先準備好自己的資料集,最好固定格式,此處以VOC為例,採用jpg格式的影象,在名字上最好使用像VOC一樣類似I000001.jpg、I000002.jpg這樣。可參照下面示例程式碼,在Opencv中使用。

// Getfile.cpp : 重新命名資料夾內的所有影象並寫入txt,同時也可通過重寫影象修改格式  
//配用Opencv2.4.10  
  
#include "stdafx.h"  
#include <stdio.h>  
#include <string.h>  
#include<io.h>    
#include <opencv2\opencv.hpp>  
#define IMGNUM 20000 //圖片所在資料夾中圖片的最大數量    
char img_files[IMGNUM][1000];  
  
using namespace cv;  
int getFiles(char *path)  
{  
    int num_of_img = 0;  
    long   hFile = 0;  
    struct _finddata_t fileinfo;  
    char p[700] = { 0 };  
    strcpy(p, path);  
    strcat(p, "\\*");  
    if ((hFile = _findfirst(p, &fileinfo)) != -1)  
    {  
        do  
        {  
            if ((fileinfo.attrib & _A_SUBDIR))  
            {  
                if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)  
                    continue;  
            }  
            else  
            {  
                strcpy(img_files[num_of_img], path);  
                strcat(img_files[num_of_img], "\\");  
                strcat(img_files[num_of_img++], fileinfo.name);  
            }  
        } while (_findnext(hFile, &fileinfo) == 0);  
        _findclose(hFile);  
    }  
    return num_of_img;  
}  
int main()  
{  
    char path[] = "SrcImage";                               //source image  
    char dstpath[] = "DstImage";                            //destination image  
    int num = getFiles(path);  
    int i;  
    char order[1000];  
    FILE *fp = fopen("train.txt", "w");  
    for (i = 0; i<num; ++i)  
    {  
        printf("%s\n", img_files[i]);  
        IplImage *pSrc = cvLoadImage(img_files[i]);  
        sprintf(order, "DstImage\\I%05d.jpg", i);  
        fprintf(fp, "I%05d\n", i);  
        cvSaveImage(order, pSrc);  
        printf("Saving %s!\n", order);  
        cvReleaseImage(&pSrc);  
    }  
    fclose(fp);  
    return 0;  
}  

讀取某一個檔案下所有的圖片(該檔案下的圖片命名是不統一的),輸出該資料夾圖片到一個目標檔案下,且對目標檔案下所有的圖片統一命名,並修改所有圖片的格式,將名字寫入到txt中。

準備好了自己的影象後,需要VOC資料集的結構放置影象檔案,VOC的結構如下:

--VOCdevkit  
    --Annotations  
    --ImageSets  
      --Main  
    --JPEGImages  

這裡面用到的資料夾是Annotation、ImageSets和JPEGImages。其中資料夾Annotation中主要存放xml檔案,每一個xml對應一張影象,並且每個xml中存放的是標記的各個目標的位置和類別資訊,命名通常與對應的原始影象一樣;在ImageSets資料夾下的Main資料夾,這裡面存放文字檔案,通常為train.txt、test.txt等,該文字檔案裡面的內容是需要用來訓練或測試的影象的名字(無後綴無路徑);JPEGImages資料夾中放我們已按統一規則命名好的原始影象。

因此,構建上述資料夾結構。

1.新建資料夾VOCdevkit(可以用其他命名,我用的是yolo-data)

2.在VOCdevkit資料夾(yolo-data)下新建三個資料夾Annotation、ImageSets和JPEGImages,並把準備好的自己的原始圖片放在JPEGImages資料夾下

3.在ImageSets資料夾中,新建空資料夾Main,然後把寫了訓練或測試的影象的名字的文字拷到Main資料夾下,這裡只做demo之用,為了方便,把所有影象用來訓練,在Main資料夾下只有train.txt檔案。上面的程式碼執行後會生成該檔案,copy進去即可。

標記影象目標區域

因為做的是目標檢測,所以接下來需要標記原始圖片中目標區域(待檢測區域)。用的是Python版本的labeling工具,GitHub上搜labeling就能找到。基本就是框住目標區域然後雙擊類別,標記完整張影象後點擊儲存即可,有Markdown說明。操作介面如下:

demo3.jpg (799×401)

通常save之後會將標記的資訊儲存在xml檔案,其名字通常與對應的原始影象一樣(每個xml裡面的內容為下圖,xml生成過程中,master版本的好像要出錯,生成的xml檔案,第10行和第11行會為0,安裝py2-qt4版本後,在標記過程中,還是檢視下xml檔案看下第10和11行是否非0)。

 

用Yolov2訓練

 生成相關檔案

首先需要修改voc_label.py中的程式碼,這裡主要修改資料集名,以及類別資訊,我所有樣本用來訓練,沒有val或test,只檢測一類目標,因此按如下設定:

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=[('2018','train')]
classes=["VN"]

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('Annotations/%s.xml'%( image_id))
    out_file = open('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 year, image_set in sets:
	if not os.path.exists('labels/'):
		os.makedirs('labels/')
	image_ids = open('ImageSets/Main/%s.txt'%(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/JPEGImages/%s.jpg\n'%(wd, image_id))
		convert_annotation(year, image_id)
	list_file.close()

修改後再改目錄下執行命令:python voc_label.py,之後就在yolo-data下生成labels資料夾,裡面包含的是類別和座標位置。同時在該資料夾下還生成了2018_train.txt檔案,裡面包含了 所有訓練樣本的絕對路徑。

配置檔案修改

做好了上述準備,就可以根據不同的網路設定(cfg檔案)來訓練了。在資料夾cfg中有很多cfg檔案,根據自己的需要選擇修改cfg。這裡以yolov2-tiny-voc.cfg為例。主要修改引數如下:

[convolutional]  
size=1  
stride=1  
pad=1  
filters=30  //修改最後一層卷積層核引數個數,計算公式是依舊自己資料的類別數filter=num×(classes + coords + 1)=5×(1+4+1)=30  
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  

另外也可根據需要修改learning_rate、max_batches等引數。修改好了cfg檔案之後,就需要修改兩個檔案,首先是yolo-data檔案下的voc.names。開啟voc.names檔案可以看到有20類的名稱,本例中只有一類目標,因此將原來所有內容清空,僅寫上labeling時的類別名稱並儲存。如果喜歡用其他名字則請按一開始製作自己資料集的時候的名字來修改。 接著需要修改voc.data檔案,根據自己的實際目錄來改:

classes= 1
train  = F:/demo_py/yolo/darknet-master/build/darknet/x64/yolo-data/2018_train.txt //訓練樣本的絕對路徑,上面py生成的
valid  = F:/demo_py/yolo/darknet-master/build/darknet/x64/yolo-data/2018_test.txt
names = F:/demo_py/yolo/darknet-master/build/darknet/x64/yolo-data/hero.names //voc.names檔案
backup = F:/demo_py/yolo/darknet-master/build/darknet/x64/yolo-data/backup/ //訓練生成的權重

開始訓練

相關設定完成後,就可以訓練了。可以選擇預訓練模型作為引數初始值,去GitHub上 可以找到預訓練的模型。訓練指令為:

darknet.exe detector train yolo-data/hero.data yolo-data/yolov2-tiny.cfg yolo-data/yolov2-tiny.weights

也可以直接訓練

darknet.exe detector train yolo-data/hero.data yolo-data/yolov2-tiny.cfg 

訓練完成後就可以拿輸出的訓練模型測試了,將圖片放在yolo-data資料夾下

darknet.exe detector test yolo-data/hero.data yolo-data/yolov2-tiny.cfg yolo-data/backup/yolov2-tiny_419000.weights yolo-data/test.jpg