介绍

  在本章中,我将主要介绍的是光流的有关概念及其的有关使用。其中,我想主要介绍的是Lucas-Kanade方法的估计。

光流场与运动场

  在理解光流法前,先要对运动场的概念有基本的理解。运动场其实就是物体在三维(3D)真实世界中的运动,运动场由图像中所有图像点的运动矢量组成,其中的每一个图像点都是一个三维的运动矢量。
  光流法是在光流场上的一种预估计算。光流场简单说来就是运动场在二维(2D)图像平面上的投影,它是指图像中所有像素点构成的一种二维瞬时速度场,其中的二维速度矢量是景物中可见点的三维速度矢量在成像表面的投影。 由定义来看,光流中不仅包含了被观察物体的运动信息,而且还包含有关景物三维结构的信息。
  运动场与光流场之间有如下关系。
(1)运动场只能是理想的构造,用来表示二维与三维之间的一种运动关系。但实际上,我们只能基于对图像数据的测量来近似真实的运动场,因为不可能达到真正理想的情况。
(2)在大多数情况下,摄像头中的每个图像点都有一个单独的运动,因此一般通过对图像数据的邻域操作来进行局部测量(这也就是稀疏光流法的思想)。一定会存在无法为某些特定类型的邻域所确定正确的运动场,它们通常被称为光流的近似值。
(3) 因为不能正确测量所有图像点的运动场,所以一般采用的都不会是运动场,而是用光流场作为对它的一种近似,因为光流本身是运动场的近似值。
  当然,现在有好几种不同的方法可以来进行光学估算,也可以用不同的标准来计算光流,本文只介绍一种比较常用的积分方法,对于其他的光流计算,感兴趣的读者可以自行查阅相关资料。
  需要注意的是,这里只介绍一种稀疏的光流法,并不意味着不能对所有图像的像素点进行估算,相应的算法称为稠密光流法。但这一算法十分耗时,所以这里只是简单介绍其定义,不进行过多描述。

光流法

  光流计算基于物体移动的光学特性提出了3个基本假设。
  (1)运动物体的亮度在很短的间隔时间内保持不变。
  (2)给定邻域内的速度向量场变化是缓慢的。
  (3)保持空间一致性;即同一子图像的像素点具有相同的运动
  注意:这3个基本假设在我们后续的积分过程中十分的重要!

1.光流法原理

  光流法用于目标追踪的原理如下。
  (1)对一个连续的视频帧序列进行处理(也可以是摄像头产生的视频流)。
  (2)针对每一个视频序列,利用一定的目标检测方法,检测可能出现的前景目标(光流法具有提前预测的能力)。
  (3)如果某一帧出现了前景目标,找到其具有代表性的关键特征点(可以随机产生,也可以利用自己选定的点来做为特征点)。
  (4)对之后的任意两个相邻视频帧而言(逐帧做差),寻找上一帧中出现的关键特征点在当前帧中的最佳位置(即预判),从而得到前景目标在当前帧中的位置坐标。
  (5)重复上述过程,便可以实现目标的追踪。

2.LK算法

  光流法根据计算像素的数量可以分为两种类型:
  ·稀疏光流:计算部分像素运动,常见的是以Lucas-Kanade为代表的LK算法;
  ·稠密光流:计算所有像素运动。
  这里只介绍比较基础的LK算法。
  根据光流假设的前提:同一个空间点的像素灰度值,在各个图像中是固定不变的(也可以称为灰度不变假设),有
  假如t时刻位于(x,y)处的像素,设t+dt的位置为(x+dt,y+dt),则假设条件表示为:

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

  然后对左边的式子进行三元一阶的泰勒展开,有式:

  然后依据上面的假设条件获得的式子,带入后相减,有式:

  将第三项移到右侧,左右两侧同时除以dt,可以整理得到式:

  为了看起来方便,不妨记为式:

  将其写成矩阵形式后,有如式:

  很显然,这是一个带有两个变量的一次方程,但是此刻只有一个点,所以是无法准确计算出u和v的。
  在这里LK算法的做法是,假设某一个窗口内的像素具有相同的运动(相同的运动轨迹,即相同的u与v,只要选定的窗口足够小,可以无限逼近这个假设)。假设窗口大小为k*k,那么它就有k的平方个像素,所以共有k的平方个方程,如式所示:

  将所有式子相加后,对应的矩阵分别记为A和b,如式:

  则方程可变为式:

  这是一个超定线性方程,可以采用最小二乘法算得一个误差最小的解,如式:

  即可得到u和v。

OpenCV中实现光流法追踪

  我们将使用cv.calcOpticalFlowPyrLK()之类的函数来跟踪视频中的特征点。
  我们将使用cv.calcOpticalFlowFarneback()方法创建一个密集的光流场。
  Lucas-Kanade方法计算稀疏特征集的光流(在我们的示例中为使用Shi-Tomasi算法检测到的角)。OpenCV提供了另一种算法来查找密集的光流。它计算帧中所有点的光通量。它基于Gunner Farneback的算法,在2003年Gunner Farneback的“基于多项式展开的两帧运动估计”中对此进行了解释。
  下面的示例显示了如何使用上述算法找到密集的光流。我们得到一个带有光流矢量( u , v ) (u,v)(u,v)的2通道阵列。我们找到了它们的大小和方向。我们对结果进行颜色编码,以实现更好的可视化。方向对应于图像的色相值。幅度对应于值平面。请参见下面的代码:
  需要vtest.avi的,可以去如下网址下载:
  https://pan.baidu.com/s/1mNAsa4QotXqohDRHSGIHig
  提取码:yr2m

import numpy as np
import cv2

cap = cv2.VideoCapture("vtest.avi")
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255

while (1):
    ret, frame2 = cap.read()
    next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    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)
    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    cv2.imshow('frame2', bgr)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break
    elif k == ord('s'):
        cv2.imwrite('opticalfb.png', frame2)
        cv2.imwrite('opticalhsv.png', bgr)
    prvs = next

  程序的运行结果如下图所示。

光流法的不足

  由于光流法的前提约束性非常强,以及它本身算法一些特点,所以光流法也有很多的不足和缺陷。
  ·关键点与描述子计算非常耗时。
  ·忽略除特征点外的其他所有信息。
  ·如何处理特征缺失的问题。