【OpenCV】Meanshift、Camshift物體跟蹤

2021-02-24 古月居


本章我們要學習的是運動物體的跟蹤,現代圖像處理中經典的幾種跟蹤方法主要是:meanshift(均值漂移),Camshift(meanshift的優化版本),KCF,光流法等。


我們本章主要介紹的是前兩種:

meanshift(均值漂移)以及Camshift(meanshift的優化版本)



首先我們需要了解什麼是均值漂移,該算法是一種尋找概率函數離散樣本的最大密度區域的算法,我們可以認為我們圖像中感興趣的區域就是離散樣本密度最大的區域。(這句話看不懂沒關係,先往下看,後面就明白了)

首先我們要有一個均值漂移的基本概念:這個算法會向我們需要跟蹤的區域的方向那慢慢移動過去。


設想在一個有N個樣本點的特徵空間,初始確定一個中心點center(隨便選一個),計算在設置的半徑為D的圓形空間內所有的點(xi)與中心點center的向量。


計算整個圓形空間內所有向量的平均值,得到一個偏移均值


將中心點center移動到偏移均值所指向的位置


重複移動,直到沒有辦法繼續增加圈內的點或移動距離過小後結束


整個過程如下圖所示,points的值為圈內點的數量

使用這種算法,就能讓我們的圓逐步向點多的地方走去,這就是均值漂移的基本概念。



接下來我們需要學習彩色直方圖的概念。


彩色直方圖的x軸表示色彩的值,y軸表示這個色彩的像素有多少個,灰度圖因為只有一個通道所以只有一個直方圖,但如果是HSV或者BGR這種的,就可以對每一個通道進行直方圖的構建,這裡我們只需要對HSV圖像的H通道進行跟蹤即可。(因為我們接下來是根據普通的顏色來進行的跟蹤,一個通道就足夠了)


構建圖像的直方圖需要使用到函數cv2.calcHist,其常用函數語法如下所示:

hist=cv2.calcHist(images, channels, mask, histSize, ranges) 
images:輸入的圖像channels:選擇圖像的通道,如果是三通道的話就可以是[0],[1],[2]mask:掩膜,是一個大小和image一樣的np數組,其中把需要處理的部分指定為1,不需要處理的部分指定為0,一般設置為None,如果有mask,會先對輸入圖像進行掩膜操作histSize:使用多少個bin(柱子),一般為256,但如果是H值就是181ranges:像素值的範圍,一般為[0,255]表示0~255,對於H通道而言就是[0,180]

需要注意的是,這裡除了mask以外,其餘的幾個參數都要加上[],如下所示:

hist=cv2.calcHist([img],[0],mask,[181],[0,180])

 這個時候我們還需要使用一種歸一化的方法來對彩色直方圖中的數量值進行規範化。


現有的直方圖中的數值為對應像素的數量,其中圖中出現數量最多的像素的數量值(最高的柱子對應的y軸數值)我們記為max的話,整個直方圖y方向上的取值範圍就是[0,max],我們需要把這個範圍縮減到[0,255],為什麼是255後面會進行解釋。



這裡我們需要使用到cv2.normalize函數,函數主要語法如下所示:

cv2.normalize(src,dst, alpha,beta, norm_type)·src-輸入數組。·dst-與SRC大小相同的輸出數組。·α-範數值在範圍歸一化的情況下歸一化到較低的範圍邊界。·β-上限範圍在範圍歸一化的情況下;它不用於範數歸一化。·范式-規範化類型(見下面詳細介紹)。

這裡我們需要注意的是範式-規範化類型,這裡有以下幾種選擇:

NORM_MINMAX:數組的數值被平移或縮放到一個指定的範圍,線性歸一化。NORM_INF:歸一化數組的(切比雪夫距離)L∞範數(絕對值的最大值)NORM_L1:歸一化數組的(曼哈頓距離)L1-範數(絕對值的和)NORM_L2:歸一化數組的(歐幾裡德距離)L2-範數

上面的名詞看起來很高大上,其實是很簡單,我們一一講解下。(不是很感興趣的只要看下第一個NORM_MINMAX即可,剩下的三個可以不看)


首先是NORM_MINMAX,這個是我們最常用的一種歸一化方法。舉個例子,我們上面提到的最高的柱子對應的y軸坐標為max,如果我們使用這種方法,想要縮放到的指定的範圍為[0,255],那麼max就會直接被賦值為255,其餘的柱子也會隨之一樣被壓縮(類似於相似三角形那樣的縮放感覺)。

沒錯,很簡單得就介紹完了一種,不是很想了解其他幾個的讀者可以直接跳過本小節剩下來的內容了,因為剩下三種不是很常用。


接下來是NORM_INF,他會對我們每一個柱子的y軸坐標進行如下操作:用當前柱子的y軸坐標,除以所有柱子中y值的絕對值最大的那個作為新的y軸的值,公式如下所示:

我們假設上面這張圖就是我們要跟蹤的對象所對應的彩色直方圖(一個很大的跟蹤對象由很多種不同的像素組成,然後我們將他們統計一下,每一種顏色分別對應著幾個像素,有的顏色可能有很多像素,有的顏色可能一個也沒有)。

上圖左邊紅色的那個突出的柱子對應的是最大y軸值,他有100000個點,其他的柱子對應的像素的數目就是在[0,100000]之間,然後我們將彩色直方圖歸一化到[0,255]之間後,紅色突出的柱子對應的最大y軸值就變成了255,其他的顏色對應的y軸的值也一一等比例改變到了0-255之間,這個我們要跟蹤物體所構成的歸一化彩色直方圖我們稱為Hist。

然後我們對我們要處理的圖像(即包含我們要跟蹤的對象的整個圖片)根據我們上面得到的Hist來判斷要處理的圖像中的每一個像素是否屬於我們跟蹤對象或者說屬於我們跟蹤對象的概率有多大。

例如,我們要處理的圖像中有一個點為紅色,那麼他就會去看我們跟蹤對象的直方圖Hist,發現直方圖中的紅色對應的屬於跟蹤對象的可能性(y軸的值)為255,則就會直接賦值為255(純白色);如果要處理的圖像中有一個點為棕色,然後去看直方圖,發現發現直方圖中的棕色對應的屬於跟蹤對象的可能性(y軸的值)為0,就會直接賦值為0(黑色),由此構成我們的直方圖反投影圖。

所以我們最後得到的圖像就是一個與原圖同樣大小的灰度圖像。例如我跟蹤了下面綠色部分的物體,得到綠色部分的彩色直方圖後,其對應的直方圖反投影圖就如下所示:

越暗的地方說明屬於跟蹤部分的可能性越低,越亮的地方屬於跟蹤部分的可能性越高。


這裡使用到的函數為cv2.calcBackProject,函數語法如下所示:

dst=cv2.calcBackProject(image,channel,hist,range,scale)
image:輸入圖像channel:用來計算反向投影的通道數,與產生直方圖對應的通道應一致hist:作為輸入的直方圖range:直方圖的取值範圍scale:輸出圖像的縮放比,一般為1,保持與輸入圖像一樣的大小dst:輸出圖像
注意:除了hist和scale外,其他的參數都要加上[]

例如:

dst=cv2.calcBackProject([hsv],[0],hist,[0,180],1)


上述講述的都是關於meanshift(均值漂移)的跟蹤方法(其實Camshift也是這個樣子的原理)。現在有了一切的基礎知識後,我們就可以進行物體跟蹤的實現了,這裡我們打算跟蹤一個綠色的物體來看看,首先我們載入我們要處理的視頻文件(或者直接用攝像頭也行):

import cv2import numpy as npcap=cv2.VideoCapture('1.mp4')

然後我們設置下我們第一個起始框的位置和長寬(可以理解為上面均值漂移原理中起始圓的起始位置和圓的大小)

r,h,c,w=(400,500,400,500)track_window=(c,r,w,h)

然後我們讀取第一幀,先將圖像轉換為HSV圖,方便目標跟蹤,然後通過掩膜操作來得到圖像中的綠色部分的掩膜:

ret,frame=cap.read()hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)mask=cv2.inRange(hsv,np.array((35,43,46)),np.array((77,255,255)))

接著我們使用cv2.calcHist函數來得到圖像中的綠色部分,並計算這部分的直方圖,我們只要收集第0通道:H的數據就好了,因為是H通道,其取值範圍為0-180,所以需要181根柱子,H通道像素的取值為0-180(柱子數量不是與像素取值範圍不能一一對應的話,柱與柱之間會有點壓縮,即x軸方向上會產生柱子與柱子之間的融合):

hist=cv2.calcHist([hsv],[0],mask,[181],[0,180])cv2.normalize(hist,hist,0,255,cv2.NORM_MINMAX)

然後我們來設置均值漂移meanshift的一次活動的終止條件:


cv2.TERM_CRITERIA_EPS:代表一次均值漂移累計的移動次數,EPS表示epsilon,這裡我們設置為10。


cv2.TERM_CRITERIA_COUNT:表示一次均值漂移移動的最小偏移像素,如果一次漂移的像素值低於這個值,就會終止這次活動,這裡我們設置為1。

term_crit=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,10,1)

這裡可能有讀者會有點疑惑為什麼要加上這個終止條件,其實這是為了讓視頻處理(攝像頭)變得流暢而設置的,不然如果不設置這個條件,對於每一幀傳入的圖像,meanshift都要找到當前圖像中最好的地方(密度最大的地方),然後才會開始處理下一幀,這樣圖像看起來就會變得異常卡頓。我們程序也沒必要每一幀都完完全全處在跟蹤物體的最佳位置上,容許有些許的偏差,只要能極大部分跟蹤到了物體就可以了。

然後我們在第一幀中得到了要跟蹤的物體的顏色直方圖後,我們開始處理圖像中的後續幀:

while 1:    ret,frame=cap.read()    if ret== True:                hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)                dst=cv2.calcBackProject([hsv],[0],hist,[0,180],1)                        ret,track_window=cv2.meanShift(dst,track_window,term_crit)                x,y,w,h=track_window                img=cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)        cv2.namedWindow('img',cv2.WINDOW_NORMAL)        cv2.imshow('img',img)        if cv2.waitKey(1)==ord('q'):            break    else:        breakcap.release()cv2.destroyAllWindows()

然後我們就能夠實現綠色物體的跟蹤了,運行結果如下所示:

完整代碼如下所示:

import cv2import numpy as npcap=cv2.VideoCapture('1.mp4')ret,frame=cap.read()r,h,c,w=(400,500,400,500)track_window=(c,r,w,h)hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)mask=cv2.inRange(hsv,np.array((35,43,46)),np.array((77,255,255)))hist=cv2.calcHist([hsv],[0],mask,[181],[0,180])cv2.normalize(hist,hist,0,255,cv2.NORM_MINMAX)term_crit=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,10,1)while 1:    ret,frame=cap.read()    if ret== True:        hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)        dst=cv2.calcBackProject([hsv],[0],hist,[0,180],1)        ret,track_window=cv2.meanShift(dst,track_window,term_crit)        x,y,w,h=track_window        img=cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)        cv2.namedWindow('img',cv2.WINDOW_NORMAL)        cv2.imshow('img',img)        if cv2.waitKey(1)==ord('q'):            break    else:        breakcap.release()cv2.destroyAllWindows()



但我們可以注意到的是,我們矩形框的大小是我們一開始就直接設置好的,在整個跟蹤過程中其大小是不會改變的(不管我們的跟蹤物體是否變小或者變大了)


為了解決這個問題,我們可以在meanshift的基礎上,讓他自適應跟蹤物體的大小來調整矩形框的大小,這就是Camshift。CamShift算法的全稱是」Continuously Adaptive Mean-SHIFT」,稱為連續自適應的meanshift算法,算法部分不變,只是能讓他能夠自我適應跟蹤物體大小而已。


代碼方面也和meanshift差不多,只要在while循環裡改幾行就可以了:

while 1 :    ret,frame=cap.read()    if ret == True:        hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)        dst=cv2.calcBackProject(hsv,[0],hist,[0,180],1)                ret,track_window=cv2.CamShift(dst,track_window,term_crit)                boxes=cv2.boxPoints(ret)                pts=np.int0(boxes)                img2=cv2.polylines(frame,[pts],True,(0,255,0),2)                cv2.imshow('img2',img2)        if cv2.waitKey(1)==ord('q'):            break    else:        break

然後運行結果中的綠色矩形框就能夠根據跟蹤的對象而自適應改變框的大小了(注意:這個Camshift很容易就會檢測出錯)



1.問題,讀者可以根據需要自行修改。


2.我們要跟蹤的對象也可以不是指定的顏色區間,其他特定的東西也沒問題,只要能得到顏色直方圖的都可以。


3.我們這裡的初始矩形框的位置是默認設置到一個位置上的,這其實不是很好,因為如果周邊沒有屬於跟蹤目標的像素或者可能性比較低,導致直方圖反投影圖中那一塊部分全黑的話,那個框就會在那自行鬼畜(因為不知道該往哪走了),這塊是可以加工的,比如直接把矩形框扔到密度比較高的部分也是可以的。(即先檢測一幀,得到第一幀中密度高的區域,然後再設置矩形框)


4.這種檢測中物體移動速度不宜過快過快,不然矩形框可能跟不上。



課程《ROS機器視覺開發入門 · 古月》將帶你入門常用2D/3D視覺傳感器的ROS驅動及數據結構,使用ROS標定功能包完成相機的參數標定,結合人臉識別、物體跟蹤等例程重點講解ROS與OpenCV的結合方法,及基於TensorFlow機器學習平臺實現對日常用品的識別,為後續ROS機器視覺開發夯實基礎。

點擊"閱讀原文"查看課程詳情

