最近手边的零食总是莫名其妙的减少,为了抓到一个元凶来帮我续零食,就想着使用手边的usb摄像头来实现一个动态物体监测和保存视频的功能,不过这里使用最简单的帧差法来实现物体的运动监测。
运动物体图像在相邻两帧间差别较大,两帧差值后进行简单的图像处理,较容易判断是否存在物体移动,类似于剪纸动画,本例中使用帧差后判断阈值分割后的面积来确定是否存在物体运动。帧差法用前一帧图像作为当前帧的背景模型具有较好的实时性,其背景不积累,且更新速度快、算法简单、计算量小。算法的不足在于对环境噪声较为敏感,阈值的选择相当关键,选择过低不足以抑制图像中的噪声,过高则忽略了图像中有用的变化。对于比较大的、颜色一致的运动目标,有可能在目标内部产生空洞,无法完整地提取运动目标。
将当前帧图像和上一帧图像进行灰度化,然后高斯滤波后做图像差值,选定合适的二值化阈值分割,最后对分割处理的区域面积进行判定。
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"<
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_OBJECT
public:
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-bit 4 channel
case CV_8UC4:
{
QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_RGB32 );
return image;
}
// 8-bit 3 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-bit 1 channel
case CV_8UC1:
{
static QVector sColorTable;
// 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;
}
}
打开摄像头后,可以进行采集视频操作,勾选“打开追踪”,程序会调用帧差算法判定是否有运动物体,如果有物体运动,就保存运动时的视频。
首先,两帧差是比较基础的检测运动物体的方法,虽然其运算速度快,但其无法过滤光照或微小抖动的干扰,而且运动目标会出现“重影”导致出现内部空洞。三帧差法是在相邻帧差法基础上改进的算法,在一定程度上优化了运动物体双边,粗轮廓的现象,相比之下,三帧差法比相邻帧差法更适用于物体移动速度较快的情况,比如道路上车辆的智能监控。