1. 程式人生 > 其它 >光流演算法的理解與實現

光流演算法的理解與實現

    光流估計實際上是根據兩張連續的幀,去估計兩幀之間 pixel-wise(基於物體畫素)的光流。凡是有關估計相關的東西,卷積神經網路經過大量資料學習後都能擬合,只要有足夠的訓練資料和一個較好的網路結構。FlowNet 開闢了這個工作,同時也釋出了一個光流估計的資料集。

    光流追蹤法 分為 稀疏光流追蹤,與稠密光流追蹤。二者的區別就是在於,稀疏光流追蹤法,稀疏光流不對影象的每個畫素點進行逐點計算,它通常需要指定一組點進行跟蹤,這組點需要具有某種明顯的影象特徵,例如 Harris 角點或者其他特徵等,那麼跟蹤就會相對穩定和可靠。稀疏光流跟蹤的計算開銷比稠密光流跟蹤小得多。這樣計算量減少了很多。下面是稀疏光流法的一個python-opencv實現:

import numpy as np
import cv2

cap = cv2.VideoCapture('test1.avi')

# 得到視訊的高度
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
# 得到視訊的寬度
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
# 得到視訊的幀數
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
# 得到視訊的幀速
fps = cap.get(cv2.CAP_PROP_FPS)

# 定義寫入視訊的編碼格式
fourcc = cv2.VideoWriter_fourcc(*'
XVID') # *'XVID' or *'MJPG' 寫入視訊類的編碼格式 # 建立寫入視訊的類 temp_video = cv2.VideoWriter("LK_method.avi", fourcc, int(fps), (int(width), int(height))) # 定義寫入視訊的編碼格式 fourcc = cv2.VideoWriter_fourcc(*'XVID') # *'XVID' or *'MJPG' 寫入視訊類的編碼格式 # 建立寫入視訊的類 output_video = cv2.VideoWriter("LK_method_output.avi", fourcc, int(fps), (int(width), int(height)))
# 定義隨機顏色 color = np.random.randint(0,255,(100,3)) # 讀取第一幀 ret, old_frame = cap.read() old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) # 檢測第一幀影象的特徵點 p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, maxCorners=100,qualityLevel=0.01,minDistance=10,blockSize=3) # 稀疏光流跟蹤 while True: ret, frame = cap.read() if ret is False: break frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 計算光流 p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None,winSize=(31, 31), maxLevel=3, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 30, 0.01)) # 根據狀態選擇 good_new = p1[st == 1] good_old = p0[st == 1] # 繪製跟蹤線 for i, (new, old) in enumerate(zip(good_new,good_old)): a,b = new.ravel() c,d = old.ravel() frame = cv2.line(frame, (a,b),(c,d), color[i].tolist(), 2) frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1) temp_video.write(frame) cv2.imwrite("LK_output.jpg",frame) output_video.write(frame) # 更新 old_gray = frame_gray.copy() p0 = good_new.reshape(-1, 1, 2) cap.release()

可以看到圖中,即是視訊的一幀,對人的運動進行了一個跟蹤。根據影象會發現稀疏光流法的效果並不是很好。

下面介紹一下稠密光流法,主要是Farneback 光流演算法的實現。

import cv2
import numpy as np

# 繪製光流場影象
def drawOptFlowMap(flow,cflowmap,step,color):
    # 以 step 步長遍歷影象
    for x in range(0,flow.shape[1],step):
        for y in range(0,flow.shape[0],step):
            # 繪製跟蹤線
            cv2.line(cflowmap, (x,y), (int(x+flow[y][x][0]),int(y+flow[y][x][1])),color)
            cv2.circle(cflowmap,(x, y), 2, color, -1)
    return cflowmap
cap = cv2.VideoCapture('test.avi')
# 得到視訊的高度
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
# 得到視訊的寬度
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
# 得到視訊的幀數
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
# 得到視訊的幀速
fps = cap.get(cv2.CAP_PROP_FPS)

# 定義寫入視訊的編碼格式
fourcc = cv2.VideoWriter_fourcc(*'XVID')  #  *'XVID' or *'MJPG'  寫入視訊類的編碼格式
# 建立寫入視訊的類
output_video = cv2.VideoWriter("GF_method_output.avi", fourcc, int(fps), (int(width), int(height)))

# 讀取第一幀影象
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)

while(1):
    ret, frame2 = cap.read()
    if ret is False:
        break
    next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
    # 使用稠密光流演算法計算光流
    flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    # 通過得到的光流繪製圖像
    bgr = drawOptFlowMap(flow,frame2,16,(0,255,0))
    cv2.imwrite("GF_output.jpg",bgr)
    output_video.write(bgr)
    # 更新
    prvs = next
cap.release()

下面是實現稠密光流法的結果。

可以看出 稠密光流法,此圖片上有采取步長為 16 所繪製的光流場。因此,稠密光流是採取的全部的畫素點進行求解,並沒有求取影象特徵點的步驟。而且上圖中有人的區域都存在部分追蹤線,可以看出,稠密光流比稀疏光流更為穩定。

但是稠密光流法的計算量更大,所以速度也會降低很多。