图像处理之前景检测(四)之自组织背景检测(SOBS)(转载)

图像处理之前景检测(四)之自组织背景检测(SOBS)(转载)

       一种基于自组织神经网络( self-Organizing through artificial neural networks )的背景减除算法(简称 SOBS 算法,全名: Self-Organizing Background Subtraction ), 用于智能视频监控系统中的目标检测,该算法不仅对光照具有较强的鲁棒性,而且具有较强的实用性。

      具体原理参考下文,其中含有算法作者论文链接,比较详细。
      Belial_2010              基于自组织背景减除的运动目标检测算法
    
      算法代码实现见:
    
    这里对于上述代码进行了些许的更改和简单的注释,环境为VS2015+opencv3.2 contrib版。如下:
     
     头文件:BackgroundSubtractorSOBS.h
     
// Author : zengdong_1991
// Date   : 2016-06-13
// HomePage : http://blog.csdn.net/zengdong_1991
// Email  : 782083852@qq.com



#pragma once
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
#include "math.h"




//! defines the parameters
#define WEIGHTS_N   3  //weight vertor 大小 = N^2
#define FRAME_NUM_K 45
#define EPSILON1 1.0
#define EPSILON2 0.008

#define PARAM_C1 1.0
#define PARAM_C2 0.05
#define MAX_W 4.0
#define ALPHA_1 (PARAM_C1 / MAX_W)
#define ALPHA_2 (PARAM_C2 / MAX_W)

#define GAMA 0.7
#define BEATA 1.0
#define TAU_S 40
#define TAU_H 48

static const double m_weightW[WEIGHTS_N*WEIGHTS_N] = { 1.0, 2.0, 1.0, 2.0, 4.0, 2.0, 1.0, 2.0, 1.0 };//高斯权重的选择 

class BackgroundSubtractorSOBS {
public:
	//! full constructor
	BackgroundSubtractorSOBS(
		int trainFrameNum = FRAME_NUM_K,
		double epsilon1 = EPSILON1,
		double epsilon2 = EPSILON2,
		double c1 = PARAM_C1,
		double c2 = PARAM_C2,
		double gama = GAMA,
		double beta = BEATA,
		double tauS = TAU_S,
		double tauH = TAU_H,
		double maxW = MAX_W);
	//! default destructor
	virtual ~BackgroundSubtractorSOBS();
	//! 
	virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI);
	//! 
	virtual void operator()(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = 0);


public:

	int m_trainFrameNum;
	double m_epsilon1;
	double m_epsilon2;
	double m_c1;
	double m_c2;
	double m_maxW;
	double m_alpha1;
	double m_alpha2;
	double m_gama;
	double m_beta;
	double m_tauS;
	double m_tauH;

	cv::Mat m_backgroundModel;   //背景模型:  n*rows, n*cols
	size_t m_frameNum = 0;
	cv::Mat m_ImgHSV;
	cv::Mat m_oROI;
	//! input image size
	cv::Size m_oImgSize;
	//! input image channel size
	size_t m_nImgChannels;
	size_t m_height;
	size_t m_width;
	size_t m_pixelNum;
	//! input image type
	int m_nImgType;


	bool findTheMatchVector(const cv::Vec3b& piexlVal, const size_t x, const size_t y, size_t& offSet_x, size_t& offSet_y, double eta); //true 匹配,false没有匹配
	void updateBackground(const cv::Vec3b& piexlVal, const size_t x, const size_t y, size_t& offSet_x, size_t& offSet_y, double alphaT);
	double getDistance(const cv::Vec3b& curr, const cv::Vec3b& bg);


};
       
       算法函数主体:BackgroundSubtractorSOBS.cpp
       
// Author : zengdong_1991
// Date   : 2016-06-13
// HomePage : http://blog.csdn.net/zengdong_1991
// Email  : 782083852@qq.com
#include "BackgroundSubtractorSOBS.h"
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>



BackgroundSubtractorSOBS::BackgroundSubtractorSOBS(int trainFrameNum,
	double epsilon1, 
	double epsilon2, 
	double c1,
	double c2,
	double gama,
	double beta,
	double tauS, 
	double tauH,
	double maxW)
	: m_trainFrameNum(trainFrameNum)
	, m_epsilon1(epsilon1)
	, m_epsilon2(epsilon2)
	, m_c1(c1)
	, m_c2(c2)
	, m_maxW(maxW)
	, m_gama(gama)
	, m_beta(beta)
	, m_tauS(tauS)
	, m_tauH(tauH)
{
	m_alpha1 = m_c1 / m_maxW;
	m_alpha2 = m_c2 / m_maxW;

	std::cout << "#########################Current Parameters Begin###################" << std::endl;
	std::cout << "m_trainFrameNum = " << m_trainFrameNum << std::endl;
	std::cout << "m_epsilon1 = " << m_epsilon1 << std::endl;
	std::cout << "m_epsilon2 = " << m_epsilon2 << std::endl;
	std::cout << "m_c1 = " << m_c1 << std::endl;
	std::cout << "m_c2 = " << m_c2 << std::endl;
	std::cout << "m_maxW = " << m_maxW << std::endl;
	std::cout << "m_alpha1 = " << m_alpha1 << std::endl;
	std::cout << "m_alpha2 = " << m_alpha2 << std::endl;
	std::cout << "m_gama = " << m_gama << std::endl;
	std::cout << "m_beta = " << m_beta << std::endl;
	std::cout << "m_tauS = " << m_tauS << std::endl;
	std::cout << "m_tauH = " << m_tauH << std::endl;
	std::cout << "#########################Current Parameters End###################" << std::endl;

}

