Lucas-Kanande金字塔光流實現(xiàn)Harris角點追蹤

附上一篇關(guān)于金字塔理論的詳細講解,想要深入了解金字塔追蹤原理的可以研究一下寂诱,這篇博文則相對簡單易懂些歉摧。http://blog.csdn.net/app_12062011/article/details/51880258

首先灿意,我們應(yīng)該明白什么是角點追蹤。
角點追蹤扫沼,即視頻中的特征點隨著幀的改變而移動,在不斷播放的視頻中,查找一個移動的物體軌跡的過程尤揣。


接下來酪穿,我們可以根據(jù)不斷的分析而自動代入金字塔的光流檢測矿瘦。
既然我們的目的是追蹤動態(tài)的角點,那么首先應(yīng)該有已確定的特征點,這時,可以使用自動設(shè)置參數(shù)進行特征點的查找渐行,查找后調(diào)節(jié)一下精度,就可以以備使用了铸董。我們還可以使用鼠標點擊的方式來確定特征點祟印,這里需要補充一個函數(shù):
onMouse函數(shù),函數(shù)的定義方式為:

static void onMouse( int event, int x, int y, int /*flags*/, void* /*param*/ )

其中粟害,event代表點擊事件蕴忆,x,y則代表點擊時鼠標指針的所在位置悲幅,引用方式為

namedWindow( "Demo", 1 );
setMouseCallback( "Demo", onMouse, 0 );

表示在某個窗口發(fā)生的點擊事件套鹅,點擊事件調(diào)用的函數(shù),以及傳給回調(diào)函數(shù)的參數(shù)夺艰,參數(shù)默認值為0芋哭。

當我們已經(jīng)在第一張圖片中固定到了特征點,那么接下來要做的就是在接下來的每幀圖片中找到 該特征點的位置了郁副。

在尋找特征值的位置的時候,則需要正式引入Lucas-Kanande金字塔的相關(guān)知識了豌习。

首先存谎,為了簡化追蹤的難度,LK光流法提出了以下三條假設(shè):

(1)亮度恒定肥隆,就是同一點隨著時間的變化既荚,其亮度不會發(fā)生改變。該假設(shè)用于得到光流法基本方程栋艳;
(2)小運動恰聘,這個也必須滿足,就是時間的變化不會引起位置的劇烈變化吸占,這樣灰度才能對位置求偏導(dǎo)晴叨。
(3)空間一致,一個場景上鄰近的點投影到圖像上也是鄰近點矾屯,且鄰近點速度一致兼蕊。這是Lucas-Kanade光流法特有的假定,因為光流法基本方程約束只有一個件蚕,而要求x孙技,y方向的速度产禾,有兩個未知變量。我們假定特征點鄰域內(nèi)做相似運動牵啦,就可以連立n多個方程求取x亚情,y方向的速度(n為特征點鄰域總點數(shù),包括該特征點)哈雏。

為了正確追蹤到角點势似,我們可以嘗試在原圖中角點的附近設(shè)置一個鄰域,由于是小運動僧著,那么兩幀圖片中的特征點位置變化也不會太大履因,我們只需要在鄰域中尋找符合原角點的亮度以及周圍環(huán)境信息的角點,確定其為運動后的角點即可盹愚。

Lucas-Kanade是一種廣泛使用的光流估計的差分方法栅迄,這個方法是由Bruce D. Lucas和Takeo Kanade發(fā)明的。它假設(shè)光流在像素點的鄰域是一個常數(shù)皆怕,然后使用最小二乘法對鄰域中的所有像素點求解基本的光流方程毅舆。

但是這是在假設(shè)完全成立的時候才正確的,一旦物體運動過快愈腾,那么會造成很大的誤差憋活,而且如果盲目擴大鄰域,算法的時間又是一個很大的問題虱黄,所以引出了金字塔分層悦即,針對仿射變換的改進Lucas-Kanade算法。

算法的關(guān)鍵內(nèi)容在于:當在鄰域內(nèi)尋找對應(yīng)特征點的時候橱乱,首先將原圖像進行縮小辜梳,縮小后的圖像相對的特征點的位移像素點就也會變小,可以先在縮小后的圖像中尋找匹配的特征值泳叠,尋找出位置之后再進行迭代作瞄,直到迭代到原圖像上的位置信息。


如圖所示危纫,最底層是分辨率最大的原圖像宗挥,向上依次是采樣1/2的低分辨率圖像,通常在使用當中金字塔的層數(shù)為3-4層即可种蝶。因為隨著圖像的移動契耿,算法可以應(yīng)對光流大于窗口尺寸的特征點跟蹤問題。
金字塔L-K光流通常用來估計圖像特征點的光流蛤吓,以提高圖像光流場的計算速度宵喂。

因此,使用Lucas-Kanande金字塔的步驟為:
首先会傲,計算金字塔最頂層圖像的光流锅棕,根據(jù)最頂層光流結(jié)果計算其次上層的光流初始值拙泽,再進一步估算其光流的精確值。最后裸燎,用計算的次上層光流結(jié)果估計下一層光流的初始值顾瞻,計算其精確的值后再繼續(xù)帶入下一層計算,直到金字塔的最底層德绿。

公式的推演可以查看:http://blog.csdn.net/zhe123zhe123zhe123/article/details/50397143

具體的實現(xiàn)步驟可以看代碼:

#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <iostream>
#include <ctype.h>

using namespace cv;
using namespace std;



//--------------------------------【help( )函數(shù)】----------------------------------------------
//      描述:輸出幫助信息
//-------------------------------------------------------------------------------------------------
static void help()
{
    //輸出歡迎信息和OpenCV版本
    cout << "\n\n\t\t\t   當前使用的OpenCV版本為:" << CV_VERSION 
        <<"\n\n  ----------------------------------------------------------------------------" ;
    cout    << "\n\n\t該Demo演示了 Lukas-Kanade基于光流的lkdemo\n";
    cout << "\n\t程序默認從攝像頭讀入視頻荷荤,可以按需改為從視頻文件讀入圖像\n";
    cout << "\n\t操作說明: \n"
        "\t\t通過點擊在圖像中添加/刪除特征點\n" 
        "\t\tESC - 退出程序\n"
        "\t\tr -自動進行追蹤\n"
        "\t\tc - 刪除所有點\n"
        "\t\tn - 開/光-夜晚模式\n"<< endl;
}

Point2f point;
bool addRemovePt = false;

//--------------------------------【onMouse( )回調(diào)函數(shù)】------------------------------------
//      描述:鼠標操作回調(diào)
//-------------------------------------------------------------------------------------------------
static void onMouse( int event, int x, int y, int /*flags*/, void* /*param*/ )
{
    if( event == CV_EVENT_LBUTTONDOWN )
    {
        point = Point2f((float)x, (float)y);
        addRemovePt = true;
    }
}

//-----------------------------------【main( )函數(shù)】--------------------------------------------
//      描述:控制臺應(yīng)用程序的入口函數(shù),我們的程序從這里開始
//-------------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
    help();

    VideoCapture cap;
    TermCriteria termcrit(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 20, 0.03);
    Size subPixWinSize(10,10), winSize(31,31);

    const int MAX_COUNT = 500;
    bool needToInit = false;
    bool nightMode = false;

    cap.open(0);

    if( !cap.isOpened() )
    {
        cout << "Could not initialize capturing...\n";
        return 0;
    }

    namedWindow( "LK Demo", 1 );
    setMouseCallback( "LK Demo", onMouse, 0 );

    Mat gray, prevGray, image;
    
    //數(shù)組為2*n移稳,每個元素存放的都是一個點信息
    vector<Point2f> points[2];
        Mat frame;
    cap >> frame;

    for(;;)
    {
        cap >> frame;
        if( frame.empty() )
            break;

        frame.copyTo(image);
        cvtColor(image, gray, COLOR_BGR2GRAY);

        if( nightMode )
            image = Scalar::all(0);

        //如果需要自動尋找角點
        if( needToInit )
        {
            // 自動初始化
            //尋找角點蕴纳,將角點信息載入到points[1]數(shù)組中
            //goodFeaturesToTrack函數(shù)的參數(shù)為:
            //(原圖像,角點存放位置个粱,需要找到的最大角點數(shù)目古毛,品質(zhì)因子,可刪除角點范圍都许,感興趣區(qū)域稻薇,計算協(xié)方差矩陣時的窗口大小,是否使用Harris角點檢測胶征,Harris角點檢測K值)
            goodFeaturesToTrack(gray, points[1], MAX_COUNT, 0.01, 10, Mat(), 3, 0, 0.04);
            //將能夠?qū)⒔屈c位置精確到亞像素級精度塞椎。
            // 輸入的源角點信息points[1],精度調(diào)整后角點信息依舊存放在points[1]中
            cornerSubPix(gray, points[1], subPixWinSize, Size(-1,-1), termcrit);
            addRemovePt = false;
        }
        else if( !points[0].empty() )
        {
            vector<uchar> status;
            vector<float> err;
            if(prevGray.empty())
                gray.copyTo(prevGray);
            //光流金字塔睛低,作用在于根據(jù)第一幀圖片的特征點所在位置找到另一幀圖片的特征點信息
            //參數(shù)設(shè)置:
            //1.第一幀圖像
            //2.第二幀圖像(需要在這幅圖像上找到特征點改變后的位置)
            //3.第一幀圖像中的特征點
            //4.輸出在第二幀圖像中找到了對應(yīng)特征點的位置
            //5.輸出特征向量案狠,每個特征點是否找到了改變后的位置,如果找到了暇昂,每個元素設(shè)置為1莺戒,沒有找到設(shè)置為0
            //6.輸出出錯信息,誤差測量的類型在第10個參數(shù)中定義急波,如果沒有錯誤信息,則err為no defined
            //7.在每個金字塔水平搜尋窗口的尺寸瘪校。
            //8.最大的金字塔0層數(shù)澄暮;如果設(shè)置為0,不使用金字塔(單級)阱扬,如果設(shè)置為1泣懊,使用兩層,等等麻惶;如果金字塔是通過輸入算法將使用多層次的金字塔有但不超過 maxlevel馍刮。
            //9.參數(shù)指定的迭代搜索算法終止準則
            //10.參數(shù):optflow_use_initial_flow 使用初始估計,   
            //optflow_lk_get_min_eigenvals 使用最小特征值的誤差測量估計
            //11.一個矩陣光流方程的特征值如果在窗口中小于該值窃蹋,則忽略不計
            calcOpticalFlowPyrLK(prevGray, gray, points[0], points[1], status, err, winSize,
                3, termcrit, 0, 0.001);
            size_t i, k;
            for( i = k = 0; i < points[1].size(); i++ )
            {
                if( addRemovePt )
                {
                    //兩個像素點之間的距離
                    if( norm(point - points[1][i]) <= 5 )
                    {
                        addRemovePt = false;
                        continue;
                    }
                }

                if( !status[i] )
                    continue;

                points[1][k++] = points[1][i];
                circle( image, points[1][i], 3, Scalar(0,255,0), -1, 8);
            }
            points[1].resize(k);
        }

        if( addRemovePt && points[1].size() < (size_t)MAX_COUNT )
        {
            vector<Point2f> tmp;
            tmp.push_back(point);
            //輸入信息temp中卡啰,精度調(diào)整后角點信息存放在tmp中
            cornerSubPix( gray, tmp, winSize, cvSize(-1,-1), termcrit);
            points[1].push_back(tmp[0]);
            addRemovePt = false;
        }

        needToInit = false;
        imshow("LK Demo", image);

        char c = (char)waitKey(10);
        if( c == 27 )
            break;
        switch( c )
        {
        case 'r':
            //自動尋找角點
            needToInit = true;
            break;
        case 'c':
            points[0].clear();
            points[1].clear();
            break;
        case 'n':
            nightMode = !nightMode;
            break;
        }

        //交換兩個vector的所有元素
        std::swap(points[1], points[0]);
        cv::swap(prevGray, gray);
    }

    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末静稻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匈辱,更是在濱河造成了極大的恐慌振湾,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亡脸,死亡現(xiàn)場離奇詭異押搪,居然都是意外死亡,警方通過查閱死者的電腦和手機浅碾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門大州,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人垂谢,你說我怎么就攤上這事厦画。” “怎么了埂陆?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵苛白,是天一觀的道長。 經(jīng)常有香客問我焚虱,道長购裙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任鹃栽,我火速辦了婚禮躏率,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘民鼓。我一直安慰自己薇芝,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布丰嘉。 她就那樣靜靜地躺著夯到,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饮亏。 梳的紋絲不亂的頭發(fā)上耍贾,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音路幸,去河邊找鬼荐开。 笑死,一個胖子當著我的面吹牛简肴,可吹牛的內(nèi)容都是我干的晃听。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼能扒!你這毒婦竟也來了佣渴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤赫粥,失蹤者是張志新(化名)和其女友劉穎观话,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體越平,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡频蛔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了秦叛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晦溪。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挣跋,靈堂內(nèi)的尸體忽然破棺而出三圆,到底是詐尸還是另有隱情,我是刑警寧澤避咆,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布舟肉,位于F島的核電站,受9級特大地震影響查库,放射性物質(zhì)發(fā)生泄漏路媚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一樊销、第九天 我趴在偏房一處隱蔽的房頂上張望整慎。 院中可真熱鬧,春花似錦围苫、人聲如沸裤园。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拧揽。三九已至,卻和暖如春腺占,著一層夾襖步出監(jiān)牢的瞬間强法,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工湾笛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闰歪。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓嚎研,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子临扮,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容