1. 程式人生 > >光流金字塔calcOpticalFlowPyrLK進行特徵點跟蹤

光流金字塔calcOpticalFlowPyrLK進行特徵點跟蹤

光流描述的是影象上每個畫素點的灰度的位置(速度)變化情況,光流的研究是利用影象序列中的畫素強度資料的時域變化和相關性來確定各自畫素位置的“運動”。研究光流場的目的就是為了從圖片序列中近似得到不能直接得到的運動場。
光流法的前提假設:
- (1)相鄰幀之間的亮度恆定;
- (2)相鄰視訊幀的取幀時間連續,或者,相鄰幀之間物體的運動比較“微小”;
- (3)保持空間一致性;即,同一子影象的畫素點具有相同的運動;

光流金字塔calcOpticalFlowPyrLK的C++定義如下:

CV_EXPORTS_W void calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,
                           InputArray prevPts, CV_OUT InputOutputArray nextPts,
                           OutputArray status, OutputArray err,
                           Size winSize=Size(21
,21), int maxLevel=3, TermCriteria criteria=TermCriteria( TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), double derivLambda=0.5, int flags=0, double
minEigThreshold=1e-4);

各個引數代表的含義如下:
- prevImg
你的標定影象的灰度圖
- nextImg
你想搜尋的影象的灰度圖
- prevPts
輸入的標定影象的特徵點(可以是其他特徵點檢測方法找到的點)
- nextPts
輸出場景的特徵點
- status
輸出狀態向量(無符號char),如果在當前影象中能夠光流得到標定的特徵點位置改變,則設定status的對應位置為1,否則設定為0
- err
輸出錯誤向量;向量的每個元素被設為相應特徵的一個錯誤,誤差測量的型別可以在flags引數中設定;如果流不被發現然後錯誤未被定義(使用status

(狀態)引數找到此情形)。
- winSize
在每個金字塔水平搜尋視窗的尺寸
- maxLevel
金字塔的高度,初始為3層

當使用calcOpticalFlowPyrLK作為光流金字塔的演算法時候,我們只需要知道以下的幾點:

  • calcOpticalFlowPyrLK必須和其他的角點識別演算法進行搭配使用,比如我這裡使用的goodFeaturesToTrack,將其他的角點識別演算法中獲得的角點作為光流演算法的prevPts
  • status 的大小和當前需要識別的光流移動的特徵點大小一樣,所以我們可以判定當前的影象是否還能與標定影象進行光流的依據

執行環境為:

- opencv 2.3.1

- vs2017

以下是一個傻瓜示例程式碼:


#include<iostream>  

#include<opencv.hpp>


using namespace std;
using namespace cv;


int main()
{
    Mat image1, image2;
    vector<Point2f> point1, point2, pointCopy;
    vector<uchar> status;
    vector<float> err;


    VideoCapture video(0);
    video >> image1;
    Mat image1Gray, image2Gray;
    cvtColor(image1, image1Gray, CV_RGB2GRAY);
    goodFeaturesToTrack(image1Gray, point1, 100, 0.01, 10, Mat());
    pointCopy = point1;
    for (int i = 0; i < point1.size(); i++)    //繪製特徵點位  
    {
        circle(image1, point1[i], 1, Scalar(0, 0, 255), 2);
    }
    namedWindow("光流特徵圖");
    while (true)
    {
        video >> image2;
        if (waitKey(33) == ' ')  //按下空格選擇當前畫面作為標定影象  
        {
            cvtColor(image2, image1Gray, CV_RGB2GRAY);
            goodFeaturesToTrack(image1Gray, point1, 100, 0.01, 10, Mat());
            pointCopy = point1;
        }
        cvtColor(image2, image2Gray, CV_RGB2GRAY);
        calcOpticalFlowPyrLK(image1Gray, image2Gray, point1, point2, status, err, Size(50, 50), 3); //LK金字塔       
        int tr_num = 0;
        vector<unsigned char>::iterator status_itr = status.begin();
        while (status_itr != status.end()) {
            if (*status_itr > 0)
                tr_num++;
            status_itr++;
        }
        if (tr_num < 6) {
            cout << "you need to change the feat-img because the background-img was all changed" << endl;
            if (waitKey(0) == ' ') {
                cvtColor(image2, image1Gray, CV_RGB2GRAY);
                goodFeaturesToTrack(image1Gray, point1, 100, 0.01, 10, Mat());
                pointCopy = point1;
            }
        }
        for (int i = 0; i < point2.size(); i++)
        {
            circle(image2, point2[i], 1, Scalar(0, 0, 255), 2);
            line(image2, pointCopy[i], point2[i], Scalar(255, 0, 0), 2);
        }

        imshow("光流特徵圖", image2);
        swap(point1, point2);
        image1Gray = image2Gray.clone();
    }
    return 0;
}

效果如圖:

- 標定影象

這裡寫圖片描述

- 鏡頭移動

這裡寫圖片描述

ps: 紅點為標定點,藍線為跟蹤點的光流移動