單目標跟蹤之相關濾波 MOSSE


相關濾波

相關操作

卷積操作

MOSSE

基本思想

具體操作流程

代碼解讀

初始化

在線更新

缺點

  論文名稱:Visual Object Tracking using Adaptive Correlation Filters

  文獻地址:https://www.cs.colostate.edu/~vision/publications/bolme_cvpr10.pdf

  源碼地址:https://github.com/xingqing45678/Mosse_CF

 

  由於基於CNNs深度學習在單目標跟蹤方法的參數量和計算量都較大,難以與目標檢測算法一起移植到嵌入式中。同時,受CVPR2020 AutoTrack的影響,開始從基於傳統CF,DCF思想角度入手,對相關濾波CF的鼻祖MOSSE進行攻讀。

  本文主要介紹相關濾波系列算法的開篇——MOSSE基本原理及其python代碼實現流程。

  由於論文中涉及到大量傅里葉變換的公式,可能顯得MOSSE晦澀難懂,本篇盡量淡化傅里葉變換的公式內容,突出MOSSE在跟蹤過程中所做的工作。

相關濾波

  相關濾波(CF)源於信號處理領域,有這么一句話"兩個信號越相似,其相關值越高。在跟蹤,就是找到與跟蹤目標響應最大的項" 。通過后續代碼的解讀,會體會到相關值越高在MOSSE中的作用。

相關操作

  假設有兩個信號f和g,則兩個信號的相關性(correlation)為:

其中,f表示f的復共軛...(說好的淡化公式的... 看不明白不要緊,只要明白卷積就可以往下看)

  對於圖像而言,相關性可以理解為相關核在圖像上進行點積操作,如下圖所示。圖像的相關公式可以表達為g = f ⓧ h,具體而言,對應元素相乘在求和:

  從上圖的結果可以看出,輸出正好是相關核旋轉了180°

  具體步驟:

  a. 相關核,中心分別位於輸入圖像的f(i, j)像素上進行滑動;(會補零)

  b. 利用上式進行點積操作,並求和,得到輸出圖像對應的像素值g(i, j);

  c. 在輸出圖像上進行滑動,重復b的操作,求出輸出圖像上的所有像素值。

  你可能會覺得相關操作和卷積操作一樣,但二者實際上是有區別的。

卷積操作

  圖像卷積操作的公式:g = f ★ h,具體而言,對應到每個像素的表達式如下所示,可以看出,卷積操作滿足交換律。

   In Convolution operation, the kernel is first flipped by an angle of 180 degrees and is then applied to the image.

  也就是說, 卷積操作與相關操作的差異在於卷積將核kernel旋轉了180度(順逆都一樣);相關可以反應兩個信號的相似度,卷積不可以;卷積滿足交換律,相關不可以;卷積可以直接通過卷積定理(時域上的卷積等於頻域上的乘積)來加速運算,相關不可以。

參考文章:https://towardsdatascience.com/convolution-vs-correlation-af868b6b4fb5

MOSSE

基本思想

  MOSSE的基本思想:以上一幀目標位置為bbox,在當前幀圖像上截取目標,使用相關核找到當前截取圖像上的最大響應,這里的最大響應就是當前目標的中心,然后更新當前幀目標的中心,並通過第一幀給出的bbox的寬高為寬高,生成此時物體的框體。(這里有一個假設,就是相鄰幀物體不會發生過大的位移,即使用上一幀的bbox截取當前幀的圖像,仍可以獲得到目標的中心) --- 可以看出,MOSSE對於物體大小的變化,以及物體的消失沒有絲毫抵抗能力。

具體操作流程

  通過基本思想可以看出,需要首先獲得一個可以在目標中心獲得最大響應的相關核h。即下述公式操作后,g中最大值是物體的中心。

  由於上式在計算機中計算量較大,作者對上式采用快速傅里葉變換(FFT):

  便得到論文中給出的

  因此,跟蹤的任務便是找到相應的H*

  在實際跟蹤中,需要考慮目標外觀的變化等因素的影響,需要進行一個優化的求解,即輸出的響應與期望的響應越接近越好:

  該優化的求解可以根據偏導數進行求取,求取結果可以直接表達為:

  以上的操作均是在跟蹤第一幀完成的。也就是說,相關核H的確定是通過第一幀標出的目標在線得出的。而上式中的i,對應到代碼中即為若干次圖像的仿射變換。而對應的輸出響應的期望G*是高斯核,后續會結合代碼具體解讀。

  為了提升算法的魯棒性,應對目標在跟蹤過程中的外觀變換,需要對核進行在線的更新,更新策略如下所示:

  值得一提的是,F、G、H的尺寸都是一致的。並且,在工程實踐中確實將H分解為A,B兩部分,並進行迭代更新。

代碼解讀

初始化

  初始化應用於第一幀的功能實現,包括初始bbox信息的獲取(中心點、寬、高、特征),以及核的生成。其中,包含圖像的預處理和准備工作:

  • 灰度化(輸入特征為灰度,特征表達能力有限)
  • 歸一化
  • 余弦窗的生成
  • 期望輸出響應G的生成(高斯核,同時讓圖片的左邊緣與右邊緣連接,上邊緣與下邊緣連接)

  之后,便是最終要的核的生成。代碼如下:

 1 def init(self,first_frame,bbox):
 2     if len(first_frame.shape)!=2:
 3         assert first_frame.shape[2] == 3
 4         first_frame=cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)  
 5     first_frame=first_frame.astype(np.float32)/255   # 歸一化
 6     x,y,w,h=tuple(bbox)   # x,y為bbox的左下角點,和寬高
 7     self._center=(x+w/2, y+h/2)  # 中心點坐標
 8     self.w,self.h=w,h
 9     w,h=int(round(w)), int(round(h))  # 取整
10     self.cos_window=cos_window((w,h))   # 生成寬高的一維hanning窗 后再進行矩陣乘法 形成h*w的hanning矩陣
11     self._fi=cv2.getRectSubPix(first_frame,(w,h), self._center)   # 根據寬高和中心點的位置截取圖像/特征
12     self._G=np.fft.fft2(gaussian2d_labels((w,h), self.sigma))   # 高斯相應圖
13     self.crop_size=(w,h)   # 裁剪尺寸
14     self._Ai=np.zeros_like(self._G)
15     self._Bi=np.zeros_like(self._G)
16     for _ in range(8):
17         fi=self._rand_warp(self._fi)   # 對裁剪的圖像進行8次仿射變化(旋轉),從而求得H*
18         Fi=np.fft.fft2(self._preprocessing(fi,self.cos_window))   # 進行余弦窗處理
19         self._Ai += self._G*np.conj(Fi)   # conj共軛負數
20         self._Bi += Fi*np.conj(Fi)     #  求解H*所需

其中,代碼第4-5行:圖像轉化為灰度圖;

      代碼第6-9行:獲取第一幀圖像的信息,將左下角點,寬高信息轉化為中心點坐標。(bbox是    通過cv2.selectROI()獲得到目標的左下角點坐標和寬高信息。)

      代碼第10行:獲取余弦窗;

      代碼第11行:通過中心點坐標和寬高在第一幀圖像上截取目標圖像;

      代碼第12行:獲得高斯響應作為期望輸出G。

      代碼第16-20行:通過8次仿射變換,獲取相關核H。

  余弦窗的生成

  余弦窗通過漢寧窗生成。

1 def cos_window(sz):
2     cos_window = np.hanning(int(sz[1]))[:, np.newaxis].dot(np.hanning(int(sz[0]))[np.newaxis, :])
3     return cos_window

  漢寧窗的模樣

  通過下圖余弦矩陣的顏色分布可以看出,余弦窗具有兩邊小中間大的特性,並且四周的值趨近於零,有利於突出靠近中心的目標。

  期望響應輸出G的生成

  G相當於是真值,但此處不需要人為的標注。而是通過高斯函數生成即可。高斯函數產生的最大值在圖像的中心。生成后,需要進行傅里葉變換轉化到頻域。

