Bootstrap

OpenCv—光流估计的代码实现

目录

一、什么是光流?

二、代码示例以及详细注释

三、代码整体

四、运行结果 


光流估计广泛应用于计算机视觉中的动态分析。它可以用于跟踪物体的运动,理解视频中的运动模式,以及其他各种计算机视觉任务。

一、什么是光流?

光流是一个描述相邻图像中像素运动的技术。它通过分析相邻帧之间的像素强度变化来估计物体或场景的运动。光流的基本思想是,如果两帧图像中的某个像素点的强度发生了变化,那么这个像素点可能是由于物体的运动引起的。光流估计的目标是找到每个像素点的位移矢量,以表示其运动方向和速度。

详细的原理可见我的另一篇博客:opencv视觉-光流估计

二、代码示例以及详细注释

1、首先导入必要的库,打开视频文件,准备一些参数,并在第一帧图像上执行角点检测以获取初始角点位置。

import numpy as np
import cv2

# 打开视频文件
cap = cv2.VideoCapture('test.avi')

# 生成一些随机颜色,用于绘制光流轨迹
color = np.random.randint(0, 255, (100, 3))

# 读取第一帧
ret, old_frame = cap.read()

# 将第一帧转换为灰度图像
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)

# 设置角点检测的参数
feature_params = dict(maxCorners=100,  # 最多检测100个角点
                      qualityLevel=0.3,  # 角点的质量因子
                      minDistance=7)    # 角点之间的最小距离

# 使用角点检测函数找到第一帧中的角点
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)

# 创建一个与第一帧相同大小的图像作为光流轨迹的可视化效果
mask = np.zeros_like(old_frame)

# 设置Lucas-Kanade光流算法的参数
lk_params = dict(winSize=(15, 15),  # 搜索窗口大小
                 maxLevel=2)        # 金字塔层数

2、 这部分代码包含了一个循环,用于处理视频中的每一帧。它计算当前帧和上一帧之间的光流,选择跟踪成功的角点,并在图像上绘制光流轨迹。你可以按下ESC键来退出循环。

while True:
    ret, frame = cap.read()
    
    # 将当前帧转换为灰度图像
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 使用Lucas-Kanade算法计算光流
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
#p1 表示特征点的新位置,st 提供了每个特征点的状态,而 err 提供了光流估计的误差度量

    # 选择跟踪成功的角点
    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()
        a, b, c, d = int(a), int(b), int(c), int(d)

        # 在mask图像上绘制光流轨迹
        mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)

    # 将光流轨迹添加到当前帧
    img = cv2.add(frame, mask)

    # 显示带有光流轨迹的图像
    cv2.imshow('frame', img)
    
    # 检测键盘输入
    k = cv2.waitKey(150) & 0xff
    if k == 27:  # 如果按下ESC键,退出循环
        break

    # 更新旧帧和旧角点
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1, 1, 2)

# 释放视频捕获对象和关闭所有窗口
cv2.destroyAllWindows()
cap.release()

 三、代码整体

import numpy as np
import cv2

cap = cv2.VideoCapture('test.avi')
color = np.random.randint(0, 255, (100, 3))
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
feature_params = dict(maxCorners=100,
                      qualityLevel=0.3,
                      minDistance=7)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
mask = np.zeros_like(old_frame)
lk_params = dict(winSize=(15, 15),
                 maxLevel=2)
while (True):
    ret, frame = cap.read()
    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]

    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.imshow('frame', img)
    k = cv2.waitKey(150) & 0xff
    if k == 27:
        break

    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1, 1, 2)

cv2.destroyAllWindows()
cap.release()

四、运行结果 

通过观看视频可得出该方法的缺点的是:

  1. 对遮挡敏感: 当物体被其他物体遮挡时,这种方法容易失效,因为它无法处理物体的遮挡。

  2. 跟踪物体局限:只能跟踪到视频开始第一帧中就运动的物体,后续出现的运动物体无法跟踪。

  3. 有积累误差: 由于它是一种基于局部区域的方法,光流估计可能在时间上积累误差,特别是在长时间序列中。

;