1. 程式人生 > >opencv入門跟蹤演算法(3)之camshift

opencv入門跟蹤演算法(3)之camshift

二.演算法原理

1、camshift利用目標的顏色直方圖模型將影象轉換為顏色概率分佈圖,初始化一個搜尋窗的大小和位置,並根據上一幀得到的結果自適應調整搜尋視窗的位置和大小,從而定位出當前影象中目標的中心位置。camshift的核心步驟仍然是Meanshift,只是在距離相似性度量的基礎之上,又增加了影象灰度相似性的度量。兩者共同作用,實現了目標的跟蹤。

2、camshift演算法目標跟蹤其具體步驟可以理解為三步:

滑鼠響應圈出初始化目標位置----對目標框內生成對應的顏色概率直方圖(反向投影)------對當前幀執行meanshift演算法對目標框進行密度函式梯度估計,找到密度最大處,定義目標框的中心和大小------以上一幀的meanshift演算法返回的目標框的大小和中心值作為本幀目標初始化位置繼續第二步,實現跟蹤。

Meanshift演算法原理圖

3、camshift演算法原理總結

總體來說,該演算法通過meanshift演算法的特徵空間分析方法不斷地比較目標框的中心和其密度梯度分析的質心來更新目標框的大小和位置,然後再通過目標的顏色直方圖模型反向投影將RGB顏色空間轉化為顏色概率密度分佈圖,二者結合更準確的定位了目標的位置,即選取顏色的區域再在每幀圖片裡進行顏色的匹配跟蹤,而且將RGB空間轉化為HSV空間,減少了對光照亮度變化的敏感,提高了跟蹤效果。

camshift能有效解決目標變形和遮擋的問題,對系統資源要求不高,時間複雜度低,在簡單背景下能夠取得良好的跟蹤效果。但當背景較為複雜,或者有許多與目標顏色相似畫素干擾的情況下,會導致跟蹤失敗。因為它單純的考慮顏色直方圖,忽略了目標的空間分佈特性,所以這種情況下需加入對跟蹤目標的預測演算法。

4、執行情況


第一個圖執行情況可以看到,這個是視訊捕捉的半自動跟蹤,跟蹤的畫面很清晰也比較流暢,而且在小範圍內時,捕捉框能精準的捕捉到物件,大小變化很靈活。當調節上面三個引數時,會導致捕捉框的大小變化,程式碼給出的原始數值是最佳的捕捉情況。

第二個圖為改過之後的捕捉視訊的執行情況,讀取幀的速度快於原視訊的速度,且跟蹤效果在該情況下比較精準。當物件運動過快或者變化太多時無法檢測到相應的顏色匹配區域時,該演算法的處理情況是保持原狀,所以會出現偶爾跟丟的情況,但是它在每一幀都會執行Meanshift演算法和顏色匹配,所以跟丟之後還是會在較短時間內重新找到物件。

三.程式碼分析

1、Onmouse()滑鼠回撥函式

