Gunnar-Farneback算法
論文:Two-Frame Motion Estimation Based on Polynomial Expansion
會議:13th Scandinavian Conference on Image Analysis (SCIA 2003)
作者:Gunnar Farnebäck(Computer Vision Laboratory, Link¨oping University, SE-581 83 Link¨oping, Sweden)
備註:[email protected] http://www.isy.liu.se/cvl/
一、多項式展開
多項式展開的想法是把每個像素點的鄰域都用多項式來表示:
(1)
其中A是對稱矩陣,b是向量,c是常量。係數是根據鄰域內信號值的加權最小二乘法估計出來的。權重由兩個組成部分,稱爲確定性和適用性。確定性與鄰近區域的信號值耦合,而適用性則根據點在鄰域中的位置確定鄰域中點的相對權重。
二、位移估計
由於每個鄰域都可以用一個多項式來逼近,所以首先分析一個多項式經過平移後會發生什麼變化。考慮以下二次多項式:
(2)
整體位移d個單位後得到信號:
(3)
設二次多項式等式如下:
(4)
(5)
(6)
當是非奇異矩陣時,可以解出:
(7)
(8)
實際情況中,不存在整個信號表示爲一個多項式以及這兩個圖像信號的全局平移,但上述公式(7)仍然適用於實際信號。
這裏用局部多項式逼近方程(2)中的全局多項式。然後對兩個圖像做多項式展開,第一個圖像對應係數爲,和,第二個圖像同理。理想情況下,根據方程(4)應該有,但實際上應該求解近似值:
(9)
(10)
然後用代替前面的全局變量:
(11)
下面開始解決鄰域估計的問題。根據方程(11)可以對每個像素點進行運算,但考慮到龐大的運算量這樣做顯然不太現實。假設位移過程是緩慢進行的,那麼應當儘可能的縮小的鄰域的搜索範圍並找到符合方程(11)的:
(12)
這裏設爲像素點對應的權重函數,根據最小二乘法可得:
(13)
三、參數化位移場
爲了提高算法魯棒性,對於一些運動模式應該建立參數化模型。下面來構建帶有8個參數的2D運動模型:
(15)
上式可以表示成:
(16)
(17)
(18)
帶入到方程(12)中,即可得到加權最小二乘問題:
(19)
現在用去索引像素點鄰域中的座標,用最小二乘法求解可以得出:
(20)
這裏可以像前面一樣分別計算出和,然後用做加權平均求出位移。
四、利用先驗信息
目前爲止仍存在一個問題,就是兩個信號在相同座標下的局部多項式除了位移之外都是一致的。由於這裏的多項式展開是局部模型,所以會隨着在空間位移而發生變化,所以會引入(11)中的誤差,且這個誤差會隨着位移的增大而增大。所以這裏引入先驗位移信息,即比較第一個信號在的多項展開式和第二個信號在的多項展開式,其中爲先驗位移四捨五入得出的整型值,這樣就只需要計算出真實值和基於先驗位移的預估值。
替代上面的(9)和(10)爲:
(21)
(22)
其中,
(23)
五、迭代與多尺度位移估計
在算法中採用先驗位移場的優勢在於可以閉合循環並迭代。具備一個好的先驗估計意味着相對位移更小,這反過來又可以提高位移估計的精度。這裏考慮兩種不同的方法,迭代位移估計和多尺度位移估計。
在這兩種方法中,迭代估計一步的位移,作爲下一步的先驗位移。第一步中的先驗位移場通常被初始化爲零,除非有明確的信息。在第一種方法中,在所有迭代中使用相同的多項式展開係數,並且只需要計算一次。這存在一個問題是,如果第一次迭代位移(相對於先驗位移)過大,輸出的位移就不能期望得到改善,迭代也就失去了意義。通過在較粗的尺度上進行分析,可以減少位移過大的問題。這意味着我們對多項式展開具有高適用性。其結果是,該估計算法可以處理較大的位移,但同時精度卻降低了。
多尺度位移估計方法,從一個較粗的尺度開始,得到一個粗略但合理的位移估計,接着通過逐級細化尺度獲得越來越精確的估計。這樣的缺點是需要重新計算每個尺度的多項式展開係數,但是可以通過在尺度變換之間進行二次採樣來降低此成本。
Farneback光流法在UCSD異常數據集上的Demo
實驗環境:Win10 | Python 3.7.3 | OpenCV 4.1.0
實驗代碼:
def draw_flow(im, flow, step=16):
# 在間隔分開的像素採樣點處繪製光流
h, w = im.shape[:2]
y, x = mgrid[step/2:h:step, step/2:w:step].reshape(2, -1).astype(int)
fx, fy = flow[y, x].T
# 創建線的終點
lines = vstack([x, y, x+fx, y+fy]).T.reshape(-1, 2, 2)
lines = int32(lines)
# 創建圖像並繪製
vis = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
for (x1, y1), (x2, y2) in lines:
cv2.line(vis, (x1, y1), (x2, y2), (0, 255, 0), 1)
cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1)
return vis
# 省略N行代碼......
# 提取第一幀
first_rgbframe = cv2.imread(frame_path + frames[0], 1)
del frames[0]
prev_gray = cv2.cvtColor(first_rgbframe, cv2.COLOR_BGR2GRAY)
for frame_i in frames:
rgbframe = cv2.imread(frame_path + frame_i, 1)
gray = cv2.cvtColor(rgbframe, cv2.COLOR_BGR2GRAY)
# 計算流
flow = cv2.calcOpticalFlowFarneback(prev_gray, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
prev_gray = gray
drawImg = draw_flow(gray, flow)
cv2.imwrite(frameFB_path + frame_i, drawImg)
# 畫出流矢量
cv2.imshow('Optical flow', drawImg)
if cv2.waitKey(10) == 27:
break
time.sleep(0.1)
實驗結果:
UCSDped1\Test\Test014
UCSDped2\Test\Test005
上面分別是UCSD anomaly dataset中的兩個Test片段,可以看到人羣或者單車/車輛經過的地方,綠色點都會伴隨聯動。
附錄:
數據集鏈接:UCSD Anomaly Detection Dataset
OpenCV - calcOpticalFlowFarneback() 參數解釋:
prev:前一張8位單通道輸入圖像
next:後一張8位單通道輸入圖像
flow:計算得出的與輸入圖像大小一致且類型爲CV_32FC2的光流圖
pyr_scale:構建金字塔時圖像的縮放係數(<1);比如係數=0.5時是典型金字塔模型,沒下一個層級圖像爲上一級的一半大小
levels:金字塔的層數;層數爲1時表示沒有額外的層級直接輸入原圖
winsize:平均窗口大小;窗口越大對於圖像噪聲越不敏感,能捕獲到更多的快動作,但會產生更遺漏的動作場
iterations:每個金字塔層級中算法的迭代次數
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:使用高斯winsize×winsize濾波器代替相同大小的盒式濾波器進行光流估計;通常,此選項以較低的速度提供比盒式濾波器更精確的z流;通常,高斯窗口的winsize應設置爲更大的值以達到相同的魯棒性級別。
OpenCV官方原文解釋:點擊跳轉到官方文檔
參考資料:
1. Gunnar Farnebäck. Two-Frame Motion Estimation Based on Polynomial Expansion[C]// 13th Scandinavian Conference on Image Analysis (SCIA 2003). Springer-Verlag, 2003.
2. OpenCV官方文檔