Farneback光流


    使用稀疏光流跟蹤算法,可以實現關鍵點跟蹤。大概流程為:首先在上一幀圖像中檢測出關鍵點,然后在下一幀圖像中評估關鍵點位移量。如果下一幀圖像以統一的方向偏移,則可以剔除該偏移以達到視頻穩像效果。

    關鍵點檢測是稀疏光流跟蹤基礎,一般情況下,關鍵點為角點,邊緣上的點由於孔徑問題難以在系數光流中實現跟蹤。使用稠密光流算法可以描述所有點(包括邊緣上點)的運動情況,這在某些邊緣較多的場景中,稠密光流算法更加便於描述物體運動。

    Faerneback 算法使用多項式擬合平面方式評估圖像中各個點的運動情況,其擬合平面方程為:

    當圖像發生平移后,平移前后擬合的二次多項式也同樣存在一個平移關系:,由於  已知,則可以求解出 

    在一維圖像下,可使用下圖表示:

    

    上圖中,擬合前后兩幀圖像相同位置附近區間的二次曲線,通過模型  可求解偏移量 d。

    以上跟蹤精度取決於擬合函數精度,當擬合區間越小,更細小的變化可以被二次函數表現處理。但過小的擬合區間可能受到噪聲影響,故需要選擇一個合適的擬合區間。同時,可以使用多次擬合的方式來提升跟蹤精度,第一次擬合時,下一幀圖像擬合中心與上一幀圖像擬合中心保持一致;當通過第一次擬合求解出偏移量后,使用偏移后位置作為下一幀圖像擬合中心,可以求解更精確擬合函數,從而得到更精確跟蹤精度。

    通過金字塔分層方式可以跟蹤到大偏移。首先使用較低分辨率跟蹤各點偏移量,再以已經得到偏移量作為擬合中心,使用更高分辨率圖像求解更精確偏移量,即可跟蹤較大偏移。

    

    在 opencv 中,使用 cv::calcOpticalFlowFarneback() 函數實現基於二次多項式擬合的稠密光流計算,其函數原型如下:

    void cv::calcOpticalFlowFarneback(cv::InputArray preImg, cv::InputArray nextImg, cv::InputOutputArray flow,

                                                          double pyrScale, int levels, int winsize, int iterations, int polyN, double polySigma, int flags);

    preImg, nextImg: 傳入圖像序列中前一幀與后一幀圖像,圖像格式均為8位單通道,且尺寸應該保持一致。

    flow: 函數記錄各點運動向量,圖像格式位32位浮點雙通道(CV_32FC2),圖像尺寸與 preImg, nextImg 保持一致。

    pyrScale: 表示金字塔各層縮減系數,一般使用 .5。

    levels: 表示金字塔層數。

    winsize: 在光流跟蹤前,使用 winsize 窗口大小對圖像平滑,避免噪聲干擾;可將 flags 設置為 cv::OPTFLOW_FARNEBACK_GAUSSIAN 以使用 Gaussian 平滑。

    iterations: 通過控制各層上迭代擬合次數控制跟蹤精度。

    polyN: 控制二次多項式擬合區間大小。

    polySigma: polyN = 5, polySigma = 1.1; polyN = 7, polySigma = 1.5。

    flags: 當使用 cv::OPTFLOW_USE_INITIAL_FLOW 時,flow 被同時當作輸入參數,表示初始光流速度場;通常在序列視頻中,相鄰幀運動方向具有相似性,故可以使用上幀速度場作為初始猜測。

    以下代碼計算兩幀圖像間的速度場:

 

 1 cv::Mat img0 = cv::imread("prev.bmp", cv::IMREAD_GRAYSCALE);
 2 cv::Mat img1 = cv::imread("next.bmp", cv::IMREAD_GRAYSCALE);
 3 cv::Mat flow(img0.size(), CV_32FC2);
 4 cv::calcOpticalFlowFarneback(img0,
 5         img1,
 6         flow,
 7         .5,
 8         3,
 9         5,
10         1,
11         7,
12         1.5,
13         cv::OPTFLOW_FARNEBACK_GAUSSIAN);
14 
15 cv::Mat flow2(flow.size(), CV_8UC1);
16 for (int y = 0; y < flow.rows; ++y)
17 {
18     float* data1 = flow.ptr<float>(y);
19     uchar* data2 = flow2.ptr<uchar>(y);
20     for (int x = 0; x < flow.cols; ++x)
21     {
22         float mx = data1[x * 2];
23         float my = data1[x * 2 + 1];
24         float mv = sqrt(mx * mx + my * my);
25         data2[x] = cv::saturate_cast<uchar>(mv * 10);
26     }
27 }
28 cv::imwrite("Farneback.bmp", flow2);

 

 

    執行效果如下:

        

   

    

 

    參考資料: Learning Opencv 3   Adrian Kaehler & Gary Bradski


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM