首頁 > 軟體

基於OpenCV4.2實現單目標跟蹤

2022-03-08 13:03:35

在本教學中,我們將學習使用OpenCV跟蹤物件。OpenCV 3.0開始引入跟蹤API。我們將學習如何和何時使用OpenCV 4.2中可用的8種不同的跟蹤器- BOOSTING, MIL, KCF, TLD, MEDIANFLOW, GOTURN, MOSSE和CSRT。我們還將學習現代跟蹤演演算法背後的一般理論。

1.什麼是目標跟蹤

簡單地說,在視訊的連續幀中定位一個物件稱為跟蹤。

這個定義聽起來很簡單,但在計算機視覺和機器學習中,跟蹤是一個非常廣泛的術語,它包含了概念相似但技術不同的想法。例如,以下所有不同但相關的思想通常都是在物件跟蹤中研究的

1.Dense Optical flow(稠密光流):這些演演算法有助於估計視訊幀中每個畫素的運動向量。

2.Sparse optical flow(稀疏光流):這些演演算法,如Kanade-Lucas-Tomashi (KLT)特徵跟蹤器,跟蹤影象中幾個特徵點的位置。

3.Kalman Filtering(卡爾曼濾波):一種非常流行的訊號處理演演算法,用於基於先驗運動資訊預測運動目標的位置。該演演算法的早期應用之一是導彈制導!

4.Meanshift and Camshift:這些是定位密度函數最大值的演演算法。它們也被用於跟蹤。

5.Single object trackers(單一物件追蹤器):在這類跟蹤器中,第一幀使用一個矩形來標記我們想要跟蹤的物件的位置。然後使用跟蹤演演算法在隨後的幀中跟蹤目標。在大多數現實生活中的應用程式中,這些跟蹤器是與物件檢測器結合使用的。

6.Multiple object track finding algorithms(多目標追蹤演演算法):在我們有快速目標檢測器的情況下,在每幀中檢測多個目標,然後執行軌跡查詢演演算法來識別一幀中的哪個矩形對應於下一幀中的矩形是有意義的。

2.跟蹤與檢測

如果你曾經玩過OpenCV人臉檢測,你知道它是實時工作的,你可以很容易地在每一幀中檢測人臉。那麼,為什麼一開始就需要跟蹤呢?讓我們來探討一下你可能想要在視訊中跟蹤物件而不僅僅是重複檢測的不同原因。

1.跟蹤比檢測快:通常跟蹤演演算法要比檢測演演算法快。原因很簡單。當您在跟蹤前一幀中檢測到的物件時,您會對該物件的外觀有很多瞭解。你也知道在前一個座標系中的位置以及它運動的方向和速度。所以在下一幀中,你可以利用所有這些資訊來預測下一幀中物體的位置,並對物體的預期位置做一個小搜尋來精確地定位物體。一個好的跟蹤演演算法會使用它所擁有的關於目標的所有資訊,而檢測演演算法總是從頭開始。因此,在設計一個高效的系統時,通常在每n幀上進行目標檢測,在n-1幀之間使用跟蹤演演算法。為什麼我們不直接在第一幀檢測目標,然後跟蹤它呢?跟蹤確實可以從它所擁有的額外資訊中獲益,但如果一個物體在障礙物後面停留了很長一段時間,或者它移動得太快,以至於跟蹤演演算法無法跟上,你也會失去對它的跟蹤。跟蹤演演算法也經常會累積誤差,跟蹤物件的包圍框會慢慢地偏離跟蹤物件。我們會經常使用檢測演演算法解決跟蹤演演算法的這些問題。檢測演演算法基於巨量資料訓練,因此,他們對物件的一般類別有更多的瞭解。另一方面,跟蹤演演算法更瞭解它們所跟蹤的類的具體範例。

2.當檢測失敗時,跟蹤可以提供幫助:如果你在視訊中執行人臉檢測器,而這個人的臉被物體遮擋,人臉檢測器很可能會失敗。一個好的跟蹤演演算法將解決某種程度的遮擋。

3.跟蹤保護身份ID:物件檢測的輸出是一個包含物件的矩形陣列。但是,該物件沒有附加身份。例如,在下面的視訊中,一個檢測紅點的探測器將輸出與它在一幀中檢測到的所有點相對應的矩形。在下一幀中,它將輸出另一個矩形陣列。在第一幀中,一個特定的點可能由陣列中位置10的矩形表示,而在第二幀中,它可能位於位置17。當在幀上使用檢測時,我們不知道哪個矩形對應哪個物件。另一方面,追蹤提供了一種將這些點連線起來的方法!

3.使用OpenCV 4實現物件跟蹤

OpenCV 4附帶了一個跟蹤API,它包含了許多單物件跟蹤演演算法的實現。在OpenCV 4.2中有8種不同的跟蹤器可用- BOOSTING, MIL, KCF, TLD, MEDIANFLOW, GOTURN, MOSSE,和CSRT。

注意: OpenCV 3.2實現了這6個跟蹤器- BOOSTING, MIL, TLD, MEDIANFLOW, MOSSE和GOTURN。OpenCV 3.1實現了這5個跟蹤器- BOOSTING, MIL, KCF, TLD, MEDIANFLOW。OpenCV 3.0實現了以下4個跟蹤器- BOOSTING, MIL, TLD, MEDIANFLOW。

在OpenCV 3.3中,跟蹤API已經改變。程式碼檢查版本,然後使用相應的API。

在簡要描述這些演演算法之前,讓我們先看看它們的設定和使用方法。在下面的註釋程式碼中,我們首先通過選擇跟蹤器型別來設定跟蹤器——BOOSTING、MIL、KCF、TLD、MEDIANFLOW、GOTURN、MOSSE或CSRT。然後我們開啟一段視訊,抓取一幀。我們定義了一個包含第一幀物件的邊界框,並用第一幀和邊界框初始化跟蹤器。最後,我們從視訊中讀取幀,並在迴圈中更新跟蹤器,以獲得當前幀的新包圍框。隨後顯示結果。

3.1使用OpenCV 4實現物件跟蹤 C++程式碼

#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/core/ocl.hpp>

using namespace cv;
using namespace std;

// 轉換為字串
#define SSTR( x ) static_cast< std::ostringstream & >( ( std::ostringstream() << std::dec << x ) ).str()

int main(int argc, char **argv)
{
    // OpenCV 3.4.1中的跟蹤器型別列表
    string trackerTypes[8] = {"BOOSTING", "MIL", "KCF", "TLD","MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"};
    // vector <string> trackerTypes(types, std::end(types));

    // 建立一個跟蹤器
    string trackerType = trackerTypes[2];

    Ptr<Tracker> tracker;

    #if (CV_MINOR_VERSION < 3)
    {
        tracker = Tracker::create(trackerType);
    }
    #else
    {
        if (trackerType == "BOOSTING")
            tracker = TrackerBoosting::create();
        if (trackerType == "MIL")
            tracker = TrackerMIL::create();
        if (trackerType == "KCF")
            tracker = TrackerKCF::create();
        if (trackerType == "TLD")
            tracker = TrackerTLD::create();
        if (trackerType == "MEDIANFLOW")
            tracker = TrackerMedianFlow::create();
        if (trackerType == "GOTURN")
            tracker = TrackerGOTURN::create();
        if (trackerType == "MOSSE")
            tracker = TrackerMOSSE::create();
        if (trackerType == "CSRT")
            tracker = TrackerCSRT::create();
    }
    #endif
    // 讀取視訊
    VideoCapture video("videos/chaplin.mp4");
    
    // 如果視訊沒有開啟,退出
    if(!video.isOpened())
    {
        cout << "Could not read video file" << endl; 
        return 1; 
    } 

    // 讀第一幀
    Mat frame; 
    bool ok = video.read(frame); 

    // 定義初始邊界框
    Rect2d bbox(287, 23, 86, 320); 

    // 取消註釋下面的行以選擇一個不同的邊界框 
    // bbox = selectROI(frame, false); 
    // 顯示邊界框
    rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 ); 

    imshow("Tracking", frame); 
    tracker->init(frame, bbox);
    
    while(video.read(frame))
    {     
        // 啟動定時器
        double timer = (double)getTickCount();
        
        // 更新跟蹤結果
        bool ok = tracker->update(frame, bbox);
        
        // 計算每秒幀數(FPS)
        float fps = getTickFrequency() / ((double)getTickCount() - timer);
        
        if (ok)
        {
            // 跟蹤成功:繪製被跟蹤物件
            rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 );
        }
        else
        {
            // 跟蹤失敗
            putText(frame, "Tracking failure detected", Point(100,80), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255),2);
        }
        
        // 在幀上顯示跟蹤器型別
        putText(frame, trackerType + " Tracker", Point(100,20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(50,170,50),2);
        
        // 幀顯示FPS
        putText(frame, "FPS : " + SSTR(int(fps)), Point(100,50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(50,170,50), 2);

        // 顯示幀
        imshow("Tracking", frame);
        
        // 按ESC鍵退出。
        int k = waitKey(1);
        if(k == 27)
        {
            break;
        }

    }
}

3.2使用OpenCV 4實現物件跟蹤 Python程式碼

import cv2
import sys

(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')

if __name__ == '__main__' :

    # 建立追蹤器
    # 除了MIL之外,您還可以使用

    tracker_types = ['BOOSTING', 'MIL','KCF', 'TLD', 'MEDIANFLOW', 'GOTURN', 'MOSSE', 'CSRT']
    tracker_type = tracker_types[2]

    if int(minor_ver) < 3:
        tracker = cv2.Tracker_create(tracker_type)
    else:
        if tracker_type == 'BOOSTING':
            tracker = cv2.TrackerBoosting_create()
        if tracker_type == 'MIL':
            tracker = cv2.TrackerMIL_create()
        if tracker_type == 'KCF':
            tracker = cv2.TrackerKCF_create()
        if tracker_type == 'TLD':
            tracker = cv2.TrackerTLD_create()
        if tracker_type == 'MEDIANFLOW':
            tracker = cv2.TrackerMedianFlow_create()
        if tracker_type == 'GOTURN':
            tracker = cv2.TrackerGOTURN_create()
        if tracker_type == 'MOSSE':
            tracker = cv2.TrackerMOSSE_create()
        if tracker_type == "CSRT":
            tracker = cv2.TrackerCSRT_create()

    # 讀取視訊
    video = cv2.VideoCapture("videos/chaplin.mp4")

    # 如果視訊沒有開啟,退出。
    if not video.isOpened():
        print "Could not open video"
        sys.exit()

    # 讀第一幀。
    ok, frame = video.read()
    if not ok:
        print('Cannot read video file')
        sys.exit()
    
    # 定義一個初始邊界框
    bbox = (287, 23, 86, 320)

    # 取消註釋下面的行以選擇一個不同的邊界框
    # bbox = cv2.selectROI(frame, False)

    # 用第一幀和包圍框初始化跟蹤器
    ok = tracker.init(frame, bbox)

    while True:
        # 讀取一個新的幀
        ok, frame = video.read()
        if not ok:
            break
        
        # 啟動計時器
        timer = cv2.getTickCount()

        # 更新跟蹤器
        ok, bbox = tracker.update(frame)

        # 計算影格率(FPS)
        fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer);

        # 繪製包圍框
        if ok:
            # 跟蹤成功
            p1 = (int(bbox[0]), int(bbox[1]))
            p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
            cv2.rectangle(frame, p1, p2, (255,0,0), 2, 1)
        else :
            # 跟蹤失敗
            cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)

        # 在幀上顯示跟蹤器型別名字
        cv2.putText(frame, tracker_type + " Tracker", (100,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50),2);
    
        # 在幀上顯示影格率FPS
        cv2.putText(frame, "FPS : " + str(int(fps)), (100,50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50), 2);

        # 顯示結果
        cv2.imshow("Tracking", frame)

        # 按ESC鍵退出
        k = cv2.waitKey(1) & 0xff
        if k == 27 : break

4.跟蹤演演算法解析

在本節中,我們將深入研究不同的跟蹤演演算法。我們的目標不是對每一個跟蹤器都有一個深刻的理論理解,而是從實際的角度來理解它們。

讓我首先解釋一些跟蹤的一般原則。在跟蹤中,我們的目標是在當前幀中找到一個物件,因為我們已經成功地在所有(或幾乎所有)之前的幀中跟蹤了這個物件。

因為我們一直跟蹤物件直到當前幀,所以我們知道它是如何移動的。換句話說,我們知道運動模型的引數。運動模型只是一種花哨的說法,表示你知道物體在前幾幀中的位置和速度(速度+運動方向)。如果你對物體一無所知,你可以根據當前的運動模型預測新的位置,你會非常接近物體的新位置。

但我們有比物體運動更多的資訊。我們知道物體在之前的每一幀中的樣子。換句話說,我們可以構建一個對物件的外觀進行編碼的外觀模型。該外觀模型可用於在運動模型預測的小鄰域內搜尋位置,從而更準確地預測物體的位置。

運動模型預測了物體的大致位置。外觀模型對這個估計進行微調,以提供基於外觀的更準確的估計。

如果物件非常簡單,並且沒有太多改變它的外觀,我們可以使用一個簡單的模板作為外觀模型,並尋找該模板。然而,現實生活並沒有那麼簡單。物體的外觀會發生巨大的變化。為了解決這個問題,在許多現代跟蹤器中,這個外觀模型是一個以線上方式訓練的分類器。別慌!讓我用更簡單的術語解釋一下。

分類器的工作是將影象中的矩形區域分類為物體或背景。分類器接收影象patch作為輸入,並返回0到1之間的分數,表示影象patch包含該物件的概率。當完全確定影象patch是背景時,分數為0;當完全確定patch是物件時,分數為1。

在機器學習中,我們用“線上”這個詞來指在執行時進行動態訓練的演演算法。離線分類器可能需要數千個範例來訓練一個分類器,但線上分類器通常在執行時使用很少的範例進行訓練。

通過向分類器輸入正(物件)和負(背景)的例子來訓練分類器。如果您想要構建一個用於檢測貓的分類器,您可以使用數千張包含貓的影象和數千張不包含貓的影象來訓練它。這樣分類器學會區分什麼是貓,什麼不是。在構建一個線上分類器時,我們沒有機會擁有數千個正面和負面類的例子。

讓我們看看不同的跟蹤演演算法是如何處理線上訓練的這個問題的。

4.1 BOOSTING Tracker

該跟蹤器基於AdaBoost的線上版本——基於HAAR級聯的人臉檢測器內部使用的演演算法。這個分類器需要在執行時用物件的正面和反面例子進行訓練。將使用者提供的初始包圍盒(或其他目標檢測演演算法提供的初始包圍盒)作為目標的正例,將包圍盒外的許多影象patch作為背景。

給定一個新的幀,分類器在前一個位置附近的每個畫素上執行,並記錄分類器的得分。物件的新位置是分數最高的位置。現在分類器又多了一個正樣本。當更多的幀進來時,分類器就會用這些額外的資料更新。

優點:沒有。 這個演演算法已經有10年的歷史了,而且執行良好,但我找不到使用它的好理由,特別是當基於類似原則的其他高階跟蹤器(MIL, KCF)可用時。
缺點:跟蹤效能平庸。 它不能可靠地知道何時跟蹤失敗了。

4.2 MIL Tracker

這個跟蹤器在思想上與上述的BOOSTING跟蹤器相似。最大的區別是,它不是隻考慮物件的當前位置作為一個正樣本,而是在當前位置周圍的一個小領域中尋找幾個潛在的正樣本。你可能會認為這不是一個好主意,因為在大多數這些“正樣本”的例子中,物體不是居中的。

這就是多範例學習 (MIL) 的用武之地。在 MIL 中,您不指定正面和負面範例,而是指定正面和負面“袋子”。正面“袋子”中的影象集合並不都是正例。取而代之的是,正面袋子中只有一張影象需要是正面的例子!

在我們的範例中,一個正面袋子包含以物件當前位置為中心的patch,以及它周圍的一個小鄰域中的patch。即使被跟蹤物件的當前位置不準確,當來自當前位置附近的樣本被放入正面袋子中時,這個正面袋子很有可能包含至少一個物件很好地居中的影象。

優點:效能很好。 它不像BOOSTING跟蹤器那樣漂移,並且在部分遮擋下做了合理的工作。如果你正在使用OpenCV 3.0,這可能是你可用的最好的跟蹤器。但是,如果您使用的是更高的版本,請考慮KCF。

缺點: 無法可靠地報告跟蹤失敗。不能從完全遮擋中恢復。

4.3 KCF Tracker

KFC 代表Kernelized Correlation Filters(Kernelized相關性過濾器)。該跟蹤器建立在前兩個跟蹤器中提出的想法之上。該跟蹤器利用了 MIL 跟蹤器中使用的多個正樣本具有較大重疊區域的事實。這種重疊資料導致了一些很好的數學特性,該跟蹤器利用這些特性使跟蹤更快、更準確。

優點:準確性和速度都優於 MIL,它報告的跟蹤失敗比 BOOSTING 和 MIL 更好。 如果您使用的是 OpenCV 3.1 及更高版本,我建議將其用於大多數應用程式。
缺點: 不能從完全遮擋中恢復。

4.4 TLD Tracker

TLD 代表跟蹤、學習和檢測。顧名思義,這個跟蹤器將長期跟蹤任務分解為三個部分——(短期)跟蹤、學習和檢測。從作者的論文中,“跟蹤器逐幀跟蹤物件。檢測器定位到目前為止已觀察到的所有外觀,並在必要時糾正跟蹤器。

學習估計檢測器的錯誤並對其進行更新以避免將來出現這些錯誤。”這個跟蹤器的輸出往往會有點跳躍。例如,如果您正在跟蹤行人並且場景中有其他行人,則此跟蹤器有時可以臨時跟蹤與您打算跟蹤的行人不同的行人。從積極的方面來說,這條軌跡似乎可以在更大的範圍、運動和遮擋範圍內跟蹤物件。如果您有一個物件隱藏在另一個物件後面的視訊序列,則此跟蹤器可能是一個不錯的選擇。

優點:在多個幀的遮擋下效果最佳。此外,跟蹤最好的規模變化。

缺點:大量的誤報使得它幾乎無法使用。

4.5 MEDIANFLOW Tracker

在內部,該跟蹤器在時間上向前和向後跟蹤物件,並測量這兩個軌跡之間的差異。最小化這種 ForwardBackward 誤差使他們能夠可靠地檢測跟蹤失敗並在視訊序列中選擇可靠的軌跡。

在我的測試中,我發現該跟蹤器在運動可預測且較小時效果最佳。與其他跟蹤器即使在跟蹤明顯失敗時仍繼續執行不同,該跟蹤器知道跟蹤何時失敗。

優點:出色的跟蹤失敗報告。當運動是可預測的並且沒有遮擋時效果很好。

缺點:在大運動下失敗。

4.6 GOTURN tracker

在跟蹤器類的所有跟蹤演演算法中,這是唯一一種基於折積神經網路 (CNN) 的演演算法。從 OpenCV 檔案中,我們知道它“對視點變化、光照變化和變形具有魯棒性”。但它不能很好地處理遮擋。

注意:GOTURN 是基於 CNN 的跟蹤器,使用 Caffe 模型進行跟蹤。 Caffe 模型和 proto 文字檔案必須存在於程式碼所在的目錄中。這些檔案也可以從 opencv_extra 儲存庫下載、連線並在使用前提取。

4.7 MOSSE tracker

最小輸出平方誤差和 (MOSSE) 使用自適應相關性進行物件跟蹤,在使用單幀初始化時會產生穩定的相關性濾波器。 MOSSE 跟蹤器對光照、比例、姿勢和非剛性變形的變化具有魯棒性。它還根據峰值旁瓣(peak-to-sidelobe)比檢測遮擋,這使跟蹤器能夠在物件重新出現時暫停並從中斷的地方恢復。 MOSSE 跟蹤器還以更高的 fps(450 fps 甚至更高)執行。除此之外,它還非常容易執行,與其他複雜追蹤器一樣準確,而且速度更快。但是,在效能尺度上,它落後於基於深度學習的跟蹤器。

4.8 CSRT tracker

在DCF-CSR (Discriminative Correlation Filter with Channel and Spatial Reliability, DCF-CSR)中,我們使用空間可靠性對映來調整濾波器的支援度,使其適應幀中被選擇區域的跟蹤部分。這確保了所選區域的放大和定位,並改進了對非矩形區域或物件的跟蹤。它只使用2個標準特性(hog和Colornames)。它也執行在一個相對較低的fps (25 fps),但提供了較高的目標跟蹤精度。

到此這篇關於基於OpenCV4.2實現單目標跟蹤的文章就介紹到這了,更多相關OpenCV單目標跟蹤內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com