深度學習(三) YOLOv2訓練自己的數據集


     本文在參考http://blog.csdn.net/ch_liu23/article/details/53558549的博客下,對其博客根據自己的要求做了一定的修改。

      YOLO相關可看主頁Darknet,有相關代碼和使用方法。由於之前做自己的數據訓練過程中出現各種問題,參照了各種博客才跑通,現在記錄下以防后面忘記,也方便自己總結一下。

      YOLO本身使用的是VOC的數據集,所以可以按照VOC數據集的架構來構建自己的數據集。

1.構建VOC數據集

1.准備數據

首先准備好自己的數據集,最好固定格式,此處以VOC為例,采用jpg格式的圖像,在名字上最好使用像VOC一樣類似I000001.jpg、I000002.jpg這樣。可參照下面示例代碼
  1. // Getfile.cpp : 重命名文件夾內的所有圖像並寫入txt,同時也可通過重寫圖像修改格式  
  2. //配用Opencv2.4.10  
  3.   
  4. #include "stdafx.h"  
  5. #include <stdio.h>  
  6. #include <string.h>  
  7. #include<io.h>    
  8. #include <opencv2\opencv.hpp>  
  9. #define IMGNUM 20000 //圖片所在文件夾中圖片的最大數量    
  10. char img_files[IMGNUM][1000];  
  11.   
  12. using namespace cv;  
  13. int getFiles(char *path)  
  14. {  
  15.     int num_of_img = 0;  
  16.     long   hFile = 0;  
  17.     struct _finddata_t fileinfo;  
  18.     char p[700] = { 0 };  
  19.     strcpy(p, path);  
  20.     strcat(p, "\\*");  
  21.     if ((hFile = _findfirst(p, &fileinfo)) != -1)  
  22.     {  
  23.         do  
  24.         {  
  25.             if ((fileinfo.attrib & _A_SUBDIR))  
  26.             {  
  27.                 if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)  
  28.                     continue;  
  29.             }  
  30.             else  
  31.             {  
  32.                 strcpy(img_files[num_of_img], path);  
  33.                 strcat(img_files[num_of_img], "\\");  
  34.                 strcat(img_files[num_of_img++], fileinfo.name);  
  35.             }  
  36.         } while (_findnext(hFile, &fileinfo) == 0);  
  37.         _findclose(hFile);  
  38.     }  
  39.     return num_of_img;  
  40. }  
  41. int main()  
  42. {  
  43.     char path[] = "SrcImage";                               //source image  
  44.     char dstpath[] = "DstImage";                            //destination image  
  45.     int num = getFiles(path);  
  46.     int i;  
  47.     char order[1000];  
  48.     FILE *fp = fopen("train.txt""w");  
  49.     for (i = 0; i<num; ++i)  
  50.     {  
  51.         printf("%s\n", img_files[i]);  
  52.         IplImage *pSrc = cvLoadImage(img_files[i]);  
  53.         sprintf(order, "DstImage\\I%05d.png", i);  
  54.         fprintf(fp, "I%05d\n", i);  
  55.         cvSaveImage(order, pSrc);  
  56.         printf("Saving %s!\n", order);  
  57.         cvReleaseImage(&pSrc);  
  58.     }  
  59.     fclose(fp);  
  60.     return 0;  
  61. }  
讀取某文件夾下的所有圖像然后統一命名,用了opencv所以順便還可以改格式。
准備好了自己的圖像后,需要按VOC數據集的結構放置圖像文件。VOC的結構如下
[plain] view plain copy
  1. --VOCdevkit 
  2.     --Annotations  
  3.     --ImageSets  
  4.       --Main  
  5.     --JPEGImages  
      這里面用到的文件夾是 Annotation、ImageSets和JPEGImages。其中文件夾 Annotation中主要存放xml文件,每一個xml對應一張圖像,並且每個xml中存放的是標記的各個目標的位置和類別信息,命名通常與對應的原始圖像一樣;在ImageSets文件夾下的Main文件夾,這里面存放文本文件,通常為train.txt、test.txt等,該文本文件里面的內容是需要用來訓練或測試的圖像的名字(無后綴無路徑);JPEGImages文件夾中放我們已按統一規則命名好的原始圖像。
      因此,首先
      1.新建文件夾VOCdevkit(可以用其他命名)
      2.在VOCdevkit文件夾下新建三個文件夾 Annotation、ImageSets和JPEGImages,並把准備好的自己的原始圖像放在JPEGImages文件夾下
      3.在ImageSets文件夾中,新建空文件夾Main,然后把寫了訓練或測試的圖像的名字的文本拷到Main文件夾下,這里只做demo之用,為了方便,把所 有圖像用來訓練,在Main文件夾下只有train.txt文件。上面的代碼運行后會生成該文件,copy進去即可。

2.標記圖像目標區域

       因為做的是目標檢測,所以接下來需要標記原始圖像中的目標區域。標記工具網上有很多,這里用 labelImg,建議安裝py2-qt4版本, 相關用法也有說明,基本就是框住目標區域然后雙擊類別,標記完整張圖像后點擊保存即可。操作界面如下:

通常save之后會將標記的信息保存在xml文件,其名字通常與對應的原始圖像一樣(每個xml里面的內容為下圖,xml生成過程中,master版本的好像要出錯,生成的xml文件,第10行和第11行會為0,安裝py2-qt4版本后,在標記過程中,還是查看下xml文件看下第10和11行是否非0)。

[html] view plain copy
  1. <annotation>
    <folder>Inria</folder>
    <filename>I00000.png</filename>
    <path>
    /home/syoung/darknet/scripts/VOCdevkit/Inria/I00000.png
    </path>
    <source>
    <database>Unknown</database>
    </source>
    <size>
    <width>818</width>
    <height>976</height>
    <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
    <name>person</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <bndbox>
    <xmin>41</xmin>
    <ymin>325</ymin>
    <xmax>212</xmax>
    <ymax>718</ymax>
    </bndbox>
    </object>

注意filename中文件的文件名名沒有后綴,因此需要統一加上后綴。只需一段命令即可
[plain] view plain copy
  1. find -name '*.xml' |xargs perl -pi -e 's|</filename>|.png</filename>|g'  
在對應目錄下執行即可,這樣就可以把后綴添上。這樣就做按照VOC做好了我們的數據集。

2.用YOLOv2訓練

1.生成相關文件

    首先需要修改voc_label.py中的代碼,這里主要修改數據集名,以及類別信息,我所有樣本用來訓練,沒有val或test,只檢測人,只有一類 目標,因此按如下設置
