Object Tracking using OpenCV (C++/Python)(使用OpenCV進行目標跟蹤)

Object Tracking using OpenCV (C++/Python)(使用OpenCV進行目標跟蹤)

本博客翻譯搬運自https://www.learnopencv.com/object-tracking-using-opencv-cpp-python,用於初入目標跟蹤的新手學習,轉貼請註明!

使用OpenCV進行目標跟蹤(C++/Python)

在本教程裏,我們將學習OpenCV3.0中引入的OpenCV跟蹤API。我們將學習如何以及何時使用OpenCV3.4.1中提供的7中不同的跟蹤器——BOOSTING,MIL,KCF,TLD,MEDIANFLOW,GOTURN和MOSSE。我們還將學習現代跟蹤算法背後的一般理論。

我的朋友Boris Babenko完美的解決了這個問題,正如下面的這個完美的實時人臉跟蹤器所示!開玩笑的說,下面的這個GIF描述了我們想要的理想物體跟蹤器——速度,準確性和麪對遮擋的魯棒性。

OpenCV目標跟蹤樣例:

如果你沒有時間閱讀整個教程,那就看看下面這個視頻並瞭解一下用法。但是如果你想系統學習一下目標跟蹤,那就完整讀下去。

視頻地址(需FQ):https://www.youtube.com/watch?reload=9&v=61QjSz-oLr8&feature=youtu.be

什麼是目標跟蹤?

簡單的說,在一個視頻的連續幀中定位目標就稱之爲跟蹤。

這個定義聽起來很直接但是在計算機視覺和機器學習領域,跟蹤是一個非常廣泛的術語,涵蓋概念上相似但技術上不同的想法。舉個例子,下面所有不同的但卻相關的方法均是可用在目標跟蹤的研究領域的。

1.密集光流法:這類算法有助於估計視頻幀中每個像素的運動矢量。

2.稀疏光流法:這類算法,如Kanade-Lucas-Tomashi(KLT)特徵跟蹤器,跟蹤圖像中幾個特徵點的位置。

3.卡爾曼濾波:這是一個非常流行的信號處理算法,用於根據先前的運動信息預測運動物體的位置。這種算法的早期應用之一是導彈制導!

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

5.單目標跟蹤器:在此類跟蹤器中,第一幀使用矩形標記來指示我們要跟蹤的對象的位置。然後使用跟蹤算法在後續幀中跟蹤對象。在大多數實際應用中,這些跟蹤器與物體檢測器結合使用。

6.多目標跟蹤器:在我們有快速物體探測器的情況下,檢測每個幀中的多個物體然後運行軌跡查找算法來識別一幀中的哪個矩形對應於下一幀中的矩形是有意義的。

跟蹤VS檢測

如果你曾經使用OpenCV做過面部檢測,你就明白它可以實時工作,你可以輕鬆地檢測每一幀中的臉部。那麼,你爲什麼需要首先進行跟蹤?讓我們探討一下你可能想要跟蹤視頻中對象的不同原因,而不僅僅是重複檢測。

1.跟蹤比檢測快:通常跟蹤算法比檢測算法快。理由很簡單。當你跟蹤在前一幀中檢測到的對象時,你對該目標的外觀瞭解很多。你還知道前一幀的位置以及運動的方向和速度。因此在下一幀中,您可以使用所有這些信息來預測下一幀中目標的位置,並圍繞目標的預期位置進行小搜索,以準確定位目標。 一個好的跟蹤算法將使用它所擁有的關於該目標的所有信息,而檢測算法總是從頭開始。因此,在設計有效系統時,通常在每第n幀上運行目標檢測,而在其間的n-1幀中採用跟蹤算法。爲什麼我們不直接檢測第一幀中的目標並隨後跟蹤?確實,跟蹤可以從它擁有的額外信息中獲益,但是當它們長時間在障礙物後面或者如果它們移動速度太快以至於跟蹤算法無法趕上時,你也可能失去對目標的跟蹤。跟蹤算法累積錯誤也很常見,跟蹤目標的邊界框會慢慢偏離其正在跟蹤的目標。爲了通過跟蹤算法解決這些問題,每隔一段時間運行一次檢測算法。檢測算法在目標的大量示例上進行訓練。因此,他們對目標的一般類有更多的瞭解。另一方面,跟蹤算法更多地瞭解他們正在跟蹤的類的特定實例。

2.檢測失敗時跟蹤可以提供幫助:如果你在視頻上運行人臉檢測器並且人臉被物體遮擋,那麼人臉檢測大概率會失敗。另一方面,優秀的跟蹤算法可以解決某種程度的遮擋。在下面的視頻中,你可以看到MIL跟蹤器的作者Boris Bakenko博士演示MIL跟蹤器如何在遮擋下工作

視頻地址(需FQ):https://www.youtube.com/watch?v=n4QA3shA8Yw

3.跟蹤保留身份:目標檢測的輸出是包含目標的矩形數組。但是,目標沒有附加標識。例如,在下面的視頻中,檢測紅點的檢測器將輸出對應於它在幀中檢測到的所有點的矩形。在下一幀中,它將輸出另一個矩形數組。在第一幀中,特定點可以由陣列中位置10處的舉行表示,並且在第二幀中,它可以在位置17處。當在幀上使用檢測時,我們不知道哪個矩形對應於哪個目標。另一方面,跟蹤提供了一種字面連接點的方法。

視頻地址(需FQ):https://www.youtube.com/watch?v=SsiHH_wrwDg

OpenCV 跟蹤API

OpenCV3附帶了新的跟蹤API,其中包括許多單目標跟蹤算法的實現。OpenCV 3.4.1中有7種不同的跟蹤器——BOOSTING,MIL,KCF,TLD,MEDIANFLOW,GOTURN和MOSSE。

注意:OpenCV 3.2包括6種跟蹤器——BOOSTING,MIL,TLD,MEDIANFLOW和MOSSE。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。然後我們打開一個視頻並讀取一幀。我們定義一個包含第一幀目標的邊界框,並用第一幀和邊界框初始化跟蹤器。最後,我們從視頻中讀取幀並僅在循環中更新跟蹤器以獲得當前幀的新邊界框。隨後顯示結果。

C++

複製代碼
#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/core/ocl.hpp>

using namespace cv;
using namespace std;

// Convert to string
#define SSTR( x ) static_cast< std::ostringstream & >( <span style=“color: #000000”>
( std::ostringstream() << std::dec << x ) ).str()

