赞
踩
如果想快速开始使用,可以直接使用编译好的,可跳过本文的前3步,由于CSDN上传文件大小限制就分卷压缩上传了(解压保存的路径最好是英文,代码中会用到):
由于版本众多,各个版本之间编译过程可能有所区别,本文选择了一个历史版本(提醒一下不同版本使用方法会存在差异,如果读者想要按照下面的方式尝试,第一次建议选择和笔者同样的版本,成功以后再尝试新版本,还有就是本文所选择的路径在代码中会用到,如果读者怕麻烦修改,甚至可以把盘符和安装路径设置成与本文一致),如下是本文所用的环境参数:
操作系统:win10
mingw32-make
CMake-3.14.1-win64-x64
opencv_contrib-3.2.0
opencv-3.2.0
下载下来过后运行安装,请选择纯英文路径:
等待安装完成:
把两个都下载下来,下面那一个包含了一些其它模块功能,比如常用的人脸识别。
保存在纯英文路径,路径中最好也不要有特殊符号。
官方下载CMake:https://cmake.org/download/
下载完后双击安装,安装到纯英文路径,安装完后把bin目录加入环境变量:
环境变量配置:
顺便也将Qt的环境变量配置一下
本文就具体描述包含opencv_contrib的编译,不想包含他只需要不添加他的路径就好了,其它的步骤完全一样。
为了cmake正常通过,先下载几个文件放到opencv_contrib的\modules\xfeatures2d\src目录,在CMake过程中也会下载,但是笔者尝试了很多遍都下载失败了,所以就事先把这几个文件都下载下来放到指定目录下就安全多了,当然如果不包含opencv_contrib就可以不要做这一步。需要添加的文件如下:
运行安装的CMake。选择路径:
注意: 若果是选择的使用exe安装的openCV,路径是选择的source。如果是通过git获取的会发现解压出来和exe安装的source下文件一样,选择到对应的目录即可:
如果是选择git下载解压下来的会发下解压出来的文件目录和exe安装的source文件加下目录几乎一样。
选择好后点击左下角Configure出现如下对话框,照图选择选择:
等待配置完成,过程可能需要等待几分钟,在配置的过程中需要下载一些文件,最好有vpn:
完成过后在列表中勾选 WITH_QT和 WITH_OPENGL(在靠后一点位置),并选择opencv_contrib的路径:
勾选过后再点击Configure等待运行完成,出现Configuring done,如果还有红色就再点一次Configure,直到界面没有红色框:
再点击Generate:
完成过后即可关闭此窗口。
使用cmd到最开始自己新建的目录下执行mingw32-make:
也可以目录下在按住shift点击鼠标右键:
在这个目录下执行mingw32-make:
等待编译完成,根据电脑配置,过程可能需要15~30分钟:
继续执行mingw32-make install,并等待结束:
结束后把如下路径添加到环境变量(注意看这个路径位置):
如果不想敲可以直接下载这个应用的工程源码。
新建一个应用:
在.pro文件下添加如下代码,注意里面的路径需要换成读者自己安装的路径:
INCLUDEPATH+=D:/openCV3_2/build/install/include/opencv \
D:/openCV3_2/build/install/include/opencv2 \
D:/openCV3_2/build/install/include
LIBS += -L D:/openCV3_2/build/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文件同级目录下,否则会编译出错:
到此就可以编译成功运行了,左边的是摄像头实时图像,右边是点击拍照后保存的图片:
openCV资源里面有一些现成的.xml文件,就是用来检测人脸的,这些文件在本文最开始openCV的安装路径下就可以找到:
本文就测试两个就一个是眼睛检测,一个是人脸检测并用方框圈出来,是在上面的工程中修改的,只修改了如下两处:
CascadeClassifier eye_Classifier; //载入分类器
CascadeClassifier face_cascade; //载入分类器
//vector 是个类模板 需要提供明确的模板实参 vector<Rect>则是个确定的类 模板的实例化 需要指点std域名才可以用:using namespace std;
vector<Rect> eyeRect;
vector<Rect> faceRect;
vector<Rect> faces;
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 "); }
效果如下:
可以看出官方提供分类训练器也有一些误检测。
分清两个概念:
上一小节已经利用opencv现成的人脸模型实现了检测人脸,这一小节就描写一下如何利用opencv提供的平台实现人脸识别,这里得所谓的人脸识别实际就是从摄像头视频或者照片里面查找人脸,然后把查找到的人脸处理成指定大小,再与人脸库对比,获取到对比结果。
为了提高识别率最好准备各个角度的面部照片(测试过jpg和pgm两种格式均可),就像下面一样,同一个人有10张不同角度的照片:
可以直接下载Database of Faces下来作为自己的参考人脸库:
下载完解压出来有如下目录:
共40个文件夹,每一个文件夹里面为同一个人的不同角度10张图片,是pgm格式的,windows的图片查看软件打不开,不过可以是使用opencv提供的方法来查看,代码的路径就是刚才下载下来的文件第一个人脸:
Mat img;
img = imread("C:/Users/XF/Desktop/orl_faces/s1/1.pgm", CV_LOAD_IMAGE_GRAYSCALE);
imshow("img", img);
准备我们自己需要的人脸照片处理跟下载下来为图片参数一致,人脸图为灰度图,大小为92*112,保存为第41个文件夹,如果自己需要添加几个人的面部信息就按照如此规律往后添加即可:
网上看的文章很多都会有一个类似如下的文本文件:
这个文件主要是给代码快速获取图片路径的,所说的标签也仅仅只是表示这张图片代表的是库里面的第几个人,所以这个文件不是必要的,本文就不用这个文件直接手动来获取,我想这样读者会更容易理解,
如下代码就是用来生成人脸库的, 当然这里只选了几个人的脸部照片,要把每一个都添加进去靠动手输入是很麻烦,这也就是为什么会有上面那个文本文档来保存路径和标签信息,通过读取文档里面的路径和标签信息可以快速准简单的实现如下功能,也是一劳永逸:
void MainWindow::on_pushButton_clicked() { vector<Mat> images; Mat img; img = imread("C:/Users/XF/Desktop/orl_faces/s1/1.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/2.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/3.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/4.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/5.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/6.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/7.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/8.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/9.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s1/10.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/1.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/2.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/3.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/4.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/5.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/6.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/7.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/8.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/9.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s2/10.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/1.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/2.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/3.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/4.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/5.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/6.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/7.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/8.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/9.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/orl_faces/s3/10.pgm", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/1.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/2.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/3.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/4.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/5.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/6.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/7.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/8.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/9.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 img = imread("C:/Users/XF/Desktop/face_jpg/10.jpg", CV_LOAD_IMAGE_GRAYSCALE); images.push_back(img); // 将图片 添加到images中 vector<int> labels; labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(0); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(1); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(2); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); labels.push_back(3); Ptr<FaceRecognizer> model = createEigenFaceRecognizer(); model->train(images, labels); model->save("MyFacePCAModel.xml"); }
可以看得出上面是通过一个按钮来实现开始训练自己的人脸库,运行过后会在exe的上一级目录下多一个MyFacePCAModel.xml文件:
有了它就可以开始进行识别了,也就不再新建工程了,其它代码和上一小节的工程中代码一致,只是修改了readFarme()方法如下,里面有些路径需要修改成自己电脑中文件所在路径:
//读取摄像头信息 void MainWindow::readFarme() { capture>>cap; //读取当前帧 if (!cap.empty()) //判断当前帧是否捕捉成功 **这步很重要 { cvtColor(cap, cap_gray, CV_BGR2GRAY); //转为灰度图 equalizeHist(cap_gray, cap_gray); //直方图均衡化,增加对比度方便处理 if (!face_cascade.load("D:\\openCV3_2\\opencv-3.2.0\\data\\haarcascades\\haarcascade_frontalface_alt.xml")) { qDebug("Load haarcascade_frontalface_alt failed!"); return; } Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer(); modelPCA->load("E:\\workspace\\Qt_workspace\\build-face-Desktop_Qt_5_11_1_MinGW_32bit-Debug\\MyFacePCAModel.xml");//加载分类器 if (!eye_Classifier.load("D:\\openCV3_2\\opencv-3.2.0\\data\\haarcascades\\haarcascade_eye.xml")) //需要将xml文档放在自己指定的路径下 { qDebug("Load haarcascade_eye.xml failed!"); return; } //检测关于脸部位置 face_cascade.detectMultiScale(cap_gray, faceRect, 1.1, 3, 0 | CV_HAAR_DO_ROUGH_SEARCH, Size(50, 50));//检测 for (size_t i = 0; i < faceRect.size(); i++) { rectangle(cap, faceRect[i], Scalar(0, 255, 0)); //用绿色矩形画出检测到的位置 Mat faceROI = cap_gray(faceRect[i]); int predictPCA = 0; Mat face_test; Point text_lb; // 不加前面的cv::的话resize()方法会和MainWindow中的resize()冲突。 cv::resize(faceROI,face_test,Size(92, 112)); imshow("frame", face_test); //测试图像应该是灰度图 predictPCA = modelPCA->predict(face_test); qDebug("%d",predictPCA); if(predictPCA==3) // 这个3也就是上面labels.push_back(3);和图片是一一对应的 { string name = "xufan"; // 文本提示 text_lb = Point(faceRect[i].x, faceRect[i].y); putText(cap, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255)); } //imshow("frame", faceROI); //检测关于眼睛部位位置 int eye_num=0; eye_Classifier.detectMultiScale(faceROI, eyeRect, 1.1, 1, 0 | CV_HAAR_DO_ROUGH_SEARCH, Size(30, 30));//检测 for (size_t j = 0; j < eyeRect.size(); j++) { Rect rect(faceRect[i].x + eyeRect[j].x, faceRect[i].y + eyeRect[j].y, eyeRect[j].width, eyeRect[j].height); rectangle(cap, rect, Scalar(255, 0, 0)); //用蓝色矩形画出检测到的位置 eye_num++; if(eye_num==2) break; } } imag = Mat2QImage(cap); // 将Mat转换成QImage对象来显示 imag = imag.scaled(ui->camera->width(), ui->camera->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//设置图片大小和label的长宽一致 ui->camera->setPixmap(QPixmap::fromImage(imag)); // 将图片显示到label上 } else qDebug("can not "); }
运行过后就会出现就可以识别到视频中的人脸:
CMake时会下载几个文件,下载失败会导致提示工程不可用,可直接手动从网上下载然后按照如下操作,添加到源文件中即可,如下所用到资源在本文最上面可以找到连接,添加文件如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。