回答

收藏

[大赛作品提交] 树莓派基于opencv和百度ai平台的宿舍环境检测系统(一)

Raspberry Pi Raspberry Pi 3367 人阅读 | 0 人回复 | 2017-12-28

本帖最后由 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、安装依赖项
  1. sudo apt-get install build-essential
  2. sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
  3. 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一个文件夹:
把所需的命令放到一个脚本文件中,方便编译:
  1. mkdir my_cmake.sh
复制代码
脚本内容:
  1. #!/bin/bash
  2. cmake -DCMAKE_BUILD_TYPE=RELEASE \
  3.     -DCMAKE_INSTALL_PREFIX=/usr/local \
  4.     -DINSTALL_C_EXAMPLES=OFF \
  5.     -DOPENCV_EXTRA_MODULES_PATH=/home/opencv_make/opencv_contrib-3.2.0/modules \
  6.     -DPYTHON_EXCUTABLE=/usr/lib/python3 \
  7.     -DWITH_TBB=ON \
  8.     -DWITH_V4L=ON \
  9.     -DWITH_QT=ON  \
  10.     -DWITH_GTK=ON \
  11.     -DWITH_OPENGL=ON \ ..
复制代码
DPYTHON_EXCUTABLE如果是用py开发的话可以加上,但楼主是忠实的c++粉丝,没碰过pu,所以就不编译省了时间。
DOPENCV_EXTRA_MODULES_PATH的路径按照你自己的来配置。
这里-DWITH_QT最好不要加上,是个大坑,具体见我后面的帖子。
接下来就是漫长的cmake,运行脚本:
  1. ./my_cmake.sh
复制代码
4、make
  1. make -j4
复制代码
启动四个线程编译,快很多。
过程中经常会有错误出现,不需要惊慌,这是cpu压力过大时gcc出错,正常现象,继续运行上述命令。如果来来去去都不行,那就只好
  1. make
复制代码
(三)opencv的运动检测
目前opencv的运动检测一般是采用模版匹配法,有两种匹配方式比较流行:
一个可以用第一帧匹配;
或者采用上一帧匹配;
1、匹配函数:
  1. void cv::minMaxLoc        (        InputArray         src,
  2. double *         minVal,
  3. double *         maxVal = 0,
  4. Point *         minLoc = 0,
  5. Point *         maxLoc = 0,
  6. InputArray         mask = noArray()
  7. )        
复制代码
src:   输入单通道阵列。

minval:指向返回最小值;如果不需要,则使用NULL。

maxval:指针指向返回的最大值;如果不需要,则使用NULL。

minloc:指针指向返回的最小位置(在2D情况下);如果不需要,则使用NULL。

maxloc:指针指向返回的最大位置(在2D情况下);如果不需要,则使用NULL。

mask:  可选掩码用于选择子阵列。


2、调用方式:
  1. void on_Matching( int, void* )
  2. {
  3.     //【1】给局部变量初始化
  4.     Mat srcImage;
  5.     g_srcImage.copyTo( srcImage );

  6.     //【2】初始化用于结果输出的矩阵
  7.     int resultImage_cols =  g_srcImage.cols - g_templateImage.cols + 1;
  8.     int resultImage_rows = g_srcImage.rows - g_templateImage.rows + 1;
  9.     g_resultImage.create( resultImage_cols, resultImage_rows, CV_32FC1 );

  10.     //【3】进行匹配和标准化
  11.     matchTemplate( g_srcImage, g_templateImage, g_resultImage, g_nMatchMethod );
  12.     normalize( g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat() );

  13.     //【4】通过函数 minMaxLoc 定位最匹配的位置
  14.     double minValue; double maxValue; Point minLocation; Point maxLocation;
  15.     Point matchLocation;
  16.     minMaxLoc( g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat() );

  17.     //【5】对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值有着更高的匹配结果. 而其余的方法, 数值越大匹配效果越好
  18.     if( g_nMatchMethod  == TM_SQDIFF || g_nMatchMethod == TM_SQDIFF_NORMED )
  19.     { matchLocation = minLocation; }
  20.     else
  21.     { matchLocation = maxLocation; }

  22.     //【6】绘制出矩形,并显示最终结果
  23.     rectangle( srcImage, matchLocation, Point( matchLocation.x + g_templateImage.cols , matchLocation.y + g_templateImage.rows ), Scalar(0,0,255), 2, 8, 0 );
  24.     rectangle( g_resultImage, matchLocation, Point( matchLocation.x + g_templateImage.cols , matchLocation.y + g_templateImage.rows ), Scalar(0,0,255), 2, 8, 0 );

  25.     imshow( WINDOW_NAME1, srcImage );
  26.     imshow( WINDOW_NAME2, g_resultImage );

  27. }
复制代码
(四)opencv的人脸检测
1、加载训练模型:
opencv识别人脸,事实上都是通过与训练出来的结果进行处理得到结果的,这些结果以xml文件的形式存放在
  1. /opencv-3.2.0/data/haarcascades/haarcascade_frontalface_alt2.xml
复制代码
里面。
创建分类器CascadeClassifier,用分类器的成员函数load加载模型。

2、检测函数:
使用对象:CascadeClassifier
函数:
  1. void cv::CascadeClassifier::detectMultiScale        (        InputArray         image,
  2. std::vector< Rect > &         objects,
  3. double         scaleFactor = 1.1,
  4. int         minNeighbors = 3,
  5. int         flags = 0,
  6. Size         minSize = Size(),
  7. Size         maxSize = Size()
  8. )        
复制代码
总共有7个参数
image: 要检测的图片,一般为灰度图
objects: Rect型的容器,存放所有检测出的人脸,每个人脸是一个矩形
scaleFactor: 缩放因子,对图片进行缩放,默认为1.1
minNeighbors: 最小邻居数,默认为3
flags: 兼容老版本的一个参数,在3.0版本中没用处。默认为0
minSize: 最小尺寸,检测出的人脸最小尺寸
maxSize: 最大尺寸,检测出的人脸最大尺寸

3、检测函数:
  1. bool face_detect(cv::Mat& img)
  2. {
  3.     string xmlPath="<编译opencv的目录>/opencv-3.2.0/data/haarcascades/haarcascade_frontalface_alt2.xml";
  4.     CascadeClassifier ccf;   //创建分类器对象
  5.     if(img.empty())
  6.     {
  7.         cout<<"error";
  8.         return 0;
  9.     }
  10.     if(!ccf.load(xmlPath))   //加载训练文件
  11.     {
  12.         cout<<"不能加载指定的xml文件"<<endl;
  13.         return 0;
  14.     }
  15.     vector<Rect> faces;  //创建一个容器保存检测出来的脸
  16.     Mat gray;
  17.     cvtColor(img,gray,CV_BGR2GRAY); //转换成灰度图,因为harr特征从灰度图中提取
  18.     equalizeHist(gray,gray);  //直方图均衡行
  19.     ccf.detectMultiScale(gray,faces,1.1,3,0,Size(10,10),Size(1000,1000)); //检测人脸
  20.     /*for(vector<Rect>::const_iterator iter=faces.begin();iter!=faces.end();iter++)
  21.     {
  22.         rectangle(img,*iter,Scalar(0,0,255),2,8); //画出脸部矩形
  23.     }*/
  24.     if(faces.empty()) return false;
  25.     else return true;
  26. }
复制代码
(五)ui界面的opencv人脸检测小尝试
虽然项目到最后是做成一个无界面的孤儿进程程序,但开发过程中来点ui还是比较赏心悦目的(我才不会告诉你是因为多文件调用百度ai的sdk出错我才不用界面的呢!)
思路大概就是用opencv处理完图像之后,转成QImage对象输出到QLable显示。
转换函数:
  1. QImage MatToQImage(const cv::Mat& mat)
  2. {
  3.     // 8-bits unsigned, NO. OF CHANNELS = 1
  4.     if(mat.type() == CV_8UC1)
  5.     {
  6.         QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
  7.         // Set the color table (used to translate colour indexes to qRgb values)
  8.         image.setColorCount(256);
  9.         for(int i = 0; i < 256; i++)
  10.         {
  11.             image.setColor(i, qRgb(i, i, i));
  12.         }
  13.         // Copy input Mat
  14.         uchar *pSrc = mat.data;
  15.         for(int row = 0; row < mat.rows; row ++)
  16.         {
  17.             uchar *pDest = image.scanLine(row);
  18.             memcpy(pDest, pSrc, mat.cols);
  19.             pSrc += mat.step;
  20.         }
  21.         return image;
  22.     }
  23.     // 8-bits unsigned, NO. OF CHANNELS = 3
  24.     else if(mat.type() == CV_8UC3)
  25.     {
  26.         // Copy input Mat
  27.         const uchar *pSrc = (const uchar*)mat.data;
  28.         // Create QImage with same dimensions as input Mat
  29.         QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
  30.         return image.rgbSwapped();
  31.     }
  32.     else if(mat.type() == CV_8UC4)
  33.     {
  34.         qDebug() << "CV_8UC4";
  35.         // Copy input Mat
  36.         const uchar *pSrc = (const uchar*)mat.data;
  37.         // Create QImage with same dimensions as input Mat
  38.         QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
  39.         return image.copy();
  40.     }
  41.     else
  42.     {
  43.         qDebug() << "ERROR: Mat could not be converted to QImage.";
  44.         return QImage();
  45.     }
  46. }
