1. 程式人生 > >Farneback 光流演算法詳解與 calcOpticalFlowFarneback 原始碼分析

Farneback 光流演算法詳解與 calcOpticalFlowFarneback 原始碼分析

光流基礎概念

真實的三維空間中,描述物體運動狀態的物理概念是運動場。三維空間中的每一個點,經過某段時間的運動之後會到達一個新的位置,而這個位移過程可以用運動場來描述。

而在計算機視覺的空間中,計算機所接收到的訊號往往是二維圖片資訊。由於缺少了一個維度的資訊,所以其不再適用以運動場描述。光流場(optical flow)就是用於描述三維空間中的運動物體表現到二維影象中,所反映出的畫素點的運動向量場。

這裡寫圖片描述

  • 當描述部分畫素時,稱為:稀疏光流
  • 當描述全部畫素時,稱為:稠密光流

光流法是利用影象序列中的畫素在時間域上的變化、相鄰幀之間的相關性來找到的上一幀跟當前幀間存在的對應關係,計算出相鄰幀之間物體的運動資訊的一種方法。光流法理解的關鍵點有:

  • 核心問題:同一個空間中的點,在下一幀即將出現的位置
  • 重要假設:光流的變化(向量場)幾乎是光滑
  • 角點處的光流能夠通過角點的鄰域完全確定下來,因此角點處的運動資訊最為可靠;其次是邊界的資訊

光流法有著各種各樣的分支,本文介紹的則是一種被廣泛使用的經典稠密光流演算法:Farneback 光流演算法

