Lucas-Kanade光流法详解

简介:个人学习分享,如有错误,欢迎批评指正。

光流(Optical Flow)描述的是图像序列中各像素点随时间的运动情况,是计算机视觉中的基本问题之一。光流问题涉及尝试找出一幅图像中的许多点在第二幅图像中移动的位置–通常是以视频序列完成的,因此可以假定第一幅图像中的大部分点框架都可以在第二幅图像中找到。光流可以用于场景中物体的运动估计,设置用于相机相对于整个场景的自运动估计。光流算法的理想输出是两帧图像中每个像素的速度的估计关联,或者等效的,一幅图像中每个像素的位移矢量,指示该像素在另一幅图像中的相对位置,如果图像中的每个像素都采用这种方法通常称为“稠密光流”。还有一种算法称为“稀疏光流”,仅仅只跟踪图像中某些点的子集,该算法通常是快速且可靠的,因为其将注意力只放在容易跟踪的特定点上,稀疏跟踪的计算成本远远低于稠密跟踪,这也导致了后者的研究基本被限制在了学术圈。

在这里插入图片描述

一、基本假设

Lucas-Kanade方法基于以下三个主要假设,这些假设简化了光流估计问题,使其在实际应用中可行:

  • 亮度恒定假设(Brightness Constancy Assumption):

    • 定义:同一物体点在连续两帧图像中的亮度值保持不变。

    • 数学表达:对于任意时间 t t t,点 ( x , y ) (x, y) (x,y) t t t t + Δ t t + \Delta t t+Δt时刻的亮度满足
      I ( x , y , t ) = I ( x + Δ x , y + Δ y , t + Δ t ) I(x, y, t) = I(x + \Delta x, y + \Delta y, t + \Delta t) I(x,y,t)=I(x+Δx,y+Δy,t+Δt)

    • 意义:将物体的运动与亮度变化联系起来,为光流估计提供约束条件。

  • 小位移假设(Small Motion Assumption):

    • 定义:物体的运动在时间间隔 Δ t \Delta t Δt内是微小的,位移 ( Δ x , Δ y ) (\Delta x, \Delta y) (Δx,Δy)较小。

    • 数学表达 Δ x \Delta x Δx Δ y \Delta y Δy相对于图像尺度而言足够小。

    • 意义:允许对图像亮度进行泰勒展开,并忽略高阶项,简化光流方程的线性化过程。

  • 局部光流一致性假设(Local Flow Constancy Assumption):

    • 定义:在一个小的邻域窗口内,所有像素点的光流向量相同。
    • 数学表达:对于窗口内的所有点 ( x i , y i ) (x_i, y_i) (xi,yi),光流向量 ( u , v ) (u, v) (u,v)相同。
    • 意义:将每个像素的光流问题转化为局部一致的运动估计,简化求解过程。

这些假设为光流估计提供了必要的约束,使得原本不可解的方程组变为可解的形式。然而,这些假设在实际应用中可能不完全成立,导致算法在某些场景下性能下降。

二、数学原理

1.亮度恒定方程的推导

基于亮度恒定假设,Lucas-Kanade方法首先建立亮度恒定方程,连接图像的空间变化与时间变化。

1.1 亮度恒定假设

I ( x , y , t ) I(x, y, t) I(x,y,t)表示在时间 t t t的图像,其中 ( x , y ) (x, y) (x,y)是像素坐标。对于一个物体点 ( x , y ) (x, y) (x,y),其在时间 t t t t + Δ t t + \Delta t t+Δt的亮度满足:
I ( x , y , t ) = I ( x + Δ x , y + Δ y , t + Δ t ) I(x, y, t) = I(x + \Delta x, y + \Delta y, t + \Delta t) I(x,y,t)=I(x+Δx,y+Δy,t+Δt)

其中, Δ x \Delta x Δx Δ y \Delta y Δy分别是物体点在 x x x y y y方向上的位移。

1.2 泰勒展开

由于假设 Δ t \Delta t Δt较小,位移 Δ x \Delta x Δx Δ y \Delta y Δy也较小,可以对 I ( x + Δ x , y + Δ y , t + Δ t ) I(x + \Delta x, y + \Delta y, t + \Delta t) I(x+Δx,y+Δy,t+Δt) ( x , y , t ) (x, y, t) (x,y,t)处进行泰勒展开,并忽略高阶项(即 O ( Δ x 2 , Δ y 2 , Δ t 2 ) O(\Delta x^2, \Delta y^2, \Delta t^2) O(Δx2,Δy2,Δt2)):
I ( x + Δ x , y + Δ y , t + Δ t ) ≈ I ( x , y , t ) + I x Δ x + I y Δ y + I t Δ t I(x + \Delta x, y + \Delta y, t + \Delta t) \approx I(x, y, t) + I_x \Delta x + I_y \Delta y + I_t \Delta t I(x+Δx,y+Δy,t+Δt)I(x,y,t)+IxΔx+IyΔy+ItΔt

其中, I x = ∂ I ∂ x I_x = \frac{\partial I}{\partial x} Ix=xI I y = ∂ I ∂ y I_y = \frac{\partial I}{\partial y} Iy=yI I t = ∂ I ∂ t I_t = \frac{\partial I}{\partial t} It=tI分别表示图像在 x x x y y y方向的空间梯度和时间梯度。

1.3 代入亮度恒定方程

根据亮度恒定假设,将泰勒展开式代入,得到:

I ( x , y , t ) + I x Δ x + I y Δ y + I t Δ t = I ( x , y , t ) I(x, y, t) + I_x \Delta x + I_y \Delta y + I_t \Delta t = I(x, y, t) I(x,y,t)+IxΔx+IyΔy+ItΔt=I(x,y,t)

两边减去 I ( x , y , t ) I(x, y, t) I(x,y,t),得到:
I x Δ x + I y Δ y + I t Δ t = 0 I_x \Delta x + I_y \Delta y + I_t \Delta t = 0 IxΔx+IyΔy+ItΔt=0

1.4 引入光流向量

定义光流向量 ( u , v ) (u, v) (u,v)为位移 ( Δ x , Δ y ) (\Delta x, \Delta y) (Δx,Δy)与时间间隔 Δ t \Delta t Δt的比值:

u = Δ x Δ t , v = Δ y Δ t u = \frac{\Delta x}{\Delta t}, \quad v = \frac{\Delta y}{\Delta t} u=ΔtΔx,v=ΔtΔy

将其代入上式,得到:
I x u + I y v + I t = 0 I_x u + I_y v + I_t = 0 Ixu+Iyv+It=0

这就是光流约束方程,它将图像的空间梯度、时间梯度与光流向量联系起来。其中 ( u , v ) (u, v) (u,v)代表两个方向(x方向和y方向)的移动速度,把 ( u , v ) (u, v) (u,v)计算出来,咱们的光流也就算出来了。

拿到当前帧,假设我们要计算点p的光流。其中 I x I_x Ix, I y I_y Iy都可以通过当前帧计算出来,而 I t I_t It 可以通过两帧的差分计算出来。所以,对于公式 I x u + I y v + I t = 0 I_x u + I_y v + I_t = 0 Ixu+Iyv+It=0 而言,未知数只有 u u u v v v

LK算法就是用来求解公式 I x u + I y v + I t = 0 I_x u + I_y v + I_t = 0 Ixu+Iyv+It=0 的。LK有一个window的概念,即我先划定一块区域比如(5x5)的像素区域,我们可以认为这块区域每个点的移动速度 u u u v v v是一致的。
首先, I x I_x Ix, I y I_y Iy是怎么得到的呢?对于光流法,咱们有个理想的假定就是:运动物体只会做平移。所以,亮度梯度咱们只需考虑当前帧的梯度即可。对于 I t I_t It 我们在两帧做一个差分就可以得到。

在这里插入图片描述

咱们看当前帧,也就是右边那个。 I x ( 3 , 3 ) = 0 I_x(3,3)=0 Ix(3,3)=0 是因为在x轴的数值左右都是3,没有梯度变化。 I y ( 3 , 3 ) = 1 I_y(3,3)=1 Iy(3,3)=1 是因为在y轴数值变化幅度为1。上图显示的情况,咱们只能算出y轴速度v,没有办法算出水平(x轴)速度u。这是因为该移动目标本身在x轴方向上就没有亮度变化。这也是一种典型的问题,叫孔径问题(Aperture Problem)。
孔径问题是讲,如果我们通过一个小孔来看全局,很多情况下的移动信息我们是看不出来的,这个用一张图就可以很好理解:
在这里插入图片描述
假设墙上破了一个洞,咱们通过这个洞来看一个图形的移动情况。假设,我们看到的是上图这种情况,绿色部分就是我们的视野,我们无法判断这个图形是否是沿着切线方向移动或者是静止。
那么基于小区域的LK光流法也可能遇到Aperture Problem,所以我们在追光流的时候,选点通常会选目标的角点(corner)。角点的情况如下图:
在这里插入图片描述
如果角点在视野内的话,咱们就可以判断这个图形的运动方向。
接着公式 I x u + I y v + I t = 0 I_x u + I_y v + I_t = 0 Ixu+Iyv+It=0 ,咱们如果通过window方式求解u、v,那还是很好办的。假设咱们取的是5x5的window,那么window内的每个点,我们都认为有一样的移动方向,咱们可以构建出25个等式。求解二元一次方程,通常两个等式就可以求解。但那是理想情况,实际情况是没有一组( u , v ) 能同时满足这25个等式,咱们要做的是最小化这个差异。

在这里插入图片描述
写成矩阵形式:
在这里插入图片描述

只需找到一组( u , v ),即上图中的x,满足

在这里插入图片描述
公式(5)
这里就可以用最小二乘法来进行优化了,分别求偏导得出导数为0的点的值就是最优值。可推导出,
在这里插入图片描述

这便是Lucas-Kanade光流算法的公式了。

2.光流约束方程的建立

对于上述问题进行重新表述:在单个像素点,光流约束方程 I x u + I y v + I t = 0 I_x u + I_y v + I_t = 0 Ixu+Iyv+It=0 包含两个未知数 u u u v v v,无法直接求解。Lucas-Kanade方法通过在一个小窗口内(如 3 × 3 3 \times 3 3×3像素)对多个像素点的光流约束方程进行组合,形成一个过定的线性方程组,从而估计出统一的 u u u v v v

2.1 局部窗口内的光流方程

假设在一个 N × N N \times N N×N的窗口内(共 n = N 2 n = N^2 n=N2个像素点),每个像素点 ( x i , y i ) (x_i, y_i) (xi,yi)都有一个光流约束方程:

I x i u + I y i v + I t i = 0 , i = 1 , 2 , … , n I_{x_i} u + I_{y_i} v + I_{t_i} = 0, \quad i = 1, 2, \dots, n Ixiu+Iyiv+Iti=0,i=1,2,,n

将这些方程表示为矩阵形式:

[ I x 1 I y 1 I x 2 I y 2 ⋮ ⋮ I x n I y n ] [ u v ] = [ − I t 1 − I t 2 ⋮ − I t n ] \begin{bmatrix} I_{x_1} & I_{y_1} \\ I_{x_2} & I_{y_2} \\ \vdots & \vdots \\ I_{x_n} & I_{y_n} \end{bmatrix} \begin{bmatrix} u \\ v \end{bmatrix} =\begin{bmatrix} -I_{t_1} \\ -I_{t_2} \\ \vdots \\ -I_{t_n} \end{bmatrix} Ix1Ix2IxnIy1Iy2Iyn [uv]= It1It2Itn

记作:
A ⋅ d = b A \cdot \mathbf{d} = \mathbf{b} Ad=b

其中:
A = [ I x 1 I y 1 I x 2 I y 2 ⋮ ⋮ I x n I y n ] , d = [ u v ] , b = [ − I t 1 − I t 2 ⋮ − I t n ] A = \begin{bmatrix} I_{x_1} & I_{y_1} \\ I_{x_2} & I_{y_2} \\ \vdots & \vdots \\ I_{x_n} & I_{y_n} \end{bmatrix}, \quad \mathbf{d} = \begin{bmatrix} u \\ v \end{bmatrix}, \quad \mathbf{b} = \begin{bmatrix} -I_{t_1} \\ -I_{t_2} \\ \vdots \\ -I_{t_n} \end{bmatrix} A= Ix1Ix2IxnIy1Iy2Iyn ,d=[uv],b= It1It2Itn

2.2 方程组的特性

  • 过定性:由于 n > 2 n > 2 n>2,方程组为过定的,存在多个方程与两个未知数。
  • 最小二乘解:通过最小化误差平方和,求解最优的 u u u v v v

3.最小二乘法求解

为了求解光流向量 ( u , v ) (u, v) (u,v),Lucas-Kanade方法采用最小二乘法,具体步骤如下:

3.1 最小二乘法基本原理

目标是找到 d = [ u v ] \mathbf{d} = \begin{bmatrix} u \ v \end{bmatrix} d=[u v],使得误差平方和最小:

E ( d ) = ∑ i = 1 n ( I x i u + I y i v + I t i ) 2 = ∥ A d − b ∥ 2 E(\mathbf{d}) = \sum_{i=1}^{n} (I_{x_i} u + I_{y_i} v + I_{t_i})^2 = \|A\mathbf{d} - \mathbf{b}\|^2 E(d)=i=1n(Ixiu+Iyiv+Iti)2=Adb2

3.2 正规方程(Normal Equations)

通过对 E ( d ) E(\mathbf{d}) E(d)关于 d \mathbf{d} d求导并设为零,可以得到正规方程:

A T A d = A T b A^T A \mathbf{d} = A^T \mathbf{b} ATAd=ATb

3.3 解的存在性与唯一性

  • 解存在的条件:矩阵 A T A A^T A ATA是对称且半正定的。
  • 解唯一性的条件 A T A A^T A ATA是正定的,即窗口内的梯度信息足够丰富(线性无关),矩阵 A A A的秩为2。

如果 A T A A^T A ATA是非奇异的(可逆),则正规方程有唯一解:

d = ( A T A ) − 1 A T b \mathbf{d} = (A^T A)^{-1} A^T \mathbf{b} d=(ATA)1ATb

3.4 解析解与数值求解方法

3.4.1 直接求逆

对于小窗口(如 3 × 3 3 \times 3 3×3),可以直接计算 ( A T A ) − 1 A T b (A^T A)^{-1} A^T \mathbf{b} (ATA)1ATb,得到光流向量。

3.4.2 矩阵分解方法
  • QR分解

    • 分解 A A A Q R Q R QR,其中 Q Q Q是正交矩阵, R R R是上三角矩阵。
    • 解方程 R d = Q T b R \mathbf{d} = Q^T \mathbf{b} Rd=QTb
  • 奇异值分解(SVD):

    • A A A分解为 U Σ V T U \Sigma V^T UΣVT,其中 U U U V V V是正交矩阵, Σ \Sigma Σ是对角矩阵。

    • 最小二乘解为 d = V Σ − 1 U T b \mathbf{d} = V \Sigma^{-1} U^T \mathbf{b} d=VΣ1UTb

    • 优点:提高数值稳定性,尤其在 A T A A^T A ATA接近奇异时。

  • Cholesky分解

    • 适用于正定矩阵 A T A A^T A ATA,通过分解为下三角矩阵和其转置的乘积,快速求解。
3.4.3 数值稳定性

在实际计算中, A T A A^T A ATA可能接近奇异,导致数值不稳定。为提高稳定性,可以采取以下措施:

  • 增加窗口内像素点数量:扩大窗口尺寸,提供更多约束条件。
  • 正则化:引入Tikhonov正则化(岭回归),在正规方程中加入正则项:
    d = ( A T A + λ I ) − 1 A T b \mathbf{d} = (A^T A + \lambda I)^{-1} A^T \mathbf{b} d=(ATA+λI)1ATb

其中, λ \lambda λ是正则化参数, I I I是单位矩阵。正则化可以改善矩阵 A T A A^T A ATA的条件数,增强解的稳定性。

鲁棒估计方法:如RANSAC,通过迭代剔除异常值,提高求解的鲁棒性。

3.5 最终光流向量

通过最小二乘法求解,得到光流向量:

d = [ u v ] = ( A T A ) − 1 A T b \mathbf{d} = \begin{bmatrix} u \\ v \end{bmatrix} = (A^T A)^{-1} A^T \mathbf{b} d=[uv]=(ATA)1ATb

其中, u u u v v v分别表示光流在 x x x y y y方向上的分量。

4.矩阵性质与数值稳定性

为了确保最小二乘解的有效性和稳定性,需要深入理解矩阵 A A A及其转置乘积 A T A A^T A ATA的性质。

4.1 矩阵 A A A的结构

矩阵 A A A的每一行对应窗口内一个像素点的空间梯度:

A = [ I x 1 I y 1 I x 2 I y 2 ⋮ ⋮ I x n I y n ] A = \begin{bmatrix} I_{x_1} & I_{y_1} \\ I_{x_2} & I_{y_2} \\ \vdots & \vdots \\ I_{x_n} & I_{y_n} \end{bmatrix} A= Ix1Ix2IxnIy1Iy2Iyn

4.2 矩阵 A T A A^T A ATA的性质

  • 对称性: A T A A^T A ATA是对称矩阵。
  • 正定性:
    • 如果窗口内的梯度向量 [ I x i I y i ] \begin{bmatrix} I_{xi} \ I_{yi} \end{bmatrix} [Ixi Iyi]线性无关,则 A T A A^TA ATA是正定的。
    • 否则, A T A A^T A ATA是半正定的。

4.3 条件数与数值稳定性

  • 条件数:矩阵 A T A A^T A ATA的条件数衡量了其数值稳定性。条件数越大,矩阵越接近奇异,数值求解越不稳定。
  • 梯度分布:窗口内梯度向量的分布影响 A T A A^T A ATA的条件数。例如:
    • 高纹理区域:梯度分布丰富, A T A A^T A ATA的条件数较小,数值稳定。
    • 低纹理区域:梯度分布稀疏或线性相关, A T A A^T A ATA的条件数较大,可能导致数值不稳定。

4.4 解决数值不稳定的方法

  • 窗口大小调整:在低纹理区域使用更大的窗口,增加方程组的约束。
  • 正则化:如前述,加入正则项改善条件数。
  • 奇异值分解:使用SVD求解最小二乘问题,避免直接求逆带来的数值不稳定。
  • 多尺度方法:通过构建图像金字塔,从粗到细估计光流,缓解局部条件数较差的问题。

三、算法步骤

Lucas-Kanade方法的具体实现包括以下主要步骤,每个步骤都包含若干子步骤和技术细节:

1. 图像预处理

图像预处理是Lucas-Kanade方法中的首要步骤,其主要目的是提高光流估计的准确性和鲁棒性。预处理通常包括图像平滑和梯度计算。

1.1. 图像平滑

目的:减小图像噪声对光流估计的影响,提升梯度计算的稳定性。

方法

  • 高斯滤波:最常用的平滑方法,通过卷积一个高斯核来平滑图像,减少高频噪声。
    G ( x , y ) = 1 2 π σ 2 e − x 2 + y 2 2 σ 2 G(x, y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2 + y^2}{2\sigma^2}} G(x,y)=2πσ21e2σ2x2+y2

其中, σ \sigma σ是高斯核的标准差,控制平滑的程度。

  • 其他平滑方法:如中值滤波、双边滤波等,根据具体应用需求选择。

实现细节:

  • 核大小选择:核大小通常选择为奇数(如3×3、5×5、7×7),较大的核提供更强的平滑效果,但可能模糊细节。
  • 边缘处理:常用的方法包括镜像扩展、零填充等,以处理图像边缘的卷积操作。

1.2. 梯度计算

目的:计算图像在 x x x y y y方向的空间梯度 I x I_x Ix I y I_y Iy,以及时间梯度 I t I_t It,为光流约束方程提供必要的信息。

方法:

  • 空间梯度
    • Sobel算子:
      I x = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] ∗ I I y = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] ∗ I I_x =\begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix}* I \quad I_y = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix}* I Ix= 121000+1+2+1 IIy= 10+120+210+1 I

    • Scharr算子:类似于Sobel算子,但具有更好的旋转对称性和梯度响应。

    • Prewitt算子:另一种常见的梯度算子,与Sobel类似。

  • 时间梯度
    • 计算连续两帧图像 I t ( x , y ) = I ( x , y , t + Δ t ) − I ( x , y , t ) I_t(x, y) = I(x, y, t+\Delta t) - I(x, y, t) It(x,y)=I(x,y,t+Δt)I(x,y,t)
    • 可以使用更精确的方法,如中心差分法,计算时间梯度。

实现细节:

  • 边缘处理:与平滑步骤类似,需要处理图像边缘的梯度计算。

  • 梯度精度:选择合适的梯度算子和参数,以平衡梯度的精度和计算复杂度。

2. 窗口选择

窗口选择是Lucas-Kanade方法中的关键步骤,决定了局部光流估计的区域范围和光流一致性的假设。

2.1. 窗口类型

  • 固定窗口:常用的固定大小窗口(如3×3、5×5、7×7),适用于光流估计假设有效的区域。
  • 自适应窗口:根据图像内容动态调整窗口大小,如在纹理丰富区域使用较小窗口,在纹理稀疏区域使用较大窗口。

