[LK光流法,disflow using Dense Inverse Search, VariationalRefinement变分优化 原理和代码]

news2024/11/16 3:29:48

文章目录

  • 1.Fast Optical Flow using Dense Inverse Search
    • 1.1 W的含义:
    • 1.2 LK光流模型
    • 1.3 LK光流模型求解(不含迭代)
    • 1.4 LK光流模型迭代求解
    • 1.5 dis_flow方法中的 LK光流模型
    • 1.6 disflow代码分析
  • 2.0 disflow中的VariationalRefinement方法
    • 2.0 python调用code:
    • 2.1 光流变分模型
      • a. 灰度光流约束:
      • b. 梯度光流约束
      • c. 平滑约束
      • d. 最终的约束方程:
    • 2.2 优化求解
    • 2.3 来看opencv的实现
      • a. 接口类和实现类
      • b. 具体实现介绍
  • 3 线性方程组解法
  • 4 变分法
  • 5 参考文献
  • 6 python 光流实验:

1.Fast Optical Flow using Dense Inverse Search

该篇论文是求dense flow,效果和速度都很好,虽然有源码(opencv上也有),但是网上的介绍不是很多,不是很容易理解。
要想理解该论文,首先要理解光流法

1.1 W的含义:

对于光流来说 W 表示光流:x方向上的光流u,y方向上的光流v
同时W也可以表示更复杂的坐标变换方法,比如affine仿射变换

在这里插入图片描述

1.2 LK光流模型

对图像2进行warp 与 图像1相减。
利用迭代的方法更新和优化
在这里插入图片描述

在这里插入图片描述

1.3 LK光流模型求解(不含迭代)

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

1.4 LK光流模型迭代求解

在4.2中提到迭代求解的光流模型
在这里插入图片描述

I 0 ( x , y ) = I 1 ( x + u , y + v ) I0(x,y) = I1(x+u, y+v) I0(x,y)=I1(x+u,y+v)变为 I 0 ( x , y ) = I 1 ( x + u + d u , y + v + d v ) I0(x,y) = I1(x+u+du, y+v+dv) I0(x,y)=I1(x+u+du,y+v+dv)

优化目标为:
m i n ∣ ∣ I 0 ( x , y ) − I 1 ( x + u + d u , y + v + d v ) ∣ ∣ 2 min ||I0(x,y)-I1(x+u+du, y+v+dv)||^2 min∣∣I0(x,y)I1(x+u+du,y+v+dv)2
I 0 ( x , y ) − I 1 ( x + u + d u , y + v + d v ) = 0 I0(x,y)-I1(x+u+du, y+v+dv) = 0 I0(x,y)I1(x+u+du,y+v+dv)=0

I 1 ( x + u + d u , y + v + d v ) = I 1 ( x + u , y + v ) + I 1 ( x + u , y + v ) x d u + I 1 ( x + u , y + v ) y d v I1(x+u+du, y+v+dv) = I1(x+u, y+v) + I1(x+u, y+v)_x du + I1(x+u, y+v)_y dv I1(x+u+du,y+v+dv)=I1(x+u,y+v)+I1(x+u,y+v)xdu+I1(x+u,y+v)ydv
I 1 w a r p = I 1 ( x + u , y + v ) I1warp = I1(x+u, y+v) I1warp=I1(x+u,y+v)
已知上一次迭代u,v光流,或者初始设为0,可以求出 I 1 w a r p I1warp I1warp
I 1 ( x + u + d u , y + v + d v ) = I 1 w a r p + I 1 w a r p x d u + I 1 w a r p y d v I1(x+u+du, y+v+dv) = I1warp + I1warp_x du + I1warp_y dv I1(x+u+du,y+v+dv)=I1warp+I1warpxdu+I1warpydv

然后:
I 0 ( x , y ) − I 1 ( x + u + d u , y + v + d v ) = 0 I 0 ( x , y ) − I 1 w a r p + I 1 w a r p x d u + I 1 w a r p y d v = 0 I 1 w a r p x d u + I 1 w a r p y d v = I 0 ( x , y ) − I 1 w a r p I 1 w a r p x d u + I 1 w a r p y d v = I z I0(x,y)-I1(x+u+du, y+v+dv)=0 \\ I0(x,y) - I1warp + I1warp_x du + I1warp_y dv =0\\ I1warp_x du + I1warp_y dv = I0(x,y) - I1warp\\ I1warp_x du + I1warp_y dv = Iz I0(x,y)I1(x+u+du,y+v+dv)=0I0(x,y)I1warp+I1warpxdu+I1warpydv=0I1warpxdu+I1warpydv=I0(x,y)I1warpI1warpxdu+I1warpydv=Iz
假设对于一个patchsize = 8邻域内的点, du, dv是相同的
则可以建立线性方程组,然后同4.3可通过最小二乘法求得 d u , d v du, dv du,dv
I 1 w a r p x ( p 1 ) d u + I 1 w a r p y ( p 1 ) d v = I z ( p 1 ) I 1 w a r p x ( p 2 ) d u + I 1 w a r p y ( p 2 ) d v = I z ( p 2 ) I 1 w a r p x ( p 2 ) d u + I 1 w a r p y ( p 2 ) d v = I z ( p 2 ) . . . I 1 w a r p x ( p 64 ) d u + I 1 w a r p y ( p 64 ) d v = I z ( p 64 ) I1warp_x(p1) du + I1warp_y(p1) dv = Iz(p1)\\ I1warp_x(p2) du + I1warp_y(p2) dv = Iz(p2)\\ I1warp_x(p2) du + I1warp_y(p2) dv = Iz(p2)\\ ...\\ I1warp_x(p64) du + I1warp_y(p64) dv = Iz(p64) I1warpx(p1)du+I1warpy(p1)dv=Iz(p1)I1warpx(p2)du+I1warpy(p2)dv=Iz(p2)I1warpx(p2)du+I1warpy(p2)dv=Iz(p2)...I1warpx(p64)du+I1warpy(p64)dv=Iz(p64)

可建立 A d = b Ad=b Ad=b
其中
A = [ I 1 w a r p x ( p 1 ) I 1 w a r p y ( p 1 ) I 1 w a r p x ( p 2 ) I 1 w a r p y ( p 2 ) . . . I 1 w a r p x ( p n ) I 1 w a r p y ( p n ) ] A=\left[ \begin{array}{ccc} I1warp_x(p1) \qquad I1warp_y(p1)\\ I1warp_x(p2) \qquad I1warp_y(p2)\\ ...\\ I1warp_x(pn) \qquad I1warp_y(pn)\\ \end{array} \right ] A= I1warpx(p1)I1warpy(p1)I1warpx(p2)I1warpy(p2)...I1warpx(pn)I1warpy(pn)
d = [ d u d v ] d=\left[ \begin{array}{ccc} du\\ dv \end{array} \right ] d=[dudv]

b = [ I z ( p 1 ) I z ( p 2 ) . . . I z ( p n ) ] b=\left[ \begin{array}{ccc} Iz(p1)\\ Iz(p2)\\ ...\\ Iz(pn) \end{array} \right ] b= Iz(p1)Iz(p2)...Iz(pn)

最小二乘法
在这里插入图片描述

H = [ ∑ I 1 w a r p x × I 1 w a r p x ∑ I 1 w a r p x × I 1 w a r p y ∑ I 1 w a r p x × I 1 w a r p y ∑ I 1 w a r p y × I 1 w a r p y ] H=\left[ \begin{array}{ccc} \sum I1warp_x\times I1warp_x & \sum I1warp_x\times I1warp_y\\ \sum I1warp_x\times I1warp_y & \sum I1warp_y\times I1warp_y\\ \end{array} \right ] H=[I1warpx×I1warpxI1warpx×I1warpyI1warpx×I1warpyI1warpy×I1warpy]
B = A T b = [ ∑ I 1 w a r p x I z ∑ I 1 w a r p y I z ] B=A^Tb = \left[ \begin{array}{ccc} \sum I1warp_x Iz\\ \sum I1warp_y Iz \end{array} \right ] B=ATb=[I1warpxIzI1warpyIz]
d = H − 1 × B d = H^{-1} \times B d=H1×B
最后更新
u = u + d u v = v + d v u = u+du\\ v=v+dv u=u+duv=v+dv
以及 I1warp也要更新

1.5 dis_flow方法中的 LK光流模型

在4.4中,每次求出du,dv后首先更新u,v,然后更新I1warp
还要根据I1warp更新方程组的参数 I 1 w a r p x , I 1 w a r p y I1warp_x ,I1warp_y I1warpxI1warpy
等于说是每次迭代都要重新算梯度。

而dis_flow方法采用下面的模型:
在这里插入图片描述

即每次求出 du, dv之后,并不是作用在 I1warp中,而是作用在 I0 中,当然光流也是相反的方向

I 0 ( x + d u , y + d v ) − I 1 ( x + u , y + v ) = 0 I 0 ( x , y ) + I 0 x d u + I 0 y d v − I 1 ( x + u , y + v ) = 0 I0(x+du, y+dv)-I1(x+u,y+v)=0\\ I0(x,y)+I0_xdu+I0_ydv-I1(x+u,y+v)=0 I0(x+du,y+dv)I1(x+u,y+v)=0I0(x,y)+I0xdu+I0ydvI1(x+u,y+v)=0
可以得到
I 0 x d u + I 0 y d v = I 1 ( x + u , y + v ) − I 0 ( x , y ) I0_xdu + I0_ydv = I1(x+u,y+v)-I0(x,y) I0xdu+I0ydv=I1(x+u,y+v)I0(x,y)

同理可以求得 H 和 b,进而求出 du, dv
H = [ ∑ I 0 x × I 0 x ∑ I 0 x × I 0 y ∑ I 0 x × I 0 y ∑ I 0 y × I 0 y ] H=\left[ \begin{array}{ccc} \sum I0_x\times I0_x & \sum I0_x\times I0_y\\ \sum I0_x\times I0_y & \sum I0_y\times I0_y\\ \end{array} \right ] H=[I0x×I0xI0x×I0yI0x×I0yI0y×I0y]
B = A T b = [ ∑ I 0 x I z ∑ I 0 y I z ] B=A^Tb = \left[ \begin{array}{ccc} \sum I0_x Iz\\ \sum I0_y Iz \end{array} \right ] B=ATb=[I0xIzI0yIz]
d = H − 1 × B d = H^{-1} \times B d=H1×B

更新 u = u − d u , v = v − d v u=u-du, v=v-dv u=udu,v=vdv 记住这里是相减,因为du,dv是对I0而言的,作用在I1上符号变号
更新 I 1 ( x + u , y + v ) I1(x+u,y+v) I1(x+u,y+v)

然后再次循环求解,H矩阵是由I0的梯度求的,而不是I1warp的梯度求得,因此不需要每次迭代更新,只需更新一次,大大减少计算量。

加入以上是针对一个小patch优化求解 u,v, 可以设置步长便利整个图像,得到每个patch的u,v如果patch有重叠,则加权平均。
最终得到每个像素点光流u,v

1.6 disflow代码分析

看代码的确实是这样:
1)computeSSD 函数计算 两个patch的ssd,
2)computeSSDMeanNorm 返回的 是 两个patch减去各自均值后的 SSD, 避免亮度差异对ssd的影响
3)processPatch 函数 计算两个patch的ssd, 且 计算 dst_dUx, dst_dUy
对应的是
B = A T b = [ ∑ I 0 x I z ∑ I 0 y I z ] B=A^Tb = \left[ \begin{array}{ccc} \sum I0_x Iz\\ \sum I0_y Iz \end{array} \right ] B=ATb=[I0xIzI0yIz]
diff对应的是 I 1 w a r p − I 0 I1warp - I0 I1warpI0
在这里插入图片描述

4)processPatchMeanNorm 函数是先均值归一化,在计算。
5)precomputeStructureTensor函数计算好每个patch的 地图平方和
对应的是H中的元素
H = [ ∑ I 0 x × I 0 x ∑ I 0 x × I 0 y ∑ I 0 x × I 0 y ∑ I 0 y × I 0 y ] H=\left[ \begin{array}{ccc} \sum I0_x\times I0_x & \sum I0_x\times I0_y\\ \sum I0_x\times I0_y & \sum I0_y\times I0_y\\ \end{array} \right ] H=[I0x×I0xI0x×I0yI0x×I0yI0y×I0y]

    Mat_<float> I0xx_buf; //!< sum of squares of x gradient values
    Mat_<float> I0yy_buf; //!< sum of squares of y gradient values
    Mat_<float> I0xy_buf; //!< sum of x and y gradient products

    /* Extra buffers that are useful if patch mean-normalization is used: */
    Mat_<float> I0x_buf; //!< sum of x gradient values
    Mat_<float> I0y_buf; //!< sum of y gradient values

6) void DISOpticalFlowImpl::PatchInverseSearch_ParBody::operator()(const Range &range) const
函数中有每个patch的迭代过程:

计算 H − 1 H^{-1} H1

/* Precomputed structure tensor */
float *xx_ptr = dis->I0xx_buf.ptr<float>();
float *yy_ptr = dis->I0yy_buf.ptr<float>();
float *xy_ptr = dis->I0xy_buf.ptr<float>();
/* And extra buffers for mean-normalization: */
float *x_ptr = dis->I0x_buf.ptr<float>();
float *y_ptr = dis->I0y_buf.ptr<float>();

 /* Use the best candidate as a starting point for the gradient descent: */
float cur_Ux = Sx_ptr[is * dis->ws + js];
float cur_Uy = Sy_ptr[is * dis->ws + js];

/* Computing the inverse of the structure tensor: */
float detH = xx_ptr[is * dis->ws + js] * yy_ptr[is * dis->ws + js] -
                xy_ptr[is * dis->ws + js] * xy_ptr[is * dis->ws + js];
if (abs(detH) < EPS)
    detH = EPS;
float invH11 = yy_ptr[is * dis->ws + js] / detH;
float invH12 = -xy_ptr[is * dis->ws + js] / detH;
float invH22 = xx_ptr[is * dis->ws + js] / detH;

计算 dUx, dUy, 就是
B = A T b = [ ∑ I 0 x I z ∑ I 0 y I z ] B=A^Tb = \left[ \begin{array}{ccc} \sum I0_x Iz\\ \sum I0_y Iz \end{array} \right ] B=ATb=[I0xIzI0yIz]

if (dis->use_mean_normalization)
    SSD = processPatchMeanNorm(dUx, dUy,
            I0_ptr  + i * dis->w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
            I0x_ptr + i * dis->w + j, I0y_ptr + i * dis->w + j,
            dis->w, w_ext, w00, w01, w10, w11, psz,
            x_grad_sum, y_grad_sum);
else
    SSD = processPatch(dUx, dUy,
            I0_ptr  + i * dis->w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
            I0x_ptr + i * dis->w + j, I0y_ptr + i * dis->w + j,
            dis->w, w_ext, w00, w01, w10, w11, psz);

H − 1 H^{-1} H1 B B B都算出来了
d = H − 1 × B d = H^{-1} \times B d=H1×B

dx = invH11 * dUx + invH12 * dUy;
dy = invH12 * dUx + invH22 * dUy;

更新I1的u,v, 注意是相减,因为du,dv是作用在I0上的。

cur_Ux -= dx;
cur_Uy -= dy;
/* If gradient descent converged to a flow vector that is very far from the initial approximation
    * (more than patch size) then we don't use it. Noticeably improves the robustness.
    */
//验证光流的范围变化不能超过patch_size
if (norm(Vec2f(cur_Ux - Sx_ptr[is * dis->ws + js], cur_Uy - Sy_ptr[is * dis->ws + js])) <= psz)
{
    Sx_ptr[is * dis->ws + js] = cur_Ux;
    Sy_ptr[is * dis->ws + js] = cur_Uy;
}

可以发现和咱们的公式推导完全一致。

7) Densification_ParBody: patch flow fusion
在论文中有介绍融合方法:
在这里插入图片描述

以上求得是每个patch的光流,由于patch间隔不同存在overlap的时候,就加权平均得到某个像素的flow。
就是说该像素点 被多少个patch覆盖了,就求出多少个weight,这里Us表示.weight计算根据 I1warp(p)和I0(p)的差异得到。
差异越小说明warp更可能是对的,则weight更大。

最后加权融合得到该像素点的 u和v

void DISOpticalFlowImpl::Densification_ParBody::operator()(const Range &range) const
和文中公式一致

在这里插入图片描述

8)最后利用变分优化方法优化u, v

if (variational_refinement_iter > 0)
    variational_refinement_processors[i]->calcUV(I0s[i], I1s[i], Ux[i], Uy[i]);

关于variational_refinement优化原理查看下一章节。

2.0 disflow中的VariationalRefinement方法

2.0 python调用code:

algo_var_refine = cv2.VariationalRefinement_create()
algo_var_refine.calcUV(im1, im2, flow_pca[..., 0].astype(np.float32), flow_pca[..., 1].astype(np.float32))
flow_var_refine = flow_pca

这是一个全局优化光流的方法,在disflow 和 deepflow中都有使用。

主要原理来自于两篇论文
论文1:High Accuracy Optical Flow Estimation Based on a
Theory for Warping[2004]

论文2:Beyond Pixels: Exploring New Representations and
Applications for Motion Analysis中的 A和B章节

2.1 光流变分模型

a. 灰度光流约束:

I ( x , y , t ) = I ( x + u , y + v , t + 1 ) I(x, y, t)=I(x+u,y+v,t+1) I(x,y,t)=I(x+u,y+v,t+1) (1)

上面公式的线性版本如下,但是下面等式成立的条件是运动是线性的:

I x u + I y u + I t = 0 I_xu+I_yu+I_t=0 Ixu+Iyu+It=0 (2)

b. 梯度光流约束

灰度可能容易受到图像亮度变化的影响,梯度会有更好的鲁棒性,因此引入梯度光流约束
在这里插入图片描述

最终的data项约束为:

在这里插入图片描述

其中
在这里插入图片描述

为什么用公式(5)而不是公式公式(4)?
原因是quadratic penalisers 容易收到outlier的影响
这也是二范数和一范数的区别。深度学习的损失函数也是同样的道理,如果想要损失都较小用平方L2; L1作为损失函数容易使部分sample loss=0,部分loss比较大,不受outlier的影响

c. 平滑约束

包含时域平滑和空域平滑,如果只有前后两帧则只包括空域平滑, 公式如下:
在这里插入图片描述

在这里插入图片描述

表示的x方向,y方向,时间t方向的偏微分(梯度),如果只有两张图像,就只剩下x方向和y方向。(最少3张图才能构成两张 时域上的梯度图,才能建立约束。)

d. 最终的约束方程:

在这里插入图片描述

约束方程是关于两个函数u(x,y), v(x,y)的泛函。
在论文1中利用变分法求解, 转化为欧拉-拉格朗日方程后,利用fixed point iteration
loop 求解,论文1和2分别采用变分法和IterativeReweightedLeastSquare求解。因为opencv中的VariationalRefinement和论文2比较一致且论文2易懂,下面主要介绍论文2的求解方法。

2.2 优化求解

以下面的优化目标为例:

在这里插入图片描述

在这里插入图片描述

为什么选择下面的惩罚函数,原理已经说过,是为例避免outlier的影响。
在这里插入图片描述在这里插入图片描述

少了一个梯度项,不过优化原理是一样的。

在迭代框架下,可以改为:

在这里插入图片描述

这里主要使用了一阶泰勒展开。

u + d u u+du u+du在x和y方向上的梯度为 ( u + d u ) x (u+du)_x (u+du)x ( u + d u ) y (u+du)_y (u+du)y,等价与
D x = [ − 1 , 0 , 1 ] Dx=[-1,0,1] Dx=[1,0,1]算子,
D y = D x T Dy=Dx^T Dy=DxT算子
做卷积。

f = ( I z + I x d u + I y d v ) 2 f=(I_z+I_xdu+I_ydv)^2 f=(Iz+Ixdu+Iydv)2
g = ( D x ∗ ( u + d u ) ) 2 + ( D y ∗ ( u + d u ) ) 2 + ( D x ∗ ( v + d v ) ) 2 + ( D y ∗ ( v + d v ) ) 2 g=(Dx*(u+du))^2+(Dy*(u+du))^2+(Dx*(v+dv))^2+(Dy*(v+dv))^2 g=(Dx(u+du))2+(Dy(u+du))2+(Dx(v+dv))2+(Dy(v+dv))2
∗ * 号卷积,表示求偏导。

则可以得到:
E ( d u , d v ) = ∑ ( φ ( f ) + α ϕ ( g ) ) = ∑ ( φ ( ( I z + I x d u + I y d v ) 2 ) + α ϕ ( ( D x ∗ ( u + d u ) ) 2 + ( D y ∗ ( u + d u ) ) 2 + ( D x ∗ ( v + d v ) ) 2 + ( D y ∗ ( v + d v ) ) 2 ) ) E(du,dv)=\sum(\varphi(f) + \alpha\phi(g))\\ =\sum(\varphi((I_z+I_xdu+I_ydv)^2) + \alpha\phi((Dx*(u+du))^2+(Dy*(u+du))^2+(Dx*(v+dv))^2+(Dy*(v+dv))^2)) E(du,dv)=(φ(f)+αϕ(g))=(φ((Iz+Ixdu+Iydv)2)+αϕ((Dx(u+du))2+(Dy(u+du))2+(Dx(v+dv))2+(Dy(v+dv))2))

求和符合表示所有pixel.

iterative reweighted least squares (IRLS) 利用
gradient = 0求解 du, dv

∂ E ( d u , d v ) ∂ d u = 0 \frac{\partial E(du,dv)}{\partial du}=0 duE(du,dv)=0
∂ E ( d u , d v ) ∂ d v = 0 \frac{\partial E(du,dv)}{\partial dv}=0 dvE(du,dv)=0

