opencv_python 深度估计与图像分割
半全局块匹配(Semi-Global Block Matching)算法
- 计算每个像素点的代价
原论文使用的方法是利用互信息熵,而OpenCV使用的是Birchfield和Tomasi的方法(参照《Depth Discontinuities by Pixel-to-Pixel Stereo》)。
1.1 利用互信息熵
所谓的熵,是用来表示随机变量的不确定性,熵的值越大,信息的不确定性也越大。熵H和互信息MI的定义分别如下:
其中,PI代表某个点i的概率分布,也就是灰度直方图为i的点出现的概率;对应地,PI1,I2就是两个图对应点i1和i2的联合概率分布,也就是:
Kim等人将上式做了一个改进:利用泰勒展开把HI1,I2的计算转化为求和问题参见论文《Visual Correspondence Using Energy Minimization and Mutual Information》)。
其中圈中带叉表示卷积运算,g(i,k)为高斯卷积核。
相应地,边缘熵以及边缘概率的计算如下:
这样的话,互信息的定义为:
MI匹配代价CMI为:
其中q是点p在视差为d的情况下的对应校正点。
原作者使用分层互信息(HMI)进行计算,每一层尺寸减少一半。单次计算的时间复杂度是O(WHD),即width×height×disparity range,所以上次迭代将会是当前迭代速度的1/8。
这里1/163要乘3的原因是小尺寸的随机视差图不靠谱,需要迭代3次。我们可以看到,相比于后文的BT方法仅仅慢了14%
1.2 Birchfield和Tomasi的方法(简称BT方法)
对于一个匹配序列M,其代价函数γ(M)表示匹配结果不准确的程度,其值越小越好。
其中,κocc表示未匹配的惩罚项(constant occlusion penalty),κr表示匹配的奖励项,Nocc和Nr分别表示未匹配和匹配的点数。
- 损失聚合
我们为视差设置一个能量函数E(D)
其中P1和P2分别表示视差差值为1和视差差值大于1的惩罚系数,一般P1<P2。添加两个正则化项一是为了保持视差图平滑,二是为了保持边缘。我们要做的是找到D使得能量函数E(D)最小,但是不幸的是,在二维图像的这个问题是一个NP-完全问题。为了解决这个问题,原文选择沿着一圈8个或者16个方向进行优化。
代价聚合公式(向量中的一个元素的成本等于当前位置的成本加上先前像素位置的所有可能视差集合的最低总和成本。)
选取使代价聚合最小的视差值mindS[emb(q,d),d]即可。
普通摄像头进行深度估计
使用普通摄像头进行深度估计主要用到的方法是几何学中的极几何,它属于立体视觉(stereo vision)几何学。立体视觉几何学是计算机视觉的一个分支,从同一物体的两张不同图像提取三维信息
视差:从一定距离的两个点上观察同一目标所产生的方向差异(百度百科),使用视差角来衡量
下面为使用OpenCV 如何使用极几何计算视差图,它是对图像中检测到的不同深度的基本表示。这样就能提取出一张图片的前景部分而抛弃其他部分。
首先需要同一物体在不同视角下拍摄两幅图像,但是要注意的是这两幅图像是距物体相同距离拍摄的,否则计算将会失败,视差图也就没有了意义.
代码实现:
import numpy as np
import cv2
def update(val=0):
stereo.setBlockSize(cv2.getTrackbarPos('window_size', 'disparity'))
stereo.setUniquenessRatio(cv2.getTrackbarPos('uniquenessRatio', 'disparity'))
stereo.setSpeckleWindowSize(cv2.getTrackbarPos('speckleWindowSize', 'disparity'))
stereo.setSpeckleRange(cv2.getTrackbarPos('speckleRange', 'disparity'))
stereo.setDisp12MaxDiff