1. ViBe 算法简介
ViBe 是一种基于像素级背景建模的前景检测算法,由 Olivier Barnich 和 Marc Van Droogenbroeck 在 2009 年提出。它的特点是:
- 初始化速度快:只需要第一帧(或少数几帧)就能建立背景模型。
- 在线更新:检测的同时不断更新背景模型,适应缓慢变化。
- 内存占用小:每个像素只存储有限数量的样本值。
- 实时性好:算法简单,计算量低,可以在普通 CPU 上实时运行。
ViBe 广泛应用于视频监控、运动检测、行人跟踪等场景。
2. 算法核心思想
ViBe 的核心是每个像素用一个随机样本库(background model)表示其背景颜色分布,对于新到来的像素值,通过比较它与样本库的相似程度来判断是前景还是背景。
2.1 背景模型表示
- 对每个像素 ((x, y)),维护一个样本集合 (B(x, y)),包含 (N) 个历史像素值(灰度或彩色)。
- 这些样本是从过去的帧中随机选取的,代表该像素可能的背景颜色。
2.2 分类规则
- 对于当前像素值 (p(x, y)),计算它与样本库 (B(x, y)) 中每个样本的距离(如欧氏距离)。
- 如果样本库中存在 至少 #min 个样本 与当前像素的距离小于阈值 (R),则认为该像素是背景 ,否则是前景。
2.3 模型初始化
- 传统方法需要多帧来构建背景模型,而 ViBe 只需要第一帧:
- 对第一帧的每个像素,从它自身和邻域像素中随机选取 (N) 个值作为初始样本库。
- 这样可以快速建立背景模型,无需等待多帧累积。
2.4 模型更新
- 检测到是背景的像素,有一定概率(如 1/16)将其值加入样本库,并随机替换掉一个旧样本。
- 同时,为了保持空间一致性,还会随机更新邻域像素的样本库(空间扩散)。
- 这种更新策略可以有效抑制"鬼影"(ghost)现象。
3. 关键参数
- NNN:每个像素的样本数(通常取 20~30)。
- RRR:距离阈值(判断相似的阈值,灰度图可设 20~40)。
- #min\#_{\min}#min:匹配阈值(样本库中满足距离条件的最小数量,通常取 2~3)。
- 更新概率 PupdateP_{\text{update}}Pupdate:背景点更新自身样本库的概率(如 1/16)。
- 邻域大小:初始化和更新时考虑的邻域范围(如 3×3)。
4. 算法流程
- 初始化 :
- 对第一帧的每个像素,从自身和 8 邻域中随机选择 (N) 个像素值作为初始样本库。
- 前景/背景分类 :
- 对新帧每个像素,计算与样本库中样本的距离。
- 统计距离小于 (R) 的样本个数,如果 ≥ (#_{\min}) 则为背景,否则为前景。
- 模型更新 :
- 如果像素被分类为背景,以概率 PupdateP_{\text{update}}Pupdate 将其值加入样本库,并随机替换一个旧样本。
- 同时随机选择一个邻域像素,同样以概率 PupdateP_{\text{update}}Pupdate 用当前像素值替换其样本库中的一个样本。
5. 优缺点
优点
- 初始化快,无需长时间积累背景。
- 计算量小,实时性好。
- 内存占用低。
- 能较好地适应缓慢光照变化。
缺点
- 对快速光照变化和动态背景(如树叶摇动)鲁棒性一般。
- 可能产生短暂的"鬼影",但通过空间扩散可以缓解。
- 对彩色图需要考虑三通道距离,计算量稍大。
6. C++ 实现示例
下面给出一个简化的灰度图版本 ViBe 实现(OpenCV 支持)。
cpp
#include <opencv2/opencv.hpp>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace cv;
using namespace std;
class ViBe {
public:
ViBe(int numSamples = 20, int matchThreshold = 2, int distanceThreshold = 20, int updateProbability = 16) {
N = numSamples;
minMatches = matchThreshold;
R = distanceThreshold;
updateProb = updateProbability;
srand(time(0));
}
void init(const Mat& firstFrame) {
height = firstFrame.rows;
width = firstFrame.cols;
// 初始化样本库
samples.resize(height, vector<vector<uchar>>(width, vector<uchar>(N)));
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
for (int k = 0; k < N; k++) {
int dx = rand() % 3 - 1;
int dy = rand() % 3 - 1;
int ni = i + dy;
int nj = j + dx;
if (ni < 0) ni = 0;
if (nj < 0) nj = 0;
if (ni >= height) ni = height - 1;
if (nj >= width) nj = width - 1;
samples[i][j][k] = firstFrame.at<uchar>(ni, nj);
}
}
}
}
void processFrame(const Mat& frame, Mat& foreground) {
foreground.create(height, width, CV_8U);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
uchar pixel = frame.at<uchar>(i, j);
int matches = 0;
for (int k = 0; k < N; k++) {
if (abs(pixel - samples[i][j][k]) < R) {
matches++;
if (matches >= minMatches) break;
}
}
if (matches >= minMatches) {
foreground.at<uchar>(i, j) = 0; // 背景
// 随机更新样本库
if (rand() % updateProb == 0) {
int idx = rand() % N;
samples[i][j][idx] = pixel;
}
// 随机更新邻域
if (rand() % updateProb == 0) {
int dx = rand() % 3 - 1;
int dy = rand() % 3 - 1;
int ni = i + dy;
int nj = j + dx;
if (ni >= 0 && ni < height && nj >= 0 && nj < width) {
int idx = rand() % N;
samples[ni][nj][idx] = pixel;
}
}
} else {
foreground.at<uchar>(i, j) = 255; // 前景
}
}
}
}
private:
int height, width;
int N; // 样本数
int minMatches; // 匹配阈值
int R; // 距离阈值
int updateProb; // 更新概率分母
vector<vector<vector<uchar>>> samples; // 样本库
};
int main() {
VideoCapture cap(0); // 打开摄像头
if (!cap.isOpened()) return -1;
Mat frame, gray, fg;
cap >> frame;
cvtColor(frame, gray, COLOR_BGR2GRAY);
ViBe vibe;
vibe.init(gray);
while (true) {
cap >> frame;
cvtColor(frame, gray, COLOR_BGR2GRAY);
vibe.processFrame(gray, fg);
imshow("Frame", frame);
imshow("Foreground", fg);
if (waitKey(30) == 27) break;
}
return 0;
}
7. 运行说明
- 编译:
g++ vibe.cpp -o vibe
pkg-config --cflags --libs opencv4`` - 运行:
./vibe
- 程序会调用摄像头,实时显示前景检测结果。
- 白色区域为前景(运动物体),黑色为背景。
8. 扩展
- 彩色图支持 :将样本库改为
Vec3b
存储,距离计算改为三通道欧氏距离。 - 后处理:对前景图进行形态学操作(腐蚀、膨胀)去除噪声。
- 自适应阈值 :根据局部亮度调整 RRR。
- 多尺度样本:考虑不同时间间隔的样本,提高模型鲁棒性。
ViBe 是一种高效的背景建模算法,适合实时视频分析。它的核心是随机样本库 + 空间扩散更新,这种设计使其初始化快、计算量低、鲁棒性较好。在实际应用中,可以根据场景需求调整参数,或结合其他算法进行优化。