void onMouse( int event, int x, int y, int flags, void*param ),其中event表示各種滑鼠訊息,x,y 表示滑鼠在當前影象座標系中的位置,flags是EVENT FLAG的組合,param是使用者定義的傳遞到SETMOUSECALLBACK函式呼叫的引數。滑鼠訊息機制如CV_EVENT_LBUTTONDOWN,CV_EVENT_LBUTTONUP,CV_EVENT_MOUSEMOVE分別代表滑鼠左鍵按下,左鍵擡起,滑鼠移動這些滑鼠訊息機制,通過不同的滑鼠訊息來進入不同的響應函式完成所需要的滑鼠響應操作,在該程式中在左鍵按下的case語句下通過設定標誌位selectObject = true標記認為已選定目標,然後作為判斷條件來通過函式得到矩形區域,一旦左鍵按下,那麼進入矩形選擇區域得到所選的目標框,當訊息機制為左鍵擡起時,selectObject = false;沒有框定目標,無跟蹤物件。在官方給出的示例中在該滑鼠訊息響應函式中並沒有畫出相應的矩形框的函式,導致實際執行的時候並不能準確框住物件。
2、命令解析器函式CommandLineParser parser(argc, argv, keys)
const char* keys = {"{1|  | 0 | camera number}"};
CommandLineParser parser(argc, argv, keys);//命令解析器函式
int camNum = parser.get<int>("1");  
這個類的出現主要是方便使用者在命令列使用過程中減少工作量,parser(argc, argv, keys)前兩個引數是命令列傳過來的,第三個引數keys的結構有一定規律,比如說"{c |camera|false| use camera or not}" 都是用大括號和雙引號引起來,然後中間的內容分成4斷,用”|”分隔開,分別表示簡稱,檔案來源,檔案值和幫助語句,在該函式中主要是用來開啟攝像頭和關閉攝像頭的。大概可以看出來用這個類的好處就是很方便,因為以前版本沒這個類時,如果要執行帶引數的.exe,必須在命令列中輸入檔案路徑以及各種引數,並且輸入的引數格式要與程式碼中的if語句判斷內容格式一樣,一不小心就輸錯了,很不方便。另外如果想要更改輸入格式的話在主函式檔案中要相應更改很多地方。現在有了這個類,只需要改keys裡面的內容就可以了。
3、cvCvtColor(src,dst_gray,CV_BGR2GRAY);顏色空間轉化函式
void cvCvtColor( const CvArr* src, CvArr* dst, int code ); code色彩空間轉換的模式,該code來實現不同型別的顏色空間轉換。比如CV_BGR2GRAY表示轉換為灰度圖,CV_BGR2HSV將圖片從RGB空間轉換為HSV空間。其中當code選用CV_BGR2GRAY時,dst需要是單通道圖片。當code選用CV_BGR2HSV時,對於8點陣圖,需要將RGB值歸一化到0-1之間。這樣得到HSV圖中的H範圍才是0-360,S和V的範圍是0-1。在該演算法中,cvtColor(image, hsv, CV_BGR2HSV)用於將rgb攝像頭幀轉化成hsv空間的。
4、void cvInRangeS( const CvArr* src, CvScalar lower, CvScalar upper, CvArr* dst );
src第一個原陣列;lower包括進的下邊界;upper不包括進的上邊界;dst輸出陣列必須是 8u 或 8s 型別.當src在所在範圍內時,dst被置1,否則置0;
5、calcBackProject(&hue, 1, 0, hist, backproj, &phranges)反向投影函式
引數分別是輸入影象,輸入影象的數量,用於計算反向投影的通道列表,輸入直方圖,目標反向投影輸出影象,直方圖中每個維度bin的取值範圍。因此該語句為計算hue影象0通道直方圖hist的反向投影,並輸入backproj中。
6、TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 )迭代終止條件
這個類是作為迭代演算法的終止條件的,該類變數需要3個引數,一個是型別,第二個引數為迭代的最大次數,最後一個是特定的閾值。型別有CV_TERMCRIT_ITER、CV_TERMCRIT_EPS、CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,分別代表著迭代終止條件為達到最大迭代次數終止,迭代到閾值終止,或者兩者都作為迭代終止條件。這裡是指迭代到最大迭代次數10或者達到特定的閾值就停止迭代。

和之前瞭解的KCF,TLD等演算法完全不一樣的體驗,之前的演算法是在在opencv3.3中已經封裝好的,在呼叫的時候只需要直接呼叫相應的類函式就行了,沒有其他的操作,而camshift演算法沒有那麼智慧,官方給出的原始碼中是通過程式碼指令來進行相應的計算,比如提取顏色直方圖,進行RGB空間到HSV的反向投影等都是通過程式碼完成的。理解起來也是需要一定的基礎,仔仔細細的百度了每個函式的相應功能之後大致動力函式整體的實現架構以及camshift演算法的實現情況,但是對於算大內部的矩陣運算,直方圖提取還是不能理解。就個人體驗來說,camshift演算法在跟蹤方面效能比較優良,但是沒有其他的附加功能,只是單純的在做顏色匹配和meanshift演算法,而其他的目標跟蹤演算法還有檢測和學習的功能,不需要手動圈出物件,同時在跟蹤過程中會一直根據當前提取到的資訊來完善跟蹤器,使後期的跟蹤更加的精準。個人想法是,跟蹤原理比較簡單的camshift演算法可以加上一個離線庫(或者線上庫)儲存一些圖片物件的顏色直方圖,然後在後期檢測的時候在視訊裡一旦有匹配的顏色分佈,就可以依據此將相應物件圈出來。不過這個演算法對於背景較複雜還有物件顏色分佈較相近的情況是有很大的漏洞的。只能作為後期更加優良的跟蹤演算法的基礎演算法。