1 引言
运动目标检测算法可分为三类:帧间差分法、光流法、背景建模法。其中,帧间差分法是利用视频中连续的帧的差分方法来检测运动的前景物体,在固定场景下具有不错的效果,并且不易受光照变化的影响,但是当前景物体运动速度过快时,这个方法就不太适合。光流法是利用二维平面内由三维运动场投影得到的速度场来区分场景中的运动目标和背景,但是算法复杂度高计算量较大。背景建模方法是利用视频中的图像序列计算得到一个背景模型,然后将每帧图像与背景模型进行比较,差别大的像素便是运动目标存在的位置。背景建模方法的好处是前景目标的提取效果不会受到前景物体运动速度的影响,但是对于光照变化的场景会存在误差,但是依然能够采用不同的特征和模型来处理复杂场景,是运动目标检测中的主流方法。
本文介绍了7种背景-前景分割方法,并进行实验对比分析。
文末附代码。
2 相关技术
2.1 基于混合高斯方法的背景建模
混合高斯模型背景建模方法是对单高斯背景建模算法进行扩展后的一种方法,通过采用多个高斯分布来构建背景模型,优点是对于场景中有微弱抖动的背景具有很好的处理能力。
对于一个视频序列,如果是光照不变的固定场景,其背景的像素值是基本不变的,考虑到摄像头捕获图像的过程中会存在系统误差产生高斯噪声,则通过一个高斯分布便可以描述帧图像中像素点的概率密度。但是,更多的场景中背景虽然不会移动但是随着时间的变化,背景依然会存在其他变化,变化多种多样,则可以通过多个高斯分布的加权来模拟背景模型的分布。
在算法使用的过程中,混合高斯模型中高斯分布的个数可以根据计算机本身的硬件配置和视频中背景的复杂程度进行自主选择。在任意时刻,帧图像坐标(x,y)(x,y)(x,y)处像素点的随机过程可以用表示为:
{X1,…,Xi}={Φ(x,y,i);1≤i≤t} \{ X_1,\dots,X_i\}=\{\Phi(\text{x,y,i});1\le\text{i}\le\text{t}\} {X1,…,Xi}={Φ(x,y,i);1≤i≤t}
其中,Φ\PhiΦ表示视频中的图像序列,XiX_iXi表示在t时刻的像素值。某时刻图像中某个像素点的背景依然可以使用一个概率密度函数表示。
混合高斯模型的背景建模过程,可以分为三个步骤:
- 模型初始化:利用初始视频帧图像信息对背景模型进行初始化。
- 背景模型学习:利用视频中的序列帧图像,不断更新背景模型。
- 前景检测:根据背景模型和当前帧图像,进行前景划分,并更新背景模型。
2.2 基于LBP的背景建模
图像本身存在纹理特征,可以表述物体表面的细节信息,也可以描述像素灰度值的空间分布。局部二值模式(Local Binary Pattern,LBP)是一种纹理描述子,通过描述图像中每个像素点与其周围领域内其他各点的差异来表示图像的局部纹理结构特征。
LBP 纹理描述子具有灰度不变性,具有很好的鲁棒性,对于视频场景中的单调变化不敏感,并且计算简单,参数依赖少。将 LBP 应用于背景建模最主要的就是建立和维护一个关于场景特征的数据分布。对视频帧图像中每个像素点计算其 LBP 特征,然后对该像素点领域内的 LBP 值进行统计,得到直方图——处理和学习的特征向量。对于视频某一帧图像的某一个区域,其 LBP 直方图序列为 {x1,x2,…,xt}\{x_1, x_2, \dots, x_t\}{x1,x2,…,xt},ttt 表示时刻。K个带权值 LBP 直方图组合来构成背景模型。
该方法分为三个阶段:
- 模型初始化:使用区域内的 LBP 直方图初始化背景模型中的 K 个直方图。
- 模型学习:不断比较新得到的直方图和背景模型中的直方图,并更新背景模型中的直方图。
- 前景检测:从背景模型中选择若干个最能代表背景分布的直方图构建背景模型,用当前帧图像的直方图与背景模型比较,若有匹配则标记为背景并同时更新匹配的背景直方图。
以上,介绍了两种经典的背景建模方法:基于高斯混合模型的背景建模和基于LBP的背景建模。在此基础上,许多改进的方法被提出,将在下一章节进行试验分析比较。
3 实验分析与比较
3.1 背景建模的难点分析
背景建模主要用于视频场景中,而场景本身存在许多的多变性和复杂性。
- 光照变化:在实际的场景中,随着时间的变化或其他人为干预,都会使得场景出现变化,则背景模型也会发生变化,若是取得良好的前景检测结果,对于算法的鲁棒性就有很高的要求。
- 动态背景:在实际的场景中,背景的构成包括许多物体,例如风吹动的树枝、草丛、水面等,这些物体会在一定区域内有抖动,好的背景建模算法应当考虑到这些,以达到良好的前景检测效果。
- 阴影:在实际的场景中,由于光照的影响,运动目标往往存在影子,使用背景建模方法进行前景检测时,很容易将目标的阴影识别为前景。
3.2 改进的背景建模算法
本文使用 OpenCV中提供的背景建模算法进行了相关实验分析。在 windows 平台下,使用 CMake 和 VS2015 对 OpenCV3.4 和 opencv_contrib 进行了源码编译。
对 opencv 提供的基于 CNT[1]、GMG[2]、GSOC、MOG[3]、MOG2、KNN、LSBP[4]的七种背景建模方法进行了实验,对各算法的前景检测效果和算法执行效率进行统计。
- CNT:一种基于统计的背景分割算法。在低价硬件设备上(树莓派),该方法比 MOG2 算法的效率高两倍。该方法由 Sagi Zeevi 提供。
- GMG:一种结合统计背景图像估计和像素级贝叶斯分割的算法。由 Andrew B. Godbehere, Akihiro Matsukawa, Ken Goldberg在 2012年的文章“Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive Audio Art Installation”中提出。该算法使用前几个(默认为120)帧进行背景建模。它采用概率前景分割算法,使用贝叶斯推理识别可能的前景对象。
- GSOC:一个来自谷歌编程之夏的算法,未在任何论文中提及。
- KNN:一种使用 K 近邻算法的背景分割方案,2006 年,由 Zoran Zivkovic 和 Ferdinand van der Heijden 在论文“Efficient adaptive density estimation per image pixel for the task of background subtraction.”中提出。
- LSBP:基于局部奇异值分解二进制模式的背景减除。该方法由 L. Guo, D. Xu, and Z. Qiang. 在 2016年 CVPRW上提出。
- MOG:高斯混合模型分离算法,它使用一种通过K个高斯分布的混合来对每个背景像素进行建模。2001年,由 P.KadewTraKuPong 和R.Bowden 在论文“An improved adaptive background mixture model for real-time tracking with shadow detection”中提出。
- MOG2:是MOG的改进算法,一个重要特征是它为每个像素选择适当数量的高斯分布,可以更好的适应不同场景的照明变化。
本文使用768*576分辨率,时长1分19秒,帧率10FPS的视频对以上7种算法进行了对比试验。试验结果如表3-1和表3-2所示。
表3-1 实验结果:背景分割效率
| 算法 | 运行时间(秒) |
|---|---|
| CNT | 2.97233 |
| GMG | 22.0164 |
| GSOC | 29.8616 |
| KNN | 20.7308 |
| LSBP | 63.561 |
| MOG | 20.2415 |
| MOG2 | 5.07296 |
通过表 3-1可以看出,在这 7种视频背景分割算法中,算法效率由高至低分别为 CNT、MOG2、MOG、KNN、GMG、GSOC、LSBP。可见,各方法间的速度差很大。
表3-2 实验结果:背景分割效果


