双目深度算法——SGM中的动态规划
双目深度算法——SGM中的动态规划
由于工作上的需要,需要学习下双目立体匹配邻域中的一个经典算法SGBM,这里我的主要学习流程是先阅读了下原paper
《Accurate and Efficient Stereo Processing by Semi-Global Matching and Mutual Information》,然后学习了李博士的SGM系列博客,写得很好很用心啊,如果需要全面了解SGM算法的同学可以去参考下,这里我主要总结下SGM中最核心的聚合步骤中使用的动态规划方法。
首先我们来重新描述下SGM中需要解决的问题,为啥需要动态规划呢?
SGM中解决的问题实际上解决的问题就是一个最优化问题,既然是最优化问题,那么首先就需要构建一个损失函数,SGM中构建的损失函数如下所示:
E
(
D
)
=
∑
p
C
(
p
,
D
p
)
+
∑
q
∈
N
p
P
1
T
[
∣
D
p
−
D
q
∣
=
1
]
+
∑
q
∈
N
p
P
2
T
[
∣
D
p
−
D
q
∣
>
1
]
E(\mathrm{D})=\sum_{\mathrm{p}} \mathrm{C}\left(\mathrm{p}, \mathrm{D}_{\mathrm{p}}\right)+\sum_{\mathrm{q} \in N_{\mathrm{p}}} P_{1} T\left[\left|\mathrm{D}_{\mathrm{p}}-\mathrm{D}_{\mathrm{q}}\right|=1\right]+\sum_{\mathrm{q} \in N_{\mathrm{p}}} P_{2} T\left[\left|\mathrm{D}_{\mathrm{p}}-\mathrm{D}_{\mathrm{q}}\right|>1\right]
E(D)=p∑C(p,Dp)+q∈Np∑P1T[∣Dp−Dq∣=1]+q∈Np∑P2T[∣Dp−Dq∣>1]我们要求的实际上就是这样一副视差图:
D
=
arg
min
D
E
(
D
)
D=\underset{D}{\arg \min } E(\mathrm{D})
D=DargminE(D)其中
C
(
p
,
D
p
)
\mathrm{C}\left(\mathrm{p}, \mathrm{D}_{\mathrm{p}}\right)
C(p,Dp)就是点
p
\mathrm{p}
p在视差图
D
p
\mathrm{D}_{\mathrm{p}}
Dp下的损失项,这个损失项的构建多种多样,在原论文中使用的是交叉熵,实际上用得比较多的是Census变换后两幅图对应像素点间的Hamming距离,或者简单点,两幅图对应点之间的灰度差都可以,差别只是好优化后的效果好坏而已。
其中损失项 P 1 T [ ∣ D p − D q ∣ = 1 ] P_{1} T\left[\left|\mathrm{D}_{\mathrm{p}}-\mathrm{D}_{\mathrm{q}}\right|=1\right] P1T[∣Dp−Dq∣=1]对相邻像素视差变化很小的情况进行惩罚,损失项 P 2 T [ ∣ D p − D q ∣ > 1 ] P_{2} T\left[\left|\mathrm{D}_{\mathrm{p}}-\mathrm{D}_{\mathrm{q}}\right|>1\right] P2T[∣Dp−Dq∣>1]对相邻像素视差变化很大的情况进行惩罚,且 P 2 P_{2} P2的惩罚力度通常大于 P 1 P_{1} P1,我们的算法更加倾向于保证视差图平滑变化而不是剧烈突变。
如果损失函数只有 ∑ p C ( p , D p ) \sum_{\mathrm{p}} \mathrm{C}\left(\mathrm{p}, \mathrm{D}_{\mathrm{p}}\right) ∑pC(p,Dp)这一项的话,局部最优就是全局最优,那么你就可以用局部最优的方式去求解视差图,例如你遍历某个像素可能视差,损失函数最小的那个视差作为该像素的视差,那么再遍历全图的像素,这样也可以得到一副视差图,但是这样极其容易受到噪声的影响,李博士在博客【码上实战】【立体匹配系列】经典SGM:(2)代价计算中就做了这个实验。
损失函数中正是由于多了两个惩罚项所以变成了全局或者半全局最优,损失项中 q ∈ N p \mathrm{q} \in N_{\mathrm{p}} q∈Np,也就是就是说,我们需要先知道周围像素的最优视差才能知道当前像素的最优视差,为了高效地解决这个问题,SGM论文中剔除了一种路径代价聚合的思路,这就是我这篇我这篇博客想要讨论的动态规划问题。
为了求得该像素所有视差下的匹配代价,我们首先通过该像素周围某一条路径上的像素的视差匹配代价一维聚合得到该路径的聚合代价,然后将所有路径聚合代价相加得到该像素聚合后的所有视差下的匹配代价值,如下图所示,就是从八个方向进行代聚合得到点
p
p
p(深蓝色点)视差匹配代价的方式:
当然,我们还可以是四个,或者十六个方向,但是不管我们一共有四个、八个还是十六个方向聚合,我们在聚合某一个方向
r
r
r时和其他方向是没有关系的,因此我们可以从一个方向先遍历全图所有的像素,每个方向的聚合都是从图像边缘开始到图像边缘结束,如下所示是从上往下聚合时的情况:
这里我们先给了路径代价聚合的一个定性的概念,那么所谓路径代价聚合到底是一个什么过程呢? 在SGM的论文中将代价聚合定义成如下这样一个公式:
L
r
(
p
,
d
)
=
C
(
p
,
d
)
+
min
{
L
r
(
p
−
r
,
d
)
L
r
(
p
−
r
,
d
−
1
)
+
P
1
L
r
(
p
−
r
,
d
+
1
)
+
P
1
min
i
L
r
(
p
−
r
,
i
)
+
P
2
}
−
min
i
L
r
(
p
−
r
,
i
)
L_{r}(\mathrm{p}, d)=\mathrm{C}(\mathrm{p}, d)+\min \left\{\begin{array}{l} L_{r}(\mathrm{p}-\mathrm{r}, d) \\ L_{r}(\mathrm{p}-\mathrm{r}, d-1)+P_{1} \\ L_{r}(\mathrm{p}-\mathrm{r}, d+1)+P_{1} \\ \min _{i} L_{r}(\mathrm{p}-\mathrm{r}, i)+P_{2} \end{array}\right\}-\min_{i} L_{r}(\mathrm{p}-\mathrm{r}, i)
Lr(p,d)=C(p,d)+min⎩
⎨
⎧Lr(p−r,d)Lr(p−r,d−1)+P1Lr(p−r,d+1)+P1miniLr(p−r,i)+P2⎭
⎬
⎫−iminLr(p−r,i)其中
C
(
p
,
d
)
\mathrm{C}(\mathrm{p}, d)
C(p,d)就是像素
p
p
p在视差为
d
d
d时的损失值,也就是前面提到的Census变换后的汉明距离或者交叉熵;
L
r
(
p
,
d
)
L_{r}(\mathrm{p}, d)
Lr(p,d)从像素
p
p
p上从路径
r
r
r聚合过来的视差为
d
d
d时候的路径聚合代价;
L
r
(
p
−
r
,
d
)
L_{r}(\mathrm{p-r}, d)
Lr(p−r,d)为路径内上一个像素视差为
d
d
d时的聚合代价值;
L
r
(
p
−
r
,
d
−
1
)
L_{r}(\mathrm{p-r}, d-1)
Lr(p−r,d−1)为路径内上一个像素视差为
d
−
1
d-1
d−1时的聚合代价值;
L
r
(
p
−
r
,
d
+
1
)
L_{r}(\mathrm{p-r}, d+1)
Lr(p−r,d+1)为路径内上一个像素视差为
d
+
1
d+1
d+1时的聚合代价值;
min
i
L
r
(
p
−
r
,
i
)
\min _{i} L_{r}(\mathrm{p}-\mathrm{r}, i)
miniLr(p−r,i)为路径内上一个像素所有聚合代价值的最小值;
P
1
P_{1}
P1为惩罚项,是一个输入参数;
P
2
P_{2}
P2为另一个惩罚项,
P
2
=
P
2
i
n
i
t
/
(
I
p
−
I
p
−
r
)
P_{2}=P_{2init}/(I_{p}-I_{p-r})
P2=P2init/(Ip−Ip−r),
I
I
I表示灰度,
P
2
i
n
i
t
P_{2init}
P2init为
P
2
P_{2}
P2的最大值,是一个输入参数。
这个公式很多同学一看就能发现这就是一个动态规划过程,先求这个方向上前一个相邻像素的聚合代价,然后在再求当前像素的聚合代码,我们拿点
p
p
p的相邻像素出来进行研究,我们假定视差范围设定为
[
−
2
,
2
]
[-2,2]
[−2,2],那么,
L
r
(
p
,
0
)
L_{r}(p, 0)
Lr(p,0)由
L
r
(
p
−
1
,
2
)
L_{r}(p-1, 2)
Lr(p−1,2)、
L
r
(
p
−
1
,
1
)
L_{r}(p-1, 1)
Lr(p−1,1)、
L
r
(
p
−
1
,
0
)
L_{r}(p-1, 0)
Lr(p−1,0)、
L
r
(
p
−
1
,
−
1
)
L_{r}(p-1, -1)
Lr(p−1,−1)、
L
r
(
p
−
1
,
−
2
)
L_{r}(p-1, -2)
Lr(p−1,−2)计算获得;
L
r
(
p
+
1
,
0
)
L_{r}(p+1, 0)
Lr(p+1,0)由
L
r
(
p
,
2
)
L_{r}(p, 2)
Lr(p,2)、
L
r
(
p
,
1
)
L_{r}(p, 1)
Lr(p,1)、
L
r
(
p
,
0
)
L_{r}(p, 0)
Lr(p,0)、
L
r
(
p
,
−
1
)
L_{r}(p, -1)
Lr(p,−1)、
L
r
(
p
,
−
2
)
L_{r}(p, -2)
Lr(p,−2)计算获得;
这个动态过程就是下图这么个过程,具体怎么计算的就是参考上面的公式:
像这样每聚合完一个方向就得到一个方向的聚合代价图,这个聚合代价图在代码里通常是一个“图像长 x 图像宽 x 视差范围”的三维数组,在各个方向都聚合完后对所有聚合代价图进行求和就得到所有方向的聚合代价,然后求得该像素聚合代价最小的那个视差就是该像素的视差,就此就完成了SGM的代价聚合过程,得到了一张基本的视差图。
因为目前主要做的不是这个方向,暂时只是了解了下这个算法核心思想,没有Coding,也没有做实验,其实想把这个算法做好还有很多前前后后的处理,大家感兴趣的话还是读读OpenCV源码或者看看李博士的博客比较合适,这里我的总结就到这里啦~