opencv运动目标跟踪预测_浅谈多目标跟踪中的相机运动
?PaperWeekly 原創 ·?作者|黃飄
學校|華中科技大學碩士生
研究方向|多目標跟蹤
之前的文章中我介紹了 Kalman 濾波器,這個算法被廣泛用于多目標跟蹤任務中的行人運動模型。然而實際場景中存在有很多相機運動,僅僅依賴行人運動模型是不夠的。這次我主要介紹下相機運動模型,以對極幾何和 ECC 為主。完整的代碼和示例我都放在了 Github:
https://github.com/nightmaredimple/libmot
多目標跟蹤中的相機運動在多目標跟蹤場景中往往存在有復雜的運動模式,這些模式除了行人這類非剛性運動,還有相機這類剛性運動。以 MOT Challenge 數據集為例,其中就存在大量相機運動場景,甚至超過了靜態相機場景數。比如 MOT17-13 號視頻中車載相機在車輛轉彎時對于兩個運動速度較慢行人的視角:
我們從示意圖可以看到,由于車輛轉彎速度很快,上一幀的行人位置映射到下一幀就變成了另一個的位置。因此相機運動對于多目標跟蹤的影響很大,尤其是僅依賴運動信息的模型,相機的運動會嚴重干擾運動模型。
對極幾何2.1 對極幾何模型關于相機運動方面的知識,我在之前介紹單目深度估計中的無監督模型時介紹過,即將變化差異不劇烈的兩幀畫面近似看作不同相機視角下同一場景的畫面,也就是對極幾何,這一點可以看看《計算機視覺中的多視幾何》中關于相機幾何方面的知識:不過這里我需要先解釋一下一些概念,以方便后續模型的講解:1. 基線 [baseline]:直線 CC'為基線。2. 對極平面束 [epipolar pencil]:以基線為軸的平面束。3. 對極平面 [epipolar plane]:任何包含基線的平面都稱為對極平面。4. 對極點 [epipole]:攝像機的基線與每幅圖像的交點。比如,上圖中的點 x 和 x'。5. 對極線 [epipolar line]:對極平面與圖像的交線。6. 5點共面:點 x,x',攝像機中心 C、C',空間點 X 是 5 點共面的。7. 極線約束:兩極線上點的對應關系。
接下來,我們首先看一篇 ACM MM2019 的論文 TNT [1],這是一篇研究端到端運動信息和表觀信息結合框架的論文:不過這里我們要講的是其提出來的相機運動模型:我們可以看到,作者將行人運動和相機運動結合了,其中目標函數的第一部分是利用了對極幾何中本質矩陣 F 的性質,相關的理論推導可以看下圖:其中 x 表示的目標框的四個頂點的坐標信息,第二部分中作者則是假設兩幀中的同一目標的形狀近似不變。因此我們只需要求得本質矩陣 F,即可根據上一幀目標框信息,利用最小二乘法求得下一幀目標框信息。關于本質矩陣 F 的求解,作者提到基于 SURF 特征點提取和 Ransac 采樣進行估計。不過作者也沒有給出詳細的實現過程,我這里試著做一下理論推導。首先由于作者在目標函數中要求了目標框形狀的一致性,那么我們不妨直接把下一幀目標框的形狀信息看做已知的。其次,我們先假設本質矩陣 F 已經被估計出來了,這個矩陣是 3x3 的形狀,那么為了推導方便,我這里做一個假設:
對于第 t 幀的任意一個目標框的每一個節點?,這里由于是三維的幾何信息,所以添加一個 z 軸坐標,令?為一個已知的三維向量,那么一個目標框就存在四個這樣的三維向量,不妨看作一個 4x3 的矩陣 M。那么就可以將目標函數展開,這里面的 (w,h) 為已知信息,(x,y) 為下一幀目標框的左上角坐標:
很明顯這就是一個典型的 Ax=b 問題,后面的問題就迎刃而解了。
2.2 實驗分析
為了保證效率,我這里采用 ORB 特征提取策略,然后采用 brute force 的匹配策略:class?Epipolar(object):????def?__init__(self,?feature_method?=?'orb',?match_method?=?'brute?force',
?????????????????metric?=?cv2.NORM_HAMMING,?n_points?=?50,?nfeatures?=?500,
?????????????????scaleFactor?=?1.2,?nlevels?=?8):
????????"""Using?Epipolar?Geometry?to?Estimate?Camara?Motion
????????Parameters
????????----------
????????feature_method?:?str
????????????the?method?of?feature?extraction,?the?default?is?ORB,?more?methods?will?be?added?in?the?future
????????match_method?:?str
????????????the?method?of?feature?matching,?the?default?is?brute?force,?more?methods?will?be?added?in?the?future
????????metric:?metrics?in?cv2
????????????distance?metric?for?feature?matching
????????n_points:?int
????????????numbers?of?matched?points?to?be?considered
????????nfeatures:?int
????????????numbers?of?features?to?be?extract
????????scaleFactor:?float
????????????scale?factor?for?orb
????????nlevels:?float
????????????levels?for?orb
????????"""
????????self.metric?=?metric
????????if?feature_method?==?'orb':
????????????self.feature_extractor?=?cv2.ORB_create(nfeatures?=?nfeatures,
????????????????????????????????????????????????????scaleFactor?=?scaleFactor,?nlevels?=?nlevels)
????????if?match_method?==?'brute?force':
????????????self.matcher?=?cv2.BFMatcher(metric,?crossCheck=True)
????????self.n_points?=?n_points
????def?FeatureExtract(self,?img):
????????"""Detect?and?Compute?the?input?image's?keypoints?and?descriptors
????????Parameters
????????----------
????????img?:?ndarray?of?opencv
????????????An?HxW(x3)?matrix?of?img
????????Returns
????????-------
????????keypoints?:?List?of?cv2.KeyPoint
????????????using?keypoint.pt?can?see?(x,y)
????????descriptors:?List?of?descriptors[keypoints,?features]
????????????keypoints:?keypoints?which?a?descriptor?cannot?be?computed?are?removed
????????????features:?An?Nx32?ndarray?of?unit8?when?using?"orb"?method
????????"""
????????if?img.ndim?==?3:
????????????img?=?cv2.cvtColor(img,?cv2.COLOR_BGR2GRAY)
????????#?find?the?keypoints?with?ORB
????????keypoints?=?self.feature_extractor.detect(img,?None)
????????#?compute?the?descriptors?with?ORB
????????keypoints,?descriptors?=?self.feature_extractor.compute(img,?keypoints)
????????return?keypoints,?descriptors?
那么對于本質矩陣的估計和最小二乘法的應用,都可以直接利用已有的工具箱 opencv 和 numpy 搞定:
?def?GetFundamentalMat(self,?keypoints1,?descriptors1,?keypoints2,?descriptors2):????????"""Estimate?FunfamentalMatrix?using?BF?matcher?and?ransac
????????????[p2;1]^T?K^(-T)?E?K^(-1)?[p1;1]?=?0,?T?means?transpose,?K?means?the?intrinsic?matrix?of?camera
????????????F?=?K^(-T)?E?K^(-1)
????????Parameters
????????----------
????????keypoints?:?List?of?cv2.KeyPoint
????????????using?keypoint.pt?can?see?(x,y)
????????descriptor?:?ndarray
????????????An?Nx32?matrix?of?descriptors
????????Returns
????????-------
????????F:?ndarray
????????????A?3x3?Matrix?of?Fundamental?Matrix
????????mask:?ndarray
????????????A?Nx1?Matrix?of?those?inline?points
????????pts1:?List?of?cv2.KeyPoint
????????????keypoints?matched
????????pts2:?List?of?cv2.KeyPoint
????????????keypoints?matched
????????matches?:?List?of?matches
????????????distance?-?distance?of?two?points,
????????????queryIdx?-?query?image's?descriptor?id,?default?is?the?second?image
????????????trainIdx?-?train?image's?descriptor?id,?default?is?the?second?image
????????????imageIdx?-?train?image's?id,?default?is?0
????????"""
????????#?matching?points
????????matches?=?self.matcher.match(descriptors1,?descriptors2)
????????matches?=?sorted(matches,?key=lambda?x:?x.distance)
????????pts1?=?[]
????????pts2?=?[]
????????for?i,?match?in?enumerate(matches):
????????????if?i?>=?self.n_points:
????????????????break
????????????pts1.append(keypoints1[match.queryIdx].pt)
????????????pts2.append(keypoints2[match.trainIdx].pt)
????????pts1?=?np.int32(pts1)
????????pts2?=?np.int32(pts2)
????????matches?=?matches[:self.n_points]
????????##?Estimate?Fundamental?Matrix?by?ransac,?distance_threshold?=?1,?confidence_threshold?=?0.99
????????F,?mask?=?cv2.findFundamentalMat(pts1,?pts2,?cv2.FM_RANSAC,?1,?0.99)
????????return?F,?mask,?pts1,?pts2,?matches
????def?EstimateBox(self,?boxes,?F):
????????"""Estimate?box?in?target?image?by?Fundamental?Matrix
????????Parameters
????????----------
????????boxes?:?array?like
????????????A?Nx4?matrix?of?boxes?in?source?images?(x,y,w,h)
????????F?:?ndarray
????????????A?3x3?Fundamental?Matrix
????????Returns
????????-------
????????aligned_boxes:?ndarray
????????????A?Nx4?matrix?of?boxes?in?source?images?(x,y,w,h)
????????Method
????????-------
????????????L?=?||Bi^T?F?Ai||2?+?||(A2-A0)+(B2-B0)||2
????????????A?is?the?four?corner?of?box?in?source?image
????????????B?is?the?four?corner?of?aligned?box?in?target?image
????????????A0,B0:top?left?corner?of?box,?[x;y;1]
????????????A1,B1:top?right?corner?of?box
????????????A2,B2:bottom?left?corner?of?box
????????????A3,B3:bottom?right?corner?of?box
????????????the?height?and?width?of?boxes?and?aligned?boxes?are?assumed?to?be?same
????????????we?can?use?greedy?strategy:?make?M?=?A^T?F^T
????????????then:
????????????????M11???x1???+???M12??y1???+?M13?=?0
????????????????M21?(x1+w)?+???M22??y1???+?M23?=?0
????????????????M31???x1???+???M32?y1+h??+?M33?=?0
????????????????M41?(x1+w)?+??M42?(y1+h)?+?M43?=?0
????????????=>
????????????????M[:2][x;y]?+?M[:3]+[0;M21w;M32h;M41w+M42h]?=?0?->Ax?=?b
????????????????x?=?(pseudo?inverse?of?A?)b
????????"""
????????boxes?=?np.asarray(boxes)
????????if?boxes.ndim?==?1:
????????????boxes?=?boxes[np.newaxis,?:]
????????aligned_boxes?=?np.zeros(boxes.shape)
????????for?i,?bbox?in?enumerate(boxes):
????????????w?=?bbox[2]
????????????h?=?bbox[3]
????????????AT?=?np.array([[bbox[0]???,?bbox[1]????,?1],
??????????????????????????[bbox[0]?+?w,?bbox[1]????,?1],
??????????????????????????[bbox[0]????,?bbox[1]?+?h,?1],
??????????????????????????[bbox[0]?+?w,?bbox[1]?+?h,?1]])
????????????M?=?AT?@?F.T
????????????b?=?-M[:,?2]?-?np.array([0,?M[1][0]*w,?M[2][1]*h,?M[3][0]*w+M[3][1]*h])
????????????aligned_tl?=?np.linalg.pinv(M[:,:2])?@?b
????????????aligned_boxes[i,?0]?=?aligned_tl[0]
????????????aligned_boxes[i,?1]?=?aligned_tl[1]
????????????aligned_boxes[i,?2]?=?w
????????????aligned_boxes[i,?3]?=?h
????????return?aligned_boxes.astype(np.int32)
具體效果如下:
上面極線的法線也正好是車載相機的方向所在,可以看到第一章的示例問題被很大緩解了:ECC3.1 原理介紹第二章所介紹的對極幾何方法,由于我們只是根據二維信息對三維信息的估計,所以也會存在誤差。這一張我們也講一個簡單有效的方案,那就是“仿射變換”。當然,并不是我們所理解的那種仿射變換,具體細節我將慢慢介紹。
第一次看到 ECC 算法,我是在 ICCV 2019 的 Tracktor++[3]中,不過作者只是一筆帶過,沒有提及如何實現。ECC 算法全名是增強相關系數算法 [2],來自于 PAMI2008 的一篇論文,這個算法適用于圖像配準任務的:也就是對于兩張內容差異小,但是存在光照、尺度、顏色、平移等變換影響的圖像,將二者對齊。ECC 算法本質是一個目標函數:
當然這只是一個原始形式,在求解過程中有所調整,我就不細講這里的理論了。可以注意到的是 y=warp(x) 這個函數,所以這個算法假設兩幀圖像之間存在某種變換,不一定是仿射變換,可能有以下幾種:
其中最后一種透視變換的矩陣形式是:
前三種變換則不考慮最后一行信息,即 2x3 的矩陣形式。
3.2 實驗分析opencv 中正好提供了 ECC 相關的功能函數,這里我們只需要再次封裝,以方便多目標跟蹤。可以知道的是 ECC 算法的核心在于變換矩陣的求解:
def?ECC(src,?dst,?warp_mode?=?cv2.MOTION_EUCLIDEAN,?eps?=?1e-5,????????max_iter?=?100,?scale?=?None,?align?=?False):
????"""Compute?the?warp?matrix?from?src?to?dst.
????Parameters
????----------
????src?:?ndarray
????????An?NxM?matrix?of?source?img(BGR?or?Gray),?it?must?be?the?same?format?as?dst.
????dst?:?ndarray
????????An?NxM?matrix?of?target?img(BGR?or?Gray).
????warp_mode:?flags?of?opencv
????????translation:?cv2.MOTION_TRANSLATION
????????rotated?and?shifted:?cv2.MOTION_EUCLIDEAN
????????affine(shift,rotated,shear):?cv2.MOTION_AFFINE
????????homography(3d):?cv2.MOTION_HOMOGRAPHY
????eps:?float
????????the?threshold?of?the?increment?in?the?correlation?coefficient?between?two?iterations
????max_iter:?int
????????the?number?of?iterations.
????scale:?float?or?[int,?int]
????????scale_ratio:?float
????????scale_size:?[W,?H]
????align:?bool
????????whether?to?warp?affine?or?perspective?transforms?to?the?source?image
????Returns
????-------
????warp?matrix?:?ndarray
????????Returns?the?warp?matrix?from?src?to?dst.
????????if?motion?model?is?homography,?the?warp?matrix?will?be?3x3,?otherwise?2x3
????src_aligned:?ndarray
????????aligned?source?image?of?gray
????"""
????assert?src.shape?==?dst.shape,?"the?source?image?must?be?the?same?format?to?the?target?image!"
????#?BGR2GRAY
????if?src.ndim?==?3:
????????#?Convert?images?to?grayscale
????????src?=?cv2.cvtColor(src,?cv2.COLOR_BGR2GRAY)
????????dst?=?cv2.cvtColor(dst,?cv2.COLOR_BGR2GRAY)
????#?make?the?imgs?smaller?to?speed?up
????if?scale?is?not?None:
????????if?isinstance(scale,?float)?or?isinstance(scale,?int):
????????????if?scale?!=?1:
????????????????src_r?=?cv2.resize(src,?(0,?0),?fx?=?scale,?fy?=?scale,interpolation?=??cv2.INTER_LINEAR)
????????????????dst_r?=?cv2.resize(dst,?(0,?0),?fx?=?scale,?fy?=?scale,interpolation?=??cv2.INTER_LINEAR)
????????????????scale?=?[scale,?scale]
????????????else:
????????????????src_r,?dst_r?=?src,?dst
????????????????scale?=?None
????????else:
????????????if?scale[0]?!=?src.shape[1]?and?scale[1]?!=?src.shape[0]:
????????????????src_r?=?cv2.resize(src,?(scale[0],?scale[1]),?interpolation?=?cv2.INTER_LINEAR)
????????????????dst_r?=?cv2.resize(dst,?(scale[0],?scale[1]),?interpolation=cv2.INTER_LINEAR)
????????????????scale?=?[scale[0]?/?src.shape[1],?scale[1]?/?src.shape[0]]
????????????else:
????????????????src_r,?dst_r?=?src,?dst
????????????????scale?=?None
????else:
????????src_r,?dst_r?=?src,?dst
????#?Define?2x3?or?3x3?matrices?and?initialize?the?matrix?to?identity
????if?warp_mode?==?cv2.MOTION_HOMOGRAPHY?:
????????warp_matrix?=?np.eye(3,?3,?dtype=np.float32)
????else?:
????????warp_matrix?=?np.eye(2,?3,?dtype=np.float32)
????#?Define?termination?criteria
????criteria?=?(cv2.TERM_CRITERIA_EPS?|?cv2.TERM_CRITERIA_COUNT,?max_iter,?eps)
????#?Run?the?ECC?algorithm.?The?results?are?stored?in?warp_matrix.
????(cc,?warp_matrix)?=?cv2.findTransformECC?(src_r,?dst_r,?warp_matrix,?warp_mode,?criteria,?None,?1)
????if?scale?is?not?None:
????????warp_matrix[0,?2]?=?warp_matrix[0,?2]?/?scale[0]
????????warp_matrix[1,?2]?=?warp_matrix[1,?2]?/?scale[1]
????if?align:
????????sz?=?src.shape
????????if?warp_mode?==?cv2.MOTION_HOMOGRAPHY:
????????????#?Use?warpPerspective?for?Homography
????????????src_aligned?=?cv2.warpPerspective(src,?warp_matrix,?(sz[1],sz[0]),?flags=cv2.INTER_LINEAR)
????????else?:
????????????#?Use?warpAffine?for?Translation,?Euclidean?and?Affine
????????????src_aligned?=?cv2.warpAffine(src,?warp_matrix,?(sz[1],sz[0]),?flags=cv2.INTER_LINEAR)
????????return?warp_matrix,?src_aligned
????else:
????????return?warp_matrix,?None這里面我添加了一個技巧,由于 ECC 算法針對的是兩幅圖,所以圖像的尺寸對于算法求解速度的影響很大。因此這里我根據變換矩陣的形式,設計了一種可以根據尺度放縮自動調節的簡易算法。效果如下:效果也很好,值得一提的是,ECC 算法只需要大約幾毫秒的時間,但是由于它的求解效率跟變換的難度相關,所以間隔越久越慢,而對極幾何的方法效率比較穩定,不過就很慢了。
其他近似方案
4.1 光流上面我介紹的都是近兩年關于相機運動的針對性解決方案,那么實際上在有一些算法模型中,如果場景變化不劇烈,并不特別需要用到運動模型。比如基于光流法的多目標跟蹤算法,這里眾所周知的就是 ICCV2015 的 NOMT [5]算法。作者用的是一種簡化版的快速光流法,那么更形象的可以看今年剛出的一篇論文《Multiple Object Tracking by Flowing and Fusing》,具體我就不說了,就是簡單的在 Tracktor++ 框架上加了一個光流預測分支:可以看到的是,光流也是在捕捉相鄰幀中相似的像素信息,這一點跟第二章中提出的兩種相機運動模型有點類似,所以不需要顯式使用相機運動模型。4.2 SOT而基于 SOT 的方法,無論是使用傳統的相關濾波算法還是使用 Siamese 類深度學習框架,都會在上一幀目標周圍 1.5~2.5 倍區域搜索下一幀的目標,這里面會顯式或者隱式用到特征的比對。只不過不同于上面的像素比對,這里是更加高層的特征比對。參考資料
[1] Wang G, Wang Y, Zhang H, et al. Exploit the connectivity: Multi-object tracking with trackletnet[C]. in: Proceedings of the 27th ACM International Conference on Multimedia. 2019. 482-490.[2] Evangelidis G D, Psarakis E Z. Parametric image alignment using enhanced correlation coefficient maximization[J]. IEEE transactions on pattern analysis and machine intelligence, 2008, 30(10): 1858-1865.[3] Bergmann P, Meinhardt T, Leal-Taixe L. Tracking without bells and whistles[C]. in: Proceedings of the IEEE International Conference on Computer Vision. 2019. 941-951.[4] Choi W. Near-online multi-target tracking with aggregated local flow descriptor[C]. in: Proceedings of the IEEE international conference on computer vision. 2015. 3029-3037.[5] Feng W, Hu Z, Wu W, et al. Multi-object tracking with multiple cues and switcher-aware classification[J]. arXiv preprint arXiv:1901.06129, 2019.[6]?https://blog.csdn.net/ssw_1990/article/details/53355572點擊以下標題查看更多往期內容:?
對抗訓練淺談:意義、方法和思考
一文讀懂領域遷移與領域適應的常見方法
借助注意力機制實現特征軟閾值化
CVPR 2020 三篇有趣的論文解讀
NAS+目標檢測:AI設計的目標檢測模型
圖神經網絡時代的深度聚類
?
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
關于PaperWeekly
PaperWeekly 是一個推薦、解讀、討論、報道人工智能前沿論文成果的學術平臺。如果你研究或從事 AI 領域,歡迎在公眾號后臺點擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的opencv运动目标跟踪预测_浅谈多目标跟踪中的相机运动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 实体类包含list 怎么取值_
- 下一篇: lex编译dos命令_微软新的命令行工具