什么是光流估計?
光流估計的前提?
基本假設
- 亮度恒定假設:目標像素點的亮度在相鄰幀之間保持不變。這是光流計算的基礎假設,基于此可以建立數學方程來求解光流。
- 時間連續或運動平滑假設:相鄰幀之間的時間間隔足夠小,使得像素點的運動是連續的,不會發生突變;或者說相鄰像素點的運動情況是相似的,具有平滑性。
經典算法
- Lucas-Kanade 光流算法:該算法基于局部平滑假設,通過在一個小的窗口內對多個像素點進行約束,求解光流方程。它是一種基于梯度的方法,計算效率較高,常用于實時性要求較高的應用中。
- Horn-Schunck 光流算法:是一種全局的光流估計算法,在亮度恒定和光滑性約束的基礎上,通過最小化一個能量函數來求解光流。它考慮了整幅圖像的信息,能夠得到較為平滑的光流場,但計算量相對較大。
- 基于深度學習的光流估計算法:近年來,隨著深度學習的發展,基于卷積神經網絡(CNN)的光流估計算法取得了很好的效果。例如 FlowNet、PWC-Net 等,這些算法通過大量的訓練數據學習圖像之間的運動模式,能夠處理復雜的場景和運動情況,并且在精度和速度上都有了很大的提升。
實例
對視頻中的人物走動軌跡進行光流追蹤處理。
實現了基于 Lucas - Kanade 算法的稀疏光流估計,用于處理視頻中的運動跟蹤。
- 導入必要的庫
import numpy as np
import cv2
導入numpy庫用于數值計算,cv2是 OpenCV 庫,用于計算機視覺任務
- 打開視頻文件并初始化顏色
cap = cv2.VideoCapture('test.avi')
color = np.random.randint(0, 255, (100, 3))
cv2.VideoCapture(‘test.avi’):打開名為test.avi的視頻文件。
np.random.randint(0, 255, (100, 3)):生成 100 個隨機的 RGB 顏色,用于后續繪制光流軌跡。
- 讀取第一幀并轉換為灰度圖
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
cap.read():讀取視頻的第一幀,ret是一個布爾值,表示是否成功讀取幀,old_frame是讀取的幀圖像。
cv2.cvtColor():將彩色圖像轉換為灰度圖像,因為光流估計通常在灰度圖像上進行。
- 檢測特征點
feature_params = dict(maxCorners=100,qualityLevel=0.3,minDistance=7)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
cv2.goodFeaturesToTrack():使用 Shi - Tomasi 角點檢測算法檢測圖像中的特征點。
feature_params是一個字典,包含了角點檢測的參數:
- maxCorners:最多檢測的角點數量。
- qualityLevel:角點質量的閾值,只有質量高于該閾值的角點才會被保留。
- minDistance:相鄰角點之間的最小距離。
- 初始化掩碼圖像
mask = np.zeros_like(old_frame)
創建一個與第一幀圖像大小相同的全零掩碼圖像,用于繪制光流軌跡。
- 設置 Lucas - Kanade 光流算法的參數
lk_params = dict(winSize=(15, 15),maxLevel=2)
winSize:搜索窗口的大小,用于在計算光流時對每個像素點周圍的區域進行分析。
maxLevel:金字塔的最大層數,用于處理大位移的光流。
- 循環處理視頻幀
while True:ret, frame = cap.read()if not ret:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)good_new = p1[st == 1]good_old = p0[st == 1]
cap.read():讀取視頻的下一幀。
cv2.calcOpticalFlowPyrLK():使用金字塔 Lucas - Kanade 算法計算光流。
- old_gray:前一幀的灰度圖像。
- frame_gray:當前幀的灰度圖像。
- p0:前一幀檢測到的特征點。
- p1:當前幀中對應的特征點位置。
- st:狀態數組,用于表示每個特征點是否被成功跟蹤,st == 1表示成功跟蹤。
- err:每個特征點的跟蹤誤差。
- 繪制光流軌跡
for i, (new, old) in enumerate(zip(good_new, good_old)):a, b = new.ravel()c, d = old.ravel()a, b, c, d = int(a), int(b), int(c), int(d)mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)img = cv2.add(frame, mask)
cv2.line():在掩碼圖像上繪制從舊特征點到新特征點的線段。
cv2.add():將掩碼圖像與當前幀圖像疊加,得到帶有光流軌跡的圖像。
- 顯示結果并更新幀信息
cv2.imshow('mask', mask)cv2.imshow('frame', img)k = cv2.waitKey(150)if k == 27:breakold_gray = frame_gray.copy()p0 = good_new.reshape(-1, 1, 2)
cv2.imshow():顯示掩碼圖像和帶有光流軌跡的圖像。
cv2.waitKey(150):等待 150 毫秒,按下按鍵則返回按鍵的 ASCII 碼。
k == 27:如果按下 ESC 鍵(ASCII 碼為 27),則退出循環。
更新前一幀的灰度圖像和特征點信息,以便下一幀的光流計算。
- 釋放資源
cv2.destroyAllWindows()
cap.release()
cv2.destroyAllWindows():關閉所有打開的窗口。
cap.release():釋放視頻捕獲對象。
這段代碼通過 Lucas - Kanade 算法實現了視頻中特征點的光流估計,并將光流軌跡繪制在視頻幀上。它可以幫助我們觀察視頻中物體的運動情況。
- 結果: