Linux Qt调用opencv实现简单的人脸检测

上一篇:Linux 配置opencv、opencv-contrib环境

之前已经安装好了opencv以及opencv_contrib,现在开始用它,先写一个简单的人脸检测代码来试试之前的环境搭建是否存在问题
我的环境:
Parrot Linux
Qt 5.12.8
opencv 4.1.0
首先,创建一个Qt的widget桌面项目,然后在项目里的pro项目文件中添加:

INCLUDEPATH += /usr/local/include/opencv2
LIBS += /usr/local/lib/*.so.*.*
LIBS += /usr/local/lib/*.so

这样三句,首先第一句是添加opencv头文件的路径,这里有一点需要注意,如果你用的也是和我一样是opencv4.1.0,那他在编译安装成功后的目录层次可能并不是这样,他在opencv2目录之上可能还存在一层目录opencv4,所以他原本应该是这样usr/local/include/opencv4/opencv2如果是这样的话,记得吧opencv2目录及其目录下的所有文件直接复制移动到opencv4的同级目录下,也就是include目录下,这一步如果不改得话,会在这一处产生报错
在这里插入图片描述在这可以看到一句#include<opencv2/*.hpp>的头文件包含语句,当然你也可以吧这些全部改掉,但我不建议这样做,因为这是opencv官方自带的头文件,我们作为普通的开发者最好不要擅自去改动,另外在这个目录下并不是只有一个头文件是这样写的如果要改得话,改动的地方会很多,所以只移动目录就好。

然后后面两句:

LIBS += /usr/local/lib/*.so.*.*
LIBS += /usr/local/lib/*.so

这两句负责将需要的动态链接库包含进来,至于这里的*号,我其实是用了一个偷懒的办法,用到了linux下通配符的一种方法,这样可以避免重复的一个一个的去写动态链接库的名称,另外,如果你不知道需要将哪些动态链接库关联进来的情况下,也可以直接用这种通配符的办法,毕竟lib目录下的库文件挺多的,如果一个一个去写实在是费事费力没有必要,直接将lib目录下的所有动态链接库文件关联进来就可以了。

完整截图
在这里插入图片描述
然后,我们就可以开始写代码了,首先我先简单说明一下我的项目目录结构:

  • Widget类就是最常见的qt下的一个窗口类了,我们要在此类中调用facerun类
  • facerun类,负责进行人脸检测并且将人脸用红色方框圈出来

首先是Widget的代码,比较简单:
头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include"facerun.h"


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

private slots:
	//此槽函数与窗口界面里的一个按钮的clicked信号关联
	//在此槽函数中打开摄像头,并获取每一帧的图片,
	//最后交给facerun类对象去处理
    void openCream();
};
#endif // WIDGET_H

cpp源文件

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

void Widget::openCream()
{
    VideoCapture cap;
    //facerun实例化对象
    faceRun* asp=new faceRun();
    //加载opencv自带的人脸级联分类器路径
    asp->setHaarPath("/home/pluviophile/myfile/opencv/opencv-4.1.0/data/haarcascades/haarcascade_frontalface_default.xml");
    //打开摄像头,opencv下摄像头都是以数字序号的形式来表示的
    //我只有一个自带摄像头所以直接传0
    cap.open(0);
    //判断摄像头是否打开成功
    if(!cap.isOpened())
    {
        return;
    }
    Mat yu;
    //开始循环获取枕图象
    while (1)
    {
        cap>>yu;
        //开始检测人脸
         try
        {
            asp->startFace(yu);
        }catch(string e){
            qDebug()<<e.c_str();
            return;
        }
        //窗口显示图片
        imshow("face",yu);
        //延时一毫秒,并且判断如果按下esc键就释放窗口并跳出循环
        if(waitKey(1)==27)
        {
            destroyWindow("face");
            break;
        }
    }
}
Widget::~Widget()
{
    delete ui;
}


然后是facerun类的代码
头文件

#ifndef FACERUN_H
#define FACERUN_H

#include<iostream>
#include<opencv2/opencv.hpp>
#include<highgui/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/features2d/features2d.hpp>
using namespace std;
using namespace cv;

class faceRun
{
public:
    faceRun();
    //设置级联分类器路径
    void setHaarPath(string haarPath);
    //开始检测人脸,这里用了引用参数
    //这样做的好处是可以直接更改形参的值
    //这样就不用再通过返回值来返回了
    void startFace(Mat &inIMage) throw(string);

private:
	//字符串,存放级联分类器路径`
    string haarPath;

};

#endif // FACERUN_H

cpp源代码

#include "facerun.h"

faceRun::faceRun()
{

}

void faceRun::setHaarPath(string haarPath)
{
    this->haarPath=haarPath;
}

void faceRun::startFace(Mat &inIMage) throw(string)
{
    //load face harr file
    CascadeClassifier fier;
    //加载级联分类器,如果加载失败,则抛出异常
    if(!fier.load(this->haarPath.c_str()))
    {
        throw string("face haarfile not found!");
        return;
    }
    //用于存放灰度图
    Mat outIMage;
    //用于存放人脸位置坐标
    vector<Rect>faceZuobiao;

    //彩色图转灰度图
    cvtColor(inIMage,outIMage,COLOR_BGR2GRAY);
    //直方均衡化,通俗来讲就是提高对比度
    //将灰度图中本来是黑色暗的的地方变得更黑更暗
    //本来亮白的地方变得更亮更白
    equalizeHist(outIMage,outIMage);
    
    fier.detectMultiScale(outIMage,faceZuobiao,1.1,9,CASCADE_DO_ROUGH_SEARCH,Size(70,70),Size(200,200));
    //循环用红色方框圈出所有人脸
    for(int i=0;i<(int)faceZuobiao.size();i++)
    {
    	//画方框,首参数是要画的图像,二三为起始坐标,第四个控制颜色
    	//最后两个参数控制2表示线条粗细(这个参数如果设置为-1则将框填满,
    	//反映到我们的代码中,就是人脸部分完全被红色大矩形盖掉),
    	//8表示线条类型,这个参数一般不用太关心
        rectangle(inIMage,
                  Point(faceZuobiao[i].x,faceZuobiao[i].y),
                  Point(faceZuobiao[i].x+faceZuobiao[i].width,
                        faceZuobiao[i].y+faceZuobiao[i].height+10),
                  Scalar(0,0,255),
                  2,8);
    }
}


这里要提出来说明的是fier.detectMultiScale(outIMage,faceZuobiao,1.1,9,0,Size(70,70),Size(200,200));
这一句,这一句是检测人脸最关键的一句,它可以检测出图片中所有的人脸,并用vector保存各个人脸的坐标,然后按照顺序简单介绍各参数:

  1. 此参数应该为一个灰度图,也是用来检测人脸的原图
  2. 次参数为一个vector容器,其类型应该为Rect,用于保存人脸坐标
  3. 次参数表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%
  4. 此参数默认值为3表明至少有3次重叠检测,我们才认为人脸确实存在,此值控制着误检率,此值越大误检率越低,当然时间复杂度也就越高(个人理解)
  5. 此参数对于新的分类器没有用,略过
  6. 此参数示寻找人脸的最小区域,用最通俗不太专业的话来讲就是看能检测到的最小人脸尺寸是多少,比如你站的越远,你的脸在画面中就越小
  7. 此参数与上个参数刚好相反是最大区域,同样通俗来讲就是如果此值设置过小,那你一旦站的近一点(面部区域变大)就无法检测到你的脸

最后结果
在这里插入图片描述

想看我的脸?,不可能的!

猜你喜欢

转载自blog.csdn.net/weixin_43815930/article/details/105884245
今日推荐