OpenCV2帧间差分检测运动目标
来源:互联网 发布:重庆优化网站 编辑:程序博客网 时间:2024/06/06 07:17
对于一个固定的场景(背景),我们感兴趣的是在场景中运动的物体(前景)。前景提取是在智能监控应用中的一个基础步骤。
帧间差分:在运动目标检测中,简单来说,就是背景与当前帧之间的差异。数字图像可以表示成一个矩阵,矩阵中每一个元素叫一个像素点。帧间差分=绝对值(背景-当前帧图像)。
怎么确定前景?
我们取帧间差分足够大的像素点,将这些像素点作为前景像素。
基于帧间差分的运动目标的主要问题:
背景建模的好坏直接影响检测的效果,由于背景是随时间改变的。如果不对背景进行更新,可想而知,检测效果不会好。所以,第一个问题就是,背景如何建模?
另外,发射光(如运动物体在大理石地面的倒影)对运动目标检测也会造成一定影响。那么,怎样处理反射光?
除了这两个问题还有其他许多问题,想了解研究的童鞋可以看看这方面的论文。
准备
在opencv2中,下面的代码实现帧间差分。
cv::absdiff(backImage, //背景gray, //当前帧foreground//前景(未加工前));
帧间差分后,通过阈值,更加精确的标识出前景物体。
//阈值,前景(黑,0),背景(白,255)cv::threshold(foreground, //前景(未加工前)output,//前景(阈值后)threshold, //阈值255, cv::THRESH_BINARY_INV);
针对背景建模问题,我们采用动态建模。每一帧通过加权和更新背景,公式如下:
背景=(1-a)背景+a*第t帧图像(去除前景)
参数a时学习率,如a=0.001。当第t帧时,通过将当前的背景和第t帧图像(除掉前景像素点)加权和,获得新的背景,用来检测第t+1帧前景。
//对背景加(通过掩模去除前景像素)cv::accumulateWeighted(gray, //第t帧图像 background, //背景 learningRate, //学习率 output //第t帧前景,所有像素为0(黑)的点不参加运算。 );
到此,核心的部分完成。
示例
程序使用了策略模式。策略模式说白了,就是将算法都封装在类中,面向对象的思想,方便扩展和重用。
本程序没有直接写在在一个源文件,而是在以期写好的一些类的基础上进行的。总之,建议用面向对象的方式写程序,掌握策略模式、控制器模式、单件、MVC模式。
VideoProcess类处理每一帧,FrameProcessor抽象类,用来继承,定义自己的帧处理函数。
源.cpp
#include"BGFGSegmentor.h"int main(){ //类对象 VideoProcess processor; BGFGSegmentor sG; //设置阈值 sG.setThreshold(30); //处理的视频 processor.setInput("D:/MatlabWork/classroom.avi"); //设置帧处理函数 processor.setFrameProcessor(&sG); processor.displayOut("前景"); processor.displayInput("输入"); //显示帧时的延迟 processor.setDelay(1000. / processor.getFrameRate()); //计算检测耗时 double time = cv::getTickCount(); processor.run(); time = cv::getTickCount() - time; std::cout << time / cv::getTickFrequency() <<"秒"<< std::endl; cv::waitKey(0);}
VideoProcess.h
#include<opencv2\highgui\highgui.hpp>#include<opencv2\imgproc\imgproc.hpp>#include<opencv2\core\core.hpp>#include<iostream>class FrameProcessor {public: // 帧处理函数(纯虚函数),用来继承 virtual void process(cv::Mat &input, cv::Mat &output) = 0;};class VideoProcess{private: void(*process)(cv::Mat&, cv::Mat&); cv::VideoCapture capture; bool callIt; std::string Input; std::string Output; int delay; long fnumbers; long frameToStop; bool stop; cv::VideoWriter w; std::string outFlie; FrameProcessor *frameProcessor;public: VideoProcess() :callIt(true), delay(0), fnumbers(0), stop(false), frameToStop(-1), frameProcessor(0){} //设置回调函数 void setFrameProcessor(void(*P)(cv::Mat&, cv::Mat&)) { frameProcessor = 0; process = P; } void setFrameProcessor(FrameProcessor* frameProcessorPtr) { process = 0; frameProcessor = frameProcessorPtr; } //得到视频编码类型 int getCodec(char codec[4]) { union { int value; char code[4]; } returned; returned.value = static_cast<int>(capture.get(CV_CAP_PROP_FOURCC)); codec[0] = returned.code[0]; codec[1] = returned.code[1]; codec[2] = returned.code[2]; codec[3] = returned.code[3]; return returned.value; } //设置输出视频文件名 bool setOutput(const std::string &filename, int codec = 0, double framerate = 0.0, bool isColor = false) { outFlie = filename; if (framerate == 0.0) { framerate = getFrameRate(); } char c[4]; if (codec == 0) { codec = getCodec(c); } //摄像头 //return w.open(outFlie, -1, framerate, getFrameSize(), isColor); //视频文件 return w.open(outFlie, codec, framerate, getFrameSize(), isColor); } //得到帧的尺寸 cv::Size getFrameSize() { // get size of from the capture device int w = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH)); int h = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT)); return cv::Size(w, h); } //设置输入视频文件 bool setInput(std::string name) { fnumbers = 0; capture.release(); return capture.open(name); } //设置摄像头ID bool setInput(int i) { fnumbers = 0; capture.release(); return capture.open(i); } //显示输入帧窗口 void displayInput(std::string wn) { Input = wn; cv::namedWindow(wn); } //显示输出帧窗口 void displayOut(std::string wo) { Output = wo; cv::namedWindow(wo); } //清空 void dontDisplay() { cv::destroyWindow(Input); cv::destroyWindow(Output); Input.clear(); Output.clear(); } //判断打开帧状态 bool isOpened() { return capture.isOpened(); } //设置停止标记位 bool isStopped() { return stop; } //读取下一帧 bool readNextFrame(cv::Mat& frame) { return capture.read(frame); } //调用回调函数 void callProcess() { callIt = true; } //不调用回调函数 void dontCallProcess() { callIt = false; } //停止 void stopIt() { stop = true; } //得到帧数 long getFrameNumber() { long f = static_cast<long>(capture.get(CV_CAP_PROP_POS_FRAMES)); return f; } //设置延迟 void setDelay(int d) { delay = d; } //帧率 double getFrameRate() { double r = capture.get(CV_CAP_PROP_FPS); return r; } //写入视频帧 void wNextFrame(cv::Mat& frame) { w.write(frame); } //run函数 void run() { cv::Mat frame; cv::Mat output; //判断打开 if (!isOpened()) { return; } stop = false; //循环 while (!isStopped()) { if (!readNextFrame(frame)) break; if (Input.length() != 0) { cv::imshow(Input, frame); } if (callIt) { if (process) { process(frame, output); } else { frameProcessor->process(frame, output); } fnumbers++; } else { output = frame; } if (outFlie.length() != 0) { wNextFrame(output); } if (Output.length() != 0) { cv::imshow(Output, output); if (delay >= 0 && cv::waitKey(delay) >= 0) { stopIt(); } if (frameToStop >= 0 && getFrameNumber() == frameToStop) stopIt(); } } }};
BGFGSegmentor.h
#include"VideoProcess.h"class BGFGSegmentor :public FrameProcessor{ //当前灰度图 cv::Mat gray; //累加的背景 cv::Mat background; //背景图像 cv::Mat backImage; //前景 cv::Mat foreground; //学习率 double learningRate; //阈值 int threshold;public: BGFGSegmentor() :threshold(10), learningRate(0.01){} //设置阈值 void setThreshold(int T) { threshold = T; } //基于帧间差分和动态背景建模的运动目标检测 void process(cv::Mat& frame, cv::Mat& output) { cv::cvtColor(frame, gray, CV_BGR2GRAY); //初始化背景 if (background.empty()) { gray.convertTo(background, CV_32F); } //转换背景为8U格式 background.convertTo(backImage, CV_8U); //计算差值 cv::absdiff(backImage, gray, foreground); //阈值,前景(黑,0),背景(白,255) cv::threshold(foreground, output, threshold, 255, cv::THRESH_BINARY_INV); //对背景加(通过掩模去除前景像素) cv::accumulateWeighted(gray, background, learningRate, output); }};
结果:
- OpenCV2帧间差分检测运动目标
- OpenCV运动目标检测
- 运动目标检测
- 运动目标检测小结
- 运动目标检测小结
- 运动目标检测概述
- 运动目标检测算法
- 运动目标检测小结
- opencv 运动目标检测
- VIBE 运动目标检测
- 运动目标检测--光流法
- 运动目标检测--帧间差分法
- 运动目标检测--光流法
- 运动目标检测--帧间差分法
- 运动目标检测 帧差法
- 运动目标检测算法
- 运动目标检测
- 运动目标检测
- Java的keytool命令都有哪些用途?
- FileMerge ——文件对比工具使用
- Android开发贴士经典大集合
- 使用SQLite数据库和Access数据库的一些经验总结
- We are unable to process your request. An unknown error occurred.
- OpenCV2帧间差分检测运动目标
- 数据缓存
- 窗口操作
- 258. Add Digits
- 奇怪的Hibernate——当?遇上%
- Class.forName()用法详解
- Java中JDK安装以及环境变量设置
- 字符串解析 —— URL
- New Start!