MTCNN代码解读

首先了解MTCNN算法

理论基础:
在这里插入图片描述

正如上图所示,该MTCNN由3个网络结构组成(P-Net,R-Net,O-Net)。

Proposal Network (P-Net):该网络结构主要获得了人脸区域的候选窗口和边界框的回归向量。并用该边界框做回归,对候选窗口进行校准,然后通过非极大值抑制(NMS)来合并高度重叠的候选框。

Refine Network (R-Net):该网络结构还是通过边界框回归和NMS来去掉那些false-positive区域。

只是由于该网络结构和P-Net网络结构有差异,多了一个全连接层,所以会取得更好的抑制false-positive的作用。

Output Network (O-Net):该层比R-Net层又多了一层卷基层,所以处理的结果会更加精细。作用和R-Net层作用一样。但是该层对人脸区域进行了更多的监督,同时还会输出5个地标(landmark)。
详细的网络结构如下图所示:
在这里插入图片描述
prototxt的更加详细的网络结构如下:分别为det1,det2,det3。

det1.prototxt结构:
在这里插入图片描述
det2.prototxt结构:
在这里插入图片描述
det3.prototxt结构:
在这里插入图片描述

其次说明几个函数

1、function B = imResample( A, scale, method, norm )

  • A:输入图片(二维或者三维图像数据)
  • scale:缩放比例
  • method:缩放的方法[‘bilinear’]
  • norm:optionally multiply every output pixel by norm
  • B:输出被缩放后的图像数据
    简单理解在mtcnn中这个函数是将输入的图片按照一定的比例将图片进行缩放。

2、function [boundingbox reg] = generateBoundingBox(map,reg,scale,t)

  • map:感受野的内容被认为是人脸的概率矩阵
  • reg:不是坐标,而是坐标的回归量。
  • scale:缩放比例
  • t:threshold的第一个值,大于这个值的认为这个感受野是人脸区域。
  • boundingbox:输出的矩形框。(举例如下)
    在这里插入图片描述
    这个函数的目的是判断哪些感受野区域是人脸。其步骤为
    (1)先驱逐map中大于阈值的值,并且取出相对应的置信度,坐标回归量和在特征值中的位置(x,y)
    (2)(x,y)的值是在特征值中的相对位置(对应原图的左上角的坐标),根据PNet的网络结构,12*12是它的感受野,对应回到原图所在的位置,并返回图片的真实坐标。
    (3)计算图片的右下角坐标,返回所有符合阈值条件的矩形区域

3、function pick = nms(boxes,threshold,type):非极大值抑制算法

  • boxes:generateBoundingBox函数返回的候选矩形框。
  • threshold:IoU 的阈值,这里设置为0.5。
  • type:这里设置为“Union”模式。模式的计算算方法是: o = s2/(s3+s2-s1),o为IoU得分。
    在这里插入图片描述
    具体算法流程如下:
    比如人脸识别的一个例子:
    已经识别出了 5 个候选框,但是我们只需要最后保留两个人脸。
    在这里插入图片描述
    首先选出分数最大的框(0.98),然后遍历剩余框,计算 IoU,会发现露丝脸上的两个绿框都和 0.98 的框重叠率很大,都要去除。
    在这里插入图片描述
    然后只剩下杰克脸上两个框,选出最大框(0.81),然后遍历剩余框(只剩下0.67这一个了),发现0.67这个框与 0.81 的 IoU 也很大,去除。

4、function [bboxA] = rerec(bboxA):将矩形框变换为正方形框

  • bboxA:输入输出矩形框
    这个函数的目的是将矩形框变换成正方形框,正方形的边长取矩形的长边

5、function [dy edy dx edx y ey x ex tmpw tmph] = pad(total_boxes,w,h)

  • total_boxes:所有的候选框
  • w:原图的宽度
  • h:原图的长度
  • x:候选框的左上角x坐标
  • y:候选框的左上角y坐标
  • ex:候选框的右下角x坐标
  • ey:候选框的与下角y坐标
  • dx:经过对齐之后的候选框左上角x坐标
  • dy:经过对齐之后的候选框左上角y坐标
  • edx:修改之后的候选框宽度w
  • edy:修改之后的候选框长度h
  • tmpw:候选框的宽度
  • tmph:候选框的长度
    这个函数的目的是将那些坐标的值大于或者小于原图尺寸的候选框的坐标值修改为对应原图位置的边界坐标,如图所示:
    在这里插入图片描述
    6、function [boundingbox] = bbreg(boundingbox,reg):校准边界框
  • 这一步跟MTCNN的训练设置有关,理解起来有些费劲,但是单单看函数的操作很好实现,其主要的步骤就是这样一个语句:boundingbox(:,1:4)=[boundingbox(:,1)+reg(:,1).*w boundingbox(:,2)+reg(:,2).*h boundingbox(:,3)+reg(:,3).*w boundingbox(:,4)+reg(:,4).*h];其中h为候选框的长度,w为候选框的宽度。

接下来是最重要的人脸检测的主要函数:detect_face()

1、首先代码的第一步是建立图像金字塔:
他的做法非常简单就是将原图尺寸反复乘以缩放因子,直到达到最小的尺寸为止,这样做的目的是不断缩放图片,以便得到更多的boundingbox。
2、将得到的不同尺寸的图片放入到PNet网络中做前向运算
这一步的做法是将上一步骤得到的N个缩放后的图片一次放到PNet网络中做前向运算,并且得到最可能是人脸的部分的boundingbox区域的集合total_boxes。
3、RNet对第二步得到的boundingbox(候选框)做进一步的判断
(1)首先按照每个候选框坐标信息在原图中获取数据,并且将所有的数据imResample到(24,24)的尺寸,方便送入到RNet网络中做前向运算。
(2)将(1)中得到的所有N个拥有(24,24)的感受野的图像数据送入到RNet网络做前向运算,得到感受野是人脸的概率,坐标的回归量两个变量。
(3)根据感受野是人脸的概率,坐标的回归量两个变量这两个量做进一步处理,筛选掉一部分候选框。
4、ONet对RNet的输出作进一步处理,得到唯一的候选框
(1)首先按照每个候选框坐标信息在原图中获取数据,并且将所有的数据imResample到(48,48)的尺寸,方便送入到ONet网络中做前向运算。
(2)将(1)中得到的所有N个拥有(48,48)的感受野的图像数据送入到RNet网络做前向运算,得到感受野是人脸的概率,坐标的回归量两个变量。
(3)根据感受野是人脸的概率,坐标的回归量两个变量这两个量做进一步处理,筛选掉一部分候选框,得到唯一的人脸框。
(4)根据ONet的五个特征点坐标值的输出,按照代码对应关系,得到原图对应的五个关键点的坐标值。

最后输出显示

在这里插入图片描述

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MTCNN(Multi-task Cascaded Convolutional Networks)是一种用于人脸检测的深度神经网络。MTCNN使用了一个三级级联的CNN进行人脸检测。以下是MTCNN代码实现。 首先,需要导入必要的库和模型: ```python import cv2 import numpy as np from keras.models import load_model PNet = load_model('PNet.h5') RNet = load_model('RNet.h5') ONet = load_model('ONet.h5') ``` 接下来,定义一个函数来进行人脸检测: ```python def detect_faces(image): img = image.copy() img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) height, width = img.shape[:2] # 图像预处理 img_resized = cv2.resize(img, (int(width/2), int(height/2))) img_resized = (img_resized - 127.5) / 128.0 img_resized = np.expand_dims(img_resized, axis=0) # PNet 预测 threshold = 0.7 scale_factor = 0.709 scales = [] bounding_boxes = [] while min(img_resized.shape[:2]) > 12: scales.append(np.array([img_resized.shape[1]/width, img_resized.shape[0]/height])) output = PNet.predict(img_resized) cls_prob = output[0][:,:,1] bbox_pred = output[1] indices = np.where(cls_prob > threshold) indices = np.stack(indices, axis=1) if indices.shape[0] == 0: img_resized = cv2.resize(img_resized, (int(img_resized.shape[1]*scale_factor), int(img_resized.shape[0]*scale_factor))) continue for index in indices: xmin = index[1] * 2 ymin = index[0] * 2 xmax = index[1] * 2 + 12 ymax = index[0] * 2 + 12 score = cls_prob[index[0], index[1]] offset = bbox_pred[index[0], index[1]] bounding_boxes.append([xmin, ymin, xmax, ymax, score, offset]) img_resized = cv2.resize(img_resized, (int(img_resized.shape[1]*scale_factor), int(img_resized.shape[0]*scale_factor))) # NMS 处理 bounding_boxes = np.array(bounding_boxes) keep = nms(bounding_boxes[:, :5], 0.5) bounding_boxes = bounding_boxes[keep] # RNet 预测 threshold = 0.7 scales = [] for b in bounding_boxes: w = b[2] - b[0] h = b[3] - b[1] size = max(w, h) img_sub = img[b[1]:b[3], b[0]:b[2]] img_resized = cv2.resize(img_sub, (24, 24)) img_resized = (img_resized - 127.5) / 128.0 img_resized = np.expand_dims(img_resized, axis=0) scales.append(size/24) output = RNet.predict(img_resized) cls_prob = output[0][:,1] bbox_pred = output[1] if cls_prob > threshold: score = cls_prob offset = bbox_pred xmin = int(b[0] + offset[0]*w) ymin = int(b[1] + offset[1]*h) xmax = int(b[2] + offset[2]*w) ymax = int(b[3] + offset[3]*h) bounding_boxes.append([xmin, ymin, xmax, ymax, score]) # NMS 处理 bounding_boxes = np.array(bounding_boxes) keep = nms(bounding_boxes[:, :5], 0.7, 'iom') bounding_boxes = bounding_boxes[keep] # ONet 预测 threshold = 0.7 faces = [] for b in bounding_boxes: w = b[2] - b[0] h = b[3] - b[1] size = max(w, h) img_sub = img[b[1]:b[3], b[0]:b[2]] img_resized = cv2.resize(img_sub, (48, 48)) img_resized = (img_resized - 127.5) / 128.0 img_resized = np.expand_dims(img_resized, axis=0) output = ONet.predict(img_resized) cls_prob = output[0][:,1] bbox_pred = output[1] landmark_pred = output[2] if cls_prob > threshold: score = cls_prob offset = bbox_pred landmark = landmark_pred xmin = int(b[0] + offset[0]*w) ymin = int(b[1] + offset[1]*h) xmax = int(b[2] + offset[2]*w) ymax = int(b[3] + offset[3]*h) x1 = xmin + landmark[0]*w y1 = ymin + landmark[1]*h x2 = xmin + landmark[2]*w y2 = ymin + landmark[3]*h x3 = xmin + landmark[4]*w y3 = ymin + landmark[5]*h x4 = xmin + landmark[6]*w y4 = ymin + landmark[7]*h faces.append([xmin, ymin, xmax, ymax, score, x1, y1, x2, y2, x3, y3, x4, y4]) return faces ``` 其中,nms函数实现了非极大值抑制: ```python def nms(dets, thresh, method='union'): x1 = dets[:, 0] y1 = dets[:, 1] x2 = dets[:, 2] y2 = dets[:, 3] scores = dets[:, 4] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.argsort()[::-1] keep = [] while order.size > 0: i = order[0] keep.append(i) xx1 = np.maximum(x1[i], x1[order[1:]]) yy1 = np.maximum(y1[i], y1[order[1:]]) xx2 = np.minimum(x2[i], x2[order[1:]]) yy2 = np.minimum(y2[i], y2[order[1:]]) w = np.maximum(0.0, xx2 - xx1 + 1) h = np.maximum(0.0, yy2 - yy1 + 1) inter = w * h if method == 'union': ovr = inter / (areas[i] + areas[order[1:]] - inter) elif method == 'min': ovr = inter / np.minimum(areas[i], areas[order[1:]]) else: print('Unknown nms method!') inds = np.where(ovr <= thresh)[0] order = order[inds + 1] return keep ``` 最后,可以使用以下代码来进行人脸检测: ```python image = cv2.imread('test.jpg') faces = detect_faces(image) for face in faces: xmin, ymin, xmax, ymax = face[:4] cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2) for i in range(5, 13, 2): x, y = int(face[i]), int(face[i+1]) cv2.circle(image, (x, y), 2, (0, 0, 255), -1) cv2.imshow('image', image) cv2.waitKey(0) cv2.destroyAllWindows() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值