∂ E ( d u , d v ) ∂ d u = ∑ 2 ( φ ′ ( f ) ( I z + I x d u + I y d v ) I x + α ϕ ′ ( g ) ( ( D x ∗ ( u + d u ) ∗ D x ) + ( D y ∗ ( u + d u ) ∗ D y ) ) ) = ∑ 2 ( φ ′ ( ( I z + I x d u + I y d v ) 2 ) ( I z + I x d u + I y d v ) I x + α ϕ ′ ( g ) ( ( D x ∗ ( u + d u ) ∗ D x ) + ( D x ∗ ( u + d u ) ∗ D x ) ) ) = ∑ 2 ( φ ′ ( ( I z + I x d u + I y d v ) 2 ) ( I z + I x d u + I y d v ) I x + α ( ( D x D x ∗ ϕ ′ ( g ) + ( D y D y ∗ ϕ ′ ( g ) ) ( u + d u ) ) \frac{\partial E(du,dv)}{\partial du}\\ =\sum2(\varphi'(f)(I_z+I_xdu+I_ydv)I_x + \alpha\phi'(g)((Dx*(u+du)*Dx)+(Dy*(u+du)*Dy)))\\ =\sum2(\varphi'((I_z+I_xdu+I_ydv)^2)(I_z+I_xdu+I_ydv)I_x + \alpha\phi'(g)((Dx*(u+du)*Dx)+(Dx*(u+du)*Dx)))\\ =\sum2(\varphi'((I_z+I_xdu+I_ydv)^2)(I_z+I_xdu+I_ydv)I_x + \alpha((DxDx*\phi'(g)+(DyDy*\phi'(g))(u+du)) duE(du,dv)=2(φ(f)(Iz+Ixdu+Iydv)Ix+αϕ(g)((Dx(u+du)Dx)+(Dy(u+du)Dy)))=2(φ((Iz+Ixdu+Iydv)2)(Iz+Ixdu+Iydv)Ix+αϕ(g)((Dx(u+du)Dx)+(Dx(u+du)Dx)))=2(φ((Iz+Ixdu+Iydv)2)(Iz+Ixdu+Iydv)Ix+α((DxDxϕ(g)+(DyDyϕ(g))(u+du))
两个一阶导近似一个空间二阶导数 拉普拉斯算子

在这里插入图片描述

L = D x D x ∗ ϕ ′ ( g ) + D y D y ∗ ϕ ′ ( g ) L=DxDx*\phi'(g)+DyDy*\phi'(g) L=DxDxϕ(g)+DyDyϕ(g)

迭代计算的时候 f f f 也可以用warp后的图像 的平方表示,
因此首先初始化 du=dv=0,
然后计算 f f f g g g
然后计算 φ ′ ( f ) \varphi'(f) φ(f), ϕ ′ ( g ) \phi'(g) ϕ(g), L

∂ E ( d u , d v ) ∂ d u = 2 ∑ ( φ ′ ( f ) I x 2 + α L ) d u + φ ′ ( f ) I x I y d v + φ ′ ( f ) I x I z + α L u \frac{\partial E(du,dv)}{\partial du}=2\sum{(\varphi'(f)I_x^2 + \alpha L) du + \varphi'(f)I_x I_y dv + \varphi'(f)I_x I_z + \alpha L u} duE(du,dv)=2(φ(f)Ix2+αL)du+φ(f)IxIydv+φ(f)IxIz+αLu

同理:
∂ E ( d u , d v ) ∂ d v = 2 ∑ ( φ ′ ( f ) I y 2 + α L ) d v + φ ′ ( f ) I x I y d u + φ ′ ( f ) I y I z + α L v \frac{\partial E(du,dv)}{\partial dv}=2\sum{(\varphi'(f)I_y^2 + \alpha L) dv + \varphi'(f)I_x I_y du + \varphi'(f)I_y I_z + \alpha L v} dvE(du,dv)=2(φ(f)Iy2+αL)dv+φ(f)IxIydu+φ(f)IyIz+αLv

∂ E ( d u , d v ) ∂ d u = 0 \frac{\partial E(du,dv)}{\partial du}=0 duE(du,dv)=0 ∂ E ( d u , d v ) ∂ d v = 0 \frac{\partial E(du,dv)}{\partial dv}=0 dvE(du,dv)=0

得到:
Ax = b
其中
A= [ ( φ ′ ( f ) I x 2 + α L ) φ ′ ( f ) I x I y φ ′ ( f ) I x I y φ ′ ( f ) I y 2 + α L ) ] \begin{bmatrix} (\varphi'(f)I_x^2 + \alpha L) & \varphi'(f)I_x I_y \\ \varphi'(f)I_x I_y & \varphi'(f)I_y^2 + \alpha L) \end{bmatrix} [(φ(f)Ix2+αL)φ(f)IxIyφ(f)IxIyφ(f)Iy2+αL)]

x = [ d u d v ] \begin{bmatrix} du \\ dv \end{bmatrix} [dudv]

b= − [ φ ′ ( f ) I x I z + α L u φ ′ ( f ) I y I z + α L v ] -\begin{bmatrix} \varphi'(f)I_x I_z + \alpha L u \\ \varphi'(f)I_y I_z + \alpha L v \end{bmatrix} [φ(f)IxIz+αLuφ(f)IyIz+αLv]

因此每次迭代求解du,dv。

2.3 来看opencv的实现

主要公式就是上面的Ax = b

a. 接口类和实现类

首先接口

#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;
/** @brief Variational optical flow refinement

This class implements variational refinement of the input flow field, i.e.
it uses input flow to initialize the minimization of the following functional:
\f$E(U) = \int_{\Omega} \delta \Psi(E_I) + \gamma \Psi(E_G) + \alpha \Psi(E_S) \f$,
where \f$E_I,E_G,E_S\f$ are color constancy, gradient constancy and smoothness terms
respectively. \f$\Psi(s^2)=\sqrt{s^2+\epsilon^2}\f$ is a robust penalizer to limit the
influence of outliers. A complete formulation and a description of the minimization
procedure can be found in @cite Brox2004
*/
//主要函数就是calcUV
class VariationalRefinement
{
public:
    /** @brief @ref calc function overload to handle separate horizontal (u) and vertical (v) flow components
    (to avoid extra splits/merges) */
    virtual void calcUV(InputArray I0, InputArray I1, InputOutputArray flow_u, InputOutputArray flow_v) = 0;

    /** @brief Number of outer (fixed-point) iterations in the minimization procedure.
    @see setFixedPointIterations */
    virtual int getFixedPointIterations() const = 0;
    /** @copybrief getFixedPointIterations @see getFixedPointIterations */
    virtual void setFixedPointIterations(int val) = 0;

    /** @brief Number of inner successive over-relaxation (SOR) iterations
        in the minimization procedure to solve the respective linear system.
    @see setSorIterations */
    virtual int getSorIterations() const = 0;
    /** @copybrief getSorIterations @see getSorIterations */
    virtual void setSorIterations(int val) = 0;

    /** @brief Relaxation factor in SOR
    @see setOmega */
    virtual float getOmega() const = 0;
    /** @copybrief getOmega @see getOmega */
    virtual void setOmega(float val) = 0;

    /** @brief Weight of the smoothness term
    @see setAlpha */
    virtual float getAlpha() const = 0;
    /** @copybrief getAlpha @see getAlpha */
    virtual void setAlpha(float val) = 0;

    /** @brief Weight of the color constancy term
    @see setDelta */
    virtual float getDelta() const = 0;
    /** @copybrief getDelta @see getDelta */
    virtual void setDelta(float val) = 0;

    /** @brief Weight of the gradient constancy term
    @see setGamma */
    virtual float getGamma() const = 0;
    /** @copybrief getGamma @see getGamma */
    virtual void setGamma(float val) = 0;

    /** @brief Creates an instance of VariationalRefinement
    */
    static Ptr<VariationalRefinement> create();
};

实现类:

class VariationalRefinementImpl
{
public:
    VariationalRefinementImpl();

    void calc(InputArray I0, InputArray I1, InputOutputArray flow) ;
    void calcUV(InputArray I0, InputArray I1, InputOutputArray flow_u, InputOutputArray flow_v)  ;
    void collectGarbage()  ;

protected: //!< algorithm parameters
    int fixedPointIterations, sorIterations;
    float omega;
    float alpha, delta, gamma;
    float zeta, epsilon;

public:
    int getFixedPointIterations() const   { return fixedPointIterations; }
    void setFixedPointIterations(int val)   { fixedPointIterations = val; }
    int getSorIterations() const   { return sorIterations; }
    void setSorIterations(int val)   { sorIterations = val; }
    float getOmega() const   { return omega; }
    void setOmega(float val)   { omega = val; }
    float getAlpha() const   { return alpha; }
    void setAlpha(float val)   { alpha = val; }
    float getDelta() const   { return delta; }
    void setDelta(float val)   { delta = val; }
    float getGamma() const   { return gamma; }
    void setGamma(float val)   { gamma = val; }

protected: //!< internal buffers
  /* This struct defines a special data layout for Mat_<float>. Original buffer is split into two: one for "red"
   * elements (sum of indices is even) and one for "black" (sum of indices is odd) in a checkerboard pattern. It
   * allows for more efficient processing in SOR iterations, more natural SIMD vectorization and parallelization
   * (Red-Black SOR). Additionally, it simplifies border handling by adding repeated borders to both red and
   * black buffers.
   */
    struct RedBlackBuffer
    {
        Mat_<float> red;   //!< (i+j)%2==0
        Mat_<float> black; //!< (i+j)%2==1

        /* Width of even and odd rows may be different */
        int red_even_len, red_odd_len;
        int black_even_len, black_odd_len;

        RedBlackBuffer();
        void create(Size s);
        void release();
    };

    Mat_<float> Ix, Iy, Iz, Ixx, Ixy, Iyy, Ixz, Iyz;                            //!< image derivative buffers
    RedBlackBuffer Ix_rb, Iy_rb, Iz_rb, Ixx_rb, Ixy_rb, Iyy_rb, Ixz_rb, Iyz_rb; //!< corresponding red-black buffers

    RedBlackBuffer A11, A12, A22, b1, b2; //!< main linear system coefficients
    RedBlackBuffer weights;               //!< smoothness term weights in the current fixed point iteration

    Mat_<float> mapX, mapY; //!< auxiliary buffers for remapping

    RedBlackBuffer tempW_u, tempW_v; //!< flow buffers that are modified in each fixed point iteration
    RedBlackBuffer dW_u, dW_v;       //!< optical flow increment
    RedBlackBuffer W_u_rb, W_v_rb;   //!< red-black-buffer version of the input flow

private: //!< private methods and parallel sections
    void splitCheckerboard(RedBlackBuffer& dst, Mat& src);
    void mergeCheckerboard(Mat& dst, RedBlackBuffer& src);
    void updateRepeatedBorders(RedBlackBuffer& dst);
    void warpImage(Mat& dst, Mat& src, Mat& flow_u, Mat& flow_v);
    void prepareBuffers(Mat& I0, Mat& I1, Mat& W_u, Mat& W_v);

    /* Parallelizing arbitrary operations with 3 input/output arguments */
    typedef void (VariationalRefinementImpl::* Op)(void* op1, void* op2, void* op3);
    struct ParallelOp_ParBody : public ParallelLoopBody
    {
        VariationalRefinementImpl* var;
        vector<Op> ops;
        vector<void*> op1s;
        vector<void*> op2s;
        vector<void*> op3s;

        ParallelOp_ParBody(VariationalRefinementImpl& _var, vector<Op> _ops, vector<void*>& _op1s,
            vector<void*>& _op2s, vector<void*>& _op3s);
        void operator()(const Range& range) const  ;
    };
    void gradHorizAndSplitOp(void* src, void* dst, void* dst_split)
    {
        

        Sobel(*(Mat*)src, *(Mat*)dst, -1, 1, 0, 1, 1, 0.00, BORDER_REPLICATE);
        splitCheckerboard(*(RedBlackBuffer*)dst_split, *(Mat*)dst);
    }
    void gradVertAndSplitOp(void* src, void* dst, void* dst_split)
    {
       

        Sobel(*(Mat*)src, *(Mat*)dst, -1, 0, 1, 1, 1, 0.00, BORDER_REPLICATE);
        splitCheckerboard(*(RedBlackBuffer*)dst_split, *(Mat*)dst);
    }
    void averageOp(void* src1, void* src2, void* dst)
    {
        

        addWeighted(*(Mat*)src1, 0.5, *(Mat*)src2, 0.5, 0.0, *(Mat*)dst, CV_32F);
    }
    void subtractOp(void* src1, void* src2, void* dst)
    {
       

        subtract(*(Mat*)src1, *(Mat*)src2, *(Mat*)dst, noArray(), CV_32F);
    }

    struct ComputeDataTerm_ParBody : public ParallelLoopBody
    {
        VariationalRefinementImpl* var;
        int nstripes, stripe_sz;
        int h;
        RedBlackBuffer* dW_u, * dW_v;
        bool red_pass;

        ComputeDataTerm_ParBody(VariationalRefinementImpl& _var, int _nstripes, int _h, RedBlackBuffer& _dW_u,
            RedBlackBuffer& _dW_v, bool _red_pass);
        void operator()(const Range& range) const  ;
    };

    struct ComputeSmoothnessTermHorPass_ParBody : public ParallelLoopBody
    {
        VariationalRefinementImpl* var;
        int nstripes, stripe_sz;
        int h;
        RedBlackBuffer* W_u, * W_v, * curW_u, * curW_v;
        bool red_pass;

        ComputeSmoothnessTermHorPass_ParBody(VariationalRefinementImpl& _var, int _nstripes, int _h,
            RedBlackBuffer& _W_u, RedBlackBuffer& _W_v, RedBlackBuffer& _tempW_u,
            RedBlackBuffer& _tempW_v, bool _red_pass);
        void operator()(const Range& range) const  ;
    };

    struct ComputeSmoothnessTermVertPass_ParBody : public ParallelLoopBody
    {
        VariationalRefinementImpl* var;
        int nstripes, stripe_sz;
        int h;
        RedBlackBuffer* W_u, * W_v;
        bool red_pass;

        ComputeSmoothnessTermVertPass_ParBody(VariationalRefinementImpl& _var, int _nstripes, int _h,
            RedBlackBuffer& W_u, RedBlackBuffer& _W_v, bool _red_pass);
        void operator()(const Range& range) const  ;
    };

    struct RedBlackSOR_ParBody : public ParallelLoopBody
    {
        VariationalRefinementImpl* var;
        int nstripes, stripe_sz;
        int h;
        RedBlackBuffer* dW_u, * dW_v;
        bool red_pass;

        RedBlackSOR_ParBody(VariationalRefinementImpl& _var, int _nstripes, int _h, RedBlackBuffer& _dW_u,
            RedBlackBuffer& _dW_v, bool _red_pass);
        void operator()(const Range& range) const  ;
    };
};

b. 具体实现介绍

  1. 上面有一些struct 继承自ParallelLoopBody, 主要是tbb的优化方法并行加速操作
  2. calc函数: 比如输入为I0,I1两个图像,和一个光流flow, 然后主要调用calcUV函数
void VariationalRefinementImpl::calc(InputArray I0, InputArray I1, InputOutputArray flow)
{
    CV_Assert(!I0.empty() && I0.channels() == 1);
    CV_Assert(!I1.empty() && I1.channels() == 1);
    CV_Assert(I0.sameSize(I1));
    CV_Assert((I0.depth() == CV_8U && I1.depth() == CV_8U) || (I0.depth() == CV_32F && I1.depth() == CV_32F));
    CV_Assert(!flow.empty() && flow.depth() == CV_32F && flow.channels() == 2);
    CV_Assert(I0.sameSize(flow));

    Mat uv[2];
    Mat& flowMat = flow.getMatRef();
    split(flowMat, uv);
    calcUV(I0, I1, uv[0], uv[1]);
    merge(uv, 2, flowMat);
}

  1. calcUV函数:也是整个算法的框架
void VariationalRefinementImpl::calcUV(InputArray I0, InputArray I1, InputOutputArray flow_u, InputOutputArray flow_v)
{
   
   //一些assert操作, 对输入的要求
    CV_Assert(!I0.empty() && I0.channels() == 1);
    CV_Assert(!I1.empty() && I1.channels() == 1);
    CV_Assert(I0.sameSize(I1));
    CV_Assert((I0.depth() == CV_8U && I1.depth() == CV_8U) || (I0.depth() == CV_32F && I1.depth() == CV_32F));
    CV_Assert(!flow_u.empty() && flow_u.depth() == CV_32F && flow_u.channels() == 1);
    CV_Assert(!flow_v.empty() && flow_v.depth() == CV_32F && flow_v.channels() == 1);
    CV_Assert(I0.sameSize(flow_u));
    CV_Assert(flow_u.sameSize(flow_v));

    int num_stripes = getNumThreads();//多线程相关,可以不做了解
    Mat I0Mat = I0.getMat();
    Mat I1Mat = I1.getMat();
    Mat& W_u = flow_u.getMatRef();
    Mat& W_v = flow_v.getMatRef();
    prepareBuffers(I0Mat, I1Mat, W_u, W_v);

    splitCheckerboard(W_u_rb, W_u);
    splitCheckerboard(W_v_rb, W_v);
    W_u_rb.red.copyTo(tempW_u.red);
    W_u_rb.black.copyTo(tempW_u.black);
    W_v_rb.red.copyTo(tempW_v.red);
    W_v_rb.black.copyTo(tempW_v.black);
    dW_u.red.setTo(0.0f);
    dW_u.black.setTo(0.0f);
    dW_v.red.setTo(0.0f);
    dW_v.black.setTo(0.0f);

    for (int i = 0; i < fixedPointIterations; i++)
    {
        //CV_TRACE_REGION("fixedPoint_iteration");

        parallel_for_(Range(0, num_stripes), ComputeDataTerm_ParBody(*this, num_stripes, I0Mat.rows, dW_u, dW_v, true));
        parallel_for_(Range(0, num_stripes), ComputeDataTerm_ParBody(*this, num_stripes, I0Mat.rows, dW_u, dW_v, false));

        parallel_for_(Range(0, num_stripes), ComputeSmoothnessTermHorPass_ParBody(
            *this, num_stripes, I0Mat.rows, W_u_rb, W_v_rb, tempW_u, tempW_v, true));
        parallel_for_(Range(0, num_stripes), ComputeSmoothnessTermHorPass_ParBody(
            *this, num_stripes, I0Mat.rows, W_u_rb, W_v_rb, tempW_u, tempW_v, false));

        parallel_for_(Range(0, num_stripes),
            ComputeSmoothnessTermVertPass_ParBody(*this, num_stripes, I0Mat.rows, W_u_rb, W_v_rb, true));
        parallel_for_(Range(0, num_stripes),
            ComputeSmoothnessTermVertPass_ParBody(*this, num_stripes, I0Mat.rows, W_u_rb, W_v_rb, false));

        for (int j = 0; j < sorIterations; j++)
        {
            //CV_TRACE_REGION("SOR_iteration");
            parallel_for_(Range(0, num_stripes), RedBlackSOR_ParBody(*this, num_stripes, I0Mat.rows, dW_u, dW_v, true));
            parallel_for_(Range(0, num_stripes), RedBlackSOR_ParBody(*this, num_stripes, I0Mat.rows, dW_u, dW_v, false));
        }

        tempW_u.red = W_u_rb.red + dW_u.red;
        tempW_u.black = W_u_rb.black + dW_u.black;
        updateRepeatedBorders(tempW_u);
        tempW_v.red = W_v_rb.red + dW_v.red;
        tempW_v.black = W_v_rb.black + dW_v.black;
        updateRepeatedBorders(tempW_v);
    }
    mergeCheckerboard(W_u, tempW_u);
    mergeCheckerboard(W_v, tempW_v);
}

算法框架:
在这里插入图片描述

1)prepareBuffers函数中warp I1得到warpedI,然后让它与 I0 求个平均(这里只是增加鲁棒性), 对应I(p+w)

主要计算
a v e r a g e d I = I 0 ∗ 0.5 + w a r p e d I ∗ 0.5 对应公式中的 I ( p + w ) I z = w a r p e d I − I 0 对应公式中的 I z I x I y I x z I y z I x x I y y I x y 都是字面意思 averagedI = I0*0.5 +warpedI*0.5 \qquad 对应公式中的 I(p+w)\\ Iz = warpedI - I0 \qquad 对应公式中的 Iz\\ Ix\\ Iy\\ Ixz\\ Iyz\\ Ixx\\ Iyy\\ Ixy \qquad 都是字面意思 averagedI=I00.5+warpedI0.5对应公式中的I(p+w)Iz=warpedII0对应公式中的IzIxIyIxzIyzIxxIyyIxy都是字面意思

后缀 _rb 是把矩阵分成两个,主要是为了方便计算。

2)ComputeDataTerm_ParBody
weight 对应 φ ′ ( f ) \varphi'(f) φ(f)
a11, a12, a22, b1,b2 都可以求出,当然这只是 gray 约束

/* Step 1: Compute color constancy terms */
/* Normalization factor:*/
derivNorm = pIx[j] * pIx[j] + pIy[j] * pIy[j] + zeta_squared;
/* Color constancy penalty (computed by Taylor expansion):*/
Ik1z = pIz[j] + pIx[j] * pdU[j] + pIy[j] * pdV[j];
/* Weight of the color constancy term in the current fixed-point iteration divided by derivNorm: */
weight = (delta2 / sqrt(Ik1z * Ik1z / derivNorm + epsilon_squared)) / derivNorm;
/* Add respective color constancy terms to the linear system coefficients: */
pa11[j] = weight * (pIx[j] * pIx[j]) + zeta_squared;
pa12[j] = weight * (pIx[j] * pIy[j]);
pa22[j] = weight * (pIy[j] * pIy[j]) + zeta_squared;
pb1[j] = -weight * (pIz[j] * pIx[j]);
pb2[j] = -weight * (pIz[j] * pIy[j]);

代码中同样计算了 gradiant 约束。

3)ComputeSmoothnessTermHorPass_ParBody 和 ComputeSmoothnessTermVertPass_ParBody
计算水平和竖直方向上的平滑约束

pWeight[j] = alpha2 / sqrt(ux * ux + vx * vx + uy * uy + vy * vy + epsilon_squared); 这里求 ϕ \phi ϕ的导数,对应 α ϕ ′ ( g ) \alpha \phi'(g) αϕ(g)

经过以上步骤后得到 A 和 b,然后求x.

4) RedBlackSOR_ParBody
solve Ax = b

sigmaU = pW_next[j - 1] * pdu_next[j - 1] + pW[j] * pdu_next[j] + pW_prev_row[j] * pdu_prev_row[j] + pW[j] * pdu_next_row[j];
sigmaV = pW_next[j - 1] * pdv_next[j - 1] + pW[j] * pdv_next[j] + pW_prev_row[j] * pdv_prev_row[j] +pW[j] * pdv_next_row[j];
pdu[j] += var->omega * ((sigmaU + pb1[j] - pdv[j] * pa12[j]) / pa11[j] - pdu[j]);
pdv[j] += var->omega * ((sigmaV + pb2[j] - pdu[j] * pa12[j]) / pa22[j] - pdv[j]);

参考:
Large Displacement Optical Flow: Descriptor Matching in Variational Motion Estimation
对上面的方法进行优化,增加匹配项约束,意思就是加入我们检测数一些特征点匹配,这些匹配点可以得到 u,v, 增加约束。

图像处理中的全局优化技术(Global optimization techniques in image processing and computer vision) (三) 介绍了几篇 变分光流法论文

3 线性方程组解法

关于线性方程组的数值解法一般有两类:直接法和迭代法

  1. 直接法:直接法就是经过有限步算术运算,可求得线性方程组精确解的方法(若计算过程中没有舍入误差)。常用于求解低阶稠密矩阵方程组及某些大型稀疏矩阵方程组(如大型带状方程组)。
  2. 迭代法:迭代法就是用某种极限过程去逐步逼近线性方程组精确解的方法。优点:存储单元少、程序设计简单、原始系数矩阵在计算过程始终不变等;缺点:存在收敛性及收敛速度问题。常用于求解大型稀疏矩阵方程组(尤其是由微分方程离散后得到的大型方程组)。

直接法的介绍参考:第5章 解线性方程组的直接方法
迭代法参考:第6章 解线性方程组的迭代法(基于MATLAB)
解线性方程组的迭代法
数值分析】Jacobi、Seidel和Sor迭代法求解线性方程组(附matlab代码)

4 变分法

定义:泛函是以函数为变量的函数。即它的输入是函数,输出是实数。而这个输出值取决于一个或多个函数(输入)在一整个路径上的积分。

那么什么是变分法呢?求泛函极值的方法称为变分法。

变分法将泛函极值问题转换为一个非线性偏微分方程,又称为PDE,怎么求这个方程呢?
一般是转化为非线性代数方程组,然后迭代的方法求解,比如梯度下降。

应用在图像降噪和修复,dense flow estimation等。

在这里插入图片描述

变分法简介Part 1.(Calculus of Variations) 该系列有系统的介绍

浅谈变分原理
详细的示例

变分法初步:book

基于变分法的感知色彩校正 这是一个特别好的应用

5 参考文献

关于Lucas-Kanade 光流法原理:

  1. Lucas-Kanade 20 Years On: A Unifying Framework 翻译(一)
    讲解了1)光流法模型,2)warp方式, 3)inverse search方法。4)耗时
    很有参考价值,比如
    不同的warp方式的优化。不是optical flow这种直接相加的warp,而是仿射变换的参数化 warp, 也可以求出仿射变换的参数。只不过每个点都有自己的仿射参数。

    换句话说,加入两幅图像满足仿射变换关系,可以通过这个方法来求解变换关系,应用在前后帧的对齐上。

  2. 视觉SLAM十四讲作业练习(6) 光流法跟踪单点,有代码。

  3. DIS 光流详解 对Fast Optical Flow using Dense Inverse Search论文的概述

