背景減除(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;
}
原視訊如上圖所示,下面是效果視訊: