Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >MTCNN人脸检测 附完整C++代码

MTCNN人脸检测 附完整C++代码

原创
作者头像
cpuimage
修改于 2018-05-07 14:44:16
修改于 2018-05-07 14:44:16
4.6K80
代码可运行
举报
文章被收录于专栏:算法+算法+
运行总次数:0
代码可运行

人脸检测 识别一直是图像算法领域一个主流话题。

前年 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等第三方库。

编写示例代码完成后,还有不少工作要做,

不过第一步感觉已经符合我的小小预期。

完整示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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[1024];

unsigned char *loadImage(const char *filename, int *Width, int *Height, int *Channels) {
    return stbi_load(filename, Width, Height, Channels, 0);
}

void saveImage(const char *filename, int Width, int Height, int Channels, unsigned char *Output) {
    memcpy(saveFile + strlen(saveFile), filename, strlen(filename));
    *(saveFile + strlen(saveFile) + 1) = 0;
    //保存为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[0] && path[1] == ':') {
        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[0] = '_';
    cur_saveFile[1] = 0;
}

void drawPoint(unsigned char *bits, int width, int depth, int x, int y, const uint8_t *color) {
    for (int i = 0; i < min(depth, 3); ++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 = 0;
        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 < 2) {
        printf("usage: %s  model_path image_file \n ", argv[0]);
        printf("eg: %s  ../models ../sample.jpg \n ", argv[0]);
        printf("press any key to exit. \n");
        getchar();
        return 0;
    }
    const char *model_path = argv[1];
    char *szfile = argv[2];
    getCurrentFilePath(szfile, saveFile);
    int Width = 0;
    int Height = 0;
    int Channels = 0;
    unsigned char *inputImage = loadImage(szfile, &Width, &Height, &Channels);
    if (inputImage == nullptr || Channels != 3) return -1;
    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 * 1000));
    int num_box = finalBbox.size();
    printf("face num: %u \n", num_box);
    for (int i = 0; i < num_box; i++) {
        const uint8_t red[3] = { 255, 0, 0 };
        drawRectangle(inputImage, Width, Channels, finalBbox[i].x1, finalBbox[i].y1,
            finalBbox[i].x2,
            finalBbox[i].y2, red);
        const uint8_t blue[3] = { 0, 0, 255 };
        for (int num = 0; num < 5; num++) {
            drawPoint(inputImage, Width, Channels, (int)(finalBbox[i].ppoint[num] + 0.5f),
                (int)(finalBbox[i].ppoint[num + 5] + 0.5f), blue);
        }
    }
    saveImage("_done.jpg", Width, Height, Channels, inputImage);
    free(inputImage);
    printf("press any key to exit. \n");
    getchar();
    return 0;
}

效果图来一个。

项目地址:

https://github.com/cpuimage/MTCNN

参数也很简单,

mtcnn 模型文件路径 图片路径

例如: mtcnn ../models ../sample.jpg

用cmake即可进行编译示例代码,详情见CMakeLists.txt。

若有其他相关问题或者需求也可以邮件联系俺探讨。

