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已展示其商业化潜力。

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

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

相关推荐
乌旭43 分钟前
量子计算与GPU的异构加速:基于CUDA Quantum的混合编程实践
人工智能·pytorch·分布式·深度学习·ai·gpu算力·量子计算
deephub2 小时前
CLIMB自举框架:基于语义聚类的迭代数据混合优化及其在LLM预训练中的应用
人工智能·深度学习·大语言模型·聚类
思通数科AI全行业智能NLP系统3 小时前
AI视频技术赋能幼儿园安全——教师离岗报警系统的智慧守护
大数据·人工智能·安全·目标检测·目标跟踪·自然语言处理·ocr
struggle20254 小时前
deepseek-cli开源的强大命令行界面,用于与 DeepSeek 的 AI 模型进行交互
人工智能·开源·自动化·交互·deepseek
ocr_sinosecu15 小时前
OCR定制识别:解锁文字识别的无限可能
人工智能·机器学习·ocr
奋斗者1号5 小时前
分类数据处理全解析:从独热编码到高维特征优化
人工智能·机器学习·分类
契合qht53_shine5 小时前
深度学习 视觉处理(CNN) day_02
人工智能·深度学习·cnn
就叫飞六吧6 小时前
如何判断你的PyTorch是GPU版还是CPU版?
人工智能·pytorch·python
zsffuture6 小时前
opencv 读取3G大图失败,又不想重新编译opencv ,可以如下操作
人工智能·opencv·webpack
AntBlack6 小时前
别说了别说了 ,Trae 已经在不停优化迭代了
前端·人工智能·后端