前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实践|OpenCV4.2使用DNN进行人脸检测一(图片篇)

实践|OpenCV4.2使用DNN进行人脸检测一(图片篇)

作者头像
Vaccae
发布2020-04-08 12:26:42
3.9K0
发布2020-04-08 12:26:42
举报
文章被收录于专栏:微卡智享

使用OpenCV进行人脸检测我写过两篇文章《C++ OpenCV之级联分类器--人脸检测》和《Android NDK OpenCV级联方式实时进行人脸检测》,不过这两篇里面用到的检测方式都是HAAR级联检测器,现在OpenCV4里面官方支持的人脸检测方法也已经是基于深度学习的方法进行检测了,所以我们这篇主要就是看OpenCV下用DNN进行人脸检测。

实现DNN的人脸检测,需要先下载模型文件,在OpenCV的\sources\samples\dnn\face_detector目录下,有一个download_weights.py脚本文件,可以通过Pycharm执行下就可以自动下载两种模型:

01

Caffe模型

代码语言:javascript
复制
res10_300x300_ssd_iter_140000_fp16.caffemodel
deploy.prototxt

直接的下载地址为:

代码语言:javascript
复制
https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel

02

tensorflow模型

代码语言:javascript
复制
opencv_face_detector_uint8.pb
opencv_face_detector.pbtxt

直接的下载地址为:

代码语言:javascript
复制
https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180220_uint8/opencv_face_detector_uint8.pb

Tips

由于我个人不用Python,所以模型我是自己下载的,这里我也只用了tensorflow的方式,所以就只下了后面的opencv_face_detector_uint8.pb

DNN的核心函数

#

核心函数

1

readNetFromTensorflow

2

blobFromImage

3

setInput

4

forward

01

readNetFrom

根据我们用的模型不同改为不同的函数,后面的参数就是加载模型文件

代码语言:javascript
复制
//Tensorflow
cv::dnn::readNetFromTensorflow(_modelbinary, _modeldesc)

//Caffe
cv::dnn::readNetFromCaffe(_modelbinary, _modeldesc)

02

blobFromImage

代码语言:javascript
复制
Mat cv::dnn::blobFromImage(
    InputArray     image,  //输入图像
    double     scalefactor = 1.0,   //图像缩放的比率
    const Size &     size = Size(),  //返回的Mat中数据的尺寸
    const Scalar &     mean = Scalar(),    //关于mean参数,如果之前没有深入研究过深度学习,这个还是不太好理解的。首先给出mean的数值:(104 117 123);数字从什么地方来的呢?这个是在Net训练的时候设定的,可以看到在训练的时候transform_param中设置了mean
    bool     swapRB = false,  //是否交换R和B分量
    bool     crop = false,  //裁剪标志,指示是否在调整大小后裁剪图像
    int     ddepth = CV_32F //图像的数据类型,目前仅支持32F和8U
    )

03

setInput

代码语言:javascript
复制
void cv::dnn::Net::setInput    (    
    InputArray     blob,  //上 个函数blobFromImage的返回值
    const String &     name = "",  //输入图层的名称
    double     scalefactor = 1.0,  //可选的标准化比例
    const Scalar &     mean = Scalar() //可选的平均减法值
    )

04

forward

代码语言:javascript
复制
 Mat forward(const String& outputName = String())
 
 //outputName:需要输出的图层的名称
 //返回:指定图层outputName的第一个输出的blob。默认情况下,为整个网络运行正向传递。注意:返回Mat类型,这是一个4D数,rows and cols can only hold 2 dimensions, so they are not used here, and set to -1

DNN检测封装

我直接把DNN的检测的封装了一个名称为dnnfacedetect的C++的类出来,可以直接拷贝复用了。

dnnfacedetect.h

代码语言:javascript
复制
#pragma once

#include<opencv2/opencv.hpp>
#include<opencv2/dnn/dnn.hpp>

using namespace std;
using namespace cv;

class dnnfacedetect
{
private:
  string _modelbinary, _modeldesc;
  dnn::Net _net;
public:
  //构造函数 传入模型文件
  dnnfacedetect();
  dnnfacedetect(string modelBinary, string modelDesc);

  ~dnnfacedetect();
  //置信阈值
  float confidenceThreshold;
  double inScaleFactor;
  size_t inWidth;
  size_t inHeight;
  Scalar meanVal;

  //初始化DNN网络
  bool initdnnNet();

  //人脸检测
  vector<Mat> detect(Mat frame);
};

dnnfacedetect.cpp

代码语言:javascript
复制
#include "dnnfacedetect.h"


dnnfacedetect::dnnfacedetect()
{
  dnnfacedetect("", "");
}

