双目摄像头实现双目测距

Posted pan_jinquan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双目摄像头实现双目测距相关的知识,希望对你有一定的参考价值。

双目摄像头实现双目测距

目录

1.双目测距原理

2.双目测距实现

3.3D点云显示

4.项目代码


双目立体视觉深度相机实现双目测距功能,主要分为4个步骤:相机标定+双目校正+双目匹配+计算深度信息:

(1)相机标定:需要对双目相机进行标定,得到两个相机的内外参数、单应矩阵。
(2)  双目校正:根据标定结果对原始图像进行校正,校正后的两张图像位于同一平面且互相平行。
(3)双目匹配:对校正后的两张图像进行像素点匹配。
(4)计算深度图:根据匹配结果计算每个像素的深度,从而获得深度图。

本篇将着重介绍双目测距的深度图计算原理和实现,关于相机标定+双目校正+双目匹配等内容,鄙人已经整理了一个完整的项目内容,详情请查看博客《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python

双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python_pan_jinquan的博客-CSDN博客​本博客将实现Python版本的双目三维重建系统,项目代码实现包含:`双目标定`,`立体校正(含消除畸变)`,`立体匹配`,`视差计算`和`深度距离计算/3D坐标计算` 的知识点。限于篇幅,本博客不会过多赘述算法原理,而是手把手教你,如果搭建一套属于自己的双目三维重建的系统。该系统包含:支持双USB连接线的双目摄像头支持单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示)支持单目相机标定:mono_camera_calibration.py支持双目相机标定:stereo_camerahttps://panjinquan.blog.csdn.net/article/details/121301896


1.双目测距原理

原理很简单,利用了相似三角形计算距离,所以双目测距的主要任务在于前期摄像头的定标、双目图像点的特征匹配上。 


2.双目测距实现

实现双目测距最关键的步骤是获得视差图,视差图计算请查看博客《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python

得到了视差图之后,就可以计算像素深度了,在opencv中使用StereoRectify()函数可以得到一个重投影矩阵Q,它是一个4*4的视差图到深度图的映射矩阵(disparity-to-depth mapping matrix ),使用Q矩阵和cv2.reprojectImageTo3D即可实现将像素坐标转换为三维坐标,该函数会返回一个3通道的矩阵,分别存储X、Y、Z坐标(左摄像机坐标系下)。

def reprojectImageTo3D(disparity, Q, _3dImage=None, handleMissingValues=None, ddepth=None):
    """
    :param disparity: 输入视差图
    :param Q: 输入4*4的视差图到深度图的映射矩阵,即重投影矩阵 通过stereoRectify得到
            (disparity-to-depth mapping matrix)
    :param _3dImage: 映射后存储三维坐标的图像
             contains 3D coordinates of the point (x,y) computed from the disparity map
    :param handleMissingValues: 计算得到的非正常值是否给值,如果为true则给值10000
    :param ddepth: 输出类型 -1 即默认为CV_32FC3 还可以是 CV_16S, CV_32S, CV_32F
    :return:
    """

运算如下:


 

重投影矩阵Q中为左相机主点在图像中的坐标,f为焦距,为两台相机投影中心间的平移(负值),即基线baseline,相当于平移向量T[0], 是右相机主点在图像中的坐标。

其中Z即是深度距离depth:

其中 f 为焦距长度(像素焦距),b为基线长度,d为视差,为两个相机主点的列坐标。

这里有个地方需要注意,如果获得视差图像是CV_16S类型的,这样的视差图的每个像素值由一个16bit表示,其中低位的4位存储的是视差值得小数部分,所以真实视差值应该是该值除以16。在进行映射后应该乘以16,以获得毫米级真实位置。

    def get_depth(self, disparity, Q, scale=1.0, method=False):
        """
        reprojectImageTo3D(disparity, Q),输入的Q,单位必须是毫米(mm)
        :param disparity: 视差图
        :param Q: 重投影矩阵Q=[[1, 0, 0, -cx]
                           [0, 1, 0, -cy]
                           [0, 0, 0,  f]
                           [1, 0, -1/Tx, (cx-cx`)/Tx]]
            其中f为焦距,Tx相当于平移向量T的第一个参数
        :param scale: 单位变换尺度,默认scale=1.0,单位为毫米
        :return depth:ndarray(np.uint16),depth返回深度图, 即距离
        """
        # 将图片扩展至3d空间中,其z方向的值则为当前的距离
        if method:
            points_3d = cv2.reprojectImageTo3D(disparity, Q)  # 单位是毫米(mm)
            x, y, depth = cv2.split(points_3d)
        else:
            # baseline = abs(camera_config["T"][0])
            baseline = 1 / Q[3, 2]  # 基线也可以由T[0]计算
            fx = abs(Q[2, 3])
            depth = (fx * baseline) / disparity
        depth = depth * scale
        # depth = np.asarray(depth, dtype=np.uint16)
        depth = np.asarray(depth, dtype=np.float32)
        return depth
  • 运行demo.py后,鼠标点击图像任意区域,终端会打印对应距离
  • 鼠标点击手部区域会打印距离摄像头的距离约633mm,即0.63米,还是比较准的
