很难准确地定义什么是前景,即什么样的运动被认为是感兴趣的,特别是在诸如光线渐变、突变,动态背景,伪装效应,阴影,鬼影等待具有挑战性的场景中,准确的定义前景并精确的提取出前景是件十分困难的任务,常用的方法是首先建立一个背景的模型,并用该模型不断的与视频中的每一帧图像进行比较,图像中与背景相似的区域被认为是背景,而不能上的区域成为前景,最后利用该图像信息更新背景模型,供下次检测使用,即传说中的背景减除法。
背景减除可以分为4个过程,即预处理、背景建模、前景检测、后处理。
一、预处理
主要包括对图像进行灰度化以及滤波。
1.灰度化,Canny边缘检测算法原理及其VC实现
2.滤波,高斯图像滤波原理及其编程离散化实现方法
二、背景建模
目前大多数思路是根据前N帧图像的灰度值进行区间统计从而得到一个具有统计意义的初始背景。在第一次的实现过程中,采用第一幅图片作为背景图,实现起来较为简单。
三、前景检测
当前最新的图像与背景做差,即可求得背景差图,然后根据一定的方法对改图进行二值化,最终获得运动前景区域,即实现图像分割。
关于图像的二值化,目前主要的难点在于阈值的选取,随着运动物体在整个监控区域内的运动,所拍摄的图片具有不同的灰度特性,因此阈值的选取是一个研究热点,目前多采用的方法有最大类间方差法,一维交叉熵阈值法,二维交叉熵阈值法以及其他的自适应阈值选取方法等
四、后处理(背景更新)
Opencv里的混合高斯模型方法,当前景里的一运动目标停止后,该模型会自动的将其当做背景来处理,但混合高斯模型比较耗时。还有一个方法就是用滑动平均来做,即:
cvRunningAvg( const CvArr* image, CvArr* acc, double alpha, const CvArr* mask=NULL );
该函数通过设置Alpha的值来更新背景,Alpha越大,背景更新得越快
五、实例代码
/*
******************************************static_background***********************************
********************************************by JA/2015-1-22***********************************
*/
#include "cv.h"
#include "highgui.h"
#include
#include
#include
#include
using namespace std;
int main(int argc, char** argv)
{
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
CvMat* pFrameMat = NULL;
CvMat* pFrMat = NULL;
CvMat* pBkMat = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
//创建窗口
cvNamedWindow("video", 1);
cvNamedWindow("background", 1);
cvNamedWindow("foreground", 1);
//使窗口有序排列
cvMoveWindow("video", 30, 0);
cvMoveWindow("background", 360, 0);
cvMoveWindow("foreground", 690, 0);
pCapture = cvCaptureFromAVI("video.mp4"); //读入已有视频用此句
//pCapture = cvCaptureFromCAM(0); //从摄像头读入视频用此
while (pFrame = cvQueryFrame(pCapture))
{
nFrmNum++;
//如果是第一帧,需要申请内存,并初始化
if (nFrmNum == 1)
{
pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U, 1);
pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U, 1);
pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
//转化成单通道图像再处理
cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
cvConvert(pFrImg, pFrMat);
cvConvert(pFrImg, pBkMat);
}
else
{
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
//先做高斯滤波,以平滑图像
cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0);
//当前帧跟背景图相减
cvAbsDiff(pFrameMat, pBkMat, pFrMat);
//二值化前景图
cvThreshold(pFrMat, pFrImg, 10, 255.0, CV_THRESH_BINARY);
//更新背景
cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);
//将背景转化为图像格式,用以显示
cvConvert(pBkMat, pBkImg);
pFrame->origin = IPL_ORIGIN_BL;
pFrImg->origin = IPL_ORIGIN_BL;
pBkImg->origin = IPL_ORIGIN_BL;
cvShowImage("video", pFrame);
cvShowImage("background", pBkImg);
cvShowImage("foreground", pFrImg);
//如果有按键事件,则跳出循环
//此等待也为cvShowImage函数提供时间完成显示
//等待时间可以根据CPU速度调整
if (cvWaitKey(2) >= 0)
break;
}
}
//销毁窗口
cvDestroyWindow("video");
cvDestroyWindow("background");
cvDestroyWindow("foreground");
//释放图像和矩阵
cvReleaseImage(&pFrImg);
cvReleaseImage(&pBkImg);
cvReleaseMat(&pFrameMat);
cvReleaseMat(&pFrMat);
cvReleaseMat(&pBkMat);
cvReleaseCapture(&pCapture);
}
参考文献
3.JustRemind《运动目标检测小结》
4.奋斗斌斌的专栏OpenCV实现静止背景下运动目标的检测