天天看點

YOLOv3訓練KITTI資料集

YOLOv3訓練KITTI資料集——Wiznote筆記

部分參考SSD: Single Shot MultiBox Detector 訓練KITTI資料集(1),感謝部落客的貢獻。

目錄

  • 1 擷取KITTI資料集及标簽
  • 2 将KITTI的标簽格式轉換為VOC資料格式的标簽xxx.xml
    • 2-1 使用modify_annotations_txt.py調整原來的8類為現在的3類
    • 2-2 将原來KITTI标注的txt格式轉換為PASCAL VOC的xml格式
  • 3 将VOC格式的标簽轉換為YOLO格式的标簽xxx.txt
  • 4 生成train.txt和val.txt
  • 5 準備資料的配置檔案
    • 5-1 準備kitti.names
    • 5-2 準備kitti.data
    • 5-3 準備yolov3-kitti.cfg
  • 6 下載下傳ImageNet預訓練的網絡參數
  • 7 訓練模型
  • 8 測試
  • 9 中斷訓練後繼續訓練

1 擷取KITTI資料集及标簽

打開KITTI官方網址,出現以下網頁:

YOLOv3訓練KITTI資料集

由于我們隻做2D的對象檢測,隻需要下載下傳第一項Download left color images of object data set (12 GB)和對應的标簽Download training labels of object data set (5 MB)。下載下傳并解壓完畢後,我們可以發現

檔案夾data_object_image_2

存放了訓練集和測試集圖檔,其中訓練集有7481張,測試集有7518張,共有 8個類别:

Car(小轎車)

Van(面包車)

Truck(卡車)

Tram(電車)

Pedestrain(行人)

Person(sit-ting)(行人)

Cyclist(騎行人)

Misc(雜項)

。還有一項

DontCare

為不關心的物體,

檔案夾training

存放了訓練集的标簽,而測試集沒有給出标簽。檔案目錄樹分布如下:

├── data_object_image_2
│   ├── testing
│   │   └── image_2
                 ├── 000000.png 
                 ├── 000001.png 
│   └── training
│   │   └── image_2
                 ├── 000000.png 
                 ├── 000001.png 
│   └── image_2
└── training
    └── label_2
        ├── 000000.txt
        ├── 000001.txt
        ├── 000002.txt

           

我們這裡用全部的7481張圖檔用來訓練

2 将KITTI的标簽格式轉換為VOC資料格式的标簽xxx.xml

我們建立一個PASCAL VOC結構的檔案夾目錄形式,原來PASCAL VOC目錄結構如下:

YOLOv3訓練KITTI資料集

其中,

Annotations

檔案夾存放标簽檔案

xxxx.xml

ImageSets

檔案夾存放了各種任務(Action,Segmentation,Detection)需要的訓練集和驗證集的圖檔名彙總,我們這裡不需要這個檔案夾,

JPEGImages

檔案夾存放了所有的圖檔,

labels

檔案夾存放了

darknet架構

的标簽格式檔案

xxxx.txt

。同一張圖檔的xml标簽格式和txt标簽格式如下所示:

2008_000003.jpg

YOLOv3訓練KITTI資料集

2008_000003.txt:

# class_id x y w h
18 0.546 0.5165165165165165 0.908 0.9669669669669669
14 0.145 0.6501501501501501 0.042 0.15915915915915915
           

2008_000003.xml

<annotation>
    <folder>VOC2012</folder>
    <filename>2008_000003.jpg</filename>
    <source>
        <database>The VOC2008 Database</database>
        <annotation>PASCAL VOC2008</annotation>
        <image>flickr</image>
    </source>
    <size>
        <width>500</width>
        <height>333</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>train</name>
        <pose>Unspecified</pose>
        <truncated>1</truncated>
        <occluded>0</occluded>
        <bndbox>
            <xmin>46</xmin>
            <ymin>11</ymin>
            <xmax>500</xmax>
            <ymax>333</ymax>
        </bndbox>
        <difficult>0</difficult>
    </object>
    <object>
        <name>person</name>
        <pose>Right</pose>
        <truncated>1</truncated>
        <occluded>0</occluded>
        <bndbox>
            <xmin>62</xmin>
            <ymin>190</ymin>
            <xmax>83</xmax>
            <ymax>243</ymax>
        </bndbox>
        <difficult>0</difficult>
    </object>
</annotation>
           

txt格式裡面的x,y,w,h對于xml的轉換公式如下:

x = ( x m i n + x m a x ) / ( 2 ∗ w i d t h ) x=(xmin+xmax)/(2*width) x=(xmin+xmax)/(2∗width)

y = ( y m i n + y m a x ) / ( 2 ∗ h e i g h t ) y=(ymin+ymax)/(2*height) y=(ymin+ymax)/(2∗height)

w = ( x m a x − x m i n ) / ( w i d t h ) w=(xmax-xmin)/(width) w=(xmax−xmin)/(width)

這裡的w為整個對象的寬度的占圖檔總寬度的比,在darknet中,一般還要把w除以2

h = ( y m a x − y m i n ) / ( h e i g h t ) h=(ymax-ymin)/(height) h=(ymax−ymin)/(height)

這裡的h為整個對象的高讀的占圖檔總高度的比,在darknet中,一般還要把h除以2

我們這裡模仿PASCAL VOC目錄格式,建立一個

VOC_KITTI檔案夾

,裡面分别建立

Annotations

檔案夾(用于存放将要生成的标簽檔案

xxxx.xml

),

JPEGImages

檔案夾(用于存放KITTI所有的訓練圖檔),

Labels

檔案夾(用于存放了KITTI的标簽格式檔案

xxxx.txt

),

還有兩個腳本檔案modify_annotations_txt.py和kitti_txt_to_xml.py

,具體用法下面再講。如下圖所示:

YOLOv3訓練KITTI資料集

這裡Annotations有14962項的原因是生成了xml和下面要講的生成的darknet格式的标簽檔案。

2-1 使用modify_annotations_txt.py調整原來的8類為現在的3類

以下提供一個腳本

modify_annotations_txt.py

來将原來的8類物體轉換為我們現在需要的3類:

Car

Pedestrain

Cyclist

。我們把原來的

Car

Van

Truck

Tram

合并為

Car

類,把原來的

Pedestrain

Person(sit-ting)

合并為現在的

Pedestrain

,原來的

Cyclist

這一類保持不變。

# modify_annotations_txt.py
import glob
import string
txt_list = glob.glob('./Labels/*.txt') # 存儲Labels檔案夾所有txt檔案路徑
def show_category(txt_list):
    category_list= []
    for item in txt_list:
        try:
            with open(item) as tdf:
                for each_line in tdf:
                    labeldata = each_line.strip().split(' ') # 去掉前後多餘的字元并把其分開
                    category_list.append(labeldata[0]) # 隻要第一個字段,即類别
        except IOError as ioerr:
            print('File error:'+str(ioerr))
    print(set(category_list)) # 輸出集合
def merge(line):
    each_line=''
    for i in range(len(line)):
        if i!= (len(line)-1):
            each_line=each_line+line[i]+' '
        else:
            each_line=each_line+line[i] # 最後一條字段後面不加空格
    each_line=each_line+'\n'
    return (each_line)
print('before modify categories are:\n')
show_category(txt_list)
for item in txt_list:
    new_txt=[]
    try:
        with open(item, 'r') as r_tdf:
            for each_line in r_tdf:
                labeldata = each_line.strip().split(' ')
                if labeldata[0] in ['Truck','Van','Tram']: # 合并汽車類
                    labeldata[0] = labeldata[0].replace(labeldata[0],'Car')
                if labeldata[0] == 'Person_sitting': # 合并行人類
                    labeldata[0] = labeldata[0].replace(labeldata[0],'Pedestrian')
                if labeldata[0] == 'DontCare': # 忽略Dontcare類
                    continue
                if labeldata[0] == 'Misc': # 忽略Misc類
                    continue
                new_txt.append(merge(labeldata)) # 重新寫入新的txt檔案
        with open(item,'w+') as w_tdf: # w+是打開原檔案将内容删除,另寫新内容進去
            for temp in new_txt:
                w_tdf.write(temp)
    except IOError as ioerr:
        print('File error:'+str(ioerr))
print('\nafter modify categories are:\n')
show_category(txt_list) 
           

執行指令python modify_annotations_txt.py運作腳本,将原來的8類物體轉換為現在的3類。這裡以000010.txt為例,展示原來的8類标簽格式和現在的3類标簽格式。

# 原來的标簽
Car 0.80 0 -2.09 1013.39 182.46 1241.00 374.00 1.57 1.65 3.35 4.43 1.65 5.20 -1.42
Car 0.00 0 1.95 354.43 185.52 549.52 294.49 1.43 1.70 3.95 -2.39 1.66 11.80 1.76
Pedestrian 0.00 2 1.41 859.54 159.80 879.68 221.40 1.96 0.72 1.09 8.33 1.55 23.51 1.75
Car 0.00 0 -1.78 819.63 178.12 926.85 251.56 1.51 1.60 3.24 5.85 1.64 16.50 -1.44
Car 0.00 2 -1.69 800.54 178.06 878.75 230.56 1.45 1.74 4.10 6.87 1.62 22.05 -1.39
Car 0.00 0 1.80 558.55 179.04 635.05 230.61 1.54 1.68 3.79 -0.38 1.76 23.64 1.78
Car 0.00 2 1.77 598.30 178.68 652.25 218.17 1.49 1.52 3.35 0.64 1.74 29.07 1.79
Car 0.00 1 -1.67 784.59 178.04 839.98 220.10 1.53 1.65 4.37 7.88 1.75 28.53 -1.40
Car 0.00 1 1.92 663.74 175.36 707.21 204.15 1.64 1.45 3.48 4.50 1.80 42.85 2.02
DontCare -1 -1 -10 737.69 163.56 790.86 197.98 -1 -1 -1 -1000 -1000 -1000 -10
DontCare -1 -1 -10 135.60 185.44 196.06 202.15 -1 -1 -1 -1000 -1000 -1000 -10
DontCare -1 -1 -10 796.02 162.52 862.73 183.40 -1 -1 -1 -1000 -1000 -1000 -10
DontCare -1 -1 -10 879.35 165.65 931.48 182.36 -1 -1 -1 -1000 -1000 -1000 -10

# 現在生成的标簽
Car 0.80 0 -2.09 1013.39 182.46 1241.00 374.00 1.57 1.65 3.35 4.43 1.65 5.20 -1.42
Car 0.00 0 1.95 354.43 185.52 549.52 294.49 1.43 1.70 3.95 -2.39 1.66 11.80 1.76
Pedestrian 0.00 2 1.41 859.54 159.80 879.68 221.40 1.96 0.72 1.09 8.33 1.55 23.51 1.75
Car 0.00 0 -1.78 819.63 178.12 926.85 251.56 1.51 1.60 3.24 5.85 1.64 16.50 -1.44
Car 0.00 2 -1.69 800.54 178.06 878.75 230.56 1.45 1.74 4.10 6.87 1.62 22.05 -1.39
Car 0.00 0 1.80 558.55 179.04 635.05 230.61 1.54 1.68 3.79 -0.38 1.76 23.64 1.78
Car 0.00 2 1.77 598.30 178.68 652.25 218.17 1.49 1.52 3.35 0.64 1.74 29.07 1.79
Car 0.00 1 -1.67 784.59 178.04 839.98 220.10 1.53 1.65 4.37 7.88 1.75 28.53 -1.40
Car 0.00 1 1.92 663.74 175.36 707.21 204.15 1.64 1.45 3.48 4.50 1.80 42.85 2.02
           

2-2 将原來KITTI标注的txt格式轉換為PASCAL VOC的xml格式

原來一張圖檔的

KITTI标注格式如下:

Car 0.00 0 -1.67 642.24 178.50 680.14 208.68 1.38 1.49 3.32 2.41 1.66 34.98 -1.60 
Car 0.00 0 -1.75 685.77 178.12 767.02 235.21 1.50 1.62 3.89 3.27 1.67 21.18 -1.60 
           

具體含義如下:

YOLOv3訓練KITTI資料集

我們現在隻需要該标簽的type、bbox等五項,還需要把float類型轉換為int類型,最後将生成的xml檔案存放于Annotations檔案夾中,使用腳本

kitti_txt_to_xml.py

# kitti_txt_to_xml.py
# encoding:utf-8
# 根據一個給定的XML Schema,使用DOM樹的形式從空白檔案生成一個XML
from xml.dom.minidom import Document
import cv2
import os
def generate_xml(name,split_lines,img_size,class_ind):
    doc = Document() # 建立DOM文檔對象
    annotation = doc.createElement('annotation')
    doc.appendChild(annotation)
    title = doc.createElement('folder')
    title_text = doc.createTextNode('KITTI')
    title.appendChild(title_text)
    annotation.appendChild(title)
    img_name=name+'.png'
    title = doc.createElement('filename')
    title_text = doc.createTextNode(img_name)
    title.appendChild(title_text)
    annotation.appendChild(title)
    source = doc.createElement('source')
    annotation.appendChild(source)
    title = doc.createElement('database')
    title_text = doc.createTextNode('The KITTI Database')
    title.appendChild(title_text)
    source.appendChild(title)
    title = doc.createElement('annotation')
    title_text = doc.createTextNode('KITTI')
    title.appendChild(title_text)
    source.appendChild(title)
    size = doc.createElement('size')
    annotation.appendChild(size)
    title = doc.createElement('width')
    title_text = doc.createTextNode(str(img_size[1]))
    title.appendChild(title_text)
    size.appendChild(title)
    title = doc.createElement('height')
    title_text = doc.createTextNode(str(img_size[0]))
    title.appendChild(title_text)
    size.appendChild(title)
    title = doc.createElement('depth')
    title_text = doc.createTextNode(str(img_size[2]))
    title.appendChild(title_text)
    size.appendChild(title)
    for split_line in split_lines:
        line=split_line.strip().split()
        if line[0] in class_ind:
            object = doc.createElement('object')
            annotation.appendChild(object)
            title = doc.createElement('name')
            title_text = doc.createTextNode(line[0])
            title.appendChild(title_text)
            object.appendChild(title)
            bndbox = doc.createElement('bndbox')
            object.appendChild(bndbox)
            title = doc.createElement('xmin')
            title_text = doc.createTextNode(str(int(float(line[4]))))
            title.appendChild(title_text)
            bndbox.appendChild(title)
            title = doc.createElement('ymin')
            title_text = doc.createTextNode(str(int(float(line[5]))))
            title.appendChild(title_text)
            bndbox.appendChild(title)
            title = doc.createElement('xmax')
            title_text = doc.createTextNode(str(int(float(line[6]))))
            title.appendChild(title_text)
            bndbox.appendChild(title)
            title = doc.createElement('ymax')
            title_text = doc.createTextNode(str(int(float(line[7]))))
            title.appendChild(title_text)
            bndbox.appendChild(title)
    # 将DOM對象doc寫入檔案
    f = open('Annotations/'+name+'.xml','w')
    f.write(doc.toprettyxml(indent = ''))
    f.close()
if __name__ == '__main__':
    class_ind=('Pedestrian', 'Car', 'Cyclist')
    cur_dir=os.getcwd()
    labels_dir=os.path.join(cur_dir,'Labels')
    for parent, dirnames, filenames in os.walk(labels_dir): # 分别得到根目錄,子目錄和根目錄下檔案   
        for file_name in filenames:
            full_path=os.path.join(parent, file_name) # 擷取檔案全路徑
            f=open(full_path)
            split_lines = f.readlines()
            name= file_name[:-4] # 後四位是擴充名.txt,隻取前面的檔案名
            img_name=name+'.png' 
            img_path=os.path.join('./JPEGImages/',img_name) # 路徑需要自行修改            
            img_size=cv2.imread(img_path).shape
            generate_xml(name,split_lines,img_size,class_ind)
print('all txts has converted into xmls')
           

由于我們使用KITTI提供的共7481張訓練集圖檔作為我們的訓練集,是以,

這裡第86行代碼為JPEGImages檔案夾内的所有圖檔, 生成的xml檔案存放于第72行的Annotations檔案夾内。

執行指令

python kitti_txt_to_xml.py

,生成PASCAL VOC格式的xml檔案。這裡以000010.txt為例,展示原來的txt标簽格式和現在xml标簽格式。

