运动检测(前景检测)之(二)混合高斯模型GMM
运动检测(前景检测)之(二)混合高斯模型GMM
因为监控发展的需求,目前前景检测的研究还是很多的,也出现了很多新的方法和思路。个人了解的大概概括为以下一些:
帧差、背景减除(GMM、CodeBook、 SOBS、 SACON、 VIBE、 W4、多帧平均……)、光流(稀疏光流、稠密光流)、运动竞争(Motion Competition)、运动模版(运动历史图像)、时间熵……等等。如果加上他们的改进版,那就是很大的一个家族了。
对于上一些方法的一点简单的对比分析可以参考下:
http://www.cnblogs.com/ronny/archive/2012/04/12/2444053.html
至于哪个最好,看使用环境吧,各有千秋,有一些适用的情况更多,有一些在某些情况下表现更好。这些都需要针对自己的使用情况作测试确定的。呵呵。
推荐一个牛逼的库:http://code.google.com/p/bgslibrary/里面包含了各种背景减除的方法,可以让自己少做很多力气活。
还有王先荣博客上存在不少的分析:
http://www.cnblogs.com/xrwang/archive/2010/02/21/ForegroundDetection.html
下面的博客上转载王先荣的上面几篇,然后加上自己分析了两篇:
本文主要关注其中的一种背景减除方法:GMM。tornadomeet的博客上对ViBe进行了分析,我这里就不再啰嗦了,具体的理论分析可以参考:
http://www.cnblogs.com/tornadomeet/archive/2012/06/02/2531565.html
里面有了GMM的代码,并有了详细的注释。我之前根据这个代码(在这里,非常感谢tornadomeet)改写了一个Mat格式的版本,现在发上来和大家交流,具体如下:(在VS2010+OpenCV2.4.2中测试通过)。(当然了,OpenCV也已经提供了MOG的背景减除方法)
MOG_BGS.h
#pragma once
#include <iostream>
#include "opencv2/opencv.hpp" using namespace cv;
using namespace std; //定义gmm模型用到的变量
#define GMM_MAX_COMPONT 6 //每个GMM最多的高斯模型个数
#define GMM_LEARN_ALPHA 0.005
#define GMM_THRESHOD_SUMW 0.7
#define TRAIN_FRAMES 60 // 对前 TRAIN_FRAMES 帧建模 class MOG_BGS
{
public:
MOG_BGS(void);
~MOG_BGS(void); void init(const Mat _image);
void processFirstFrame(const Mat _image);
void trainGMM(const Mat _image);
void getFitNum(const Mat _image);
void testGMM(const Mat _image);
Mat getMask(void){return m_mask;}; private:
Mat m_weight[GMM_MAX_COMPONT]; //权值
Mat m_mean[GMM_MAX_COMPONT]; //均值
Mat m_sigma[GMM_MAX_COMPONT]; //方差 Mat m_mask;
Mat m_fit_num;
};
MOG_BGS.cpp
#include "MOG_BGS.h" MOG_BGS::MOG_BGS(void)
{ } MOG_BGS::~MOG_BGS(void)
{ } // 全部初始化为0
void MOG_BGS::init(const Mat _image)
{
/****initialization the three parameters ****/
for(int i = 0; i < GMM_MAX_COMPONT; i++)
{
m_weight[i] = Mat::zeros(_image.size(), CV_32FC1);
m_mean[i] = Mat::zeros(_image.size(), CV_8UC1);
m_sigma[i] = Mat::zeros(_image.size(), CV_32FC1);
}
m_mask = Mat::zeros(_image.size(),CV_8UC1);
m_fit_num = Mat::ones(_image.size(),CV_8UC1);
} //gmm第一帧初始化函数实现
//捕获到第一帧时对高斯分布进行初始化.主要包括对每个高斯分布的权值、期望和方差赋初值.
//其中第一个高斯分布的权值为1,期望为第一个像素数据.其余高斯分布权值为0,期望为0.
//每个高斯分布都被赋予适当的相等的初始方差 15
void MOG_BGS::processFirstFrame(const Mat _image)
{
for(int i = 0; i < GMM_MAX_COMPONT; i++)
{
if (i == 0)
{
m_weight[i].setTo(1.0);
_image.copyTo(m_mean[i]);
m_sigma[i].setTo(15.0);
}
else
{
m_weight[i].setTo(0.0);
m_mean[i].setTo(0);
m_sigma[i].setTo(15.0);
}
}
} // 通过新的帧来训练GMM
void MOG_BGS::trainGMM(const Mat _image)
{
for(int i = 0; i < _image.rows; i++)
{
for(int j = 0; j < _image.cols; j++)
{
int num_fit = 0; /**************************** Update parameters Start ******************************************/
for(int k = 0 ; k < GMM_MAX_COMPONT; k++)
{
int delm = abs(_image.at<uchar>(i, j) - m_mean[k].at<uchar>(i, j));
long dist = delm * delm;
// 判断是否匹配:采样值与高斯分布的均值的距离小于3倍方差(表示匹配)
if( dist < 3.0 * m_sigma[k].at<float>(i, j))
{
// 如果匹配
/****update the weight****/
m_weight[k].at<float>(i, j) += GMM_LEARN_ALPHA * (1 - m_weight[k].at<float>(i, j)); /****update the average****/
m_mean[k].at<uchar>(i, j) += (GMM_LEARN_ALPHA / m_weight[k].at<uchar>(i, j)) * delm; /****update the variance****/
m_sigma[k].at<float>(i, j) += (GMM_LEARN_ALPHA / m_weight[k].at<float>(i, j)) * (dist - m_sigma[k].at<float>(i, j));
}
else
{
// 如果不匹配。则该该高斯模型的权值变小
m_weight[k].at<float>(i, j) += GMM_LEARN_ALPHA * (0 - m_weight[k].at<float>(i, j));
num_fit++; // 不匹配的模型个数
}
}
/**************************** Update parameters End ******************************************/ /*********************** Sort Gaussian component by 'weight / sigma' Start ****************************/
//对gmm各个高斯进行排序,从大到小排序,排序依据为 weight / sigma
for(int kk = 0; kk < GMM_MAX_COMPONT; kk++)
{
for(int rr=kk; rr< GMM_MAX_COMPONT; rr++)
{
if(m_weight[rr].at<float>(i, j)/m_sigma[rr].at<float>(i, j) > m_weight[kk].at<float>(i, j)/m_sigma[kk].at<float>(i, j))
{
//权值交换
float temp_weight = m_weight[rr].at<float>(i, j);
m_weight[rr].at<float>(i, j) = m_weight[kk].at<float>(i, j);
m_weight[kk].at<float>(i, j) = temp_weight; //均值交换
uchar temp_mean = m_mean[rr].at<uchar>(i, j);
m_mean[rr].at<uchar>(i, j) = m_mean[kk].at<uchar>(i, j);
m_mean[kk].at<uchar>(i, j) = temp_mean; //方差交换
float temp_sigma = m_sigma[rr].at<float>(i, j);
m_sigma[rr].at<float>(i, j) = m_sigma[kk].at<float>(i, j);
m_sigma[kk].at<float>(i, j) = temp_sigma;
}
}
}
/*********************** Sort Gaussian model by 'weight / sigma' End ****************************/ /*********************** Create new Gaussian component Start ****************************/
if(num_fit == GMM_MAX_COMPONT && 0 == m_weight[GMM_MAX_COMPONT - 1].at<float>(i, j))
{
//if there is no exit component fit,then start a new component
//当有新值出现的时候,若目前分布个数小于M,新添一个分布,以新采样值作为均值,并赋予较大方差和较小权值
for(int k = 0 ; k < GMM_MAX_COMPONT; k++)
{
if(0 == m_weight[k].at<float>(i, j))
{
m_weight[k].at<float>(i, j) = GMM_LEARN_ALPHA;
m_mean[k].at<uchar>(i, j) = _image.at<uchar>(i, j);
m_sigma[k].at<float>(i, j) = 15.0; //normalization the weight,let they sum to 1
for(int q = 0; q < GMM_MAX_COMPONT && q != k; q++)
{
//对其他的高斯模型的权值进行更新,保持权值和为1
/****update the other unfit's weight,u and sigma remain unchanged****/
m_weight[q].at<float>(i, j) *= (1 - GMM_LEARN_ALPHA);
}
break; //找到第一个权值不为0的即可
}
}
}
else if(num_fit == GMM_MAX_COMPONT && m_weight[GMM_MAX_COMPONT -1].at<float>(i, j) != 0)
{
//如果GMM_MAX_COMPONT都曾经赋值过,则用新来的高斯代替权值最弱的高斯,权值不变,只更新均值和方差
m_mean[GMM_MAX_COMPONT-1].at<uchar>(i, j) = _image.at<uchar>(i, j);
m_sigma[GMM_MAX_COMPONT-1].at<float>(i, j) = 15.0;
}
/*********************** Create new Gaussian component End ****************************/
}
}
} //对输入图像每个像素gmm选择合适的高斯分量个数
//排序后最有可能是背景分布的排在最前面,较小可能的短暂的分布趋向于末端.我们将排序后的前fit_num个分布选为背景模型;
//在排过序的分布中,累积概率超过GMM_THRESHOD_SUMW的前fit_num个分布被当作背景模型,剩余的其它分布被当作前景模型.
void MOG_BGS::getFitNum(const Mat _image)
{
for(int i = 0; i < _image.rows; i++)
{
for(int j = 0; j < _image.cols; j++)
{
float sum_w = 0.0; //重新赋值为0,给下一个像素做累积
for(uchar k = 0; k < GMM_MAX_COMPONT; k++)
{
sum_w += m_weight[k].at<float>(i, j);
if(sum_w >= GMM_THRESHOD_SUMW) //如果这里THRESHOD_SUMW=0.6的话,那么得到的高斯数目都为1,因为每个像素都有一个权值接近1
{
m_fit_num.at<uchar>(i, j) = k + 1;
break;
}
}
}
}
} //gmm测试函数的实现
void MOG_BGS::testGMM(const Mat _image)
{
for(int i = 0; i < _image.rows; i++)
{
for(int j = 0; j < _image.cols; j++)
{
int k = 0;
for( ; k < m_fit_num.at<uchar>(i, j); k++)
{
if(abs(_image.at<uchar>(i, j) - m_mean[k].at<uchar>(i, j)) < (uchar)( 2.5 * m_sigma[k].at<float>(i, j)))
{
m_mask.at<uchar>(i, j) = 0;
break;
}
}
if(k == m_fit_num.at<uchar>(i, j))
{
m_mask.at<uchar>(i, j) = 255;
}
}
}
}
Main.cpp
// This is based on the "An Improved Adaptive Background Mixture Model for
// Real-time Tracking with Shadow Detection" by P. KaewTraKulPong and R. Bowden
// Author : zouxy
// Date : 2013-4-13
// HomePage : http://blog.csdn.net/zouxy09
// Email : zouxy09@qq.com #include "opencv2/opencv.hpp"
#include "MOG_BGS.h"
#include <iostream>
#include <cstdio> using namespace cv;
using namespace std; int main(int argc, char* argv[])
{
Mat frame, gray, mask;
VideoCapture capture;
capture.open("video.avi"); if (!capture.isOpened())
{
cout<<"No camera or video input!\n"<<endl;
return -1;
} MOG_BGS Mog_Bgs;
int count = 0; while (1)
{
count++;
capture >> frame;
if (frame.empty())
break;
cvtColor(frame, gray, CV_RGB2GRAY); if (count == 1)
{
Mog_Bgs.init(gray);
Mog_Bgs.processFirstFrame(gray);
cout<<" Using "<<TRAIN_FRAMES<<" frames to training GMM..."<<endl;
}
else if (count < TRAIN_FRAMES)
{
Mog_Bgs.trainGMM(gray);
}
else if (count == TRAIN_FRAMES)
{
Mog_Bgs.getFitNum(gray);
cout<<" Training GMM complete!"<<endl;
}
else
{
Mog_Bgs.testGMM(gray);
mask = Mog_Bgs.getMask();
morphologyEx(mask, mask, MORPH_OPEN, Mat());
erode(mask, mask, Mat(7, 7, CV_8UC1), Point(-1, -1)); // You can use Mat(5, 5, CV_8UC1) here for less distortion
dilate(mask, mask, Mat(7, 7, CV_8UC1), Point(-1, -1));
imshow("mask", mask);
} imshow("input", frame); if ( cvWaitKey(10) == 'q' )
break;
} return 0;
}
运动检测(前景检测)之(二)混合高斯模型GMM的更多相关文章
- [转]运动检测(前景检测)之(二)混合高斯模型GMM
转自:http://blog.csdn.net/zouxy09/article/details/9622401 因为监控发展的需求,目前前景检测的研究还是很多的,也出现了很多新的方法和思路.个人了解的 ...
- PRML读书会第九章 Mixture Models and EM(Kmeans,混合高斯模型,Expectation Maximization)
主讲人 网络上的尼采 (新浪微博: @Nietzsche_复杂网络机器学习) 网络上的尼采(813394698) 9:10:56 今天的主要内容有k-means.混合高斯模型. EM算法.对于k-me ...
- Opencv混合高斯模型前景分离
#include "stdio.h" #include "string.h" #include "iostream" #include &q ...
- [zz] 混合高斯模型 Gaussian Mixture Model
聚类(1)——混合高斯模型 Gaussian Mixture Model http://blog.csdn.net/jwh_bupt/article/details/7663885 聚类系列: 聚类( ...
- OpenCV混合高斯模型函数注释说明
OpenCV混合高斯模型函数注释说明 一.cvaux.h #define CV_BGFG_MOG_MAX_NGAUSSIANS 500 //高斯背景检测算法的默认参数设置 #define CV_BGF ...
- 混合高斯模型:opencv中MOG2的代码结构梳理
/* 头文件:OurGaussmix2.h */ #include "opencv2/core/core.hpp" #include <list> #include&q ...
- 混合高斯模型(GMM)推导及实现
作者:桂. 时间:2017-03-20 06:20:54 链接:http://www.cnblogs.com/xingshansi/p/6584555.html 声明:欢迎被转载,不过记得注明出处哦 ...
- 机器学习进阶-背景建模-(帧差法与混合高斯模型) 1.cv2.VideoCapture(进行视频读取) 2.cv2.getStructureElement(构造形态学的卷积) 3.cv2.createBackgroundSubtractorMOG2(构造高斯混合模型) 4.cv2.morpholyEx(对图像进行形态学的变化)
1. cv2.VideoCapture('test.avi') 进行视频读取 参数说明:‘test.avi’ 输入视频的地址2. cv2.getStructureElement(cv2.MORPH_E ...
- 混合高斯模型(Gaussian mixture model, GMM)
1. 前言 这就是为什么我要学习一下二维高斯分布的原因: 总感觉数学知识不够用呐,顺带把混合高斯模型也回顾一下. 2. 单高斯模型(Gaussian single model, GSM) 2.1 一维 ...
随机推荐
- 四:redis的sets类型 - 相关操作(有序和无序集合)
================四十五种(有序和无序集合):sets种类(它是一个集)============= 简介: set它代表的集合.加入是随意添加----->无序集合 ...
- shell编程控制结构:expr、let、for、while、until、shift、if、case、break、continue、功能、select
1.expr计算整数变量值 s=`expr 2 + 3` 运算符号和參数之间要有空格分开: 2.let命令 let s=(2+3)*4 echo $s 3.for语句 for 变量 in 列表 do ...
- 异步编程(Async和Await)的使用
.net4.5新特性之异步编程(Async和Await)的使用 一.简介 首先来看看.net的发展中的各个阶段的特性:NET 与C# 的每个版本发布都是有一个“主题”.即:C#1.0托管代码→C#2. ...
- 收集的css布局
1 <title>左定宽,右自动</title> 2 <style> 3 body{margin:0px;padding:0px;} 4 .box .left,.b ...
- HDU 3639 Hawk-and-Chicken(Tarjan缩点+反向DFS)
Problem Description Kids in kindergarten enjoy playing a game called Hawk-and-Chicken. But there alw ...
- monoTouch for android visual studio c#开发
本人 c# 程序员,第一次配置环境 按照此流程安装 http://www.wuleba.com/18892.html 本次下载的 8 个安装程序,我是安装以上顺序安装的,分别标志为1至8,暂时不 ...
- 学习Swift写iOS?那写安卓和WinPhone呢?请看一石三鸟终极解决方案 - Silver!
首先,你必须知道的是,Silver是苹果最新编程语言Swift的免费实现版本. 通过Silver,你可以使用Swift语言来编写.NET,Java,安卓和Cocoa APIs.你甚至可以在这些平台上共 ...
- Installshield建立IE快捷方式的方法
原文:Installshield建立IE快捷方式的方法 实现方法:在project assistant里或者install design里随便建一个快捷方式,然后去install design里修改刚 ...
- 利用docker搭建yii2 详细步骤
定位镜像 在hub.docker.com 搜索yii2,并且最后定位到 https://hub.docker.com/r/codemix/yii2-base/codemix/yii2-base 然后在 ...
- 摘录DirectShow数据,视频采集
DirectShow在,数据流(Data Flow)它们是依次流过每Filter的.管理其数据具有其自己的方法,并且并没有向用户提供一个统一的接口,供用户操作数据流.这里以提取视频採集在的每帧为位图数 ...