int main(int argc, char **argv)
{
// List of tracker types in OpenCV 3.4.1
string trackerTypes[7] = {BOOSTING, MIL, KCF, TLD,MEDIANFLOW, GOTURN, MOSSE};
// vector <string> trackerTypes(types, std::end(types));

</span><span style="color: #008000">//</span><span style="color: #008000"> Create a tracker</span>
<span style="color: #0000ff">string</span> trackerType = trackerTypes[<span style="color: #800080">2</span><span style="color: #000000">];

Ptr</span>&lt;Tracker&gt;<span style="color: #000000"> tracker;

</span><span style="color: #0000ff">#if</span> (CV_MINOR_VERSION &lt; 3)<span style="color: #000000">
{
    tracker </span>=<span style="color: #000000"> Tracker::create(trackerType);
}
</span><span style="color: #0000ff">#else</span><span style="color: #000000">
{
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">BOOSTING</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerBoosting::create();
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">MIL</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerMIL::create();
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">KCF</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerKCF::create();
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">TLD</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerTLD::create();
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">MEDIANFLOW</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerMedianFlow::create();
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">GOTURN</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerGOTURN::create();
    </span><span style="color: #0000ff">if</span> (trackerType == <span style="color: #800000">"</span><span style="color: #800000">MOSSE</span><span style="color: #800000">"</span><span style="color: #000000">)
        tracker </span>=<span style="color: #000000"> TrackerMOSSE::create();
}
</span><span style="color: #0000ff">#endif</span>
<span style="color: #008000">//</span><span style="color: #008000"> Read video</span>
VideoCapture video(<span style="color: #800000">"</span><span style="color: #800000">videos/chaplin.mp4</span><span style="color: #800000">"</span><span style="color: #000000">);
 
</span><span style="color: #008000">//</span><span style="color: #008000"> Exit if video is not opened</span>
<span style="color: #0000ff">if</span>(!<span style="color: #000000">video.isOpened())
{
    cout </span>&lt;&lt; <span style="color: #800000">"</span><span style="color: #800000">Could not read video file</span><span style="color: #800000">"</span> &lt;&lt; endl; <span style="color: #0000ff">return</span> <span style="color: #800080">1</span>; } <span style="color: #008000">//</span><span style="color: #008000"> Read first frame Mat frame; bool ok = video.read(frame); </span><span style="color: #008000">//</span><span style="color: #008000"> Define initial boundibg box Rect2d bbox(287, 23, 86, 320); </span><span style="color: #008000">//</span><span style="color: #008000"> Uncomment the line below to select a different bounding box bbox = selectROI(frame, false); </span><span style="color: #008000">//</span><span style="color: #008000"> Display bounding box. rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 ); imshow("Tracking", frame); tracker-&gt;init(frame, bbox);</span>
 
<span style="color: #0000ff">while</span><span style="color: #000000">(video.read(frame))
{     
    </span><span style="color: #008000">//</span><span style="color: #008000"> Start timer</span>
    <span style="color: #0000ff">double</span> timer = (<span style="color: #0000ff">double</span><span style="color: #000000">)getTickCount();
     
    </span><span style="color: #008000">//</span><span style="color: #008000"> Update the tracking result</span>
    <span style="color: #0000ff">bool</span> ok = tracker-&gt;<span style="color: #000000">update(frame, bbox);
     
    </span><span style="color: #008000">//</span><span style="color: #008000"> Calculate Frames per second (FPS)</span>
    <span style="color: #0000ff">float</span> fps = getTickFrequency() / ((<span style="color: #0000ff">double</span>)getTickCount() -<span style="color: #000000"> timer);
     
    </span><span style="color: #0000ff">if</span><span style="color: #000000"> (ok)
    {
        </span><span style="color: #008000">//</span><span style="color: #008000"> Tracking success : Draw the tracked object</span>
        rectangle(frame, bbox, Scalar( <span style="color: #800080">255</span>, <span style="color: #800080">0</span>, <span style="color: #800080">0</span> ), <span style="color: #800080">2</span>, <span style="color: #800080">1</span><span style="color: #000000"> );
    }
    </span><span style="color: #0000ff">else</span><span style="color: #000000">
    {
        </span><span style="color: #008000">//</span><span style="color: #008000"> Tracking failure detected.</span>
        putText(frame, <span style="color: #800000">"</span><span style="color: #800000">Tracking failure detected</span><span style="color: #800000">"</span>, Point(<span style="color: #800080">100</span>,<span style="color: #800080">80</span>), FONT_HERSHEY_SIMPLEX, <span style="color: #800080">0.75</span>, Scalar(<span style="color: #800080">0</span>,<span style="color: #800080">0</span>,<span style="color: #800080">255</span>),<span style="color: #800080">2</span><span style="color: #000000">);
    }
     
    </span><span style="color: #008000">//</span><span style="color: #008000"> Display tracker type on frame</span>
    putText(frame, trackerType + <span style="color: #800000">"</span><span style="color: #800000"> Tracker</span><span style="color: #800000">"</span>, Point(<span style="color: #800080">100</span>,<span style="color: #800080">20</span>), FONT_HERSHEY_SIMPLEX, <span style="color: #800080">0.75</span>, Scalar(<span style="color: #800080">50</span>,<span style="color: #800080">170</span>,<span style="color: #800080">50</span>),<span style="color: #800080">2</span><span style="color: #000000">);
     
    </span><span style="color: #008000">//</span><span style="color: #008000"> Display FPS on frame</span>
    putText(frame, <span style="color: #800000">"</span><span style="color: #800000">FPS : </span><span style="color: #800000">"</span> + SSTR(<span style="color: #0000ff">int</span>(fps)), Point(<span style="color: #800080">100</span>,<span style="color: #800080">50</span>), FONT_HERSHEY_SIMPLEX, <span style="color: #800080">0.75</span>, Scalar(<span style="color: #800080">50</span>,<span style="color: #800080">170</span>,<span style="color: #800080">50</span>), <span style="color: #800080">2</span><span style="color: #000000">);

    </span><span style="color: #008000">//</span><span style="color: #008000"> Display frame.</span>
    imshow(<span style="color: #800000">"</span><span style="color: #800000">Tracking</span><span style="color: #800000">"</span><span style="color: #000000">, frame);
     
    </span><span style="color: #008000">//</span><span style="color: #008000"> Exit if ESC pressed.</span>
    <span style="color: #0000ff">int</span> k = waitKey(<span style="color: #800080">1</span><span style="color: #000000">);
    </span><span style="color: #0000ff">if</span>(k == <span style="color: #800080">27</span><span style="color: #000000">)
    {
        </span><span style="color: #0000ff">break</span><span style="color: #000000">;
    }

}

}

複製代碼

Python

複製代碼
import cv2
import sys

(major_ver, minor_ver, subminor_ver) = (cv2.version).split(.)

if name == main :

# Set up tracker.
# Instead of MIL, you can also use

tracker_types </span>= [<span style="color: #800000">'</span><span style="color: #800000">BOOSTING</span><span style="color: #800000">'</span>, <span style="color: #800000">'</span><span style="color: #800000">MIL</span><span style="color: #800000">'</span>,<span style="color: #800000">'</span><span style="color: #800000">KCF</span><span style="color: #800000">'</span>, <span style="color: #800000">'</span><span style="color: #800000">TLD</span><span style="color: #800000">'</span>, <span style="color: #800000">'</span><span style="color: #800000">MEDIANFLOW</span><span style="color: #800000">'</span>, <span style="color: #800000">'</span><span style="color: #800000">GOTURN</span><span style="color: #800000">'</span>, <span style="color: #800000">'</span><span style="color: #800000">MOSSE</span><span style="color: #800000">'</span><span style="color: #000000">]
tracker_type </span>= tracker_types[<span style="color: #800080">2</span><span style="color: #000000">]

</span><span style="color: #0000ff">if</span> <span style="color: #0000ff">int</span>(minor_ver) &lt; <span style="color: #800080">3</span><span style="color: #000000">:
    tracker </span>=<span style="color: #000000"> cv2.Tracker_create(tracker_type)
</span><span style="color: #0000ff">else</span><span style="color: #000000">:
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">BOOSTING</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerBoosting_create()
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">MIL</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerMIL_create()
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">KCF</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerKCF_create()
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">TLD</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerTLD_create()
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">MEDIANFLOW</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerMedianFlow_create()
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">GOTURN</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerGOTURN_create()
    </span><span style="color: #0000ff">if</span> tracker_type == <span style="color: #800000">'</span><span style="color: #800000">MOSSE</span><span style="color: #800000">'</span><span style="color: #000000">:
        tracker </span>=<span style="color: #000000"> cv2.TrackerMOSSE_create()

# Read video
video </span>= cv2.VideoCapture(<span style="color: #800000">"</span><span style="color: #800000">videos/chaplin.mp4</span><span style="color: #800000">"</span><span style="color: #000000">)

# Exit </span><span style="color: #0000ff">if</span><span style="color: #000000"> video not opened.
</span><span style="color: #0000ff">if</span><span style="color: #000000"> not video.isOpened():
    print </span><span style="color: #800000">"</span><span style="color: #800000">Could not open video</span><span style="color: #800000">"</span><span style="color: #000000">
    sys.exit()

# Read first frame.
ok, frame </span>=<span style="color: #000000"> video.read()
</span><span style="color: #0000ff">if</span><span style="color: #000000"> not ok:
    print </span><span style="color: #800000">'</span><span style="color: #800000">Cannot read video file</span><span style="color: #800000">'</span><span style="color: #000000">
    sys.exit()
 
# Define an initial bounding box
bbox </span>= (<span style="color: #800080">287</span>, <span style="color: #800080">23</span>, <span style="color: #800080">86</span>, <span style="color: #800080">320</span><span style="color: #000000">)

# Uncomment the line below to </span><span style="color: #0000ff">select</span><span style="color: #000000"> a different bounding box
bbox </span>=<span style="color: #000000"> cv2.selectROI(frame, False)

# Initialize tracker with first frame and bounding box
ok </span>=<span style="color: #000000"> tracker.init(frame, bbox)

</span><span style="color: #0000ff">while</span><span style="color: #000000"> True:
    # Read a </span><span style="color: #0000ff">new</span><span style="color: #000000"> frame
    ok, frame </span>=<span style="color: #000000"> video.read()
    </span><span style="color: #0000ff">if</span><span style="color: #000000"> not ok:
        </span><span style="color: #0000ff">break</span><span style="color: #000000">
     
    # Start timer
    timer </span>=<span style="color: #000000"> cv2.getTickCount()

    # Update tracker
    ok, bbox </span>=<span style="color: #000000"> tracker.update(frame)

    # Calculate Frames per second (FPS)
    fps </span>= cv2.getTickFrequency() / (cv2.getTickCount() -<span style="color: #000000"> timer);

    # Draw bounding box
    </span><span style="color: #0000ff">if</span><span style="color: #000000"> ok:
        # Tracking success
        p1 </span>= (<span style="color: #0000ff">int</span>(bbox[<span style="color: #800080">0</span>]), <span style="color: #0000ff">int</span>(bbox[<span style="color: #800080">1</span><span style="color: #000000">]))
        p2 </span>= (<span style="color: #0000ff">int</span>(bbox[<span style="color: #800080">0</span>] + bbox[<span style="color: #800080">2</span>]), <span style="color: #0000ff">int</span>(bbox[<span style="color: #800080">1</span>] + bbox[<span style="color: #800080">3</span><span style="color: #000000">]))
        cv2.rectangle(frame, p1, p2, (</span><span style="color: #800080">255</span>,<span style="color: #800080">0</span>,<span style="color: #800080">0</span>), <span style="color: #800080">2</span>, <span style="color: #800080">1</span><span style="color: #000000">)
    </span><span style="color: #0000ff">else</span><span style="color: #000000"> :
        # Tracking failure
        cv2.putText(frame, </span><span style="color: #800000">"</span><span style="color: #800000">Tracking failure detected</span><span style="color: #800000">"</span>, (<span style="color: #800080">100</span>,<span style="color: #800080">80</span>), cv2.FONT_HERSHEY_SIMPLEX, <span style="color: #800080">0.75</span>,(<span style="color: #800080">0</span>,<span style="color: #800080">0</span>,<span style="color: #800080">255</span>),<span style="color: #800080">2</span><span style="color: #000000">)

    # Display tracker type on frame
    cv2.putText(frame, tracker_type </span>+ <span style="color: #800000">"</span><span style="color: #800000"> Tracker</span><span style="color: #800000">"</span>, (<span style="color: #800080">100</span>,<span style="color: #800080">20</span>), cv2.FONT_HERSHEY_SIMPLEX, <span style="color: #800080">0.75</span>, (<span style="color: #800080">50</span>,<span style="color: #800080">170</span>,<span style="color: #800080">50</span>),<span style="color: #800080">2</span><span style="color: #000000">);
 
    # Display FPS on frame
    cv2.putText(frame, </span><span style="color: #800000">"</span><span style="color: #800000">FPS : </span><span style="color: #800000">"</span> + str(<span style="color: #0000ff">int</span>(fps)), (<span style="color: #800080">100</span>,<span style="color: #800080">50</span>), cv2.FONT_HERSHEY_SIMPLEX, <span style="color: #800080">0.75</span>, (<span style="color: #800080">50</span>,<span style="color: #800080">170</span>,<span style="color: #800080">50</span>), <span style="color: #800080">2</span><span style="color: #000000">);

    # Display result
    cv2.imshow(</span><span style="color: #800000">"</span><span style="color: #800000">Tracking</span><span style="color: #800000">"</span><span style="color: #000000">, frame)

    # Exit </span><span style="color: #0000ff">if</span><span style="color: #000000"> ESC pressed
    k </span>= cv2.waitKey(<span style="color: #800080">1</span>) &amp; <span style="color: #800080">0xff</span>
    <span style="color: #0000ff">if</span> k == <span style="color: #800080">27</span> : <span style="color: #0000ff">break</span></pre>
複製代碼

目標跟蹤算法

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

首先讓我解釋一下跟蹤背後的一些一般原則。 在跟蹤中,我們的目標是在當前幀中找到一個對象,因爲我們已經在所有(或幾乎所有)前一幀中成功跟蹤了對象。

由於我們已經跟蹤了當前幀的目標,因此我們知道它是如何移動的。 換句話說,我們知道運動模型的參數。 運動模型只是一種奇特的方式,表示你知道前一幀中物體的位置和速度(速度+運動方向)。如果你對該目標一無所知,則可以根據當前運動模型預測新位置,並且你將非常接近目標的新位置。

我們有更多的信息,但是它們僅僅是關於目標的運動的。我們知道目標在每個先前幀中的外觀。換句話說,我們可以構建一個外觀模型來編碼目標的外觀。該外觀模型可用於在運動模型預測的位置的小領域中搜索,以更準確地預測目標的位置。

運動模型預測目標的大致位置,外觀模型精細調整該估計以基於外觀提供更準確的估計。

如果目標非常簡單並且沒有更改它的外觀,我們可以使用一個簡單的模板作爲外觀模型並查找該模板。然而,顯示並非那麼簡單。目標的外觀可能會發生巨大的變化,爲了解決這個問題,在許多現代跟蹤器中,外觀模型是以在線方式訓練的分類器。別怕,讓我用簡單的術語解釋一下。分類器的工作是將圖像的矩形區域分類爲目標或背景。分類器將圖像作爲輸入,並返回0和1之間的分數,以指示圖像塊包含目標的概率。當絕對確定圖像塊是背景時得分爲0,當絕對確定圖像塊是目標時得分爲1。在機器學習中,我們使用“在線”一詞來指代在運行時即時訓練的算法。 離線分類器可能需要數千個示例來訓練分類器,但是在線分類器通常在運行時使用極少數示例進行訓練。通過將其分爲正(目標)和負(目標)示例來訓練分類器。 如果你想建立一個用於檢測貓的分類器,你可以使用包含貓的數千個圖像和數千個不包含貓的圖像來訓練它。 通過這種方式,分類器學會區分什麼是貓而什麼不是。 你可以在此處詳細瞭解圖像分類。 在構建在線分類器的過程中,我們沒有數千個正面和負面類的例子。

讓我們看看不同的跟蹤算法如何解決在線訓練的這個問題。

BOOSTING跟蹤器

這個跟蹤器基於AdaBoost的在線版本——基於HAAR面部檢測器在內部使用的算法。需要在運行時使用目標的正面和負面示例訓練此分類器。由用戶(或另一個目標檢測算法)提供的初始邊界框被視爲對象的正例,並且邊界框外的許多圖像塊被視爲背景。 給定新幀,分類器在先前位置的鄰域中的每個像素上運行,並且記錄分類器的分數。 目標的新位置是得分最大的位置。 所以現在我們又有了一個分類器的正面例子。 隨着更多幀進入,分類器將使用此附加數據進行更新

優點:沒有。 這個算法已有十年曆史了,雖然工作正常,但我找不到使用它的好理由,特別是當其他基於類似原理的高級跟蹤器(MIL,KCF)可用時。

缺點:跟蹤性能平庸。且它無法可靠地知道跟蹤失敗的時間。

MIL跟蹤器

該跟蹤器在概念上類似於上述的BOOSTING跟蹤器。 最大的區別在於,不是僅考慮目標的當前位置作爲正例,而是在當前位置周圍的小鄰域中查找以產生若干潛在的正例。 你可能認爲這是一個壞主意,因爲在大多數這些“積極”的例子中,目標不是居中的。這正是MIL拯救的地方。在MIL中,你沒有指定正面和負面的例子,而是正面和負面的“包”。 正面“包”中的圖像集合並非都是正面的例子。 相反,只有正面包中的一個圖像需要是一個正面的例子! 在我們的示例中,正面包包含以目標當前位置爲中心的補丁,以及在其周圍的小鄰域中的補丁。 即使被跟蹤目標的當前位置不準確,當來自當前位置附近的樣本被放入正面包中時,該包很可能包含至少一個圖像,其中目標很好地居中。 MIL項目主頁爲喜歡深入瞭解MIL跟蹤器內部工作原理的人提供了更多信息。

MIL項目主頁:http://vision.ucsd.edu/~bbabenko/new/project_miltrack.shtml

優點:表現非常好,它不會像BOOSTING跟蹤器那樣漂移,並且在部分遮擋下可以完成合理的工作,如果你使用的是OpenCV3.0,這可能是你可以使用的最佳跟蹤器。但是如果你是用的是更高版本,請考慮使用KCF。

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

KCF跟蹤器

KCF表示核相關濾波器,該跟蹤器基於前兩個跟蹤器中提出的想法,利用MIL跟蹤器中使用的多個正樣本具有大的重疊區域的事實,這種重疊的數據導致一些很好的數學屬性,這個跟蹤器利用這些屬性可以使跟蹤更快更準確。

優點:精度和速度都優於MIL,它報告跟蹤失敗比BOOSTING和MIL更好。 如果你使用的是OpenCV 3.1及更高版本,我建議你在大多數應用程序中使用它。

缺點:無法從完全遮擋中恢復。 未在OpenCV 3.0中實現。

BUG警告OpenCV 3.1(僅限Python)中存在一個錯誤,因爲返回了錯誤的邊界框。 查看錯誤報告。 感謝Andrei Cheremskoy指出這一點。

錯誤報告:https://github.com/opencv/opencv_contrib/issues/640

TLD跟蹤器

TLD代表tracking,learning和detection。顧名思義,該跟蹤器將長期跟蹤任務分解爲三個部分——(短期)跟蹤,學習和檢測。作者的論文中指出,“跟蹤器在幀與幀之間跟蹤對象。檢測器定位到目前爲止觀察到的所有外觀,並在必要時糾正跟蹤器。 學習估計檢測器的錯誤並更新它以避免將來出現這些錯誤。“這個跟蹤器的輸出往往會跳躍一下。 例如,如果你正在跟蹤行人並且場景中還有其他行人,則此跟蹤器有時可以臨時跟蹤與你要跟蹤的行人不同的行人。 從積極的方面來看,這個跟蹤器似乎可以在更大的大小,運動和遮擋上跟蹤物體。 如果你有一個視頻序列,其中目標隱藏在另一個目標後面,則此跟蹤器可能是一個不錯的選擇。

優點:在多個幀的遮擋下工作效果最佳。 此外,在目標大小變化時跟蹤效果最佳。

缺點:很多誤報使它幾乎無法使用。

MEDIANFLOW跟蹤器

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

在我的測試中,我發現當動作可預測且很小時,這個跟蹤器效果最好。 與其他即使在跟蹤明顯失敗時繼續運行的跟蹤器不同,此跟蹤器也知道跟蹤失敗的時間。

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

缺點:目標動作大時跟蹤失敗。

GOTURN跟蹤器

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

注意:GOTURN是基於CNN的跟蹤器,使用caffe模型進行跟蹤。 Caffe模型和原始文本文件必須存在於代碼所在的目錄中。 這些文件也可以從opencv_extra存儲庫下載,在使用前連接並解壓縮。

MOSSE跟蹤器

誤差最小平方和濾波器(MOSSE)使用自適應相關進行目標跟蹤,當使用單個幀初始化時產生穩定的相關濾波器。 MOSSE跟蹤器對照明,比例,姿勢和非剛性變形的變化非常穩健。 它還根據峯值與旁瓣比率檢測遮擋,這使得跟蹤器能夠暫停並在對象重新出現時從中斷處繼續。 MOSSE跟蹤器也以更高的fps(450 fps甚至更高)運行。 爲了增加積極性,它也很容易實現,與其他複雜的跟蹤器一樣準確,速度更快。 但是,在性能方面,它落後於基於深度學習的跟蹤器。

訂閱&下載代碼

如果你喜歡這篇文章並想下載此文章中使用的代碼(C++和Python)和示例圖片,請訂閱我的欄目。你還將收到免費的計算機視覺資源指南。在我們的欄目裏,我們還分享了用C++/Python編寫的OpenCV教程和示例,以及計算機視覺和機器學習的算法和新聞。

訂閱欄目:https://bigvisionllc.leadpages.net/leadbox/143948b73f72a2%3A173c9390c346dc/5649050225344512/

轉載自:http://www.cnblogs.com/annie22wang/p/9366610.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章