OpenCV 4.10.0
开源计算机视觉库
|
上一篇教程:均值漂移和CamShift
下一篇教程:级联分类器
在本节中,
光流是图像物体在两帧之间出现的运动模式,由物体或摄像头的运动引起。它是一个二维向量场,每个向量都是一个位移向量,显示了点从第一帧到第二帧的运动。考虑下面的图像(图片来源:维基百科关于光流的文章)。
它显示了一个球在 5 帧连续画面中的移动。箭头显示了它的位移向量。光流在以下领域有许多应用:
光流基于以下几个假设:
考虑第一帧中的一个像素 \(I(x,y,t)\)(注意这里增加了一个新的维度,即时间。之前我们只处理图像,所以不需要时间)。它在下一帧中移动了 \((dx,dy)\) 的距离,下一帧是在 \(dt\) 时间后拍摄的。由于这些像素是相同的,并且强度不会发生变化,我们可以说:
\[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 方法在点周围取一个 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 在一个函数中提供了所有这些功能,即 cv.calcOpticalFlowPyrLK()。在这里,我们将创建一个简单的应用程序,它跟踪视频中的某些点。为了确定这些点,我们使用 cv.goodFeaturesToTrack()。我们取第一帧,在其中检测一些 Shi-Tomasi 角点,然后我们使用 Lucas-Kanade 光流迭代地跟踪这些点。对于函数 cv.calcOpticalFlowPyrLK(),我们传递上一帧、上一帧的点和下一帧。它返回下一帧的点,以及一些状态号,如果找到下一帧的点,则值为 1,否则为零。我们迭代地将这些下一帧的点作为上一帧的点传递到下一步。请查看下面的代码。
(此代码未检查下一个关键点的正确性。因此,即使任何特征点在图像中消失,光流也可能找到看起来接近它的下一个点。所以实际上,对于鲁棒跟踪,应该在特定间隔检测角点。OpenCV 示例提供了一个这样的示例,它每隔 5 帧查找特征点。它还运行获得的光流点的反向检查,以仅选择好的点。查看 samples/python/lk_track.py)。
查看我们获得的结果
Lucas-Kanade 方法计算稀疏特征集的光流(在我们的示例中,使用 Shi-Tomasi 算法检测到的角点)。OpenCV 提供了另一种算法来查找稠密光流。它计算帧中所有点的光流。它基于 Gunnar Farneback 的算法,该算法在 2003 年由 Gunnar Farneback 在“基于多项式扩展的两帧运动估计”中进行了解释。
以下示例展示了如何使用上述算法查找稠密光流。我们得到一个包含光流向量的二维数组,\((u,v)\)。我们找到它们的幅度和方向。为了更好地可视化,我们对结果进行颜色编码。方向对应于图像的色调值。幅度对应于值平面。查看下面的代码
请查看下面的结果