全网最详细的Yolov3训练Caltech Pedestrain数据集并绘制fppi miss rate图
目錄
- 1.環境要求
- 2.caltech數據集做成VOC格式
- 2.1官網下載數據集
- 2.2將seq格式文件轉換為jpg圖片
- 2.3將vbb格式轉換為xml格式
- 2.4將jpg圖片放在一個統一的文件夾下和xml文件對應
- 2.5重命名圖片和xml文件
- 2.6替換標簽
- 2.7對xml文件和jpg圖片采樣(可選)
- 2.8重命名xml文件和jpg圖片(可選)
- 2.9生成txt文件指定訓練集、驗證集、數據集、訓練驗證集
- 3.使用yolov3訓練caltech數據集
- 3.1生成訓練所需要的的2007_test.txt、2007_train.txt、2007_val.txt、train.all.txt、train.txt以及lables文件夾
- 3.2創建yolov3-caltech.cfg文件
- 3.3創建caltech.names文件
- 3.4創建caltech.data文件
- 3.5開始訓練
- 4繪制fppi miss rate圖
- 4.1生成detectionResults內容
- 4.2生成trainingData內容
- 4.3繪制fppi miss rate曲線
- 5.補充
1.環境要求
1.python3,網上很多代碼是python2版本的,我大概修改了一下,所有代碼都只適用于python3版本。
2.matlab ,這是因為matlab提供了這個畫圖的函數,所以畫圖必須要用到matlab。
3.AlexeyAB 版本的yolo,本文基于的是這個版本。因為圖片數量巨大,在ubuntu打開對應文件夾時會很卡,所以將caltech轉換為yolo格式時我是在windows下處理的。訓練的時候我是在ubuntu訓練的(因為環境配置簡單很多)。但是代碼對系統沒有要求,哪個系統都可以。
4.路徑如果跟我設置成一樣的基本不用修改代碼,否則在代碼中修改成自己的路徑即可。
2.caltech數據集做成VOC格式
? 這一部分內容參考自github上大神寫的caltech2VOC代碼。
2.1官網下載數據集
? 在數據集官網上將所有文件下載下來,建議搭梯子,會快很多。其中annotations是標注文件,下載下來格式為vbb,后續轉換為xml格式;set00-set10包含是圖像,格式是seq,后續轉換為jpg。
2.2將seq格式文件轉換為jpg圖片
? 調用seq2jpg_py3.py文件進行轉換,只需在代碼41行和42行修改輸入輸出路徑即可。
#coding=utf-8 # Deal with .seq format for video sequence # Author: Kaij # The .seq file is combined with images, # so I split the file into several images with the image prefix # "\xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46".import os.path import fnmatch import shutildef open_save(file,savepath):# read .seq file, and save the images into the savepathf = open(file,'rb+')string = f.read().decode('latin-1')splitstring = "\xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46"# split .seq file into segment with the image prefixstrlist=string.split(splitstring)f.close()count = 0# delete the image folder path if it existsif os.path.exists(savepath):shutil.rmtree(savepath)# create the image folder pathif not os.path.exists(savepath):os.makedirs(savepath)# deal with file segment, every segment is an image except the first onefor img in strlist:filename = str(count)+'.jpg'filenamewithpath=os.path.join(savepath, filename)# abandon the first one, which is filled with .seq headerif count > 0:i=open(filenamewithpath,'wb+')i.write(splitstring.encode('latin-1'))i.write(img.encode('latin-1'))i.close()count += 1if __name__=="__main__":rootdir = "D:\data set\caltech"saveroot = "D:/data set/caltech/VOCdevkit/JPEG"# walk in the rootdir, take down the .seq filename and filepathfor parent, dirnames, filenames in os.walk(rootdir):for filename in filenames:# check .seq file with suffixif fnmatch.fnmatch(filename,'*.seq'):# take down the filename with path of .seq filethefilename = os.path.join(parent, filename)# create the image folder by combining .seq file path with .seq filenamethesavepath = saveroot +'\\'+ parent.split('\\')[-1] + '\\' + filename.split('.')[0]+'\\'print ("Filename=" + thefilename)print ("Savepath=" + thesavepath)open_save(thefilename,thesavepath)輸出目錄如下圖所示:
2.3將vbb格式轉換為xml格式
? 調用vbb2voc.py文件,只需修改代碼161和162行的輸入和輸出目錄即可。
#-*- coding:utf-8 -*- import os, glob import cv2 from scipy.io import loadmat from collections import defaultdict import numpy as np from lxml import etree, objectifydef vbb_anno2dict(vbb_file, cam_id):#通過os.path.basename獲得路徑的最后部分“文件名.擴展名”#通過os.path.splitext獲得文件名filename = os.path.splitext(os.path.basename(vbb_file))[0]#定義字典對象annosannos = defaultdict(dict)vbb = loadmat(vbb_file)# object info in each frame: id, pos, occlusion, lock, posvobjLists = vbb['A'][0][0][1][0]objLbl = [str(v[0]) for v in vbb['A'][0][0][4][0]] #可查看所有類別 # person indexperson_index_list = np.where(np.array(objLbl) == "person")[0] #只選取類別為‘person’的xmlfor frame_id, obj in enumerate(objLists):if len(obj) > 0:frame_name = str(cam_id) + "_" + str(filename) + "_" + str(frame_id+1) + ".jpg"annos[frame_name] = defaultdict(list)annos[frame_name]["id"] = frame_nameannos[frame_name]["label"] = "person"for id, pos, occl in zip(obj['id'][0], obj['pos'][0], obj['occl'][0]):id = int(id[0][0]) - 1 # for matlab start from 1 not 0if not id in person_index_list: # only use bbox whose label is personcontinuepos = pos[0].tolist()occl = int(occl[0][0])annos[frame_name]["occlusion"].append(occl)annos[frame_name]["bbox"].append(pos)if not annos[frame_name]["bbox"]:del annos[frame_name]print (annos)return annosdef seq2img(annos, seq_file, outdir, cam_id):cap = cv2.VideoCapture(seq_file)index = 1# captured frame listv_id = os.path.splitext(os.path.basename(seq_file))[0]cap_frames_index = np.sort([int(os.path.splitext(id)[0].split("_")[2]) for id in annos.keys()])while True:ret, frame = cap.read()print (ret)if ret:if not index in cap_frames_index:index += 1continueif not os.path.exists(outdir):os.makedirs(outdir)outname = os.path.join(outdir, str(cam_id)+"_"+v_id+"_"+str(index)+".jpg")print ("Current frame: ", v_id, str(index))cv2.imwrite(outname, frame)height, width, _ = frame.shapeelse:breakindex += 1img_size = (width, height)return img_sizedef instance2xml_base(anno, bbox_type='xyxy'):"""bbox_type: xyxy (xmin, ymin, xmax, ymax); xywh (xmin, ymin, width, height)"""assert bbox_type in ['xyxy', 'xywh']E = objectify.ElementMaker(annotate=False)anno_tree = E.annotation(E.folder('VOC2014_instance/person'),E.filename(anno['id']),E.source(E.database('Caltech pedestrian'),E.annotation('Caltech pedestrian'),E.image('Caltech pedestrian'),E.url('None')),E.size(E.width(640),E.height(480),E.depth(3)),E.segmented(0),)for index, bbox in enumerate(anno['bbox']):bbox = [float(x) for x in bbox]if bbox_type == 'xyxy':xmin, ymin, w, h = bboxxmax = xmin+wymax = ymin+helse:xmin, ymin, xmax, ymax = bboxE = objectify.ElementMaker(annotate=False)anno_tree.append(E.object(E.name(anno['label']),E.bndbox(E.xmin(xmin),E.ymin(ymin),E.xmax(xmax),E.ymax(ymax)),E.difficult(0),E.occlusion(anno["occlusion"][index])))return anno_treedef parse_anno_file(vbb_inputdir,vbb_outputdir):# annotation sub-directories in hda annotation input directoryassert os.path.exists(vbb_inputdir)sub_dirs = os.listdir(vbb_inputdir) #對應set00,set01...for sub_dir in sub_dirs:print ("Parsing annotations of camera: ", sub_dir)cam_id = sub_dir#獲取某一個子set下面的所有vbb文件vbb_files = glob.glob(os.path.join(vbb_inputdir, sub_dir, "*.vbb")) for vbb_file in vbb_files:#返回一個vbb文件中所有的幀的標注結果annos = vbb_anno2dict(vbb_file, cam_id)if annos:#組成xml文件的存儲文件夾,形如“/Users/chenguanghao/Desktop/Caltech/xmlresult/”vbb_outdir = vbb_outputdir#如果不存在if not os.path.exists(vbb_outdir):os.makedirs(vbb_outdir)for filename, anno in sorted(annos.items(), key=lambda x: x[0]): if "bbox" in anno:anno_tree = instance2xml_base(anno)outfile = os.path.join(vbb_outdir, os.path.splitext(filename)[0]+".xml")print ("Generating annotation xml file of picture: ", filename)#生成最終的xml文件,對應一張圖片etree.ElementTree(anno_tree).write(outfile, pretty_print=True) def visualize_bbox(xml_file, img_file):import cv2tree = etree.parse(xml_file)# load imageimage = cv2.imread(img_file)origin = cv2.imread(img_file)# 獲取一張圖片的所有bboxfor bbox in tree.xpath('//bndbox'):coord = []for corner in bbox.getchildren():coord.append(int(float(corner.text)))print (coord)cv2.rectangle(image, (coord[0], coord[1]), (coord[2], coord[3]), (0, 0, 255), 2)# visualize imagecv2.imshow("test", image)cv2.imshow('origin',origin)cv2.waitKey(0)def main():vbb_inputdir = "D:/data set/caltech/annotations"vbb_outputdir = "D:/data set/caltech/VOCdevkit/annotations"parse_anno_file(vbb_inputdir,vbb_outputdir)if __name__ == "__main__":main()? 輸出目錄在VOCdevkit/annotations下,總共生成122187個xml文件,且全部放在了一起。第一個文件名是 set00_V000_69.xml,這是因為并不是每一張圖片都有行人,只有含有行人的圖片才被標注。這里細心的小伙伴可能發現在set00_V000_69.jpg圖片里找不到人,其實是因為caltech數據集行人太小,分辨率過低的緣故。圖中人在天橋上特別小,可以用opencv畫矩形框在圖上就可以發現。這里給出一個在圖片上批量標注矩形框的代碼。此處需要安裝opencv。代碼主要修改第11行的輸入圖片路徑、12行的輸入xml路徑、28行的輸出圖片路徑和30行的輸入路徑。運行完可以查看一下標注圖片,發現確實有人。
#coding=tuf-8 import xml.etree.ElementTree as ET import pickle import os from os import listdir, getcwd from os.path import join import cv2def convert_annotation(setxxx,vxxx,xxxjpg):img = cv2.imread("D:\data set\caltech\VOCdevkit\JPEG\set%s\V%s\%s.jpg"%(setxxx,vxxx,xxxjpg))in_file = open('D:\data set\caltech\VOCdevkit\\annotations\set%s_V%s_%s.xml'%(setxxx,vxxx,xxxjpg))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').textif int(difficult)==1:continuexmlbox = obj.find('bndbox')b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))print(b[0],b[1],b[2],b[3])colors = (0,0,255)cv2.rectangle(img,(int(b[0]),int(b[2])),(int(b[1]),int(b[3])),colors)cv2.imwrite('D:\data set\caltech\VOCdevkit\JPEGS\%s.jpg'%(xxxjpg), img) if __name__=="__main__": rootdir = "D:\data set\caltech\VOCdevkit\\annotations"for parent, dirnames, filenames in os.walk(rootdir):for name in filenames:temp = name.split(".",1)[0].split("_",2)setxx = temp[0][3:5]Vxxx = temp[1][1:4]xxxjpg = temp[2]convert_annotation(setxx,Vxxx,xxxjpg)2.4將jpg圖片放在一個統一的文件夾下和xml文件對應
? 2.3步驟中xml文件已經放在了一個文件夾下,而圖片還放在不同目錄下,沒有和xml標注文件相對應。因此調用mergejpg.py文件,將所有圖片放在一起,只需修改代碼第6和7行的輸入輸出路徑即可。
#-*- coding:utf-8 -*- import os import glob import shutil if __name__ == "__main__":imgpathin = 'D:\data set\caltech\VOCdevkit\JPEG'imgout = 'D:\data set\caltech\VOCdevkit\JPEG'for subdir in os.listdir(imgpathin):print(subdir)file_path = os.path.join(imgpathin, subdir)for subdir1 in os.listdir(file_path):print(subdir1)file_path1 = os.path.join(file_path, subdir1)print(file_path1)for jpg_file in os.listdir(file_path1):src = os.path.join(file_path1, jpg_file)new_name=str(subdir+"_"+subdir1+"_"+jpg_file)print(new_name)dst=os.path.join(imgout,new_name)os.rename(src,dst)? 我這里輸入輸出同目錄,生成的圖片放在了一起。刪除不需要的set00-set10文件夾,剩下的名為set0_V000_1.jpg格式圖片,總共有249884張。如2.3節所述,圖片數量比xml文件要多很多。
2.5重命名圖片和xml文件
? 按照“xxxxxx”6位數字格式給圖片和xml文件命名方便后續操作。標注有人的圖片命名為xxxxxx.jpg和命名為xxxxxx.xml的xml文件相對應,多余的圖片保持原名。調用renameindex.py文件,只需要修改代碼的第3和4行的輸入和輸出目錄即可。
#-*- coding:utf-8 -*- import os xmlpath = 'D:/data set/caltech/VOCdevkit/annotations' imgpath = 'D:/data set/caltech/VOCdevkit/JPEG' index = 0 count = 0 emptyset = set() xmlFiles = os.listdir(xmlpath) imgFiles = os .listdir(imgpath) print (len(xmlFiles),len(imgFiles))for xml in xmlFiles: xmlname = os.path.splitext(xml)[0]imgname = os.path.join(imgpath,xmlname+'.jpg') print(imgname)if os.path.exists(imgname):newName = str(index).zfill(6)#重命名圖像os.rename(imgname,os.path.join(imgpath,newName+'.jpg'))#重命名xml文件os.rename(os.path.join(xmlpath,xml),os.path.join(xmlpath,newName+'.xml'))print ('============================================')print ('img',imgname,os.path.join(imgpath,newName+'.jpg'))print ('__________________________________________')print ('xml',os.path.join(xmlpath,xml),os.path.join(xmlpath,newName+'.xml'))print ('============================================')index = index + 1else:count += 1emptyset.add(xmlname.split('_')[0]+'_'+xmlname.split('_')[1]) sortedSet = sorted(emptyset,key= lambda x:(x.split('_')[0],x.split('_')[1])) for i in sortedSet:print (i) print (count)? 調用完畢在annotations下生成名為xxxxxx.xml格式的標注文件,JPEG下生成xxxxxx.jpg圖片,將多余的沒有重命名圖片刪除。可以右鍵屬性查看到標注文件和圖片分別有122187個,且各自對應。
2.6替換標簽
? 這里網上說是caltech標注中行人還會細劃分為幾類不同的行人,但我好像沒發現,如果有大佬知道歡迎留言,這里為了以防萬一還是運行此文件。運行findPeople.py將不同類person轉換為同類person。代碼只需修改第6和7行的輸入和輸出路徑即可。
# -*- coding:utf-8 -*- import os import reif __name__ == "__main__":xmlin = 'D:\data set\caltech\VOCdevkit\\annotations'xmlout = 'D:\data set\caltech\VOCdevkit\\annotations'files = os.listdir(xmlin)#編譯一個patternpattern = re.compile('people')#每張圖片進行判斷for file in files:f = open(os.path.join(xmlin,file), 'r')content = f.read()f.close()result = re.search('people', content)if (result!=None):updateFile = pattern.sub('person', content)else:updateFile = contentwith open(os.path.join(xmlout,file), 'w') as fout:fout.write(updateFile)print ('updating file {}'.format(file))2.7對xml文件和jpg圖片采樣(可選)
? 因為生成圖片過多,我不想訓練這么多的內容,選擇每8幀對圖片和標注文件采樣,減少訓練內容。調用delete_file.py采樣,仍然保持標注文件和圖片對應。代碼只需修改第22行輸入目錄和第14行的采樣頻率即可。這里分兩步,第一步先對圖片采樣,然后將圖片的路徑改為xml文件的路徑再對xml文件采樣。
#!/usr/bin/python # -*- coding: UTF-8 -*- import os import shutil def get_file_path(root_path, file_list):dir_or_files = os.listdir(root_path)for dir_file in dir_or_files:dir_file_path = os.path.join(root_path, dir_file)file_list.append(dir_file_path)def delete_file(file_list, length):count = 0for file_name in file_list:if (count % 8 == 0): #數字自行修改count = count + 1continueelse:os.remove(file_name)count = count + 1if __name__ == "__main__":root_path = r"D:\data set\caltech\VOCdevkit\JPEG" #先對圖片采樣,之后更換路勁對標注文件采樣。file_list = []get_file_path(root_path, file_list)length = len(file_list)delete_file(file_list, length)? 采樣后xml和jpg分別有15274張。
2.8重命名xml文件和jpg圖片(可選)
? 2.7步驟采樣后刪除很多文件,需要重新排列命名。調用rename_after_cut.py文件重命名,代碼只需修改第3、4行路徑即可。
#-*- coding:utf-8 -*- import os xmlpath = 'D:/data set/caltech/VOCdevkit/annotations' imgpath = 'D:/data set/caltech/VOCdevkit/JPEG' index = 0 count = 0 emptyset = set() xmlFiles = os.listdir(xmlpath) imgFiles = os .listdir(imgpath) print (len(xmlFiles),len(imgFiles))for xml in xmlFiles: xmlname = os.path.splitext(xml)[0]imgname = os.path.join(imgpath,xmlname+'.jpg') print(imgname)if os.path.exists(imgname):newName = str(index).zfill(6)#重命名圖像os.rename(imgname,os.path.join(imgpath,newName+'.jpg'))#重命名xml文件os.rename(os.path.join(xmlpath,xml),os.path.join(xmlpath,newName+'.xml'))print ('img',imgname,os.path.join(imgpath,newName+'.jpg'))print ('xml',os.path.join(xmlpath,xml),os.path.join(xmlpath,newName+'.xml'))index = index + 1else:count += 1emptyset.add(xmlname.split('_')[0]+'_'+xmlname.split('_')[1]) sortedSet = sorted(emptyset,key= lambda x:(x.split('_')[0],x.split('_')[1])) for i in sortedSet:print (i) print (count)2.9生成txt文件指定訓練集、驗證集、數據集、訓練驗證集
? 調用generate_txt.py生成trainval.txt、test.txt、train.txt、val.txt四個文件,這些文件內容只包含圖像名字的數字索引,也就是xxxxxx。因為要轉換為標準VOC數據集格式,這幾個文件最后放入VOC2007/ImageSets/Main文件夾中。代碼只需要修改第6和7行的目錄,第11行和12行的訓練集測試集百分比即可。這里我選取測試集占比28%,因此trainval.txt有10997行,test.txt有4277行,加起來正好15274行。每次選取圖片均是隨機選取,所以后面的文件格式跟我相同即可,內容是不一樣的。
#coding=utf-8 import os import random import timexmlfilepath='D:/data set/caltech/VOCdevkit/annotations' saveBasePath='D:/data set/caltech/VOCdevkit/txt' if not os.path.exists(saveBasePath):os.makedirs(saveBasePath) #設置訓練集和測試集的百分比,訓練驗證集72%,測試集28%,訓練集和驗證集在對半分。 trainval_percent=0.72 train_percent=0.5 total_xml = os.listdir(xmlfilepath)#所有的num = len(total_xml) #xml文件的數量 index_list = range(num) #生成一個index列表 trainval_num = int(num*trainval_percent) train_num = int(trainval_num*train_percent) trainval_index = random.sample(index_list,trainval_num)#從index_list中隨機采樣trainval_num數量的內容(正好一半) train_index = random.sample(trainval_index,train_num)#再次取一半print("train and val size", trainval_num) print("train size", train_num)ftrainval = open(os.path.join(saveBasePath,'trainval.txt'), 'w') ftest = open(os.path.join(saveBasePath,'test.txt'), 'w') ftrain = open(os.path.join(saveBasePath,'train.txt'), 'w') fval = open(os.path.join(saveBasePath,'val.txt'), 'w')# Start time start = time.time() for i in index_list:name = os.path.splitext(total_xml[i])[0] + '\n' if i in trainval_index:ftrainval.write(name)if i in train_index:ftrain.write(name)else:fval.write(name)else:ftest.write(name) # End time end = time.time() seconds = end - start print( "Time taken : {0} seconds".format(seconds)) ftrainval.close() ftrain.close() fval.close() ftest .close()3.使用yolov3訓練caltech數據集
? 這里參考自官方內容。
? 這里前期的準備工作已經完成,我們把annotations文件夾和JPEG文件夾以及txt文件夾里面的內容按照VOC數據集的格式存放。這里我為了編譯方便用的Ubuntu系統,路徑是ubuntu風格,用windows也一樣。Alexey版本的yolo下,在data目錄里建立VOC文件夾,VOC文件夾里建立VOCdevkit文件夾,VOCdevkit文件夾里建立VOC2007文件夾,VOC2007文件夾下建立Annotations、ImageSets、JPEGImages三個文件夾,其中**Annotations將所有的xml文件放入,JPETImages將所有圖片放入,ImageSets文件夾下建立Main文件夾并放入2.9步驟生成的4個txt文件。**注意這里目錄名字一定要打對,仔細核對。
3.1生成訓練所需要的的2007_test.txt、2007_train.txt、2007_val.txt、train.all.txt、train.txt以及lables文件夾
? 將voc_lable.py文件放在和VOCdevkit同級目錄下,調用voc_lable.py在同級目錄下生成2007_test.txt、2007_train.txt、2007_val.txt、train.all.txt、train.txt5個文件,里面內容是圖片的路徑。VOC2007文件夾下生成lables文件夾,里面是一堆txt文件,每個文件表示對應圖片里歸一化的xywh值。代碼修改自VOC數據集的voc_lable.py文件,代碼路徑不建議修改,只是文件一定放在VOCdevkit同級目錄下。
import xml.etree.ElementTree as ET import pickle import os from os import listdir, getcwd from os.path import joinsets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')] classes = ["person"]def convert(size, box):dw = 1./(size[0])dh = 1./(size[1])x = (box[0] + box[1])/2.0 - 1y = (box[2] + box[3])/2.0 - 1w = box[1] - box[0]h = box[3] - box[2]x = x*dww = w*dwy = y*dhh = h*dhreturn (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').textcls = obj.find('name').textif cls not in classes or int(difficult)==1:continuecls_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()os.system("cat 2007_train.txt 2007_val.txt > train.txt") os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt > train.all.txt")3.2創建yolov3-caltech.cfg文件
? 創建與yolov3-voc.cfg類似的yolov3-caltech.cfg,放入cfg文件夾下:
- 設置training下的batch=64,subdivisions=8.(視電腦配置決定)
- max_batches設置30000。(自己決定)
- steps參數為80%和90%max_batches,這里是steps=24000,27000.
- 網絡輸入長寬必須能整除32,這里是416x416.
- 3個yolo層的classes類別數改為1.
- 3個yolo層前一個卷積層的filter數為18.(公式是filters=(classes+5)×3)
3.3創建caltech.names文件
? 創建類似voc.names的caltech.names放入data文件夾下,文件里只有person。
3.4創建caltech.data文件
? 創建類似voc.data的caltech.data放入cfg文件夾下,修改為自己對應的路徑
classes= 1 # 你的類別的個數 train =/home/davy/下載/yolo_each_version/alexey_simple/data/VOC/train.txt # 存儲用于訓練的圖片位置 valid =/home/davy/下載/yolo_each_version/alexey_simple/data/VOC/2007_test.txt# 存儲用于測試的圖片的位置 names = data/caltech.names # 每行一個類別的名稱 backup = backup/3.5開始訓練
- ./darknet detector train cfg/caltech.data cfg/yolov3-caltech.cfg darknet53.conv.74 -map實時顯示map
- ./darknet detector train cfg/caltech.data cfg/yolov3-caltech.cfg darknet53.conv.74 2>1 | tee visualization/train_yolov3.log可視化中間參數并保存終端內容到visualization/train_yolov3.log文件中
4繪制fppi miss rate圖
? 這里參考了大佬的內容。
? 這里運用的是matlab下的[am,fppi,missRate] = evaluateDetectionMissRate(detectionResults,trainingData)函數。detectionResults是自己算法的預測結果,類型如下圖所示:
Boxes代表坐標信息(左上角xy、w、h),維度很高是因為一張圖中有多個預測框,Src是對應的置信度。
trainingData是真實的gt結果,類型如下圖所示:
Boxes代表真實坐標信息,name是圖片名稱。
4.1生成detectionResults內容
? 訓練完畢后需要在主目錄下創建results文件夾,在源碼中的detector.c文件中validate_detector函數下找到int classes = l.classes;在后面添加char *classesnum = option_find_str(options, "classes", "80");classes = atoi(classesnum);這樣在文件夾results中生成的txt文件才和你自定義的類別數相同。記得改完重新編譯。
? 在終端輸入./darknet detector valid cfg/caltech.data cfg/yolov3-caltech.cfg backup/yolov3-caltech_last.weights -out "" -gpu 0,權重文件根據自己的修改。這會在results文件夾下生成person.txt。這個文件是我們根據3.4步驟中valid路徑的2007_test.txt中對應圖片的自己預測的結果,每張圖會生成幾個預測框,里面包含圖片名、置信度、坐標這幾個內容。person.txt文件部分內容如下(每個人預測結果不同):
000001 0.559587 224.088120 133.759949 231.628952 146.779480 000001 0.145668 232.491028 132.304642 239.329895 148.342789 000009 0.468610 228.921371 131.483200 235.663467 144.126236 000009 0.257587 235.649506 132.068237 241.292664 143.776550 000009 0.011003 247.271637 131.625412 253.146362 143.372421 000010 0.939331 592.104309 169.121338 608.267395 212.208038 000010 0.787617 480.261322 171.480667 491.733246 196.037827 000010 0.014944 475.234589 171.413132 485.475250 195.526871? 在主目錄下創建caltech_test文件夾,里面創建out.txt,out1.txt,out2.txt,person_gt.txt,person_gt_matlab.txt這幾個空文件。文件內容下面會解釋。
? 調用pro_det.py文件,生成三個文件,一個文件out.txt只存儲圖片名**(根據person.txt得出,重復的圖片名只計一次),一個文件out1.txt只包含置信度(一個圖片內所有預測框的置信度放在一行中,每個預測框的置信度用分號間隔),一個文件out2.txt保存坐標(一個圖片內所有預測框的四個坐標放在一行,每個框的內容之間用分號間隔,四個坐標之間用逗號間隔)。三個文件內容分別對應。代碼只需修改第4、5、63行**路徑。
#coding=utf-8 import os def generate_result(resource_path, des_path):des_path1 = "/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/out1.txt"des_path2 = "/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/out2.txt"rf = open(resource_path)content = rf.readline()cnt = 0tmp_dick = {}#tmp_dick是一個字典,cls表示圖片名字,bbox表示圖片的置信度和四個坐標while content:res = content.replace("\n", "").split(" ")cls = str(res[0:1][0])bbox = res[1:6]#一個圖有多個框if cls in tmp_dick:tmp_dick[cls].append(bbox)#一個圖就一個框else:tmp_dick[cls] = [bbox]content = rf.readline()rf.close()wfname = open(des_path, "r+")#圖片名,a+表示追加寫入方式,r+覆蓋寫入wfsrc = open(des_path1, "r+")#置信度wfbox = open(des_path2, "r+")#坐標#字典的鍵,也就是圖片名for key_ in tmp_dick:wfname.write(str(key_)+',')#字典的值,就是每個bbox(包含置信度和坐標)for detail in tmp_dick[key_]:#取一個bbox中的一個值,分別是置信度,x,y,w,hfor index in detail:if index == detail[0]:wfsrc.write(str(index))else:if index is detail[1]:#左上角xtmpp1 = indexwfbox.write(str((float(index))))if index is detail[2]:#左上角ytmpp2 = indexwfbox.write(str((float(index))))if index is detail[3]:#寬wfbox.write(str((float(index) - float(tmpp1))))if index is detail[4]:#高wfbox.write(str((float(index) - float(tmpp2))))if index is not detail[-1]:#每個坐標間用,隔開wfbox.write(",")if len(tmp_dick[key_]) > 1:if detail is not tmp_dick[key_][-1]:wfsrc.write(";")#不同框間的內容用;隔開wfbox.write(";")wfname.write("\n")#不同圖片換行wfsrc.write("\n")wfbox.write("\n")wfname.close()wfsrc.close()wfbox.close() #生成三個文件,out保存圖片名,換行間隔;out1保存置信度,一個圖片的置信度分號間隔,不同圖片換行;out2保存坐標信息(左上和右下),一個圖片的坐標信息分號間隔,每個坐標直接逗號間隔,圖片換行。 generate_result("/home/davy/下載/yolo_each_version/alexey_simple/results/person.txt", "/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/out.txt")? out.txt部分內容如下(每個人預測結果不同):
000001, 000009, 000010, 000020, 000025, 000028,? out1.txt部分內容如下(每個人預測結果不同):
0.559587;0.145668 0.468610;0.257587;0.011003 0.939331;0.787617;0.014944 0.867930;0.172820;0.022374;0.005487;0.005403 0.827769;0.011797;0.008957? out2.txt部分內容如下(每個人預測結果不同):
224.08812,133.759949,7.540831999999995,13.019531;232.491028,132.304642,6.838866999999993,16.03814700000001 228.921371,131.4832,6.742096000000004,12.643035999999995;235.649506,132.068237,5.643158,11.708312999999976;247.271637,131.625412,5.874725000000012,11.747008999999991 592.104309,169.121338,16.16308600000002,43.08669999999998;480.261322,171.480667,11.471924000000001,24.557159999999982;475.234589,171.413132,10.240660999999989,24.11373900000001 233.762619,130.848083,7.07607999999999,11.57074;230.70842,130.462173,6.369751000000008,12.236480999999998;247.076218,130.692719,6.628844999999984,10.81832799999998;385.98465,168.248779,11.765930000000026,20.181701999999973;213.189636,131.733978,7.869202000000001,11.511993999999987 594.865295,123.218018,22.319703000000004,57.115263999999996;568.346802,130.529251,17.81664999999998,43.75082400000002;633.757751,114.223183,6.242249000000015,78.37160499999999? 這里我每次生成的的預測結果數量總是比2007_test.txt中圖片數量少幾張,就是總有幾張沒有給出預測,不過這并不影響我們后續的步驟。
4.2生成trainingData內容
? 上一步生成的out.txt文件中是你預測的測試集圖片名字。這一步是找到預測圖片相對應的真實坐標信息的xml格式的標注文件并轉換為txt類型。調用xml2txt.py文件,代碼同樣必須放在和VOCdevkit同級目錄下,此外只需修改第18、35的路徑即可。
import xml.etree.ElementTree as ET import pickle import os from os import listdir, getcwd from os.path import join classes = ["person"]def convert(box):#左上角和whx = box[0]y = box[2]w = box[1] - box[0]h = box[3] - box[2]return (x,y,w,h)def convert_annotation(year, image_id):in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id)) #存放xml格式文件的目錄out_file = open('/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/person_gt.txt', 'a+') #保存到這個文件中,a+以追加方式寫入tree=ET.parse(in_file)root = tree.getroot()for obj in root.iter('object'):difficult = obj.find('difficult').textcls = obj.find('name').textif cls not in classes or int(difficult)==1:continue#cls_id = classes.index(cls)#改成人的話只有1類,cls_id永遠為0xmlbox = 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(b)print(bb)out_file.write(str(image_id) + " " + " ".join([str(a) for a in bb]) + '\n') #圖片名+空格+坐標信息(坐標之間空格間隔)out_file.close() #讀取out文件下對應的圖片名,并去掉逗號 image_ids = open('/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/out.txt').read().strip().split() for i in range(len(image_ids)):image_ids[i] = image_ids[i][:-1] #print(image_ids) for image_id in image_ids:convert_annotation(2007, image_id)? 這里生成的person_gt.txt部分內容如下:
000001 228.04708948403487 132.5344370077146 6.439862509174759 12.555317447834682 000009 230.92744661144314 131.24515230156234 7.053654320016761 13.13606348956688 000010 591.1319702602229 168.52973977695154 18.088599752168648 44.33240396530363 000010 478.6104328881311 167.79792175807526 11.85185185185179 25.55877616747182 000020 233.06535999869328 129.9418563797974 6.9233349203163925 13.112218043096448? 可以看到一張圖片可能包含多個gt框,需要把一張圖片的內容放在一行來表示。調用pro_train.py文件處理,生成person_gt_matlab.txt。代碼只需修改第52行路徑即可。
#coding=utf-8 import os def generate_result(resource_path, des_path):rf = open(resource_path)content = rf.readline()cnt = 0tmp_dick = {}#tmp_dick是一個字典,cls表示圖片名字,bbox表示圖片四個坐標while content:res = content.replace("\n", "").split(" ")cls = str(res[0:1][0])bbox = res[1:5]print("cls:",cls)print("bbox", bbox)#一個圖有多個框if cls in tmp_dick:tmp_dick[cls].append(bbox)#一個圖就一個框else:tmp_dick[cls] = [bbox]cnt += 1content = rf.readline()#print(content)print(tmp_dick)rf.close()wfname = open(des_path, "r+")#圖片名,r+表示覆蓋寫入方式#字典的鍵,也就是圖片名for key_ in tmp_dick:#print("key:",key_)wfname.write(str(key_)+',')#字典的值,就是每個bbox(包含坐標)for detail in tmp_dick[key_]:#取一個bbox中的一個值,分別是x,y,w,hfor index in detail:#print("index:",index)if index is detail[0]:#左上角xwfname.write(str((float(index))))if index is detail[1]:#左上角ywfname.write(str((float(index))))if index is detail[2]:#寬wfname.write(str((float(index))))if index is detail[3]:#高wfname.write(str((float(index))))if index is not detail[-1]:#每個坐標間用,隔開wfname.write(" ")if len(tmp_dick[key_]) > 1:if detail is not tmp_dick[key_][-1]:wfname.write(";")#不同框間的內容用;隔開wfname.write("\n")#不同圖片換行wfname.close() generate_result("/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/person_gt.txt", "/home/davy/下載/yolo_each_version/alexey_simple/caltech_test/person_gt_matlab.txt")? 生成的person_gt_matlab.txt和out.txt、out1.txt、out2.txt的內容對應。部分內容如下所示:
000001,228.04708948403487 132.5344370077146 6.439862509174759 12.555317447834682 000009,230.92744661144314 131.24515230156234 7.053654320016761 13.13606348956688 000010,591.1319702602229 168.52973977695154 18.088599752168648 44.33240396530363;478.6104328881311 167.79792175807526 11.85185185185179 25.55877616747182 000020,233.06535999869328 129.9418563797974 6.9233349203163925 13.112218043096448 000025,598.681926683717 129.96268826371127 19.121328502415395 45.682222222222244.3繪制fppi miss rate曲線
? 這里我們需要的數據已經處理完畢,可以開始轉到matlab畫圖了。首先調用detectionResults.m生成matlab所需要的預測結果格式。代碼修改第1和2行的路徑,第11和12行你生成結果的行數。
fid1=fopen("D:\Desktop\ubuntu-shared-files\FPPI-miss rate\caltech-none\out2.txt", "rt");%坐標信息 fid2=fopen("D:\Desktop\ubuntu-shared-files\FPPI-miss rate\caltech-none\out1.txt", "rt");%置信度data1 = textscan(fid1, '%s', 'delimiter', '\n'); data2 = textscan(fid2, '%s', 'delimiter', '\n');data1 = data1{1,1}; data2 = data2{1,1};get_scr_bbox(4258) = struct('Boxes',[],'Scr',[]); for i=1:4258A = data1{i};A = cellstr(A);A = str2num(cell2mat(A));B = data2{i};B = cellstr(B);B = str2num(cell2mat(B));get_scr_bbox(i).Boxes = A;get_scr_bbox(i).Scr = B; end get_scr_bbox = struct2table(get_scr_bbox);fclose(fid1); fclose(fid2);? 接下來調用trainingData.m生成matlab所需要的gt框結果的格式。代碼修改第1、8、9行。
fid=fopen("D:\Desktop\ubuntu-shared-files\FPPI-miss rate\caltech-none\person_gt_matlab.txt", "rt");data = textscan(fid, '%s', 'delimiter', '\n');data = data{1,1}; count = 0;get_results(4258) = struct('Boxes',[],'name',[]); for i=1:4258A = data{i};A = regexp(A, ',', 'split'); get_results(i).name = A(1); %圖片名B = A(2);B = str2num(cell2mat(B)); get_results(i).Boxes = B; end get_results = struct2table(get_results); while feof(fid) ~= 1file = fgetl(fpn); endfclose(fid);? 運行FPPI_miss_rate.m,里面含有matlab內置函數生成圖像。
[am,fppi,missRate] = evaluateDetectionMissRate(get_scr_bbox,get_results(:,1),0.5); % Plot log average miss rate - FPPI. figure loglog(fppi, missRate, 'g','linewidth',1.5); %還有線條類型可以選擇 xlabel('false positives per image') ylabel('miss rate') grid on set(gca, 'yMinorTick','on') set(gca, 'xMinorTick', 'on') hleg1 = legend('yolov3') set(hleg1, 'Location', 'NorthEast') title(sprintf('log Average Miss Rate = %.5f',am))? 最終圖像:
5.補充
1.在results下生成的預測結果文件person.txt,坐標信息分別是左上角xy坐標和右下角xy坐標且是真實坐標。
2.繪制fppi/miss rate圖中detectionResults中的Boxes,坐標信息左上角xy、w、h且是真實值。
3.用voc_label.py生成的labels文件夾里的坐標信息是左上角xy、w、h但歸一化后的值。
4.生成的person_gt.txt和person_gt_matlab.txt都是左上角xy,wh真實值。
總結
以上是生活随笔為你收集整理的全网最详细的Yolov3训练Caltech Pedestrain数据集并绘制fppi miss rate图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ug中模型不见了怎么办_UG双击prt文
- 下一篇: OceanBase数据库大赛