//构造函数
dnnfacedetect::dnnfacedetect(string modelBinary, string modelDesc)
{
  _modelbinary = modelBinary;
  _modeldesc = modelDesc;

  //初始化置信阈值
  confidenceThreshold = 0.6;
  inScaleFactor = 0.5;
  inWidth = 300;
  inHeight = 300;
  meanVal = Scalar(104.0, 177.0, 123.0);
}

dnnfacedetect::~dnnfacedetect()
{
  _net.~Net();
}

//初始化dnnnet
bool dnnfacedetect::initdnnNet()
{
  _net = dnn::readNetFromTensorflow(_modelbinary, _modeldesc);
  _net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
  _net.setPreferableTarget(dnn::DNN_TARGET_CPU);

  return !_net.empty();
}

//人脸检测
vector<Mat> dnnfacedetect::detect(Mat frame)
{
  Mat tmpsrc = frame;
  vector<Mat> dsts = vector<Mat>();
  // 修改通道数
  if (tmpsrc.channels() == 4)
    cvtColor(tmpsrc, tmpsrc, COLOR_BGRA2BGR);
  // 输入数据调整
  Mat inputBlob = dnn::blobFromImage(tmpsrc, inScaleFactor,
    Size(inWidth, inHeight), meanVal, false, false);
  _net.setInput(inputBlob, "data");

  //人脸检测
  Mat detection = _net.forward("detection_out");

  Mat detectionMat(detection.size[2], detection.size[3],
    CV_32F, detection.ptr<float>());

  //检测出的结果进行绘制和存放到dsts中
  for (int i = 0; i < detectionMat.rows; i++) {
    //置值度获取
    float confidence = detectionMat.at<float>(i, 2);
    //如果大于阈值说明检测到人脸
    if (confidence > confidenceThreshold) {
      //计算矩形
      int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * tmpsrc.cols);
      int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * tmpsrc.rows);
      int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * tmpsrc.cols);
      int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * tmpsrc.rows);
      //生成矩形
      Rect rect((int)xLeftBottom, (int)yLeftBottom,
        (int)(xRightTop - xLeftBottom),
        (int)(yRightTop - yLeftBottom));

      //截出图矩形存放到dsts数组中
      Mat tmp = tmpsrc(rect);
      dsts.push_back(tmp);

      //在原图上用红框画出矩形
      rectangle(frame, rect, Scalar(0, 0, 255));
    }
  }

  return dsts;
}

Tips

上面的初始化和检测这块基本都是这个模式,最主要就是几个参数的配置,

创建项目

新建一个C++的项目,配置OpenCV可以看《VS2017配置OpenCV通用属性》,然后把我们需要用的模型文件和要检测的图片拷贝进去

在main.cpp的调用代码

代码语言:javascript
复制
#include<opencv2/opencv.hpp>
#include<iostream>
#include <direct.h>
#include "dnnfacedetect.h"

using namespace std;
using namespace cv;

int main(int argc, char* argv) {
  //获取程序目录
  char filepath[256];
  _getcwd(filepath, sizeof(filepath));

  cout << filepath << endl;
  //定义模型文件
  string ModelBinary = (string)filepath + "/opencv_face_detector_uint8.pb";
  string ModelDesc = (string)filepath + "/opencv_face_detector.pbtxt";

  //图片文件
  string picdesc = (string)filepath + "/lena.jpg";

  cout << ModelBinary << endl;
  cout << ModelDesc << endl;

  //加载图片
  Mat frame = imread(picdesc);
  imshow("src", frame);

  try
  {
    //初始化
    dnnfacedetect fdetect = dnnfacedetect(ModelBinary, ModelDesc);
    if (!fdetect.initdnnNet())
    {
      cout << "初始化DNN人脸检测失败!" << endl;
      return -1;
    }

    if (!frame.empty()) {
      vector<Mat> dst = fdetect.detect(frame);
      if (!dst.empty()) {
        for (int i = 0; i < dst.size(); i++) {
          string title = "dst" + i;
          imshow(title, dst[i]);
        }
        imshow("src2", frame);
      }
    }
  }
  catch (const std::exception & ex)
  {
    cout << ex.what() << endl;
  }

  waitKey(0);
  return 0;
}

这样整个代码就完成了,我们来看看运行的效果。

实现效果

上图中可以看到,左边的是源图,中间小的就是我们人脸检测出来后截取的图,右图就是在源图的基础上用红框把人脸标识出来的图,这样我们的DNN实现人脸检测就完成了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • dnnfacedetect.h
  • dnnfacedetect.cpp
相关产品与服务
人脸识别
腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档