OpenCV4入门教程143:YOLOv4蒂法训练与识别

索引地址:系列索引

功能测试

先从作者的Github上下载yolov4.weights

然后输入命令执行

1
./darknet detector test cfg/coco.data cfg/yolov4.cfg ./yolov4.weights data/dog.jpg

执行结果为

predictions

测试数据集

先从网上盗点蒂法的图片,当然图片的文件名默认应该是一大串字符。我们用脚本批量改名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# !/bin/env python3
# -*- coding: utf-8

import os
path=input('请输入文件路径(结尾加上/):')

#获取该目录下所有文件,存入列表中
fileList=os.listdir(path)

n=0
for i in fileList:
#设置旧文件名(就是路径+文件名)
oldname=path+ os.sep + fileList[n] # os.sep添加系统分隔符

#设置新文件名
newname=path + os.sep +'tifa_'+str(n+1).zfill(3)+'.'+oldname.split('.')[-1]

os.rename(oldname,newname) #用os模块中的rename方法对文件改名
print(oldname,'======>',newname)

n+=1

改完后图片为

tifa

标注数据

一张图片并不是所有的部分都是蒂法,我们需要使用工具将其标注出来。

这里使用的是labelImg。

Manjaro Linux直接安装就可以了

1
yaourt -S labelimg-git

软件打开后如图

labelimg

我们选择Open Dir打开蒂法图片所在文件夹,会自动显示按文件名排序的第一张图片

open dir

选择左侧工具栏中的Create RectBox按钮,如果没有就把软件窗口放大直到看见为止

鼠标放在图片上会出现以鼠标为中心点的十字架,把蒂法的脸选中,然后会弹出标签对话框。可以选择标签,如果没有需要的标签可以在文本框中输入。

rectbox

点击OK后,Create RectBox回到可点击状态,蒂法脸被框住,右中显示当前图片的标签

created

点击左侧的Verify Image按钮,将标注数据保存到图片所在文件夹,文件名保持默认。

然后点击Next Image标注下一张图片,直到全部标注完。标注结果为

label

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
0 0.5 0.5 1.0 1.0

因为文件比较多,所以使用脚本批量处理

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
# 此代码和data文件夹同目录
import glob
import xml.etree.ElementTree as ET


# 类名
class_names = ['KuLi', 'DuLanTe']
# xml文件路径,train_images只需改为val_images就可以处理val_images的了
path = 'data/train_images/'

# 转换一个xml文件为txt
def single_xml_to_txt(xml_file):
tree = ET.parse(xml_file)
root = tree.getroot()
# 保存的txt文件路径
txt_file = xml_file.split('.')[0]+'.txt'
with open(txt_file, 'w') as txt_file:
for member in root.findall('object'):
# filename = root.find('filename').text
picture_width = int(root.find('size')[0].text)
picture_height = int(root.find('size')[1].text)
class_name = member[0].text
# 类名对应的index
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')

# 转换文件夹下的所有xml文件为txt
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)

转换后结果为

result

准备训练

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
#此代码和data文件夹同目录
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')
#generate_train_and_val(path + 'val_images/', path + 'val.txt')

需要先在data文件夹创建一个train.txt。

创建build/darknet/x64/data/tifa.names文件,内容是类名

1
tifa

创建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小时

下面是我运行的损失图:

chart

在build/darknet/x64/backup/文件夹下有训练的权重文件

weights

将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

结果为

result

小号脸/侧脸都能准确识别出来,但是有的时候会把爱丽丝和女装cloud识别成蒂法,可能和标注中有他们有关。

YOLOv4训练并识别蒂法人脸效果 - B站

shika - NO.67 cos

cos1

一小央泽 cos

cos2

星之迟迟 cos

cos3

完整结果打包下载darknet + 脚本 + 训练数据集 + 测试图片 + 结果权重 = 1G


OpenCV4入门教程143:YOLOv4蒂法训练与识别
https://blog.jackeylea.com/opencv/tifa-with-yolov4/
作者
JackeyLea
发布于
2022年3月12日
许可协议