雪泥鸿爪 发表于 2017-12-28 14:05:09

树莓派基于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]
查看完整版本: 树莓派基于opencv和百度ai平台的宿舍环境检测系统(一)

Baidu
map