OpenCV-Python系列之稠密光流
之前我們討論過LK演算法,其本質來講屬於稀疏光流演算法,我們在OpenCV中所用的函式為:calcOpticalFlowPyrLK。這次來介紹一種稠密光流演算法(即影象上所有畫素點的光流都計算出來),它由Gunnar Farneback 所提出。
光流是由物體或相機的運動引起的影象物件在兩個連續幀之間的視在運動模式.光流方法計算在t和 t+Δt時刻拍攝的兩個影象幀之間的每個畫素的運動位置。這些方法被稱為差分,因為它們基於影象訊號的區域性泰勒級數近似; 也就是說,它們使用關於空間和時間座標的偏導數。
和稀疏光流相比,稠密光流不僅僅是選取影象中的某些特徵點(一般用角點)進行計算;而是對影象進行逐點匹配,計算所有點的偏移量,得到光流場,從而進行配準.因此其計算量會顯著大於稀疏光流,但效果一般優於稀疏光流。
Farneback演算法原理
非常簡單,總共兩步。
1、將影象視為二維訊號的函式(輸出影象是灰度影象),因變數是二維座標位置 ,並且利用二次多項式對於影象進行近似建模的話,會得到:
其中,
· A 是一個2×2的對稱矩陣(是通過畫素的鄰域資訊的最小二乘加權擬合得到的,權重係數與鄰域的畫素大小和位置有關)
· b是一個2×1的矩陣向量;
· c為標量;
因此係數化之後,以上公式等號右側可以寫為:
2、如果將原有(笛卡爾座標系)影象的二維訊號空間,轉換到以作為基函式的空間,則表示影象需要一個六維向量作為係數,代入不同畫素點的位置 x,y求出不同畫素點的灰度值。
OpenCV中的使用
函式API:
def calcOpticalFlowFarneback(prev, next, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags)
使用Gunnar Farneback演算法計算密集光流。
相關引數:
prev 輸入前一幀影象(8位單通道);
next 輸入後一幀影象(與prev大小和型別相同);
flow 計算的流量影象具有與prev相同的大小併為CV_32FC2型別;
pyr_scale 指定影象比例(\ <1)為每個影象構建金字塔; pyr_scale = 0.5意味著一個古典金字塔,其中每個下一層比前一層小兩倍。
levels 金字塔層數包括初始影象; levels = 1意味著不會建立額外的圖層,只會使用原始影象。
winsize 平均視窗大小;較大的值會增加演算法對影象噪聲的魯棒性,並可以檢測更快速的運動,但會產生更模糊的運動場。
iterations 每個金字塔等級上執行迭代演算法的迭代次數。用於在每個畫素中查詢多項式展開的畫素鄰域;
poly_n大小;較大的值意味著影象將近似於更光滑的表面,產生更穩健的演算法和更模糊的運動場,一般取poly_n = 5或7。
poly_sigma用於平滑導數的高斯的標準偏差,用作多項式展開的基礎;對於poly_n = 5,可以設定poly_sigma = 1.1,對於poly_n = 7,可以設定poly_sigma = 1.5;
flags 操作標誌,可取計算方法有:
OPTFLOW_USE_INITIAL_FLOW 使用輸入流作為初始流近似。
OPTFLOW_FARNEBACK_GAUSSIAN 使用Gaussian winsize×winsiz過濾器代替光流估計的相同大小的盒子過濾器;通常情況下,這個選項可以比使用箱式過濾器提供更精確的流量,代價是速度更低;通常,應將高斯視窗的勝利設定為更大的值以實現相同的穩健性水平。
我們同樣使用之前的視訊做實驗,來看程式碼:
def Farne(cap): # 獲取第一幀 ret, frame1 = cap.read() prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY) hsv = np.zeros_like(frame1) # 遍歷每一行的第1列 hsv[..., 1] = 255 while (1): ret, frame2 = cap.read() next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY) # 返回一個兩通道的光流向量,實際上是每個點的畫素位移值 flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0) # print(flow.shape) print(flow) # 笛卡爾座標轉換為極座標,獲得極軸和極角 mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1]) hsv[..., 0] = ang * 180 / np.pi / 2 hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX) rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) cv2.imshow('frame2', rgb) k = cv2.waitKey(30) & 0xff if k == 27 & k == 0xff: break prvs = next cap.release() cv2.destroyAllWindows()
來看部分演示的結果:
天道酬勤 循序漸進 技壓群雄