Lucas-Kanade算法


Lucas-Kanade 方法

其原论文链接:http://cseweb.ucsd.edu/classes/sp02/cse252/lucaskanade81.pdf


光流法

在目标跟踪的领域引入光流法是一个突破性进展,这还得归功于Barron,他在1994年发布了论文“Performance of optical flow techniques”,并提出了光流这一概念。


虽然这一方法能够对目标更好地进行跟踪,但同时也存在一些限制性因素,包括运动连续性、运动刚性、光的变化、地表实况等。现在大多数的光流算法都是针对全局能量函数的优化问题而提出的,这里的能量函数可以看作为代价函数,它的作用是保持系统状态的稳定。其中它的表达式为:
E G l o b a l = E D a t a + λ E P r i o r ( 1 ) E_{Global} = E_{Data} + \lambda E_{Prior} (1) EGlobal=EData+λEPrior1
第一项 E D a t a E_{Data} EData 表示测量光流与输入图像的一致性问题,因为Data中的约束方程小于未知量个数,所以需要一个先验条件来优化该函数,即使用 E P r i o r E_{Prior} EPrior 来表示,并用 λ \lambda λ 作为系数进行参数调整。

在这里插入图片描述
以上是基本的光流约束方程




Lucas-Kanade方法原理

Lucas-Kanade算法是根据两帧图像之间像素点的变化进行处理的,而且具有一定的稀疏性

使用这一方法有几个假设的前提条件,如下所示:

  1. 亮度恒定,从 t 到 t+1 时刻不会出现亮度变化
  2. 运动缓慢,从 t 到 t+1 时刻不会引起位置的剧烈变化
  3. 空间一致,从 t 到 t+1 时刻像素点的邻近点不发生变化


1. 以下是图像匹配时考虑的问题

在这里插入图片描述
假设图片的大小为NxN,特征点的大小为MxM

论文中提到了穷举搜索(exhaustively search)进行图像匹配的时间复杂度为 O ( M 2 N 2 ) O(M^2N^2) O(M2N2),消耗太长的时间;也提到了爬山算法(hill-climbing algorithm)是一个局部搜索技术,在一定的情况下无法找到全局最优值,其时间复杂度为 O ( M 2 N ) O(M^2N) O(M2N);还提到了序惯性检测算法(sequential similarity detection algorithm)中的规则不确定性。

然后提出了自己的方法,先对图 Figure1 中的 h 进行初始估计,再针对每一个像素点使用空间强度梯度(spatial intensity gradient)方法优化 h 值,并使用类似于NR的迭代方法(Newton-Raphson iteration)对该过程进行迭代,该方法的时间复杂度为 O ( M 2 l o g N ) O(M^2 log N) O(M2logN)


2.在简单的一维情况下进行分析

在这里插入图片描述

通过分析我们得到了 h 的表达式,然后对 h 的值进行优化

在这里插入图片描述
最后我们得到了优化后的 h 表达式,进而求出更加精确的 h 值,以此来获得相同的特征点。然后我们可以从一维扩展到多维,利用同样的方法实现,具体步骤论文中详细介绍了。


将LK方法应用到光流法中可参考链接:https://www.cnblogs.com/gnuhpc/archive/2012/12/04/2802124.html

其中的 I ( x , y , t ) I(x,y,t) I(x,y,t) I ( x + Δ x , y + Δ y ) I(x+ \Delta x,y+ \Delta y) I(x+Δx,y+Δy) 就相当于是前面图中所示的 F ( x ) F(x) F(x) G ( x ) G(x) G(x) ,而 h 则相当于转变的方位跟大小,用一阶泰勒展开后的一阶项




在OpenCV中使用LK方法

C++: void calcOpticalFlowPyrLK(
		InputArray prevImg, 
		InputArray nextImg,
		InputArray prevPts, 
		InputOutputArray nextPts, 
		OutputArray status, 
		OutputArray err, 
		Size winSize=Size(21,21), 
		int maxLevel=3, 
		TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), 
		int flags=0, 
		double minEigThreshold=1e-4 
		)


第一个参数 prevImg 表示的是8位输入图像 或者是 使用 buildOpticalFlowPyramid() 方法构建的金字塔图像(pyramid)

第二个参数 nextImg 表示的是与 prevImg 同样格式的输入图像,且是对应于 preImg 的下一帧图像

第三个参数 prevPts 表示的是检测到的光流(特征)的二维点向量,其中点坐标必须是单精度浮点数(single-precison floating-point)

第四个参数 nextPts 表示的是一个 包含了在下一帧图像中计算得出的新特征位置 的二维点向量

第五个参数 status 表示的是输出的状态向量,如果能够找到对应特征的流 即将向量元素置为1,若不能则置为0

第六个参数 err 表示的是输出的误差向量,每一个向量相关联的特征点均设置一个误差值,并且误差测量的方式由 参数flags来设定

