Windos10 linux同樣過程
現有問題:
本文過程在linux下類似,可以正常通過。windons下,運行python腳本出現問題,無法正常輸出0001.txt(標定文件),所以只能使用linux生成標定文件和路徑文件txt,然后復制到windons下使用。
待解決問題:
重寫訓練部分代碼,使得windons程序直接讀取原始圖像和標記文件,其余文件自動生成,然后開始訓練、
1 准備數據
輸入原始數據--yolo2訓練----輸出權重文件
這里用100張做樣例:
原始數據
- 原始圖像0001.jpg - 0100.jpg 10張
- 描述圖像中目標信息的0001.txt - 0100.txt 10個
- 存儲每個訓練圖像絕對完整路徑的train.txt 1個
- 存儲每個測試圖像絕對完整路徑 var.txt 1個
問題1 無法直接得到 原始數據 2.描述圖像中目標信息的txt,采間接的辦法
- 使用標圖軟件得到描述圖像信息的0001.xml 每個圖象對應一張
- 0001.xml 通過python腳本轉換 0001.txt
問題2 無法直接得到3,4完整路徑,間接先獲取圖片名,然后加上路徑名
- 獲取 訓練圖像的名字到一個 train.txt
- 將這個txt轉換成完整路徑
- 獲取 測試圖像的名字到一個var.txt
- 將這個txt轉換成完整路徑
1.1 原始圖像
首先准備好自己的數據集,最好固定格式,此處為例,采用jpg格式的圖像,在名字上最好使用像VOC一樣類似000001.jpg、000002.jpg這樣
1.2 xml文件 描述圖像中要識別的目標信息
每一個xml對應一張圖像,並且每個xml中存放的是標記的各個目標的位置和類別信息,命名通常與對應的原始圖像一樣
因為做的是目標檢測,所以接下來需要標記原始圖像中的目標區域。相關方法和工具有很多,這里需用labelImg,相關用法也有說明,基本就是框住目標區域然后雙擊類別,標記完整張圖像后點擊保存即可。操作界面如下:
通常save之后會將標記的信息保存在xml文件,其名字通常與對應的原始圖像一樣。
其中每個xml文件是這樣的畫風
<?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>
1.3 xml-txt文件 描述圖像中要識別的目標信息
生成的xml文件不是yolo2讀取的格式,所以需要轉換為和圖像名稱一樣的txt。
轉換后的txt格式:<object-class> <x> <y> <width> <height>
- 0 15 4 25 25 目標為第一類 起始位置 (15, 4) 長 25 寬 25
- 0 11 36 25 25 目標為第一類 起始位置 (11, 36) 長 25 寬 25
-
該部分由1.4中的creat_list.py腳本自動完成。
1.4獲取圖像的路徑,以便YOLO程序能夠找到圖片
圖像分為兩類,一類充當測試數據 剩下的充當訓練數據。所以需要分別建立txt保存他們的路徑地址。這里使用python腳本來自動生成路徑。
生成過程分兩步,首先獲取圖像的名字
1.4.1 圖片名 train.txt 只存每個訓練的圖象的名字
1.4.2 圖片名 val.txt 只存每個驗證的圖像的名字
在生成infrared_train.txt與infrared_val.txt這兩個文件時,會分別用到這兩個文檔。文檔里包含了用於訓練/驗證的圖片的名稱,里面的數據組成很簡單,每行都是一個圖片的名稱,並不包含圖片的后綴(.jpg),比如文檔中:
- 第一行是: 0000
- 第二行是: 0001
該部分工作由生成腳本:creat_list.py:完成
自己需要改三個路徑:
- 圖片原始地址
- 生成用於保存訓練集圖像名字train.txt的路徑
- 生成用於保存測試集圖像名字val.txt的路徑
#這個小腳本是用來打開圖片文件所在文件夾,把前900個用於訓練的圖片的名稱保存在tain.txt,后103個用於驗證的圖片保存在val.txt import os from os import listdir, getcwd from os.path import join if __name__ == '__main__': source_folder='/home/yolo_v2_tinydarknet/darknet/infrared/image/dout/'#地址是所有圖片的保存地點 dest='/home/yolo_v2_tinydarknet/darknet/infrared/train.txt' #保存train.txt的地址 dest2='/home/yolo_v2_tinydarknet/darknet/infrared/val.txt' #保存val.txt的地址 file_list=os.listdir(source_folder) #賦值圖片所在文件夾的文件列表 train_file=open(dest,'a') #打開文件 val_file=open(dest2,'a') #打開文件 for file_obj in file_list: #訪問文件列表中的每一個文件 file_path=os.path.join(source_folder,file_obj) #file_path保存每一個文件的完整路徑 file_name,file_extend=os.path.splitext(file_obj) #file_name 保存文件的名字,file_extend保存文件擴展名 file_num=int(file_name) #把每一個文件命str轉換為 數字 int型 每一文件名字都是由四位數字組成的 如 0201 代表 201 高位補零 if(file_num<900): #保留900個文件用於訓練 #print file_num train_file.write(file_name+'\n') #用於訓練前900個的圖片路徑保存在train.txt里面,結尾加回車換行 else : val_file.write(file_name+'\n') #其余的文件保存在val.txt里面 train_file.close()#關閉文件 val_file.close()
然后通過得到的名字生成完整路徑
3.3 infrared_train.txt 存放訓練的圖像的地址
3.4 infrared_val.txt 存放測試的圖像的地址
文檔里包含了所有用於訓練/驗證的圖片的完整路徑,每一行都是一個圖片的完整路徑,例如
第一行是: /home/yolo_v2_tinydarknet/darknet/infrared/image/dout/0000.jpg
第二行是 :/home/yolo_v2_tinydarknet/darknet/infrared/image/dout/0001.jpg
該部分由以下腳本完成
需要分兩次執行腳本。兩次不一樣的地方標紅
第一次 生成訓練集的完整路徑
- 1修改圖像生成的xml文件所在地
- in_file = open('/home/yolo_v2_tinydarknet/darknet/infrared/labels/dout_original/%s.xml'%(image_id))#與圖片對應的xml文件所在的地址
- 2修改轉化后生成的txt的保存路徑
- out_file = open('/home/yolo_v2_tinydarknet/darknet/infrared/labels/%s.txt'%(image_id),'w') #與此xml對應的轉換后的txt,這個txt的保存完整路徑
- 3打開訓練集的兩行代碼注釋,並將測試集的兩行代碼注釋加上
- image_ids = open('/home/yolo_v2_tinydarknet/darknet/infrared/train.txt').read().strip().split() #如果是訓練集數據打開這一行,注釋下一行
- list_file = open('infrared_train.txt', 'w') #把結果寫入到indrared_train.txt文件中,如果是訓練集數據打開這一行,注釋下一行
- 4修改圖片路徑
- list_file.write('/home/yolo_v2_tinydarknet/darknet/infrared/image/dout/%s.jpg\n'%(image_id)) #把每一用於訓練或驗證的圖片的完整的路徑寫入到infrared_train.txt中 這個文件會被voc.data yolo.c調用
第二次 生成測試集的完整路徑
- 1修改圖像生成的xml文件所在地
- in_file = open('/home/yolo_v2_tinydarknet/darknet/infrared/labels/dout_original/%s.xml'%(image_id))#與圖片對應的xml文件所在的地址
- 2修改轉化后生成的txt的保存路徑
- out_file = open('/home/yolo_v2_tinydarknet/darknet/infrared/labels/%s.txt'%(image_id),'w') #與此xml對應的轉換后的txt,這個txt的保存完整路徑
- 3打開測試集的兩行代碼注釋,並將訓練集的兩行代碼注釋加上
- image_ids = open('/home/yolo_v2_tinydarknet/darknet/infrared/train.txt').read().strip().split() #如果是測試數據打開這一行,注釋下一行
- list_file = open('infrared_train.txt', 'w') #把結果寫入到indrared_train.txt文件中,如果測試集數據打開這一行,注釋下一行
- 4修改圖片路徑
- list_file.write('/home/yolo_v2_tinydarknet/darknet/infrared/image/dout/%s.jpg\n'%(image_id)) #把每一用於訓練或驗證的圖片的完整的路徑寫入到infrared_train.txt中 這個文件會被voc.data yolo.c調用
#此腳本修改自voc_label.py。修改的原因是:我的訓練集跟voc有些不同。 #由於數據集中包括用於訓練的數據和用於驗證的數據,所以此腳本可能需要分別對這兩種數據各運行一次,對兩種數據只需要簡單地注釋掉相應語句即可 #這個腳本需要train.txt ,這個文件是我用腳本creat_list.py生成的,保存了用於訓練的圖片的名字id,保存了用於訓練的圖片的名字 #這個腳本需要val.txt文件,這個文件是我用腳本creat_list.py生成的,保存了用於驗證的圖片的名字id,保存了用於驗證的圖片的名字 #這個腳本還需要xml格式的標簽文件,我的訓練集xml文件的格式與voc2007的類似,xml文件的名稱與對應的用於訓練的圖片的名稱相同 #這個腳本會生成 indrared_train.txt文件 ,用於保存每一用於訓練的圖片的完整的路徑,隨后會被voc.data yolo.c使用 #這個腳本會生成 indrared_val.txt文件 ,用於保存每一用於驗證的圖片的完整的路徑,隨后會被voc.data yolo.c使用 #這個腳本還會生成 txt格式的yolo可識別的標簽文件,轉換自每一個用於訓練或驗證的圖片對應的xml文件,txt格式的文件名稱與對應的xml文件名相同,但是內容不同,擴展名不同 #這個腳本 需要與圖片對應的xml文件所在的地址,需要,轉換后生成的txt的完整保存路徑 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')] #按照自己的文件格式改的,不需要判斷是那個voc數據包 classes = ["n00000001"]#因為我的數據集只有一個類別 def convert(size, box):#voc_label.py 自帶的函數,沒有修改 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(image_id): #in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id)) in_file = open('/home/yolo_v2_tinydarknet/darknet/infrared/labels/dout_original/%s.xml'%(image_id))#與圖片對應的xml文件所在的地址 out_file = open('/home/yolo_v2_tinydarknet/darknet/infrared/labels/%s.txt'%(image_id),'w') #與此xml對應的轉換后的txt,這個txt的保存完整路徑 tree=ET.parse(in_file) root = tree.getroot() size = root.find('size') #訪問size標簽的數據 w = int(size.find('width').text)#讀取size標簽中寬度的數據 h = int(size.find('height').text)#讀取size標簽中高度的數據 for obj in root.iter('object'): # difficult = obj.find('difficult').text #由於自己的文件里面沒有diffcult這一個標簽,所以就屏蔽之 cls = obj.find('name').text if cls not in classes :#or int(difficult) == 1: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') #訪問boundbox標簽的數據並進行處理,都按yolo自帶的代碼來,沒有改動 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') #image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split() #之前代碼是按照sets里面的字符來訪問保存有圖片名字的train或者val的txt文件 image_ids = open('/home/yolo_v2_tinydarknet/darknet/infrared/train.txt').read().strip().split() #如果是訓練集數據打開這一行,注釋下一行 #image_ids = open('/home/yolo_v2_tinydarknet/darknet/infrared/val.txt').read().strip().split() #如果是驗證數據集數據打開這一行,注釋上一行 #list_file = open('%s_%s.txt'%(year, image_set), 'w') list_file = open('infrared_train.txt', 'w') #把結果寫入到indrared_train.txt文件中,如果是訓練集數據打開這一行,注釋下一行 #list_file = open('infrared_val.txt', 'w') #把結果寫入到indrared_train.txt文件中,如果是驗證數據集數據打開這一行,注釋上一行 for image_id in image_ids: #list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id)) list_file.write('/home/yolo_v2_tinydarknet/darknet/infrared/image/dout/%s.jpg\n'%(image_id)) #把每一用於訓練或驗證的圖片的完整的路徑寫入到infrared_train.txt中 這個文件會被voc.data yolo.c調用 convert_annotation(image_id) #把圖片的名稱id傳給函數,用於把此圖片對應的xml中的數據轉換成yolo要求的txt格式 list_file.close() #關閉文件
原始數據結果
經過以上步驟,我們現在得到以下數據:
- 原始照片 100張 jpg
- 和原始照片名一樣的,用於保存圖像中要識別的目標信息的txt 100個 txt
- 用於參加訓練的圖片的路徑地址 infrared_train.txt 一個 txt
- 用於參加訓練的圖片的路徑地址 infrared_val.txt 一個 txt
2 修改工程配置文件
在工程生成的x64/data路徑下
2.1新建 yolo-obj.cfg 文件,內容復制yolo-voc.2.0.cfg文件(x64路徑下),但是修改:
- 第一行
- change line batch to
batch=64
- 第二行
- change line subdivisions to
subdivisions=8
-
- 修改region層 classes 自己訓練的類 這里是1類 只有car
- 修改region上一層convolutional層中 filters= (classes+5)*5 這里 classes為一類 filters= (1+5)*5=30
2.2 創建obj.names 在文件夾build\darknet\x64\data\里,里面是你所分的類 這里為一類 car
如果很多類,例如voc.data 分了很多類數據
2.3 創建obj.data在 build\darknet\x64\data\路徑下
- 我們只有一類car 所以classes=1
- train= 數據准備生成的 保存訓練圖片完整路徑的 train.txt
- valid=數據准備生成的 保存測試圖片完整路徑的 var.txt
- backup = backup 存放的是 生成的權重文件路徑 默認x64/backp下
2.4 將原始圖片集存在build\darknet\x64\data\obj\
下
2.4 將數據處理得到的描述每個圖像中 目標信息的txt文件 也放在build\darknet\x64\data\obj\ 下
每個txt內容
3 開始訓練
3.1 訓練需要用到一個文件
下載 pre-trained weights for the convolutional layers (76 MB):
http://pjreddie.com/media/files/darknet19_448.conv.23
放在build\darknet\x64下
3.2 運行訓練命令 命令行進入build\darknet\x64下
darknet.exe detector train data/obj.data data/yolo-obj.cfg darknet19_448.conv.23
3.3生成結果在 build\darknet\x64\backup
每次迭代100次,保存下,取最后一個