OpenCv高阶(十)——光流估计

文章目录


前言

在计算机视觉领域,光流估计是捕捉图像序列中像素点运动信息的核心技术。它描述了图像中每个像素点在相邻帧间的运动方向和速度,能从静态图像帧中解析出动态变化。

光流估计基于像素亮度不变和运动平滑两个关键假设,通过数学建模解算出像素运动场,为视频分析、目标跟踪等任务提供基础。OpenCV 集成了稀疏光流、稠密光流等计算方法,广泛应用于无人机避障、电影特效等场景。

接下来,我们将深入解析光流估计原理、OpenCV 函数使用及实战应用,探索这项技术的奥秘。

一、光流估计

光流估计是空间运动物体在观测成像平面上的像素运动的"瞬时速度",根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。

光流估计的前提:

(1)亮度恒定:同一点随着时间的变化,其亮度不会发生改变。

(2)小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。

(3)空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。

二、使用步骤

1、导库读取视频、随机初始化颜色

python 复制代码
# 导入数值计算库和计算机视觉库
import numpy as np
import cv2

# 创建视频捕获对象,读取测试视频
cap = cv2.VideoCapture('../data/test.avi')

# 生成100种随机颜色,用于不同特征点的轨迹绘制
# 格式为(100,3)的数组,每个元素代表BGR颜色值
color = np.random.randint(0, 255, (100, 3))

2、初始化光流跟踪

python 复制代码
# 读取视频第一帧
ret, old_frame = cap.read()

# 将第一帧转换为灰度图像(光流算法需要灰度输入)
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)

# 设置特征点检测参数字典
feature_params = dict(
    maxCorners = 100,      # 检测的最大特征点数量
    qualityLevel = 0.3,    # 特征点质量阈值(0-1,值越大质量越高)
    minDistance = 7        # 特征点之间的最小像素距离
)

# 使用Shi-Tomasi算法检测角点特征
# goodFeaturesToTrack:在灰度图像中寻找适合跟踪的强角点
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          # 金字塔层数(0表示不使用金字塔)
)

3、视频帧处理循环

python 复制代码
while True:
    # 读取新帧
    ret, frame = cap.read()
    
    # 视频结束或读取失败时退出循环
    if not ret:
        break
    
    # 将当前帧转换为灰度图像
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

4、光流计算与可视化

python 复制代码
     # 使用Lucas-Kanade金字塔光流法计算特征点运动
    # p1:新帧中特征点位置
    # st:状态标记(1表示成功跟踪,0表示丢失)
    # err:跟踪误差
    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().astype(int)
        c, d = old.ravel().astype(int)
        
        # 在mask图像上绘制运动轨迹线
        # 参数:目标图像,起点,终点,颜色,线宽
        mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
        
        # 显示纯轨迹图像
        cv2.imshow("mask", mask)

    # 将轨迹叠加到原始帧上
    img = cv2.add(frame, mask)
    
    # 显示叠加后的结果帧
    cv2.imshow('frame', img)

5、循环控制与资源释放

python 复制代码
    # 等待150ms并检测ESC按键(ASCII 27)
    k = cv2.waitKey(150)
    if k == 27:
        break

    # 更新前一帧数据
    old_gray = frame_gray.copy()  # 更新灰度图像
    
    # 更新特征点(只保留成功跟踪的点)
    # reshape(-1,1,2)保持与原始p0相同的维度结构
    p0 = good_new.reshape(-1, 1, 2)

# 释放视频资源并销毁所有窗口
cv2.destroyAllWindows()
cap.release()

效果:

完整代码

python 复制代码
# 导入必要的库
import numpy as np
import cv2

# 创建视频捕获对象,读取视频文件
cap = cv2.VideoCapture('../data/test.avi')

# 生成随机颜色数组,用于绘制不同特征点的轨迹(100种颜色)
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, # 特征点质量等级(0-1之间,越大质量越高)
    minDistance=7     # 特征点之间的最小欧氏距离
)

# 使用Shi-Tomasi方法检测初始特征点(角点检测)
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         # 金字塔层数(0表示仅当前层)
)

# 主循环处理视频帧
while True:
    # 读取新的一帧
    ret, frame = cap.read()
    if not ret:  # 如果读取失败(如视频结束)则退出循环
        break

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

    # 计算光流(Lucas-Kanade方法)
    p1, st, err = cv2.calcOpticalFlowPyrLK(
        old_gray,  # 前一帧的灰度图像
        frame_gray, # 当前帧的灰度图像
        p0,         # 需要跟踪的特征点
        None,       # 不使用前一帧的特征点位置
        **lk_params
    )

    # 筛选成功跟踪的特征点(st=1表示成功跟踪)
    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().astype(int)
        c, d = old.ravel().astype(int)
        
        # 在mask上绘制运动轨迹线(使用随机颜色)
        mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
        
    # 将轨迹mask与当前帧叠加显示
    img = cv2.add(frame, mask)
    cv2.imshow('frame', img)
    cv2.imshow('mask', mask)  # 单独显示轨迹mask

    # 等待按键(150ms延迟),ESC键退出
    k = cv2.waitKey(150)
    if k == 27:
        break

    # 更新前一帧的灰度图像和特征点
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1, 1, 2)  # 更新为当前帧的有效特征点

# 释放资源并关闭所有窗口
cv2.destroyAllWindows()
cap.release()

总结

光流估计的应用:

视频增强与创作:光流不仅用于运动补偿压缩(如MPEG标准),还被应用于视频插帧(生成中间帧提升流畅度)和特效合成(如电影中动态背景替换),英伟达SDK已展示其商业化潜力。

医疗精准化:在超声影像中,光流估计校正探头移动导致的图像偏移,辅助心脏手术的实时导航;还可量化器官运动参数(如心肌应变率),为疾病诊断提供动态指标。

工业智能化升级:生产线中,光流实时监测机械臂运动轨迹,结合异常检测算法预防故障;在精密装配场景,通过微位移分析提升质检精度。

相关推荐
gogoMark24 分钟前
口播视频怎么剪!利用AI提高口播视频剪辑效率并增强”网感”
人工智能·音视频
2201_7549184131 分钟前
OpenCV 特征检测全面解析与实战应用
人工智能·opencv·计算机视觉
love530love2 小时前
Windows避坑部署CosyVoice多语言大语言模型
人工智能·windows·python·语言模型·自然语言处理·pycharm
985小水博一枚呀3 小时前
【AI大模型学习路线】第二阶段之RAG基础与架构——第七章(【项目实战】基于RAG的PDF文档助手)技术方案与架构设计?
人工智能·学习·语言模型·架构·大模型
白熊1883 小时前
【图像生成大模型】Wan2.1:下一代开源大规模视频生成模型
人工智能·计算机视觉·开源·文生图·音视频
weixin_514548893 小时前
一种开源的高斯泼溅实现库——gsplat: An Open-Source Library for Gaussian Splatting
人工智能·计算机视觉·3d
四口鲸鱼爱吃盐3 小时前
BMVC2023 | 多样化高层特征以提升对抗迁移性
人工智能·深度学习·cnn·vit·对抗攻击·迁移攻击
Echo``4 小时前
3:OpenCV—视频播放
图像处理·人工智能·opencv·算法·机器学习·视觉检测·音视频
Douglassssssss4 小时前
【深度学习】使用块的网络(VGG)
网络·人工智能·深度学习
okok__TXF4 小时前
SpringBoot3+AI
java·人工智能·spring