本文轉載自:
http://blog.csdn.net/ch_liu23/article/details/53558549
?最近在用yolo來做視頻中的人員檢測,選擇YOLO是從速度考慮,當然也可以用ssd。YOLO相關可看主頁Darknet,有相關代碼和使用方法。由于之前做自己的數據訓練過程中出現各種問題,參照了各種博客才跑通,現在記錄下以防后面忘記,也方便自己總結一下。
????? YOLO本身使用的是VOC的數據集,所以可以按照VOC數據集的架構來構建自己的數據集。
1.構建VOC數據集
1.準備數據
首先準備好自己的數據集,最好固定格式,此處以VOC為例,采用jpg格式的圖像,在名字上最好使用像VOC一樣類似000001.jpg、000002.jpg這樣??蓞⒄障旅媸纠a
[cpp] ?view plaincopy
?? ?? ?? #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" ;??????????????????????????????? ?? ????char ?dstpath[]?=? "DstImage" ;???????????????????????????? ?? ????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\\%05d.jpg" ,?i);?? ????????fprintf(fp,?"%05d\n" ,?i);?? ????????cvSaveImage(order,?pSrc);?? ????????printf("Saving?%s!\n" ,?order);?? ????????cvReleaseImage(&pSrc);?? ????}?? ????fclose(fp);?? ????return ?0;?? }?? 讀取某文件夾下的所有圖像然后統一命名,用了opencv所以順便還可以改格式。
準備好了自己的圖像后,需要按VOC數據集的結構放置圖像文件。VOC的結構如下
[plain] ?view plaincopy
--VOC?? ????--Annotations?? ????--ImageSets?? ??????--Main?? ??????--Layout?? ??????--Segmentation?? ????--JPEGImages?? ????--SegmentationClass?? ????--SegmentationObject??
????? 這里面用到的文件夾是
Annotation 、ImageSets和JPEGImages。其中文件夾
Annotation 中主要存放xml文件,每一個xml對應一張圖像,并且每個xml中存放的是標記的各個目標的位置和類別信息,命名通常與對應的原始圖像一樣;而ImageSets我們只需要用到Main文件夾,這里面存放的是一些文本文件,通常為train.txt、test.txt等,該文本文件里面的內容是需要用來訓練或測試的圖像的名字(無后綴無路徑);JPEGImages文件夾中放我們已按統一規則命名好的原始圖像。
????? 因此,首先
????? 1.新建文件夾VOC2007(通常命名為這個,也可以用其他命名,但一定是名字+年份,例如MYDATA2016,無論叫什么后面都需要改相關代碼匹配這里,本例中以
VOC2007為例)
????? 2.在VOC2007文件夾下新建三個文件夾
Annotation 、ImageSets和JPEGImages,并把準備好的自己的原始圖像放在JPEGImages文件夾下
????? 3.在ImageSets文件夾中,新建三個空文件夾Layout、Main、Segmentation,然后把寫了訓練或測試的圖像的名字的文本拷到Main文件夾下,按目的命名,我這里所
有圖像用來訓練,故而Main文件夾下只有train.txt文件。上面說的小代碼運行后會生成該文件,把它拷進去即可。
2.標記圖像目標區域
?????? 因為做的是目標檢測,所以接下來需要標記原始圖像中的目標區域。相關方法和工具有很多,這里需用labelImg,相關用法也有說明,基本就是框住目標區域然后雙擊類別,標記完整張圖像后點擊保存即可。操作界面如下:
通常save之后會將標記的信息保存在xml文件,其名字通常與對應的原始圖像一樣。最后生成的畫風是這樣的
其中每個xml文件是這樣的畫風
[html] ?view plaincopy
<? 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 > ?? ????< object > ?? ????????< name > person </ name > ?? ????????< pose > Unspecified </ pose > ?? ????????< truncated > 0 </ truncated > ?? ????????< difficult > 0 </ difficult > ?? ????????< bndbox > ?? ????????????< xmin > 152 </ xmin > ?? ????????????< ymin > 70 </ ymin > ?? ????????????< xmax > 181 </ xmax > ?? ????????????< ymax > 144 </ ymax > ?? ????????</ bndbox > ?? ????</ object > ?? </ annotation > ?? 注意filename中文件的文件名名沒有后綴,因此需要統一加上后綴。只需一段命令即可
[plain] ?view plaincopy
find?-name?'*.xml'?|xargs?perl?-pi?-e?'s|</filename>|.jpg</filename>|g'?? 在對應目錄下執行即可,這樣就可以把后綴添上。這樣就做按照VOC做好了我們的數據集,接下來就是放到算法中去訓練跑起來。
2.用YOLOv2訓練
1.生成相關文件
??? 按darknet的說明編譯好后,接下來在darknet-master/scripts文件夾中新建文件夾VOCdevkit,然后將整個VOC2007文件夾都拷到VOCdevkit文件夾下。
??? 然后,需要利用scripts文件夾中的voc_label.py文件生成一系列訓練文件和label,具體操作如下:
??? 首先需要修改voc_label.py中的代碼,這里主要修改數據集名,以及類別信息,我的是VOC2007,并且所有樣本用來訓練,沒有val或test,并且只檢測人,故只有一類
目標,因此按如下設置
[python] ?view plaincopy
import ?xml.etree.ElementTree?as?ET?? import ?pickle?? import ?os?? from ?os? import ?listdir,?getcwd?? from ?os.path? import ?join?? ?? ?? ?? ?? ?? sets=[('2007' ,? 'train' )]?? classes?=?[?"person" ]?? ?? ?? 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()??
修改好后在該目錄下運行命令:python voc_label.py,之后則在文件夾scripts\VOCdevkit\VOC2007下生成了文件夾lable,該文件夾下的畫風是這樣的
這里包含了類別和對應歸一化后的位置(i guess,如有錯請指正)。同時在scripts\下應該也生成了train_2007.txt這個文件,里面包含了所有訓練樣本的絕對路徑。
2.配置文件修改 ????? 做好了上述準備,就可以根據不同的網絡設置(cfg文件)來訓練了。在文件夾cfg中有很多cfg文件,應該跟caffe中的prototxt文件是一個意思。這里以tiny-yolo-voc.cfg為例,該網絡是yolo-voc的簡版,相對速度會快些。主要修改參數如下
[plain] ?view plaincopy
.?? .?? .?? [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等參數。這里歪個樓吐槽一下其他網絡配置,一開始是想用tiny.cfg來訓練的官網作者說它夠小也夠快,但是它的網絡配置最后幾層是這樣的畫風:
[html] ?view plaincopy
[convolutional]?? filters = 1000 ?? size = 1 ?? stride = 1 ?? pad = 1 ?? activation = linear ?? ?? [avgpool]?? ?? [softmax]?? groups = 1 ?? ?? [cost]?? type = sse ?? 這里沒有類別數,完全不知道怎么修改,強行把最后一層卷積層卷積核個數修改又跑不通會出錯,如有大神知道還望賜教。
????? Back to the point。修改好了cfg文件之后,就需要修改兩個文件,首先是data文件下的voc.names。打開voc.names文件可以看到有20類的名稱,本例中只有一類,檢測人,因此將原來所有內容清空,僅寫上person并保存。名字仍然用這個名字,如果喜歡用其他名字則請按一開始制作自己數據集的時候的名字來修改。
????? 接著需要修改cfg文件夾中的voc.data文件。也是按自己需求修改,我的修改之后是這樣的畫風:
[plain] ?view plaincopy
classes=?1??//類別數?? train??=?/home/kinglch/darknet-master/scripts/2007_train.txt??//訓練樣本的絕對路徑文件,也就是上文2.1中最后生成的?? //valid??=?/home/pjreddie/data/voc/2007_test.txt??//本例未用到?? names?=?data/voc.names??//上一步修改的voc.names文件?? backup?=?/home/kinglch/darknet-master/results/??//指示訓練后生成的權重放在哪?? 修改后按原名保存最好,接下來就可以訓練了。
ps:yolo v1中這些細節是直接在源代碼的yolo.c中修改的,源代碼如下
比如這里的類別,訓練樣本的路徑文件和模型保存路徑均在此指定,修改后從新編譯。而yolov2似乎擯棄了這種做法,所以訓練的命令也與v1版本的不一樣。
3.運行訓練 ????? 上面完成了就可以命令訓練了,可以在官網上找到一些預訓練的模型作為參數初始值,也可以直接訓練,訓練命令為
[plain] ?view plaincopy
$./darknet?detector?train?./cfg/voc.data?cfg/tiny-yolo-voc.cfg?? 如果用官網的預訓練模型darknet.conv.weights做初始化,則訓練命令為
[plain] ?view plaincopy
$./darknet?detector?train?./cfg/voc.data?.cfg/tiny-yolo-voc.cfg?darknet.conv.weights?? 不過我沒試成功,加上這個模型直接就除了final,不知道啥情況。當然也可以用自己訓練的模型做參數初始化,萬一訓練的時候被人終端了,可以再用訓練好的模型接上去接著訓練。
????? 訓練過程中會根據迭代次數保存訓練的權重模型,然后就可以拿來測試了,測試的命令同理:
. /darknet detector test
cfg/voc
. data cfg/tiny-yolo
- voc. cfg results/tiny-yolo
-voc_6000 . weights data/images.jpg
????? 這樣就完成了整個流程,目前測試感覺同種網絡v2版本似乎比v1版本慢很多,莫非是為了精度的提高犧牲了部分速度。然而我需要的是速度,這個就尷尬了。
????? 初學yolo,如有問題歡迎加我q675143196討論交流.
總結
以上是生活随笔 為你收集整理的YOLOv2训练:制作VOC格式的数据集 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。