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

学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为4829,预计阅读9分钟

OpenCV DNN人脸检测

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

DNN人脸检测

微卡智享

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

01

Caffe模型

res10_300x300_ssd_iter_140000_fp16.caffemodel
deploy.prototxt

直接的下载地址为:

https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel

02

tensorflow模型

opencv_face_detector_uint8.pb
opencv_face_detector.pbtxt

直接的下载地址为:

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的核心函数

微卡智享

#核心函数
1readNetFromTensorflow
2blobFromImage
3setInput
4
forward

01

readNetFrom

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

//Tensorflow
cv::dnn::readNetFromTensorflow(_modelbinary, _modeldesc)


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

02

blobFromImage

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

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

04

forward

 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

#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

#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的调用代码

#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实现人脸检测就完成了。

这篇主要介绍了DNN的人脸检测,并用图片中检测出人脸,下一篇我们在这个基础上来看看视频中实时进行人脸检测的使用

扫描二维码

获取更多精彩

微卡智享

微信圈子:微卡时光

学更好的别人,做更好的自己

除了关联本公众相关文章外,还记录了平时自己的一些计划,分享和生活的日常,让你更多的了解博主。

「 往期文章 」

学习|Android中JetPack中的几个组件简单使用

Android通讯库VNanoMsg的1.0.2发布

学习|C++线程与指针结合的小例子

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值