基于混合高斯模型建模,来分割背景与目标。
@转载请注明出处
算法步骤:
- 视频图像输入
- 初始化混合高斯模型参数:均值,方差,权重,高斯模型个数等,假设模型5个,一般位3~5个。
- 计算当前像素与每个模型的均值差的绝对值,称该像素与当前模型匹配,判断为背景。若不存在匹配的模型,则判断该点位前景,并且对模型的重要性进行排序,对较小的模型重新赋值。
- 更新每个模型的系数
- 权重归一化
- 重复3~5。
- 输出检测二值图。
代码如下: 包括GMM.cpp, GMM.h 和main.cpp
GMM.h
#pragma once
/*
GMM.h
created at 2019/4/19
*/
#ifndef _GMM_H_HH
#define _GMM_H_HH
#include <opencv2/opencv.hpp>
class Gauss_mixture_model
{
public:
Gauss_mixture_model();
~Gauss_mixture_model();
public:
void gmm_init(int area, int nums);
void gmm_update(cv::Mat src, cv::Mat& dst);
void gmm_quit();
private:
void sort_important(float* val, int nums, int* sortpos);
protected:
float* u_mean;
float* w_eight;
float* v_rianc;
float* i_mport;
int* sort_loc;
int model_nums;
float biase; /*用于计算偏差的阈值*/
float rate; /*学习率*/
float pho;
float thr;
};
#endif
GMM.cpp
#include "GMM.h"
Gauss_mixture_model::Gauss_mixture_model()
{
}
Gauss_mixture_model::~Gauss_mixture_model()
{
}
void Gauss_mixture_model::gmm_init(int area, int nums)
{
biase = 2.5f;
model_nums = nums;
rate = 0.01f;
thr = 0.7f;
pho = rate * model_nums;
int size = area * model_nums;
u_mean = new float[size];
w_eight = new float[size];
v_rianc = new float[size];
i_mport = new float[model_nums];
sort_loc = new int[model_nums];
memset(i_mport, 0.0f, model_nums*sizeof(float));
memset(sort_loc, 0, model_nums*sizeof(int));
srand((unsigned)(time(NULL)));
/*init coeff*/
for (int r = 0; r < size; ++r)
{
u_mean[r] = rand() % 256;
w_eight[r] = 1.0f / nums;
v_rianc[r] = 6.0f;
}
}
void Gauss_mixture_model::gmm_update(cv::Mat src, cv::Mat& dst)
{
int rows = src.rows, cols = src.cols;
int size = rows * cols;
bool match;
for (int r = 0; r < rows; ++r)
for (int c = 0; c < cols; ++c)
{
match = false;
unsigned char pixel = src.at<uchar>(r, c);
float* ptr_u = u_mean + r * cols + c;
float* ptr_v = v_rianc + r * cols + c;
float* ptr_w = w_eight + r * cols + c;
for (int k = 0; k < model_nums; ++k)
{
float diff = abs(pixel - (*(ptr_u + k * size)));
float weigt = *(ptr_w + k * size);
if (diff <= biase * (*(ptr_v + k * size)))
{
match = true;
*(ptr_w + k * size) = (1 - rate)*weigt + rate;
}
else
*(ptr_w + k * size) = (1 - rate)*weigt;
}
if (!match)
{
dst.at<uchar>(r, c) = 255;
for (int k = 0; k < model_nums; ++k)
i_mport[k] = *(ptr_w + k*size) / *(ptr_v + k*size);
sort_important(i_mport, model_nums, sort_loc);
float sum_w = 0.0f;
for (int k = 0; k < model_nums; ++k)
{
int loc = sort_loc[k];
sum_w += *(ptr_w + loc * size);
if (sum_w > thr)
{
for (int lose = k + 1; lose < model_nums; ++lose)
{
*(ptr_w + lose*size) = 0.001f;
*(ptr_u + lose*size) = pixel;
*(ptr_v + lose*size) = 20.0f;
}
break;
}
}
}
else
{
dst.at<uchar>(r, c) = 0;
for (int k = 0; k < model_nums; ++k)
{
pho = rate / *(ptr_w + k*size);
float means = *(ptr_u + k*size);
*(ptr_u + k*size) = (1 - pho) * means + pho * pixel;
float var = *(ptr_v + k*size);
*(ptr_v + k*size) = sqrt((1 - pho)*(var*var) + pho * pow((pixel - *(ptr_u + k*size)), 2));
}
}
/*权重归一化*/
float norm_sum = 0.0f;
for (int k = 0; k < model_nums; ++k)
norm_sum += *(ptr_w + k * size);
for (int k = 0; k < model_nums; ++k)
*(ptr_w + k * size) /= norm_sum;
}
}
static void swap_pos(float* x, float* y)
{
float temp = *x;
*x = *y;
*y = temp;
}
void Gauss_mixture_model::sort_important(float* val, int nums, int* sortpos)
{
float max = FLT_MIN;
int pos = 0;
for (int i = 0; i < nums - 1; ++i)
{
float max = val[i];
for (int j = i + 1; j < nums; ++j)
{
if (val[j] >= max)
{
max = val[j];
pos = j;
}
}
sortpos[i] = pos;
swap_pos(val+i, val+pos);
}
}
void Gauss_mixture_model::gmm_quit()
{
printf("[gmm] release memory...\n");
delete[]u_mean;
delete[]w_eight;
delete[]v_rianc;
delete[]i_mport;
delete[]sort_loc;
}
main.cpp
#include <iostream>
#include "GMM.h"
const char* path = "vtest.avi";
void t_main()
{
Gauss_mixture_model detector;
cv::VideoCapture cap;
cap.open(path);
cv::Mat src, gray;
int initflag = 1;
while (1)
{
cap >> src;
if (src.empty())
break;
if (src.channels() == 3)
cvtColor(src, gray, CV_BGR2GRAY);
else
src.copyTo(gray);
if (initflag)
{
int areas = gray.rows * gray.cols;
detector.gmm_init(areas, 5); /*init coeff*/
initflag = 0;
}
cv::Mat dst(src.rows, src.cols, CV_8UC1);
detector.gmm_update(gray, dst); /*update coeff*/
cv::imshow("original.jpg", src);
cv::imshow("gmm_detect.jpg", dst);
cv::waitKey(10);
}
detector.gmm_quit(); /*release memory*/
printf("[gmm] end of avi...\n");
}
int main(int argc, char* argv[])
{
t_main();
return 0;
}
取视频中的一帧:
检测结果: