深刻理解MTCNN原理, 超级详细,从零开始做人脸检测。Tensorflow2实现

本文详细介绍了MTCNN(多任务级联卷积网络)的工作原理,从简单的物体检测概念出发,通过滑动窗口方法解释了如何处理多物体检测的问题。接着,文章详细探讨了MTCNN的三个组成部分——PNET、RNET和ONET,以及它们在人脸检测中的应用,包括图像预处理、网络结构和输出结果的解析。通过实例和代码,作者帮助读者理解MTCNN如何识别和定位人脸。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:https://www.jianshu.com/p/5495c4b87cf8?ivk_sa=1024320u

前言

水了几篇不痛不痒的博客,于是决定把mtcnn的原理记录下,分享给想要学习人脸识别,但是很纠结如何开始的人。网上关于mtcnn的教程大同小异,不同不痒,认真看完此文,如果还不会mtcnn,那么请你来掐死我


来看看效果

关于检测

检测,顾名思义就是找到我们需要的物体,并且标注他在图像中的位置。
  • 从mnist数据集开始

  • 大多数朋友上有的第一个数据集,有着深度学习的hello world之称!我们先看看数据集的样子:

经过我处理后可视化的mnist数据集的一部分

经过处理后(可能以后我会出文如何处理得到这样的图片),我把里面的零拿了出来,每张图片是28x28大小,都有一个0~9之间的数字

今天不讲如何处理mnist,稍微提一下,我们把每张有数字的图片放到 神经网络中,然后会输出一个结果告诉我们这张图片是数字几,这样就做到了物体的识别

同样地,我们可以把0~9的数字图片换成其他,例如猫狗,那我们就可以训练得到区分猫还是狗的神经网络。

import tensorflow as tf
(train_images, train_labels),(test_images, test_labels)= tf.keras.datasets.mnist.load_data()
print(train_images.shape)# 6000,28,28,1


  • 出现问题
    貌似我们可以用这个思路区分其他的物体,可是,看mnist数据集的图片我们发现,每张图片只有一个数字,也就是,我们训练好的网络,将来也只能识别单个物体,如果要识别的图片有两个数字,我们需要向办法将两个数字分开,分别输入到网络进行预测。那我们有没有办法让我们训练的网络一次可以识别多个物体呢?

  • 解决

滑动窗口

先看张图片:

有人有动物,很唯美很和谐

如果直接把图片输入神经网络预测可行吗?显然,大多数情况下是不可以直接传入神经网络获得预测结果的。

把一张图片分成许多张小图片

像这样,我们把图片分成许多小图片,然后每张小图片截取下来,上图我们可以得到8张小图片,我们将这8张图片一次传入网络中判断那个小格子里面是什么物体。问题解决了吗?我们可以看到,小动物的头我们可以找到,可是小女孩的头部被分成了两张图片了,显然这样子我们找不到小女孩。此时,我们可以利用滑动窗口的方法

图片有点乱,慢慢看~~

最左上角的红色框框就是我们选定的框框,我们先把红色框框的图片截取下来传入神经网络中预测,然后我们把红色框框向右平移,我们得到粉红色的框框,然后我们再把粉红色框框的图片截取出来送到网络中预测,以此类推我们每次将框框向右移动一定的长度,然后获得下一个框框,当框框移动到第一行的最后时候,也就是和黑色框框重合的时候,第一行我们取完了,然后将框框向下平移,此时当作第二行,简单总结就是将框框按照一定的长度平移,遍历整张图片

此刻,我们可也找到小女孩和小动物的位置。

我们暂且叫这个方法框框滑动理论

我们来看看这个方法有什么有缺点:

优点是可以找到一幅图像的多个物体,还能找到大致位置,缺点也很明显,越是想要找到多的物体,那么框框移动的距离就要小,当移动的距离越小,那么图片越多,识别的效率就降低。

MTCNN

我们看了一下单个物体的检测和多个物体的检测,接下来是有关人脸的检测建议看看原论文

  • MTCNN由三个级联的网络组成,分别是PNET,RNET,ONET

图片经过预处理,先经过pnet网络,将结果给rnet网络,rnet网络的输出再传入onet网络,onet最后得到输出结果
  • Pnet

pnet网络结构

不需要看懂,我们看最左边的正方形,下面标注12x12x3,没错这个就是框框的大小,就是我们我们上面找到小女孩的框框,长宽都是12个像素,最后面的3代表图像是三通道的,也就是RGB彩色图像。我们将用12x12大小的框框来找人脸(重点),这里框框每次移动的距离是两个像素(不展开论述)

  • 小朋友你现在是否有很多问号

  • 12x12不会太小了吗?我的脸肯定比12x12大呀QvQ

别急,听我娓娓道来。

论文对要进行检测的图像做了图像金字塔

图像金字塔

大家在脑海里想一想 金字塔长什么样子,尖尖的,对吧!其实就是将图像进行一定比例,一系列的 缩放。。。 (瞬间low了不少)

缩放的极限是到12x12,再小下去的话,框框都比图像大了,哪怕你再大的头,总会有个缩放比例,把你的头变小的,此刻应该有一波掌声,原论文就是妙.不妨给我点个小赞,加个关注

下面我将验证我的框框滑动理论,将有一大波代码来袭,请注意—_—

import cv2  # opencv库
import tensorflow as tf
import numpy as np

下面是网络结构

defPnet():
    input= tf.keras.Input(shape=[None,None,3])
    x = tf.keras.layers.Conv2D(10,(3,3), strides=1, padding='valid', name='conv1')(input)
    x = tf.keras.layers.PReLU(shared_axes=[1,2], name='PReLU1')(x)
    x = tf.keras.layers.MaxPooling2D()(x)
    x = tf.keras.layers.Conv2D(16,(3,3), strides=1, padding='valid', name='conv2')(x)
    x = tf.keras.layers.PReLU(shared_axes=[1,2], name='PReLU2')(x)
    x = tf.keras.layers.Conv2D(32,(3,3), strides=1, padding='valid', name='conv3')(x)
    x = tf.keras.layers.PReLU(shared_axes=[1,2], name='PReLU3')(x)
    classifier = tf.keras.layers.Conv2D(2,(1,1), activation='softmax',name='conv4-1')(x)
    bbox_regress = tf.keras.layers.Conv2D(4,(1,1), name='conv4-2')(x)
    model = tf.keras.models.Model([input],[classifier, bbox_regress])
    model.summary()
    return model
model = Pnet()# 读取网络结构
model.load_weights("./pnet.h5", by_name=True)# 读取预训练权重,此步骤可以省略
讲讲我的思路,我将准备一张 12x12x3大小的人脸图片传入网络,看看输出结果,之后,我在这张图片的最后边添加两列大小全为255的像素点,底下也添加两行,此时图片变成14x14
img = cv2.imread("./face1.jpg")# 用opencv的方法把图像读取进来
img = cv2.cvtColor(img, cv2.COLOR_BRG2RGB)
img =(img-127.5)/127.5# 归一化,不展开
img = img.reshape(1,*img.shape)# shape=1x12x12x3 只有四维才符合输入格式
  • 图像处理完毕,接下来传入网络进行预测,我们看看会得到什么结果

out = model.predict(img)

结果

我们可以看到两个array,为什么会这样?我们回到pnet的网络结构图:

pnet结构

这次我们看到最右边,从上到下一次是face classificationboundingbox regressinoFacial landmark localization也就是人脸概率边框回归标记点,简单来说就是会输出三个结果,第一个结果的大小是1x1x2的这个代表是否是人脸的概率也就对应我们得到结果的第一个array:

第一个array

这个数组有两个值,第一个代表非人脸的概率,第二个值代表人脸的概率,我们很清晰看到第二值约等于0.9926633,换成百分比就是百分之九九,我们的网络说这个框框有99的概率是人脸!!!先别激动,我们看看第二个数组:

第二个array

这个array有四个值,也就是对应网络结构中的boundingbox regression,它的shape是1x1x4,也就对应我们第二个数组的四个输出值,这四个值可以姑且当作offset(偏移量), 分别对应左上角坐标x和y值的偏移量,右下角坐标x和y的偏移量,用原来的坐标加上坐标对应的偏移量的数值,就可以得到我们网络预测的人脸框的大小。

对于最后的Facial landmark, pnet代码中的代码中没有体现,所以就没有输出结果。

如果没有看明白,可以联系我,讨论学习。

  • 接下来让我把图片处理一下,变成14x14大小

为了方便看,我在右边和下边加了两条绿色的像素,接着我们把这张图片传入网络看看是什么效果:

结果

看着有点小复杂,不荒,我们研究一下:

同样只有两个array,第一个array,里面是4x2的矩阵第二个array是4x4的矩阵

为什么是4x2和4x4?

  • 当我们输入的大小是12x12的时候,输出是1x2和1x4

  • 当我们输入的大学是14x14的时候,输出是4x2和4x4

由此猜测,当输入16x16的时候,输出结果是9x2和9x4,感兴趣可以试试,一定是这个结果或者说应该是3x3x2和3x3x4

  • 所以,14x14,输出大小准确点应该是2x2x2和2x2x4。(有点小复杂)

画重点

也就是说,14x14的图片被分成了4张12x12的小图片被传入了网络,得到的结果在组合起来,因为框框的大小是12x12,所以可以去到左上角一张图片右上角一张图片,左下角和右下角的图片,有4张,并且位置对应2x2,所以才有这个结果

有点饶,多看几次

换个角度

我上面已经得到了14x14的图片,我安下面方式截取四张图片

也就是对应于原图的左上右上左下右下各取12x12大小出来,依次传入网络中

左上

右上

左下

右下

下面我放一下直接传入14x14的原图的输出结果:

结果

自行比对一下,我不用多说


小结

通过这种叙事方式,我还没有看到叙事得比我详细的mtcnn讲解,限于篇幅原因,我不会放大量代码,也只是讲一下我当时最难以理解的地方,后面会考虑出源码的解析(当然是我自己重构后的代码),剩余部分我会后面更新完,不过我觉得到这里已经茶并不多了,后面大同小异,最难的部分已经过去了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值