复制代码
由于有时候图像超过界面分辨率,不好查看效果,可以使用QImage的成员函数scaled处理。
具体主函数如下:
  1. #include "mainwindow.hpp"
  2. #include "ui_mainwindow.h"
  3. #include <QBoxLayout>


  4. MainWindow::MainWindow(QWidget *parent) :
  5.     QMainWindow(parent),
  6.     ui(new Ui::MainWindow)
  7. {
  8.     ui->setupUi(this);

  9.     //1
  10.     QWidget *widget=new QWidget();
  11.     this->setCentralWidget(widget);

  12.     Mat img=imread("<图片所在位置>");
  13.     if(img.empty()) qDebug()<<"error";
  14.     if(!face_detect(img)) close();

  15.     //2
  16.     QPixmap *mp = new QPixmap(QPixmap::fromImage(MatToQImage(img)));

  17.     label = new QLabel;
  18.     label->resize(800,300);

  19.     QPixmap fitpixmap = mp->scaled(label->size(), Qt::KeepAspectRatio);
  20.     label->setPixmap(fitpixmap);

  21.     //3
  22.     btn_mov_detect = new QPushButton(tr("ok"));
  23.     connect(btn_mov_detect,SIGNAL(clicked(bool)),this,SLOT(close()));

  24.     layout = new QGridLayout;
  25.     layout->addWidget(label,0,0,1,1);

  26.     QBoxLayout *button = new QBoxLayout(QBoxLayout::RightToLeft);
  27.     button->addWidget(btn_mov_detect);

  28.     layout->addLayout(button,1,1,1,1);
  29.     centralWidget()->setLayout(layout);

  30.     setWindowTitle(tr("Client"));
  31. }

  32. MainWindow::~MainWindow()
  33. {
  34.     delete ui;
  35. }

复制代码
效果展示


(六)驱动识别动画效果
先用一段视频代替摄像头读取,实现对视频流的循环检测,并把确定的人脸保存为文件。
  1. int main()
  2. {
  3.     int counter = 0;
  4.     int calcul = 0;

  5.     VideoCapture capture;
  6.     capture.open("<路径>/4.mp4");
  7.     Mat frame;

  8.     while(!check_flag)
  9.     {
  10.         /*VideoCapture结构体,保存图像信息,open()参数为int index(0为默认摄像头),读入摄像头视频,
  11.              *                           open()参数为路径,读入视频文件*/

  12.         while(1)
  13.         {
  14.             /*采用>>的方式读入视频*/
  15.             capture >> frame;
  16.             if(frame.empty())
  17.                 break;
  18.             if(face_detect(frame))
  19.             {
  20.                 std::cout << "people\t" << std::endl;
  21.                 counter++;
  22.             }
  23.             else
  24.                 counter = 0;

  25.             std::cout << calcul++ << '\t';

  26.             if(counter >= 5)
  27.             {
  28.                 counter = 0;
  29.                 break;
  30.             }

  31.             /*imshow()在窗口中显示*/
  32.             imshow("read",frame);

  33.             /*WaitKey()控制帧率*/
  34.             waitKey(1);
  35.         }



  36.         std::cout << "judge";

  37.         imwrite("frame.jpg", frame);   //  将image图像保存为my.jpg

  38.     }

  39.     capture.release();
  40.     std::cout << "finish";

  41.     return 0;
  42. }
复制代码
效果展示


关注下面的标签,发现更多相似文章
分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条