幀差法
幀差法,顧名思義就是將視頻中前後兩幀做減法,當前幀在(x,y)點處的像素值減去上一幀在(x,y)處的像素值。該方法的優點是提取效果比較穩定,速度比較快。缺點也是蠻明顯的,如果你要提取的目標是靜止的或者移動速度很慢,前後兩幀的前景物體會有很大一部分重合,從而會導致提取出來的圖像有空洞。
當我們要提取前景時,幀差法基本上是最簡單的一種方法了,變化緩慢的背景,以及運動較快的物體,在進行幀差法之後,進行閾值分割,將差值圖像變成二值圖像,就完成了運動目標以及背景的分離。
本文主要講一下最簡單的幀差,三幀差法原理相差不大,對稱差分就不講了。
公式如下:
示例
我簡單編寫了一個幀差法及閾值分割的程序如下:
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
VideoCapture capture("F://crop.avi");
//VideoCapture capture(0);//直接調用攝像頭
if (!capture.isOpened())
{
cout << "data error" << endl;
return false;
}//測試數據是否讀入
Mat temp, pre, current, result;
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));//結構元素
Mat image1, image2;
Mat After_midfiler;
//Mat bsmaskMOG2;
int num = 0;
///Ptr<BackgroundSubtractor> pMOG2 = createBackgroundSubtractorMOG2();
while (1)
{
capture >> temp;
imshow("show", temp);
//------------------------------------------
++num;
//pMOG2->apply(temp, bsmaskMOG2);
if (num == 1)
{
//BackgroundSubtractorMOG2::getBackgroundImage();
cvtColor(temp, pre, COLOR_BGR2GRAY);
}
if (num > 1)
{
cvtColor(temp, current, COLOR_BGR2GRAY);
absdiff(pre, current, result);//用幀差法求前景
imshow("【背景】", pre);
imshow("【幀差】", result);
threshold(result, result, 60, 255, 0);
imshow("【閾值分割後】", result);
medianBlur(result, After_midfiler, 3); //中值濾波法
imshow("After_midfiler", After_midfiler);
morphologyEx(After_midfiler, image1, MORPH_OPEN, element);
morphologyEx(image1, image2, MORPH_CLOSE, element);
imshow("image2", image2);
char c = waitKey(30);
if (c == 27)
break;
cvtColor(temp, pre, COLOR_BGR2GRAY);
}
}
}
這裏只放上原視頻截圖以及幀差的結果
由於魚的遊動十分緩慢,幀差的結果並不理想。
優化嘗試1
當運動物體變化很慢時,前面已經說過幀差會產生空洞,再進行閾值分割,這些部分會被當做背景處理。思考了一下,每一幀的變化很慢,但是總是會有變化的,如果將這每一幀與第一幀進行差分會有什麼結果?
(註釋掉代碼cvtColor(temp, pre, COLOR_BGR2GRAY);即可)
嘗試的運行結果如下:
這裏的結果明顯清晰了許多,因爲後面魚的位置與第一幀相比有了很大的區別,然而這裏的問題是,第一幀中的魚乾擾了結果,本來只有兩條,幀差之後變成了四條。
優化嘗試2
再上面的基礎上進行優化,思路是想辦法將第一幀中的魚去掉,只留下魚缸。也就是嘗試進行簡單的背景建模。
我的具體做法是,將當前每一幀與第一幀進行逐個像素的對比,由於魚的遊動,每一像素點都會存在沒有魚的時刻,想辦法將這些不同時刻的像素點整合到一幀裏就可以。我用的代碼如下:
for (int i = 0;i < current.rows;i++)
{
uchar *p = current.ptr<uchar>(i);
uchar *q = pre.ptr<uchar>(i);
for (int j = 0;j < current.cols;j++)
{
if (q[j] < p[j])
q[j] = p[j];
}
}
運行效果如下:
結果較爲喜人,但是這個建模需要一定的時間,並且建模過程中會受到光照的影響。通過遍歷,會把光照增加的部分添加到背景之中。
——————————————————————————————
分割線,後續針對幀差法的優化措施待更新~~~~~~~~~~~~~