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