【OpenCV】计算视频的光流并跟踪物体calcOpticalFlowPyrLK

 一、介绍

        计算光流可以使用OpenCV的calcOpticalFlowPyrLK方法,cv2.calcOpticalFlowPyrLK是OpenCV库中的一个函数,用于计算稀疏光流。它实现的是Lucas-Kanade方法,这是一种常用的光流计算方法。

        光流是图像中物体运动的近似表示,它描述了图像中每个像素点在连续两帧之间的移动。Lucas-Kanade方法假设图像中的一个小邻域内的所有像素在运动上是一致的(即具有相同的光流)。

二、原理

        以下是cv2.calcOpticalFlowPyrLK的基本工作原理:

        1. 选择特征点:在第一帧图像中选择一些特征点。这些特征点通常是角点,因为角点具有在所有方向上的变化,更容易被跟踪。

        2. 窗口:对于每一个特征点,定义一个周围的窗口。

        3. 光流:对于每一个窗口,假设所有像素具有相同的光流,然后通过最小化该窗口内像素在第一帧和第二帧之间的亮度差异,来求解光流。

        4. 金字塔:实际上,由于运动可能在不同的尺度上发生,所以cv2.calcOpticalFlowPyrLK实际上在图像的多个尺度(金字塔的每一层)上重复以上步骤。这就是所谓的金字塔Lucas-Kanade方法。

        需要注意的是,由于Lucas-Kanade方法假设一个窗口内的所有像素具有相同的光流,所以它只能处理小的和平滑的运动。对于大的或复杂的运动,需要使用更复杂的方法,如Horn-Schunck方法 

三、代码实现

        下面这段代码实现了识别特征点,并跟踪这些特征点,画出每个点的移动轨迹

import numpy as np
import cv2

src_path = r'input.mp4'
target_path = r'output.mp4'

fps = 25
cap = cv2.VideoCapture(src_path)

# ShiTomasi corner detection的参数
feature_params = dict(maxCorners=300,
                      qualityLevel=0.3,
                      minDistance=7,
                      blockSize=7)
# 光流法参数
# maxLevel 未使用的图像金字塔层数
lk_params = dict(winSize=(10, 10),
                 maxLevel=2,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

ret, old_frame = cap.read()                             # 取出视频的第一帧
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)  # 灰度化
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
mask = np.zeros_like(old_frame)                         # 为绘制创建掩码图片
h, w, _ = old_frame.shape
target_video = cv2.VideoWriter(target_path,cv2.VideoWriter_fourcc(*"H264"), fps, (w, h))
while True:
    res, frame = cap.read()
    if not res:
        break
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 计算光流以获取点的新位置
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
    # 选择good points
    good_new = p1[st == 1]
    good_old = p0[st == 1]
    good_new1 = good_new.copy()
    for i, (new, old) in enumerate(zip(good_new, good_old)):
        a, b = new.ravel()
        c, d = old.ravel()
        mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), (0,255,0), 3)
        frame = cv2.circle(frame, (int(a), int(b)), 5, (0,255,0), -1)
    img = cv2.add(frame, mask)
    target_video.write(img)
    old_gray = frame_gray.copy()
    p0 = good_new1.reshape(-1, 1, 2)

target_video.release()
cv2.destroyAllWindows()
cap.release()

四、参数解释

1.calcOpticalFlowPyrLK输入参数解释

1. prevImg:上一帧输入图像。

2. nextImg:本轮输入图像。

3. prevPts:从上一帧图像中提取的特征点(2D点向量)。

4. nextPts:从本轮图像中提取的点(2D点向量),包含计算得到的新位置。

5. winSize:每一级金字塔的搜索窗口大小。默认值是Size(21,21)。

        图像金字塔是通过对原始图像进行连续的下采样操作得到的一系列图像,每一层的图像都比上一层的图像小。这就像一个金字塔,底部是原始的大图像,顶部是最小的图像。图像金字塔被用于处理不同尺度的运动。这是因为在实际的图像中,物体的运动可能在不同的尺度上发生。例如,远离摄像头的物体在图像中的运动可能比较小,而靠近摄像头的物体在图像中的运动可能比较大。通过在图像金字塔的每一层上计算光流,可以处理这种不同尺度的运动。

6. maxLevel:基于0的最大金字塔等级数;如果设置为0,则不使用金字塔(单级),如果设置为1,则使用两级等。默认值是3。

        在构建图像金字塔时,从原始图像开始,每一层图像的尺寸都是上一层图像尺寸的一半,maxLevel就是这个金字塔的最大层数。maxLevel=0表示只使用原始图像,maxLevel=1表示原始图像和它的一半尺寸的图像,maxLevel=2表示原始图像、一半尺寸的图像和四分之一尺寸的图像,以此类推。

7. criteria:criteria是一个重要的参数,需要详细说一下,该参数是一个指定迭代搜索算法的终止准则的元组。它包含三个元素:
        (1) cv2.TERM_CRITERIA_EPS 或 cv2.TERM_CRITERIA_COUNT 或两者的组合。这些是终止准则的类型:
- cv2.TERM_CRITERIA_EPS:达到指定的精度epsilon时,迭代就会停止。
- cv2.TERM_CRITERIA_COUNT:达到指定的最大迭代次数时,迭代就会停止。
- cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT:当任一上述条件满足时,迭代就会停止。
        (2) 最大迭代次数。在这个例子中,最大迭代次数是10。
        (3)epsilon,即所需的精度。在这个例子中,epsilon是0.03。
例如,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)意味着迭代将在达到10次迭代或误差精度达到0.03时停止,以先到达的条件为准 

8. flags:操作标志。可以设置为

        (1)0:没有特殊行为。

        (2)cv2.OPTFLOW_USE_INITIAL_FLOW:如果设置了这个标志,那么函数会使用nextPts参数中的点作为初始近似值,然后进行优化。否则,它会直接使用prevPts参数中的点作为初始值。
         (3)cv2.OPTFLOW_LK_GET_MIN_EIGENVALS:如果设置了这个标志,那么函数会计算每个点的最小特征值并将它们存储在err参数中。

        0,cv2.OPTFLOW_USE_INITIAL_FLOW或cv2.OPTFLOW_LK_GET_MIN_EIGENVALS。

        例如,如果你想使用nextPts中的点作为初始近似值,你可以这样调用函数:

p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, flags=cv2.OPTFLOW_USE_INITIAL_FLOW, **lk_params)

9. minEigThreshold:最小特征值的阈值。默认值是1e-4。

这个函数的主要目的是使用Lucas-Kanade方法在两个图像之间跟踪一些特征点(prevPts)。这些特征点的新位置被存储在nextPts中。如果一个点不能被跟踪(例如,因为它走出了图像),那么status向量的相应元素就会被设置为0 

2.calcOpticalFlowPyrLK输出参数解释

1. nextPts:这是一个数组,包含了在下一帧图像中找到的输入特征点的新位置。

2. status:这是一个数组,和输入的特征点数组大小相同。如果status[i]为1,表示找到了第i个特征点的新位置,如果为0,表示没有找到。

3. err:这是一个数组,和输入的特征点数组大小相同。err[i]表示第i个特征点的新位置和原位置之间的误差,这个误差是基于窗口大小的。

这三个输出参数可以用于理解和评估光流的计算结果。例如,你可以检查status数组来看哪些特征点在下一帧图像中被成功找到,或者查看err数组来评估光流的计算精度 

        calcOpticalFlowPyrLK的用法就简单介绍到这,关注不迷路(#^.^#)

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要测量视频物体的位移,可以使用OpenCV中的运动估计技术。运动估计是一种计算图像序列中物体在时间上的位移的技术。 以下是使用OpenCV进行运动估计的一般步骤: 1. 读取视频帧并将它们转换为灰度图像。 2. 选择一个运动估计算法,例如Lucas-Kanade光流算法或基于区域的相位相关算法。 3. 对于每个相邻的帧,使用所选的算法进行运动估计。这将为每个像素提供一个位移向量。 4. 将位移向量转换为物体的位移,例如通过计算每个像素的平均位移。 5. 可以使用所得到的物体位移来跟踪物体的运动,例如绘制其轨迹或计算其速度。 下面是一个简单的示例代码,使用Lucas-Kanade光流算法对视频中的物体进行运动估计: ``` import cv2 # 读取视频 cap = cv2.VideoCapture('video.mp4') # 创建Lucas-Kanade光流对象 lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) # 读取第一帧 ret, frame1 = cap.read() prev_gray = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY) # 初始化物体的位置 prev_points = cv2.goodFeaturesToTrack(prev_gray, 100, 0.3, 7) # 循环处理每一帧 while True: # 读取下一帧 ret, frame2 = cap.read() if not ret: break next_gray = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY) # 计算光流 next_points, status, error = cv2.calcOpticalFlowPyrLK(prev_gray, next_gray, prev_points, None, **lk_params) # 计算物体的位移 dx = next_points[:, 0, 0] - prev_points[:, 0, 0] dy = next_points[:, 0, 1] - prev_points[:, 0, 1] mean_dx = dx.mean() mean_dy = dy.mean() # 绘制物体轨迹 for i in range(len(prev_points)): x1, y1 = prev_points[i, 0, :] x2, y2 = next_points[i, 0, :] cv2.line(frame2, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) # 更新物体位置和图像 prev_gray = next_gray.copy() prev_points = next_points.copy() cv2.imshow('frame', frame2) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows() ``` 在这个示例中,我们使用Lucas-Kanade光流算法计算每帧之间的光流计算物体的位移并绘制物体的轨迹。注意,这个示例只是一个简单的演示,实际的运动估计应该根据具体情况进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小殊小殊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值