树莓派基于opencv和百度ai平台的宿舍环境检测系统(一)
本帖最后由 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-dev3、cmake
把opencv和opencv_contrib提取到home目录下,在opencv下mkdir一个文件夹:
把所需的命令放到一个脚本文件中,方便编译:mkdir my_cmake.sh脚本内容:#!/bin/bash
cmake -DCM***E_BUILD_TYPE=RELEASE \
-DCM***E_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,运行脚本:./my_cmake.sh4、make
make -j4启动四个线程编译,快很多。
过程中经常会有错误出现,不需要惊慌,这是cpu压力过大时gcc出错,正常现象,继续运行上述命令。如果来来去去都不行,那就只好
make(三)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,***NAL(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***");
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;
}效果展示
页:
[1]