视频处理之光流估计

引言:

        光流估计是计算图像序列中物体运动的方法之一。在计算机视觉和图像处理中,光流被用来估计图像中像素的运动方向和速度。它是通过比较两帧图像中相邻像素的亮度值来实现的。

那么会实现什么样子的功能呢?我们来看一下效果

        上图就是某视频中截取到的一帧画面,那么我们接下来看一下光流估计可以达到什么样子的效果

        我们可以看见上图中有很多的光点,以及出现了很多绿色的轨迹,这些绿色的轨迹就是视频中人物运动的轨迹。通过这些轨迹我们可以更好的识别图像中的运动物体,并对它们进行分割。通过检测物体的运动模式,可以更准确地对物体进行识别和跟踪。

那么我们接下来看一下如何通过代码实现

import cv2
import numpy as np

# 打开视频文件
cap = cv2.VideoCapture(r'C:\Users\35173\Desktop\test.avi')

# 获取第一帧
ret, first_frame = cap.read()
if not ret:
    print("Error: Could not read the first frame.")
    cap.release()
    exit()

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

第一部分:

导入必要的库,开始读取文件,视频文件用VideoCapture来进行读取,同时逐帧的读取视频的读取视频,如果读取不到就释放,这个时候要考虑是不是文件地址没选成功,或者视频损坏

# 将第一帧转换为灰度图像
prev_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
# 角点检测参数
feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
# Lucas-Kanade光流法参数
lk_params = dict(winSize=(15, 15), maxLevel=2,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 使用Shi-Tomasi角点检测器检测第一帧中的角点
prev_points = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
# 创建用于绘制轨迹的掩膜
mask = np.zeros_like(first_frame)

第二部分:

首先将第一帧转化为灰度图这一步很重要用于减少计算量,然后设置以下角点检测的参数。其中,各参数含义如下所示:

  • maxCorners: 最大角点数,表示算法尝试找到的角点的最大数量。
  • qualityLevel: 质量水平,一个介于0和1之间的值,用于评估角点的可靠性。较高的值意味着更严格的质量评估。
  • minDistance: 角点之间的最小欧氏距离,用于确保角点的分布。
  • blockSize: 用于提取角点的图像块的大小,影响角点检测的精度。

然后再设置一些光流检测的参数,各参数的含义如下:

  • winSize: 光流计算中使用的窗口大小。较大的窗口可以提供更平滑的光流场,但可能会降低精度。
  • maxLevel: 金字塔的最大层数,用于多尺度光流估计。较高的值可以提供更好的性能,但会增加计算量。
  • criteria: 终止条件,由三部分组成:
    • cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT: 表示当满足以下任一条件时停止迭代:达到指定的精度(EPS)或迭代次数(COUNT)。
    • 10: 迭代次数的阈值。
    • 0.03: 精度的阈值,即算法认为已经找到足够好的匹配时的误差阈值。

然后使用角点检测器来检测第一帧的角点,同时返回的prev_points是一个数组,包含了检测到的角点的坐标。然后创建了一个大小和第一帧相似的全零矩阵作为掩膜。

while True:
    ret, frame = cap.read()
    if not ret:
        print("Error: Could not read frame.")
        break

    # 将当前帧转换为灰度图像
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 计算光流
    next_points, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, gray, prev_points, None, **lk_params)

    # 选择好的跟踪点
    good_new = next_points[status == 1]
    good_old = prev_points[status == 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), (0, 255, 0), 2)
        frame = cv2.circle(frame, (a, b), 5, (0, 0, 255), -1)

    img = cv2.add(frame, mask)

    # 显示结果
    cv2.imshow('Optical Flow', img)

第三部分:

在逐帧的读取视频并转化为灰度图之后开始计算光流,

使用cv2.calcOpticalFlowPyrLK函数计算前一帧(prev_gray)和当前帧(gray)之间的光流。prev_points是前一帧中检测到的角点。该函数返回三个值:

  • next_points: 当前帧中角点的预测位置。
  • status: 一个布尔数组,表示每个角点是否成功跟踪。
  • err: 一个数组,包含每个角点跟踪的误差。

        根据status数组,选择成功跟踪的角点。statusTrue(即数值为1)表示角点被成功跟踪。紧接着遍历成功跟踪的角点,new是当前帧中角点的位置,old是前一帧中角点的位置。接着将角点坐标降维成一维之后从浮点数转换为整数,因为图像坐标必须是整数。然后使用cv2.line函数在掩膜mask上绘制从旧位置到新位置的绿色线条,以可视化角点的移动轨迹。同时在当前帧的角点上绘制新的圆圈,表示角点的新位置。将当前帧和掩膜合并之后显示它们的结果。

    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break

    # 更新前一帧和前一帧的角点
    prev_gray = gray.copy()
    prev_points = good_new.reshape(-1, 1, 2)

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

第四部分:

作为结尾部分,其中cv2.waitKey函数会等待用户按下一个键盘按键,参数30表示等待时间(毫秒)。如果30毫秒内没有按键被按下,函数返回-1,如果用户按下了Esc键(ASCII码为27),则k的值将等于27。如果检测到这个值,循环将通过break语句被终止,这意味着视频处理和光流估计将停止。同时更新光流估计中使用的前一帧和前一帧的角点。最后释放掉获取到的资源,并且关闭掉所有窗口,避免对系统造成不必要的负担。

完整代码:

import cv2
import numpy as np

# 打开视频文件
cap = cv2.VideoCapture(r'C:\Users\35173\Desktop\test.avi')

# 获取第一帧
ret, first_frame = cap.read()
if not ret:
    print("Error: Could not read the first frame.")
    cap.release()
    exit()

# 将第一帧转换为灰度图像
prev_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
# 角点检测参数
feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
# Lucas-Kanade光流法参数
lk_params = dict(winSize=(15, 15), maxLevel=2,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 使用Shi-Tomasi角点检测器检测第一帧中的角点
prev_points = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
# 创建用于绘制轨迹的掩膜
mask = np.zeros_like(first_frame)

while True:
    ret, frame = cap.read()
    if not ret:
        print("Error: Could not read frame.")
        break

    # 将当前帧转换为灰度图像
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 计算光流
    next_points, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, gray, prev_points, None, **lk_params)

    # 选择好的跟踪点
    good_new = next_points[status == 1]
    good_old = prev_points[status == 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), (0, 255, 0), 2)
        frame = cv2.circle(frame, (a, b), 5, (0, 0, 255), -1)

    img = cv2.add(frame, mask)

    # 显示结果
    cv2.imshow('Optical Flow', img)



    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break

    # 更新前一帧和前一帧的角点
    prev_gray = gray.copy()
    prev_points = good_new.reshape(-1, 1, 2)

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

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值