MTCNN人脸检测 附完整C++代码
人脸检测 识别一直是图像算法领域一个主流话题。
前年 SeetaFace 开源了人脸识别引擎,一度成为热门话题。
虽然后来SeetaFace 又放出来 2.0版本,但是,我说但是。。。
没有训练代码,想要自己训练一下模型那可就犯难了。
虽然可以阅读源码,从前向传播的角度,反过来实现训练代码,
但是谁有那个闲功夫和时间,去折腾这个呢?
有的时候还是要站在巨人的肩膀上,你才能看得更远。
而SeetaFace 不算巨人,只是当年风口上的猪罢了。
前年,为了做一个人脸项目,也是看遍了网上各种项目。
林林总总,各有优劣。
不多做评价,很多东西还是要具体实操,实战才能见真知。
有一段时间,用SeetaFace的人脸检测来做一些小的演示demo,
也花了一点小时间去优化它的算法。
不过很明显我只是把他当成玩具看待。
毕竟不能自己训练模型,这是很大的诟病。
直到后来深度学习大放异彩,印象最深刻莫过于MTCNN。
Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Neural Networks
相关资料见:https://github.com/kpzhang93/MTCNN_face_detection_alignment
大合照下,人脸圈出来很准确,壮观了去,这是第一印象。
上图,大家感受一下。
MTCNN的有三个网络结构。
Stage1: Proposal Net

Stage2: Refine Net

Stage3: Output Net

具体算法思路就不展开了。
我对MTCNN感兴趣的点在于,
MTCNN的思路可以拓展到各种物体检测和识别方向。
也许唯一缺少的就是打标好的数据,
而标注五个点,足够用于适配大多数物体了。
符合小而美的理念,这个是我比较推崇的。
所以MTCNN是一个很值得品味的算法。
github上也有不少MTCNN的实现和资源。
基于mxnet 基于caffe 基于ncnn 等等。。。
很明显,mxnet 和 caffe 不符合小而美的理念。
果断抛弃了。
ncnn有点肥大,不合我心。
所以,我动了杀气。。
移除NCNN 与mtcnn无关的层,
梳理ncnn的一些逻辑代码。
简单做了一些适配和优化。
砍掉一些边边角角。
不依赖opencv等第三方库。
编写示例代码完成后,还有不少工作要做,
不过第一步感觉已经符合我的小小预期。
完整示例代码:
#include "mtcnn.h"
#include "browse.h"
#define USE_SHELL_OPEN
#ifndef nullptr
#define nullptr 0
#endif
#if defined(_MSC_VER)
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#else
#include <unistd.h>
#endif
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION #include "stb_image.h"
//ref:https://github.com/nothings/stb/blob/master/stb_image.h
#define TJE_IMPLEMENTATION #include "tiny_jpeg.h"
//ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h #include <stdint.h>
#include "timing.h" char saveFile[]; unsigned char *loadImage(const char *filename, int *Width, int *Height, int *Channels) {
return stbi_load(filename, Width, Height, Channels, );
} void saveImage(const char *filename, int Width, int Height, int Channels, unsigned char *Output) {
memcpy(saveFile + strlen(saveFile), filename, strlen(filename));
*(saveFile + strlen(saveFile) + ) = ;
//保存为jpg
if (!tje_encode_to_file(saveFile, Width, Height, Channels, true, Output)) {
fprintf(stderr, "save JPEG fail.\n");
return;
} #ifdef USE_SHELL_OPEN
browse(saveFile);
#endif
} void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) {
const char *end;
const char *p;
const char *s;
if (path[] && path[] == ':') {
if (drv) {
*drv++ = *path++;
*drv++ = *path++;
*drv = '\0';
}
}
else if (drv)
*drv = '\0';
for (end = path; *end && *end != ':';)
end++;
for (p = end; p > path && *--p != '\\' && *p != '/';)
if (*p == '.') {
end = p;
break;
}
if (ext)
for (s = end; (*ext = *s++);)
ext++;
for (p = end; p > path;)
if (*--p == '\\' || *p == '/') {
p++;
break;
}
if (name) {
for (s = p; s < end;)
*name++ = *s++;
*name = '\0';
}
if (dir) {
for (s = path; s < p;)
*dir++ = *s++;
*dir = '\0';
}
} void getCurrentFilePath(const char *filePath, char *saveFile) {
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
splitpath(filePath, drive, dir, fname, ext);
size_t n = strlen(filePath);
memcpy(saveFile, filePath, n);
char *cur_saveFile = saveFile + (n - strlen(ext));
cur_saveFile[] = '_';
cur_saveFile[] = ;
} void drawPoint(unsigned char *bits, int width, int depth, int x, int y, const uint8_t *color) {
for (int i = ; i < min(depth, ); ++i) {
bits[(y * width + x) * depth + i] = color[i];
}
} void drawLine(unsigned char *bits, int width, int depth, int startX, int startY, int endX, int endY,
const uint8_t *col) {
if (endX == startX) {
if (startY > endY) {
int a = startY;
startY = endY;
endY = a;
}
for (int y = startY; y <= endY; y++) {
drawPoint(bits, width, depth, startX, y, col);
}
}
else {
float m = 1.0f * (endY - startY) / (endX - startX);
int y = ;
if (startX > endX) {
int a = startX;
startX = endX;
endX = a;
}
for (int x = startX; x <= endX; x++) {
y = (int)(m * (x - startX) + startY);
drawPoint(bits, width, depth, x, y, col);
}
}
} void drawRectangle(unsigned char *bits, int width, int depth, int x1, int y1, int x2, int y2, const uint8_t *col) {
drawLine(bits, width, depth, x1, y1, x2, y1, col);
drawLine(bits, width, depth, x2, y1, x2, y2, col);
drawLine(bits, width, depth, x2, y2, x1, y2, col);
drawLine(bits, width, depth, x1, y2, x1, y1, col);
} int main(int argc, char **argv) {
printf("mtcnn face detection\n");
printf("blog:http://cpuimage.cnblogs.com/\n"); if (argc < ) {
printf("usage: %s model_path image_file \n ", argv[]);
printf("eg: %s ../models ../sample.jpg \n ", argv[]);
printf("press any key to exit. \n");
getchar();
return ;
}
const char *model_path = argv[];
char *szfile = argv[];
getCurrentFilePath(szfile, saveFile);
int Width = ;
int Height = ;
int Channels = ;
unsigned char *inputImage = loadImage(szfile, &Width, &Height, &Channels);
if (inputImage == nullptr || Channels != ) return -;
ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(inputImage, ncnn::Mat::PIXEL_RGB, Width, Height);
std::vector<Bbox> finalBbox;
MTCNN mtcnn(model_path);
double startTime = now();
mtcnn.detect(ncnn_img, finalBbox);
double nDetectTime = calcElapsed(startTime, now());
printf("time: %d ms.\n ", (int)(nDetectTime * ));
int num_box = finalBbox.size();
printf("face num: %u \n", num_box);
for (int i = ; i < num_box; i++) {
const uint8_t red[] = { , , };
drawRectangle(inputImage, Width, Channels, finalBbox[i].x1, finalBbox[i].y1,
finalBbox[i].x2,
finalBbox[i].y2, red);
const uint8_t blue[] = { , , };
for (int num = ; num < ; num++) {
drawPoint(inputImage, Width, Channels, (int)(finalBbox[i].ppoint[num] + 0.5f),
(int)(finalBbox[i].ppoint[num + ] + 0.5f), blue);
}
}
saveImage("_done.jpg", Width, Height, Channels, inputImage);
free(inputImage);
printf("press any key to exit. \n");
getchar();
return ;
}
效果图来一个。

项目地址:
https://github.com/cpuimage/MTCNN
参数也很简单,
mtcnn 模型文件路径 图片路径
例如: mtcnn ../models ../sample.jpg
用cmake即可进行编译示例代码,详情见CMakeLists.txt。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com
MTCNN人脸检测 附完整C++代码的更多相关文章
- 音频自动增益 与 静音检测 算法 附完整C代码
前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...
- 音频自动增益 与 静音检测 算法 附完整C代码【转】
转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...
- 项目实战 - 原理讲解<-> Keras框架搭建Mtcnn人脸检测平台
Mtcnn它是2016年中国科学院深圳研究院提出的用于人脸检测任务的多任务神经网络模型,该模型主要采用了三个级联的网络,采用候选框加分类器的思想,进行快速高效的人脸检测.这三个级联的网络分别是快速生成 ...
- 基于RNN的音频降噪算法 (附完整C代码)
前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...
- 音频降噪算法 附完整C代码
降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...
- mser 最大稳定极值区域(文字区域定位)算法 附完整C代码
mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...
- 经典傅里叶算法小集合 附完整c代码
前面写过关于傅里叶算法的应用例子. <基于傅里叶变换的音频重采样算法 (附完整c代码)> 当然也就是举个例子,主要是学习傅里叶变换. 这个重采样思路还有点瑕疵, 稍微改一下,就可以支持多通 ...
- 自动曝光修复算法 附完整C代码
众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...
- 基于傅里叶变换的音频重采样算法 (附完整c代码)
前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...
随机推荐
- Android的ANR详解(原因和方案)
ANR的定义 在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框 ...
- 与信号相关的linux系统编程API
1. kill(pid_t pid, int sig); //给指定的进程发送sig信号 raise(int sig); //给当前进程发送sig信号2. 处理指定的信号 typedef v ...
- iOS中 基于LBXScan库二维码扫描 韩俊强的博客
每日更新关注:http://weibo.com/hanjunqiang 新浪微博 首先声明这个二维码扫描是借助于zxing. 功能模块都完全封装好了,不过界面合你口味,直接使用就好,如果不合口味,后 ...
- [译]百里挑一:21个优质Swift开源App
Mybridge AI根据代码质量和start排名从900多个开源项目中选出21个开源项目. 1:Firefox iOS [Official] Firefox iOS app built in Swi ...
- Mybatis源码之Statement处理器SimpleStatementHandler(四)
SimpleStatementHandler就是使用基本的Statement来执行query.batch.update等操作,其实现还是比较简单的,当然在执行过程中会涉及keyGenerator和Re ...
- 存储那些事儿(三):OpenStack的块存储Cinder与商业存储的融合
OpenStack是一个美国国家航空航天局和Rackspace合作研发的云端运算软件,以Apache许可证授权,并且是一个自由软件和开放源代码项目.OpenStack是IaaS(基础设施即服务)软 ...
- Linux Shell 命令--cut
解读-help 用法:cut [选项]... [文件]... 从每个文件中输出指定部分到标准输出. 长选项必须使用的参数对于短选项时也是必需使用的. -b, --bytes=列表 ...
- (NO.00001)iOS游戏SpeedBoy Lite成形记(十八)
现在需要实现具体的游戏逻辑大致如下: 玩家点击某条赛道选择一个选手,然后会弹出菜单窗口让玩家输入压赌的金额,如果输入的金额值非法,则在GameInterface下部的状态栏中显示提示,要求玩家重新输入 ...
- How To Transact Move Order Using INV_PICK_WAVE_PICK_CONFIRM_PUB.Pick_Confirm API
In this Document Goal Solution Sample Code: Steps: FAQ References APPLIES TO: Oracle Inven ...
- 再回首UML之上篇
UML,统一建模语言,是一种用来对真实世界物体进行建模的标准标记,这个建模的过程是开发面向对象设计方法的第一步,UML不是一种方法学,不需要任何正式的工作产品. UML提供多种类型的模型描述图,当在某 ...