// vibe背景建模
#ifndef vibe_h
#define vibe_h
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define VIBE_DEF_MODEL_NUM (1)
#define VIBE_DEF_STEP (8)
#define VIBE_DEF_NUM_SAMPLE (20)
#define VIBE_DEF_MIN_MATCHS (2)
#define VIBE_DEF_RADIUS (15)
#define VIBE_DEF_RAND_SAMPLES (4)
#define VIBE_DEF_STATIC_FG_FRAMES (200)
typedef struct{
int w, h; //输入图像宽高
int model_num; //多模型, def=1。 (适用于固定多阶AE)
int step; //采样步长, bg/fg图像尺寸=[w/step, h/step] (控制内存/耗时与精度间平衡)
int num_samples; //单像素样本数, def=20
int min_matchs; //Match Number of make pixel as Background, def=2。 (背景判断,约小前景越灵敏)
int radius; //Radius of pixel value, def=15。 (前景与背景差异阈值,越小前景越灵敏)
int rand_samples; //probability of random sample, def=4。 (控制背景更新速度,越大越慢)
int static_fg_frames;//连续判断为fg的帧数,达到阈值后该像素会被设置成bg, def=200。 (由于类型限制,需小于255)
} VIBE_PARAMS;
typedef struct{
VIBE_PARAMS params;
int model_idx; //当前命中模型索引(多模型)
int *bg_mean_val; //背景灰度均值
unsigned char *bg_samples; //背景模型
unsigned char *fg; //前景mask
} VIBE_MODEL;
/*获取默认参数*/
void vibe_get_def_params(int w, int h, VIBE_PARAMS *vibe_params);
/*初始化*/
int vibe_init(VIBE_PARAMS vibe_params, void **vibe_model);
/*主算法*/
void vibe_run(void *vibe_model, unsigned char *image, int image_w, int image_h);
/*获取前景*/
void vibe_get_fg(void *vibe_model, unsigned char **fg);
/*获取背景*/
void vibe_get_bg(void *vibe_model, int bg_w, int bg_h, unsigned char *bg);
/*资源释放*/
void vibe_release(void *vibe_model);
#ifdef __cplusplus
}
#endif
#endif /* vibe_h */
#include "vibe.h"
#include <string.h>
#include <stdlib.h>
#define BG_VAL (0)
#define FG_VAL (255)
// Compute a pseudorandom integer.
// Output value in range [0, 32767]
static unsigned int g_seed = 123;
static inline int fast_rand(void) {
g_seed = (214013*g_seed+2531011);
return (g_seed>>16)&0x7FFF;
}
void vibe_get_def_params(int w, int h, VIBE_PARAMS *vibe_params){
vibe_params->w = w;
vibe_params->h = h;
vibe_params->model_num = VIBE_DEF_MODEL_NUM;
vibe_params->step = VIBE_DEF_STEP;
vibe_params->num_samples = VIBE_DEF_NUM_SAMPLE;
vibe_params->min_matchs = VIBE_DEF_MIN_MATCHS;
vibe_params->radius = VIBE_DEF_RADIUS;
vibe_params->rand_samples = VIBE_DEF_RAND_SAMPLES;
vibe_params->static_fg_frames = VIBE_DEF_STATIC_FG_FRAMES;
}
int vibe_init(VIBE_PARAMS vibe_params, void **vibe_model){
int ret = 0, bg_size, fg_size;
if(vibe_params.h % vibe_params.step != 0 || vibe_params.w % vibe_params.step != 0)
return -1;
if(NULL == *vibe_model){
*vibe_model = malloc(sizeof(VIBE_MODEL));
if(!(*vibe_model)) {
ALOGE("%s %d failed!\n", __FUNCTION__, __LINE__);
ret = -2;
goto ERR;
}
VIBE_MODEL *model = (VIBE_MODEL *)(*vibe_model);
bg_size = vibe_params.model_num * vibe_params.h * vibe_params.w * (vibe_params.num_samples+1) / (vibe_params.step * vibe_params.step);// 数组中,在num_samples之外多增的一个值,用于统计该像素点连续成为前景的次数;
model->bg_samples = (unsigned char *)malloc(bg_size);
if(!model->bg_samples) {
ALOGE("%s %d failed!\n", __FUNCTION__, __LINE__);
ret = -3;
goto ERR;
}
memset(model->bg_samples, 0, bg_size);
fg_size = vibe_params.h * vibe_params.w / (vibe_params.step * vibe_params.step);
model->fg = (unsigned char *)malloc(fg_size);
if(!model->fg) {
ALOGE("%s %d failed!\n", __FUNCTION__, __LINE__);
ret = -4;
goto ERR;
}
memset(model->fg, 0, fg_size);
model->bg_mean_val = (int *)malloc(vibe_params.model_num * sizeof(int));
if(!model->bg_mean_val) {
ALOGE("%s %d failed!\n", __FUNCTION__, __LINE__);
ret = -5;
goto ERR;
}
memset(model->bg_mean_val, 0, vibe_params.model_num * sizeof(int));
memcpy(&(model->params), &vibe_params, sizeof(VIBE_PARAMS));
}
return 0;
ERR:
if(*vibe_model){
VIBE_MODEL *model = (VIBE_MODEL *)(*vibe_model);
if(model->bg_samples)
free(model->bg_samples);
if(model->fg)
free(model->fg);
free(*vibe_model);
}
return ret;
}
int vibe_get_image_mean(VIBE_PARAMS params, unsigned char *image){
int mean = 0;
unsigned char *p;
for(int i = 0; i < params.h; i+=params.step){
p = image + params.w * i;
for(int j = 0; j < params.w; j+=params.step){
mean += p[j];
}
}
mean /= (params.h*params.w/(params.step*params.step));
return mean;
}
int vibe_get_model_idx(VIBE_MODEL *model, unsigned char *image){
int model_idx = 0;
int min_diff = 255, diff;
int image_mean = vibe_get_image_mean(model->params, image);
//选择灰度差最小的模型
for(int i = 0; i < model->params.model_num; i++){
diff = abs(image_mean - model->bg_mean_val[i]);
if(diff < min_diff){
min_diff = diff;
model_idx = i;
}
}
//灰度差太大时,切到空模型
for(int i = 0; i < model->params.model_num; i++){
if(model->bg_mean_val[i] == 0 && min_diff > 5){
model_idx = i;
break;
}
}
return model_idx;
}
void vibe_run_fisrt_frame(VIBE_MODEL *model, unsigned char *image){
VIBE_PARAMS *params = &(model->params);
int row, col;
int c_off[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};
int bg_h = params->h / params->step;
int bg_w = params->w / params->step;
int chn = params->num_samples+1;
int stride = bg_w * chn;
int model_buff_oft = bg_h * bg_w * chn * model->model_idx;
model->bg_mean_val[model->model_idx] = 0;
for(int i = 0; i < bg_h; i++){
for(int j = 0; j < bg_w; j++){
unsigned char *psamples = model->bg_samples + model_buff_oft + stride * i + chn * j;
for(int k = 0 ; k < params->num_samples; k++){
// 随机选择num_samples个邻域像素点,构建背景模型
int random;
random = fast_rand() % 9; row = i * params->step + c_off[random];
random = fast_rand() % 9; col = j * params->step + c_off[random];
// 防止选取的像素点越界
if (row < 0) row = 0;
if (row >= params->h) row = params->h - 1;
if (col < 0) col = 0;
if (col >= params->w) col = params->w - 1;
// 为样本库赋随机值
psamples[k] = image[params->w * row + col];
}
//update bg mean
model->bg_mean_val[model->model_idx] += psamples[0];
}
}
model->bg_mean_val[model->model_idx] /= (bg_h*bg_w);
}
void vibe_run_frame(VIBE_MODEL *model, unsigned char *image){
VIBE_PARAMS *params = &(model->params);
int c_off[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};
int bg_h = params->h / params->step;
int bg_w = params->w / params->step;
int chn = params->num_samples+1;
int stride = bg_w * chn;
int model_buff_oft = bg_h * bg_w * chn * model->model_idx;
int k = 0, dist = 0, matches = 0;
model->bg_mean_val[model->model_idx] = 0;
for(int i = 0; i < bg_h; i++){
for(int j = 0; j < bg_w; j++){
unsigned char *psamples = model->bg_samples + model_buff_oft + stride * i + chn * j;
unsigned char *pfg = model->fg + bg_w * i + j;
unsigned char img_val = image[(params->w * i + j) * params->step];
//计算当前像素值与背景样本的匹配情况
for(k = 0, matches = 0; matches < params->min_matchs && k < params->num_samples; k++){
dist = abs(psamples[k] - img_val);
if (dist < params->radius)
matches++;
}
if (matches >= params->min_matchs) {
// 已经认为是背景像素,故该像素的前景统计次数置0
psamples[params->num_samples] = 0;
// 该像素点被的前景模型像素值置0
*pfg = BG_VAL;
}
else {
// 已经认为是前景像素,故该像素的前景统计次数+1
psamples[params->num_samples]++;
// 该像素点被的前景模型像素值
*pfg = FG_VAL;
// 如果某个像素点连续static_fg_frames次被检测为前景,则认为一块静止区域被误判为运动,将其更新为背景点
if(psamples[params->num_samples] > params->static_fg_frames) {
int random = fast_rand() % params->num_samples;
psamples[random] = img_val;
}
}
// 更新模型样本库
if (matches >= params->min_matchs){
// 已经认为该像素是背景像素,那么它有 1 / φ 的概率去更新自己的模型样本值
int random = fast_rand() % params->rand_samples;
if (random == 0) {
random = fast_rand() % params->num_samples;
psamples[random] = img_val;
}
// 同时也有 1 / φ 的概率去更新它的邻居点的模型样本值
random = fast_rand() % params->rand_samples;
if (random == 0) {
int row, col;
random = fast_rand() % 9; row = i + c_off[random];
random = fast_rand() % 9; col = j + c_off[random];
// 防止选取的像素点越界
if (row < 0) row = 0;
if (row >= bg_h) row = bg_h - 1;
if (col < 0) col = 0;
if (col >= bg_w) col = bg_w - 1;
// 为样本库赋随机值
random = fast_rand() % params->num_samples;
model->bg_samples[model_buff_oft + stride * row + chn * col + random] = img_val;
}
}
//update bg mean
model->bg_mean_val[model->model_idx] += psamples[0];
}
}
model->bg_mean_val[model->model_idx] /= (bg_h*bg_w);
}
void vibe_run(void *vibe_model, unsigned char *image, int image_w, int image_h){
VIBE_MODEL *model = (VIBE_MODEL *)vibe_model;
if(!model || model->params.w != image_w || model->params.h != image_h)
return;
//根据灰度均值,选择多模型其中一个
model->model_idx = vibe_get_model_idx(model, image);
//背景模型第一帧
if(model->bg_mean_val[model->model_idx] == 0){
vibe_run_fisrt_frame(model, image);
}
else{
vibe_run_frame(model, image);
}
}
void vibe_get_fg(void *vibe_model, unsigned char **fg){
VIBE_MODEL *model = (VIBE_MODEL *)vibe_model;
if(!model || !fg)
return;
*fg = model->fg;
}
void vibe_get_bg(void *vibe_model, int bg_w, int bg_h, unsigned char *bg){
VIBE_MODEL *model = (VIBE_MODEL *)vibe_model;
if(!model || !bg)
return;
if(model->params.w / model->params.step != bg_w ||
model->params.h / model->params.step != bg_h)
return;
int chn = model->params.num_samples+1;
int stride = bg_w * chn;
int model_buff_oft = bg_h * bg_w * chn * model->model_idx;
for(int i = 0; i < bg_h; i++){
for(int j = 0; j < bg_w; j++){
bg[bg_w*i + j] = model->bg_samples[model_buff_oft + stride*i + chn*j];
}
}
}
void vibe_release(void *vibe_model){
if(vibe_model){
VIBE_MODEL *model = (VIBE_MODEL *)vibe_model;
if(model->bg_samples)
free(model->bg_samples);
if(model->fg)
free(model->fg);
free(vibe_model);
if(model->bg_mean_val)
free(model->bg_mean_val);
}
}