2.2. 窗口形状

  • 矩形窗口:最常用的窗口形状,覆盖一个矩形区域。
  • 其他形状:如圆形窗口,可以更好地适应某些应用需求。

2.3. 窗口中心的选择

  • 稠密光流:为图像中的每个像素点选择窗口,生成稠密的光流场。
  • 稀疏光流:仅为图像中的特征点(如角点、边缘点)选择窗口,减少计算量。

2.4. 窗口大小的选择

影响因素:

  • 运动范围:较大的窗口能够捕捉更大范围的运动,但可能降低局部精度。
  • 纹理信息:较大的窗口在纹理稀疏区域更有效,较小窗口在纹理丰富区域更精准。

选择策略:

  • 经验法则:根据应用场景和图像特性选择合适的窗口大小。
  • 多尺度方法:结合金字塔Lucas-Kanade方法,通过多尺度处理适应不同窗口大小。

3. 构建局部方程组

在选定的窗口内,构建光流约束方程组,以求解统一的光流向量。

3.1. 收集窗口内的梯度和时间梯度

对于窗口内的每个像素点 ( x i , y i ) (x_i, y_i) (xi,yi),收集其空间梯度 I x i I_{xi} Ixi I y i I_{yi} Iyi和时间梯度 I t i I_{ti} Iti

3.2. 构建矩阵$ A 和向量 和向量 和向量 b $

根据光流约束方程 I x u + I y v = − I t I_x u + I_y v = -I_t Ixu+Iyv=It,为窗口内的每个像素点构建一个方程,形成过定的线性方程组:

A = [ I x 1 I y 1 I x 2 I y 2 ⋮ ⋮ I x n I y n ] , b = [ − I t 1 − I t 2 ⋮ − I t n ] A = \begin{bmatrix} I_{x1} & I_{y1} \\ I_{x2} & I_{y2} \\ \vdots & \vdots \\ I_{xn} & I_{yn} \end{bmatrix}, \quad b = \begin{bmatrix} -I_{t1} \\ -I_{t2} \\ \vdots \\ -I_{tn} \end{bmatrix} A= Ix1Ix2IxnIy1Iy2Iyn ,b= It1It2Itn

其中,$ n $是窗口内像素点的数量。

3.3. 数据质量检查

在构建方程组之前,需检查数据质量:

  • 梯度的非零性:确保窗口内存在足够的梯度信息,避免平坦区域导致矩阵 A A A退化。
  • 异常值检测:剔除异常的梯度值或时间梯度,提升方程组的稳定性。

4. 最小二乘解法

通过最小二乘法求解过定的线性方程组,获得光流向量 ( u , v ) (u, v) (u,v)

4.1. 最小二乘法基本原理

目标是找到 ( u , v ) (u, v) (u,v),使得 ∣ A ⋅ [ u v ] − b ∣ 2 | A \cdot \begin{bmatrix} u \ v \end{bmatrix} - b |^2 A[u v]b2最小,即最小化误差平方和。

4.2. 解析解

当矩阵 A T A A^T A ATA可逆时,解析解为:

4.3 数值求解方法

直接求逆:在小窗口情况下(如3×3),可以直接计算 ( A T A ) − 1 A T b (A^T A)^{-1} A^T b (ATA)1ATb

矩阵分解

  • QR分解:分解 A A A Q R Q R QR,然后解 R [ u v ] = Q T b R \begin{bmatrix} u \ v \end{bmatrix} = Q^T b R[u v]=QTb
  • 奇异值分解(SVD):将 A A A分解为 U Σ V T U \Sigma V^T UΣVT,避免直接求逆,提高数值稳定性。
  • Cholesky分解:用于正定矩阵 A T A A^T A ATA的高效分解。

梯度下降法:在大规模问题或需要迭代优化时使用,但在小窗口情况下通常不必要。

4.4 正则化(可选)

为处理矩阵 A T A A^T A ATA接近奇异的情况,可以引入正则化项,如Tikhonov正则化:

[ u v ] = ( A T A + λ I ) − 1 A T b \begin{bmatrix} u \\ v \end{bmatrix} = (A^T A + \lambda I)^{-1} A^T b [uv]=(ATA+λI)1ATb

其中, λ \lambda λ是正则化参数, I I I是单位矩阵。

4.5. 解的有效性检查

  • 可逆性检查:确保 A T A A^T A ATA的条件数合理,避免数值不稳定。
  • 异常值检测:在解出 ( u , v ) (u, v) (u,v)后,检查是否存在异常值,如过大的光流向量,可能需要进一步处理。

5. 光流向量估计

将求解得到的光流向量 ( u , v ) (u, v) (u,v)赋值给窗口中心的像素点,完成局部光流估计。

5.1 光流场的构建

通过遍历整个图像,对每个需要计算光流的像素点执行上述步骤,构建整个图像的光流场。

5.2 数据结构选择

  • 稀疏光流:存储为特征点及其光流向量的列表,适用于稀疏光流估计。
  • 稠密光流:存储为与图像同尺寸的二维向量场,适用于稠密光流估计。

5.3 插值与填充(可选)

在某些应用中,可能需要对光流场进行插值或填充,以处理缺失的光流向量或增强光流场的连续性。

5.4 后处理(可选)

  • 光流平滑:对光流场进行平滑处理,减少噪声影响。
  • 边缘保留:在光流估计中保留运动边界,避免模糊运动区域。
  • 一致性检查:通过前向和后向光流的一致性,检测和剔除错误的光流向量。

6. 整体流程概述

以下是Lucas-Kanade方法的整体算法流程:

1.输入:连续两帧图像 I ( t ) I(t) I(t) I ( t + Δ t ) I(t+\Delta t) I(t+Δt)

2.预处理

  • I ( t ) I(t) I(t) I ( t + Δ t ) I(t+\Delta t) I(t+Δt)进行高斯平滑。
  • 计算 I x I_x Ix I y I_y Iy I t I_t It

3.光流估计:

对每个需要计算光流的像素点:

  • 选择包含该点的窗口。
  • 构建局部光流约束方程组。
  • 通过最小二乘法求解 ( u , v ) (u, v) (u,v)
  • 赋值给窗口中心点。

4.输出:光流场,包含每个像素点的光流向量 ( u , v ) (u, v) (u,v)

7.实例分析

1. 示例场景
假设在一个 3 × 3 3 \times 3 3×3的窗口内,有以下梯度信息:

Pixel I x I_x Ix I y I_y Iy I t I_t It
110-1
220-2
310-1
4010
5010
6010
710-1
820-2
910-1

2. 构建矩阵 A A A和向量 b b b
根据上表,矩阵 A A A和向量 b b b如下:

A = [ 1 0 2 0 1 0 0 1 0 1 0 1 1 0 2 0 1 0 ] , b = [ − ( − 1 ) − ( − 2 ) − ( − 1 ) − 0 − 0 − 0 − ( − 1 ) − ( − 2 ) − ( − 1 ) ] = [ 1 2 1 0 0 0 1 2 1 ] A = \begin{bmatrix} 1 & 0 \\ 2 & 0 \\ 1 & 0 \\ 0 & 1 \\ 0 & 1 \\ 0 & 1 \\ 1 & 0 \\ 2 & 0 \\ 1 & 0 \end{bmatrix}, \quad b = \begin{bmatrix} -(-1) \\ -(-2) \\ -(-1) \\ -0 \\ -0 \\ -0 \\ -(-1) \\ -(-2) \\-(-1)\end{bmatrix} =\begin{bmatrix} 1 \\ 2 \\ 1 \\ 0 \\ 0 \\ 0 \\ 1 \\ 2 \\ 1 \end{bmatrix} A= 121000121000111000 ,b= (1)(2)(1)000(1)(2)(1) = 121000121

3. 计算 A T A A^T A ATA A T b A^T b ATb

A T A = [ 1 2 1 0 0 0 1 2 1 0 0 0 1 1 1 0 0 0 ] [ 1 0 2 0 1 0 0 1 0 1 0 1 1 0 2 0 1 0 ] = [ 1 2 + 2 2 + 1 2 + 0 2 + 0 2 + 0 2 + 1 2 + 2 2 + 1 2 1 ⋅ 0 + 2 ⋅ 0 + 1 ⋅ 0 + 0 ⋅ 1 + 0 ⋅ 1 + 0 ⋅ 1 + 1 ⋅ 0 + 2 ⋅ 0 + 1 ⋅ 0 0 ⋅ 1 + 0 ⋅ 2 + 0 ⋅ 1 + 1 ⋅ 0 + 1 ⋅ 0 + 1 ⋅ 0 + 0 ⋅ 1 + 0 ⋅ 2 + 0 ⋅ 1 0 2 + 0 2 + 0 2 + 1 2 + 1 2 + 1 2 + 0 2 + 0 2 + 0 2 ] = [ 12 0 0 3 ] A^T A = \begin{bmatrix} 1 & 2 & 1 & 0 & 0 & 0 & 1 & 2 & 1 \\ 0 & 0 & 0 & 1 & 1 & 1 & 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} 1 & 0 \\ 2 & 0 \\ 1 & 0 \\ 0 & 1 \\ 0 & 1 \\ 0 & 1 \\ 1 & 0 \\ 2 & 0 \\ 1 & 0 \end{bmatrix} =\begin{bmatrix} 1^2 + 2^2 + 1^2 + 0^2 + 0^2 + 0^2 + 1^2 + 2^2 + 1^2 & 1 \cdot 0 + 2 \cdot 0 + 1 \cdot 0 + 0 \cdot 1 + 0 \cdot 1 + 0 \cdot 1 + 1 \cdot 0 + 2 \cdot 0 + 1 \cdot 0 \\ 0 \cdot 1 + 0 \cdot 2 + 0 \cdot 1 + 1 \cdot 0 + 1 \cdot 0 + 1 \cdot 0 + 0 \cdot 1 + 0 \cdot 2 + 0 \cdot 1 & 0^2 + 0^2 + 0^2 + 1^2 + 1^2 + 1^2 + 0^2 + 0^2 + 0^2 \end{bmatrix} =\begin{bmatrix} 12 & 0 \\ 0 & 3 \end{bmatrix} ATA=[102010010101102010] 121000121000111000 =[12+22+12+02+02+02+12+22+1201+02+01+10+10+10+01+02+0110+20+10+01+01+01+10+20+1002+02+02+12+12+12+02+02+02]=[12003]

A T b = [ 1 2 1 0 0 0 1 2 1 0 0 0 1 1 1 0 0 0 ] [ 1 2 1 0 0 0 1 2 1 ] = [ 1 ⋅ 1 + 2 ⋅ 2 + 1 ⋅ 1 + 0 ⋅ 0 + 0 ⋅ 0 + 0 ⋅ 0 + 1 ⋅ 1 + 2 ⋅ 2 + 1 ⋅ 1 0 ⋅ 1 + 0 ⋅ 2 + 0 ⋅ 1 + 1 ⋅ 0 + 1 ⋅ 0 + 1 ⋅ 0 + 0 ⋅ 1 + 0 ⋅ 2 + 0 ⋅ 1 ] = [ 1 + 4 + 1 + 0 + 0 + 0 + 1 + 4 + 1 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 ] = [ 12 0 ] A^T b = \begin{bmatrix} 1 & 2 & 1 & 0 & 0 & 0 & 1 & 2 & 1 \\ 0 & 0 & 0 & 1 & 1 & 1 & 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} 1 \\ 2 \\ 1 \\ 0 \\ 0 \\ 0 \\ 1 \\ 2 \\ 1 \end{bmatrix} =\begin{bmatrix} 1 \cdot 1 + 2 \cdot 2 + 1 \cdot 1 + 0 \cdot 0 + 0 \cdot 0 + 0 \cdot 0 + 1 \cdot 1 + 2 \cdot 2 + 1 \cdot 1 \\ 0 \cdot 1 + 0 \cdot 2 + 0 \cdot 1 + 1 \cdot 0 + 1 \cdot 0 + 1 \cdot 0 + 0 \cdot 1 + 0 \cdot 2 + 0 \cdot 1 \end{bmatrix} =\begin{bmatrix} 1 + 4 + 1 + 0 + 0 + 0 + 1 + 4 + 1 \\ 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 \end{bmatrix} =\begin{bmatrix} 12 \\ 0 \end{bmatrix} ATb=[102010010101102010] 121000121 =[11+22+11+00+00+00+11+22+1101+02+01+10+10+10+01+02+01]=[1+4+1+0+0+0+1+4+10+0+0+0+0+0+0+0+0]=[120]

4. 求解正规方程
正规方程为:
A T A d = A T b ⟹ [ 12 0 0 3 ] [ u v ] = [ 12 0 ] A^T A d = A^T b \implies \begin{bmatrix} 12 & 0 \\ 0 & 3 \end{bmatrix} \begin{bmatrix} u \\ v \end{bmatrix} =\begin{bmatrix} 12 \\ 0 \end{bmatrix} ATAd=ATb[12003][uv]=[120]

解得:

12 u = 12 ⟹ u = 1 3 v = 0 ⟹ v = 0 12u = 12 \implies u = 1 \\ 3v = 0 \implies v = 0 12u=12u=13v=0v=0

因此,光流向量为 ( u , v ) = ( 1 , 0 ) (u, v) = (1, 0) (u,v)=(1,0),表示图像在 x x x方向上以单位速度移动,而在 y y y方向上无运动。

5. 分析

  • 矩阵 A T A A^T A ATA的可逆性:在本例中, A T A A^T A ATA是对角矩阵,且对角元素均大于零,故可逆。
  • 条件数 A T A A^T A ATA的条件数为 λ max λ min = 12 3 = 4 \frac{\lambda_{\text{max}}}{\lambda_{\text{min}}} = \frac{12}{3} = 4 λminλmax=312=4,较小,数值稳定。
  • 光流方向:光流仅在 x x x方向上有运动,符合光流向量的计算结果。

四、代码实例

简单版

import cv2
import numpy as npdef lucas_kanade_optical_flow(I1, I2, window_size=5, max_iterations=10):# 1. Preprocessing: Convert to grayscale if necessaryif len(I1.shape) == 3:I1 = cv2.cvtColor(I1, cv2.COLOR_BGR2GRAY)if len(I2.shape) == 3:I2 = cv2.cvtColor(I2, cv2.COLOR_BGR2GRAY)# 2. Smooth imagesI1_blur = cv2.GaussianBlur(I1, (5, 5), sigmaX=1.5)I2_blur = cv2.GaussianBlur(I2, (5, 5), sigmaX=1.5)# 3. Compute gradientsIx = cv2.Sobel(I1_blur, cv2.CV_64F, 1, 0, ksize=3)Iy = cv2.Sobel(I1_blur, cv2.CV_64F, 0, 1, ksize=3)It = I2_blur - I1_blur# 4. Initialize flow fieldflow_u = np.zeros(I1.shape)flow_v = np.zeros(I1.shape)# 5. Iterate over each pixelhalf_w = window_size // 2for y in range(half_w, I1.shape[0] - half_w):for x in range(half_w, I1.shape[1] - half_w):# Define windowIx_window = Ix[y - half_w : y + half_w + 1, x - half_w : x + half_w + 1].flatten()Iy_window = Iy[y - half_w : y + half_w + 1, x - half_w : x + half_w + 1].flatten()It_window = It[y - half_w : y + half_w + 1, x - half_w : x + half_w + 1].flatten()# Construct A and bA = np.vstack((Ix_window, Iy_window)).T  # Shape: (window_size^2, 2)b = -It_window  # Shape: (window_size^2,)# Check if A^T A is invertibleATA = A.T @ Aif np.linalg.cond(ATA) < 1 / np.finfo(float).eps:# Solve for (u, v) using least squaresnu, nv = np.linalg.lstsq(A, b, rcond=None)[0]flow_u[y, x] = nuflow_v[y, x] = nvelse:flow_u[y, x] = 0flow_v[y, x] = 0# Combine flow fieldsflow = np.stack((flow_u, flow_v), axis=2)return flow# 示例使用
if __name__ == "__main__":# 读取连续两帧图像I1 = cv2.imread('frame1.png')I2 = cv2.imread('frame2.png')# 计算光流flow = lucas_kanade_optical_flow(I1, I2, window_size=5)# 可视化光流hsv = np.zeros_like(I1)hsv = cv2.cvtColor(hsv, cv2.COLOR_BGR2HSV)mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])hsv[...,0] = ang * 180 / np.pi / 2hsv[...,1] = 255hsv[...,2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)rgb_flow = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)cv2.imshow('Optical Flow', rgb_flow)cv2.waitKey(0)cv2.destroyAllWindows()

复杂版:图像对齐融合中的光流法

import rawpy
import cv2
import numpy as np
import matplotlib.pyplot as plt
import imutilsdef read_dng(file_path, scale=1.0):"""读取DNG图像并转换为RGB和灰度图像。Args:file_path (str): DNG图像文件路径。scale (float): 图像缩放比例,默认为1.0(不缩放)。Returns:tuple: (RGB图像, 灰度图像)"""with rawpy.imread(file_path) as raw:# 使用raw.postprocess将RAW数据转换为RGB图像,设置输出位深为16位rgb = raw.postprocess(use_camera_wb=True, no_auto_bright=True, output_bps=16)if scale != 1.0:rgb = cv2.resize(rgb, None, fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)# 将RGB图像转换为灰度图gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)# 检查灰度图像的类型if gray.dtype == np.uint16:# 16-bit到8-bit的转换,归一化gray = cv2.convertScaleAbs(gray, alpha=(255.0 / 65535.0))elif gray.dtype != np.uint8:# 如果不是8位,进行归一化并转换为8位gray = cv2.normalize(gray, None, 0, 255, cv2.NORM_MINMAX)gray = gray.astype(np.uint8)# 高斯平滑,减少噪声gray = cv2.GaussianBlur(gray, (5, 5), 0)return rgb, graydef detect_features(gray, maxCorners=500, qualityLevel=0.3, minDistance=7, blockSize=7):"""使用Shi-Tomasi方法检测特征点。Args:gray (ndarray): 灰度图像。maxCorners (int): 最大特征点数。qualityLevel (float): 质量水平阈值。minDistance (float): 特征点之间的最小距离。blockSize (int): 阻塞大小。Returns:ndarray: 检测到的特征点坐标。"""feature_params = dict(maxCorners=maxCorners,qualityLevel=qualityLevel,minDistance=minDistance,blockSize=blockSize)p = cv2.goodFeaturesToTrack(gray, mask=None, **feature_params)return pdef track_features(old_gray, new_gray, p0, lk_params):"""使用Lucas-Kanade方法跟踪特征点。Args:old_gray (ndarray): 上一帧的灰度图像。new_gray (ndarray): 当前帧的灰度图像。p0 (ndarray): 上一帧的特征点坐标。lk_params (dict): Lucas-Kanade方法参数。Returns:tuple: (新特征点, 上一帧的特征点, 状态)"""p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, new_gray, p0, None, **lk_params)if p1 is None:return None, None, Nonegood_new = p1[st == 1]good_old = p0[st == 1]return good_new, good_old, stdef estimate_global_transform(good_new, good_old):"""使用RANSAC方法估计全局仿射变换矩阵。Args:good_new (ndarray): 当前帧的特征点。good_old (ndarray): 参考帧的特征点。Returns:tuple: (变换矩阵, inliers mask)"""if len(good_new) < 3:return None, NoneM, mask = cv2.estimateAffinePartial2D(good_old, good_new, method=cv2.RANSAC, ransacReprojThreshold=5.0)return M, maskdef estimate_local_transforms(good_new, good_old, grid_size=(8, 8), block_size=50):"""分块估计局部仿射变换矩阵。Args:good_new (ndarray): 当前帧的特征点。good_old (ndarray): 参考帧的特征点。grid_size (tuple): 网格大小(x, y)。block_size (int): 每个块的大小。Returns:dict: 每个块的变换矩阵。"""transforms = {}for x in range(grid_size[0]):for y in range(grid_size[1]):# 定义块的区域mask = (good_new[:, 0] > x * block_size) & (good_new[:, 0] < (x + 1) * block_size) & \(good_new[:, 1] > y * block_size) & (good_new[:, 1] < (y + 1) * block_size)block_new = good_new[mask]block_old = good_old[mask]if len(block_new) >= 3:M, _ = cv2.estimateAffinePartial2D(block_old, block_new, method=cv2.RANSAC, ransacReprojThreshold=5.0)if M is not None:transforms[(x, y)] = Mreturn transformsdef apply_global_transform(img, M):"""应用全局仿射变换矩阵到图像。Args:img (ndarray): 目标图像。M (ndarray): 仿射变换矩阵。Returns:ndarray: 变换后的图像。"""if M is None:return imgrows, cols, _ = img.shapewarped = cv2.warpAffine(img, M, (cols, rows), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT)return warpeddef apply_local_transforms(img, transforms, grid_size=(8, 8), block_size=50):"""应用分块的局部仿射变换矩阵到图像。Args:img (ndarray): 目标图像。transforms (dict): 每个块的变换矩阵。grid_size (tuple): 网格大小(x, y)。block_size (int): 每个块的大小。Returns:ndarray: 变换后的图像。"""warped = np.zeros_like(img)for x in range(grid_size[0]):for y in range(grid_size[1]):M = transforms.get((x, y))if M is not None:# 定义块的区域start_x = x * block_sizestart_y = y * block_sizeend_x = (x + 1) * block_sizeend_y = (y + 1) * block_size# 提取块block = img[start_y:end_y, start_x:end_x]if block.shape[0] == 0 or block.shape[1] == 0:continue# 应用变换warped_block = cv2.warpAffine(block, M, (block.shape[1], block.shape[0]), flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT)warped[start_y:end_y, start_x:end_x] = warped_blockreturn warpeddef laplacian_pyramid_fusion(img1, img2, levels=5):"""使用拉普拉斯金字塔方法融合两张图像。Args:img1 (ndarray): 参考图像。img2 (ndarray): 对齐后的图像。levels (int): 金字塔层数。Returns:ndarray: 融合后的图像。"""# 构建高斯金字塔G1 = img1.copy()G2 = img2.copy()gp1 = [G1]gp2 = [G2]for i in range(levels):G1 = cv2.pyrDown(G1)G2 = cv2.pyrDown(G2)gp1.append(G1)gp2.append(G2)# 构建拉普拉斯金字塔lp1 = [gp1[levels - 1]]lp2 = [gp2[levels - 1]]for i in range(levels - 1, 0, -1):GE1 = cv2.pyrUp(gp1[i])GE2 = cv2.pyrUp(gp2[i])L1 = cv2.subtract(gp1[i - 1], GE1)L2 = cv2.subtract(gp2[i - 1], GE2)lp1.append(L1)lp2.append(L2)# 融合拉普拉斯金字塔LS = []for l1, l2 in zip(lp1, lp2):rows, cols, dpt = l1.shapels = np.hstack((l1, l2))LS.append(ls)# 重建图像fused_image = LS[0]for i in range(1, levels):fused_image = cv2.pyrUp(fused_image)fused_image = cv2.add(fused_image, LS[i])return fused_imagedef main():# 多帧DNG图像路径列表(请替换为实际的DNG文件路径)dng_files = ['./input_file/payload_N000.dng', './input_file/payload_N001.dng', './input_file/payload_N002.dng', './input_file/payload_N003.dng']# 读取所有DNG图像images_rgb = []images_gray = []for file in dng_files:rgb, gray = read_dng(file, scale=1.0)  # 根据需要调整scaleimages_rgb.append(rgb)images_gray.append(gray)# 设定参考帧为第一帧ref_rgb = images_rgb[0]ref_gray = images_gray[0]# 特征点检测参数feature_params = dict(maxCorners=500,qualityLevel=0.3,minDistance=7,blockSize=7)# Lucas-Kanade光流参数lk_params = dict(winSize=(15, 15),maxLevel=4,  # 使用更高的金字塔层数处理更大位移criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))# 检测参考帧的特征点p0 = detect_features(ref_gray, **feature_params)if p0 is None:print("参考帧未检测到任何特征点。")return# 初始化融合图像列表fused_images = [ref_rgb.copy()]# 依次处理后续帧for i in range(1, len(images_rgb)):print(f"Processing frame {i + 1}/{len(images_rgb)}...")current_rgb = images_rgb[i]current_gray = images_gray[i]# 跟踪特征点good_new, good_old, st = track_features(ref_gray, current_gray, p0, lk_params)if good_new is None or good_old is None:print(f"No features tracked in frame {i + 1}. Skipping...")continue# 全局变换矩阵估计M_global, mask_global = estimate_global_transform(good_new, good_old)if M_global is None:print(f"Global transform estimation failed for frame {i + 1}. Skipping...")continue# 应用全局变换对齐图像aligned_img = apply_global_transform(current_rgb, M_global)# 局部变换矩阵估计(分块)transforms_local = estimate_local_transforms(good_new, good_old, grid_size=(8, 8), block_size=50)# 应用局部变换对齐图像aligned_img_local = apply_local_transforms(aligned_img, transforms_local, grid_size=(8, 8), block_size=50)# 图像融合(拉普拉斯金字塔融合)fused_img = laplacian_pyramid_fusion(ref_rgb, aligned_img, levels=5)# 更新参考帧为当前融合结果ref_rgb = fused_img.copy()ref_gray = cv2.cvtColor(ref_rgb, cv2.COLOR_RGB2GRAY)ref_gray = cv2.GaussianBlur(ref_gray, (5, 5), 0)# 重新检测特征点# p0 = detect_features(ref_gray, **feature_params)if p0 is None:print(f"No features detected in fused frame {i + 1}.")# 添加到融合图像列表fused_images.append(fused_img)# 显示最终融合结果plt.figure(figsize=(10, 10))plt.imshow(cv2.cvtColor(fused_images[-1], cv2.COLOR_RGB2BGR))plt.title('Fused Image')plt.axis('off')plt.show()# 保存最终融合结果cv2.imwrite('fused_image.png', cv2.cvtColor(fused_images[-1], cv2.COLOR_RGB2BGR))print("Fused image saved as 'fused_image.png'.")if __name__ == "__main__":main()

五、优缺点分析

1. 优点

  • 计算效率高:Lucas-Kanade方法基于局部线性假设,计算简单,适合实时应用。
  • 实现简单:算法流程明确,易于实现,适合嵌入式系统和实时视频处理。
  • 鲁棒性强:通过窗口内多个像素点的约束,提高了对噪声和误差的鲁棒性。
  • 适用性广:在小位移和亮度变化较小的情况下,具有较高的准确性。

2. 缺点

  • 小位移限制:对于大位移或快速运动的场景,单次估计可能不准确,需多次迭代或金字塔方法。

  • 纹理依赖性:依赖于图像的梯度信息,对于纹理稀疏或均匀区域,光流估计不准确。

  • 遮挡处理困难:难以处理复杂的遮挡情况,可能导致错误的运动估计。

  • 假设限制:局部恒定光流和亮度恒定假设在实际场景中可能不完全成立,影响估计精度。

Lucas-Kanade方法作为光流估计的经典算法,凭借其简洁高效的特点在计算机视觉领域占据重要地位。尽管存在一些局限性,但通过各种改进和结合现代技术,Lucas-Kanade方法及其衍生算法在实际应用中依然表现出色。随着深度学习和计算能力的提升,未来光流估计将朝着更加精确、高效和智能化的方向发展,Lucas-Kanade方法仍将在其中发挥基础性作用。

参考文献:
经典光流算法Lucas-Kanade(有图助理解)

结~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502162.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑里msvcr120.dll文件丢失怎样修复?

电脑里msvcr120.dll文件丢失的修复指南 在电脑的日常使用中&#xff0c;我们可能会遇到各种各样的系统文件丢失问题&#xff0c;其中msvcr120.dll文件的丢失就是较为常见的一种。作为一名在软件开发领域深耕多年的从业者&#xff0c;我将为大家详细解析msvcr120.dll文件的重要…

windows终端conda activate命令行不显示环境名

问题&#xff1a; 始终不显示环境名 解决 首先需要配置conda的环境变量 确保conda --version能显示版本 然后对cmd进行初始化&#xff0c;如果用的是vscode中的终端&#xff0c;那需要对powershell进行初始化 Windows CMD conda init cmd.exeWindows PowerShell conda …

django vue3实现大文件分段续传(断点续传)

