人脸检测矫正-MTCNN(包含数据、模型训练、模型测试、模型部署)

本文详细介绍了使用MTCNN进行人脸识别的过程,涉及数据集WIDERFACE和CNN_FacePoint的选择,模型Pnet、Rnet和Onet的训练挑战,包括框回归不稳定和参数初始化问题。重点讨论了如何处理不稳定的框回归并优化模型,以及通过IoU检测标准筛选模型的实践。
摘要由CSDN通过智能技术生成

Part1 数据

数据集通过网址 http://shuoyang1213.me/WIDERFACE/下载。

模型Pnet,Rnet 训练、测试选择使用的数据 WIDER FACE 。

WIDER FACE简介:数据标签只有bbox,没有landmark。注释:bbox [x1,y1,width,height]

模型Onet训练采用的数据通过网址http://mmlab.ie.cuhk.edu.hk/archive/CNN_FacePoint.htm 下载

CNN_FacePoint简介 :数据标签有bbox,以及landmark。注释:bbox[x1,x2,y1,y2]

landmark [x1,y1,x2,y2,x3,y3,x4,y4,x5,y5]

坑1:注意标签每个坐标含义。

Part2 模型训练

工程需要做一个人脸识别的东东,所以准备把人脸识别的每块内容都认真的走一遍,步骤大概是:人脸检测 - 人脸矫正 - 活体检测 - 质量评估 - 人脸识别  ,为了快速搞定,利用别人的代码就是最便捷的方法,自己选择pytorch的一个版本。网址为:https://github.com/Sierkinhane/mtcnn-pytorch

这里大概介绍一下mtcnn的工作原理

mtcnn网络结构如图1所示:

图1 

他是一种多任务级联的网络,每一个网络都包含人脸分类任务,边界框回归任务、人脸关键点回归任务。P-Net 主要用来产生候选框可能的位置,R-Net 再过滤掉一部分候选框,O-Net 产生最终的候选框和landmark位置。

注意:PNet 输出的是map格式,而Rnet和ONet的输出的是条状的,原因是因为实际测试时,PNet输入图片大小是不固定的,所以维度是不固定的不能接入全连接层。

测试时,一张图片先通过PNet,按照一个比例系数进行resize 得到图片金字塔,分别在每张图片上面进行边界框预测输出,预测的方法是根据class_map上大于阈值的保留下来,作为边界框的左上角坐标,坐标通过resize 相应的系数映射回原图,右下角的坐标=(坐上角的坐标*stirde+12)/scale。对应红色的部分。stride =2 猜测比如输入一张256大小的图片,最后输出类别、位置信息时图像大小变为123 ,原图是特征图的二倍关系。

boundingbox = np.vstack([np.round((stride * t_index[1]) / scale),            # x1 of prediction box in original image
                         np.round((stride * t_index[0]) / scale),            # y1 of prediction box in original image
                         np.round((stride * t_index[1] + cellsize) / scale), # x2 of prediction box in original image
                         np.round((stride * t_index[0] + cellsize) / scale), # y2 of prediction box in original image #reconstruct the box in original image
                         score,
                         reg])

reg 是网络输出框的相对偏移的位置信息,最终的坐标输出以x1为例,为x1 = np.round((stride * t_index[1]) / scale)+ (np.round((stride * t_index[1] + cellsize) / scale)-np.round((stride * t_index[1]) / scale))*reg[:,0]

 RNet输入是Pnet的输出,RNet主要会相对于Pnet输出位置进行调整。ONet同Rnet。landmark 输出的是相对位置,是相对boundingbox的相对位置坐标。

回到模型训练上,PNet,RNet,ONet模型的输入分别是12*12,24*24,48*48。输出的类别共包含0-负样本,1-正样本,-1-part样本就是部分含有正样本的数据,-2-landmark 样本。

需要注意的是模型训练的数据种类标签是否正确,否则训练的时候会出现某一个loss的梯度爆炸,原文作者代码的iou 计算输入有些问题,导致在训练ONet正负样本不均衡。

目前训练的PNet,RNet,ONet,比例大致是4:7:15 。

目前模型训练结果出现的问题:

1、框回归的不稳,抖动情况非常严重。这个问题目前没有解决。不知道该怎样优化。可能数据量还是不够。

2、关于参数初始化的问题,发现参数初始化不对loss降不太下去,现在每次训练都是在自己某一次实验结果上开始训练的。

Part3 模型测试

自己写了个测试程序,分别统计检出率和准确率。目前iou=0.65,检出和准确率在0.7以上。可以通过这个小程序进行模型筛选。代码如下:

import cv2
import torch
from mtcnn.core.detect import create_mtcnn_net, MtcnnDetector
import numpy as np
import os
import mtcnn.core.utils as utils

testfiles = "anno_train.txt"
testdata = "face_landmark/CNN_FacePoint"
label = []

showlabel = True

with open(testfiles, "r") as f:
    read = f.readlines()
print("test Data num is :", len(read))
pnet, rnet, onet = create_mtcnn_net(p_model_path="pnet_epoch.pt",
                                    r_model_path="rnet_epoch.pt",
                                    o_model_path="onet_epoch.pt",
                                    use_cuda = False)
mtcnn_detector = MtcnnDetector(pnet=pnet, rnet=rnet, onet=onet, min_face_size=12)

pos = 0
numsdetect = 0
for file in read:
    #print("file is :", file)
    file = file.strip().split(" ")
    img = cv2.imread(os.path.join(testdata, file[0].replace("\\", "/")))
    print(os.path.join(testdata, file[0].replace("\\", "/")))
    print("img.shape is :", img.shape)
    #print(file[0])
    boxes_align_r, bboxs, landmarks = mtcnn_detector.detect_face(img)
    gtbox = list(map(float, file[1:5]))
    print("gtbox is :", gtbox)
    gtlandmarks = list(map(float, file[5:]))
    numdetect = len(bboxs)
    numsdetect = numsdetect + numdetect
    gtbox = np.array(gtbox, dtype=np.float32).reshape(-1, 4)
    for box in bboxs:
        if showlabel:
            cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 0, 255), 2, 1)
            #cv2.rectangle(img, (int(gtbox[0][0]), int(gtbox[0][2])), (int(gtbox[0][1]), int(gtbox[0][3])), (255, 0, 0), 2, 1)
            p1 = int(gtbox[0][0])+int(gtbox[0][2])
            p2 = int(gtbox[0][1])+int(gtbox[0][3])
            print("p1", int(gtbox[0][0]), int(gtbox[0][1]),p1,p2)
            cv2.rectangle(img, (int(gtbox[0][0]), int(gtbox[0][1])), (p1, p2), (255, 0, 0),
                          2, 1)
        iou = utils.IoU1(box, gtbox)
        print("iou is :", iou)
        if iou > 0.65:

            pos = pos + 1
    if showlabel:
        cv2.imshow("img", img)
        cv2.waitKey(1000)
print("recall is : %s accuracy is : %s "%(pos/len(read),pos/numsdetect))

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值