文章目录
- 1、光流
- 2、Opencv 中光流的实现
- 3、稀疏光流
- 4、密集光流
- 4.1、farneback
- 4.2、lucaskanade_dense
- 4.3、rlof
- 5、涉及到的库
- 5.1、cv2.goodFeaturesToTrack
- 5.2、cv2.calcOpticalFlowPyrLK
- 5.3、cv2.optflow.calcOpticalFlowSparseToDense
- 5.4、cv2.calcOpticalFlowFarneback
- 5.5、cv2.optflow.calcOpticalFlowDenseRLOF
- 参考
1、光流
光流(Optical Flow)是计算机视觉和图像处理中的一个重要概念,它描述了连续帧图像中像素点随时间的运动轨迹和速度的二维矢量场。以下是关于光流的详细解释:
一、光流的概念
- 定义:光流是指空间运动物体在观察成像平面上的像素运动瞬时速度。它利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息。
- 起源:光流的概念最早由Gibson在1950年提出,用于描述视觉场景中物体的运动信息。
稀疏光流:仅计算图像中选定特征点(如角点、边缘点等)的光流。
密集光流:计算图像中每个像素点的光流,从而得到整个图像的光流场。
二、光流的基本假设
光流算法通常基于以下几个基本假设:
- 亮度恒定:即同一物体在不同帧间运动时,其亮度不会发生改变。这是光流法的基本假定,用于推导光流法的基本方程。
- 时间连续或“小运动”:即时间的变化不会引起目标位置的剧烈变化,相邻帧之间位移要比较小。这保证了在短时间内,像素的运动可以被认为是连续的。
- 空间一致性:一个场景中同一表面上邻近的点具有相近的运动,在图像平面上的投影也在邻近区域。这一假设有助于在局部区域内对像素运动进行建模。
三、光流的应用场景
光流法因其实时性和计算简单的特点,在计算机视觉和机器人视觉中有许多应用场景:
- 视频稳定:通过光流法分析视频帧中像素的运动,可以稳定视频画面,减少运动模糊和震动对视觉感知的影响。
- 目标跟踪:通过分析连续帧图像中物体的运动情况,光流法可以追踪目标的位置和速度,实现对目标的跟踪和监控。
- 动作识别:在人体动作识别中,光流法可以提取人体在连续帧图像中的运动信息,用于动作分析和识别。
- 三维重建:通过分析连续帧图像中物体的运动情况,光流法可以恢复出物体的三维结构和形状,实现物体的三维重建和建模。
- 智能驾驶:在智能驾驶系统中,光流法可以用于车辆感知和环境感知,提取道路和物体的运动特征,帮助车辆做出决策。
- 视觉导航:在机器人的视觉导航中,光流法可以分析连续帧图像中地面和障碍物的运动情况,实现机器人对环境的感知和定位。
四、光流的计算方法
光流的计算方法多种多样,主要包括以下几种:
- 基于梯度(微分)的方法:通过计算图像序列中像素的灰度梯度来估计光流。
- 基于匹配的方法:包括基于特征和基于区域的方法,通过匹配相邻帧中的特征或区域来计算光流。
- 基于能量(频率)的方法:首先对输入图像序列进行时空滤波处理,然后利用滤波结果来计算光流。
- 基于相位的方法:利用带通滤波器输出的相位特性来确定光流的速度和方向。
- 神经动力学方法:利用神经网络等机器学习方法来模拟生物视觉系统的功能,实现光流的计算。
五、总结
光流作为计算机视觉和图像处理中的一个重要工具,具有广泛的应用前景。通过分析连续帧图像中像素的运动信息,光流法可以实现对物体运动和环境变化的感知和分析,为智能控制和决策提供支持。随着计算机视觉技术的不断发展,光流法的计算精度和效率将不断提高,其应用场景也将更加广泛。
2、Opencv 中光流的实现
OpenCV提供了一些算法实现来解决稀疏光流任务
1)Pyramid Lucas-Kanade
2)Sparse RLOF
仅使用稀疏特征集意味着我们将不会有不包含在其中的像素的运动信息。使用密集光流算法可以消除这一限制,该算法假定为图像中的每个像素计算一个运动向量。
OpenCV中已经实现了一些密集光流算法:
1)Dense Pyramid Lucas-Kanade
2)Farneback
3)PCAFlow
4)SimpleFlow
5)RLOF
6)DeepFlow
7)DualTVL1
3、稀疏光流
Lucas-Kanade 方法
它假设在一个小的窗口内,所有的像素点都有相似的运动
# lucas_kanade.py
import cv2
import numpy as np
def lucas_kanade_method(video_path):
cap = cv2.VideoCapture(video_path)
# ShiTomasi角点检测的参数
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),
)
# 创建一些随机的颜色
color = np.random.randint(0, 255, (100, 3))
# 取第一帧并在其中找到角点
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)
index = 0
while True:
index += 1
ret, frame = cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算光流
# calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, nextPts[, status[, err[, \\
# winSize[, maxLevel[, criteria[, flags[, minEigThreshold]]]]]]]) -> nextPts, status, err
p1, st, err = cv2.calcOpticalFlowPyrLK(
old_gray, frame_gray, p0, None, **lk_params
)
# 返回成功跟踪的特征点的位置
# 哪些点成功跟踪(True)或失败(False)
# 每个点的错误度量(通常是跟踪的质量或置信度)
# 选择比较好的点
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() # 多维数组展开
mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), color[i].tolist(), 2)
frame = cv2.circle(frame, (int(a), int(b)), 5, color[i].tolist(), -1)
img = cv2.add(frame, mask)
cv2.imshow("frame", img)
cv2.imwrite(f"duck_{str(index)}.jpg", img)
k = cv2.waitKey(25) & 0xFF
if k == 27:
break
if k == ord("c"):
mask = np.zeros_like(old_frame)
# 现在更新之前的帧和之前的点
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)
if __name__ == "__main__":
video_path = "duck.mp4"
lucas_kanade_method(video_path)
# python lucas_kanade.py
原理是检测前帧的角点,然后计算光流,绘制
结果展示,分割成多个片段,前后片段是连续的
可以观测到还是有些异常值的(比较直的线段)
4、密集光流
# dense_optical_flow.py
import cv2
import numpy as np
import argparse
def dense_optical_flow(method, video_path, params=[], to_gray=False):
# 读取视频
cap = cv2.VideoCapture(video_path)
# 读取第一帧
ret, old_frame = cap.read()
# 创建HSV并使Value为常量
hsv = np.zeros_like(old_frame)
hsv[..., 1] = 255
# 精确方法的预处理
if to_gray:
old_frame = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
index = 0
while True:
index += 1
# 读取下一帧
ret, new_frame = cap.read()
frame_copy = new_frame
if not ret:
break
# 精确方法的预处理
if to_gray:
new_frame = cv2.cvtColor(new_frame, cv2.COLOR_BGR2GRAY)
# 计算光流
flow = method(old_frame, new_frame, None, *params)
# 编码:将算法的输出转换为极坐标
mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
# 使用色相和饱和度来编码光流
hsv[..., 0] = ang * 180 / np.pi / 2
hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
# 转换HSV图像为BGR
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
cv2.imshow("frame", frame_copy)
cv2.imshow("optical flow", bgr)
cv2.imwrite(f"duck_{str(index)}.jpg", bgr)
k = cv2.waitKey(25) & 0xFF
if k == 27:
break
old_frame = new_frame
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--algorithm",
choices=["farneback", "lucaskanade_dense", "rlof"],
required=True,
help="Optical flow algorithm to use",
)
parser.add_argument(
"--video_path", default="duck.mp4", help="Path to the video",
)
args = parser.parse_args()
video_path = args.video_path
if args.algorithm == "lucaskanade_dense":
method = cv2.optflow.calcOpticalFlowSparseToDense
dense_optical_flow(method, video_path, to_gray=True)
elif args.algorithm == "farneback":
# OpenCV Farneback算法需要一个单通道的输入图像,因此我们将BRG图像转换为灰度。
method = cv2.calcOpticalFlowFarneback
params = [0.5, 3, 15, 3, 5, 1.2, 0] # Farneback的算法参数
dense_optical_flow(method, video_path, params, to_gray=True)
elif args.algorithm == "rlof":
# 与Farneback算法相比,RLOF算法需要3通道图像,所以这里没有预处理。
method = cv2.optflow.calcOpticalFlowDenseRLOF
dense_optical_flow(method, video_path)
if __name__ == "__main__":
main()
# python dense_optical_flow.py
4.1、farneback
输出结果
效果还行,显示出来的部分仅为手拿着物体在移动
4.2、lucaskanade_dense
输出结果
这个感觉更稠密
4.3、rlof
输出结果
这个光流不太稳定的样子,噪点比较大
5、涉及到的库
5.1、cv2.goodFeaturesToTrack
cv2.goodFeaturesToTrack
用于检测图像中的角点,这些角点通常用于后续的图像跟踪或特征匹配任务
函数原型
corners = cv2.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance, [, corners[, mask[, blockSize[, useHarrisDetector[, k]]]]])
参数说明
- image:输入图像,应为8位或32位浮点型,单通道图像(灰度图)。
- maxCorners:返回角点的最大数量。如果实际检测到的角点数超过此值,则只返回最强的前maxCorners个角点。如果设置为0,则返回所有检测到的角点。
- qualityLevel:角点的最小可接受质量水平,是角点检测算法中用于筛选角点的阈值参数。
- minDistance:检测到的角点之间的最小欧氏距离。在此距离内的多个角点将被视为同一角点并只保留一个。
- corners(可选):输出参数,用于存储检测到的角点坐标。如果提供了此参数,则检测到的角点将存储在此数组中。
- mask(可选):一个与输入图像同样大小的8位单通道图像,用于指定感兴趣区域(ROI)。非零(通常是255)像素位置表示该位置是角点检测的候选区域。
- blockSize(可选):计算角点时使用的邻域大小,默认为3。该值越大,检测到的角点越稳定,但计算量也越大。
- useHarrisDetector(可选):一个布尔值,指定是否使用Harris角点检测器。如果为True,则使用Harris角点检测;如果为False(默认值),则使用Shi-Tomasi角点检测。
- k(可选):Harris角点检测器中的自由参数,仅当useHarrisDetector为True时有效。
返回值
- corners:如果函数调用时未提供corners参数,则该函数将返回检测到的角点坐标,格式为numpy.ndarray,每个角点由(x, y)坐标表示。
使用示例
import cv2
import numpy as np
# 读取图像
image = cv2.imread('path_to_image.jpg', 0) # 0 表示以灰度模式读取图像
# 设置角点检测参数
maxCorners = 100
qualityLevel = 0.3
minDistance = 7
# 检测角点
corners = cv2.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance)
# 如果检测到了角点,则绘制并显示它们
if corners is not None:
for i in corners:
x, y = i.ravel()
cv2.circle(image, (x, y), 3, 255, -1)
cv2.imshow('Corners', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
注意事项
- 输入图像应为灰度图,因为角点检测通常在灰度空间中进行。
- qualityLevel 和 minDistance 参数对检测到的角点数量和分布有显著影响,需要根据具体应用场景进行调整。
- 使用 mask 参数可以指定只在图像的特定区域中检测角点,有助于减少计算量和提高检测精度。
- blockSize 参数和 useHarrisDetector 参数提供了检测算法的灵活性,可以根据需要选择合适的设置。
5.2、cv2.calcOpticalFlowPyrLK
cv2.calcOpticalFlowPyrLK 是 OpenCV 库中用于计算两幅图像之间稀疏光流(Sparse Optical Flow)的一个函数,特别是通过 Lucas-Kanade 方法结合金字塔(PyrLK)来跟踪图像中的特征点。
函数原型
p1, st, err = cv2.calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, nextPts[, status[, err[, winSize[, maxLevel[, criteria[, flags[, minEigThreshold]]]]]]])
参数说明
- prevImg:前一帧的图像(8位单通道图像)。
- nextImg:当前帧的图像(与前一帧同样大小和类型)。
- prevPts:前一帧图像中的特征点(关键点)数组,数据类型为 numpy 数组,形状为 (N, 1, 2),其中 N 是特征点的数量。
- nextPts:输出参数,表示在当前帧图像中计算出的特征点位置,与 prevPts 大小相同。如果传递 None,则函数会创建一个新的数组来存储结果。
- status(可选):输出参数,表示每个特征点的跟踪状态。如果某个特征点被成功跟踪,其对应的 status 值为 1,否则为 0。如果传递 None,则函数会创建一个新的数组来存储状态。
- err(可选):输出参数,表示每个特征点的错误向量(误差)。在某些情况下,这个参数可能被忽略。如果传递 None,则函数会创建一个新的数组来存储错误向量。
- winSize(可选):搜索窗口的大小,默认值为 (21, 21)。
- maxLevel(可选):金字塔的最大层数,0 表示不使用图像金字塔。默认值为 3。
- criteria(可选):迭代搜索算法的终止条件。通常为 cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,并设置最大迭代次数和 epsilon 值。
- flags(可选):操作标志,默认为 0。可以使用的标志包括 cv2.OPTFLOW_USE_INITIAL_FLOW,表示使用初始估计的点位置。
- minEigThreshold(可选):测量是否被视为良好特征点的最小特征值。默认值为 1e-4。
返回值
- p1:二维点数组,表示在 nextImg 图像中成功跟踪的特征点的位置。
- st:与 prevPts 大小相同的布尔数组,表示哪些点成功跟踪(True)或失败(False)。
- err:与 prevPts 大小相同的数组,表示每个点的错误度量(通常是跟踪的质量或置信度)。
工作原理
- cv2.calcOpticalFlowPyrLK 函数通过 Lucas-Kanade 方法结合金字塔(PyrLK)来跟踪特征点。它首先将输入的两帧图像构建成金字塔,然后从金字塔的顶层开始逐层向下计算光流。这种金字塔方法可以提高计算效率并提供更好的光流估计结果。
使用示例
import cv2
import numpy as np
# 读取前一帧和当前帧图像
prevImg = cv2.imread('frame1.png', cv2.IMREAD_GRAYSCALE)
nextImg = cv2.imread('frame2.png', cv2.IMREAD_GRAYSCALE)
# 使用 Shi-Tomasi 角点检测器找到前一帧图像中的关键点
prevPts = cv2.goodFeaturesToTrack(prevImg, maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
# 设置 LK 光流算法的参数
lk_params = dict(winSize=(15, 15), maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 计算光流
nextPts, st, err = cv2.calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, None, **lk_params)
# 筛选出被成功跟踪的点
good_new = nextPts[st == 1]
good_old = prevPts[st == 1]
# 绘制跟踪结果(省略具体绘制代码)
注意事项
- 输入图像应为灰度图,因为光流计算通常在灰度空间中进行。
- prevPts 中的特征点应使用适当的特征检测算法(如 Shi-Tomasi 角点检测器)获得。
5.3、cv2.optflow.calcOpticalFlowSparseToDense
在OpenCV中,cv2.optflow.calcOpticalFlowSparseToDense 是一个用于计算从稀疏特征点到密集光流场的函数。这个函数结合了稀疏特征匹配和光流算法的优点,通过先找到一组稀疏的关键点(如使用Shi-Tomasi角点检测器),然后利用这些稀疏点来估计整个图像的光流场。
函数原型
flow = cv2.optflow.calcOpticalFlowSparseToDense(prevImg, nextImg, prevPts, nextPts, density=1, sigma_dist=0.05, sigma_color=0.1, patchSize=21, maxLevel=2)
参数说明
- prevImg: 上一帧图像,类型为np.uint8或np.float32。
- nextImg: 当前帧图像,与prevImg具有相同的类型和尺寸。
- prevPts: 上一帧图像中检测到的稀疏点集,类型为np.float32,形状为(N, 1, 2),其中N是点的数量,每个点是一个(x, y)坐标。
- nextPts: 当前帧图像中对应prevPts的稀疏点集,同样为np.float32类型,形状为(N, 1, 2)。
- density: 光流场的密度,控制输出光流场的平滑程度。较高的值意味着更密集的光流场,但也可能包含更多的噪声。默认值为1.0。
- sigma_dist: 在计算光流时,空间邻近性的高斯核标准差。较小的值意味着更关注局部邻域。
- sigma_color: 在计算光流时,颜色相似性的高斯核标准差。较小的值意味着颜色变化对光流的影响更大。
- patchSize: 用于计算光流时考虑的邻域大小。较大的值可以提高算法的鲁棒性,但也会增加计算量。
- maxLevel: 金字塔的最大层数。算法会在多个尺度上计算光流,以提高对大尺度运动的处理能力。
返回值 - flow: 密集光流场,类型为np.float32,形状为(height, width, 2),其中每个像素点(x, y)的光流是一个(u, v)向量,分别表示在水平和垂直方向上的运动分量。
示例代码
import cv2
import numpy as np
# 假设 prevImg, nextImg, prevPts, nextPts 已经准备好
# ...
# 计算密集光流场
flow = cv2.optflow.calcOpticalFlowSparseToDense(prevImg, nextImg, prevPts, nextPts, density=1.0, sigma_dist=0.05, sigma_color=0.1, patchSize=21, maxLevel=2)
# 可视化光流场
hsv = np.zeros_like(nextImg)
hsv[...,1] = 255
hsv[...,0] = 0.5 * flow[...,0] + 0.5
hsv[...,2] = 0.5 * flow[...,1] + 0.5
hsv = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
img = cv2.add(nextImg, hsv)
# 显示结果
cv2.imshow('Optical Flow', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
5.4、cv2.calcOpticalFlowFarneback
cv2.calcOpticalFlowFarneback 是 OpenCV 库中用于计算两个连续帧之间稠密光流的函数。该函数基于 Gunnar Farneback 的算法,该算法在计算速度和准确性之间取得了良好的平衡。
函数原型
cv2.calcOpticalFlowFarneback(prevImg, nextImg, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags[, flow])
参数说明
- prevImg: 前一帧的图像(灰度图像)。类型为 np.uint8。
- nextImg: 下一帧的图像(灰度图像),与前一帧保持同样的格式和尺寸。类型为 np.uint8。
- pyr_scale: 指定图像金字塔上下两层之间的尺度关系(<1)。通常设置为 0.5,表示图像金字塔上一层是下一层的两倍降采样。
- levels: 金字塔的层数,包括初始图像在内。levels=1 表示不创建额外的图层,仅使用原始图像。
- winsize: 均值窗口大小。较大的值会增加算法对图像噪声的鲁棒性,并可以检测更快速的运动,但会产生更模糊的运动场。
- iterations: 算法迭代次数。每个金字塔层级的迭代次数。
- poly_n: 用于在每个像素点处计算多项式展开的相邻像素点的个数。较大的值意味着图像的近似逼近越光滑,算法鲁棒性更好,但也会带来更多的运动区域模糊。通常设置为 5 或 7。
- poly_sigma: 高斯标准差,用于平滑用作多项式展开基础的导数。poly_n=5 时,poly_sigma 可设置为 1.1;poly_n=7 时,可设置为 1.5。
- flags: 操作标志,可以是以下选项的组合:
- OPTFLOW_USE_INITIAL_FLOW: 使用输入流作为初始流近似。
- OPTFLOW_FARNEBACK_GAUSSIAN: 使用高斯滤波器代替盒式滤波器进行光流估计。这通常可以提供更精确的光流估计,但会降低速度。
- flow: 输出参数,与 prevImg 大小相同,类型为 CV_32FC2 的计算流图像。如果不提供,则函数将分配并返回一个新的流图像。
返回值
- flow: 类型为 np.float32 的二维数组,表示每个像素点的运动向量。其形状为 (height, width, 2),其中每个像素点 (x, y) 的光流是一个 (u, v) 向量,分别表示在水平和垂直方向上的运动分量。
使用示例
import cv2
import numpy as np
# 读取前后两帧图像
prev_frame = cv2.imread('frame1.jpg')
next_frame = cv2.imread('frame2.jpg')
# 将图像转换为灰度图
gray_prev = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
gray_next = cv2.cvtColor(next_frame, cv2.COLOR_BGR2GRAY)
# 计算光流
flow = cv2.calcOpticalFlowFarneback(gray_prev, gray_next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
# 可视化光流(这里省略了可视化代码,通常使用箭头或其他方式表示光流)
注意事项
- 确保输入的两帧图像是灰度图像,因为 cv2.calcOpticalFlowFarneback 函数仅处理灰度图像。
- 参数的选择对光流计算的准确性和计算速度有很大影响,需要根据实际情况进行调整。
- 光流计算的结果是一个二维数组,表示每个像素点的运动向量,可以通过可视化工具进行展示和分析。
5.5、cv2.optflow.calcOpticalFlowDenseRLOF
cv2.optflow.calcOpticalFlowDenseRLOF 是 OpenCV 中用于计算两帧之间光流的一个函数,它属于密集光流算法的一种。RLOF 代表 Robust Local Optical Flow,即鲁棒的局部光流算法。这种算法试图通过考虑图像中的局部特征和区域来更准确地估计光流,即使在图像中存在噪声、遮挡或运动不连续的情况下也能表现出较好的性能。
函数原型
cv2.optflow.calcOpticalFlowDenseRLOF(prevImg, nextImg, win_size, max_iter, eps, criteria_type[, dst[, flow[, layers[, scale_layers]]]])
参数解释
- prevImg: 前一帧图像,必须是单通道、8位或浮点型图像。
- nextImg: 当前帧图像,与前一帧图像具有相同的类型和大小。
- win_size: 用于局部邻域的窗口大小,通常为奇数。
- max_iter: 迭代算法的最大迭代次数。
- eps: 算法停止的阈值。
- criteria_type: 迭代停止的条件,可以是 cv2.TERM_CRITERIA_EPS 或 cv2.TERM_CRITERIA_COUNT 或它们的组合,表示根据迭代次数或阈值来停止。
- dst: 输出光流图,如果提供,则必须是与前两帧相同大小的浮点型双通道图像(每个像素的光流向量)。
- flow: 初始化光流场(可选),用于在连续帧之间传递光流信息。
- layers: 金字塔层数(可选),用于多尺度处理。
- scale_layers: 金字塔各层之间的缩放因子(可选)。
返回值
- dst: 如果未提供 dst 参数,则函数返回计算得到的光流图,它是一个与输入图像大小相同的浮点型双通道图像,其中每个像素的值表示该像素的光流向量(水平和垂直分量)。
使用示例
import cv2
import numpy as np
# 加载视频或图像序列
cap = cv2.VideoCapture('video.mp4')
ret, prev = cap.read()
if not ret:
exit()
prev_gray = cv2.cvtColor(prev, cv2.COLOR_BGR2GRAY)
while cap.isOpened():
ret, next = cap.read()
if not ret:
break
next_gray = cv2.cvtColor(next, cv2.COLOR_BGR2GRAY)
# 计算光流
flow = cv2.optflow.calcOpticalFlowDenseRLOF(prev_gray, next_gray, win_size=15, max_iter=3, eps=0.01, criteria_type=cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_COUNT, dst=None)
# 可视化光流
hsv = np.zeros_like(next)
hsv[..., 1] = 255
hsv[..., 0] = 0.5 * flow[..., 0] + 0.5
hsv[..., 2] = 0.5 * flow[..., 1] + 0.5
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
# 显示结果
cv2.imshow('Optical Flow', bgr)
if cv2.waitKey(30) & 0xFF == ord('q'):
break
# 更新前一帧
prev_gray = next_gray.copy()
cap.release()
cv2.destroyAllWindows()
参考
-
OpenCV进阶(2)OpenCV中的光流
-
Optical Flow in OpenCV (C++/Python)