本帖最后由 ky123 于 2018-1-31 14:12 编辑
感谢e络盟提供的助赛基金。本次作品拟定搭建一个宿舍环境检测平台,可以通过人脸检测和识别,准确地识别宿舍内人体信息。
(一)方案确定
方案拟定使用人体红外模块激活opencv的运动检测,运动检测激活opencv进行人脸检测,在检测到人脸后上传至百度ai平台匹配用户组、检测信息,在匹配率过低的情况下发送邮件警告主体。
系统信息:树莓派+ubuntu mate 16.04+qt5+opencv 3.3.0+百度ai人脸识别sdk0.4
(二)准备工作——opencv安装
1、源码下载,可以到官网下载opencv3.3.0的源码:
opencv 3.3.0:https://github.com/opencv/opencv/archive/3.3.0.zip
opencv_contrib 3.3.0 :https://github.com/opencv/opencv_contrib/archive/3.3.0.tar.gz
2、安装依赖项- sudo apt-get install build-essential
- sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
- sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
复制代码 3、cmake
把opencv和opencv_contrib提取到home目录下,在opencv下mkdir一个文件夹:
把所需的命令放到一个脚本文件中,方便编译:脚本内容:- #!/bin/bash
- cmake -DCMAKE_BUILD_TYPE=RELEASE \
- -DCMAKE_INSTALL_PREFIX=/usr/local \
- -DINSTALL_C_EXAMPLES=OFF \
- -DOPENCV_EXTRA_MODULES_PATH=/home/opencv_make/opencv_contrib-3.2.0/modules \
- -DPYTHON_EXCUTABLE=/usr/lib/python3 \
- -DWITH_TBB=ON \
- -DWITH_V4L=ON \
- -DWITH_QT=ON \
- -DWITH_GTK=ON \
- -DWITH_OPENGL=ON \ ..
复制代码 DPYTHON_EXCUTABLE如果是用py开发的话可以加上,但楼主是忠实的c++粉丝,没碰过pu,所以就不编译省了时间。
DOPENCV_EXTRA_MODULES_PATH的路径按照你自己的来配置。
这里-DWITH_QT最好不要加上,是个大坑,具体见我后面的帖子。
接下来就是漫长的cmake,运行脚本:4、make
启动四个线程编译,快很多。
过程中经常会有错误出现,不需要惊慌,这是cpu压力过大时gcc出错,正常现象,继续运行上述命令。如果来来去去都不行,那就只好
(三)opencv的运动检测
目前opencv的运动检测一般是采用模版匹配法,有两种匹配方式比较流行:
一个可以用第一帧匹配;
或者采用上一帧匹配;
1、匹配函数:- void cv::minMaxLoc ( InputArray src,
- double * minVal,
- double * maxVal = 0,
- Point * minLoc = 0,
- Point * maxLoc = 0,
- InputArray mask = noArray()
- )
复制代码 src: 输入单通道阵列。
minval:指向返回最小值;如果不需要,则使用NULL。 maxval:指针指向返回的最大值;如果不需要,则使用NULL。 minloc:指针指向返回的最小位置(在2D情况下);如果不需要,则使用NULL。 maxloc:指针指向返回的最大位置(在2D情况下);如果不需要,则使用NULL。 mask: 可选掩码用于选择子阵列。
2、调用方式:- void on_Matching( int, void* )
- {
- //【1】给局部变量初始化
- Mat srcImage;
- g_srcImage.copyTo( srcImage );
- //【2】初始化用于结果输出的矩阵
- int resultImage_cols = g_srcImage.cols - g_templateImage.cols + 1;
- int resultImage_rows = g_srcImage.rows - g_templateImage.rows + 1;
- g_resultImage.create( resultImage_cols, resultImage_rows, CV_32FC1 );
- //【3】进行匹配和标准化
- matchTemplate( g_srcImage, g_templateImage, g_resultImage, g_nMatchMethod );
- normalize( g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat() );
- //【4】通过函数 minMaxLoc 定位最匹配的位置
- double minValue; double maxValue; Point minLocation; Point maxLocation;
- Point matchLocation;
- minMaxLoc( g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat() );
- //【5】对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值有着更高的匹配结果. 而其余的方法, 数值越大匹配效果越好
- if( g_nMatchMethod == TM_SQDIFF || g_nMatchMethod == TM_SQDIFF_NORMED )
- { matchLocation = minLocation; }
- else
- { matchLocation = maxLocation; }
- //【6】绘制出矩形,并显示最终结果
- rectangle( srcImage, matchLocation, Point( matchLocation.x + g_templateImage.cols , matchLocation.y + g_templateImage.rows ), Scalar(0,0,255), 2, 8, 0 );
- rectangle( g_resultImage, matchLocation, Point( matchLocation.x + g_templateImage.cols , matchLocation.y + g_templateImage.rows ), Scalar(0,0,255), 2, 8, 0 );
- imshow( WINDOW_NAME1, srcImage );
- imshow( WINDOW_NAME2, g_resultImage );
- }
复制代码 (四)opencv的人脸检测
1、加载训练模型:
opencv识别人脸,事实上都是通过与训练出来的结果进行处理得到结果的,这些结果以xml文件的形式存放在- /opencv-3.2.0/data/haarcascades/haarcascade_frontalface_alt2.xml
复制代码 里面。
创建分类器CascadeClassifier,用分类器的成员函数load加载模型。
2、检测函数:
使用对象:CascadeClassifier
函数:- void cv::CascadeClassifier::detectMultiScale ( InputArray image,
- std::vector< Rect > & objects,
- double scaleFactor = 1.1,
- int minNeighbors = 3,
- int flags = 0,
- Size minSize = Size(),
- Size maxSize = Size()
- )
复制代码总共有7个参数
image: 要检测的图片,一般为灰度图
objects: Rect型的容器,存放所有检测出的人脸,每个人脸是一个矩形
scaleFactor: 缩放因子,对图片进行缩放,默认为1.1
minNeighbors: 最小邻居数,默认为3
flags: 兼容老版本的一个参数,在3.0版本中没用处。默认为0
minSize: 最小尺寸,检测出的人脸最小尺寸
maxSize: 最大尺寸,检测出的人脸最大尺寸
3、检测函数:- bool face_detect(cv::Mat& img)
- {
- string xmlPath="<编译opencv的目录>/opencv-3.2.0/data/haarcascades/haarcascade_frontalface_alt2.xml";
- CascadeClassifier ccf; //创建分类器对象
- if(img.empty())
- {
- cout<<"error";
- return 0;
- }
- if(!ccf.load(xmlPath)) //加载训练文件
- {
- cout<<"不能加载指定的xml文件"<<endl;
- return 0;
- }
- vector<Rect> faces; //创建一个容器保存检测出来的脸
- Mat gray;
- cvtColor(img,gray,CV_BGR2GRAY); //转换成灰度图,因为harr特征从灰度图中提取
- equalizeHist(gray,gray); //直方图均衡行
- ccf.detectMultiScale(gray,faces,1.1,3,0,Size(10,10),Size(1000,1000)); //检测人脸
- /*for(vector<Rect>::const_iterator iter=faces.begin();iter!=faces.end();iter++)
- {
- rectangle(img,*iter,Scalar(0,0,255),2,8); //画出脸部矩形
- }*/
- if(faces.empty()) return false;
- else return true;
- }
复制代码 (五)ui界面的opencv人脸检测小尝试
虽然项目到最后是做成一个无界面的孤儿进程程序,但开发过程中来点ui还是比较赏心悦目的(我才不会告诉你是因为多文件调用百度ai的sdk出错我才不用界面的呢!)
思路大概就是用opencv处理完图像之后,转成QImage对象输出到QLable显示。
转换函数:- QImage MatToQImage(const cv::Mat& mat)
- {
- // 8-bits unsigned, NO. OF CHANNELS = 1
- if(mat.type() == CV_8UC1)
- {
- QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
- // Set the color table (used to translate colour indexes to qRgb values)
- image.setColorCount(256);
- for(int i = 0; i < 256; i++)
- {
- image.setColor(i, qRgb(i, i, i));
- }
- // Copy input Mat
- uchar *pSrc = mat.data;
- for(int row = 0; row < mat.rows; row ++)
- {
- uchar *pDest = image.scanLine(row);
- memcpy(pDest, pSrc, mat.cols);
- pSrc += mat.step;
- }
- return image;
- }
- // 8-bits unsigned, NO. OF CHANNELS = 3
- else if(mat.type() == CV_8UC3)
- {
- // Copy input Mat
- const uchar *pSrc = (const uchar*)mat.data;
- // Create QImage with same dimensions as input Mat
- QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
- return image.rgbSwapped();
- }
- else if(mat.type() == CV_8UC4)
- {
- qDebug() << "CV_8UC4";
- // Copy input Mat
- const uchar *pSrc = (const uchar*)mat.data;
- // Create QImage with same dimensions as input Mat
- QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
- return image.copy();
- }
- else
- {
- qDebug() << "ERROR: Mat could not be converted to QImage.";
- return QImage();
- }
- }
复制代码 由于有时候图像超过界面分辨率,不好查看效果,可以使用QImage的成员函数scaled处理。
具体主函数如下:- #include "mainwindow.hpp"
- #include "ui_mainwindow.h"
- #include <QBoxLayout>
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- //1
- QWidget *widget=new QWidget();
- this->setCentralWidget(widget);
- Mat img=imread("<图片所在位置>");
- if(img.empty()) qDebug()<<"error";
- if(!face_detect(img)) close();
- //2
- QPixmap *mp = new QPixmap(QPixmap::fromImage(MatToQImage(img)));
- label = new QLabel;
- label->resize(800,300);
- QPixmap fitpixmap = mp->scaled(label->size(), Qt::KeepAspectRatio);
- label->setPixmap(fitpixmap);
- //3
- btn_mov_detect = new QPushButton(tr("ok"));
- connect(btn_mov_detect,SIGNAL(clicked(bool)),this,SLOT(close()));
- layout = new QGridLayout;
- layout->addWidget(label,0,0,1,1);
- QBoxLayout *button = new QBoxLayout(QBoxLayout::RightToLeft);
- button->addWidget(btn_mov_detect);
- layout->addLayout(button,1,1,1,1);
- centralWidget()->setLayout(layout);
- setWindowTitle(tr("Client"));
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
复制代码 效果展示
(六)驱动识别动画效果
先用一段视频代替摄像头读取,实现对视频流的循环检测,并把确定的人脸保存为文件。- int main()
- {
- int counter = 0;
- int calcul = 0;
- VideoCapture capture;
- capture.open("<路径>/4.mp4");
- Mat frame;
- while(!check_flag)
- {
- /*VideoCapture结构体,保存图像信息,open()参数为int index(0为默认摄像头),读入摄像头视频,
- * open()参数为路径,读入视频文件*/
- while(1)
- {
- /*采用>>的方式读入视频*/
- capture >> frame;
- if(frame.empty())
- break;
- if(face_detect(frame))
- {
- std::cout << "people\t" << std::endl;
- counter++;
- }
- else
- counter = 0;
- std::cout << calcul++ << '\t';
- if(counter >= 5)
- {
- counter = 0;
- break;
- }
- /*imshow()在窗口中显示*/
- imshow("read",frame);
- /*WaitKey()控制帧率*/
- waitKey(1);
- }
- std::cout << "judge";
- imwrite("frame.jpg", frame); // 将image图像保存为my.jpg
- }
- capture.release();
- std::cout << "finish";
- return 0;
- }
复制代码 效果展示
|