背景减除(Background Subtraction)是许多基于计算机视觉的任务中的主要预处理步骤。如果我们有完整的静止的背景帧,那么我们可以通过帧差法来计算像素差从而获取到前景对象。但是在大多数情况下,我们可能没有这样的图像,所以我们需要从我们拥有的任何图像中提取背景。当运动物体有阴影时,由于阴影也在移动,情况会变的变得更加复杂。为此引入了背景减除算法,通过这一方法我们能够从视频中分离出运动的物体前景,从而达到目标检测的目的。
MOG2
MOG2算法,也是高斯混合模型分离算法,是MOG的改进算法。它基于Z.Zivkovic发布的两篇论文,即2004年发布的“Improved adaptive Gausian mixture model for background subtraction”和2006年发布的“Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction”中提出。该算法的一个重要特征是 它为每个像素选择适当数量的高斯分布,它可以更好地适应不同场景的照明变化等。
基本思路:
混合高斯模型:
将图像分为3-5个高斯模型,一个像素点来了,如果该像素点离任何一个高斯模型的距离大于其2倍的标准差,则为前景即运动物体,否则则是背景
步骤:第一步:初始各种参数
第二步:使用T帧图像构造模型,对于第一个帧图像的第一个像素点,使用u1,σ1构造高斯模型
第三步:对于一个新来的模型,如果该像素在高斯模型3*σ1内,则属于该高斯模型,对参数进行更新
第四步:如果不满足该高斯模型,重新建立一个新的高斯模型
//构造高斯混合模型
Ptr<BackgroundSubtractor> ptrMOG2 = createBackgroundSubtractorMOG2();
//getStructuringElement构造形态学使用的kernel
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
//运用高斯模型进行拟合,在两个标准差内设置为0,在两个标准差外设置为255
ptrMOG2->apply(frame, bgMask_MOG2);
//使用形态学的开运算做背景的去除
morphologyEx(bgMask_MOG2, bgMask_MOG2, MORPH_OPEN, kernel);
完整项目代码,可以进行读写视频:
#include <iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include<cstdio>
using namespace std;
using namespace cv;
int main()
{
VideoCapture video = VideoCapture("F:\\计算机视觉\\c5.mp4");
VideoWriter write;
double fps = video.get(CV_CAP_PROP_FPS); //获取视频帧率
double pauseTime = 1000 / fps; //两幅画面中间间隔
int w = static_cast<int>(video.get(CV_CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(video.get(CV_CAP_PROP_FRAME_HEIGHT));
cv::Size S(w, h);
write.open("testMOGMask.avi", -1, fps, S, true);
if (!video.isOpened()) {
cout << "could not load video file ..." << endl;
return -1;
}
Mat frame, bgMask_MOG2, bgMask_KNN, background;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
namedWindow("highway_test", WINDOW_AUTOSIZE);
namedWindow("background_mask_by_MOG2", WINDOW_AUTOSIZE);
Ptr<BackgroundSubtractor> ptrMOG2 = createBackgroundSubtractorMOG2();
while (video.read(frame)) {
imshow("highway_test", frame);
ptrMOG2->apply(frame, bgMask_MOG2);
morphologyEx(bgMask_MOG2, bgMask_MOG2, MORPH_OPEN, kernel);
imshow("background_mask_by_MOG2", bgMask_MOG2);
write.write(bgMask_MOG2);
char c = waitKey(50);
if (c == 27) {
break;
}
}
//waitKey(0);
write.release();
video.release();
cv::destroyWindow("video");
return 0;
}

KNN
KNN算法,即K-nearest neigbours - based Background/Foreground Segmentation Algorithm。2006年,由Zoran Zivkovic 和Ferdinand van der Heijden在论文"Efficient adaptive density estimation per image pixel for the task of background subtraction."中提出。
Ptr<BackgroundSubtractor> ptrKNN = createBackgroundSubtractorKNN();
ptrKNN->apply(frame, bgMask_KNN);
完整代码,可以读取视频,写入视频,并提取前后景
//以下代码用OpenCV实现了视频中背景消除和提取的建模,涉及到KNN(K近邻算法)
#include <iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include<cstdio>
using namespace std;
using namespace cv;
int main()
{
VideoCapture video = VideoCapture("F:\\计算机视觉\\c5.mp4");
VideoWriter write;
double fps = video.get(CV_CAP_PROP_FPS); //获取视频帧率
double pauseTime = 1000 / fps; //两幅画面中间间隔
int w = static_cast<int>(video.get(CV_CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(video.get(CV_CAP_PROP_FRAME_HEIGHT));
cv::Size S(w, h);
write.open("testKNNMASK.avi", -1, fps, S, true);
if (!video.isOpened()) {
cout << "could not load video file ..." << endl;
return -1;
}
Mat frame, bgMask_KNN, background;
namedWindow("highway_test", WINDOW_AUTOSIZE);
//namedWindow("background_mask_by_MOG2", WINDOW_AUTOSIZE);
//namedWindow("background_by_KNN", WINDOW_AUTOSIZE);
namedWindow("background_mask_by_KNN", WINDOW_AUTOSIZE);
Ptr<BackgroundSubtractor> ptrKNN = createBackgroundSubtractorKNN();
while (video.read(frame)) {
imshow("highway_test", frame);
ptrKNN->apply(frame, bgMask_KNN);
//ptrKNN->getBackgroundImage(background);
//imshow("background_by_KNN", background);
imshow("background_mask_by_KNN", bgMask_KNN);
write.write(bgMask_KNN);
char c = waitKey(50);
if (c == 27) {
break;
}
}
write.release();
video.release();
cv::destroyWindow("video");
return 0;
}
原视频如上图所示,下面是效果视频: