C++ OpenCV Contrib模块LBF人脸特征点检测

学更好的别人,

做更好的自己。

——《微卡智享》

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

前言

OpenCV源码Android端编译,用时三天,我编了个寂寞。。。》文中介绍了编译OpenCV+Contrib模块的编译,虽然Andorid下编译始终没有获得libopencv_java4.so的库,不过在Windows下编译还是正常的,今天主要介绍人脸特征点的检测功能,就用到Contrib模块中的FaceMarkLBF。

实现效果

上面的GIF图中可以看出来,除了第一张多人里面有侧脸的,检测特征点时有点差,基本检测的都还挺不错。文章最后有Demo的GitHub源码地址。

实现方式

#思路
1加载OpenCV DNN和FacemarkLBF的模型(FacemarkLBF在OpenCVr的Contrib模块中)
2使用DNN人脸检测获取图像中所有人脸的矩形框
3调用FaceMarkLBF中的fit针对Mat和上面获得的人脸矩形框进行特征点检测,检测的结果存放为vector<vector<Point2f>>格式。

重点说明

微卡智享

01

关于OpenCV配置

使用VS2019编译的OpenCV及Contrib模块的源码后,生成的dll的动态库是VC16了,不是原来的VC15,而且在Debug和Relese都编译了一遍(用处就在正式环境中速度会提高接近10倍),所以会有opencv_world451.dll和opencv_world451d.dll两个动态库。

属性管理器里面也加入Debug和Relese的配置

Debug和Relese的VC++目录还是编译后的OpenCV目录。

链接器中Debug这里填上opencv_world451d.lib,而Relese里就填opencv_world451.lib。

02

DNN人脸检测

同《实践|OpenCV4.2使用DNN进行人脸检测一(图片篇)》文章中一样,已经把DNN的人脸检测单位写在一个类中

原来的detect的函数中我们是在原图上画出红色矩形框,返回的是vector<Mat>的值,因为特征点需要的是vector<Rect>的值,所以又新写了一个函数

bool dnnfacedetect::detectRect(Mat frame, vector<Rect> &rects)
{
  Mat tmpsrc = frame;


  // 修改通道数
  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>());


  if (detectionMat.rows <= 0) return false;


  //检测出的结果进行绘制和存放到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));


      rects.push_back(rect);
    }
  }


  return true;
}

03

LBF人脸特征点

LBF人脸特征点检测也单独写了一个类,里面也比较简单,构造函数直接加载模型,然后一个检测的函数。

#include "chkfacemark.h"




chkfacemark::chkfacemark(string lfmodel)
{
  _lfbmodel = lfmodel;
  //创建对象
  _facemark = FacemarkLBF::create();
  //加载模型
  _facemark->loadModel(_lfbmodel);
}


chkfacemark::~chkfacemark()
{
  _facemark.release();
}


bool chkfacemark::facemarkdetector(Mat src, vector<Rect> faces, vector<vector<Point2f>> &facemarks)
{
  return _facemark->fit(src, faces, facemarks);
}


04

多张图片加载

以前的Demo中只是加载了一张图片,这次是直接加了一个目录下的文件。定义了文件目录后,使用cv::glob的函数可以把所有的文件名存放到vector<string>的变量里。

  //加载多张图片
  string picdesc = "E:/DCIM/person/";
  vector<string> filenames;
  cv::glob(picdesc, filenames);

然后再循环处理filenames的方法即可。

05

关于图像缩放的问题

上面两个图中可以看到,像素差别好大,如果不用缩放的话,第二张图只能看到左上角的图片,为了解决这个问题,所以写了一个函数用于处理图像绽放的问题。

#图像缩放思路
1设定横向图像的最大宽度、纵向图像的最大高度
2根据输入的图像判断是横向还是纵向
3横向如果宽度超过最大宽度,按最大宽度除当前宽度算出比例,然后进行Resize的缩放(纵向就是高度超过最大高度处理)

//计算图像缩放
void MatResize(Mat& frame, int maxwidth, int maxheight)
{
  double scale;
  //判断图像是水平还是垂直
  bool isHorizontal = frame.cols > frame.rows ? true : false;


  //根据水平还是垂直计算缩放
  if (isHorizontal) {
    if (frame.cols > maxwidth) {
      scale = (double)maxwidth / frame.cols;
      resize(frame, frame, Size(0, 0), scale, scale);
      }
  }
  else {
    if (frame.rows > maxheight) {
      scale = (double)maxheight / frame.rows;
      resize(frame, frame, Size(0, 0), scale, scale);
    }
  }
}

然后在每次读取完图像后先进行缩放处理,即可正常显示了。

比较核心的东西上面都已经讲完了,全部的代码可以访问下面的链接,或是点击最后的阅读原文下载。里面的LBF模型文件还有DNN的模型文件都在代码里面。

源码地址

https://github.com/Vaccae/OpenCVDnnfacedecet.git

扫描二维码

获取更多精彩

微卡智享

「 往期文章 」

牛气冲天

OpenCV源码Android端编译,用时三天,我编了个寂寞。。。

.Net5中Swagger采用Attribute方式实现Api隐藏

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Vaccae

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

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

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

打赏作者

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

抵扣说明:

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

余额充值