OpenCV:使用普通摄像头进行深度估计

OpenCV:使用普通摄像头进行深度估计

1.双目视觉测距原理

(1)理想情况

双目立体视觉是基于视差原理,从双目相机中获取的多幅图像中恢复被测物体三维几何信息的方法。如图,对于空间物体表面任意一点,如果从左右2个摄像机同时观察P,并能确定在左摄像机图像上的点Pl与右摄像机图像上的点Pr是空间同一点P的图像点(称Pl与Pr,为共轭对应点),则可计算出空间点P的三维坐标(包含距离信息)。基于双目立体视觉的测距系统包含摄像机标定、立体校正、立体匹配和三维重建等步骤。

理想双目相机成像模型

(1)计算原理

根据三角形相似原理:

z/f=(x-b)/xr=x/xl=y/yl

解方程得:

z=bf/d, x=zxl/d, y=z*y/f

由此可知:想要得到距离z,必须知道:

1、相机焦距f,左右相机基线b(可以通过先验信息或者相机标定得到)。
2、视差 :d=xl-xr,即左相机像素点(xl, yl)和右相机中对应点(xr, yr)的关系,这是双目视觉的核心问题。

(2)极线约束

对于左图中的一个像素点,来确定该点在右图中的位置,不需要在整个图像中地毯式搜索,需要用到极线约束。如上图所示。O1,O2是两个相机,P是空间中的一个点,P和两个相机中心点O1、O2形成了三维空间中的一个平面PO1O2,称为极平面(Epipolar plane)。极平面和两幅图像相交于两条直线,这两条直线称为极线(Epipolar line)。P在相机O1中的成像点是P1,在相机O2中的成像点是P2,但是P的位置是未知的。我们的目标是:对于左图的P1点,寻找它在右图中的对应点P2,这样就能确定P点的空间位置。极线约束(Epipolar Constraint)是指当空间点在两幅图像上分别成像时,已知左图投影点p1,那么对应右图投影点p2一定在相对于p1的极线上,这样可以极大的缩小匹配范围。即P2一定在对应极线上,所以只需要沿着极线搜索便可以找到P1的对应点P2。

当两个相机光心不是处于同一水平面时:这种情况下拍摄的两张左右图片,右图中对应的极线是右图中的多条直线,也就是对应的搜索区域。这多条直线并不是水平的,如果进行逐点搜索效率非常低。

(3)图像矫正技术:

像矫正是通过分别对两张图片用单应性矩阵变换得到,目的是把两个不同方向的图像平面(下图中灰色平面)重新投影到同一个平面且光轴互相平行(下图中黄色平面),这样转化为理想情况的模型。

经过图像矫正后,左图中的像素点只需要沿着水平的极线方向搜索对应点,从下图中我们可以看到三个点对应的视差(红色双箭头线段)是不同的,越远的物体视差越小,越近的物体视差越大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#使用普通摄像头进行深度估计


import cv2
import numpy as np

def update(val=0):
stereo.setBlockSize(cv2.getTrackbarPos('window_size','disparity'))
stereo.setUniquenessRatio(cv2.getTrackbarPos('uniquenessRatio','disparity'))
stereo.setSpeckleWindowSize(cv2.getTrackbarPos('speckleRange','disparity'))
stereo.setSpeckleRange(cv2.getTrackbarPos('speckleRange','disparity'))
stereo.setDisp12MaxDiff(cv2.getTrackbarPos('disp12MaxDiff','disparity'))

print('computing disparity.......')
disp=stereo.compute(imgL,imgR).astype(np.float32)/16.0

cv2.imshow('left',imgL)
cv2.imshow('right',imgR)
cv2.imshow('disparity',(disp-min_disp)/num_disp)

#创建窗口,设置初始值
cv2.namedWindow('disparity')
cv2.createTrackbar('speckleRange','disparity',speckleRange,50,update)
cv2.createTrackbar('window_size','disparity',window_size,21,update)
cv2.createTrackbar('speckleWindowSize','disparity',speckleWindowSize,200,update)
cv2.createTrackbar('uniquenessRatio','disparity',uniquenessRatio,50,update)
cv2.createTrackbar('disp12MaxDiff','disparity',disp12MaxDiff,250,update)
#创建stereoSGBM算法的实例
stereo=cv2.StereoSGBM_create(
minDisparity=min_disp,
numDisparities=num_disp,
blockSize=window_size,
uniquenessRatio=uniquenessRatio,
speckleRange=speckleRange,
speckleWindowSize=speckleWindowSize,
disp12MaxDiff=disp12MaxDiff,
P1=p1,
P2=p2
)
update()
cv2.waitKey(0)

运行结果: