溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

OpenCV怎么使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤

發(fā)布時(shí)間:2023-02-25 11:45:08 來源:億速云 閱讀:140 作者:iii 欄目:開發(fā)技術(shù)

這篇“OpenCV怎么使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“OpenCV怎么使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤”文章吧。

1、概述

案例:使用稀疏光流實(shí)現(xiàn)對象跟蹤

稀疏光流API介紹:

calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,
                                        InputArray prevPts, InputOutputArray nextPts,
                                        OutputArray status, OutputArray err,
                                        Size winSize = Size(21,21), int maxLevel = 3,
                                        TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),
                                        int flags = 0, double minEigThreshold = 1e-4 );
  • prevImg:視頻前一幀圖像/金字塔,單通道CV_8UC1

  • nextImg:視頻后一幀圖像/金字塔,單通道CV_8UC1

  • preVPts:前一幀圖像的特征向量(輸入)需要找到流的2D點(diǎn)的矢量(vector of 2D points for which the flow needs to be found;);點(diǎn)坐標(biāo)必須是單精度浮點(diǎn)數(shù)

  • nextPts:后一幀圖像的特征向量(輸出),輸出二維點(diǎn)的矢量(具有單精度浮點(diǎn)坐標(biāo)),包含第二圖像中輸入特征的計(jì)算新位置;當(dāng)傳遞OPTFLOW_USE_INITIAL_FLOW標(biāo)志時(shí),向量必須與輸入中的大小相同。

  • status:輸出狀態(tài)向量(無符號字符);如果找到相應(yīng)特征的流,則向量的每個元素設(shè)置為1,否則設(shè)置為0

  • err:輸出錯誤的矢量; 向量的每個元素都設(shè)置為相應(yīng)特征的錯誤,錯誤度量的類型可以在flags參數(shù)中設(shè)置; 如果未找到流,則未定義錯誤(使用status參數(shù)查找此類情況)

  • winSize:每個金字塔等級的搜索窗口的winSize大小

  • maxLevel:基于0的最大金字塔等級數(shù);如果設(shè)置為0,則不使用金字塔(單級),如果設(shè)置為1,則使用兩個級別,依此類推;如果將金字塔傳遞給輸入,那么算法將使用與金字塔一樣多的級別,但不超過maxLevel

  • criteria:停止條件,指定迭代搜索算法的終止條件(在指定的最大迭代次數(shù)criteria.maxCount之后或當(dāng)搜索窗口移動小于criteria.epsilon時(shí))。

  • flags:操作標(biāo)志,OPTFLOW_USE_INITIAL_FLOW使用初始估計(jì),存儲在nextPts中;如果未設(shè)置標(biāo)志,則將prevPts復(fù)制到nextPts并將其視為初始估計(jì)。

  • OPTFLOW_LK_GET_MIN_EIGENVALS使用最小特征值作為誤差測量(參見minEigThreshold描述);如果沒有設(shè)置標(biāo)志,則將原稿周圍的色塊和移動點(diǎn)之間的L1距離除以窗口中的像素?cái)?shù),用作誤差測量

  • minEigThreshold:算法計(jì)算光流方程的2x2正常矩陣的最小特征值,除以窗口中的像素?cái)?shù);如果此值小于minEigThreshold,則過濾掉相應(yīng)的功能并且不處理其流程,因此它允許刪除壞點(diǎn)并獲得性能提升

算法實(shí)現(xiàn)步驟:

1.實(shí)例化VideoCapture

2.循環(huán)讀取視頻數(shù)據(jù)

3.視頻幀灰度轉(zhuǎn)換

4.執(zhí)行角點(diǎn)檢測

5.保存角點(diǎn)檢測的特征數(shù)據(jù)

6.初始化時(shí)如果檢測到前一幀為空,把當(dāng)前幀的灰度圖像給前一幀

7.執(zhí)行光流跟蹤,并輸出跟蹤后的特征向量

8.遍歷光流跟蹤的輸出特征向量,并得到距離和狀態(tài)都符合預(yù)期的特征向量。讓后將其重新填充到fpts[1]中備用

9.重置集合大小

10.繪制光流線

11.交換特征向量的輸入和輸出

12.將用于跟蹤的角點(diǎn)繪制出來

13.展示最終的跟蹤效果

14.循環(huán)3~13步驟

15.結(jié)束

2、代碼示例

KLT_Object_Tracking::KLT_Object_Tracking(QWidget *parent)
    : MyGraphicsView{parent}
{
    isShowLine = false;
    this->setWindowTitle("KLT稀疏光流實(shí)現(xiàn)對象跟蹤");
    QPushButton *btn = new QPushButton(this);
    btn->setText("選擇視頻");
    connect(btn,&QPushButton::clicked,[=](){
        //選擇視頻
        path = QFileDialog::getOpenFileName(this,"請選擇視頻","/Users/yangwei/Downloads/",tr("Image Files(*.mp4 *.avi)"));
        qDebug()<<"視頻路徑:"<<path;
        startKltTracking(path.toStdString().c_str());
    });
    //
    QButtonGroup * group = new QButtonGroup(this);
    QRadioButton * radioNo = new QRadioButton(this);
    radioNo->setText("否");
    radioNo->setChecked(true);
    QRadioButton *radioYes = new QRadioButton(this);
    radioYes->setText("是");
    group->addButton(radioNo,0);
    group->addButton(radioYes,1);
 
    radioNo->move(0,btn->y()+btn->height()+20);
    radioYes->move(radioNo->x()+radioNo->width()+20,btn->y()+btn->height()+20);
    connect(radioNo,&QRadioButton::clicked,[=](){
        isShowLine = false;//顯示光流線
    });
    connect(radioYes,&QRadioButton::clicked,[=](){
        isShowLine = true;//不顯示光流線
    });
 
}
 
void KLT_Object_Tracking::startKltTracking(const char* filePath){
    //【1】實(shí)例化VideoCapture并打開視頻
    VideoCapture capture;//實(shí)例化視頻捕獲器
    capture.open(filePath);//打開視頻文件(或攝像頭)
    if(!capture.isOpened()){//檢測文件是否打開,如果沒打開直接退出
        qDebug()<<"無法打開視頻";
        return;
    }
 
    Mat frame,gray;
    vector<Point2f> features;//檢測出來的角點(diǎn)集合
    vector<Point2f> inPoints;//這個主要是為了畫線用的
    vector<Point2f> fpts[2];//[0],存入的是是二維特征向量,[1]輸出的二維特征向量
    Mat pre_frame,pre_gray;
    vector<uchar> status;//光流輸出狀態(tài)
    vector<float> err;//光流輸出錯誤
    //【2】循環(huán)讀取視頻
    while(capture.read(frame)){//循環(huán)讀取視頻中每一幀的圖像
        //【3】將視頻幀圖像轉(zhuǎn)為灰度圖
        cvtColor(frame,gray,COLOR_BGR2GRAY);//ps:角點(diǎn)檢測輸入要求單通道
 
        //【4】如果特征向量(角點(diǎn))小于40個我們就重新執(zhí)行角點(diǎn)檢測
        if(fpts[0].size()<40){//如果小于40個角點(diǎn)就重新開始執(zhí)行角點(diǎn)檢測
            //執(zhí)行角點(diǎn)檢測
            goodFeaturesToTrack(gray,features,5000,0.01,10,Mat(),3,false,0.04);
            //【5】將檢測到的角點(diǎn)放入fpts[0]中作為,光流跟蹤的輸入特征向量
            //將檢測到的角點(diǎn)插入vector
            fpts[0].insert(fpts[0].begin(),features.begin(),features.end());
            inPoints.insert(inPoints.end(),features.begin(),features.end());
            qDebug()<<"角點(diǎn)檢測執(zhí)行完成,角點(diǎn)個數(shù)為:"<<features.size();
        }else{
            qDebug()<<"正在跟蹤...";
        }
        //【6】初始化的時(shí)候如果檢測到前一幀為空,這個把當(dāng)前幀的灰度圖像給前一幀
        if(pre_gray.empty()){//如果前一幀為空就給前一幀賦值一次
            gray.copyTo(pre_gray);
        }
 
        //執(zhí)行光流跟蹤
        qDebug()<<"開始執(zhí)行光流跟蹤";
        //【7】執(zhí)行光流跟蹤,并將輸出的特征向量放入fpts[1]中
        calcOpticalFlowPyrLK(pre_gray,gray,fpts[0],fpts[1],status,err);
        qDebug()<<"光流跟蹤執(zhí)行結(jié)束";
        //【8】遍歷光流跟蹤的輸出特征向量,并得到距離和狀態(tài)都符合預(yù)期的特征向量。讓后將其重新填充到fpts[1]中備用
        int k =0;
        for(size_t i=0;i<fpts[1].size();i++){//循環(huán)遍歷二維輸出向量
            double dist = abs(fpts[0][i].x - fpts[1][i].x) + abs(fpts[0][i].y - fpts[1][i].y);//特征向量移動距離
            if(dist>2&&status[i]){//如果距離大于2,status=true(正常)
                inPoints[k] = inPoints[i];
                fpts[1][k++] = fpts[1][i];
            }
        }
        //【9】重置集合大小(由于有錯誤/不符合條件的輸出特征向量),只拿狀態(tài)正確的
        //重新設(shè)置集合大小
        inPoints.resize(k);
        fpts[1].resize(k);
        //【10】繪制光流線,這一步要不要都行
        //繪制光流線
        if(isShowLine){
            for(size_t i = 0;i<fpts[1].size();i++){
                line(frame,inPoints[i],fpts[1][i],Scalar(0,255,0),1,8,0);
                circle(frame, fpts[1][i], 2, Scalar(0, 0, 255), 2, 8, 0);
            }
        }
 
        qDebug()<<"特征向量的輸入輸出交換數(shù)據(jù)";
        //【11】交換特征向量的輸入和輸出,(循環(huán)往復(fù)/進(jìn)入下一個循環(huán)),此時(shí)特征向量的值會遞減
        std::swap(fpts[1],fpts[0]);//交換特征向量的輸入和輸出,此處焦點(diǎn)的總數(shù)量會遞減
 
        //【12】將用于跟蹤的角點(diǎn)繪制出來
        //將角點(diǎn)繪制出來
        for(size_t i = 0;i<fpts[0].size();i++){
            circle(frame,fpts[0][i],2,Scalar(0,0,255),2,8,0);
        }
 
        //【13】重置前一幀圖像(每一個循環(huán)都要刷新)
        gray.copyTo(pre_gray);
        frame.copyTo(pre_frame);
        //【14】展示最終的效果
        imshow("frame",frame);
        int keyValue = waitKey(100);
        if(keyValue==27){//如果用戶按ese鍵退出播放
            break;
        }
    }
}

以上就是關(guān)于“OpenCV怎么使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI