yolo v2 之車牌檢測

一、前言

        本文主要使用yolo v2 訓練自己的車牌圖片數據,並能夠框出測試圖片中存在的車牌區域,也即車牌檢測。本文參考了博文http://m.blog.csdn.net/qq_34484472/article/details/73135354http://blog.csdn.net/zhuiqiuk/article/details/72722227

二、準備工作

        首先需要下載正確配置好darknet, 使用./darknet detect cfg/yolo.cfg yolo.weights data/dog.jpg 命令可得檢測結果。本文主要是爲了檢測車牌區域,在darknet下新建一文件夾plate_imgae,其中存放train和val、pic三個文件夾,train文件夾下爲1296張訓練圖片,val文件夾下爲196張驗證圖片,pic爲236張待檢測的圖片,圖片格式均爲jpg格式,訓練模型主要用到train和val這兩個文件夾。


三、製作標籤文件

        yolo v2使用VOC格式的數據集,故這裏首先需要對訓練集及驗證集的圖片進行標註,每張圖片均可得到相應的xml文件,然後再將xml文件轉化爲txt標籤文件。參考這裏http://blog.csdn.net/jesse_mx/article/details/53606897 安裝標註工具LabelImg,安裝完成後,./labelImg.py打開標註工具,先對train文件夾內的圖片作標註,在plate_image文件夾下新建train_xml文件夾,用於存放生成的xml文件。所生成的xml文件如下所示:


同樣的方法,也在在plate_image文件夾下新建val_xml文件夾,保存驗證集的xml標註文件。這樣就有VOC格式標記的XML文件,接下來需要將xml文件轉換爲txt文件,使用python轉換,將train_xml中的xml文件,轉換爲txt保存於文件夾train_txt下;同理,將val_xml中的xml文件轉換後保存於val_txt文件夾下

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

xml_label_Dir = 'train_xml'        #需轉換的xml路徑
txt_label_Dir = 'train_txt/'          #轉換得的txt文件保存路徑

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)  
  
if not os.path.exists(txt_label_Dir):   
    os.makedirs(txt_label_Dir)  
for rootDir,dirs,files in os.walk(xml_label_Dir):  
    for file in files:  
        file_name = file.split('.')[0]  
        out_file = open(txt_label_Dir+'%s.txt'%(file_name),'w')  
        in_file = open("%s/%s"%(rootDir,file))  
        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'):  
            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("0" + " " + " ".join([str(a) for a in bb]) + '\n')      #only one class,index 0
        out_file.close()    
接着還需要獲得訓練集圖片的絕對路徑train_imgae_path.txt和驗證集的絕對路徑val_image_path.txt,同樣使用以下python代碼
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join

TrainDir = '/home/jyang/darknet/plate_image/train'

out_file = open('train_image_path.txt','w')
for root,dirs,files in os.walk(TrainDir):
    for file in files:
        out_file.write('%s/%s\n'%(root,file))
out_file.close()
YOLO是直接通過替換原圖片絕對路徑的後綴名來找到對應標記文件的。比如原圖片的絕對路徑爲/home/ubuntu/data/train/1.jpg。則YOLO將會直接認爲其對應的標記文件路徑爲/home/ubuntu/data/train/1.txt。所以,我們將之前生成的txt文件都放到相應的圖片路徑下,即將train_txt下txt文件放到train文件夾下,將val_txt下txt文件放到val文件夾下

看看plate_image下都有啥。


四、修改配置文件

(1)創建names文件

      在YOLO主目錄的data文件夾下,創建一個.names文件,文件名任意,我的是myClass.names,在該文件中寫入所有類別的名稱,每一類佔一行,我只檢測車牌,故只在第一行寫licence.

(2)修改data文件

     修改darknet主目錄下的 cfg/voc.data文件,修改如下:

classes= 1                      #只有一個類別
train  = /home/jyang/darknet/plate_image/train_image_path.txt        #訓練集的絕對路徑
valid  = /home/jyang/darknet/plate_image/val_image_path.txt           #驗證集的絕對路徑
names = data/myClass.names
backup = /home/jyang/darknet/plate_image/backup                     #訓練得的模型保存路徑
(3)修改cfg文件

     修改darknet主目錄下的cfg/yolo-voc.cfg。

1.[region]層中classes改爲1

2.[region]層上方的[convolution]層中,filters的數量改成(classes+coords+1)*NUM。我這裏改成了(1+4+1)*5=30.

五、 修改src/yolo.c文件

