兴趣尝试,训练一下自己的数据集做图像识别人脸口罩。
darknet网络下载
下载地址:https://pjreddie.com/darknet/yolov2/
直接按照步骤,里面有不同yolo版本的基本配置文件,区别是使用什么样的预训练模型就用什么样的weight文件,下载好后直接放在darknet目录下。
尝试运行:
cd darknet
./darknet detector test cfg/voc.data cfg/yolov2-tiny-voc.cfg yolov2-tiny-voc.weights data/dog.jpg
测试结果:
bicycle居然没识别出来。确实精度差点。
数据集处理
一、转换数据集格式
我是直接从网上下载的数据集,但jpg和xml是一一对应放在同一个文件夹下,而一般训练大多是按照VOC格式存储,所以要先转换数据集格式。
VOC数据集格式如下:
VOCdevkit
——VOC2020 #文件夹的年份可以自己取
————Annotations #放入所有的xml文件
————ImageSets
——————Main #放入train.txt,val.txt,test.txt,trainval.txt文件(不一定全要建)
————JPEGImages #放入所有的图片文件
Main中的文件分别表示test.txt是测试集,train.txt是训练集,val.txt是验证集,trainval.txt是训练和验证集
二、数据增强:
参考链接
博主用的os.walk(),获取路径,但是读取的文件路径是乱序(害苦我了),之后稍微改了一下。
在网上搜索发现,获取文件路径有两种方法:
- os.listdir(path)只能获取当前目录下的所有文件或者文件夹的名称,而不能获取文件夹的绝对路径
- os.walk(path) 返回包含(root,dirs,files)三种信息的生成器。
os.walk(path)获得的并不是路径,所以需要将获得的三种信息进行链接才能得到路径
root 所指的是当前正在遍历的这个文件夹的本身的地址
dirs 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录)
这里采用的是os.listdir(),再sort(),将文件排序后再依次读取并且重命名为000123.xml/.jpg格式的文件。
#author:gr
# -*- coding:utf-8 -*-
import os
import shutil
path = '/Users/apple/train' #数据集文件夹路径,下面包含每个类,改成你自己的
new_img_path = '/Users/apple/VOCdevkit/VOC2020/JPEGImages/' #新的图片路径,改成你自己的
new_ann_path = '/Users/apple/VOCdevkit/VOC2020/Annotations/' #新的xml路径,搞成你自己的
count_img = 1 #每提取一张图片,count+1,也能够按顺序给图片重命名
count_ann = 1 #每提取一个xml,count+1也能够按顺序给文件重命名
path_list = os.listdir(path) #获取文件名+后缀
path_list.sort()#我对数据集没有顺序要求,只需jpg和xml一一对应,所以最简单的排序
#print(path_list)
root = path
for files in path_list:
'''循环path文件下每个每个文件夹,每个图片,按照以.jpg结尾和.xml结尾区分'''
#print(files)
if files[-3:] == 'jpg':
file_path = root + '/' + files
shutil.copy(file_path, os.path.join(new_img_path, str(count_img).zfill(6)+'.jpg'))
count_img = count_img + 1
elif files[-3:] == 'xml':
file_path = root + '/' + files
shutil.copy(file_path, os.path.join(new_ann_path, str(count_ann).zfill(6)+'.xml'))
count_ann = count_ann + 1
print(count_img)
print(count_ann)
二、接着生成tain.txt
再将生成之后的文件VOCdevkit放到/darknet/scripts/目录下
#author:gr
# -*- coding:utf-8 -*-
# 生成main.txt
import os
def main(src, dest):
count = 0
out_file = open(dest,'w') #生成了在指定目录下的txt文件
path_list = os.listdir(src)
path_list.sort()
with open(dest, 'w') as f:
for name in path_list:
base_name = os.path.basename(name)
file_name = base_name.split('.')[0]
f.write('%s\n' % file_name)
count = count + 1
print(count)
if __name__ == '__main__':
TrainDir = '/Users/apple/VOCdevkit/VOC2020/JPEGImages/' #图片文件所在目录
target = '/Users/apple/VOCdevkit/VOC2020/ImageSets/Main/train.txt'
main(TrainDir, target)
三、修改voc_label.py.直接在vocdevkit文件里自动创建label文件夹包含每张照片对应的txt文件。
按照如下格式:
<object-class> <x> <y> <width> <height>
分别代表物体类别,
中心归一化横坐标(x)
中心归一化纵坐标(y)
归一化宽度(w)
归一化高度(h)
计算公式参考:
dw = 1 / width
dh = 1/ height
x = ( xmin + xmax ) / 2 * dw
y = ( ymin + yman ) / 2 * dh
w = ( xmax - xmin ) / 2 * dw
h = ( ymax - ymin ) / 2 * dh
实现代码:
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')]
#classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
sets = [('2020','train')] #训练的集合
classes = ["face_mask","face"] #标注的类别
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(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').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 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 2012_train.txt 2012_val.txt > train.txt")
#os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt")
os.system("cat 2020_train.txt 2020_val.txt > train.txt")
最后生成了label文件夹包含所有的图片对应的txt文件,生成2020_train.txt,2020_val.txt之类的文本文件列出了该年份的图像文件和图像集。 train.txt需要一个文本文件,其中包含要训练的所有图像。
不过我的数据集有一点不规范没有< size >标签,弄了好久。不过一般按照正常情况标注的应该不会发生,如果有出入相应修改代码就行了。这里就不详述啦
修改相应配置文件
四、修改cfg文件和names文件
在cfg文件夹下找到voc.data文件
1 classes= 2 #classes训练类别数
2 train = <path-to-voc>/train.txt #训练集
3 valid = <path-to-voc>2020_test.txt#测试集
4 names = data/voc.names
5 backup = backup
< path-to-voc>就是你放数据集的位置
修改data文件夹下voc.name文件,换成你训练的类别:
mask
face
五、下载预训练模型
wget https://pjreddie.com/media/files/darknet19_448.conv.23
六、修改cfg/yolo-tiny-voc.cfg
1.修改了batch和subdivison
[net]
# Testing
#batch=1
#subdivisions=1
# Training
batch=64
subdivisions=8
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
2.修改最后一层的卷积核和类别
filter =5*(5+2)
[convolutional]
size=1
stride=1
pad=1
filters=35//
activation=linear
[region]
anchors = 1.08,1.19, 3.42,4.41, 6.63,11.38, 9.42,5.11, 16.62,10.52
bias_match=1
classes=2//
coords=4
num=5
softmax=1
jitter=.2
rescore=1
object_scale=5
noobject_scale=1
class_scale=1
coord_scale=1
absolute=1
thresh = .6
random=1
五、Makefile文件
GPU=1
CUDNN=1
OPENCV=1
OPENMP=0
DEBUG=0
cd darkent
make
数据集终于弄好了,下一步准备上服务器了,嘿嘿嘿。
训练
cd darknet目录下执行
./darknet detector train cfg/voc.data cfg/yolov2-tiny-voc.cfg darknet19_448.conv.23
测试
cd darknet
./darknet detector test cfg/voc.data cfg/yolov2-tiny-voc.cfg backup/yolov2-tiny-voc_final.weights
输入图片名进行测试
Enter Image Path: data/1.jpg
data/1.jpg: Predicted in 1.202904 seconds.
face: 56%
mask: 91%
(一般放在darknet/data下)
./darknet detector test cfg/voc.data cfg/yolov2-tiny-voc.cfg backup/yolov2-tiny-voc_final.weights data/1.jpg
点开prediction.jpg,效果还行,就不放出来了。
计算map值
由于yolov2版本比较低,无法直接计算出map,所以先生成检测结果文件(以下保存在mask.txt中)
./darknet detector valid cfg/voc.data cfg/yolov2-tiny-voc.cfg backup/yolov2-tiny-voc_final.weights -out mask.txt -gpu 0 -thresh .5
其他
训练暂停:ctrl+z
恢复训练:fg
训练终止:ctrl+c
训练中遇到的一些问题
一、Couldn’t open file
Couldn't open file: /darknet/scripts/2020_train.txt
solution:
修改cfg/voc.data,要用绝对路径不要用相对路径
train = /home/你的用户名/darknet/scripts/2020_train.txt
valid = /home/你的用户名/darknet/scripts/2020_val.txt
二、Cannot load image
Cannot load image "/darknet/scripts/VOCdevkit/VOC2020/JPEGImages/002846.jpg"
STB Reason: can't fopen
solution:
重要的事情说三遍
绝对路径!绝对路径!绝对路径!
ps:这个如果用之前的文件生成的文件的确是绝对路径,由于我个人原因修改了一下路径,变成了相对路径,所以我的问题不一定都会遇到。
其他学习
服务器上传,下载文件参考:
https://blog.csdn.net/resilient/article/details/85334594
上传文件:scp -P(大写) 端口号。。。。。