[python] view plain copy
  1. import xml.etree.ElementTree as ET  
  2. import pickle  
  3. import os  
  4. from os import listdir, getcwd  
  5. from os.path import join  
  6.   
  7. #sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]  
  8.   
  9. #classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]  
  10.   
  11. sets=[ 'train' ]  
  12. classes = [ "person"]  
  13.   
  14.   
  15. def convert(size, box):  
  16.     dw = 1./size[0]  
  17.     dh = 1./size[1]  
  18.     x = (box[0] + box[1])/2.0  
  19.     y = (box[2] + box[3])/2.0  
  20.     w = box[1] - box[0]  
  21.     h = box[3] - box[2]  
  22.     x = x*dw  
  23.     w = w*dw  
  24.     y = y*dh  
  25.     h = h*dh  
  26.     return (x,y,w,h)  
  27.   
  28. def convert_annotation(year, image_id):  
  29.     in_file = open('VOCdevkit/Annotations/%s.xml'%( image_id))  #(如果使用的不是VOC而是自設置數據集名字,則這里需要修改)  
  30.     out_file = open('VOCdevkit/labels/%s.txt'%( image_id), 'w')  #(同上)  
  31.     tree=ET.parse(in_file)  
  32.     root = tree.getroot()  
  33.     size = root.find('size')  
  34.     w = int(size.find('width').text)  
  35.     h = int(size.find('height').text)  
  36.   
  37.     for obj in root.iter('object'):  
  38.         difficult = obj.find('difficult').text  
  39.         cls = obj.find('name').text  
  40.         if cls not in classes or int(difficult) == 1:  
  41.             continue  
  42.         cls_id = classes.index(cls)  
  43.         xmlbox = obj.find('bndbox')  
  44.         b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))  
  45.         bb = convert((w,h), b)  
  46.         out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')  
  47.   
  48. wd = getcwd()  
  49.   
  50. for year, image_set in sets:  
  51.     if not os.path.exists('VOCdevkit/labels/'):  
  52.         os.makedirs('VOCdevkit/labels/')  
  53.     image_ids = open('VOCdevkit/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()  
  54.     list_file = open('%s.txt'%(image_set), 'w')  
  55.     for image_id in image_ids:  
  56.         list_file.write('%s/VOCdevkit/PNGImages/%s.png\n'%(wd, image_id))  
  57.         convert_annotation(year, image_id)  
  58.     list_file.close()  
修改好后在該目錄下運行命令:python voc_label.py,之后則在文件夾scripts\VOCdevkit下生成了文件夾lables,將該文件夾下txt文檔全部復制到PNGImages下,
同時在/scripts文件夾下生成了train.txt文件,里面包含了所有訓練樣本的絕對路徑。

2.配置文件修改

      做好了上述准備,就可以根據不同的網絡設置(cfg文件)來訓練了。在文件夾cfg中有很多cfg文件,根據自己的需要選擇修改cfg。這里以yolo-voc.cfg為例。主要修改參數如下

[plain] view plain copy
  1. .  
  2. .  
  3. .  
  4. [convolutional]  
  5. size=1  
  6. stride=1  
  7. pad=1  
  8. filters=30  //修改最后一層卷積層核參數個數,計算公式是依舊自己數據的類別數filter=num×(classes + coords + 1)=5×(1+4+1)=30  
  9. activation=linear  
  10.   
  11. [region]  
  12. anchors = 1.08,1.19,  3.42,4.41,  6.63,11.38,  9.42,5.11,  16.62,10.52  
  13. bias_match=1  
  14. classes=1  //類別數,本例為1類  
  15. coords=4  
  16. num=5  
  17. softmax=1  
  18. jitter=.2  
  19. rescore=1  
  20.   
  21. object_scale=5  
  22. noobject_scale=1  
  23. class_scale=1  
  24. coord_scale=1  
  25.   
  26. absolute=1  
  27. thresh = .6  
  28. random=1  
        另外也可根據需要修改learning_rate、max_batches等參數。修改好了cfg文件之后,就需要修改兩個文件,首先是data文件下的voc.names。打開voc.names文件可以看到有20類的名稱,本例中只有一類,檢測人,因此將原來所有內容清空,僅寫上person並保存。名字仍然用這個名字,如果喜歡用其他名字則請按一開始制作自己數據集的時候的名字來修改。

      接着需要修改cfg文件夾中的voc.data文件。也是按自己需求修改:

[plain] view plain copy
  1. classes= 1  //類別數  
  2. train  = /home/syoung/darknet/scripts/train.txt  //訓練樣本的絕對路徑文件,也就是上文2.1中最后生成的  
  3. names = data/voc.names  //上一步修改的voc.names文件  
  4. backup = /home/syoung/darknet/backup/  //指示訓練后生成的權重放在哪  
修改后保存,接下來就可以訓練了。

3.運行訓練

      上面完成了就可以命令訓練了,可以在官網上找到一些預訓練的模型作為參數初始值,也可以直接訓練,訓練命令為

[plain] view plain copy
  1. $./darknet detector train cfg/voc.data cfg/yolo-voc.cfg  
如果一次訓練時間太長,可以用中間自動保存的模型繼續訓練,中間自動保存模型,默認文件夾不改變的情況下在backup里面,訓練命令為
[plain] view plain copy
  1. $./darknet detector train cfg/voc.data cfg/yolo-voc.cfg yolo-voc_3000.weights
不過我沒試成功,加上這個模型直接就除了final,不知道啥情況。當然也可以用自己訓練的模型做參數初始化,萬一訓練的時候被人終端了,可以再用訓練好的模型接上去接着訓練。

      訓練過程中會根據迭代次數保存訓練的權重模型,然后就可以拿來測試圖像或者視頻了,圖像測試命令:(將圖像放於data文件夾下)

 ./darknet detect cfg/voc.data cfg/yolo-voc_3000.weights  data/images.jpg

      視頻測試命令:(將視頻放於data文件夾下)

       ./darknet detector demo cfg/yolo-voc.cfg yolo-voc_3000.weights data/video.avi    

       訓練時間較長,測試了訓練模型3000的檢測效果,效果還很渣,可能也與數據集制作的時候有一定關系。

      


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com