Qt 使用摄像头通过openCV进行人脸识别
如果想快速开始使用,可以直接使用编译好的,可跳过本文的前3步,由于CSDN上传文件大小限制就分卷压缩上传了(解压保存的路径最好是英文,代码中会用到):
卷一: https://download.csdn.net/download/u012902367/11079135
卷二: https://download.csdn.net/download/u012902367/11079147
卷三: https://download.csdn.net/download/u012902367/11079153
第四小节的demo工程: https://download.csdn.net/download/u012902367/11079205
1. 安装cpenCV
从官网获取到安装程序:
本文选择了一个历史版本(提醒一下不同版本使用方法会存在差异,如果读者想要按照下面的方式尝试,第一次建议选择和笔者同样的版本,成功以后再尝试新版本,还有就是本文所选择的路径在代码中会用到,如果读者怕麻烦修改,甚至可以把盘符和安装路径设置成与本文一致):
如果官网下载太慢可以选择再CSDN里面搜索下载:
下载下来过后运行安装,请选择纯英文路径:
等待安装完成:
2. 安装CMake
官方下载CMake:https://cmake.org/download/
下载完后双击安装,安装到纯英文路径,安装完后把bin目录加入环境变量:
环境变量配置:
顺便也将Qt的环境变量配置一下
3. 编译openCV
用管理员身份运行刚才安装CMake,选择好路径后点击左下角Configure:
按照如下选择:
等待配置完成,过程可能需要等待几分钟:
在配置的过程中需要下载一些文件,最好有VPN,否则会下载失败:
完成过后在列表中勾选 WITH_QT和 WITH_OPENGL(在靠后一点位置),勾选过后再点击Configure:
出现Configuring done提示再点击Generate:
完成过后即可关闭此窗口。
开始编译
使用cmd到最开始自己新建的目录下执行mingw32-make:
等待编译完成,可能需要等一小会:
继续执行mingw32-make install,并等待结束:
结束后把如下路径添加到环境变量(注意看这个路径位置):
4. 新建工程调用openCV
如果不想敲可以直接下载这个应用的工程源码。
新建一个应用:
在.pro文件下添加如下代码,注意里面的路径需要换成读者自己安装的路径:
INCLUDEPATH+=D:/opencvSet/bulidOpencv/install/include/opencv \
D:/opencvSet/bulidOpencv/install/include/opencv2 \
D:/opencvSet/bulidOpencv/install/include
LIBS += -L D:/opencvSet/bulidOpencv/install/x86/mingw/lib/libopencv_*.a
布局文件中就有两个QLabel,3个QPushButton,为了让读者看得清楚点把两个QLabel背景设置成了绿色,这个对使用没有一点影响,读者可以不用管这个颜色,其中对象的名字也写在图里面了:(camera,photo,open,take,close)
mainwindow.h代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QTimer>
#include <QImage>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QImage Mat2QImage(Mat cvImg);
private slots:
void openCamara(); // 打开摄像头
void readFarme(); // 读取当前帧信息
void closeCamara(); // 关闭摄像头。
void takingPictures(); // 拍照
private:
Ui::MainWindow *ui;
QTimer *timer;
QImage imag;
Mat cap,cap_gray,cap_tmp; //定义一个Mat变量,用于存储每一帧的图像
VideoCapture capture; //声明视频读入类
};
#endif // MAINWINDOW_H
mainwindow.cpp代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(readFarme())); // 时间到,读取当前摄像头信息
connect(ui->open, SIGNAL(clicked()), this, SLOT(openCamara()));
connect(ui->take, SIGNAL(clicked()), this, SLOT(takingPictures()));
connect(ui->close, SIGNAL(clicked()), this, SLOT(closeCamara()));
}
//打开摄像头
void MainWindow::openCamara()
{
capture.open(0); //从摄像头读入视频如果设备只有一个摄像头就传入参数0
qDebug("open");
if (!capture.isOpened()) //先判断是否打开摄像头
{
qDebug("err");
}
timer->start(20); // 开始计时,20ms获取一帧
}
//读取摄像头信息
void MainWindow::readFarme()
{
capture>>cap; //读取当前帧
if (!cap.empty()) //判断当前帧是否捕捉成功 **这步很重要
{
imag = Mat2QImage(cap);
imag = imag.scaled(ui->camera->width(), ui->camera->height(),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//设置图片大小和label的长宽一致
//imshow(name, cap); //若当前帧捕捉成功,显示
ui->camera->setPixmap(QPixmap::fromImage(imag)); // 将图片显示到label上
}
else
qDebug("can not ");
}
// 拍照
void MainWindow::takingPictures()
{
capture>>cap; //读取当前帧
if (!cap.empty()) //判断当前帧是否捕捉成功 **这步很重要
{
imag = Mat2QImage(cap);
imag = imag.scaled(ui->photo->width(), ui->photo->height(),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//设置图片大小和label的长宽一致
//imshow(name, cap); //若当前帧捕捉成功,显示
ui->photo->setPixmap(QPixmap::fromImage(imag)); // 将图片显示到label上
}
else
qDebug("can not ");
}
//关闭摄像头,释放资源,必须释放***
void MainWindow::closeCamara()
{
timer->stop(); // 停止读取数据。
}
// 图片转换(网上抄的)
QImage MainWindow::Mat2QImage(Mat cvImg)
{
QImage qImg;
if(cvImg.channels()==3) //3 channels color image
{
cv::cvtColor(cvImg,cvImg,CV_BGR2RGB);
qImg =QImage((const unsigned char*)(cvImg.data),
cvImg.cols, cvImg.rows,
cvImg.cols*cvImg.channels(),
QImage::Format_RGB888);
}
else if(cvImg.channels()==1) //grayscale image
{
qImg =QImage((const unsigned char*)(cvImg.data),
cvImg.cols,cvImg.rows,
cvImg.cols*cvImg.channels(),
QImage::Format_Indexed8);
}
else
{
qImg =QImage((const unsigned char*)(cvImg.data),
cvImg.cols,cvImg.rows,
cvImg.cols*cvImg.channels(),
QImage::Format_RGB888);
}
return qImg;
}
MainWindow::~MainWindow()
{
delete ui;
}
如果需要打包应用,那就需要把如下的文件全部拷贝到应用编译生成的.exe文件同级目录下一起打包:
到此就可以编译成功运行了,左边的是摄像头实时图像,右边是点击拍照后保存的图片:
5. openCV中的基础人脸识别调用
openCV资源里面有一些现成的.xml文件,就是用来识别人脸的,这些文件在本文最开始openCV的安装路径下就可以找到:
本文就测试两个就一个是眼睛检测,一个是人脸检测并用方框圈出来,是在上面的工程中修改的,只修改了如下两处:
- 将如下代码放入到mainwindow.h中:
CascadeClassifier eye_Classifier; //载入分类器
CascadeClassifier face_cascade; //载入分类器
//vector 是个类模板 需要提供明确的模板实参 vector<Rect>则是个确定的类 模板的实例化 需要指点std域名才可以用:using namespace std;
vector<Rect> eyeRect;
vector<Rect> faceRect;
vector<Rect> faces;
- 修改mainwindow.cpp中readFarme()方法的内容为如下:
void MainWindow::readFarme()
{
capture>>cap; //读取当前帧
if (!cap.empty()) //判断当前帧是否捕捉成功 **这步很重要
{
cvtColor(cap, cap_gray, CV_BGR2GRAY);//转为灰度图
equalizeHist(cap_gray, cap_gray);//直方图均衡化,增加对比度方便处理
//加载分类训练器,OpenCv官方文档提供的xml文档,可以直接调用
//xml文档路径, opencv\sources\data\haarcascades
if (!eye_Classifier.load("D:\\opencvSet\\opencv\\sources\\data\\haarcascades\\haarcascade_eye.xml")) //需要将xml文档放在自己指定的路径下
{
qDebug("Load haarcascade_eye.xml failed!");
return;
}
if (!face_cascade.load("D:\\opencvSet\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml"))
{
qDebug("Load haarcascade_frontalface_alt failed!");
return;
}
//检测关于眼睛部位位置
eye_Classifier.detectMultiScale(cap_gray, eyeRect, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));//检测
for (size_t eyeIdx = 0; eyeIdx < eyeRect.size(); eyeIdx++)
{
rectangle(cap, eyeRect[eyeIdx], Scalar(0, 0, 255)); //用红色矩形画出检测到的位置
}
//检测关于脸部位置
face_cascade.detectMultiScale(cap_gray, faceRect, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));//检测
for (size_t i = 0; i < faceRect.size(); i++)
{
rectangle(cap, faceRect[i], Scalar(0, 255, 0)); //用绿色矩形画出检测到的位置
}
imag = Mat2QImage(cap); // 将Mat转换成QImage对象来显示
imag = imag.scaled(ui->camera->width(), ui->camera->height(),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//设置图片大小和label的长宽一致
//imshow(name, cap); //若当前帧捕捉成功,显示
ui->camera->setPixmap(QPixmap::fromImage(imag)); // 将图片显示到label上
}
else
qDebug("can not ");
}
效果如下:
可以看出官方提供分类训练器也有一些误检测,也许是笔者环境问题吧。