关于DIS代码:

  1. of_dis
  2. Optical flow using dense inverse search
  3. opencv代码:opencv4以上版本 在 video/tracking 模块中, 还是opencv清晰易读一些

6 python 光流实验:

利用MPI-Sintel的一组图片,调用各种光流方法:
在这里插入图片描述

import os
import cv2
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.colors import hsv_to_rgb
from torchvision.utils import flow_to_image
import torch
from show_flow import draw_flow
import time

def flow_to_image_torch(flow):
    flow = torch.from_numpy(np.transpose(flow, [2, 0, 1]))
    flow_im = flow_to_image(flow)
    img = np.transpose(flow_im.numpy(), [1, 2, 0])
    #print(img.shape)
    return img

def show_flow_im(frame, flow):
    h, w, c = frame.shape
    flow_im3 = flow_to_image_torch(flow)
    #print(h, w, c, min(h, w)//40)
    flow_im4 = draw_flow(frame, flow, 20)
    plt.figure()
    plt.subplot(121)
    plt.imshow(flow_im3)
    plt.subplot(122)
    plt.imshow(flow_im4)
    plt.show()

def cal_flow(frame1, frame2):
    """
    :param frame1: input image 1
    :param frame2: input image 2
    :return: optical flow
    """
    im1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
    im2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)

    algo_dis = cv2.DISOpticalFlow_create(cv2.DISOPTICAL_FLOW_PRESET_MEDIUM)
    algo_dis.setUseSpatialPropagation(True)
    flow = algo_dis.calc(im1, im2, None, )

    return flow
