基于平均背景法的背景提取,最简单,最基础的背景提取

基于平均背景法的背景提取。

首先我们得明白,一幅图像中什么属于背景什么属于前景。我们简单的可以这么理解,前景一般是会动的物体,而背景一般是不会动的物体。我们可以以此为依据,从而辨别简单的前景和背景。不会动的物体,我们可以认为在一个很长的时间段内,它的像素值几乎都是保持一个数的。那么我们可以取若干张图片将其对应点的像素大小相加,然后再求均值,我们即可以认为这个是我们所需要的背景。可以用以下公式来表示:
在这里插入图片描述
那我们怎么去区分前景呢?
因为前景是运动的物体,我们可以用差分的形式去表现一个前景,也就是用当前图片的像素值减去上一张图片对应的像素值,得到的结果再累加求均值。
在这里插入图片描述
这里用的是平均差分但是并等价于标准差,但是差分计算速度更快。
代码实现:

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

//平均背景提取法

//3通道图片 float
//IavgF 累计背景图片的均值
//IdiffF 累计绝对值帧的均值
//IprevF 当前帧的前一帧
Mat IavgF, IdiffF, IprevF, IhiF, IlowF;
Mat tmp, tmp2;//背景图

//单通道 float
vector<Mat> Igry(3);
vector<Mat> Ilow(3);
vector<Mat> Ihi(3);

Mat maskt;//掩模
float Icount;

//为所有变量申请内存,由于定义的都是全局变量,故只需要申请栈上的空间即可

void AllocationImage(const Mat &I) {
	Size sz = I.size();

	IavgF = Mat::zeros(sz, CV_32FC3);
	IdiffF = Mat::zeros(sz, CV_32FC3);
	IprevF = Mat::zeros(sz, CV_32FC3);
	IhiF = Mat::zeros(sz, CV_32FC3);
	IlowF = Mat::zeros(sz, CV_32FC3);
	Icount = 0.0000001; //最好浮点数别定义为0.0

	tmp = Mat::zeros(sz, CV_32FC3);
	tmp2= Mat::zeros(sz, CV_32FC3);
	maskt= Mat::zeros(sz, CV_32FC1);
}
//计算累计背景图片和累计绝对值帧(一种比学习图片标准差更快的方式)平均差分
void accmulateBackground(Mat&I) {
	static int first = 1;
	I.convertTo(tmp, CV_32F);
	if (!first) {
		IavgF += tmp;//累计背景图片
		absdiff(tmp, IprevF, tmp2);//计算帧间差的绝对值
		IdiffF += tmp2;//累计绝对值帧
		Icount += 1.0;//计算总共加了多少图片。

	}
	first = 0;
	IprevF = tmp;
}

至此我们只是得到了前景和背景的大概描述,但是我们怎么去真正定义前景和背景呢?
我们定义:
在这里插入图片描述
当然参数是可以根据实际情况调整的。

//我们把高于平均值的n倍平均绝对差(可以理解为累计差分的平均值,实际计算也是这么回事)理解为前景
void setHighThreshold(float high) {
	IhiF = IavgF + (IdiffF*high);
	split(IhiF, Ihi);
}
//我们把低于平均值的n倍平均绝对差(可以理解为累计差分的平均值,实际计算也是这么回事)理解为背景
void setLowThreshold(float low) {
	IlowF = IavgF - (IdiffF*low);
	split(IlowF, Ilow);
}
//为什么可以这么理解呢?我们可以这么理解,作为前景,一般是运动的物体,所以一般来说差分的结果是比较大的。

//计算每个像素的均值和偏移值(平均绝对差)
void createModelsfromStats() {
	IavgF *= 1 / Icount; //计算累计背景图片的均值
	IdiffF *= 1 / Icount;//累计绝对值帧的均值
	//为了使累计绝对值帧的均值不全为0,给每个元素都加1
	IdiffF += Scalar(1.0, 1.0, 1.0);
	setHighThreshold(7.0);
	setLowThreshold(6.0);

}

为此我们得到了前景和背景的阈值,我们用此阈值来获取前景的掩模:

//为此我们进行前景和背景的分割 之前得到的前景与背景作为判别一个图像前景和背景的阈值

void backgroundDiff(Mat & I,Mat & mask) {
	I.convertTo(tmp, CV_32F);
	split(tmp, Igry);
	//通道1
	inRange(Igry[0], Ilow[0], Ihi[0], mask);
	//2
	
	inRange(Igry[1], Ilow[1], Ihi[1], maskt);
	mask = cv::min(mask, maskt);
	//3
	inRange(Igry[2], Ilow[2], Ihi[2], maskt);
	mask = cv::min(mask, maskt);
	mask = 255 - mask;

}

获取到每个通道的掩模后,我们选择那个共有的部分。
下面就是准备测试

  1. 读取30-1000帧的图像作为训练数据
  2. 获取到前景和背景的阈值
  3. 获取掩模
  4. 用掩模去修改调整图像
int main()
{
	VideoCapture cap;
	cap.open(1);
	Mat frame;
	cap >> frame;
	AllocationImage(frame);//初始化
	//我们先利用摄像头记录30-1000帧的图像进行训练
	int i = 0;
	while (i<500)
	{
		cap >> frame;
		if (!frame.data) {
			break;
		}
		accmulateBackground(frame);
	/*	imshow("frame", frame);
		if (waitKey(1) == 27) {
			break;
		}*/
		i++;
	}
	destroyAllWindows();
	cout << "训练完毕" << endl;
	createModelsfromStats();
	Mat Mymask;
	while (true)
	{
		cap >> frame;
		if (!frame.data) {
			break;
		}
		backgroundDiff(frame, Mymask);//分离前景和背景得到掩模
		split(frame, Igry);
		Igry[0] = cv::max(Mymask, Igry[0]);
		Igry[1] = cv::max(Mymask, Igry[1]);
		Igry[2] = cv::max(Mymask, Igry[2]);
		merge(Igry, frame);
		imshow("frame", frame);
		if (waitKey(1) == 27) {
			break;
		}

	}

	
	return 0;
}

完整代码为:


//作者:dwy
//日期:2019/07/14
//用途:测试背景提取:平均背景法和码书背景提取法
#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;

//平均背景提取法

//3通道图片 float
//IavgF 累计背景图片的均值
//IdiffF 累计绝对值帧的均值
//IprevF 当前帧的前一帧
Mat IavgF, IdiffF, IprevF, IhiF, IlowF;
Mat tmp, tmp2;//背景图

//单通道 float
vector<Mat> Igry(3);
vector<Mat> Ilow(3);
vector<Mat> Ihi(3);

Mat maskt;//掩模
float Icount;

//为所有变量申请内存,由于定义的都是全局变量,故只需要申请栈上的空间即可

void AllocationImage(const Mat &I) {
	Size sz = I.size();

	IavgF = Mat::zeros(sz, CV_32FC3);
	IdiffF = Mat::zeros(sz, CV_32FC3);
	IprevF = Mat::zeros(sz, CV_32FC3);
	IhiF = Mat::zeros(sz, CV_32FC3);
	IlowF = Mat::zeros(sz, CV_32FC3);
	Icount = 0.0000001; //最好浮点数别定义为0.0

	tmp = Mat::zeros(sz, CV_32FC3);
	tmp2= Mat::zeros(sz, CV_32FC3);
	maskt= Mat::zeros(sz, CV_32FC1);
}

//计算累计背景图片和累计绝对值帧(一种比学习图片标准差更快的方式)平均差分
void accmulateBackground(Mat&I) {
	static int first = 1;
	I.convertTo(tmp, CV_32F);
	if (!first) {
		IavgF += tmp;//累计背景图片
		absdiff(tmp, IprevF, tmp2);//计算帧间差的绝对值
		IdiffF += tmp2;//累计绝对值帧
		Icount += 1.0;//计算总共加了多少图片。

	}
	first = 0;
	IprevF = tmp;
}
//我们把高于平均值的n倍平均绝对差(可以理解为累计差分的平均值,实际计算也是这么回事)理解为前景
void setHighThreshold(float high) {
	IhiF = IavgF + (IdiffF*high);
	split(IhiF, Ihi);
}
//我们把低于平均值的n倍平均绝对差(可以理解为累计差分的平均值,实际计算也是这么回事)理解为背景
void setLowThreshold(float low) {
	IlowF = IavgF - (IdiffF*low);
	split(IlowF, Ilow);
}
//为什么可以这么理解呢?我们可以这么理解,作为前景,一般是运动的物体,所以一般来说差分的结果是比较大的。

//计算每个像素的均值和偏移值(平均绝对差)
void createModelsfromStats() {
	IavgF *= 1 / Icount; //计算累计背景图片的均值
	IdiffF *= 1 / Icount;//累计绝对值帧的均值
	//为了使累计绝对值帧的均值不全为0,给每个元素都加1
	IdiffF += Scalar(1.0, 1.0, 1.0);
	setHighThreshold(7.0);
	setLowThreshold(6.0);

}
//为此我们进行前景和背景的分割 之前得到的前景与背景作为判别一个图像前景和背景的阈值

void backgroundDiff(Mat & I,Mat & mask) {
	I.convertTo(tmp, CV_32F);
	split(tmp, Igry);
	//通道1
	inRange(Igry[0], Ilow[0], Ihi[0], mask);
	//2
	
	inRange(Igry[1], Ilow[1], Ihi[1], maskt);
	mask = cv::min(mask, maskt);
	//3
	inRange(Igry[2], Ilow[2], Ihi[2], maskt);
	mask = cv::min(mask, maskt);
	mask = 255 - mask;

}
int main()
{
	VideoCapture cap;
	cap.open(1);
	Mat frame;
	cap >> frame;
	AllocationImage(frame);//初始化
	//我们先利用摄像头记录30-1000帧的图像进行训练
	int i = 0;
	while (i<500)
	{
		cap >> frame;
		if (!frame.data) {
			break;
		}
		accmulateBackground(frame);
	/*	imshow("frame", frame);
		if (waitKey(1) == 27) {
			break;
		}*/
		i++;
	}
	destroyAllWindows();
	cout << "训练完毕" << endl;
	createModelsfromStats();
	Mat Mymask;
	while (true)
	{
		cap >> frame;
		if (!frame.data) {
			break;
		}
		backgroundDiff(frame, Mymask);//分离前景和背景得到掩模
		split(frame, Igry);
		Igry[0] = cv::max(Mymask, Igry[0]);
		Igry[1] = cv::max(Mymask, Igry[1]);
		Igry[2] = cv::max(Mymask, Igry[2]);
		merge(Igry, frame);
		imshow("frame", frame);
		if (waitKey(1) == 27) {
			break;
		}

	}

	
	return 0;
}


效果为:
在这里插入图片描述
这只是背景提取最基础的,当然只是给大家提供一个思路,想法,不至于看到较难的方法无从下手

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值