[视觉实战案例]Qt+OpenCV实现USB摄像头监测移动物体并录制视频功能(帧差法)

1、背景介绍 最近手边的零食总是莫名其妙的减少,为了抓到一个元凶来帮我续零食,就想着使用手边的usb摄像头来实现一个动态物体监测和保存视频的功能,不过这里使用最简单的帧差法来实现物体的运动监测。
2、使用OpenCV的帧差法实现运动物体监测

  • 开发环境
    Qt5.9 + OpenCV
  • 硬件
    Logitech摄像头
    2.1 帧差法介绍
    运动物体图像在相邻两帧间差别较大,两帧差值后进行简单的图像处理,较容易判断是否存在物体移动,类似于剪纸动画,本例中使用帧差后判断阈值分割后的面积来确定是否存在物体运动。帧差法用前一帧图像作为当前帧的背景模型具有较好的实时性,其背景不积累,且更新速度快、算法简单、计算量小。算法的不足在于对环境噪声较为敏感,阈值的选择相当关键,选择过低不足以抑制图像中的噪声,过高则忽略了图像中有用的变化。对于比较大的、颜色一致的运动目标,有可能在目标内部产生空洞,无法完整地提取运动目标。
    2.2 帧差法部分实现代码
    将当前帧图像和上一帧图像进行灰度化,然后高斯滤波后做图像差值,选定合适的二值化阈值分割,最后对分割处理的区域面积进行判定。
    Mat grayframePre,frameDet; Mat frameNow,grayframeNow; cvtColor(matFrame,grayframeNow,COLOR_RGB2GRAY); cvtColor(framePre,grayframePre,COLOR_RGB2GRAY); GaussianBlur(grayframeNow,grayframeNow,Size(21,21),0,0); GaussianBlur(grayframePre,grayframePre,Size(21,21),0,0); absdiff(grayframeNow,grayframePre,frameDet); framePre = matFrame; threshold(frameDet,frameDet,20,255,THRESH_BINARY); Mat element = getStructuringElement(0,Size(3,3)); vector contours; dilate(frameDet,frameDet,element); findContours(frameDet,contours,RETR_TREE,CHAIN_APPROX_SIMPLE,Point()); qDebug()<<"Num"<

    3、在Qt平台下使用opencv对运动物体进行监测
    widget.h
    #ifndef WIDGET_H #define WIDGET_H#include #include "opencv2/opencv.hpp" #include using namespace cv; namespace Ui { class Widget; }class Widget : public QWidget { Q_OBJECTpublic: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: void on_btnOpenVedio_clicked(); void on_btnQuit_clicked(); void readFrame(); void on_ckb_Track_clicked(bool checked); private: Ui::Widget *ui; bool openCam; bool isTrack=false; bool isSaveFrame = false; QTimer *timer; VideoCapture *cap; Mat framePre; int fps,frameWidth,frameHeight; VideoWriter writer; int VideoNum = 0; //Mat转换QImage QImage cvMat2QImage(const cv::Mat& mat); }; #endif // WIDGET_H

    widget.cpp
    #pragma execution_character_set("utf-8") #include "widget.h" #include "ui_widget.h" #include #include using namespace std; Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); timer = new QTimer(this); timer->stop(); connect(timer,SIGNAL(timeout()),this,SLOT(readFrame())); openCam = true; cap = new VideoCapture(0); frameWidth = cap->get(CAP_PROP_FRAME_WIDTH); frameHeight = cap->get(CAP_PROP_FRAME_HEIGHT); fps = cap->get(CAP_PROP_FPS); qDebug()<<"width"<btnOpenVedio->setText("关闭摄像头"); timer->start(30); } else { ui->btnOpenVedio->setText("打开摄像头"); timer->stop(); } openCam = !openCam; }QImage Widget::cvMat2QImage(const cv::Mat &mat) { switch ( mat.type() ) { // 8-bit4 channel case CV_8UC4: { QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_RGB32 ); return image; }// 8-bit3 channel case CV_8UC3: { QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_RGB888 ); return image.rgbSwapped(); }// 8-bit1 channel case CV_8UC1: { static QVectorsColorTable; // only create our color table once if ( sColorTable.isEmpty() ) { sColorTable.resize( 256 ); for ( int i = 0; i < 256; ++i ) { sColorTable[i] = qRgb( i, i, i ); } } QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_Indexed8 ); image.setColorTable( sColorTable ); return image; }default: qDebug("Image format is not supported: depth=%d and %d channels\n", mat.depth(), mat.channels()); qWarning() << "cvMatToQImage - cv::Mat image type not handled in switch:" << mat.type(); break; }return QImage(); }void Widget::on_btnQuit_clicked() { timer->stop(); cap->release(); close(); }void Widget::readFrame() { Mat matFrame; cap->read(matFrame); if(isTrack) { Mat grayframePre,frameDet; Mat frameNow,grayframeNow; cvtColor(matFrame,grayframeNow,COLOR_RGB2GRAY); cvtColor(framePre,grayframePre,COLOR_RGB2GRAY); GaussianBlur(grayframeNow,grayframeNow,Size(21,21),0,0); GaussianBlur(grayframePre,grayframePre,Size(21,21),0,0); absdiff(grayframeNow,grayframePre,frameDet); framePre = matFrame; threshold(frameDet,frameDet,20,255,THRESH_BINARY); Mat element = getStructuringElement(0,Size(3,3)); vector contours; dilate(frameDet,frameDet,element); findContours(frameDet,contours,RETR_TREE,CHAIN_APPROX_SIMPLE,Point()); qDebug()<<"Num"<picshow->setPixmap(QPixmap::fromImage(Qimg)); }void Widget::on_ckb_Track_clicked(bool checked) { if(checked) { isTrack = true; } else { isTrack = false; }}

    4、界面效果展示[视觉实战案例]Qt+OpenCV实现USB摄像头监测移动物体并录制视频功能(帧差法)
    文章图片

    打开摄像头后,可以进行采集视频操作,勾选“打开追踪”,程序会调用帧差算法判定是否有运动物体,如果有物体运动,就保存运动时的视频。
    [视觉实战案例]Qt+OpenCV实现USB摄像头监测移动物体并录制视频功能(帧差法)
    文章图片

5、总结 【[视觉实战案例]Qt+OpenCV实现USB摄像头监测移动物体并录制视频功能(帧差法)】首先,两帧差是比较基础的检测运动物体的方法,虽然其运算速度快,但其无法过滤光照或微小抖动的干扰,而且运动目标会出现“重影”导致出现内部空洞。三帧差法是在相邻帧差法基础上改进的算法,在一定程度上优化了运动物体双边,粗轮廓的现象,相比之下,三帧差法比相邻帧差法更适用于物体移动速度较快的情况,比如道路上车辆的智能监控。

    推荐阅读