使用YOLO進行目標檢測在保證速度的前提下能有較好的準確性,這篇文章是在yolov3的代碼基礎上進行修改爲行人檢測,由原來的20個分類改爲識別person,使用tiny-yolo的進行訓練和檢測,最後能達到還不錯的效果。
1.數據集製作
收集圖片並標註,這是一件工作量較大的事情,所以我們直接使用了PASCAL VOC2007的數據集,抽取其中帶有行人的圖片。
具體過程如下:
1.1 創建相應的文件夾
- 在yolo代碼裏的scripts/目錄下創建VOC_person文件夾,在其文件夾下創建Annotations、ImageSets、JPEGImages文件夾,在ImageSets文件夾下建立Main文件夾。這裏順便說下這三個文件夾的作用:
Annotations -- Annotations文件夾中存放的是xml格式的標籤文件,每一個xml文件都對應於JPEGImages文件夾中的一張圖片;
ImageSets -- ImageSets存放的是每一種類型的challenge對應的圖像數據。在ImageSets下有Main文件夾,存放的是圖像物體識別的數據,總共分爲20類;
JPEGImages -- JPEGImages文件夾中包含了PASCAL VOC所提供的所有的圖片信息,包括了訓練圖片和測試圖片。
1.2 拷貝相應的文件
- 提取VOC2007/ImageSets/Main/文件夾裏的person_test.txt,person_trainval.txt,person_train.txt person_val.txt 這四個文件到VOC_person/ImageSets/Main/目錄下;
- 拷貝VOC2007/Annotations/ 目錄下的所有的文件到VOC_person/Annotations/ 目錄下;
- 拷貝VOC2007/JPEGImages/ 目錄下的所有的文件到VOC_person/JPEGImages/ 目錄下;
1.3 生成訓練集,測試集和開發集文件
- 我們需要生成VOC_person/ImageSets/Main/目錄下的test.txt,train.txt,trainval.txt,val.txt 這四個文件。
train.txt是訓練集,test.txt是測試集,val.txt是驗證集,trainval.txt是訓練和驗證集。VOC2007中,trainval大概是整個數據集的50%,test也大概是整個數據集的50%;train大概是trainval的50%,val大概是trainval的50%,當數據集較小時,可減少val和test的數量。
使用Matlab生成這四個txt文件,make_train_data.m 代碼如下:
%注意修改下面四個值
xmlfilepath='E:\Image Sets\VOC_person\Annotations';
txtsavepath='E:\Image Sets\VOC_person\ImageSets\Main\';
trainval_percent=0.8;%trainval佔整個數據集的百分比,剩下部分就是test所佔百分比
train_percent=0.8;%train佔trainval的百分比,剩下部分就是val所佔百分比
xmlfile=dir(xmlfilepath);
numOfxml=length(xmlfile)-2;%減去.和.. 總的數據集大小
trainval=sort(randperm(numOfxml,floor(numOfxml*trainval_percent)));
test=sort(setdiff(1:numOfxml,trainval));
trainvalsize=length(trainval);%trainval的大小
train=sort(trainval(randperm(trainvalsize,floor(trainvalsize*train_percent))));
val=sort(setdiff(trainval,train));
ftrainval=fopen([txtsavepath 'trainval.txt'],'w');
ftest=fopen([txtsavepath 'test.txt'],'w');
ftrain=fopen([txtsavepath 'train.txt'],'w');
fval=fopen([txtsavepath 'val.txt'],'w');
for i=1:numOfxml
if ismember(i,trainval)
fprintf(ftrainval,'%s\n',xmlfile(i+2).name(1:end-4));
if ismember(i,train)
fprintf(ftrain,'%s\n',xmlfile(i+2).name(1:end-4));
else
fprintf(fval,'%s\n',xmlfile(i+2).name(1:end-4));
end
else
fprintf(ftest,'%s\n',xmlfile(i+2).name(1:end-4));
end
end
fclose(ftrainval);
fclose(ftrain);
fclose(fval);
fclose(ftest);
- 將數據轉換成YOLO訓練所需的標註各色,並生成訓練/驗證/測試圖片路徑文件
1)打開yolo代碼下的scripts目錄,拷貝voc_label.py文件並重命名爲person_voc_label.py 文件:
cp voc_label.py person_voc_label.py
2)修改person_voc_label.py文件:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets=['train', 'val', 'test']
classes = ["person"]
def convert(size, box):
dw = 1./(size[0])
dh = 1./(size[1])
x = (box[0] + box[1])/2.0 - 1
y = (box[2] + box[3])/2.0 - 1
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('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/Annotations/%s.xml'%(image_id))
out_file = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/%s.txt'%(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 image_set in sets:
if not os.path.exists('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/'):
os.makedirs('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/')
image_ids = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
list_file = open('person_%s.txt'%(image_set), 'w')
for image_id in image_ids:
list_file.write('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/JPEGImages/%s.jpg\n'%(image_id))
convert_annotation(image_id)
list_file.close()
os.system("cat person_train.txt person_val.txt > my_person_train.txt")
3)執行person_voc_label.py腳本。
發現在scripts/VOC_person目錄下生成label文件夾,裏面有每張圖片的標註文件(文件內容形如0 0.488888888889 0.289256198347 0.977777777778 0.429752066116),其中第一個數字表示類別標籤(0表示person,也即前面寫的classes的順序),後面四個數爲包圍框信息。
4)生成訓練文件
在上一步執行了person_voc_label.py腳本後,會在 scripts下多了person_train.txt、person_val.txt和person_test.txt這三個文件,這三個文件是數據集中圖片的路徑。由於yolo訓練只需要一個my_person_train.txt文件,文件中包含所有你想要訓練的圖片的路徑。因此,我們還要生成my_person_train.txt文件。
cat person_train.txt person_val.txt > my_person_train.txt
到這裏數據集製作算是製作完成了。
2.修改代碼訓練
(1)在data目錄下新建文件my_person_train.names,內容爲:
person
(2)在cfg目錄下新建 my_person_train.data文件,內容爲:
classes= 1
train = /home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/my_person_train.txt
valid = /home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt
names = data/my_person_train.names
backup = backup
(3)修改examples/yolo.c文件:
- 檢測目標類別改成自己的數據集類別:
char *voc_names[] = {"person"};
- train_yolo函數:
char *train_images = "/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/my_person_train.txt";
char *backup_directory = "/home/liguiyuan/deep_learning/project/darknet/backup/";
train_images應該指向我們剛得到的my_person_train.txt;backup_directory指向的路徑是訓練過程中生成的weights文件保存的路徑(可以在darknet-master下新建文件夾backup然後指向它)。這兩個路徑按自己系統修改即可。
- validate_yolo函數:
char *base = "results/comp4_det_test_";
//list *plist = get_paths("data/voc.2007.test");
list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
//list *plist = get_paths("data/voc.2012.test");
- validate_yolo_recall函數:
char *base = "results/comp4_det_test_";
list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
- test_yolo函數:
//draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, 0, voc_names, alphabet, 20);
draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, 0, voc_names, alphabet, 1);
類別數改爲1類。
- run_yolo函數:
//demo(cfg, weights, thresh, cam_index, filename, voc_names, 20, frame_skip, prefix, avg, .5, 0,0,0,0);
demo(cfg, weights, thresh, cam_index, filename, voc_names, 1, frame_skip, prefix, avg, .5, 0,0,0,0);
類別數改爲1類。
(4)修改examples/detector.c文件
- validate_detector_recall函數:
list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
- run_detector函數:
//int classes = option_find_int(options, "classes", 20);
int classes = option_find_int(options, "classes", 1);
類別數改爲1類
(5)修改cfg/yolov3-voc_person.cfg文件:
複製cfg/yolov3-voc.cfg文件並命名爲yolov3-voc_person.cfg,如果想用其他模型修改類似。
【yolo】層中 classes 把20改成1,因爲我們檢測的類別只有penson,有3個地方要改;
【yolo】層的上一層【convolutional】,即網絡中的最後一層卷積,的filters改爲18,也是3處地方要修改。計算方法是這樣:1*1*[3*(4+1+1)]=18。[num*(coords+1+classes) ,num爲3,因爲yolov3預測3個boxes(yolov1爲1個,yolov2爲5個),coords爲4個座標(預測boundingbox的 x,y,w,h), 1表示置信度,識別1個類別。N爲卷積核的size。
[convolutional]
size=1
stride=1
pad=1
filters=18
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=1
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
(6)其他的一些參數可以按自己需求修改,比如batch, subdivisions等,都是在這個cfg文件裏面修改。我這裏把batch改爲32,要不然使用GTX1060顯卡訓練時會出現CUDA Error: out of memory。subdivision:這個參數意思是,讓你的每一個batch不是一下子都丟到網絡裏,而是分成多次丟到GPU內存中,例如2,就是分成2次讀入。
[net]
# Testing
# batch=1
# subdivisions=1
# Training
batch=32
subdivisions=16
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
learning_rate=0.001
burn_in=1000
max_batches = 50200
policy=steps
steps=40000,45000
scales=.1,.1
3.訓練模型
下載預訓練darknet53.conv.74的weights權重文件,下載地址爲:
wget https://pjreddie.com/media/files/darknet53.conv.74
開始訓練模型:
./darknet detector train cfg/my_person_train.data cfg/yolov3-voc_person.cfg darknet53.conv.74
看到開始訓練了:
訓練中斷後從斷點開始訓練模型:
./darknet detector train cfg/my_person_train.data cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup
使用GTX1060的GPU,經過30多個小時的訓練,訓練了10000批次,在person_backup目錄下看到 yolov3-voc_person_10000.weights等訓練好的模型。這裏附上我訓練的模型鏈接: https://pan.baidu.com/s/1sfeEqdR-K-9q4NZFD5FdmQ 提取碼: 8n8g
4.檢測模型的結果
修改cfg/yolov3-voc_person.cfg 文件,把Training的batch, subdivisions屏蔽掉,使用Testing的batch, subdivisions 都爲1
# Training
#batch=32
#subdivisions=2
# Testing
batch=1
subdivisions=1
檢測圖片:
./darknet detect cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup data/person.jpg
攝像頭實時檢測:
./darknet detector demo cfg/my_person_train.data cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup
效果圖如下:
文章到這裏完成了整個訓練的過程。
注:轉載請註明出處!
文章裏部分教程參考:https://blog.csdn.net/yudiemiaomiao/article/details/71635257