【图像处理】 -041 MTCNN+DCNN人脸检测
1 简介
相比于R-CNN系列通用检测方法,本文更加针对人脸检测这一专门的任务,速度和精度都有足够的提升。R-CNN,Fast R-CNN,FasterR-CNN这一系列的方法不是一篇博客能讲清楚的,有兴趣可以找相关论文阅读。类似于TCDCN,本文提出了一种Multi-task的人脸检测框架,将人脸检测和人脸特征点检测同时进行。论文使用3个CNN级联的方式,和Viola-Jones类似,实现了coarse-to-fine的算法结构。
当给定一张照片的时候,将其缩放到不同尺度形成图像金字塔,以达到尺度不变。
-
Stage 1:使用P-Net是一个全卷积网络,用来生成候选窗和边框回归向量(bounding box regression vectors)。使用Bounding box regression的方法来校正这些候选窗,使用非极大值抑制(NMS)合并重叠的候选框。全卷积网络和Faster R-CNN中的RPN一脉相承。
-
Stage 2:使用N-Net改善候选窗。将通过P-Net的候选窗输入R-Net中,拒绝掉大部分false的窗口,继续使用Bounding box regression和NMS合并。
-
Stage 3:最后使用O-Net输出最终的人脸框和特征点位置。和第二步类似,但是不同的是生成5个特征点位置。
2 C++实现
#include <opencv2/opencv.hpp>
#include "mtcnn.h"
#include "HighPerformanceTimer.hpp"
#include <fstream>
using namespace cv;
#define MAXFACEOPEN 0 //设置是否开关最大人脸调试,1为开,其它为关
//读取待检测文件列表
std::vector<std::string> ReadImgList(std::string& imglistfilename)
{
std::vector<std::string> imgs;
std::ifstream imglistfile(imglistfilename, std::ifstream::in);
std::string line;
while (getline(imglistfile, line))//按行读取
{
imgs.push_back(line);
}
return imgs;
}
int main(int argc, char** argv) {
if (argc < 3)
{
std::cout << "Please use this exe like this:" << std::endl;
std::cout << "OpenCV_Harrx.exe imglist.txt outputpath" << std::endl;
system("pause");
}
std::string imglistfile(argv[1]);
std::string outputpath(argv[2]);
std::vector<std::string> imgs = ReadImgList(imglistfile);
char *model_path = "./models";
MTCNN mtcnn(model_path);
char* pTname = (char*)"timer";
CHighPerformanceTimer* pTimer = new CHighPerformanceTimer(pTname, 6, true);
std::ofstream of(outputpath, std::ofstream::out);
for (int i = 0; i < imgs.size(); i++)
{
cv::Mat image = cv::imread((char*)imgs[i].c_str());
pTimer->Reset();
ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(image.data, ncnn::Mat::PIXEL_BGR2RGB, image.cols, image.rows);
std::vector<Bbox> finalBbox;
#if(MAXFACEOPEN==1)
mtcnn.detectMaxFace(ncnn_img, finalBbox);
#else
mtcnn.detect(ncnn_img, finalBbox);
#endif
double dt = pTimer->GetTime();
const int num_box = finalBbox.size();
std::vector<cv::Rect> bbox;
bbox.resize(num_box);
of << imgs[i] << " " << dt << "s " << bbox.size() << " ";
std::cout << imgs[i] << " " << dt << "s " << bbox.size() << " ";
for (int i = 0; i < num_box; i++) {
bbox[i] = cv::Rect(finalBbox[i].x1, finalBbox[i].y1, finalBbox[i].x2 - finalBbox[i].x1 + 1, finalBbox[i].y2 - finalBbox[i].y1 + 1);
of << bbox[i];
std::cout << bbox[i];
for (int j = 0; j < 5; j = j + 1)
{
cv::circle(image, cv::Point(finalBbox[i].ppoint[j], finalBbox[i].ppoint[j + 5]), 2, CV_RGB(0, 255, 0), cv::FILLED);
}
}
for (vector<cv::Rect>::iterator it = bbox.begin(); it != bbox.end(); it++)
{
rectangle(image, (*it), Scalar(0, 0, 255), 2, 8, 0);
}
of << std::endl;
std::cout << std::endl;
imshow("face_detection", image);
cv::waitKey(0);
}
of.close();
return 0;
}
#pragma once
#ifndef __MTCNN_NCNN_H__
#define __MTCNN_NCNN_H__
#include "net.h"
//#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include <time.h>
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
//using namespace cv;
struct Bbox
{
float score;
int x1;
int y1;
int x2;
int y2;
float area;
float ppoint[10];
float regreCoord[4];
};
class MTCNN {
public:
MTCNN(const string &model_path);
MTCNN(const std::vector<std::string> param_files, const std::vector<std::string> bin_files);
~MTCNN();
void SetMinFace(int minSize);
void detect(ncnn::Mat& img_, std::vector<Bbox>& finalBbox);
void detectMaxFace(ncnn::Mat& img_, std::vector<Bbox>& finalBbox);
// void detection(cons