《OpenCV》——光流估计

发布于:2025-02-27 ⋅ 阅读:(30) ⋅ 点赞:(0)

什么是光流估计?

在这里插入图片描述

光流估计的前提?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

基本假设

  • 亮度恒定假设:目标像素点的亮度在相邻帧之间保持不变。这是光流计算的基础假设,基于此可以建立数学方程来求解光流。
  • 时间连续或运动平滑假设:相邻帧之间的时间间隔足够小,使得像素点的运动是连续的,不会发生突变;或者说相邻像素点的运动情况是相似的,具有平滑性。

经典算法

  • 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:
        break
    frame_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:
        break
    old_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 算法实现了视频中特征点的光流估计,并将光流轨迹绘制在视频帧上。它可以帮助我们观察视频中物体的运动情况。

  • 结果:
    在这里插入图片描述