# 步驟2-1生成的txt格式
Car 0.80 0 -2.09 1013.39 182.46 1241.00 374.00 1.57 1.65 3.35 4.43 1.65 5.20 -1.42
Car 0.00 0 1.95 354.43 185.52 549.52 294.49 1.43 1.70 3.95 -2.39 1.66 11.80 1.76
Pedestrian 0.00 2 1.41 859.54 159.80 879.68 221.40 1.96 0.72 1.09 8.33 1.55 23.51 1.75
Car 0.00 0 -1.78 819.63 178.12 926.85 251.56 1.51 1.60 3.24 5.85 1.64 16.50 -1.44
Car 0.00 2 -1.69 800.54 178.06 878.75 230.56 1.45 1.74 4.10 6.87 1.62 22.05 -1.39
Car 0.00 0 1.80 558.55 179.04 635.05 230.61 1.54 1.68 3.79 -0.38 1.76 23.64 1.78
Car 0.00 2 1.77 598.30 178.68 652.25 218.17 1.49 1.52 3.35 0.64 1.74 29.07 1.79
Car 0.00 1 -1.67 784.59 178.04 839.98 220.10 1.53 1.65 4.37 7.88 1.75 28.53 -1.40
Car 0.00 1 1.92 663.74 175.36 707.21 204.15 1.64 1.45 3.48 4.50 1.80 42.85 2.02

# 此時轉換的xml格式
<?xml version="1.0" ?>
<annotation>
    <folder>KITTI</folder>
    <filename>000010.png</filename>
    <source>
        <database>The KITTI Database</database>
        <annotation>KITTI</annotation>
    </source>
    <size>
        <width>1242</width>
        <height>375</height>
        <depth>3</depth>
    </size>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>1013</xmin>
            <ymin>182</ymin>
            <xmax>1241</xmax>
            <ymax>374</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>354</xmin>
            <ymin>185</ymin>
            <xmax>549</xmax>
            <ymax>294</ymax>
        </bndbox>
    </object>
    <object>
        <name>Pedestrian</name>
        <bndbox>
            <xmin>859</xmin>
            <ymin>159</ymin>
            <xmax>879</xmax>
            <ymax>221</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>819</xmin>
            <ymin>178</ymin>
            <xmax>926</xmax>
            <ymax>251</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>800</xmin>
            <ymin>178</ymin>
            <xmax>878</xmax>
            <ymax>230</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>558</xmin>
            <ymin>179</ymin>
            <xmax>635</xmax>
            <ymax>230</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>598</xmin>
            <ymin>178</ymin>
            <xmax>652</xmax>
            <ymax>218</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>784</xmin>
            <ymin>178</ymin>
            <xmax>839</xmax>
            <ymax>220</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>663</xmin>
            <ymin>175</ymin>
            <xmax>707</xmax>
            <ymax>204</ymax>
        </bndbox>
    </object>
</annotation>
           

3 将VOC格式的xml标簽轉換為darknet格式的标簽xxx.txt

我們現在已經有了VOC标簽格式的xml檔案,現在我們需要生成darknet中YOLO使用的txt标簽格式。我們在VOC_KITTI檔案夾内建立一個

xml_to_yolo_txt.py檔案,代碼如下:

# xml_to_yolo_txt.py
# 此代碼和VOC_KITTI檔案夾同目錄
import glob
import xml.etree.ElementTree as ET
# 這裡的類名為我們xml裡面的類名,順序現在不需要考慮
class_names = ['Car', 'Cyclist', 'Pedestrian']
# xml檔案路徑
path = './Annotations/' 
# 轉換一個xml檔案為txt
def single_xml_to_txt(xml_file):
    tree = ET.parse(xml_file)
    root = tree.getroot()
    # 儲存的txt檔案路徑
    txt_file = xml_file.split('.')[0]+'.txt'
    with open(txt_file, 'w') as txt_file:
        for member in root.findall('object'):
            #filename = root.find('filename').text
            picture_width = int(root.find('size')[0].text)
            picture_height = int(root.find('size')[1].text)
            class_name = member[0].text
            # 類名對應的index
            class_num = class_names.index(class_name)

            box_x_min = int(member[4][0].text) # 左上角橫坐标
            box_y_min = int(member[4][1].text) # 左上角縱坐标
            box_x_max = int(member[4][2].text) # 右下角橫坐标
            box_y_max = int(member[4][3].text) # 右下角縱坐标
            # 轉成相對位置和寬高
            x_center = float(box_x_min + box_x_max) / (2 * picture_width)
            y_center = float(box_y_min + box_y_max) / (2 * picture_height)
            width = float(box_x_max - box_x_min) /  picture_width
            height = float(box_y_max - box_y_min) /  picture_height
            print(class_num, x_center, y_center, width, height)
            txt_file.write(str(class_num) + ' ' + str(x_center) + ' ' + str(y_center) + ' ' + str(width) + ' ' + str(height) + '\n')
