索引地址:系列索引
功能测试先从作者的Github上下载yolov4.weights
然后输入命令执行
1 ./darknet detector test cfg/coco.data cfg/yolov4.cfg ./yolov4.weights data/dog.jpg
执行结果为
测试数据集先从网上盗点蒂法的图片,当然图片的文件名默认应该是一大串字符。我们用脚本批量改名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import os path=input ('请输入文件路径(结尾加上/):' ) fileList=os.listdir(path) n=0 for i in fileList: oldname=path+ os.sep + fileList[n] newname=path + os.sep +'tifa_' +str (n+1 ).zfill(3 )+'.' +oldname.split('.' )[-1 ] os.rename(oldname,newname) print (oldname,'======>' ,newname) n+=1
改完后图片为
标注数据一张图片并不是所有的部分都是蒂法,我们需要使用工具将其标注出来。
这里使用的是labelImg。
Manjaro Linux直接安装就可以了
软件打开后如图
我们选择Open Dir
打开蒂法图片所在文件夹,会自动显示按文件名排序的第一张图片
选择左侧工具栏中的Create RectBox按钮,如果没有就把软件窗口放大直到看见为止
鼠标放在图片上会出现以鼠标为中心点的十字架,把蒂法的脸选中,然后会弹出标签对话框。可以选择标签,如果没有需要的标签可以在文本框中输入。
点击OK后,Create RectBox回到可点击状态,蒂法脸被框住,右中显示当前图片的标签
点击左侧的Verify Image按钮,将标注数据保存到图片所在文件夹,文件名保持默认。
然后点击Next Image标注下一张图片,直到全部标注完。标注结果为
YOLO的标注文件格式为txt,所以我们要把xml格式的文件转换为txt格式。格式内容为:
1 <object-class> <x_center> <y_center> <width> <height>
类名:从0到(类别数-1),在这里一共1类,只需要标出文字,至于文字是什么不重要。
<x_center> 中心点横坐标:这里的坐标并不是绝对坐标(真实坐标),而是相对于图片宽度的相对坐标,转换公式为<x_center> = <absolute_x> / <image_width>,这里的absolute_x是指标注框中心的横坐标
<y_center> 中心点纵坐标:这里的坐标并不是绝对坐标(真实坐标),而是相对于图片高度的相对坐标,转换公式为<x_center> = <absolute_y> / <image_height>,这里的absolute_x是指标注框中心的纵坐标
标注框宽度:这里的宽度并不是绝对宽度(真实宽度),而是相对于图片宽度的相对宽度,转换公式为= <absolute_width> / <image_width>
标注框宽度:这里的宽度并不是绝对宽度(真实宽度),而是相对于图片宽度的相对高度,转换公式为= <absolute_height> / <image_height>
如果一张400*400
图片都是有效数据,那么标注框就是图片的边框,那么中心点就是(200,200)
中心点横坐标=200/400=0.5
中心点纵坐标=200/400=0.5
标注框宽度=400/400=1
标注框高度=400/400=1
那么txt文件为
因为文件比较多,所以使用脚本批量处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import globimport xml.etree.ElementTree as ET class_names = ['KuLi' , 'DuLanTe' ] path = 'data/train_images/' def single_xml_to_txt (xml_file ): tree = ET.parse(xml_file) root = tree.getroot() txt_file = xml_file.split('.' )[0 ]+'.txt' with open (txt_file, 'w' ) as txt_file: for member in root.findall('object' ): picture_width = int (root.find('size' )[0 ].text) picture_height = int (root.find('size' )[1 ].text) class_name = member[0 ].text class_num = class_names.index(class_name) box_x_min = int (member[4 ][0 ].text) box_y_min = int (member[4 ][1 ].text) box_x_max = int (member[4 ][2 ].text) box_y_max = int (member[4 ][3 ].text) x_center = (box_x_min + box_x_max) / (2 * picture_width) y_center = (box_y_min + box_y_max) / (2 * picture_height) width = (box_x_max - box_x_min) / picture_width height = (box_y_max - box_y_min) / picture_height print (class_num, x_center, y_center, width, height) txt_file.write(str (class_num) + ' ' + str (x_center) + ' ' + str (y_center) + ' ' + str (width) + ' ' + str (height) + '\n' )def dir_xml_to_txt (path ): for xml_file in glob.glob(path + '*.xml' ): single_xml_to_txt(xml_file) dir_xml_to_txt(path)
转换后结果为
准备训练 train.txt / val.txt本次并没有准备用于验证的图片,因为测试图片本身就太少了。但是两个操作是一样的。
将训练图片放置于build/darknet/x64/data/train_images文件夹,验证图片放置与build/darknet/x64/data/val_images
train.txt一般放置于build/darknet/x64/data/文件夹
train.txt包含train_images的所有图片路径每个图片一行,val.txt类似。
1 2 data/train_images/tifa_001.jpg data/train_images/tifa_002.jpg
我们使用脚本生成
1 2 3 4 5 6 7 8 9 10 11 12 import glob path = 'data/' def generate_train_and_val (image_path, txt_file ): with open (txt_file, 'w' ) as tf: for jpg_file in glob.glob(image_path + '*.jpg' ): tf.write(jpg_file + '\n' ) generate_train_and_val(path + 'train_images/' , path + 'train.txt' )
需要先在data文件夹创建一个train.txt。
创建build/darknet/x64/data/tifa.names文件,内容是类名
创建build/darknet/x64/data/tifa.data文件,内容是
1 2 3 4 5 classes = 1 train = data/train.txt valid = data/val.txt names = data/tifa.names backup = backup/
如果文件夹/文件不存在,就创建一个空的。注意,backup位置应该是build/darknet/x64/backup/
预训练模型从YOLOv4的github仓库下载预训练模型yolov4.conv.137,如果下载不了,可以从此处下载。将其放置于build/darknet/x64中。
复制并修改build/darknet/x64/cfg文件夹下的yolov4-custom.cfg。修改如下
1 2 3 4 5 6 7 8 batch=64 subdivisions=64 #显存不足时要提高 max_batches=6000 #一般设置为classes*2000,但是不要低于4000,如果三个类就是6000 steps #一般为80%和90%的max_batches width height 必须能够整除32,比如416 classes #数据集类别,在三个yolo层,共计三处 filter # (classes+5)*3,每个yolo layer前的卷积层,共计三处
如果有疑问,可以查看完整的配置文件。
先验框可以不要这一步
原本框架中提供的anchor大小是一个通用值,不修改直接训练自己的数据集当然也可以,但为了更加适合自己的数据集,通过Kmeans聚类算出属于自己数据集的anchor大小。
进入build/darknet/x64文件夹,输入命令
1 darknet detector calc_anchors data/tifa.data -num_of_clusters 9 -width 608 -height 608
运行效果为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ ../../../darknet detector calc_anchors data/tifa.data -num_of_clusters 9 -width 608 -height 608 DEBUG=1 CUDA-version: 11060 (11040) Warning: CUDA-version is higher than Driver-version! , cuDNN: 8.3.1, CUDNN_HALF=1, GPU count: 1 CUDNN_HALF=1 OpenCV version: 4.5.5d num_of_clusters = 9, width = 608, height = 608 read labels from 185 images loaded image: 185 box: 185 all loaded. calculating k-means++ ... iterations = 11 counters_per_class = 185 avg IoU = 84.89 % Saving anchors to the file: anchors.txt anchors = 45, 93, 80,141, 175,101, 110,200, 145,272, 186,334, 222,406, 285,452, 471,360
将上面这九对数字复制替换到yolov4-custom.cfg配置文件中的三个yolo层的地方,
训练在build/darknet/x64文件夹下输入训练命令
1 darknet detector train data/tifa.data yolov4-tifa.cfg yolov4.conv.137
执行训练时,如果正常,会显示下面的窗口,如果把窗口关闭,会自动再次显示(具体原因可以探索OpenCV的窗口)。
运行时如若提示显存不足,可以适当减少batch参数或者增加subdivisions参数,如果还不行,就减少width/height的数值。生成的模型在build\darknet\x64\backup文件夹下
训练从3月10日22:37到3月12日1:40,总计耗时16小时
下面是我运行的损失图:
在build/darknet/x64/backup/文件夹下有训练的权重文件
将yolov4-tifa.cfg/tifa.data复制到cfg/
将tifa.names/复制到data
从B站盗了一个视频用于测试。
执行命令测试
1 ./darknet detector demo cfg/tifa.data cfg/yolov4-tifa.cfg ./yolov4-tifa_final.weights ./tifa.mp4
结果为
小号脸/侧脸都能准确识别出来,但是有的时候会把爱丽丝和女装cloud识别成蒂法,可能和标注中有他们有关。
YOLOv4训练并识别蒂法人脸效果 - B站
shika - NO.67 cos
一小央泽 cos
星之迟迟 cos
完整结果打包下载darknet + 脚本 + 训练数据集 + 测试图片 + 结果权重 = 1G