联系邮箱:gaozhihan@vip.qq.com

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
8 条评论
热度
最新
人脸识别 感觉还是使用Python更加容易实现一些
人脸识别 感觉还是使用Python更加容易实现一些
11点赞举报
这个要看应用场景和具体需求了,至于说容易,没有一件事情是容易的,死也不容易。
这个要看应用场景和具体需求了,至于说容易,没有一件事情是容易的,死也不容易。
回复回复点赞举报
您好,有根据mtcnn训练自己数据集的方法吗?谢谢
您好,有根据mtcnn训练自己数据集的方法吗?谢谢
11点赞举报
github是个宝库
github是个宝库
回复回复点赞举报
博主知道怎么从C++中调用Python训练好的模型(框架是mxnet,gluon)?
博主知道怎么从C++中调用Python训练好的模型(框架是mxnet,gluon)?
11点赞举报
把权重导出,实现对应的前向传播操作
把权重导出,实现对应的前向传播操作
回复回复点赞举报
我发现这个版本虽然能检测到人脸但是检测率低 有时候检测不到
我发现这个版本虽然能检测到人脸但是检测率低 有时候检测不到
11点赞举报
自己训练之
自己训练之
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
自动红眼移除算法 附c++完整代码
“红眼”一般是指在人物摄影时,当闪光灯照射到人眼的时候,瞳孔放大而产生的视网膜泛红现象。
cpuimage
2018/05/07
1.6K6
自动红眼移除算法 附c++完整代码
自动曝光修复算法 附完整C代码
AF自动对焦(Automatic Focus) 自动对焦即调节摄像头焦距自动得到清晰的图像的过程
cpuimage
2018/06/02
2.9K0
3D Lut 电影级调色算法 附完整C代码
长话短说,3d lut(全称 : 3D Lookup table )它是通过建立一个颜色映射表,对图像的色调进行重调的算法。
cpuimage
2018/05/11
4.5K2
绚丽烟花:HTML5 Canvas 烟花效果实现(文末附完整代码)
在节日或特殊场合,绚丽的烟花总能带来无尽的欢乐和惊喜。今天,我们将通过 HTML5 Canvas 实现一个绚丽的烟花效果,让你的网页也能绽放出美丽的烟花。
码事漫谈
2025/01/27
6940
绚丽烟花:HTML5 Canvas 烟花效果实现(文末附完整代码)
图片文档倾斜矫正算法 附完整c代码
 2年前在学习图像算法的时候看到一个文档倾斜矫正的算法。 也就是说能将一些文档图像进行旋转矫正, 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr 文字识别做前处理。 相关的关键词: 抗倾斜 反倾斜  Deskew 等等。 最简单算法实现思路,采用 霍夫变换(Hough Transform)进行直线检测, 当然也可以用霍夫变换检测圆。 在倾斜矫正算法中,自然就是检测直线。 通过对检测出来的直线进行角度判断, 一般取 认可度最高的几条直线进行计算, 最后求取均衡后的角度值。 进行图像角度的旋转即可
cpuimage
2018/04/12
3.2K0
图片文档倾斜矫正算法 附完整c代码
opencv--基于深度学习的人脸检测器
首先, 一直以来就在考虑这么牛逼的opencv该换一下里边一些过时的东西了,像:检测器、识别器等等,果不其然,openv的大佬们还是偷偷的换了。
MachineLP
2022/05/09
4720
opencv--基于深度学习的人脸检测器
分享用于学习C++图像处理的代码示例
为了便于学习图像处理并研究图像算法, 俺写了一个适合初学者学习的小小框架。 麻雀虽小五脏俱全。 采用Decoder:stb_image https://github.com/nothings/stb/blob/master/stb_image.h 采用Encoder:tiny_jpeg https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h stb_image.h用于解析图片格式:  JPG, PNG, TGA, BMP, PSD, GIF
cpuimage
2018/04/12
2K0
人脸姿态校正算法 附完整C++示例代码
那么假如一张图片只有一个人脸,其实很好判断,通过眼睛的位置的坐标,根据两眼的直线角度,
cpuimage
2018/05/08
2.7K14
人脸姿态校正算法 附完整C++示例代码
Github项目|几行代码即可实现人脸检测、目标检测的开源计算机视觉库
今天介绍一个简单、易用的开源计算机视觉库,名字是 cvlib,其 Github 地址:
kbsc13
2019/09/25
1.7K0
Github项目|几行代码即可实现人脸检测、目标检测的开源计算机视觉库
利用OpenCV和深度学习实现人脸检测
今天偷点儿懒,就没有及时整理最新的paper,还请各位看官谅解。这里整理了一份前段时间做的小demo,实现献丑了 本文基于OpenCV3.3.1或以上版本(如OpenCV3.4)、DNN模块和face_detector示例实现简单、实时的人脸检测。 往期回顾 [计算机视觉] 入门学习资料 [计算机视觉论文速递] 2018-03-20 [计算机视觉论文速递] 2018-03-18 注: [1]:主要参考Face detection with OpenCV and deep learning这个英文教程
Amusi
2018/04/12
6.9K1
利用OpenCV和深度学习实现人脸检测
人脸检测——mtcnn思想,生成negative、positive、part样本。
摘要总结:本文研究了面部识别技术,详细阐述了其基本原理、发展历程、应用领域以及未来前景。
MachineLP
2018/01/09
1.3K0
Java 实现图片合成
图片合成 利用Java的绘图方法,实现图片合成 在开始之前,先定一个小目标,我们希望通过图片合成的方式,创建一个类似下面样式的图片 I. 设计思路 首先解析一下我们的目标实现图片合成,那么这些合成
一灰灰blog
2018/02/06
5.6K0
Java 实现图片合成
Echarts 无法实现这个曲线图,那我手写一个
最近有个图表需求,怎么配置也配置不好,十分头疼。所以想借着这个问题手写实现一个交互体验还不错的曲线图,支持开场动画、自动根据父盒子宽度适配、比echarts更全的配置项,分区线段的可以更好的自定义等。 效果如下
zz_jesse
2023/09/11
5400
Echarts 无法实现这个曲线图,那我手写一个
android画图之贝塞尔曲线讲解
首先对于《赛贝尔曲线》不是很了解的童鞋,请自觉白度百科、google等等... 为了方便偷懒的童鞋,这里给个《贝赛尔曲线》百科地址,以及一段话简述《贝赛尔曲线》: 《贝赛尔曲线》白度百科快速地址:http://baike.baidu.com/view/4019466.htm 贝塞尔曲线又称贝兹曲线或贝济埃曲线,一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋; 上面这一段话其实就“线段像可伸缩的皮筋”这一句比较重要,也很容易理解;         至
xiangzhihong
2018/01/29
1.2K0
android画图之贝塞尔曲线讲解
Qt编写自定义控件56-波浪曲线
波浪曲线控件,其实是之前一个水波进度条控件的一个核心,其实就是利用正弦曲线来生成对应的坐标进行绘制,把这个功能单独提取出来,是为了更详细的研究各种正弦余弦等拓展效果,当时写这个效果的时候,参考的是网上android绘制的水波效果的代码,然后自己重新理解以后,整理成Qt的版本,拓展了部分效果比如可以设置浪的高度,浪的密度,密度越大越浪,^_^,演示控件效果提供了滑块来设置对应的参数。
feiyangqingyun
2019/09/25
1.1K0
Qt编写自定义控件56-波浪曲线
TDesign——投放时间段组件(48 * 7 位字符串)
思索
2024/08/15
1200
TDesign——投放时间段组件(48 * 7 位字符串)
音频降噪算法 附完整C代码
音频降噪目前感觉大有所为,像前面分享的《基于RNN的音频降噪算法 (附完整C代码)》
cpuimage
2018/05/07
9.4K5
人脸识别系列三 | MTCNN算法详解下篇
上篇讲解了MTCNN算法的算法原理以及训练细节,这篇文章主要从源码实现的角度来解析一下MTCNN算法。我要解析的代码来自github的https://github.com/ElegantGod/ncnn中的mtcnn.cpp。
BBuf
2019/12/09
1.7K0
深度学习AI美颜系列——人像静态/动态贴纸特效算法实现
人像静态/动态贴纸特效几乎已经是所有图像视频处理类/直播类app的必需品了,这个功能看起来复杂,实际上很简单,本文将给大家做个详细的讲解。
AI算法与图像处理
2019/11/09
2.3K0
深度学习AI美颜系列——人像静态/动态贴纸特效算法实现
基于Appium实现Monkey小工具
基于了appium实现了UI遍历的工具,在这个的基础上,可以参考上面的方式去实现appium版本的monkey。
雷子
2022/09/29
9680
推荐阅读
相关推荐
自动红眼移除算法 附c++完整代码
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档