# 轉換檔案夾下的所有xml檔案為txt
def dir_xml_to_txt(path):
    for xml_file in glob.glob(path + '*.xml'):
        single_xml_to_txt(xml_file)
dir_xml_to_txt(path)
           

執行指令:

python xml_to_yolo_txt.py

,生成的txt檔案在Annotations檔案夾内。這裡以000010.txt為例,展示原來的xml标簽格式和現在darknet的txt标簽格式。

# 原來的xml格式
<?xml version="1.0" ?>
<annotation>
    <folder>KITTI</folder>
    <filename>000010.png</filename>
    <source>
        <database>The KITTI Database</database>
        <annotation>KITTI</annotation>
    </source>
    <size>
        <width>1242</width>
        <height>375</height>
        <depth>3</depth>
    </size>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>1013</xmin>
            <ymin>182</ymin>
            <xmax>1241</xmax>
            <ymax>374</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>354</xmin>
            <ymin>185</ymin>
            <xmax>549</xmax>
            <ymax>294</ymax>
        </bndbox>
    </object>
    <object>
        <name>Pedestrian</name>
        <bndbox>
            <xmin>859</xmin>
            <ymin>159</ymin>
            <xmax>879</xmax>
            <ymax>221</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>819</xmin>
            <ymin>178</ymin>
            <xmax>926</xmax>
            <ymax>251</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>800</xmin>
            <ymin>178</ymin>
            <xmax>878</xmax>
            <ymax>230</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>558</xmin>
            <ymin>179</ymin>
            <xmax>635</xmax>
            <ymax>230</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>598</xmin>
            <ymin>178</ymin>
            <xmax>652</xmax>
            <ymax>218</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>784</xmin>
            <ymin>178</ymin>
            <xmax>839</xmax>
            <ymax>220</ymax>
        </bndbox>
    </object>
    <object>
        <name>Car</name>
        <bndbox>
            <xmin>663</xmin>
            <ymin>175</ymin>
            <xmax>707</xmax>
            <ymax>204</ymax>
        </bndbox>
    </object>
</annotation>

# 現在darknet的txt檔案格式
0 0.9074074074074074 0.7413333333333333 0.09178743961352658 0.256
0 0.3635265700483092 0.6386666666666667 0.0785024154589372 0.14533333333333334
2 0.6996779388083736 0.5066666666666667 0.008051529790660225 0.08266666666666667
0 0.7024959742351047 0.572 0.0430756843800322 0.09733333333333333
0 0.6755233494363929 0.544 0.03140096618357488 0.06933333333333333
0 0.48027375201288247 0.5453333333333333 0.030998389694041867 0.068
0 0.5032206119162641 0.528 0.021739130434782608 0.05333333333333334
0 0.6533816425120773 0.5306666666666666 0.02214170692431562 0.056
0 0.5515297906602254 0.5053333333333333 0.017713365539452495 0.03866666666666667
           

可以看出,原來的bbox資料現在已經全部歸一化,原來的Car類型變成現在的索引0,原來的Pedestrain類型變成現在的索引2。

4 生成train.txt和val.txt

現在我們需要生成train.txt檔案,裡面存放了每一張訓練圖檔的路徑,由于我們沒有測試,可以先不生成val.txt。這裡我們在

darknet安裝目錄下

建立一個

kitti_train_val.py檔案

,代碼如下:

# kitti_train_val.py
# 此代碼和data檔案夾同目錄
import glob
path = 'kitti_data/'
def generate_train_and_val(image_path, txt_file):
    with open(txt_file, 'w') as tf:
        for jpg_file in glob.glob(image_path + '*.png'):
            tf.write(jpg_file + '\n')
generate_train_and_val(path + 'train_images/', path + 'train.txt') # 生成的train.txt檔案所在路徑
# generate_train_and_val(path + 'val_images/', path + 'val.txt') # 生成的val.txt檔案所在路徑
           

根據代碼可以看出,我們需要在

daeknet安裝目錄下

建立一個

kitti_data檔案夾

,裡面需要建立

train_images檔案夾和val_images檔案夾

,此外我們還需要建立

train_labels和val_labels檔案夾

我們把VOC_KITTI/JPEGImages檔案夾裡面的圖檔剪切或複制到kitti_data/train_images下,把VOC_KITTI/Annotaations下的txt檔案剪切或複制到kitti_data/train_labels下

,可以執行指令:

# 剪切圖檔
mv VOC_KITTI/JPEGImages/* darknet/kitti_data/train_images
# 剪切标簽
mv VOC_KITTI/Annotaations/*.txt darknet/kitti_data/train_labels
           

接着在darknet路徑下打開終端,執行指令:python kitti_train_val.py

,最終在kitti_data目錄下生成train.txt和val.txt。

如下所示:

YOLOv3訓練KITTI資料集

這裡的

kitti.data和kitti.names即将建立。

5 準備資料的配置檔案

現在我們需要準備

kitti.data

kitti.names

和網絡模型配置檔案

yolov3-kitti.cfg

5-1 準備kitti.names

kitti.names存放了每一類的類名,這個将在測試一張圖檔時顯示一個物體的标簽名。檔案内容如下(注意順序要與xml_to_yolo_txt.py檔案裡面的順序一緻。):

Car
Pedestrian
Cyclist
           

5-2 準備kitti.data

kitti.data内容如下,其中classes表示類的數目,train和val表示第4步生成的train.txt和val.txt的存放路徑,backup表示訓練的yolo權重存放的位置。

classes= 3
train = kitti_data/train.txt
valid = kitti_data/val.txt
names = kitti_data/kitti.names
backup = backup/
           

5-3 準備yolov3-kitti.cfg

在darknet/cfg目錄下,建立一個檔案yolov3-kitti.cfg,裡面的内容可以先拷貝yolov3.cfg,在修改以下幾個部分:

  • 三處classes=80修改為classes=3
  • 三處filters=255(注意隻需要修改[yolo]上面的[convolutional]的filters)修改為filters=24( filters=3*(classes+5) ),如下:
[convolutional]
size=1
stride=1
pad=1
filters=255 # 此處需要修改為filters=24
activation=linear
[yolo]
mask = 3,4,5
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
           
注意,這裡的yolov3的filters的計算公式為 filters=3*(classes+5),yolov2的filter的計算公式為 filters=num*(classes+coords+1),yolov1的計算公式為 filters=sideside(num*5+classes)。
  • 還可以修改batch,subdivisions和max_batches等參數。其中batch表示一個批次訓練的圖檔數目,一個epoch=total_train_images/batch,而subdivisions表示将一個batch分為subdivisions個組進行分别訓練,每個組有batch/subdivisions個圖檔。max_batches表示最大的批次數,而iterations=max_batches/batch。

6 下載下傳ImageNet預訓練的網絡參數

yolov3預設的訓練權重為darknet53,我們可以在darknet路徑下打開終端,輸入指令下載下傳權重:

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

7 訓練模型

./darknet detector train kitti_data/kitti.data cfg/yolov3-kitti.cfg darknet53.conv.74
           

在訓練中,終端會列印輸出

Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.879770, Class: 0.999963, Obj: 0.999019, No Obj: 0.000455, .5R: 1.000000, .75R: 1.000000, count: 2
Region 106 Avg IOU: 0.726660, Class: 0.981278, Obj: 0.848324, No Obj: 0.000362, .5R: 0.933333, .75R: 0.600000, count: 15
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000001, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: 0.739621, Class: 0.999773, Obj: 0.788386, No Obj: 0.000512, .5R: 0.937500, .75R: 0.625000, count: 16
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: 0.842399, Class: 0.999971, Obj: 0.978920, No Obj: 0.000325, .5R: 1.000000, .75R: 1.000000, count: 11
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000012, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: 0.705575, Class: 0.949637, Obj: 0.893539, No Obj: 0.000549, .5R: 0.888889, .75R: 0.555556, count: 18
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000220, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.885422, Class: 0.999904, Obj: 0.996486, No Obj: 0.000944, .5R: 1.000000, .75R: 1.000000, count: 5
Region 106 Avg IOU: 0.685692, Class: 0.999810, Obj: 0.818832, No Obj: 0.000441, .5R: 0.866667, .75R: 0.400000, count: 15
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.917769, Class: 0.999986, Obj: 0.999057, No Obj: 0.000483, .5R: 1.000000, .75R: 1.000000, count: 2
Region 106 Avg IOU: 0.734660, Class: 0.999783, Obj: 0.806102, No Obj: 0.000853, .5R: 0.857143, .75R: 0.500000, count: 28
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000290, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.883034, Class: 0.999598, Obj: 0.952420, No Obj: 0.000755, .5R: 1.000000, .75R: 1.000000, count: 5
Region 106 Avg IOU: 0.763281, Class: 0.999828, Obj: 0.776274, No Obj: 0.000371, .5R: 0.857143, .75R: 0.642857, count: 14
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000002, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: 0.788140, Class: 0.999921, Obj: 0.805065, No Obj: 0.000677, .5R: 1.000000, .75R: 0.761905, count: 21
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.823024, Class: 0.999452, Obj: 0.746079, No Obj: 0.000631, .5R: 1.000000, .75R: 0.600000, count: 5
Region 106 Avg IOU: 0.734842, Class: 0.999643, Obj: 0.771043, No Obj: 0.000651, .5R: 0.950000, .75R: 0.500000, count: 20
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.832970, Class: 0.999085, Obj: 0.915872, No Obj: 0.000134, .5R: 1.000000, .75R: 1.000000, count: 1
Region 106 Avg IOU: 0.647650, Class: 0.999931, Obj: 0.914428, No Obj: 0.000316, .5R: 0.818182, .75R: 0.363636, count: 11
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 106 Avg IOU: 0.647275, Class: 0.998951, Obj: 0.835732, No Obj: 0.000277, .5R: 1.000000, .75R: 0.200000, count: 10
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.859042, Class: 0.999873, Obj: 0.920159, No Obj: 0.001146, .5R: 1.000000, .75R: 1.000000, count: 8
Region 106 Avg IOU: 0.760058, Class: 0.999575, Obj: 0.829456, No Obj: 0.000763, .5R: 0.950000, .75R: 0.650000, count: 20
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.780092, Class: 0.999876, Obj: 0.873641, No Obj: 0.000480, .5R: 1.000000, .75R: 0.500000, count: 2
Region 106 Avg IOU: 0.714661, Class: 0.998230, Obj: 0.930809, No Obj: 0.000893, .5R: 0.862069, .75R: 0.551724, count: 29
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.748140, Class: 0.999394, Obj: 0.698991, No Obj: 0.000304, .5R: 0.666667, .75R: 0.666667, count: 3
Region 106 Avg IOU: 0.661677, Class: 0.999870, Obj: 0.814655, No Obj: 0.000667, .5R: 0.818182, .75R: 0.318182, count: 22
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.839435, Class: 0.994511, Obj: 0.497955, No Obj: 0.000293, .5R: 1.000000, .75R: 1.000000, count: 2
Region 106 Avg IOU: 0.697895, Class: 0.999256, Obj: 0.669627, No Obj: 0.000654, .5R: 0.958333, .75R: 0.333333, count: 24
Region 82 Avg IOU: -nan, Class: -nan, Obj: -nan, No Obj: 0.000000, .5R: -nan, .75R: -nan, count: 0
Region 94 Avg IOU: 0.858090, Class: 0.999985, Obj: 0.999754, No Obj: 0.000153, .5R: 1.000000, .75R: 1.000000, count: 1
Region 106 Avg IOU: 0.664703, Class: 0.989184, Obj: 0.793579, No Obj: 0.000681, .5R: 0.791667, .75R: 0.458333, count: 24
13202: 0.830514, 0.765624 avg, 0.001000 rate, 4.867255 seconds, 844928 images
Loaded: 0.000038 seconds
           

8 測試

我們把batch設為64,經過13200iterations後,我們可以把KITTI的測試集中找一些圖檔放在darknet/data/目錄下,輸入以下指令在一張圖檔進行測試

./darknet detector test kitti_data/kitti.data cfg/yolov3-kitti.cfg backup/yolov3-kitti.backup data/000005.png
           
YOLOv3訓練KITTI資料集

可以看出,預測對象及其種類的精度還可以,但是定位誤差較大,還需要再訓練一段時間看看。

9 中斷訓練後繼續訓練

./darknet detector train kitti_data/kitti.data cfg/yolov3-kitti.cfg backup/yolov3-kitti.backup -gpus 0,1,2,3
           

繼續閱讀