OpenCV 4.13.0
开源计算机视觉库 (Open Source Computer Vision)
正在加载...
正在搜索...
未找到匹配项
光流

上一个教程: 均值漂移和 Camshift
下一个教程: 级联分类器

目标

在本章中,

光流

光流是由于物体或摄像机的移动,图像对象在两个连续帧之间出现的运动模式。它是一个二维矢量场,其中每个矢量都是一个位移矢量,显示了点从第一帧到第二帧的移动。考虑下图(图片来源:维基百科光流文章)。

图像

它显示了一个球在 5 个连续帧中移动。箭头显示了它的位移矢量。光流在许多领域都有应用,例如:

  • 运动结构
  • 视频压缩
  • 视频稳定化...

光流基于几个假设:

  1. 对象像素强度在连续帧之间不发生变化。
  2. 相邻像素具有相似的运动。

考虑第一帧中的一个像素 \(I(x,y,t)\)(请注意这里添加了一个新的维度,时间。之前我们只处理图像,所以不需要时间)。它在 \(dt\) 时间后的下一帧中移动了 \((dx,dy)\) 的距离。由于这些像素相同且强度不变,我们可以说:

\[I(x,y,t) = I(x+dx, y+dy, t+dt)\]

然后对右侧进行泰勒级数近似,去除相同项并除以 \(dt\) 得到以下方程:

\[f_x u + f_y v + f_t = 0 \;\]

其中

\[f_x = \frac{\partial f}{\partial x} \; ; \; f_y = \frac{\partial f}{\partial y}\]

\[u = \frac{dx}{dt} \; ; \; v = \frac{dy}{dt}\]

上述方程称为光流方程。其中,我们可以找到 \(f_x\) 和 \(f_y\),它们是图像梯度。同样,\(f_t\) 是沿时间的梯度。但 \((u,v)\) 是未知数。我们无法用两个未知变量求解这一个方程。因此,提供了几种方法来解决此问题,其中之一是 Lucas-Kanade 方法。

Lucas-Kanade 方法

我们之前看到一个假设,即所有相邻像素都将具有相似的运动。Lucas-Kanade 方法围绕该点取一个 3x3 的补丁。因此,所有 9 个点都具有相同的运动。我们可以找到这 9 个点的 \((f_x, f_y, f_t)\)。所以现在我们的问题变成了求解 9 个方程和两个未知变量,这是一个过定问题。通过最小二乘拟合方法可以获得更好的解决方案。下面是最终解决方案,它是一个两方程两未知数问题,求解即可得到答案。

\[\begin{bmatrix} u \\ v \end{bmatrix} = \begin{bmatrix} \sum_{i}{f_{x_i}}^2 & \sum_{i}{f_{x_i} f_{y_i} } \\ \sum_{i}{f_{x_i} f_{y_i}} & \sum_{i}{f_{y_i}}^2 \end{bmatrix}^{-1} \begin{bmatrix} - \sum_{i}{f_{x_i} f_{t_i}} \\ - \sum_{i}{f_{y_i} f_{t_i}} \end{bmatrix}\]

( 将逆矩阵与 Harris 角点检测器进行比较。它表明角点是更好的跟踪点。)

因此,从用户的角度来看,这个想法很简单,我们给出一些点来跟踪,我们收到这些点的光流向量。但又出现了一些问题。到目前为止,我们处理的是小运动,所以当运动很大时,它就会失败。为了解决这个问题,我们使用金字塔。当我们在金字塔中向上移动时,小运动被消除,大运动变成小运动。因此,通过在那里应用 Lucas-Kanade,我们得到了光流以及尺度。

OpenCV 中的 Lucas-Kanade 光流

OpenCV 在一个函数 cv.calcOpticalFlowPyrLK() 中提供了所有这些功能。在这里,我们创建一个简单的应用程序来跟踪视频中的一些点。为了确定这些点,我们使用 cv.goodFeaturesToTrack()。我们获取第一帧,检测其中的一些 Shi-Tomasi 角点,然后我们使用 Lucas-Kanade 光流迭代跟踪这些点。对于函数 cv.calcOpticalFlowPyrLK(),我们传入上一帧、上一点和下一帧。它返回下一点以及一些状态数字,如果找到下一点,则状态为 1,否则为 0。我们在下一步中迭代地将这些下一点作为上一点传入。请参见下面的代码

(此代码未检查下一个关键点的正确性。因此,即使图像中任何特征点消失,光流也有可能找到可能与其接近的下一个点。因此,对于鲁棒的跟踪,应该在特定时间间隔内检测角点。OpenCV 示例提供了一个这样的示例,它每 5 帧查找一次特征点。它还对获得的光流点进行反向检查,以仅选择好的点。请查看 samples/python/lk_track.py)。

请看我们得到的结果

图像

OpenCV 中的稠密光流

Lucas-Kanade 方法计算稀疏特征集的光流(在我们的例子中,是使用 Shi-Tomasi 算法检测到的角点)。OpenCV 提供了另一种算法来查找稠密光流。它计算帧中所有点的光流。它基于 Gunnar Farneback 在 2003 年的“基于多项式展开的两帧运动估计”中解释的算法。

以下示例演示了如何使用上述算法查找稠密光流。我们得到一个带有光流矢量 \((u,v)\) 的双通道数组。我们计算它们的幅度和方向。为了更好地可视化,我们对结果进行颜色编码。方向对应于图像的色调值。幅度对应于亮度平面。请参见下面的代码

请看下面的结果

图像