def cal_flow_algos(frame1, frame2):
    """
    :param frame1: input image 1
    :param frame2: input image 2
    :return: optical flow
    """
    im1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
    im2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    times = []
    start = time.time()
    algo_dis = cv2.DISOpticalFlow_create(cv2.DISOPTICAL_FLOW_PRESET_MEDIUM)
    algo_dis.setUseSpatialPropagation(True)
    flow_dis = algo_dis.calc(im1, im2, None, )
    end = time.time()
    times.append(end-start)

    start = end
    algo_deepflow = cv2.optflow.createOptFlow_DeepFlow()
    flow_deepflow = algo_deepflow.calc(im1, im2, None)
    end = time.time()
    times.append(end-start)

    start = end
    flow_sparse = cv2.optflow.calcOpticalFlowSparseToDense(im1, im2)
    end = time.time()
    times.append(end - start)

    start = end
    flow_fb = cv2.calcOpticalFlowFarneback(im1, im2, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    end = time.time()
    times.append(end - start)

    start = end
    algo_pca = cv2.optflow.createOptFlow_PCAFlow()
    flow_pca = algo_pca.calc(im1, im2, None)
    end = time.time()
    times.append(end - start)

    start = end
    flow_simple = cv2.optflow.calcOpticalFlowSF(frame1, frame2, 1, 5, 5) # color image
    end = time.time()
    times.append(end - start)

    start = end
    flow_TVL1 = cv2.optflow.DualTVL1OpticalFlow_create()
    flow_tvl1 = flow_TVL1.calc(im1, im2, None)
    end = time.time()
    times.append(end - start)

    start = end
    algo_var_refine = cv2.VariationalRefinement_create()
    flow_var_refine = flow_fb
    algo_var_refine.calcUV(im1, im2, flow_var_refine[..., 0].astype(np.float32), flow_var_refine[..., 1].astype(np.float32))

    end = time.time()
    times.append(end - start)
    print(flow_var_refine.shape)
    print('run time:', times)
    return [flow_dis, flow_deepflow, flow_sparse, flow_fb, flow_pca, flow_simple, flow_tvl1, flow_var_refine]

def flow_warp(im1, im2, flow):
    """
    :param im1: pre image
    :param im2: next image
    :param flow: optical flow ,im1 to im2
    :return:
    """
    h, w, c = im1.shape
    hh = np.arange(0, h)
    ww = np.arange(0, w)
    xx, yy = np.meshgrid(ww, hh)

    tx = xx - flow[..., 0]
    ty = yy - flow[..., 1]
    mask = ((tx < 0) + (ty < 0) + (tx > w - 1) + (ty > h - 1)) > 0
    # print(mask.shape, mask.dtype, np.sum(mask))
    # out = interp_linear(image, tx, ty)
    # warped im1, should compare with im2
    out = cv2.remap(im1, tx.astype(np.float32), ty.astype(np.float32), interpolation=cv2.INTER_LINEAR)
    out[mask] = 0
    return out

def cal_epe(flow_gt, flow):
    diff = flow_gt - flow
    return np.mean(np.sqrt((np.sum(diff*diff, axis=-1))))

def load_flow_to_numpy(path):
    with open(path, 'rb') as f:
        magic = np.fromfile(f, np.float32, count=1)
        assert (202021.25 == magic), 'Magic number incorrect. Invalid .flo file'
        w = np.fromfile(f, np.int32, count=1)[0]
        h = np.fromfile(f, np.int32, count=1)[0]
        data = np.fromfile(f, np.float32, count=2 * w * h)
    data2D = np.reshape(data, (h, w, 2))
    #print(data2D[:10,:10,0])
    return data2D




if __name__ == "__main__":
    print('cv2 version: {}'.format(cv2.__version__))

    file1 = r'D:\codeflow\MPI-Sintel-complete\training\final\alley_1\frame_0001.png'
    file2 = r'D:\codeflow\MPI-Sintel-complete\training\final\alley_1\frame_0002.png'
    # 读取图片
    frame1 = cv2.imread(file1)
    h, w, c = frame1.shape

    frame2 = cv2.imread(file2)
    frame2 = cv2.resize(frame2, (w, h))

    im1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
    im2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)

    algo_names = ['flow_dis', 'flow_deepflow', 'flow_sparse', 'flow_fb', 'flow_pca', 'flow_simple', 'flow_tvl1', 'flow_var_refine']
    # 计算光流
    flows = cal_flow_algos(frame1, frame2)

    # 读取flow gt
    file_flow = r'D:\codeflow\MPI-Sintel-complete\training\flow\alley_1\frame_0001.flo'
    flow_gt = load_flow_to_numpy(file_flow)
    flow_gt_im = flow_to_image_torch(flow_gt)
    # 计算epe
    flow_gt = cv2.resize(flow_gt, (w, h))
    epes = []
    for flow in flows:
        epe = cal_epe(flow_gt, flow)
        epes.append(epe)
    print(epes)
    # 画出光流图
    plt.figure()
    num = len(flows)
    col_num = (num + 1) // 2
    for i in range(num):
        print(flows[i].min(), flows[i].max(), flows[i].dtype, flows[i].shape)
        flows[i][np.isnan(flows[i])] = 0
        plt.subplot(2, col_num, i + 1)
        flow_im = flow_to_image_torch(flows[i])
        plt.imshow(flow_im)
        # warp
        im_warp = flow_warp(frame1, frame2, flows[i])
        # cv2.imwrite(file1[:-4]+algo_names[i]+'.png', im_warp)
    plt.show()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大佬们都说tcp有黏包的问题,tcp却说:我冤枉!

