一.基本概念
背景消减法可以看作一种特殊的帧差法。
基本思想:利用当前帧图像与背景图像对应象素点的灰度差值来检测车辆。如果当前图像的象素点和背景图像的象素点灰度值差别很大,就认为此象素点有车通过;相反,如果当前图像的象素点和背景图像的象素点灰度值差别较小,在一定的阈值范围内,我们就认为此象素点为背景象素点。背景差值法假定图像背景是静止不变的,即图像背景不随图像帧数而变,可表示为b(x,y),定义图像序列为f(x,y,i),其中(x,y)为图像位置坐标,i为图像帧数,将每一帧图像的灰度值减去背景的灰度值可得到一个差值图像: id(x,y,i)=f(x,y,i)-b(x,y)
背景差值法检测运动目标速度快,检测准确,易于实现,其关键是背景图像的获取与背景更新。在实际应用中,静止背景是不易直接获得的,同时,由于背景图像的动态变化,需要通过视频序列的帧间信息来估计和恢复背景,即背景重建,所以要选择性的更新背景。然而它对于动态场景的变化,例如光照的变化和阴影的干扰等特别敏感。因此,选取一个可靠的背景模型进行背景的提取与动态更新以适应环境的变化是必要的。
使用背景差分法进行运动检测通常会遇到如下一些问题:
(1)背景获取:最简单的方法是在场景中没有运动目标的情况下进行,但在现实中肯定是无法满足的,如高速公路和城市交通的监控,需要一种方法能在场景存在运动目标的情况下获得背景图像。
(2)背景的扰动:如树叶、树枝等各种东西的摇动
(3)外界光照条件的变化
(4)背景中固定对象的移动
(5)背景的更新
(6)阴影的影响
背景消减法根据其背景模型的不同又可分为:直方图法、平均值法、单分布高斯背景模型、混合高斯分布背景模型、Kalman滤波器法,HMM模型法。
(1)背景即为第一帧图像,简单的先看看程序的基本步骤和实现方法。这种方法适用于第一帧即为全部背景,如果存在不是背景的物体,将出现误差;同时进行了简单的背景更新;
(2)背景为前50帧的平均值,对于高速的车流量较少的地段,背景提取较理想,车辆缓慢移动时会在背景上留下痕迹。同时也进行了背景的简单更新;
(3)同样的方法可以尝试不同的背景提取模型。
三.程序源代码
#include "stdafx.h" //背景为第一帧
#include "highgui.h"
#include "cv.h"
#include "cxcore.h"
#include "ml.h"
int main(int argc, char* argv[])
{
CvCapture* pCapture = cvCaptureFromFile("video.avi");
if( !pCapture) return -1;
IplImage* pImgFrame = NULL;
IplImage* pImgProcessed = NULL;
IplImage* pImgBackground = NULL;
IplImage* pyrImage = NULL;
CvMat* pMatFrame = NULL;
CvMat* pMatProcessed = NULL;
CvMat* pMatBackground = NULL;
cvNamedWindow("video", 0);
cvNamedWindow("background",0);
cvNamedWindow("processed",0);
cvResizeWindow("video",300,300); //重新定义窗口的大小
cvResizeWindow("background",300,300);
cvResizeWindow("processed",300,300);
cvMoveWindow("video", 0, 100); //设定窗口的位置
cvMoveWindow("background", 350, 100);
cvMoveWindow("processed", 700, 100);
//int thresh_low = 20;
//cvCreateTrackbar("Low","processed",&thresh_low,255,NULL); //创建滚动条,显示阈值
pImgFrame = cvQueryFrame( pCapture ); //取第一帧
pImgBackground = cvCreateImage(cvSize(pImgFrame->width, pImgFrame->height), IPL_DEPTH_8U,1);
pImgProcessed = cvCreateImage(cvSize(pImgFrame->width, pImgFrame->height), IPL_DEPTH_8U,1);
pyrImage = cvCreateImage(cvSize(pImgFrame->width/2, pImgFrame->height/2), IPL_DEPTH_8U,1);
pMatBackground = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1);
pMatProcessed = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1);
pMatFrame = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1);
cvSmooth(pImgFrame, pImgFrame, CV_GAUSSIAN, 3, 0, 0); //高斯平滑
cvCvtColor(pImgFrame, pImgProcessed, CV_BGR2GRAY);
cvCvtColor(pImgFrame, pImgBackground, CV_BGR2GRAY);
cvConvert(pImgProcessed, pMatBackground);
cvConvert(pImgProcessed, pMatFrame);
cvConvert(pImgProcessed, pMatProcessed);
cvSmooth(pMatBackground, pMatBackground, CV_GAUSSIAN, 3, 0, 0);
while(pImgFrame = cvQueryFrame( pCapture ))
{
cvShowImage("video", pImgFrame);
cvSmooth(pImgFrame, pImgFrame, CV_GAUSSIAN, 3, 0, 0);
cvCvtColor(pImgFrame, pImgProcessed, CV_BGR2GRAY);
cvConvert(pImgProcessed, pMatFrame);
cvSmooth(pMatFrame, pMatFrame, CV_GAUSSIAN, 3, 0, 0);
cvAbsDiff(pMatFrame, pMatBackground, pMatProcessed);
cvThreshold(pMatProcessed, pImgProcessed, 20, 255.0, CV_THRESH_BINARY);
//cvPyrDown(pImgProcessed,pyrImage,CV_GAUSSIAN_5x5); //GAUSSIAN金字塔向下采样
//cvPyrUp(pyrImage,pImgProcessed,CV_GAUSSIAN_5x5);
cvErode(pImgProcessed, pImgProcessed, 0, 1); //腐蚀
cvDilate(pImgProcessed, pImgProcessed, 0, 1); //膨胀
cvRunningAvg(pMatFrame, pMatBackground, 0.0003, 0); //背景更新
cvConvert(pMatBackground, pImgBackground);
cvFlip(pImgBackground,NULL,0); //垂直旋转图像
cvFlip(pImgProcessed,NULL,0);
cvShowImage("background", pImgBackground);
cvShowImage("processed", pImgProcessed);
if( cvWaitKey(33) == 27 ) //触发Esc键,跳出
{
break;
}
}
cvDestroyWindow("video");
cvDestroyWindow("background");
cvDestroyWindow("processed");
cvReleaseImage(&pImgProcessed);
cvReleaseImage(&pImgBackground);
cvReleaseMat(&pMatFrame);
cvReleaseMat(&pMatProcessed);
cvReleaseMat(&pMatBackground);
cvReleaseCapture(&pCapture);
return 0;
}
#include "stdafx.h" //背景为前50帧的平均值
#include "highgui.h"
#include "cv.h"
#include "cxcore.h"
#include "ml.h"
int main(int argc, char* argv[])
{
CvCapture* pCapture = cvCaptureFromFile("video.avi");
if( !pCapture) return -1;
IplImage* pImgFrame = NULL;
IplImage* pImgProcessed = NULL;
IplImage* pImgBackground = NULL;
IplImage* pyrImage = NULL;
CvMat* pMatFrame = NULL;
CvMat* pMatProcessed = NULL;
CvMat* pMatBackground = NULL;
cvNamedWindow("video", 0);
cvNamedWindow("background",0);
cvNamedWindow("processed",0);
cvResizeWindow("video",300,300); //重新定义窗口的大小
cvResizeWindow("background",300,300);
cvResizeWindow("processed",300,300);
cvMoveWindow("video", 0, 100); //设定窗口的位置
cvMoveWindow("background", 350, 100);
cvMoveWindow("processed", 700, 100);
//int thresh_low = 20;
//cvCreateTrackbar("Low","processed",&thresh_low,255,NULL); //创建滚动条,显示阈值
pImgFrame = cvQueryFrame( pCapture ); //取第一帧
pImgBackground = cvCreateImage(cvSize(pImgFrame->width, pImgFrame->height), IPL_DEPTH_8U,1);
pImgProcessed = cvCreateImage(cvSize(pImgFrame->width, pImgFrame->height), IPL_DEPTH_8U,1);
pyrImage = cvCreateImage(cvSize(pImgFrame->width/2, pImgFrame->height/2), IPL_DEPTH_8U,1);
pMatBackground = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1);
pMatProcessed = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1);
pMatFrame = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1);
int i=1;
CvMat* pMat = NULL,*pMat1=NULL,*pMatSum=NULL,*pMatAve=NULL;
pMat = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1); //存放当前帧
pMat1 = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1); //存放前一帧
pMatSum = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1); //所有帧的和
pMatAve = cvCreateMat(pImgFrame->height, pImgFrame->width, CV_32FC1); //帧的平均
while(i<=50)
{
pImgFrame = cvQueryFrame( pCapture );
cvCvtColor(pImgFrame, pImgBackground, CV_BGR2GRAY);
cvConvert(pImgBackground, pMat);
cvAdd(pMat,pMat1,pMatSum,NULL);
pMat1=pMatSum;
i++;
}
cvConvertScale(pMatSum, pMatAve, 0.02, 0 ); //求平均值
cvConvert(pMatAve, pMatBackground);
cvSmooth(pMatBackground, pMatBackground, CV_GAUSSIAN, 3, 0, 0);
cvSetCaptureProperty(pCapture, CV_CAP_PROP_POS_FRAMES, 0. );
cvSmooth(pImgFrame, pImgFrame, CV_GAUSSIAN, 3, 0, 0); //高斯平滑
cvCvtColor(pImgFrame, pImgProcessed, CV_BGR2GRAY);
cvConvert(pImgProcessed, pMatFrame);
cvConvert(pImgProcessed, pMatProcessed);
while(pImgFrame = cvQueryFrame( pCapture ))
{
cvShowImage("video", pImgFrame);
cvSmooth(pImgFrame, pImgFrame, CV_GAUSSIAN, 3, 0, 0);
cvCvtColor(pImgFrame, pImgProcessed, CV_BGR2GRAY);
cvConvert(pImgProcessed, pMatFrame);
cvSmooth(pMatFrame, pMatFrame, CV_GAUSSIAN, 3, 0, 0);
cvAbsDiff(pMatFrame, pMatBackground, pMatProcessed);
cvThreshold(pMatProcessed, pImgProcessed, 20, 255.0, CV_THRESH_BINARY);
//cvPyrDown(pImgProcessed,pyrImage,CV_GAUSSIAN_5x5); //GAUSSIAN金字塔向下采样
//cvPyrUp(pyrImage,pImgProcessed,CV_GAUSSIAN_5x5);
cvErode(pImgProcessed, pImgProcessed, 0, 1); //腐蚀
cvDilate(pImgProcessed, pImgProcessed, 0, 1); //膨胀
cvRunningAvg(pMatFrame, pMatBackground, 0.0003, 0); //背景更新
cvConvert(pMatBackground, pImgBackground);
cvFlip(pImgBackground,NULL,0); //垂直旋转图像
cvFlip(pImgProcessed,NULL,0);
cvShowImage("background", pImgBackground);
cvShowImage("processed", pImgProcessed);
if( cvWaitKey(50) == 27 ) //触发Esc键,跳出
{
break;
}
}
cvDestroyWindow("video");
cvDestroyWindow("background");
cvDestroyWindow("processed");
cvReleaseImage(&pImgProcessed);
cvReleaseImage(&pImgBackground);
cvReleaseMat(&pMatFrame);
cvReleaseMat(&pMatProcessed);
cvReleaseMat(&pMatBackground);
cvReleaseCapture(&pCapture);
return 0;
}