BackgroundSubtractorSOBS::~BackgroundSubtractorSOBS() {

}

void BackgroundSubtractorSOBS::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI)//初始化
{
	// == init
	CV_Assert(!oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0);
	CV_Assert(oInitImg.isContinuous());
	CV_Assert(oInitImg.type() == CV_8UC3);   //必须是彩色图像

	if (oInitImg.type() == CV_8UC3)
	{
		std::vector<cv::Mat> voInitImgChannels;
		cv::split(oInitImg, voInitImgChannels);
		if (!cv::countNonZero((voInitImgChannels[0] != voInitImgChannels[1]) | (voInitImgChannels[2] != voInitImgChannels[1])))
			std::cout << std::endl << "\tBackgroundSubtractorSuBSENSE : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl;
	}
	m_oImgSize = oInitImg.size();
	m_nImgType = oInitImg.type();
	m_nImgChannels = oInitImg.channels();

	m_height = oInitImg.rows;
	m_width = oInitImg.cols;
	m_pixelNum = m_height * m_width;

	m_frameNum = 1;
	//将第一帧转换到 HSV 空间
	cv::Mat hsv;
	cv::cvtColor(oInitImg, hsv, CV_BGR2HSV);    //H范围是 【0-->180】



												//根据hsv初始化背景模型  大小为 (n*rows, n*cols)
	m_backgroundModel.create(cv::Size(WEIGHTS_N*m_width, WEIGHTS_N*m_height), CV_8UC3);
	for (size_t x = 0; x < m_height; x++)
	{
		for (size_t y = 0; y < m_width; y++)
		{
			cv::Vec3b  val = hsv.at<cv::Vec3b>(x, y);
			for (size_t i = WEIGHTS_N*x; i < WEIGHTS_N*(x + 1); i++)
			{
				for (size_t j = WEIGHTS_N*y; j < WEIGHTS_N*(y + 1); j++)
				{
					m_backgroundModel.at<cv::Vec3b>(i, j) = val;
				}
			}
		}
	}


	//cv::Mat debugMat = m_backgroundModel;

}


void BackgroundSubtractorSOBS::operator()(cv::InputArray _image, cv::OutputArray _fgmask, double learningRateOverride)//对于后续图像进行对比操作,区分前景背景并颜色标注
{
	// == process
	cv::Mat oInputImg = _image.getMat();
	CV_Assert(oInputImg.type() == m_nImgType && oInputImg.size() == m_oImgSize);
	CV_Assert(oInputImg.isContinuous());
	_fgmask.create(m_oImgSize, CV_8UC1);
	cv::Mat oCurrFGMask = _fgmask.getMat();   //外界 mask 矩阵
	oCurrFGMask = cv::Scalar_<uchar>(0);


	//转换到HSV空间: 
	cv::cvtColor(oInputImg, m_ImgHSV, CV_BGR2HSV);
	//cv::Mat debugMat = m_ImgHSV;


	m_frameNum++;



	size_t offSet_x = 0, offSet_y = 0;  //匹配偏移量
	double alphaT = m_alpha1 - (m_frameNum*(m_alpha1 - m_alpha2) / m_trainFrameNum); //公式(3)(4)  a(T)

	for (size_t x = 0; x < m_height; x++)
	{
		for (size_t y = 0; y < m_width; y++)
		{


			cv::Vec3b  val = m_ImgHSV.at<cv::Vec3b>(x, y);
			//calibration phase  执行 step 2--> 6
			if (m_frameNum <= m_trainFrameNum)
			{
				//查找匹配                  
				if (findTheMatchVector(val, x, y, offSet_x, offSet_y, m_epsilon1))
				{
					updateBackground(val, x, y, offSet_x, offSet_y, alphaT);
					oCurrFGMask.at<uchar>(x, y) = 0;
				}
				else
				{
					oCurrFGMask.at<uchar>(x, y) = 255;

				}
			}
			//on line phase  执行 step 2-->10             
			else
			{
				if (findTheMatchVector(val, x, y, offSet_x, offSet_y, m_epsilon2))
				{
					updateBackground(val, x, y, offSet_x, offSet_y, m_alpha2);
					oCurrFGMask.at<uchar>(x, y) = 0;
				}
				/*else if (isShadow(val, x, y, offSet_x, offSet_y))
				{
				oCurrFGMask.at<uchar>(x, y) = 0;
				}*/
				else
				{
					oCurrFGMask.at<uchar>(x, y) = 255;
				}
			}

		}
	}


}
void BackgroundSubtractorSOBS::updateBackground(const cv::Vec3b& piexlVal, const size_t x, const size_t y, size_t& offSet_x, size_t& offSet_y, double alphaT)//背景更新
{
	//  cv::Vec3b* ptrVec = m_backgroundModel.ptr<cv::Vec3b>(0);
	int pad = (WEIGHTS_N - 1) / 2;


	//当前匹配像素的坐标是    (nx+offset_x, ny+offset_y), 更新其周围的 N*N像素,如下:
	for (int i = -pad; i <= pad; i++)
	{
		int coordinateX = WEIGHTS_N*x + offSet_x + i;
		if (coordinateX < 0 || coordinateX >= WEIGHTS_N*m_height)   //边界保护
			continue;


		for (int j = -pad; j <= pad; j++)
		{
			int coordinateY = WEIGHTS_N*y + offSet_y + j;
			if (coordinateY < 0 || coordinateY >= WEIGHTS_N*m_width)  //边界保护
				continue;


			cv::Vec3b& pixelValBg = m_backgroundModel.at<cv::Vec3b>(coordinateX, coordinateY);

			double alpha = alphaT * m_weightW[(i + pad)*WEIGHTS_N + j + pad];  //【未解决】 w 怎么归一化...现在是【1,2,1;...】

																			   /*防止损失精度还是不这么做好了*///【未解决】 测试以下是否相等...
			pixelValBg = (1 - alpha)*pixelValBg + alpha*piexlVal;
			//cv::Vec3b At_1 = (1 - alpha)*pixelValBg;
			//cv::Vec3b Pt = alpha * piexlVal;
			//pixelValBg = At_1 + Pt;

			//cv::Vec3b test = pixelValBg;


			int a = 0;
		}
	}

}



bool BackgroundSubtractorSOBS::findTheMatchVector(const cv::Vec3b& piexlVal, const size_t x, const size_t y, size_t& offSet_x, size_t& offSet_y, double eta)//判断对应点是否符合属于背景条件
{
	cv::Mat temp = m_backgroundModel;
	//9个像素值...
	for (size_t i = 0; i < WEIGHTS_N; i++)
	{
		for (size_t j = 0; j < WEIGHTS_N; j++)
		{
			cv::Vec3b piexlValBg = m_backgroundModel.at<cv::Vec3b>(WEIGHTS_N*x + i, WEIGHTS_N*y + j);
			//当前像素与背景像素距离小于阈值
			double dist = getDistance(piexlVal, piexlValBg);
			if (dist < eta)
			{
				offSet_x = i;
				offSet_y = j;
				return true;
			}
		}
	}

	return false;
}
double BackgroundSubtractorSOBS::getDistance(const cv::Vec3b& curr, const cv::Vec3b& bg)//求取HSV Color Hexcone距离
{

	//opencv BGR2HSV中的H分量范围属于【0-->180】,将其隐射回【0-->360°(2*pi)】
	double hRatio = 2 * CV_PI / 180.0;
	double sRatio = 1. / 255.;
	double vRatio = 1. / 255.;

	double h1 = (double)curr[0] * hRatio;
	double s1 = (double)curr[1] * sRatio;
	double v1 = (double)curr[2] * vRatio;

	double h2 = (double)bg[0] * hRatio;
	double s2 = (double)bg[1] * sRatio;
	double v2 = (double)bg[2] * vRatio;

	double distance = (v1*s1*cos(h1) - v2*s2*cos(h2))*(v1*s1*cos(h1) - v2*s2*cos(h2)) +
		(v1*s1*sin(h1) - v2*s2*sin(h2))*(v1*s1*sin(h1) - v2*s2*sin(h2)) +
		(v1 - v2)*(v1 - v2);

	return (distance);

}

       主函数:main.cpp
// Author : zengdong_1991
// Date   : 2016-06-13
// HomePage : http://blog.csdn.net/zengdong_1991
// Email  : 782083852@qq.com
#include <iostream>
#include<opencv.hpp>

#include "BackgroundSubtractorSOBS.h"

using namespace std;
using namespace cv;



int main(int argc, char **argv)
{
	
	VideoCapture cap("E:\\素材\\2.mp4");
	Mat frame;
	cap >> frame;


	BackgroundSubtractorSOBS bgs;



	int key = 0;

	bool initialized = false;
	cv::Mat img_mask;
	cv::Mat img_bkgmodel;

	while (key != 'q')
	{
		cap>>frame ;
		if (frame.empty() == 1) {
			std::cerr << "Cannot open video!" << std::endl;
			return 1;
		}
		cv::Mat img_input(frame);

		if (!initialized)
		{
			bgs.initialize(img_input, cv::Mat());
			initialized = true;
			continue;
		}

		bgs(img_input, img_mask);
		//cv::medianBlur(img_mask, img_mask, 5);





		if (!img_mask.empty())
		{
			//cv::medianBlur(img_mask, img_mask, 5);
			cv::imshow("output", img_mask);
		}

		cv::imshow("input", img_input);

		cv::waitKey(1);
		//    key = cvWaitKey(1);
	}


	//cvDestroyAllWindows();
	//cvReleaseCapture(&capture);

	return 0;
}

     运行结果:(现在看,速度确实有些慢)。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值