前言
人臉檢測(cè)跟蹤技術(shù)在監(jiān)控安防, 消費(fèi)領(lǐng)域用途廣泛, 比如手機(jī)上的人臉解鎖, 美顏App。 人臉檢測(cè)技術(shù)最經(jīng)典的算法之一——Haar Cascade Classifier級(jí)聯(lián)分類器在opencv中早已經(jīng)被集成,Haar特征比較簡(jiǎn)單,巧妙利于圖像積分圖進(jìn)行快速計(jì)算,但是缺點(diǎn)就是精度不高?;谏疃葘W(xué)習(xí)的目標(biāo)檢測(cè)算法層出不窮,猶如雨后春筍,人臉檢測(cè)由于其特殊性發(fā)展出了一系列專用的檢測(cè)算法,比如深度級(jí)聯(lián)人臉檢測(cè), MTCNN等。
MTCNN是基于深度學(xué)習(xí)的人臉檢測(cè)算法,MTCNN算法不僅包含人臉檢測(cè),還可以進(jìn)行人臉對(duì)齊(Face Alignment),細(xì)節(jié)不展開(kāi)敘述。
本文采用開(kāi)源的人臉檢測(cè)跟蹤工程,利用pybind11將其封裝為python接口,在python中實(shí)現(xiàn)人臉檢測(cè)跟蹤。
News! 2019-3-16最新最快的開(kāi)源人臉檢測(cè)libfacedetection python 接口實(shí)現(xiàn)了, 最快可以達(dá)到1500fps!, 詳細(xì)請(qǐng)見(jiàn):https://github.com/ShiqiYu/libfacedetection
本人編譯好的動(dòng)態(tài)鏈接庫(kù)python接口工程

開(kāi)發(fā)測(cè)試環(huán)境
- windows 10, 64bit
- Anaconda3, with pyhon 3.7
- Visual Studio 2017
- pycharm
- opencv3.4.0
- ncnn(tencent開(kāi)源深度學(xué)習(xí)庫(kù))
NCNN環(huán)境配置
步驟:
下載ncnn庫(kù):
https://github.com/Tencent/ncnn
使用cmake進(jìn)行編譯
使用visual studio編譯生成ncnn.lib


python API封裝
繼承faceTrack類, 將其封裝為python的類
PYBIND11_MODULE(face_tracking_demo, m) {
NDArrayConverter::init_numpy();
py::class_<FaceTracker>(m, "FaceTracker")
.def(py::init<>())
.def("trackerInit", &FaceTracker::trackerInit, py::arg("model_path"), py::arg("min_face"))
.def("trackerUpdate", &FaceTracker::trackerUpdate, py::arg("img"));
}
demo.cpp
#include <opencv2/opencv.hpp>
#include"include/ncnn_mtcnn_tld_so.hpp"
#include <stdio.h>
#include<pybind11/pybind11.h>
#include<pybind11/stl.h>
#include<pybind11/numpy.h>
#include"ndarray_converter.h"
using namespace cv;
using namespace std;
namespace py = pybind11;
class FaceTracker :private faceTrack
{
public:
FaceTracker() { faceTrack(); };
~FaceTracker() {};
public:
void trackerInit(const std::string& model_path, const int min_face) {
this->Init(model_path, min_face);
}
std::vector<int> trackerUpdate(cv::Mat& image) {
cv::Rect rect;
this->DetectFace(rect, image);
return vector<int>{rect.x, rect.y, rect.x + rect.width, rect.y + rect.height};
};
public:
std::string version = "v1.0.0";
};
#if 0
int main() {
cv::VideoCapture capture;
capture.open("./test.avi");
cv::Mat frame;
faceTrack tracker;
std::string modelPath = "./models";
int minFace = 40;
tracker.Init(modelPath, minFace);
while (capture.read(frame)) {
int q = cv::waitKey(1);
if (q == 27) break;
cv::Rect result;
double t1 = (double)getTickCount();
tracker.DetectFace(result, frame);
printf("total %gms\n", ((double)getTickCount() - t1) * 1000 / getTickFrequency());
printf("------------------\n");
rectangle(frame, result, Scalar(0, 0, 255), 2);
imshow("frame", frame);
// outputVideo << frame;
}
// outputVideo.release();
capture.release();
cv::destroyAllWindows();
return 0;
}
#endif // 0
#if 1
PYBIND11_MODULE(face_tracking_demo, m) {
NDArrayConverter::init_numpy();
py::class_<FaceTracker>(m, "FaceTracker")
.def(py::init<>())
.def("trackerInit", &FaceTracker::trackerInit, py::arg("model_path"), py::arg("min_face"))
.def("trackerUpdate", &FaceTracker::trackerUpdate, py::arg("img"));
}
#endif
python測(cè)試代碼
import demo16.face_tracking_demo as demo
import cv2
capture = cv2.VideoCapture()
capture.open('./demo16/test.avi')
tracker = demo.FaceTracker()
tracker.trackerInit(model_path='./demo16/models/', min_face=40)
while True:
ret, frame = capture.read()
if not ret:
print('Finish!')
break
rect = tracker.trackerUpdate(frame)
cv2.rectangle(frame, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 255), 2)
cv2.imshow('tracking', frame)
cv2.waitKey(33)
跟蹤結(jié)果
- video1



-
video2
image.png



-
video3
image.png

libfacedetection
初次接觸這個(gè)庫(kù)的時(shí)候,發(fā)現(xiàn)比opencv提供的Haar人臉檢測(cè)器好的多,而且?guī)斓淖髡呤谴笈?,佩服?。?!?之前l(fā)ibfacedetction只提供了編譯好的DLL,現(xiàn)在最新libfacedetection已經(jīng)開(kāi)源,采用深度學(xué)習(xí)CNN,很猛?。?!
Requires
- Ananconda3, python
- numpy
- opencv-python
- opencv C++
- pybind11
python 接口
此源碼是為了編譯python接口使用
#include<array>
#include<pybind11/pybind11.h>
#include<pybind11/numpy.h>
#include<pybind11/stl.h>
#include<opencv2/opencv.hpp>
#include<facedetectcnn.h>
#include"ndarray_converter.h"
namespace py = pybind11;
class Face {
public:
std::array<int, 4> rect; //[xmin,ymin,xmax,ymax]
int angle;
int neighbors;
public:
Face() {};
Face(std::array<int,4>& rect, int angle, int neighbors) {
this->rect = rect;
this->angle = angle;
this->neighbors = neighbors;
}
~Face() {};
};
//define the buffer size. Do not change the size!
#define DETECT_BUFFER_SIZE 0x20000
std::vector<Face> facedetect(cv::Mat& image) {
int * pResults = NULL;
//pBuffer is used in the detection functions.
//If you call functions in multiple threads, please create one buffer for each thread!
unsigned char * pBuffer = (unsigned char *)malloc(DETECT_BUFFER_SIZE);
if (!pBuffer)
{
std::runtime_error("Can not alloc buffer.\n");
//fprintf(stderr, "Can not alloc buffer.\n");
}
///////////////////////////////////////////
// CNN face detection
// Best detection rate
//////////////////////////////////////////
//!!! The input image must be a RGB one (three-channel)
//!!! DO NOT RELEASE pResults !!!
pResults = facedetect_cnn(pBuffer, (unsigned char*)(image.ptr(0)), image.cols, image.rows, (int)image.step);
//printf("%d faces detected.\n", (pResults ? *pResults : 0));
cv::Mat result_cnn = image.clone();;
//print the detection results
std::vector<Face> faces;
for (int i = 0; i < (pResults ? *pResults : 0); i++)
{
short * p = ((short*)(pResults + 1)) + 142 * i;
int x = p[0];
int y = p[1];
int w = p[2];
int h = p[3];
int neighbors = p[4];
int angle = p[5];
std::array<int, 4> arr;
arr[0] = x;
arr[1] = y;
arr[2] = x + w;
arr[3] = y + h;
faces.push_back(Face(arr, angle, neighbors));
//printf("face_rect=[%d, %d, %d, %d], neighbors=%d, angle=%d\n", x, y, w, h, neighbors, angle);
//rectangle(result_cnn, Rect(x, y, w, h), Scalar(0, 255, 0), 2);
}
return faces;
}
//std::vector<Face> facedetect(std::string filename) {
//
// cv::Mat image = cv::imread(filename);
// if (image.empty())
// {
// std::runtime_error("image read failed!\n");
// }
//
// int * pResults = NULL;
// //pBuffer is used in the detection functions.
// //If you call functions in multiple threads, please create one buffer for each thread!
// unsigned char * pBuffer = (unsigned char *)malloc(DETECT_BUFFER_SIZE);
// if (!pBuffer)
// {
// std::runtime_error("Can not alloc buffer.\n");
// //fprintf(stderr, "Can not alloc buffer.\n");
//
// }
//
//
// ///////////////////////////////////////////
// // CNN face detection
// // Best detection rate
// //////////////////////////////////////////
// //!!! The input image must be a RGB one (three-channel)
// //!!! DO NOT RELEASE pResults !!!
// pResults = facedetect_cnn(pBuffer, (unsigned char*)(image.ptr(0)), image.cols, image.rows, (int)image.step);
//
// //printf("%d faces detected.\n", (pResults ? *pResults : 0));
// cv::Mat result_cnn = image.clone();;
// //print the detection results
//
// std::vector<Face> faces;
// for (int i = 0; i < (pResults ? *pResults : 0); i++)
// {
// short * p = ((short*)(pResults + 1)) + 142 * i;
// int x = p[0];
// int y = p[1];
// int w = p[2];
// int h = p[3];
// int neighbors = p[4];
// int angle = p[5];
//
// std::array<int, 4> arr;
// arr[0] = x;
// arr[1] = y;
// arr[2] = x + w;
// arr[3] = y + h;
// faces.push_back(Face(arr, angle, neighbors));
//
// //printf("face_rect=[%d, %d, %d, %d], neighbors=%d, angle=%d\n", x, y, w, h, neighbors, angle);
// //rectangle(result_cnn, Rect(x, y, w, h), Scalar(0, 255, 0), 2);
// }
//
// return faces;
//
//
//}
//
PYBIND11_MODULE(pyLibfacedetection_cnn, m) {
m.doc() = "Simple python warper of libfacedetection-cnn";
NDArrayConverter::init_numpy();
py::class_<Face>(m, "Face")
.def(py::init())
.def_readwrite("rect", &Face::rect)
.def_readwrite("angle", &Face::angle)
.def_readwrite("neighbors", &Face::neighbors);
m.def("facedetect",&facedetect);
}
編譯好之后生成pyd文件

在此路徑下,打開(kāi)python, 直接運(yùn)行

為了可移植、脫離本機(jī)平臺(tái),使用VS自帶的工具查看pyd動(dòng)態(tài)鏈接庫(kù)的依賴:


測(cè)試




End
感謝甜心 的支持