相关参考添加链接描述 相关参考 什么是tcp TCP,全称Transmission Control Protocol&#xff0c;是一种传输控制协议&#xff0c;TCP协议也是计算机网络中非常复杂的一个协议 tcp的特点 tcp是面向连接的协议tcp是端到端的链接tcp提供可靠的传输服务tcp协议提供双工通信tcp是…

【计算机考研408】快速排序的趟数问题 + PAT 甲级 7-2 The Second Run of Quicksort

前言 该题还未加入PAT甲级题库中&#xff0c;可以通过购买2022年秋季甲级考试进行答题&#xff0c;纯考研题改编 快速排序 常考的知识点 快速排序是基于分治法快速排序是所有内部排序算法中平均性能最优的排序算法快速排序是一种不稳定的排序算法快速排序算法中&#xff0c…

异步Buck和同步Buck的特点

1 介绍 随着时代的发展&#xff0c;工业&#xff0c;车载&#xff0c;通信&#xff0c;消费类等产品都提出了小型化&#xff0c;智能化的需求。相应的&#xff0c;对于这些系统中的电源模块提出了小型化的要求。目前&#xff0c;市场上依然存在很多异步Buck电源管理芯片使用的场…

atomic 原子操作

atomic 原子操作前言atomic_t定义内核中的实现armv7的实现armv8的实现Exclusive monitor实现所处的位置External exclusive monitorAtomic指令的支持QA前言 修改一个变量会经过读、修改、写的操作序列。但有时该操作序列在执行完毕前会被其他任务或事件打断。 比如在多CPU体系…

python基础学习3--切片(slice)

在python中&#xff0c;切片&#xff08;slice&#xff09;是对序列型对象&#xff08;如list,string,tuple)的一种高级索引方法。普通索引只取出序列一个下标对应的元素&#xff0c;而切片取出序列中一个范围对应的元素&#xff0c;这里的范围不是狭义上的连续片段。通俗一点就…

CLion Debug 调试 Makefile 构建的 C 语言程序断点不起作用

最近在研究 jattach&#xff0c;打算在本地调试项目&#xff0c;发现 CLion 可以正常编译运行代码&#xff0c;却无法断点 Debug。由于笔者对 C/C 项目不熟悉&#xff0c;在此记录研究过程中遇到的一些基本问题与解决方法。 文章目录解决方式尝试过的手段【未解决】找 Native D…

RIG Exploit Kit 仍然通过 IE 感染企业用户

RIG Exploit Kit 正处于最成功的时期&#xff0c;每天尝试大约 2000 次入侵并在大约 30% 的案例中成功&#xff0c;这是该服务长期运行历史中的最高比率。 通过利用相对较旧的 Internet Explorer 漏洞&#xff0c;RIG EK 已被发现分发各种恶意软件系列&#xff0c;包括 Dridex…

内科大机器学习期末重点

1. 什么是机器学习 &#xff08;由于图床原因导致部分图片错位&#xff0c;可以借鉴着看&#xff09; 语音识别算法推荐人脸识别垃圾邮件过滤贷款资格审核 2. 学习的概念 与经验有关 学习可以改善系统性能 学习是一个有反馈的信息处理与控制过程 3. 学习分类&#xff1a…

996的压力下,程序员还有时间做副业吗?

996怎么搞副业&#xff1f; 这个问题其实蛮奇怪的&#xff1a;996的压力下&#xff0c;怎么会还想着搞副业呢&#xff1f; 996还想搞副业的原因有哪些&#xff1f; 大家对于996应该都不陌生&#xff0c;总结就是一个字&#xff1a;忙。 996的工作性质就是加班&#xff0c;就…

基于龙芯+国产FPGA 的VPX以太网交换板设计(二)

3.1 板卡技术要求 3.1.1 主要性能指标 本着向下兼容的原则&#xff0c;以太网交换板的设计尽量保留传统信息处理平台的基本功 能和接口&#xff0c;重点考虑提升设备的性能和扩展性。本课题以太网交换板的主要性能指标 如下&#xff1a; &#xff08;1&#xff09; 具有大容量无…

一文搞懂华为防火墙的原理和配置

“防火墙”一词起源于建筑领域&#xff0c;用来隔离火灾&#xff0c;阻止火势从一个区域蔓延到另一个区域。引入到通信领域&#xff0c;防火墙这一具体设备通常用于两个网络之间有针对性的、逻辑意义上的隔离。这种隔离是选择性的&#xff0c;隔离“火”的蔓延&#xff0c;而又…

mac安装docker hub及使用

1. docker hub安装 官网&#xff1a;Docker https://hub.docker.com/ 去官网 下载 Docker.dmg 并安装 2. docker hub的使用 step1: 首先克隆一个仓库 Getting Started 项目是一个简单的Github仓库&#xff0c;他包含了你创建镜像的所有东西&#xff0c;并且可以把他当容…

文心一言的蝴蝶振翅,云计算的飓风狂飙

ChatGPT带来的多米诺效应正在不断涌现。社会各界都在关注一系列问题&#xff0c;比如中国版ChatGPT什么时候能来到&#xff1f;其效果如何&#xff1f;类ChatGPT应用的投资与创业前景会怎样&#xff1f;相关产品能带来哪些应用价值&#xff1f;随着百度文心一言等产品相继官宣&…

面试问题【数据库】

数据库数据库的三范式是什么drop、delete、truncate 分别在什么场景之下使用char 和 varchar 的区别是什么数据库的乐观锁和悲观锁是什么SQL 约束有哪几种mysql 的内连接、左连接、右连接有什么区别MyIASM和Innodb两种引擎所使用的索引的数据结构是什么mysql 有关权限的表都有哪…

SpringSecurity常见面试题汇总(超详细回答)

1.什么是Spring Security&#xff1f;核心功能&#xff1f;Spring Security是一个基于Spring框架的安全框架&#xff0c;提供了完整的安全解决方案&#xff0c;包括认证、授权、攻击防护等功能。其核心功能包括&#xff1a;认证&#xff1a;提供了多种认证方式&#xff0c;如表…

线性表 链表表示

初识链表 用一组物理位置任意的存储单元来存放线性表的数据元素。这组存储单元既可以是连续的&#xff0c;也可以是不连续的&#xff0c;甚至是零散分布在内存中的任意位置上的。链表中元素的逻辑次序和物理次序不一定相同。 在存储自己内容的同时也存储下一个元素的地址。存…

Adobe illustrator使用教程

抓手工具&#xff1a;绘制大型图片拖动图片 画放大缩小&#xff1a;Alt鼠标滚轮 间接选择工具&#xff1a;点击图标shift 进行多个对象选择&#xff0c;再次点击取消选择&#xff08;用于对多个对象进行批量操作&#xff09; 直接选择工具&#xff1a;可以对图案本身进行精细选…

(二十二)操作系统-生产者·消费者问题

文章目录一、问题描述二、问题分析三、PV操作题目分析步骤1. 关系分析2. 整理思路3. 设置信号量4. 编写代码四、能否改变相邻P、V操作的顺序?五、小结1. PV操作题目的解题思路2. 注一、问题描述 系统中有一组生产者进程和一组消费者进程&#xff0c;生产者进程每次生产一个产品…

什么是文件传输中台?

企业文件传输的场景有哪些&#xff1f; 企业日常办公中无时无刻不在产生数据文件。多样化的数据已成为企业的重要资产&#xff0c;更被称为是“新石油”。数据并不是单单存储起来就行了&#xff0c;而是需要高效又安全的让数据流转起来&#xff0c;释放其自身的价值&#xff0…

XGBoost和LightGBM时间序列预测对比

XGBoost和LightGBM都是目前非常流行的基于决策树的机器学习模型&#xff0c;它们都有着高效的性能表现&#xff0c;但是在某些情况下&#xff0c;它们也有着不同的特点。 XGBoost和LightGBM简单对比 训练速度 LightGBM相较于xgboost在训练速度方面有明显的优势。这是因为Ligh…