相關焦點

  • opencv教程-目標跟蹤
    算法retval, window=cv.meanShift(probImage, window, criteria)參數probImage:當前圖像反向投影圖參數window:前一圖像目標框參數criteria:迭代停止條件算法原理:屬於核密度估計算法,向密度最大的方向移動(反向投影就是一種概率密度圖,顏色亮的地方密度高,與目標物體更相似
  • OpenCV測量物體的尺寸技能 get~
    其原理非常的簡單,實際的空間距離=圖上距離/地圖上的比例尺。我們今天要介紹的內容實際上原理和上面所說的是相同的,關鍵是如何獲取圖上距離和比例尺呢?首先需要知道一個和比例尺類似的概念叫 pixels per metric ratio。
  • 使用OpenCV和Python構建自己的車輛檢測模型
    想想看,如果你能在紅綠燈攝像頭中集成車輛檢測系統,你可以輕鬆地同時跟蹤許多有用的東西:白天交通路口有多少輛車?什麼時候交通堵塞?什麼樣的車輛(重型車輛、汽車等)正在通過交叉路口?有沒有辦法優化交通,並通過不同的街道進行分配?還有很多例子就不一一列舉。應用程式是無止境的!我們人類可以很容易地在一瞬間從複雜的場景中檢測和識別出物體。
  • 基於OpenCv 和 Python 的手指識別及追蹤
    翻譯 | 餘杭 Lamaric 校對 | 吳曉曼 審核 | 餘杭詳細代碼參考:https://github.com/amarlearning/opencv手指追蹤是許多計算機視覺應用的重要特徵。在該應用中,使用基於直方圖的方法將手與背景幀分離。
  • OpenCV(四)邊緣檢測
    (以下內容引用自百度百科)邊緣可能與視角有關—— 也就是說邊緣可能隨著視角不同而變化,典型地反映在場景、物體的幾何形狀一個將另一個遮擋起來,也可能與視角無關——這通常反映被觀察物體的屬性如表面紋理和表面形狀。在二維乃至更高維空間中,需要考慮透視投影的影響。
  • 基於opencv 的圖像處理入門教程
    https://github.com/ccc013/CodesNotes/blob/master/opencv_notes/opencv_image_process_tutorial.ipynb1.檢測圖片的輪廓輪廓是圖片中將連續的點連接在一起的曲線,通常檢測輪廓的目的是為了檢測物體。
  • OpenCV 之 霍夫變換
    它常用來檢測 直線和曲線 (圓形),識別圖像中的幾何形狀,甚至可用來分割重疊或有部分遮擋的物體。 1  平面坐標和極坐標1)  平面坐標的點 <=> 極坐標(平面化)的曲線    所謂極坐標平面化是指, 將 ρ-θ 的關係像 x-y 那樣在平面內展開。
  • opencv教程-光流法
    opencv裡的光流法有兩種,稀疏光流法 和 稠密光流法。簡單理解的話光流就是一個向量,包含在一定時間間隔內x方向位置的變化、y方向位置的變化,所以其三個主要因素就是dx,dy,dt。基本假設:(1):相鄰幀亮度恆定(2):相鄰幀物體運動較小(3):同一物體具有相同運動1:稀疏光流法 cv.calcOpticalFlowPyrLK只匹配指定的點比如前一張圖片的角點。
  • 「python opencv視覺零基礎實戰」七邏輯運算應用
    一、學習目標了解opencv中圖像的邏輯運算了解opencv中邏輯運算的應用如有錯誤歡迎指出~目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python
  • 如何快速簡單的安裝opencv-python
    這樣就會從清華鏡像安裝opencv-contrib-python庫。目前opencv最新版本為4.1.1 ----2019-8-28在opencv-contrib-python 版本中含有額外模塊( Extra modules ),而 opencv-python 版本中只含有基礎模塊。
  • OpenCV中的快速直線檢測
    cv::ximgproc::FastLineDetectors是opencv-contrib中用於檢測直線的模塊,該方法能在較短時間內獲得精度較高的直線檢測結果
  • OpenCV-Python 直方圖-4:直方圖反投影|二十九
    簡而言之,它創建的圖像大小與輸入圖像相同(但只有一個通道),其中每個像素對應於該像素屬於我們物體的概率。用更簡單的話來說,與其餘部分相比,輸出圖像將在可能有對象的區域具有更多的白色值。好吧,這是一個直觀的解釋。(我無法使其更簡單)。直方圖反投影與camshift算法等配合使用。我們該怎麼做呢?
  • OpenCV特徵點檢測——ORB特徵
    但是這樣只求速度的特徵描述子,一般都是應用在實時的視頻處理中的,這樣的話就可以通過跟蹤還有一些啟發式的策略來解決尺度不變性的問題。關於計算速度:ORB是sift的100倍,是surf的10倍。關於性能:下面是一個性能對比,ORB還是很給力。點擊看大圖。
  • 世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下
    php-opencv 項目的 logo我曾考慮使用 SWIG 寫一個 php-opencv 模塊,並花費了大量時間在上面,但是並沒有取得任何成果。一切都因為我不懂 C / C++ 並且沒有為 PHP 7 編寫過擴展文件而變得複雜。
  • 「python opencv視覺零基礎」十四、直方圖反向投影
    前文提醒:博主正在參加博客之星評比,成功入選Top200,現在暫居第九歡迎各位點擊了解更多幫我投票,非常感謝~目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python
  • 「python opencv視覺零基礎」十、圖片效果毛玻璃
    一、學習目標了解高斯模糊的使用方法了解毛玻璃的圖片效果添加了解如何自己做一個噪聲圖片目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python opencv視覺入門到實戰
  • OpenCV+深度學習預訓練模型,簡單搞定圖像識別 | 教程
    李林 編譯自 pyimagesearch作者 Adrian Rosebrock量子位 報導 | 公眾號 QbitAIOpenCV是一個2000年發布的開源計算機視覺庫,有進行物體識別、圖像分割、人臉識別、動作識別等多種功能,可以在Linux、Windows、
  • 「python opencv計算機視覺零基礎到實戰」九模糊
    一、學習目標了解什麼是卷積了解模糊的使用方法與應用目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python opencv視覺入門到實戰」 第四節色彩空間
  • opencv-python圖像預處理-濾波
    圖像濾波(模糊)濾波也叫模糊,下面是opencv中常見的五種濾波方法,先看一下濾波前後的效果。# -*- coding: utf-8 -*-"""運行環境 python==3.6 opencv-contrib-python== 4.1 win10系統第一行 「 # -*- coding: utf-8 -*- 」 告訴Python解釋器,按照UTF-8編碼讀取原始碼"""dir1="lena.jpg"
  • 「python opencv視覺零到實戰」八、圖片選區操作
    一、學習目標了解什麼是ROI了解floodFill的使用方法如有錯誤歡迎指出~目錄「python opencv 計算機視覺零基礎實戰」 第一節「python opencv視覺入門到實戰」二、格式與攝像頭「python opencv 視覺入門到實戰」 三、圖像編輯「python opencv視覺入門到實戰