前言
人脸检测跟踪技术在监控安防, 消费领域用途广泛, 比如手机上的人脸解锁, 美颜App。 人脸检测技术最经典的算法之一——Haar Cascade Classifier级联分类器在opencv中早已经被集成,Haar特征比较简单,巧妙利于图像积分图进行快速计算,但是缺点就是精度不高。基于深度学习的目标检测算法层出不穷,犹如雨后春笋,人脸检测由于其特殊性发展出了一系列专用的检测算法,比如深度级联人脸检测, MTCNN等。
MTCNN是基于深度学习的人脸检测算法,MTCNN算法不仅包含人脸检测,还可以进行人脸对齐(Face Alignment),细节不展开叙述。
本文采用开源的人脸检测跟踪工程,利用pybind11将其封装为python接口,在python中实现人脸检测跟踪。
News! 2019-3-16最新最快的开源人脸检测libfacedetection python 接口实现了, 最快可以达到1500fps!, 详细请见:https://github.com/ShiqiYu/libfacedetection
本人编译好的动态链接库python接口工程
开发测试环境
- windows 10, 64bit
- Anaconda3, with pyhon 3.7
- Visual Studio 2017
- pycharm
- opencv3.4.0
- ncnn(tencent开源深度学习库)
NCNN环境配置
步骤:
下载ncnn库:
https://github.com/Tencent/ncnn
使用cmake进行编译
使用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测试代码
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)
跟踪结果
- video1
-
video2
-
video3
libfacedetection
初次接触这个库的时候,发现比opencv提供的Haar人脸检测器好的多,而且库的作者是大牛,佩服!!!。 之前libfacedetction只提供了编译好的DLL,现在最新libfacedetection已经开源,采用深度学习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文件
在此路径下,打开python, 直接运行
为了可移植、脱离本机平台,使用VS自带的工具查看pyd动态链接库的依赖:
测试
End
感谢甜心 的支持