pybind11—目标跟踪demo(深度学习人脸检测跟踪)

前言

人脸检测跟踪技术在监控安防, 消费领域用途广泛, 比如手机上的人脸解锁美颜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接口工程

image.png


开发测试环境

  • 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

image.png
image.png

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
image.png
image.png
image.png
  • video2


    image.png
image.png
image.png
image.png
  • video3


    image.png
image.png

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文件


image.png

在此路径下,打开python, 直接运行


image.png

为了可移植、脱离本机平台,使用VS自带的工具查看pyd动态链接库的依赖:


image.png
image.png

测试

image.png

image.png
image.png
dst.jpg

End

感谢甜心 的支持

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容