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

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

前年 SeetaFace 开源了人脸识别引擎,一度成为热门话题。

虽然后来SeetaFace 又放出来 2.0版本,但是,我说但是。。。

没有训练代码,想要自己训练一下模型那可就犯难了。

虽然可以阅读源码,从前向传播的角度,反过来实现训练代码,

但是谁有那个闲功夫和时间,去折腾这个呢?

你还在为没有学习平台而苦恼吗?你还在为没有学习资料而烦心吗?你还在为没人指导而担忧吗?可以私信小编 C++或者 加群 710520381 验证灵狐,为你提供学习的平台和资料。

有的时候还是要站在巨人的肩膀上,你才能看得更远。

而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

MTCNN人脸检测 附完整C++代码_第1张图片

Stage2: Refine Net

MTCNN人脸检测 附完整C++代码_第2张图片


Stage3: Output Net

MTCNN人脸检测 附完整C++代码_第3张图片


具体算法思路就不展开了。

我对MTCNN感兴趣的点在于,

MTCNN的思路可以拓展到各种物体检测和识别方向。

也许唯一缺少的就是打标好的数据,

而标注五个点,足够用于适配大多数物体了。

符合小而美的理念,这个是我比较推崇的。

所以MTCNN是一个很值得品味的算法。

github上也有不少MTCNN的实现和资源。

基于mxnet 基于caffe 基于ncnn 等等。。。

很明显,mxnet 和  caffe 不符合小而美的理念。

果断抛弃了。

ncnn有点肥大,不合我心。

所以,我动了杀气。。

移除NCNN 与mtcnn无关的层,

梳理ncnn的一些逻辑代码。

简单做了一些适配和优化。

砍掉一些边边角角。

不依赖opencv等第三方库。

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

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

你还在为没有学习平台而苦恼吗?你还在为没有学习资料而烦心吗?你还在为没人指导而担忧吗?可以私信小编 C++或者 加群 710520381 验证灵狐,为你提供学习的平台和资料。

完整示例代码:

#include"mtcnn.h"#include "browse.h"#defineUSE_SHELL_OPEN#ifndef  nullptr#definenullptr 0#endif#ifdefined(_MSC_VER)#define_CRT_SECURE_NO_WARNINGS#include #else#include #endif#defineSTB_IMAGE_STATIC#defineSTB_IMAGE_IMPLEMENTATION#include "stb_image.h"//ref:https://github.com/nothings/stb/blob/master/stb_image.h#defineTJE_IMPLEMENTATION#include "tiny_jpeg.h"//ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h#include #include "timing.h"charsaveFile[1024];

unsigned char*loadImage(constchar*filename,int*Width,int*Height,int*Channels) {

    returnstbi_load(filename, Width, Height, Channels,0);

}voidsaveImage(constchar*filename,intWidth,intHeight,intChannels, unsignedchar*Output) {

    memcpy(saveFile + strlen(saveFile), filename, strlen(filename));

    *(saveFile + strlen(saveFile) +1) =0;

    //保存为jpgif(!tje_encode_to_file(saveFile, Width, Height, Channels,true, Output)) {

        fprintf(stderr, "save JPEG fail.\n");

        return;

    }

#ifdef USE_SHELL_OPEN

    browse(saveFile);#endif}voidsplitpath(constchar*path,char*drv,char*dir,char*name,char*ext) {

    constchar*end;

    constchar*p;

    constchar*s;

    if(path[0] && path[1] ==':') {

        if (drv) {

            *drv++ = *path++;

            *drv++ = *path++;

            *drv ='\0';

        }

    }

    elseif (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';

    }

}voidgetCurrentFilePath(constchar*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;

}voiddrawPoint(unsignedchar*bits,intwidth,intdepth,intx,inty,constuint8_t *color) {

    for(inti =0; i < min(depth,3); ++i) {

        bits[(y * width + x) * depth + i] = color[i];

    }

}voiddrawLine(unsignedchar*bits,intwidth,intdepth,intstartX,intstartY,intendX,int endY,

    constuint8_t *col) {

    if(endX == startX) {

        if(startY > endY) {

            inta = startY;

            startY = endY;

            endY = a;

        }

        for(inty = startY; y <= endY; y++) {

            drawPoint(bits, width, depth, startX, y, col);

        }

    }

    else {

        floatm =1.0f* (endY - startY) / (endX - startX);

        inty =0;

        if(startX > endX) {

            inta = startX;

            startX = endX;

            endX = a;

        }

        for(intx = startX; x <= endX; x++) {

            y = (int)(m * (x - startX) + startY);

            drawPoint(bits, width, depth, x, y, col);

        }

    }

}voiddrawRectangle(unsignedchar*bits,intwidth,intdepth,intx1,inty1,intx2,inty2,constuint8_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);

}intmain(intargc,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();

        return0;

    }

    constchar*model_path = argv[1];

    char*szfile = argv[2];

    getCurrentFilePath(szfile, saveFile);

    intWidth =0;

    intHeight =0;

    intChannels =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 finalBbox;

    MTCNN mtcnn(model_path);

    doublestartTime = now();

    mtcnn.detect(ncnn_img, finalBbox);

    doublenDetectTime = calcElapsed(startTime, now());

    printf("time: %d ms.\n ", (int)(nDetectTime *1000));

    intnum_box = finalBbox.size();

    printf("face num: %u \n", num_box);

    for(inti =0; i < num_box; i++) {

        constuint8_t red[3] = {255,0,0 };

        drawRectangle(inputImage, Width, Channels, finalBbox[i].x1, finalBbox[i].y1,

            finalBbox[i].x2,

            finalBbox[i].y2, red);

        constuint8_t blue[3] = {0,0,255 };

        for(intnum =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();

    return0;

}


效果图来一个。

MTCNN人脸检测 附完整C++代码_第4张图片
你还在为没有学习平台而苦恼吗?你还在为没有学习资料而烦心吗?你还在为没人指导而担忧吗?可以私信小编 C++或者 加群 710520381 验证灵狐,为你提供学习的平台和资料。

你可能感兴趣的:(MTCNN人脸检测 附完整C++代码)