(x,y)=(203,273),depth=633.881653mm
(x,y)=(197,329),depth=640.386047mm
(x,y)=(222,292),depth=631.549072mm
(x,y)=(237,270),depth=630.389221mm
(x,y)=(208,246),depth=652.560669mm

 双目测距的精度 说明:

根据上式可以看出,某点像素的深度精度取决于该点处估计的视差d的精度。假设视差d的误差恒定,当测量距离越远,得到的深度精度则越差,因此使用双目相机不适宜测量太远的目标。

如果想要对与较远的目标能够得到较为可靠的深度,一方面需要提高相机的基线距离,但是基线距离越大,左右视图的重叠区域就会变小,内容差异变大,从而提高立体匹配的难度,另一方面可以选择更大焦距的相机,然而焦距越大,相机的视域则越小,导致离相机较近的物体的距离难以估计。

理论上,深度方向的测量误差与测量距离的平方成正比,而X/Y方向的误差与距离成正比;而距离很近时,由于存在死角,会导致难以匹配的问题;想象一下,如果你眼前放置一块物体,那你左眼只能看到物体左侧表面,右眼同理只能看到物体右侧表面,这时由于配准失败,导致视差计算失败;这个问题在基线越长,问题就越严重

 双目立体视觉系统精度分析_3D Vision-CSDN博客_双目视觉定位精度在一个三维测量项目中,如果采用立体视觉方案,首先,要根据测量需求(精度、测量范围、速度等),确定立体视觉的硬件方案。Thomas Luhmann在他的《Close-Range Photogrammetry and 3D Imaging》(2014)中,给出立体视觉系统的简化分析方法。这个方法假设两个相机的光轴平行,基线与光轴垂直,基线长度b和焦距值c不存在误差。分析了图像处理误差对立体定位https://blog.csdn.net/xuyuhua1985/article/details/50151269


3.3D点云显示

恢复三维坐标后,就可以使用python-pcl和Open3D库显示点云图

PCL Python版比较难安装,如果安装不了,那可以采用Open3D勉强凑合使用吧

如下图所示,你可以用鼠标旋转坐标轴,放大点云

2D-RGBOpen3D点云显示PCL点云显示

    def show_3dcloud_for_open3d(self, frameL, frameR, points_3d):
        """
        使用open3d显示点云
        :param frameL:
        :param frameR:
        :param points_3d:
        :return:
        """
        if self.use_open3d:
            x, y, depth = cv2.split(points_3d)  # depth = points_3d[:, :, 2]
            self.open3d_viewer.show(color_image=frameL, depth_image=depth)

    def show_3dcloud_for_pcl(self, frameL, frameR, points_3d):
        """
        使用PCL显示点云
        :param frameL:
        :param frameR:
        :param points_3d:
        :return:
        """
        if self.use_pcl:
            self.pcl_viewer.add_3dpoints(points_3d, frameL)
            self.pcl_viewer.show()

4.项目代码

完整的项目代码】:https://download.csdn.net/download/guyuealian/42517006

《双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python》

双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python_pan_jinquan的博客-CSDN博客​本博客将实现Python版本的双目三维重建系统,项目代码实现包含:`双目标定`,`立体校正(含消除畸变)`,`立体匹配`,`视差计算`和`深度距离计算/3D坐标计算` 的知识点。限于篇幅,本博客不会过多赘述算法原理,而是手把手教你,如果搭建一套属于自己的双目三维重建的系统。该系统包含:支持双USB连接线的双目摄像头支持单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示)支持单目相机标定:mono_camera_calibration.py支持双目相机标定:stereo_camerahttps://panjinquan.blog.csdn.net/article/details/121301896

以上是关于双目摄像头实现双目测距的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV C++双目三维重建:双目摄像头实现双目测距

OpenCV C++双目摄像头实现双目测距

Android双目三维重建:Android双目摄像头实现双目测距

双目测距 SGBM算法 Python版

双目测距 BM算法 Python版

双目测距 BM算法 Python版