Farneback 光流演算法

  • 作者:Gunnar Farneback
  • 參考資料
    • Two-Frame Motion Estimation Based on Polynomial Expansion,提綱閱讀
    • Polynomial Expansion for Orientation and Motion Estimation,作者博士畢業論文,深讀(以下簡稱:博士論文

OpenCV 主函式原始碼:

void calcOpticalFlowFarneback( const Mat& prev0, const Mat& next0, Mat& flow0, 
    double pyr_scale, int levels, 
    int winsize, int iterations, int poly_n, double poly_sigma, int flags )
{
    ……
    for( k = 0, scale = 1; k < levels; k++ )
    {
        scale *= pyr_scale;
        if
( prev0.cols*scale < min_size || prev0.rows*scale < min_size ) break; } levels = k; for( k = levels; k >= 0; k-- ) { for( i = 0, scale = 1; i < k; i++ ) scale *= pyr_scale; …… Mat R[2], I, M; for( i = 0; i < 2; i++ ) { img[i]->convertTo(fimg, CV_32F); GaussianBlur(fimg, fimg, Size(smooth_sz, smooth_sz), sigma, sigma); resize( fimg, I, Size(width, height), CV_INTER_LINEAR ); FarnebackPolyExp( I, R[i], poly_n, poly_sigma ); } FarnebackUpdateMatrices( R[0], R[1], flow, M, 0, flow.rows ); for( i = 0; i < iterations; i++ ) { if( flags & OPTFLOW_FARNEBACK_GAUSSIAN ) FarnebackUpdateFlow_GaussianBlur( R[0], R[1], flow, M, winsize, i < iterations - 1 ); else FarnebackUpdateFlow_Blur( R[0], R[1], flow, M, winsize, i < iterations - 1 ); } prevFlow = flow; } }

原始碼中為了解決孔徑問題,主函式中引入了影象金字塔。

孔徑問題(Aperture Problem):
- http://blog.csdn.net/hankai1024/article/details/23433157
- 形象理解:從小孔中觀察一塊移動的黑色幕布觀察不到任何變化。但實際情況是幕布一直在移動中
- 解決方案:從不同尺度(影象金字塔)上對影象進行觀察,由高到低逐層利用上一層已求得資訊來計算下一層資訊

主函式 calcOpticalFlowFarneback 中需要的變數引數包括:
1. _prev0:輸入前一幀影象
2. _next0:輸入後一幀影象
3. _flow0:輸出的光流
4. pyr_scale:金字塔上下兩層之間的尺度關係
5. levels:金字塔層數
6. winsize:均值視窗大小,越大越能 denoise 並且能夠檢測快速移動目標,但會引起模糊運動區域
7. iterations:迭代次數
8. poly_n:畫素鄰域範圍大小,一般為 5、7 等
9. poly_sigma:高斯標準差,一般為 1~1.5(函式處理中需要高斯分佈權重)
10. flags:計算方法,包括 OPTFLOW_USE_INITIAL_FLOW 和 OPTFLOW_FARNEBACK_GAUSSIAN

實際的 Farneback 演算法在每一層金字塔上的處理過程為:

Mat R[2], I, M;
for( i = 0; i < 2; i++ )
{
    img[i]->convertTo(fimg, CV_32F);
    GaussianBlur(fimg, fimg, Size(smooth_sz, smooth_sz), sigma, sigma);
    resize( fimg, I, Size(width, height), CV_INTER_LINEAR );
    FarnebackPolyExp( I, R[i], poly_n, poly_sigma );
}

FarnebackUpdateMatrices( R[0], R[1], flow, M, 0, flow.rows );

for( i = 0; i < iterations; i++ )
{
    if( flags & OPTFLOW_FARNEBACK_GAUSSIAN )
        FarnebackUpdateFlow_GaussianBlur( R[0], R[1], flow, M, winsize, i < iterations - 1 );
    else
        FarnebackUpdateFlow_Blur( R[0], R[1], flow, M, winsize, i < iterations - 1 );
}    

輸入的影象預設為灰度圖片,預設只有亮度值

影象輸入與輸出時進行的高斯模糊操作(Gaussian Blur)操作都是使得光流場結果平滑,從而滿足假設:光流的變化幾乎是光滑的

所以需要關注的子函式有 4 個:
1. FarnebackPolyExp
2. FarnebackUpdateMatrices
3. FarnebackUpdateFlow_GaussianBlur
4. FarnebackUpdateFlow_Blur

OpenCV 子函式 FarnebackPolyExp:

static void FarnebackPolyExp( const Mat& src, Mat& dst, int n, double sigma )

理論基礎

影象建模

將影象視為二維訊號的函式(輸出影象是灰度影象),因變數是二維座標位置 x=(xy)T,並且利用二次多項式對於影象進行近似建模的話,會得到:

f(x)xTAx+bTx+c
其中,A 是一個2×2的矩陣,b是一個2×1的矩陣

因此係數化之後,以上公式等號右側可以寫為:

(xy)T(r4r6/2r6/2r5)(xy)+(r2r3)T(xy)+r1=r1+r2x+r3y+r4x2+r5y2+r6xy

求解空間轉換

如果將原有(笛卡爾座標系)影象的二維訊號空間,轉換到以 (1,x,y,x2,y2,xy) 作為基函式的空間,則表示影象需要一個六維向量作為係數,代入不同畫素點的位置 x,y 求出不同畫素點的灰度值。

Farneback 演算法對於每幀影象中的每個畫素點周圍設定一個鄰域(2n+1)×(2n+1),利用鄰域內的共(2n+1)2個畫素點作為最小二乘法的樣本點,擬合得到中心畫素點的六維繫數。因此對於影象中的每個畫素點,都能得到一個六維向量。

在一個鄰域內灰度值的 (2n+1)×(2n+1) 矩陣中,將矩陣按列優先次序拆分組合成 (2n+1)2×1 的向量 f,同時已知 (1,x,y,x2,y2,xy) 作為基函式的轉換矩陣 B 維度為 (2n+1)2×6(也可以視為 6 個列向量 bi 共同組成的矩陣),鄰域內共有的係數向量 r 維度為 6×1,則有:

f=B×r=(b1b2b3b4b5b6)×r

此處博士論文中有舉例說明,非常便於理解,詳見博士論文 3.4 小節

權重分配

利用最小二乘法求解時,並非是鄰域內每個畫素點樣本誤差都對中心點有著同樣的影響力,函式中利用二維高斯分佈將影響力賦予權重。