第七个参数 winSize 表示的是定义的金字塔图像每一层的搜索窗口大小

第八个参数 maxLevel 表示的是定义的金字塔图像的层次,如果 maxLevel=0 即只有一层图像, maxLevel=n 即有n+1层图像。如果金字塔图像被传递给了输入图像,该算法会使用不超过maxLevel的层次数

第九个参数 criteria 表示的是迭代搜索算法 (iterative search algorithm) 的最终收敛条件

第十个参数 flags 表示的是操作标志

  1. OPTFLOW_USE_INITIAL_FLOW
    使用初始估计将特征点(流)储存在 nextPts 上;如果该标志未设置,即将prePts复制到nextPts作为初始的估计值
  2. OPTFLOW_LK_GET_MIN_EIGENVALS
    使用最小特征值 (minimum eigen values) 作为误差度量;如果该标志未设置,即取起始点到运动点之间的L1范数 (L1 norm),然后除以窗口的像素大小作为误差度量

第十一个参数 minEigThreshold 表示的是一个算法,计算2x2标准光流矩阵最小特征值再除以窗口的像素大小;如果求出的值小于minEigThreshold 则过滤掉相应的特征,所以这个方法可以除去不好的特征点并提高性能。



构建金字塔图像的方法

C++: int buildOpticalFlowPyramid(
		InputArray img, 
		OutputArrayOfArrays pyramid, 
		Size winSize, 
		int maxLevel, 
		bool withDerivatives=true, 
		int pyrBorder=BORDER_REFLECT_101, 
		int derivBorder=BORDER_CONSTANT, 
		bool tryReuseInputImage=true
		)


第一个参数 img 为8位的输入图像

第二个参数 pyramid 为输出的金字塔图像

第三个参数 winSize 表示的是实现光流算法的窗口大小,不能小于calcOpticalFlowPyrLK() 中的winSize参数。并且它需要计算金字塔层次所需的填充。

第四个参数 maxLevel 表示的是金字塔层次的最大层次数

第五个参数 withDerivatives 表示设置一个预计算梯度处理金字塔图像的每一层图像

第六个参数 pyrBorder 表示的是金字塔图像的边界模式

第七个参数 derivBorder 表示的是梯度的边界模式

第八个参数 tryReuseInputImage 表示的是是否将输入图像的兴趣区域 (ROI : Region of Intretest) 设置到金字塔图像中,默认为 true


进行边界填充是为了避免图像膨胀后导致的边界模糊的状态,填充后才能保证对边界像素的安全操作,以下有两种填充边界(borderType) 的方法

  1. ORDER_CONSTANT: 使用常数填充边界
  2. BORDER_REPLICATE: 复制原图中最临近的行或者列

该函数返回一个int型数据,表示金字塔图像的层次大小,不过该值小于 maxLevel



  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然!下面是一个简单的 Lucas-Kanade 算法的 Python 代码实现示例: ```python import numpy as np def lucas_kanade(image1, image2, window_size=3): # 转换为灰度图像 if len(image1.shape) > 2: image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY) if len(image2.shape) > 2: image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY) # 设置窗口大小和步长 window_half = window_size // 2 step = 1 # 计算图像梯度 gradient_x = cv2.Sobel(image1, cv2.CV_64F, 1, 0, ksize=3) gradient_y = cv2.Sobel(image1, cv2.CV_64F, 0, 1, ksize=3) rows, cols = image1.shape u = np.zeros((rows, cols)) v = np.zeros((rows, cols)) for i in range(window_half, rows - window_half, step): for j in range(window_half, cols - window_half, step): # 提取窗口内的图像 window1 = image1[i-window_half:i+window_half+1, j-window_half:j+window_half+1] # 计算窗口在第二幅图像中的位置 x = j + int(u[i, j]) y = i + int(v[i, j]) # 如果窗口越界,则跳过 if x < window_half or x >= cols - window_half or y < window_half or y >= rows - window_half: continue # 提取窗口内的图像 window2 = image2[y-window_half:y+window_half+1, x-window_half:x+window_half+1] # 计算窗口内的光流 error = window1 - window2 gradient = np.array([gradient_x[i-window_half:i+window_half+1, j-window_half:j+window_half+1].flatten(), gradient_y[i-window_half:i+window_half+1, j-window_half:j+window_half+1].flatten()]).T steepest_descent = gradient.T.dot(gradient) delta_p = np.linalg.inv(steepest_descent).dot(gradient.T).dot(error.flatten()) # 更新光流 u[i, j] += delta_p[0] v[i, j] += delta_p[1] return u, v ``` 这只是一个简单的示例,实际上,Lucas-Kanade 算法还有很多改进和优化的方法。这段代码实现了基本的光流计算,但请注意,你需要安装并导入适当的库(如 Numpy 和 OpenCV)来运行此代码。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值