前端环境准备及目录结构&#xff1a; npm create vue 并取名为big-file-upload-fontend 通过 npm i 安装以下内容"dependencies": {"axios": "^1.7.9","element-plus": "^2.9.1","js-sha256": "^0.11.0&quo…

黑马跟学.苍穹外卖.Day01

黑马跟学.苍穹外卖.Day01 苍穹外卖-day01课程内容1. 软件开发整体介绍1.1 软件开发流程1.2 角色分工1.3 软件环境 2. 苍穹外卖项目介绍2.1 项目介绍2.2 产品原型2.3 技术选型 3. 开发环境搭建3.1 前端环境搭建3.2 后端环境搭建3.2.1 熟悉项目结构3.2.2 Git版本控制3.2.3 数据库…

基于动力学的MPC控制器设计盲点解析

文章目录 Apollo MPC控制器的设计架构误差模型和离散化预测模型推导目标函数和约束设计优化求解优化OSQP求解器参考文献 Apollo MPC控制器的设计架构 误差模型和离散化 状态变量和控制变量 1、Apollo MPC控制器中状态变量主要有如下6个 matrix_state_ Matrix::Zero(basic_stat…

2025/1/1 路由期末复习作业二

呼呼呼祝大家元旦节快乐啦&#xff01;&#xff08;我顶着我超重的黑眼圈说&#xff09; 昨天一个人在寝室一边吃泡面&#xff0c;一边看步步惊心&#xff0c;一边吃一边哭呜呜呜呜呜若曦为什么不和八爷在一起好好爱&#xff0c;就因为他不当皇帝蛮&#xff01;难测最是帝王心…

面试题解,JVM中的“类加载”剖析

一、JVM类加载机制说一下 其中&#xff0c;从加载到初始化就是我们的类加载阶段&#xff0c;我们逐一来分析 加载 “加载 loading”是整个类加载&#xff08;class loading&#xff09;过程的一个阶段&#xff0c;加载阶段JVM需要完成以下 3 件事情&#xff1a; 1&#xff0…

后端开发-Maven

环境说明&#xff1a; windows系统&#xff1a;11版本 idea版本&#xff1a;2023.3.2 Maven 介绍 Apache Maven 是一个 Java 项目的构建管理和理解工具。Maven 使用一个项目对象模型&#xff08;POM&#xff09;&#xff0c;通过一组构建规则和约定来管理项目的构建&#xf…

UML之泛化、特化和继承

在UML&#xff08;统一建模语言&#xff09;中&#xff0c;泛化&#xff08;Generalization&#xff09;和特化&#xff08;Specialization&#xff09;是面向对象思想中继承&#xff08;Inheritance&#xff09;关系的重要概念&#xff0c;它们描述类与类&#xff08;或用例与…

【时时三省】(C语言基础)常见的动态内存错误2

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 对非动态开辟空间内存使用free释放 示例&#xff1a; 这个arr数组是在栈上的 *p指向的就是arr 对非动态空间也用了free ferr只能在动态开辟空间使用 使用free释放一块动态开辟空间的一部分…

leecode718.最长重复子数组

二维空间版 class Solution { public:int findLength(vector<int>& nums1, vector<int>& nums2) {int mnums1.size(),nnums2.size();vector<vector<int>> dp(m,vector<int>(n));int result0;for(int i0;i<m;i)if(nums1[i]nums2[0]){…

「Mac畅玩鸿蒙与硬件54」UI互动应用篇31 - 滑动解锁屏幕功能

本篇教程将实现滑动解锁屏幕功能&#xff0c;通过 Slider 组件实现滑动操作&#xff0c;学习事件监听、状态更新和交互逻辑的实现方法。 关键词 滑动解锁UI交互状态管理动态更新事件监听 一、功能说明 滑动解锁屏幕功能包含以下功能&#xff1a; 滑动解锁区域&#xff1a;用…

电子应用设计方案86:智能 AI背景墙系统设计

智能 AI 背景墙系统设计 一、引言 智能 AI 背景墙系统旨在为用户创造一个动态、个性化且具有交互性的空间装饰体验&#xff0c;通过融合先进的技术和创意设计&#xff0c;提升室内环境的美观度和功能性。 二、系统概述 1. 系统目标 - 提供多种主题和风格的背景墙显示效果&…

基于物联网疫苗冷链物流监测系统设计

1. 项目开发背景 随着全球对疫苗运输要求的提高&#xff0c;特别是针对温度敏感型药品&#xff08;如疫苗&#xff09;的冷链管理&#xff0c;如何保证疫苗在运输过程中的温度、湿度、震动等环境因素的稳定性已成为亟需解决的问题。疫苗运输过程中&#xff0c;任何温度或湿度的…

Trimble天宝X9三维扫描仪为建筑外墙检测提供了全新的解决方案【沪敖3D】

随着城市化进程的快速推进&#xff0c;城市高层建筑不断增多&#xff0c;对建筑质量的要求也在不断提高。建筑外墙检测&#xff0c;如平整度和垂直度检测&#xff0c;是衡量建筑质量的重要指标之一。传统人工检测方法不仅操作繁琐、效率低下&#xff0c;还难以全面反映墙体的真…

瑞吉外卖项目学习笔记(十)修改套餐、删除套餐、起售和停售套餐

瑞吉外卖项目学习笔记(一)准备工作、员工登录功能实现 瑞吉外卖项目学习笔记(二)Swagger、logback、表单校验和参数打印功能的实现 瑞吉外卖项目学习笔记(三)过滤器实现登录校验、添加员工、分页查询员工信息 瑞吉外卖项目学习笔记(四)TableField(fill FieldFill.INSERT)公共字…

Python 实时获取Linux服务器信息

在进行服务器监控、运维管理时&#xff0c;实时获取服务器信息至关重要。特别是在 Linux 环境下&#xff0c;我们常常需要获取系统的运行状态、资源占用情况以及硬件信息。如果你是运维人员、开发者或是正在做自动化运维任务的人&#xff0c;那么如何高效地实时获取 Linux 服务…

MATLAB程序转C# WPF,dll集成,混合编程

工作中遇到一个需求&#xff0c;有一部分算法的代码需要MATLAB来进行处理&#xff0c;而最后需要集成到C#中的wpf项目中去&#xff0c;选择灵活性更高的dll&#xff0c;去进行集成。&#xff08;可以简单理解为&#xff1a;将MATLAB的函数&#xff0c;变为C#中类的函数成员&…

「Mac畅玩鸿蒙与硬件49」UI互动应用篇26 - 数字填色游戏

本篇教程将带你实现一个数字填色小游戏&#xff0c;通过简单的交互逻辑&#xff0c;学习如何使用鸿蒙开发组件创建趣味性强的应用。 关键词 UI互动应用数字填色动态交互逻辑判断游戏开发 一、功能说明 数字填色小游戏包含以下功能&#xff1a; 数字选择&#xff1a;用户点击…

深入理解 pytest Fixture 方法及其应用

在 Python 自动化测试领域&#xff0c;pytest 是当之无愧的王者。提到 pytest&#xff0c;不得不说它的一大核心功能——Fixture。Fixture 的强大&#xff0c;让复杂的测试流程变得井井有条&#xff0c;让测试代码更加灵活和可复用。 那么&#xff0c;pytest 的 Fixture 究竟是…