1.第13行改成 char *voc_names[] = {"licence"}; //如果你是一類的話,就改成這樣,如果你有多類,自己根據需求改。

2.第322行draw_detections函數中,最後一個參數由20改成你的類別數,我這裏是1

3.第354行demo函數中,倒數第三個參數由20改成你的類別數,我這裏是1

4.第17行改成 char *train_images = "<你的路徑>/train_imgae_path";

5.第18行改成 char *backup_directory = "你的路徑/backup/";

6.第121行改成 char *base = "results/comp4_det_test_"

7.第123行改成 list *plist = get_paths("<你的路徑>/val_image_path.txt"); 

8.第209行改成 char *base = "results/comp4_det_test_"

9.第210行改成 list *plist = get_paths("<你的路徑>/val_image_path.txt");

10.將src/yolo_kernels.cu文件中,第62行draw_detections函數最後一個參數由20改成你的類別數,我這裏是1.

11.scripts/voc_label.py 文件中 ,位置第9行改成:classes=[“licence”],因爲我只有一類

12.將src/detector.c文件中,第372行改成 list *plist = get_paths("<你的路徑>/val_image_path.txt");,第542行option_find_int函數的最後一個參數由20改成1.

六、重新編譯darknet yolo

       進入darknet主目錄,make clean後 make -j8

七、下載預訓練文件cfg/darknet19_448.conv.23

        爲了加快訓練速度,下載官方提供的預訓練模型,保存至cfg下。下載地址爲

http://pjreddie.com/media/files/darknet19_448.conv.23

八、訓練

        在darknet文件夾路徑下運行命令:

        ./darknet detector train cfg/voc.data cfg/yolo-voc.cfg cfg/darknet19_448.conv.23
        系統默認會迭代45000次batch,如果需要修改訓練次數,進入cfg/yolo_voc.cfg修改max_batches的值。

九、訓練過程輸出log

       參考自這篇文章http://blog.csdn.net/renhanchi/article/details/71077830?locationNum=13&fps=1,以下摘自這篇文章

       Region Avg IOU: 這個是預測出的bbox和實際標註的bbox的交集 除以 他們的並集。顯然,這個數值越大,說明預測的結果越好

       Avg Recall: 這個表示平均召回率, 意思是 檢測出物體的個數 除以 標註的所有物體個數。

       count: 標註的所有物體的個數。 如果 count = 6, recall = 0.66667, 就是表示一共有6個物體(可能包含不同類別,這個不管類別),然後我預測出來了4個,所以Recall 就是 4 除以 6 = 0.66667

       有一行跟上面不一樣的,最開始的是iteration次數,然後是train loss,然後是avg train loss, 然後是學習率, 然後是一batch的處理時間, 然後是已經一共處理了多少張圖片。 重點關注 train loss 和avg train loss,這兩個值應該是隨着iteration增加而逐漸降低的。如果loss增大到幾百那就是訓練發散了,如果loss在一段時間不變,就需要降低learning rate或者改變batch來加強學習效果。當然也可能是訓練已經充分。這個需要自己判斷

十、評估訓練得的模型

       訓練了3天,得到了迭代30000次的權值,在plate_image/backup/下生成了yolo-voc_30000.weights,當然還有其他迭代次數的權值,使用以下語句可以測試單張圖片

./darknet detector test cfg/voc.data cfg/yolo-voc.cfg plate_image/backup/yolo-voc_30000.weights plate_image/pic/099999.jpg

       顯示的檢測結果爲

       

      訓練的時候只是用到了訓練集,評估檢測性能纔會使用到驗證集。使用以下指令評估

      root@computer:/home/jyang/darknet# ./darknet detector recall cfg/voc.data cfg/yolo-voc.cfg plate_image/backup/yolo-voc_30000.weights

      根據參考的第2篇博文,使用該指令的時候,會調用src/detector.c裏的validate_detector_recall函數,將其中的閾值從0.001修改爲0.25,防止過多框被識別出,將432行代碼修改爲如下:

fprintf(stderr, "Id:%5d Correct:%5d Total%5d\tRPs/Img: %.2f\tIOU: %.2f\tRecall:%.2f%%\tPrecision:%.2f%%\n", i, 
         correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total ,100.*correct/proposals);
重新make之後,運行結果爲:

可見196張圖片,準確率有97%以上。

結語

        本文只是使用了yolo v2 檢測了車牌,後續對框下來的車牌圖片作識別,請看《yolo v2之車牌檢測後續識別字符(一)》


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章