查看: 2210|回复: 0

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

[复制链接]
  • TA的每日心情
    开心
    2018-5-25 23:35
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2017-12-28 14:05:09 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 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{LPT%$R5WT%R6I({AG]AFE.png

    (六)驱动识别动画效果
    先用一段视频代替摄像头读取,实现对视频流的循环检测,并把确定的人脸保存为文件。
    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. }
    复制代码
    效果展示
    MM1_]R0[33TVN4[97IK8HO2.jpg

    回复

    使用道具 举报

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

    本版积分规则

    关闭

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



    手机版|小黑屋|与非网

    GMT+8, 2024-3-29 22:34 , Processed in 0.122232 second(s), 19 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.