ubuntu14.04+opencv2.4.10+cuda7.5+cudnn5.0
训练集:20张图片 00001.jpg-00020.jpg 测试集:5张图片 00021.jpg-00025.jpg
其中,训练集中18张用于Train,2张用于Validation。
分类个数6个
一、生成xml,使用工具https://github.com/tzutalin/labelImg
二、生成txt文件
1. 在darknet文件夹中
新建文件夹mytrain
在mytrain文件夹中
将scripts/voc_label.py复制到该文件夹中
新建文件夹JPEGImages,将训练集中20张图片放入该文件夹中
新建文件夹Annotations,将训练集中20张图片对应的xml文件放入该文件夹中
新建文件夹ImageSets,将create_list.py生成的train.txt和val.txt存放在该文件夹中
新建文件夹labels,用于存放每张图片生成的txt
新建文件create_list.py
2.生成train.txt和val.txt
脚本create_list.py如下:
import os
from os import listdir, getcwd
from os.path import join
if __name__ == '__main__':
source_folder='JPEGImages/'
dest='ImageSets/Main/train.txt'
dest2='ImageSets/Main/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_name,file_extend=os.path.splitext(file_obj)
file_num=int(file_name)
if(file_num<19):
train_file.write(file_name+'\n')
else :
val_file.write(file_name+'\n')
train_file.close()
val_file.close()
3.生成每张图片的txt文件以及Trian和Validation的路径分别存放在mytrain/train.txt和mytrain/val.txt
脚本voc_label.py修改如下:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
classes = ["chariot","bike","box","bag","car","person"]
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)
def convert_annotation(image_id):
in_file = open('Annotations/%s.xml'%(image_id))
out_file = open('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()
if not os.path.exists('labels/'):
os.makedirs('labels/')
image_ids = open('ImageSets/Main/train.txt').read().strip().split()
list_file = open('train.txt', 'w')
#image_ids = open('ImageSets/Main/val.txt').read().strip().split()
#list_file = open('val.txt', 'w')
for image_id in image_ids:
list_file.write('%s/JPEGImages/%s.jpg\n'%(wd,image_id))
convert_annotation(image_id)
list_file.close()
更改注释,分别运行两次,生成mytrain/train.txt和mytrain/val.txt
三、修改文件
1.cfg/voc.data中
classes改为分类的个数6
train 改为 /home/ndd/darknet/mytrain/train.txt
valid 改为 /home/ndd/darknet/mytrain/val.txt
names 改为 data/chariot.names
backup 改为 /home/ndd/darknet/backup
2.cfg/yolo-voc.cfg(tiny-yolo-voc.cfg)中
【region】层classes改为分类的个数6
【region】层上一个【convolution】层中filters改为(classes+ coords+ 1)* (NUM)=(6+4+1)×5=55
3.src/yolo.c中
char *voc_names[] = {"chariot","car","bike","bag","box","person"};//分类的名称
draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, voc_names, alphabet, 6);//20改为6
else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, voc_names, 6, frame_skip, prefix, .5);//20改为6
train_yolo函数中:char *train_images = "/home/ndd/darknet/mytrain/train.txt";
char *backup_directory = "/home/ndd/darknet/backup/";
validate_yolo函数中:
char *base = "/home/ndd/darknet/results/comp4_det_test_";
list *plist = get_paths("/home/ndd/darknet/mytrain/val.txt");
validate_yolo_recall函数中:
char *base = "/home/ndd/darknet/results/comp4_det_test_";
list *plist = get_paths("/home/ndd/darknet/mytrain/val.txt");
src/detector.c 文件中validate_detector_recall函数中:
list *plist = get_paths("/home/ndd/darknet/mytrain/val.txt");
四、编译
1.Makefile修改
GPU=1
CUDNN=1
OPENCV=1
DEBUG=0
NVCC=/usr/local/cuda-7.5/bin/nvcc
2. 在darknet/下运行
make clean
make
生成darknet.exe
五、训练
1.下载预训练模型darknet19_448.conv.23放在darknet目录下
2.运行./darknet detector train cfg/voc.data cfg/yolo-voc.cfg darknet19_448.conv.23
3.最大迭代次数在yolo-voc.cfg中max_matches修改
六、评估性能
1.detector.c中validate_detector_recall函数中修改阈值thresh
默认的值是.001,这个默认值设的很小,会让系统识别出更多的框来,导致proposals值激增,还会让recall值变高,达到98.5%。(最终我改成了 .25。)
上面的函数只会显示出recall值,没有precision值,precision的值计算方法是:识别为正确的个数/画了多少个框,所以我修改了代码。我把第447行显示结果的代码修改为 :
我运行后显示的结果是:
…..
ID: 101 Correct: 106 Total: 111 RPs/Img: 1.07 IOU: 82.00% Recall:95.50% proposals: 109 Precision:97.25%
ID: 102 Correct: 107 Total: 112 RPs/Img: 1.07 IOU: 82.11% Recall:95.54% proposals: 110 Precision:97.27%
结果中的参数,我的理解是:
Correct :可以理解为正确地画了多少个框,遍历每张图片的Ground Truth,网络会预测出很多的框,对每一Groud Truth框与所有预测出的框计算IoU,在所有IoU中找一个最大值,如果最大值超过一个预设的阈值,则correct加一。
Total:一共有多少个Groud Truth框。
Rps/img:p 代表proposals, r 代表region。 意思就是平均下来每个图片会有预测出多少个框。预测框的决定条件是,预测某一类的概率大于阈值。在validation_yolo_recall函数中,默认的这一个阈值是0.001,这一阈值设置的比较低,这就会导致会预测出很多个框,但是这样做是可以提升recall的值,一般yolo用于画框的默认值是.25,使用这个阈值会让画出来的框比较准确。而validation_yolo_recall使用的阈值改成。25的时候,Rps/img 值会降低,recall的值会降低,所以validation_yolo_recall默认使用一个较低的阈值,有可能作者的目的就是为了提高recall值,想在某种程度上体现网络的识别精度比较高。
IoU、Recall、Precision:解释起来比较麻烦,请看博客有详细说明:
http://blog.csdn.net/hysteric314/article/details/54093734
2.运行
./darknet detector recall cfg/voc.data cfg/yolo-voc.cfg backup/yolo_voc_final.weights
(.weights为自己训练好的weights)