MTCNN人脸检测的详细介绍及完整C++代码你能学会吗?
人脸检测识别一直是图像算法领域一个主流话题。
前年seetaface开源了人脸识别引擎,一度成为热门话题。
虽然后来seetaface又放出来 2.0版本,但是,我说但是。。。
没有训练代码,想要自己训练一下模型那可就犯难了。
虽然可以阅读源码,从前向传播的角度,反过来实现训练代码,
但是谁有那个闲功夫和时间,去折腾这个呢?
有的时候还是要站在巨人的肩膀上,你才能看得更远。
而seetaface不算巨人,只是当年风口上的猪罢了。
前年,为了做一个人脸项目,也是看遍了网上各种项目。
林林总总,各有优劣。
不多做评价,很多东西还是要具体实操,实战才能见真知。
有一段时间,用seetaface的人脸检测来做一些小的演示demo,
也花了一点小时间去优化它的算法。
不过很明显我只是把他当成玩具看待。
毕竟不能自己训练模型,这是很大的诟病。
直到后来深度学习大放异彩,印象最深刻莫过于mtcnn。
joint face detection and alignment using multi-task cascaded convolutional neural networks
大合照下,人脸圈出来很准确,壮观了去,这是第一印象。
上图,大家感受一下。
cnn的有三个网络结构。
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 #else#include#endif#define stb_image_static#define stb_image_implementation#includestb_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 #include timing.hchar savefile[1024];unsignedchar *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.); 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 = '