ZED立体相机再现了人类双目视觉的工作方式。通过比较左眼和右眼看到的两种视图,不仅可以推断深度,还可以推断空间中的3D运动。
ZED立体相机可以捕捉到场景的高分辨率3D视频,通过比较左右图像之间的像素位移可以估计深度和运动。
深度感知是指确定物体之间的距离,以三维的角度看世界。到目前为止,深度传感器仅限于近距离和室内的深度感知,限制了其在手势控制和身体跟踪方面的应用。ZED是第一个使用立体视觉的通用深度传感器:
深度可以达到20米;
深度捕捉的帧率可以达到100FPS;
视野可以达到110度(H)x70度(V);
可以在室内和室外工作。
ZED捕获的深度图为图像中的每个像素(X,Y)存储距离值(Z)。距离以公制单位表示(例如米),并计算从相机左镜头后到场景对象的距离。
因为深度图是32位编码的所以不能直接显示。可以用[0,255]之间的8位单色(灰度)值表示灰度图,255表示最近的深度近似值,0表示最远的深度近似值。
另一种表示深度信息的常用方法是3D点云。点云可以看作是三维的深度图。深度图只包含每个像素的距离或Z信息,而点云代表场景外表面的3D点(X,Y,Z)的集合,可以包含颜色信息。
ZED SDK提供了两种深度感知模式:STANDARD和FILL。
STANDARD模式是ZED的默认深度感知模式。该模式保存了距离测量和形状,运行速度比FILL模式快,但是由于视觉遮挡和过滤,它会包含孔洞。这些洞在深度图中用黑色区域表示。使用STANDARD模式的应用包括自主导航,障碍物检测,3D地图,人员检测和跟踪。
在STANDARD模式下,孔洞(黑色像素)根据其类型有不同的值:
NAN 称为OCCLUSION_VALUE。像素的深度无法估计,因为被遮挡或者是异常值。
-INFINITY 称为TOO_CLOSE。像素的深度无法估计,因为太靠近相机。
INFINITY 称为TOO_FAR。像素的深度无法估计,因为离相机太远了。
可以用isValidMeasure()检查有效的深度值。
FILL模式为左侧图像中的每个像素(X,Y)提供了一个具有Z值的完全密集深度图。FILL模式填充深度图中的孔洞和遮挡,并添加了一个过滤阶段,能够提高边缘和时间稳定性,但会改变场景中物体的实际距离。
该模式推荐应用于混合现实捕获和视觉效果等。FILL模式比STANDARD模式需要更多的资源,并且以更低的FPS运行。
如果要将深度感知设置为FILL模式,在“RuntimeParameters”中设置“SENSING_MODE_FILL"。
# Set depth mode in ULTRA
init_parameters = sl.InitParameters()
init_parameters.depth_mode = sl.DEPTH_MODE.ULTRA
这些设置可以调整精度水平,范围和深度传感模块的计算性能。从更好到更快的排列如下:
NEURAL:通过AI技术把深度感知提升到了一个新的水平。即使在最具有挑战性的情况下也是准确和平稳的。
ULTRA:为基于计算机视觉的技术提供了最高的深度范围和更好的Z精度。
QUALITY:具有使表面光滑的强大的过滤阶段。
PERFORMANCE:设计得很光滑,可能会漏掉一些细节。
一般来说,建议对桌面和嵌入式应用都使用ULTRA模式。如果应用需要大量资源,切换到PERFORMANCE模式。
# Set depth mode in ULTRA
init_parameters = sl.InitParameters()
init_parameters.depth_mode = sl.DEPTH_MODE.ULTRA
深度范围对应于可以估计物体深度的最小和最大距离。
最小范围可以使用InitParameters中的depth_minimum_distance设置从默认值降低。
init_params = sl.InitParameters()
init_parameters.coordinate_units = sl.UNIT.METER
init_parameters.depth_minimum_distance = 0.15 # Set the minimum depth perception distance to 15cm
将最小范围降低到非常小的值可以显著增加内存需求并降低FPS。增加这个值可以提高性能。
对于需要远距离深度感知的应用,建议将depth_minimum_distancee设置为1m或更大以提高性能。
最大范围可以用sl.InitParameters中的depth_maximum_distance增加。
init_params = sl.InitParameters()
init_parameters.depth_mode = sl.DEPTH_MODE.ULTRA # Set the depth mode to ULTRA
init_parameters.coordinate_units = UNIT.METER
init_parameters.depth_maximum_distance = 40 # Set the maximum depth perception distance to 40m
建议使用ULTRA深度模式来提高远距离的深度精度。
最大深度范围一定程度上可以缩小到一定距离以上的钳制值,这对于减少远距离的深度抖动很有用。
增大最大范围对内存和FPS没有影响。
深度稳定是一种暂时融合和过滤几帧深度图的功能。这样可以减少抖动并提高静态对象的深度精度。即使当相机是通过使用ZED SDK的位置跟踪能力移动时,深度稳定也在运行。它还可以检测运动物体,以避免融合动态区域的深度。
默认情况下,深度稳定是启用的。由于它在后台启用位置跟踪,可以使用init_parameters.depth_stabilization = false来禁用深度稳定,以提高计算性能。
对于固定相机,建议在使用深度稳定时启用PositionalTrackingParameters::set_as_static。这可以让深度稳定模块知道相机是静态的,因此它可以禁用视觉跟踪并减少计算负载。
立体视觉使用三角测量从视差图像中估计深度,下面的公式描述了深度分辨率在立体相机范围内的变化情况:
Dr=Z^2*alpha,其中Dr为深度分辨率,Z为距离,alpha为常数。
深度精度随z-距离呈二次递减,近距离立体深度精度为距离的1%,远距离立体深度精度为距离的9%。在均匀和无纹理表面(如白墙、绿色屏幕和镜面区域)上的异常值测量也会影响深度精度。这些表面通常在深度测量中产生暂时的不稳定。
所有来自Stereolabs的相机都可以在室内和室外远距离(可达20米)使用。提供最佳深度精度的配置是将相机放置在距离场景30厘米至1米的工作距离处。要捕捉快速运动,请使用相机的高速模式(HD720 @ 60 FPS或VGA @ 100 FPS)。
避免在光线非常弱的环境中使用相机,因为相机使用彩色图像进行深度感知。但是可以通过将相机帧速率降低到15FPS来改善弱光性能。
可以使用USB 3.0扩展线将USB线的距离扩展到15m,使用光纤USB 3.0扩展器将USB线缆的距扩展到100m。
由于深度数据是估计的,有些深度点可能不准确。在许多情况下,整个集合中有一些不准确的点并不是什么大问题,但在某些情况下却并非如此。
为了克服这个问题,提出了置信度图(confidence map)。它为图像中的每个像素(X, Y)提供一个在[1,100]范围内的值,值接近100的像素是不可信的。
confidence_map = sl.Mat()
zed.retrieve_measure(confidence_map, sl.MEASURE.MEASURE_CONFIDENCE)
要从深度或点云中删除相应的点,可以使用检索到的数据执行自己的函数,或者可以直接在sl::RuntimeParameters::confidence_threshold中定义它,这样ZED SDK就可以完成这项工作,并从数据中删除所有置信度高于定义阈值的点。
有两个可信阈值:
confence_threshold:移除边缘上的点以避免“链接”对象。
texture_confence_threshold:从图像的统一区域移除点。
要配置深度感知,在初始化时使用InitParameters,在使用过程中使用RuntimeParameters来更改特定参数。
# Set configuration parameters
init_params = sl.InitParameters()
init_params.depth_mode = sl.DEPTH_MODE.ULTRA # Use ULTRA depth mode
init_params.coordinate_units = sl.UNIT.MILLIMETER # Use millimeter units (for depth measurements)
为了提取场景的深度图,使用grab()来抓取新图像,并使用retrievemmeasure()来检索与左侧图像对齐的深度。retrievemmeasure()可用于检索深度图、置信度图、法线图或点云。
image = sl.Mat()
depth_map = sl.Mat()
runtime_parameters = sl.RuntimeParameters()
if zed.grab(runtime_parameters) == SUCCESS :
# A new image and depth is available if grab() returns SUCCESS
zed.retrieve_image(image, sl.VIEW.LEFT) # Retrieve left image
zed.retrieve_measure(depth_map, sl.MEASURE.DEPTH) # Retrieve depth
深度矩阵存储32位浮点值,表示每个(X,Y)像素的深度(Z)。使用getValue()可以访问这些值。
depth_value = depth_map.get_value(x, y)
默认情况下,深度值以毫米表示。单位可以使用InitParameters::coordinate_units来改变。高级用户可以使用retrievemmeasure (*, *, MEM_GPU)在CPU内存(默认)或GPU内存中检索图像,深度和点云。
32位深度图可以显示为灰度8位图像。为了显示深度图,我们将其值缩放为[0,255],其中255(白色)表示最近的可能深度值,0(黑色)表示最远的可能深度值。这个过程称为深度归一化。使用retrieveImage(depth,VIEW_DEPTH)检索深度图像。在应用中不要将8位深度图像用于显示深度以外的其他目的。
depth_for_display = sl.Mat()
zed.retrieve_image(depth_for_display, sl.VIEW.DEPTH)
使用retrievemmeasure()可以检索具有(X,Y,Z)坐标和RGBA颜色的3D点云。
point_cloud = sl.Mat()
zed.retrieve_measure(point_cloud, sl.MEASURE.XYZRGBA)
使用getValue()访问特定的像素值。
# Get the 3D point cloud values for pixel (i, j)
point3D = point_cloud.get_value(i, j)
x = point3D[0]
y = point3D[1]
z = point3D[2]
color = point3D[3]
点云将数据存储在4个通道上,每个通道使用32位浮点数。最后一个浮点数用于存储颜色信息,其中R、G、B和alpha通道(4 x 8位)被连接到一个32位浮点数中。可以用XYZ< color >选择不同的颜色格式。例如,使用retrievemmeasure (point_cloud, MEASURE::XYZBGRA)可以获得BGRA颜色。
当测量距离时,使用3D点云代替深度图。用欧几里得距离公式可以计算物体相对于左相机的距离。
# Measure the distance of a point in the scene represented by pixel (i,j)
point3D = point_cloud.get_value(i, j)
distance = math.sqrt(point3D[0] * point3D[0] + point3D[1] * point3D[1] + point3D[2] * point3D[2])
表面法线可以使用retrievemmeasure (normal_map, MEASURE_NORMALS)来检索。法线图对可穿越性估计(traversability estimation)和实时照明(real-time lighting)很有用。输出是一个4通道32位矩阵(X,Y,Z,空),其中X,Y,Z值编码法向量的方向。
为了提高应用的性能并加快数据采集速度,可以通过在retrievemmeasure()中指定宽度和高度参数来检索较低分辨率的度量。还可以指定希望在CPU (RAM)或GPU内存中提供数据的位置。
point_cloud = sl.Mat()
# Retrieve a resized point cloud
# width and height specify the total number of columns and rows for the point cloud dataset
width = zed.get_resolution().width / 2
height = zed.get_resolution().height / 2
zed.retrieve_measure(point_cloud, sl.MEASURE.XYZRGBA, sl.MEM.GPU, width, height)