表3-2展示的是各算法在视频前中后期前景检测的效果。可以观察到,在视频刚开始的几帧,各算法对背景的处理并不很好,尤其是对被风吹动的条幅的处理更明显不好。但随着算法学习的图像帧数的增加,各方法前景检测的结果都逐渐变好。由于光照的问题,行人会存在阴影,通过表中效果图可以看到MOG算法对于阴影的处理最好,无论是视频前期、中期还是后期,行人的阴影没有被检测为前景运动对象。
值得一提的是,在本次实验中,可以看到KNN算法对于局部抖动的条幅没有做到很好的处理,LSBP算法检测到的前景目标具有较大的毛边,MOG2算法对于阴影的处理十分不好,GSOC算法也没有很好的将行人阴影去除掉。就整体而言,MOG算法在此测试视频上的效果表现最佳。
本文进行的实验对比,仅针对一个视频进行的,并不能全面反映一个算法的好坏。
4 结论
本文对典型的视频背景建模的方法进行了介绍,从头编译了计算机视觉开源库 OpenCV3.4和其实验代码库,使用OpenCV中的提供的7种背景建模分割的算法进行了实验,并对实验结果进行了分析对比,比较了算法间的计算效率和处理效果。
main.cpp
#include<opencv2/opencv.hpp>
#include "BGSegmAlg.h"
int main() {
BGSegmAlg bgsegmalg;
bgsegmalg.Seg();
return 0;
}
BGSegmAlg.h
#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2\bgsegm.hpp>
#include<time.h>
#include<numeric>
const
class BGSegmAlg
{
public:
BGSegmAlg();
void Seg();
~BGSegmAlg();
private:
cv::Ptr<cv::BackgroundSubtractor> cnt=cv::bgsegm::createBackgroundSubtractorCNT();
cv::Ptr<cv::BackgroundSubtractor> mog = cv::bgsegm::createBackgroundSubtractorMOG();
cv::Ptr<cv::BackgroundSubtractor> gsoc = cv::bgsegm::createBackgroundSubtractorGSOC();
cv::Ptr<cv::BackgroundSubtractor> gmg = cv::bgsegm::createBackgroundSubtractorGMG();
cv::Ptr<cv::BackgroundSubtractor> lsbp = cv::bgsegm::createBackgroundSubtractorLSBP();
cv::Ptr<cv::BackgroundSubtractor> knn = cv::createBackgroundSubtractorKNN();
cv::Ptr<cv::BackgroundSubtractor> mog2 = cv::createBackgroundSubtractorMOG2();
clock_t start, finish;
std::vector<float> runtime_cnt, runtime_mog, runtime_gsoc, runtime_gmg, runtime_lsbp, runtime_knn, runtime_mog2;
cv::VideoCapture cap;
};
BGSegmAlg.cpp
#include "BGSegmAlg.h"
BGSegmAlg::BGSegmAlg()
{
cap.open("768x576.avi");
}
void BGSegmAlg::Seg() {
cv::Mat frame;
cv::Mat fg_CNT,fg_MOG,fg_GSOC,fg_GMG,fg_LSBP,fg_KNN,fg_MOG2;
char c = cv::waitKey(10);
while (c != 's') {
cap >> frame;
if (frame.empty()) {
break;
}
start = clock(); cnt->apply(frame, fg_CNT); finish = clock(); runtime_cnt.push_back(finish - start);
start = clock(); mog->apply(frame, fg_MOG); finish = clock(); runtime_mog.push_back(finish - start);
start = clock(); mog2->apply(frame, fg_MOG2); finish = clock(); runtime_mog2.push_back(finish - start);
start = clock(); gsoc->apply(frame, fg_GSOC); finish = clock(); runtime_gsoc.push_back(finish - start);
start = clock(); lsbp->apply(frame, fg_LSBP); finish = clock(); runtime_lsbp.push_back(finish - start);
start = clock(); knn->apply(frame, fg_KNN); finish = clock(); runtime_knn.push_back(finish - start);
start = clock(); gmg->apply(frame, fg_GMG); finish = clock(); runtime_gmg.push_back(finish - start);
cv::imshow("CNT", fg_CNT);
cv::imshow("MOG",fg_MOG);
cv::imshow("MOG2",fg_MOG2);
cv::imshow("GSOC",fg_GSOC);
cv::imshow("LSBP", fg_LSBP);
cv::imshow("KNN",fg_KNN);
cv::imshow("GMG",fg_GMG);
c = cv::waitKey(33);
if (c == 'g') {
cv::imwrite("CNT.jpg", fg_CNT);
cv::imwrite("MOG.jpg", fg_MOG);
cv::imwrite("MOG2.jpg", fg_MOG2);
cv::imwrite("GSOC.jpg", fg_GSOC);
cv::imwrite("LSBP.jpg", fg_LSBP);
cv::imwrite("KNN.jpg", fg_KNN);
cv::imwrite("GMG.jpg", fg_GMG);
}
}
std::cout << "CNT:" << std::accumulate(std::begin(runtime_cnt), std::end(runtime_cnt), 0.0) / runtime_cnt.size() << std::endl;
std::cout << "MOG:" << std::accumulate(std::begin(runtime_mog), std::end(runtime_mog), 0.0) / runtime_mog.size() << std::endl;
std::cout << "MOG2:" << std::accumulate(std::begin(runtime_mog2), std::end(runtime_mog2), 0.0) / runtime_mog2.size() << std::endl;
std::cout << "GSOC:" << std::accumulate(std::begin(runtime_gsoc), std::end(runtime_gsoc), 0.0) / runtime_gsoc.size() << std::endl;
std::cout << "LSBP:" << std::accumulate(std::begin(runtime_lsbp), std::end(runtime_lsbp), 0.0) / runtime_lsbp.size() << std::endl;
std::cout << "KNN:" << std::accumulate(std::begin(runtime_knn), std::end(runtime_knn), 0.0) / runtime_knn.size() << std::endl;
std::cout << "GMG:" << std::accumulate(std::begin(runtime_gmg), std::end(runtime_gmg), 0.0) / runtime_gmg.size() << std::endl;
}
BGSegmAlg::~BGSegmAlg()
{
}
参考文献
[1] BackgroundSubtractorCNT https://github.com/sagi-z/BackgroundSubtractorCNT.
[2] Andrew B Godbehere, Akihiro Matsukawa, and Ken Goldberg. Visual tracking of human visitors under variable-lighting conditions for a responsive audio art installation. In American Control Conference (ACC), 2012, pages 4305–4312. IEEE, 2012.
[3] Pakorn KaewTraKulPong and Richard Bowden. An improved adaptive background mixture model for real-time tracking with shadow detection. In Video-Based Surveillance Systems, pages 135–144. Springer, 2002.
[4] L. Guo, D. Xu, and Z. Qiang. Background subtraction using local svd binary pattern. In 2016 IEEE Conference on Computer Vision and Pattern Recognition Workshops (CVPRW), pages 1159–1167, June 2016.
307

被折叠的 条评论
为什么被折叠?



