光流法的基本概念:
計算光流
:使用 OpenCV 的
cv2.calcOpticalFlowFarneback()
或
cv2.calcOpticalFlowPyrLK()
來追蹤特徵點的移動。
分析運動方向
:
若視覺流向
放射狀擴散(diverging)
,表示有物體接近。
若視覺流向
集中收斂(converging)
,表示場景遠離。
距離估算
:
大範圍、高速度的光流變化,代表障礙物接近。
可透過光流向量的長度來估算相對距離(向量越長,代表物體越近)。
# 讀取第一幀並轉成灰階
ret, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
while True:
# 讀取新影像
ret, frame = cap.read()
if not ret:
break
# 轉成灰階
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 計算光流
flow = cv2.calcOpticalFlowFarneback(prev_gray, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
# 計算光流向量的大小
magnitude, angle = cv2.cartToPolar(flow[..., 0], flow[..., 1])
# 繪製光流
hsv = np.zeros_like(frame)
hsv[..., 1] = 255 # 設定飽和度
hsv[..., 0] = angle * 180 / np.pi / 2 # 色相(H)表示方向
hsv[..., 2] = cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX) # 亮度(V)表示速度
flow_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
# 顯示結果
cv2.imshow("Optical Flow", flow_img)
# 計算障礙物接近的程度
avg_magnitude = np.mean(magnitude)
if avg_magnitude > 2.0: # 設定閾值(根據實驗調整)
cv2.putText(frame, "Obstacle detected!", (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.imshow("Frame", frame)
# 更新前一幀
prev_gray = gray.copy()
# 按 'q' 退出
if cv2.waitKey(30) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
優化與進階應用:
使用金字塔 Lucas-Kanade(PyrLK)方法
PyrLK 只追蹤特徵點,比 Farneback 更適合即時應用。
可以用
cv2.goodFeaturesToTrack()
找出畫面中有代表性的特徵點,然後用
cv2.calcOpticalFlowPyrLK()
來追蹤這些點的移動。
進一步距離估算
可以將光流向量的長度與攝影機焦距、視角對應起來,計算相對距離。
若有已知尺寸的物體出現在畫面中,可以用三角測量法來推算實際距離。