光流
在本章中, - 我们将理解光流的概念及其使用 Lucas-Kanade 方法的估计。 - 我们将使用 cv.calcOpticalFlowPyrLK() 等函数来跟踪视频中的特征点。 - 我们将使用 cv.calcOpticalFlowFarneback() 方法创建一个稠密光流场。
光流是图像目标在两个连续帧之间由目标或相机运动引起的表观运动模式。它是二维向量场,其中每个向量都是一个位移向量,显示点从第一帧到第二帧的运动。考虑下面的图像(图像来源:关于光流的维基百科文章)。
它显示了一个球在 5 个连续帧中的运动。箭头显示其位移向量。光流在以下领域有许多应用:
- 运动恢复结构(Structure from Motion)
- 视频压缩
- 视频稳定 …
光流基于几个假设:
- 目标的像素强度在连续帧之间不发生变化。
- 相邻像素具有相似的运动。
考虑第一帧中的一个像素 (注意这里添加了一个新维度,时间。之前我们只处理图像,所以不需要时间)。它在 时间后拍摄的下一帧中移动了 的距离。因此,由于这些像素是相同的且强度不发生变化,我们可以说,
然后对右边取泰勒级数近似,去掉公共项并除以 ,得到以下方程:
其中:
f_x = \frac{\partial f}{\partial x} \; ; \; f_y = \frac{\partial f}{\partial y}$$$$u = \frac{dx}{dt} \; ; \; v = \frac{dy}{dt}
上面的方程称为光流方程。在其中,我们可以求出 和 ,它们是图像梯度。类似地, 是沿时间的梯度。但 是未知的。我们无法用两个未知变量求解这一个方程。因此,提供了几种方法来解决这个问题,其中之一是 Lucas-Kanade。
Lucas-Kanade 方法
Section titled “Lucas-Kanade 方法”我们之前已经看到过一个假设,即所有相邻像素将具有相似的运动。 Lucas-Kanade 方法在该点周围取一个 3x3 的图块。因此,这 9 个点具有相同的运动。我们 可以求出这 9 个点的 。所以现在我们的问题变成了求解具有 两个未知变量的 9 个方程,这是一个超定问题。使用最小二乘拟合 方法可以获得更好的解。下面是最终解,这是一个两个方程两个未知变量的问题,求解即可得到 解。
\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,否则为零。我们迭代地将这些下一组点作为下一步的前一些点传入。参见下面的代码: - **可下载代码**:点击 [这里](https://github.com/opencv/opencv/tree/4.x/samples/cpp/tutorial_code/video/optical_flow/optical_flow.cpp) - **代码一览:** *代码见下方示例* - **可下载代码**:点击 [这里](https://github.com/opencv/opencv/tree/4.x/samples/python/tutorial_code/video/optical_flow/optical_flow.py) - **代码一览:** *代码见下方示例* - **可下载代码**:点击 [这里](https://github.com/opencv/opencv/tree/4.x/samples/java/tutorial_code/video/optical_flow/OpticalFlowDemo.java) - **代码一览:** *代码见下方示例* (此代码不检查下一组关键点的正确程度。因此,即使任何特征点在图像中 消失,光流也有可能找到看起来接近它的下一个点。所以实际上,对于鲁棒的跟踪, 应当以特定间隔检测角点。OpenCV 示例提供了这样一个示例,它每 5 帧查找一次特征点。它还运行 对所得光流点的反向检查,以仅选择好的点。参见 samples/python/lk_track.py)。 参见我们得到的结果: ## OpenCV 中的稠密光流  Lucas-Kanade 方法计算稀疏特征集(在我们的示例中,是使用 Shi-Tomasi 算法检测到的角点)的光流。OpenCV 提供了另一种算法来查找稠密光流。它 计算帧中所有点的光流。它基于 Gunnar Farneback 的 算法,该算法在 Gunnar Farneback 2003 年的 "Two-Frame Motion Estimation Based on Polynomial Expansion" 中有说明。 下面的示例展示了如何使用上述算法查找稠密光流。我们得到一个 2 通道 的光流向量数组 $(u,v)$。我们求其幅度和方向。我们对结果进行颜色编码以获得更好的可视化。方向对应于图像的 Hue 值。幅度 对应于 Value 平面。参见下面的代码: - **可下载代码**:点击 [这里](https://github.com/opencv/opencv/tree/4.x/samples/cpp/tutorial_code/video/optical_flow/optical_flow_dense.cpp) - **代码一览:** *代码见下方示例* - **可下载代码**:点击 [这里](https://github.com/opencv/opencv/tree/4.x/samples/python/tutorial_code/video/optical_flow/optical_flow_dense.py) - **代码一览:** *代码见下方示例* - **可下载代码**:点击 [这里](https://github.com/opencv/opencv/tree/4.x/samples/java/tutorial_code/video/optical_flow/OpticalFlowDenseDemo.java) - **代码一览:** *代码见下方示例* 参见下面的结果: 