图像处理之前景检测(一)之单高斯模型
运动前景对象检测一直是国内外视觉监控领域研究的难点和热点之一,其目的是从序列图像中将变化区域从背景图像中提取出来,运动前景对象的有效检测对于对象跟踪、目标分类、行为理解等后期处理至关重要,那么区分前景对象,非常关键的一个问题是确定一个非常合适的背景,背景从象素的角度来理解,每一个象素就是有可能是前景点,也有可能是背景点,那么我们就要防止背景中误进入原属于前景点的对象,目前有几种常用的方法,但分别有利弊。
具体类型见(参考及推荐文献):
这里按照顺序,将感兴趣的有关算法进行梳理。首先是单高斯模型。
单高斯模型建模比较简单,利用没有运动目标的图像将整个窗口进行高斯建模,对比后续对应点上的像素数据,符合高斯模型,持续为背景,超出某个阈值,则认为为前景。
具体原理参考:
沈春旭 运动目标检测_单高斯背景建模。
这里主要将 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;
}
代码只是对于函数进行升级,但实现结果较老版些许不同,不知是版本更新问题,还是升级代码
存在未知错误
,大家辩证看待。
原代码结果:
升级代码结果:
结束。
视频素材下载地址:行人检测。