MTCNN(Multi-task convolution network)
多任务卷积神经网络
https://www.bilibili.com/video/BV1i741177hd?p=2
https://www.bilibili.com/video/BV1fJ411C7AJ
https://github.com/bubbliiiing/mask-recognize
https://blog.csdn.net/weixin_44791964/article/details/103530206
用MTCNN网络识别人脸,结合代码记一篇学习笔记
1、MTCNN网络结构
MTCNN包含4个主要部分:图像金字塔、PNet、RNet、ONet
(1) 图像金字塔
首先,对于一张输入到网络中的图像, 利用图像金字塔得到不同尺度的图像,目的是检测到图像中不同大小的人脸。
构建图像金字塔代码如下:
def calculateScales(img):
# 把原图像的一条边按比例变为500,另一个按照相同比例缩放(对原图像进行标准化)
copy_img = img.copy()
pr_scale = 1.0
h,w,_ = copy_img.shape
if min(w,h) > 500:
# 如果宽高的较小值>500,宽和高都>500,
pr_scale = 500.0/min(h,w)
# 较短的边 缩小为500,另一个边按照相同的比例缩小
w = int(w*pr_scale)
h = int(h*pr_scale)
elif max(w,h) < 500:
# 如果宽高较大值<500,那么说明宽和高都小于500
pr_scale = 500.0/max(h,w)
# 较长的边 放大为500,另一边也按照相同的比例放大
w = int(w*pr_scale)
h = int(h*pr_scale)
scales = []
# 图像金字塔为了得到不同大小的图片,将标准化后的图像乘以factor这个缩放因子
# factor表示缩放因子
# 截止的条件就是:宽或高其中的一个值小于12
factor = 0.709
factor_count = 0
minl = min(h,w)
while minl >= 12:
# pow(x,y)表示x的y次方
# 只要宽和高中最小的值>=12,则乘以0.709
scales.append(pr_scale*pow(factor, factor_count))
minl *= factor
factor_count += 1
return scales
将输入的图片大小进行标准化:resize到500左右,把图片宽高中接近500的那条边转化为500,另一条边按照相同的比例进行缩放。那么另一种情况呢?宽和高一个大于500,一个小于500,那就不变,通常在这种情况下一张图片的宽高就与500接近。
scales
中存放的是图片缩小的尺度值。保留一张原图大小的图片,接着以0.709的缩放因子来缩小图片,到什么时候停止呢?缩小后的图片宽、高其中一个小于12,则停止。
这一步,得到了不同尺度的图片。称为图像金字塔。
(2)PNet(Proposal Network)
构建PNet网络结构的代码如下
def create_Pnet(weight_path):
# h, w, 3
input = Input(shape=[None, None, 3])
x = Conv2D(10, (3, 3), strides=1, padding='valid', name='conv1')(input)
x = PReLU(shared_axes=[1,2],name='PReLU1')(x)
x = MaxPool2D(pool_size=2)(x)
# h/2,w/2,10
x = Conv2D(16, (3, 3), strides=1, padding='valid', name='conv2')(x)
x = PReLU(shared_axes=[1,2],name='PReLU2')(x)
# h/2,w/2,32
x = Conv2D(32, (3, 3), strides=1, padding='valid', name='conv3')(x)
x = PReLU(shared_axes=[1,2],name='PReLU3')(x)
# 将h/2,w/2,32 分别进行通道数为2和通道数为4的卷积操作
# 可以看做将原图片划分成h/2,w/2,32的网格,
classifier = Conv2D(2, (1, 1), activation='softmax', name='conv4-1')(x) # 框中有人脸的置信度
# 无激活函数,线性。
bbox_regress = Conv2D(4, (1, 1), name='conv4-2')(x)# 有人脸的框的左上角和右下角的坐标
# Pnet输出的结果是很多人脸的候选框和框中存在人脸的置信度
model = Model([input], [classifier, bbox_regress])
model.load_weights(weight_path, by_name=True)
return model
PNet的输入是一张3通道的图片(h,w,3),
输出:classifier
框中有人脸的置信度
bbox_regress
有人脸的框对应的左上角和右下角的坐标
PNet的网络结构:
Conv2D 卷积核大小为3×3,输出通道数为10
激活函数PReLU
最大池化层MaxPool2D,宽高减半
输出(h/2,w/2,10)
Conv2D 卷积核大小为3×3,输出通道数为16
激活函数PReLU
输出(h/2,w/2,16)
Conv2D 卷积核大小为3×3,输出通道数为32
激活函数PReLU
输出(h/2,w/2,32)
classifier
:Conv2D卷积核大小为1×1,输出通道数为2,表示框中存在人脸的置信度
bbox_regress
Conv2D卷积核大小为1×1,输出通道数为4,表示存在人脸的框左上角和右下角的坐标。
(3)RNet(Refine Network)
RNet网络代码如下:
def create_Rnet(weight_path):
# resize成24,24,3
input = Input(shape=[24, 24, 3])
# 24,24,3 -> 11,11,28
x = Conv2D(28, (3, 3), strides=1, padding='valid', name='conv1')(input)
x = PReLU(shared_axes=[1, 2