1 def gaussian2d_labels(sz,sigma):
2     w, h=sz
3     xs, ys = np.meshgrid(np.arange(w), np.arange(h))   # 生成兩個矩陣 一個矩陣各行是0-w-1  一個矩陣各列是0-h-1
4     center_x, center_y = w / 2, h / 2
5     dist = ((xs - center_x) ** 2 + (ys - center_y) ** 2) / (sigma**2)
6     labels = np.exp(-0.5*dist)
7     return labels

  為什么可以用高斯函數產生的值來作為期望的響應輸出呢?這是由於高斯函數自帶中間大兩邊小的特性,十分符合物體中心是最大值的要求。並且在第一幀框定目標時,也會標出十分貼合的bbox,bbox的中心可以近似為物體的中心

  隨機仿射變換

  通過隨機數的生成控制仿射變換的力度。

 1 def _rand_warp(self,img):   # 輸入是裁剪下的圖形
 2     h, w = img.shape[:2]
 3     C = .1
 4     ang = np.random.uniform(-C, C)
 5     c, s = np.cos(ang), np.sin(ang)
 6     W = np.array([[c + np.random.uniform(-C, C), -s + np.random.uniform(-C, C), 0],
 7                   [s + np.random.uniform(-C, C), c + np.random.uniform(-C, C), 0]])
 8     center_warp = np.array([[w / 2], [h / 2]])
 9     tmp = np.sum(W[:, :2], axis=1).reshape((2, 1))
10     W[:, 2:] = center_warp - center_warp * tmp  # 仿射矩陣
11     warped = cv2.warpAffine(img, W, (w, h), cv2.BORDER_REFLECT)    # 進行仿射變換  W是仿射矩陣
12     return warped

在線更新

  在線更新核心操作在於相關核的更新(下述代碼第6行)以及影響相關核的兩個因素的更新(下述代碼22-23行)。

  從代碼14-18行以及返回值可以看出,在線更新的過程只有中心點位置的更新,寬高不更新,如果物體的大小發生明顯的變化,很難自適應。

 1 def update(self,current_frame,vis=False):
 2     if len(current_frame.shape)!=2:
 3         assert current_frame.shape[2]==3
 4         current_frame=cv2.cvtColor(current_frame,cv2.COLOR_BGR2GRAY)
 5     current_frame=current_frame.astype(np.float32)/255
 6     Hi=self._Ai/self._Bi   # Ht  kernel
 7     fi=cv2.getRectSubPix(current_frame,(int(round(self.w)),int(round(self.h))),self._center)   # 裁剪
 8     fi=self._preprocessing(fi,self.cos_window)
 9     Gi=Hi*np.fft.fft2(fi)   # fft
10     gi=np.real(np.fft.ifft2(Gi))   # 返回負數類型參數的實部, 以及二維傅里葉反變換
11     if vis is True:
12         self.score=gi
13     curr=np.unravel_index(np.argmax(gi, axis=None),gi.shape)   # 找到最大那個相應的位置
14     dy,dx=curr[0]-(self.h/2),curr[1]-(self.w/2)   # 中心點位置移動量
15     x_c,y_c=self._center
16     x_c+=dx
17     y_c+=dy   # 中心點更新
18     self._center=(x_c,y_c)
19     fi=cv2.getRectSubPix(current_frame,(int(round(self.w)),int(round(self.h))),self._center)
20     fi=self._preprocessing(fi,self.cos_window)
21     Fi=np.fft.fft2(fi)
22     self._Ai=self.interp_factor*(self._G*np.conj(Fi))+(1-self.interp_factor)*self._Ai
23     self._Bi=self.interp_factor*(Fi*np.conj(Fi))+(1-self.interp_factor)*self._Bi
24     return [self._center[0]-self.w/2,self._center[1]-self.h/2,self.w,self.h]

缺點

  • 輸入的特征為單通道灰度圖像,特征表達能力有限
  • 沒有尺度更新,對於尺度變化的跟蹤目標不敏感

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM