当前位置: 首页 > news >正文

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(有图助理解)

结~~~

相关文章:

Lucas-Kanade光流法详解

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正。 光流&#xff08;Optical Flow&#xff09;描述的是图像序列中各像素点随时间的运动情况&#xff0c;是计算机视觉中的基本问题之一。光流问题涉及尝试找出一幅图像中的许多点在第二幅图像中移动的…...

python多张图片生成/合成gif

你可以通过调整帧率来提高GIF的流畅度。默认情况下,代码中的帧率为每秒1帧(fps=1)。我们可以增加这个值来加快动画速度。 下面是修改后的代码,将帧率从每秒1帧提高到每秒5帧(你可以根据需要进一步调整): 在这个版本中,我添加了一个可选参数fps,默认值为5帧每秒。你可…...

iptable限制多个端口出站

iptable限制多个端口出站 安装包 rootiptable:/home/bb# apt-get update rootiptable:/home/bb# apt-get -y install iptables iptables-restoreweb准备 rootweb:/home/bb/test-iptables# docker run -itd --name web -p 80:80 -v ./web1/index.html:/usr/share/nginx/html…...

springmvc--请求参数的绑定

目录 一、创建项目&#xff0c;pom文件 二、web.xml 三、spring-mvc.xml 四、index.jsp 五、实体类 Address类 User类 六、UserController类 七、请求参数解决中文乱码 八、配置tomcat,然后启动tomcat 1. 2. 3. 4. 九、接收Map类型 1.直接接收Map类型 &#x…...

Redis查询缓存

什么是缓存&#xff1f; 缓存是一种提高数据访问效率的技术&#xff0c;通过在内存中存储数据的副本来减少对数据库或其他慢速存储设备的频繁访问。缓存通常用于存储热点数据或计算代价高的结果&#xff0c;以加快响应速度。 添加Redis缓存有什么好处&#xff1f; Redis 基…...

双馈风电DFIG并网系统次转子侧变流器RSC抑制策略研究基于LADRC和重复控制的方法

风电装机容量的持续增长以及电力电子装置的大规模接入&#xff0c;导致电网强度降低&#xff0c;系 统运行特性发生深刻变化&#xff0c;严重威胁风电并网系统的安全稳定运行。因此本文以双馈风 电场经串补线路并网系统为研究对象&#xff0c;在深入分析双馈风电并网系统振荡…...

国产编辑器EverEdit - 使用技巧:变量重命名的一种简单替代方法

1 使用技巧&#xff1a;变量重命名的一种简单替代方法 1.1 应用场景 写过代码的都知道&#xff0c;经常添加功能的时候&#xff0c;是把别的地方的代码拷贝过来&#xff0c;改吧改吧&#xff0c;就能用了&#xff0c;改的过程中&#xff0c;就涉及到一个变量名的问题&#xff…...

使用SSH建立内网穿透,能够访问内网的web服务器

搞了一个晚上&#xff0c;终于建立了一个内网穿透。和AI配合&#xff0c;还是得自己思考&#xff0c;AI配合才能搞定&#xff0c;不思考只依赖AI也不行。内网服务器只是简单地使用了python -m http.server 8899&#xff0c;但是对于Gradio建立的服务器好像不行&#xff0c;会出…...

JWT认证实战

JWT&#xff08;JSON Web Token&#xff09;是一种轻量级的、基于 JSON 的开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在各方之间安全地传递信息。JWT 的特点是结构简单、轻量化和跨平台支持&#xff0c;适用于用户身份验证、信息加密以及无状态的 API 访问控制…...

计算机网络 (23)IP层转发分组的过程

一、IP层的基本功能 IP层&#xff08;Internet Protocol Layer&#xff09;是网络通信模型中的关键层&#xff0c;属于OSI模型的第三层&#xff0c;即网络层。它负责在不同网络之间传输数据包&#xff0c;实现网络间的互联。IP层的主要功能包括寻址、路由、分段和重组、错误检测…...

权限管理的方法

模块化分类 功能模块划分 把人资管理系统按业务逻辑拆分成清晰的功能区&#xff0c;例如招聘管理、培训管理、绩效管理、员工档案管理等。招聘管理模块下还能细分职位发布、简历筛选、面试安排等子功能&#xff1b;员工档案管理涵盖基本信息、教育经历、工作履历录入与查询等。…...

【郑大主办、ACM出版、EI稳定检索】第四届密码学、网络安全与通信技术国际会议 (CNSCT 2025)

第四届密码学、网络安全与通信技术国际会议(CNSCT 2025)将于2025年1月17-19日在中国郑州盛大启幕&#xff08;线上召开&#xff09;。本次会议旨在汇聚全球密码学、网络安全与通信技术领域的顶尖学者、研究人员与行业领袖&#xff0c;共同探索计算机科学的最新进展与未来趋势。…...

48小时,搭建一个设备巡检报修系统

背景 时不时的&#xff0c;工地的设备又出了状况。巡检人员一顿懵逼、维修人员手忙脚乱&#xff0c;操作工人抱怨影响进度。老板看着待完成的订单&#xff0c;就差骂娘了&#xff1a;“这么搞下去&#xff0c;还能有效率吗&#xff1f;”。 于是&#xff0c;抱着试一试的心态…...

基于Redisson实现重入锁

一. 分布式锁基础 在分布式系统中&#xff0c;当多个客户端&#xff08;应用实例&#xff09;需要访问同一资源时&#xff0c;可以使用分布式锁来确保同一时刻只有一个客户端能访问该资源。Redis作为高性能的内存数据库&#xff0c;提供了基于键值对的分布式锁实现&#xff0c…...

Java文件操作的简单示例

使用原生库 创建空白文件 package com.company; import java.io.File; import java.io.IOException;public class Main {public static void main(String[] args) {File f new File("newfile.txt");try {boolean flag f.createNewFile();System.out.println(&quo…...

删除与增加特定行

1.删除特定行 new_df <- df[-c(4), ] #删除第4行 new_df <- df[-c(2:4), ] #去除第2-4行 new_df <- subset(df, col1 < 10 & col2 < 6) #删除特定第一列<10和第二列&#xff1c;6的行。按名字删除 无论行列&#xff0c;可以找出对应索引或构造相同长…...

动态规划六——两个数组的dp问题

目录 题目一——1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 题目二——1035. 不相交的线 - 力扣&#xff08;LeetCode&#xff09; 题目三——115. 不同的子序列 - 力扣&#xff08;LeetCode&#xff09; 题目四—— 44. 通配符匹配 - 力扣&#xff08;…...

项目优化之策略模式

目录 策略模式基本概念 策略模式的应用场景 实际项目中具体应用 项目背景&#xff1a; 策略模式解决方案&#xff1a; 计费模块策略模式简要代码 策略模式基本概念 策略模式(Strategy Pattern) 是一种行为型设计模式&#xff0c;把算法的使用放到环境类中&#xff0c;而算…...

[读书日志]从零开始学习Chisel 第四篇:Scala面向对象编程——操作符即方法(敏捷硬件开发语言Chisel与数字系统设计)

3.2操作符即方法 3.2.1操作符在Scala中的解释 在其它语言中&#xff0c;定义了一些基本的类型&#xff0c;但这些类型并不是我们在面向对象中所说的类。比如说1&#xff0c;这是一个int类型常量&#xff0c;但不能说它是int类型的对象。针对这些数据类型&#xff0c;存在一些…...

三子棋游戏

目录 1.创建项目 2.主函数编写 3.菜单函数编写 4.宏定义棋盘行和列 5.棋盘初始化 6.打印棋盘 7.玩家下棋 8.电脑下棋 9.平局判断 10.输赢判断 11.game函数 三子棋游戏&#xff08;通过改变宏定义可以变成五子棋&#xff09;&#xff0c;玩家与电脑下棋 1.创建项目…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing

Muffin 论文 现有方法 CRADLE 和 LEMON&#xff0c;依赖模型推理阶段输出进行差分测试&#xff0c;但在训练阶段是不可行的&#xff0c;因为训练阶段直到最后才有固定输出&#xff0c;中间过程是不断变化的。API 库覆盖低&#xff0c;因为各个 API 都是在各种具体场景下使用。…...