图像处理之前景检测(一)之单高斯模型(主要为代码升级)

本文介绍了一种基于单高斯模型的运动前景对象检测方法,该方法通过将背景建模为高斯分布并更新背景模型来区分前景与背景。在视频序列中,通过比较像素值与背景模型来提取变化区域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     图像处理之前景检测(一)之单高斯模型

    

     运动前景对象检测一直是国内外视觉监控领域研究的难点和热点之一,其目的是从序列图像中将变化区域从背景图像中提取出来,运动前景对象的有效检测对于对象跟踪、目标分类、行为理解等后期处理至关重要,那么区分前景对象,非常关键的一个问题是确定一个非常合适的背景,背景从象素的角度来理解,每一个象素就是有可能是前景点,也有可能是背景点,那么我们就要防止背景中误进入原属于前景点的对象,目前有几种常用的方法,但分别有利弊。

     具体类型见(参考及推荐文献):

     ☆Ronny丶   目标检测中背景建模方法

    smallroof    前景目标检测1(总结)

    这里按照顺序,将感兴趣的有关算法进行梳理。首先是单高斯模型。

      单高斯模型建模比较简单,利用没有运动目标的图像将整个窗口进行高斯建模,对比后续对应点上的像素数据,符合高斯模型,持续为背景,超出某个阈值,则认为为前景。

      具体原理参考:

      沈春旭    运动目标检测_单高斯背景建模

     这里主要将  zhengtu009  的  单高斯背景建模原理及代码实现  中的C接口代码进行升级为C++接口。环境为VS2015+opencv3.2 contrib版。

        

#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc, char* argv[])
{
	//新建窗口
	namedWindow("origin", WINDOW_AUTOSIZE);
	namedWindow("background", WINDOW_AUTOSIZE);
	namedWindow("foreground", WINDOW_AUTOSIZE);


	double alpha = 0.05;    //背景建模alpha值
	double std_init = 20;    //初始标准差
	double var_init = std_init * std_init;    //初始方差    
	double lamda = 2.5 * 1.2;    //背景更新参数

								 //视频文件
	VideoCapture capture("E://素材//3.mp4");
	

	Mat frame;        //原始图像

	Scalar pixel = { 0 };        //像素原始值
	Scalar pixel_u = { 0 };        //像素期望
	Scalar pixel_d = { 0 };        //像素前景
	Scalar pixel_var = { 0 };    //像素方差
	Scalar pixel_std = { 0 };    //像素标准差

	capture >> frame;
	Mat frame_u(frame.rows,frame.cols,  CV_8UC3);
	Mat frame_d(frame.rows,frame.cols , CV_8UC3);
	Mat frame_var(frame.rows,frame.cols,  CV_8UC3);
	Mat frame_std(frame.rows,frame.cols,  CV_8UC3);

	for (int y = 0; y < frame.rows; ++y)
	{
		for (int x = 0; x < frame.cols; ++x)
		{
			pixel =frame.at<Vec3b>(y,x);
			pixel_u.val[0] = pixel.val[0];
			pixel_u.val[1] = pixel.val[1];
			pixel_u.val[2] = pixel.val[2];

			pixel_d.val[0] = 0;
			pixel_d.val[1] = 0;
			pixel_d.val[2] = 0;

			pixel_std.val[0] = std_init;
			pixel_std.val[1] = std_init;
			pixel_std.val[2] = std_init;

			pixel_var.val[0] = var_init;
			pixel_var.val[1] = var_init;
			pixel_var.val[2] = var_init;
			
			//cvSet2D(frame_u, y, x, pixel_u);
			frame_u.at<Vec3b>(y, x)[0] = pixel_u[0];
			frame_u.at<Vec3b>(y, x)[1] = pixel_u[1];
			frame_u.at<Vec3b>(y, x)[2] = pixel_u[2];
			frame_d.at<Vec3b>(y, x)[0] = pixel_d[0];
			frame_d.at<Vec3b>(y, x)[1] = pixel_d[1];
			frame_d.at<Vec3b>(y, x)[2] = pixel_d[2];
			frame_var.at<Vec3b>(y, x)[0] = pixel_var[0];
			frame_var.at<Vec3b>(y, x)[1] = pixel_var[1];
			frame_var.at<Vec3b>(y, x)[2] = pixel_var[2];
			frame_std.at<Vec3b>(y, x)[0] = pixel_std[0];
			frame_std.at<Vec3b>(y, x)[1] = pixel_std[1];
			frame_std.at<Vec3b>(y, x)[2] = pixel_std[2];
		}
	}
	while (1)        //按ESC键退出, 帧率33ms
	{
		capture>>frame ;
		//视频结束退出
		if (frame.empty()==1)
		{
			break;
		}
		//单高斯背景更新
		for (int y = 0; y < frame.rows; ++y)
		{
			for (int x = 0; x < frame.cols; ++x)
			{
				
				pixel = frame.at<Vec3b>(y, x);
				
				pixel_u = frame_u.at<Vec3b>(y, x);
				
				pixel_d = frame_d.at<Vec3b>(y, x);
				
				pixel_std = frame_std.at<Vec3b>(y, x);
				
				pixel_var = frame_var.at<Vec3b>(y, x);

				//|I-u| < lamda*std 时认为是背景, 进行更新
				if (fabs(pixel.val[0] - pixel_u.val[0]) < lamda * pixel_std.val[0] &&
					fabs(pixel.val[1] - pixel_u.val[1]) < lamda * pixel_std.val[1] &&
					fabs(pixel.val[2] - pixel_u.val[2]) < lamda * pixel_std.val[2])
				{
					//更新期望 u = (1-alpha)*u + alpha*I
					pixel_u.val[0] = (1 - alpha) * pixel_u.val[0] + alpha * pixel.val[0];
					pixel_u.val[1] = (1 - alpha) * pixel_u.val[1] + alpha * pixel.val[1];
					pixel_u.val[2] = (1 - alpha) * pixel_u.val[2] + alpha * pixel.val[2];


					//更新方差 var = (1-alpha)*var + alpha*(I-u)^2
					pixel_var.val[0] = (1 - alpha) * pixel_var.val[0] +
						(pixel.val[0] - pixel_u.val[0]) * (pixel.val[0] - pixel_u.val[0]);
					pixel_var.val[1] = (1 - alpha) * pixel_var.val[1] +
						(pixel.val[1] - pixel_u.val[1]) * (pixel.val[1] - pixel_u.val[1]);
					pixel_var.val[2] = (1 - alpha) * pixel_var.val[2] +
						(pixel.val[2] - pixel_u.val[2]) * (pixel.val[2] - pixel_u.val[2]);

					//更新标准差
					pixel_std.val[0] = sqrt(pixel_var.val[0]);
					pixel_std.val[1] = sqrt(pixel_var.val[1]);
					pixel_std.val[2] = sqrt(pixel_var.val[2]);

					//写入矩阵
					//cvSet2D(frame_u, y, x, pixel_u);
					frame_u.at<Vec3b>(y, x)[0] = pixel_u[0];
					frame_u.at<Vec3b>(y, x)[1] = pixel_u[1];
					frame_u.at<Vec3b>(y, x)[2] = pixel_u[2];
					//cvSet2D(frame_var, y, x, pixel_var);
					//cvSet2D(frame_std, y, x, pixel_std);
					frame_var.at<Vec3b>(y, x)[0] = pixel_var[0];
					frame_var.at<Vec3b>(y, x)[1] = pixel_var[1];
					frame_var.at<Vec3b>(y, x)[2] = pixel_var[2];
					frame_std.at<Vec3b>(y, x)[0] = pixel_std[0];
					frame_std.at<Vec3b>(y, x)[1] = pixel_std[1];
					frame_std.at<Vec3b>(y, x)[2] = pixel_std[2];
				}
				else
				{
					pixel_d.val[0] = pixel.val[0] - pixel_u.val[0];
					pixel_d.val[1] = pixel.val[1] - pixel_u.val[1];
					pixel_d.val[2] = pixel.val[2] - pixel_u.val[2];
					//cvSet2D(frame_d, y, x, pixel_d);
					frame_d.at<Vec3b>(y, x)[0] = pixel_d[0];
					frame_d.at<Vec3b>(y, x)[1] = pixel_d[1];
					frame_d.at<Vec3b>(y, x)[2] = pixel_d[2];
				}
			}
		}
     	imshow("origin", frame);
		imshow("background", frame_u);		
		imshow("foreground", frame_d);
		waitKey(20);
	}

	return 0;
}
      代码只是对于函数进行升级,但实现结果较老版些许不同,不知是版本更新问题,还是升级代码 存在未知错误 ,大家辩证看待。

     

    原代码结果:

   

    

    升级代码结果:



    

     结束。

     

     视频素材下载地址:行人检测

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coming_is_winter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值