1. 准备
1.1 相关论文地址
1.2 相关代码地址
1.3 相关资源地址
ECCV16 Face Tracking数据集地址,ECCV16 Face Tracking百度云下载地址,密码s9to
FDDB人脸数据集,FDDB百度云下载地址,密码z35q
WIDER FACE人脸数据集,WIDER FACE百度云下载地址,密码itlw,貌似此数据集不能通过百度网盘传输,直接点击前面链接就行
CASIA-WebFace百度云下载地址,密码95ct
2. 训练
2.1 前话
由于在我们实验室机器上第一次DCN编译通过,后来不知什么原因再次编译就出错了,加上代码一直调试不成功,果断放弃原始代码的backbone,使用了FairMOT多人脸跟踪代码的以mobilenetv2为backbone,感谢大佬。这里其实很多backbone都可以使用,例如ResNet、HRNet等,只要最后输出维度和原始代码backbone输出维度相同即可。
2.2 制作数据集
我们训练的代码直接使用上面这个大佬的代码即可,不需要使用原始的代码。不过这位大佬使用的训练数据集是FDDB和CASIA-WebFace,这两个数据集只是简单的人脸检测和人脸识别数据图像,没有出现在视频中。我们知道,视频中一秒大概30帧图像,在这30帧图像中人脸位置变化可能非常小,多人脸跟踪目标是要跟踪人脸在视频中的位置,因此,我们使用ECCV16的数据集,它是多个视频人脸数据集。当然,也可以使用那两个人脸数据集,制作过程后面我们再说。
ECCV16视频多人脸跟踪数据集制作如下:
- 对于每一张图像生成对应的txt文件,每一行代表图像中出现的一个人脸目标,每一行格式为“class id x_center/img_width y_center/img_height w/img_width h/img_height”,代码如下所示。其中,参数filename表示我们需要解析的xml文件;参数image_path表示任意一张图像,因为我们需要读取图像的长宽进行下面的计算;参数out表示我们将生成的txt文件输出的文件夹位置。每一行格式中class表示类别,由于我们只有人脸这一个类,因此全为0;id表示所有图像中出现的第几张人脸,如果整个视频中出现6张人脸,就按0~5给id赋值,但由于我们使用了8个视频数据集,所以如果第一个视频中人脸id为0~5,那第二个视频中出现的人脸id从6开始标号,以此类推;剩下四个参数比较容易理解。
from PIL import Image
from xml.dom.minidom import parse
def read_xml(filename, image_path, out):
img = Image.open(image_path)
image_width = img.size[0]
image_height = img.size[1]
domTree = parse(filename)
rootNode = domTree.documentElement
start_frame = int(rootNode.getAttribute("start_frame"))
end_frame = int(rootNode.getAttribute("end_frame"))
# for i in range(start_frame, end_frame + 1):
# open(out + str("%05d" % i) + ".txt", 'w')
trajectorys = rootNode.getElementsByTagName("Trajectory")
for trajectory in trajectorys:
tid = int(trajectory.getAttribute("obj_id")) - 1 # 需要按照人脸id进行加减调整
frames = trajectory.getElementsByTagName("Frame")
for frame in frames:
frame_no = int(frame.getAttribute("frame_no"))
x = int(frame.getAttribute("x"))
y = int(frame.getAttribute("y"))
width = int(frame.getAttribute("width"))
height = int(frame.getAttribute("height"))
cx = (x + width / 2) / image_width
cy = (y + height / 2) / image_height
w = width / image_width
h = height / image_height
with open(out + "%05d" % frame_no + ".txt", 'a') as f:
f.write("%d %d %.6f %.6f %.6f %.6f\n" % (0, tid, cx, cy, w, h))
if __name__ == '__main__':
read_xml("/home/hengyuli/FaceMFT/dataset/eccv2016/Westlife/Westlife_gt.xml", "/home/hengyuli/FaceMFT/dataset/eccv2016/Westlife/images/00001.jpg", "/home/hengyuli/FaceMFT/dataset/eccv2016/Westlife/labels_with_ids/")
- 生成包含图像路径的文件。我们发现,上面视频中有些图像帧没有人脸出现,因此我们需要先去除没有任何内容的txt文件,然后根据剩余的txt文件路径,构建出所需要的图像文件的路径。这里我自己创建了一个数据集文件夹dataset,数据集我全部放在了这里,代码中应该很容易看出来。
import os
def deletenull(label_path):
files = os.listdir(label_path)
for file in files:
if os.path.getsize(label_path + "/" + file) == 0:
os.remove(label_path + "/" + file)
if __name__ == '__main__':
deletenull("Westlife/labels_with_ids")
import os
with open("eccv2016/eccv2016.train", 'a') as f:
files = os.listdir("eccv2016")
for file in files:
dir = "/home/hengyuli/FaceMFT/dataset/eccv2016/" + file
if os.path.isdir(dir):
lables_with_ids = os.listdir(dir + "/" + "labels_with_ids")
lables_with_ids.sort()
for label in lables_with_ids:
f.write(dir + "/" + "images/" + label[:-4] + ".jpg" + "\n")
- 在src/lib/cfg/文件夹下创建一个json文件,例如eccv2016.json,指定root和train路径,root表示我们数据集根目录,train代表刚才生成的包含所有需要训练的图像的地址文件,代码如下。
{
"root":"/home/hengyuli/FaceMFT/dataset",
"train":
{
"eccv2016":"/home/hengyuli/FaceMFT/dataset/eccv2016/eccv2016.train"
}
}
- 更改src/lib/opts.py中data_dir为/home/hengyuli/FaceMFT/dataset。最后,训练参数设置如下,其中--load_model使用了作者在FDDB和CASIA-WebFace上训练好的模型,作为初始化参数。
python train.py mot --batch_size 8 --num_epochs 100 --gpus "0" --lr_step "50"
--data_cfg "/home/hengyuli/FaceMFT/src/lib/cfg/eccv2016.json"
--load_model "/home/hengyuli/FaceMFT/exp_mobile/mot/default/model_41epoch.pth"