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

Stage2: Refine Net


Stage3: Output Net


具體算法思路就不展開了。

我對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"http://ref:https://github.com/nothings/stb/blob/master/stb_image.h#defineTJE_IMPLEMENTATION#include "tiny_jpeg.h"http://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;

}


效果圖來一個。

你還在為沒有學習平臺而苦惱嗎?你還在為沒有學習資料而煩心嗎?你還在為沒人指導而擔憂嗎?可以私信小編 C++或者 加群 710520381 驗證靈狐,為你提供學習的平臺和資料。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容