开发环境:Qt5.12.4, MSVC2017, win10
首先,梳理一下流程
创建好工程后,取消勾选shadow build
然后再工程.pro文件的同级目录创建四个文件夹, debug, release,lib, resources。
然后在lib文件夹里面创建两个文件夹, opencvInclude, opencvlib
我使用的opencv版本是3.4.1 ,64位,点击翻页寻找opencv3.4.1
下载完成后,安装:
安装完成后,在安装路径找到 build文件夹,
在build文件夹里面有一个X64文件夹,进去选择相应的编译器版本,
这里我使用的是MSVC2017,也就是VC15, 然后在下一层路径找到lib文件夹里面的两个库, 一个是opencv_world341.lib( release 使用的库 ),另一个是opencv_world341d.lib( debug使用的库 )。
将这两个库复制进工程目录 lib/opencvlib 里面
然后再将opencv3.4.1安装路径下的 build/include下的两个文件夹:opencv, opencv2复制进工程目录下的 lib/opencvinclude
然后,再将opencv3.4.1安装路径下 build/x64/vc15/bin 路径下的两个动态库:opencv_world314.dll -> 复制进工程路径下的debug文件夹,opencv_world314d.dll ->复制进工程路径下的release文件夹,
找到opencv3.4.1安装路径下 build/etc/haarcascades/ 下的haarcascade_frontalface_alt2.xml 文件,然后再工程路径下创建文件夹resources , 将haarcascade_frontalface_alt2.xml 复制到resources文件夹里面
###至此,所需要的文件添加完毕!
在工程 .pro文件中添加配置信息如下:
INCLUDEPATH += $$PWD/lib/opencvInclude
CONFIG( debug, debug | release ){
DESTDIR = $$PWD/debug
LIBS += -L$$PWD/lib/opencvlib -lopencv_world341d
}
CONFIG( release , debug | release ){
DESTDIR = $$PWD/release
LIBS += -L$$PWD/lib/opencvlib -lopencv_world341
}
#####至此,opencv库配置完毕
在写这篇博客之前,我最开始使用的是Quick自带的Camera类来打开摄像头并获取图像(参考这篇文章,虽然是安卓平台,但是和windows上的开发几乎一样)
,后来因为公司采购的电脑性能太低了,用Quick自带的Camera类画面比较卡顿,所以采用opencv的方法打开摄像头。
首先,新建一个人脸检测的类
FaceRecognition.h
#ifndef FACERECOGNITION_H
#define FACERECOGNITION_H
#include
#include
#include
#include
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include "opencv2/core.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
class FaceRecognition : public QObject
{
Q_OBJECT
public:
explicit FaceRecognition(QObject *parent = nullptr);
//初始化人脸识别
void initFaceRecog();
//初始化定时器
void initTimer();
//设置opencv人脸识别库的xml文件
bool setXmlFilePath( QString path );
//获取opencv人脸识别库文件的路径
QString getXmlFilePath();
//设置成员变量m_objFrame的值
void setFrame( Mat frame );
// 将人脸在图像中使用矩形框出来
Mat paintRect( Mat frame );
//获取人脸的数量
int getFaces();
//http标志位,用于等待返回结果
void setHttpAvalable( bool state );
//获取http的标志位结果
bool getHttpAvailable();
signals:
public slots:
void onTimerTimeout();
private:
//人脸分类器
CascadeClassifier m_objFace_cascade;
//opencv的Mat
Mat m_objFrame;
//人脸识别库的xml文件路径
QString m_qsXmlFilePath;
//存储人脸框
vector< Rect > m_veFaces;
//是否识别标志位
bool m_bRecognition;
//人脸数量
int m_iFaces;
//定时器
QTimer m_timer;
// 等待http返回结果的标志位
bool m_bHttpAvalable;
};
#endif // FACERECOGNITION_H
FaceRecognition.cpp
#include "FaceRecognition.h"
FaceRecognition::FaceRecognition(QObject *parent) : QObject(parent)
{
initFaceRecog();
initTimer();
}
void FaceRecognition::initFaceRecog()
{
m_bRecognition = true;
bool ok = setXmlFilePath( "../../resources/haarcascade_frontalface_alt2.xml" );
if( !ok )
{
qDebug() << " xml file not existed";
return;
}
//加载人物训练模型
if( !m_objFace_cascade.load( m_qsXmlFilePath.toLocal8Bit().toStdString() ) )
{
qDebug() << " load xml file failed ";
return;
}
qDebug() << " load xml file success ";
}
void FaceRecognition::initTimer()
{
m_timer.setInterval( 300 );
connect( &m_timer, &QTimer::timeout, this, &FaceRecognition::onTimerTimeout );
m_timer.start();
}
bool FaceRecognition::setXmlFilePath( QString path )
{
QFile file( path );
QFileInfo fileInfo( file );
if( !fileInfo.exists() )
{
return false;
}
m_qsXmlFilePath = fileInfo.absoluteFilePath();
qDebug() << "xml path = " << m_qsXmlFilePath;
return true;
}
QString FaceRecognition::getXmlFilePath()
{
return m_qsXmlFilePath;
}
void FaceRecognition::setFrame( Mat frame )
{
if( frame.empty() )
{
qDebug() << " empty frame ";
return;
}
m_objFrame = frame;
}
Mat FaceRecognition::paintRect( Mat frame )
{
if( !m_bRecognition )
{
return frame;
}
setFrame( frame );
m_veFaces.clear();
Mat grayImg;
Mat outPutGrayImg;
Mat smallImg;
//转为灰度图
cvtColor( m_objFrame, grayImg, cv::COLOR_BGR2GRAY );
// resize( grayImg, smallImg, Size(), 0.9, 0.9, INTER_LINEAR );
//直方图均衡化,提升图像质量
equalizeHist( grayImg, outPutGrayImg );
// cout<< " size = " <
// qint64 time1 = QDateTime::currentMSecsSinceEpoch();
//开始检测人脸, Size 的大小会影响人脸检测的性能, 建议将最小Size稍微设大一点
m_objFace_cascade.detectMultiScale( outPutGrayImg, m_veFaces, 1.1, 3, 0, Size( 100, 100 ), Size( 300, 300 ) );
//存储图像中人脸的数量, 以此判断是否将该张图在DevCamera.cpp的run()函数中,通过http发送至服务器
m_iFaces = m_veFaces.size();
// qDebug() << " there are " << m_veFaces.size() << " faces ";
// qDebug() << QString( " coast %1 " ).arg( QDateTime::currentMSecsSinceEpoch() - time1 );
for( size_t i = 0; i < m_veFaces.size(); i++ )
{
Rect tmpFace = m_veFaces[ i ];
Point point1( tmpFace.x, tmpFace.y );
Point point2( tmpFace.x + tmpFace.width, tmpFace.y + tmpFace.height );
rectangle( m_objFrame, point1, point2, Scalar( 255, 0, 0 ) );
}
return m_objFrame;
}
int FaceRecognition::getFaces()
{
return m_iFaces;
}
void FaceRecognition::setHttpAvalable( bool state )
{
m_bHttpAvalable = state;
}
bool FaceRecognition::getHttpAvailable()
{
return m_bHttpAvalable;
}
void FaceRecognition::onTimerTimeout()
{
m_bRecognition = !m_bRecognition;
}
然后,在新建一个摄像机设备类,用于获取摄像头的图像,此处需要开启一个新的线程,用于不断的获取摄像头的图像。并传入人脸识别类进行人脸识别,输入带人脸框的图像.
DevCamera.h
#ifndef DEVCAMERA_H
#define DEVCAMERA_H
#include
#include
#include
#include
#include
#include
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "FaceRecognition.h"
class DevCamera : public QThread
{
Q_OBJECT
public:
DevCamera();
//将opencv的Mat类型转换为QImage
QImage matToQimage( Mat frame );
//QThread 实际所在的线程
void run();
//关闭摄像头
void closeCamera();
signals:
void signalNewQimageAvailable( QImage image );
void signalQimageToServer( QImage image );
private:
//摄像机对象
VideoCapture *m_pCamera;
//摄像机运行标志位
bool m_bCameraRunning;
//线程锁
QMutex m_objMutex;
//opencv人脸识别库文件的路径
QString m_objXmlPath;
//
QImage m_objQimg;
// opencv的 Mat类型
Mat m_objFrame;
//人脸识别类
FaceRecognition m_objFaceRecog;
};
#endif // DEVCAMERA_H
DevCamera.cpp
#include "DevCamera.h"
DevCamera::DevCamera()
{
m_pCamera = new VideoCapture();
}
void DevCamera::run()
{
if( !m_pCamera->isOpened() )
{
m_pCamera->open( 0 );
}
m_bCameraRunning = true;
while( true )
{
m_objMutex.lock();
if( !m_bCameraRunning )
{
m_objMutex.unlock();
break;
}
m_objMutex.unlock();
*m_pCamera >> m_objFrame;
//检测人脸
m_objFrame = m_objFaceRecog.paintRect( m_objFrame );
QImage img = matToQimage( m_objFrame );
if( m_objFaceRecog.getFaces() > 0 )
{
emit signalQimageToServer( img );
}
emit signalNewQimageAvailable( img );
QThread::msleep( 20 );
}
m_pCamera->release();
m_bCameraRunning = false;
qDebug() << " break while ";
}
void DevCamera::closeCamera()
{
m_objMutex.lock();
m_bCameraRunning = false;
m_objMutex.unlock();
}
QImage DevCamera::matToQimage( Mat frame )
{
QImage img;
if (frame.channels()==3)
{
cvtColor(frame, frame, CV_BGR2RGB);
img = QImage((const unsigned char *)(frame.data), frame.cols, frame.rows,
frame.cols*frame.channels(), QImage::Format_RGB888);
}
else if (frame.channels()==1)
{
img = QImage((const unsigned char *)(frame.data), frame.cols, frame.rows,
frame.cols*frame.channels(), QImage::Format_ARGB32);
}
else
{
img = QImage((const unsigned char *)(frame.data), frame.cols, frame.rows,
frame.cols*frame.channels(), QImage::Format_RGB888);
}
return img;
}
显示效果如下图所示:
。关键代码到此已经结束,由于我是用QML的Image控件来显示图像的,但我的工程为QWidget工程,所以其余代码都是用来将QML嵌入QWidget的代码,对识别部分无影响。