精华内容
下载资源
问答
  • 双目

    2018-09-09 23:03:28
    在机器视觉系统中,双目视觉一般由双摄像机从不同角度同时获取周围景物的两幅数字图像,或有由单摄像机在不同时刻从不同角度获取周围景物的两幅数字图像,并基于视差原理即可恢复出物体三维几何信息,重建周围景物的...

    1.前言戏说

    双目立体视觉是基于视差原理,由多幅图像获取物体三维几何信息的方法。在机器视觉系统中,双目视觉一般由双摄像机从不同角度同时获取周围景物的两幅数字图像,或有由单摄像机在不同时刻从不同角度获取周围景物的两幅数字图像,并基于视差原理即可恢复出物体三维几何信息,重建周围景物的三维形状与位置。

    双目视觉有的时候我们也会把它称为体视,是人类利用双眼获取环境三维信息的主要途径。从目前来看,随着机器视觉理论的发展,双目立体视觉在机器视觉研究中发回来看了越来越重要的作用。本篇帖子主要研究了双目视觉的数学原理。

    2.双目立体视觉的数学原理

    双目立体视觉是基于视差,由三角法原理进行三维信息的获取,即由两个摄像机的图像平面和北侧物体之间构成一个三角形。一直两个摄像机之间的位置关系,便可以获得两摄像机公共视场内物体的三维尺寸及空间物体特征点的三维坐标。所以,双目视觉系统一般由两个摄像机构成。

    2.1 双目立体视觉三维测量原理

    上图所示为简单的平视双目立体成像原理图,两摄像机的投影中心连线的距离,即基线距离B。两摄像机在同一时刻观看时空物体的同一特征点P,分别在“左眼”和“右眼”上获取了点P的图像,他们的坐标分别为Pleft=(Xleft,Yleft);Pright=(Xright,Yright)。将定两摄像机的图像在同一平面上,则特征点P的图像坐标的Y坐标一定是相同的,即Yleft = Yright =Y。由三角几何关系可以得到如下关系式:

    则视差为:Disparity=Xleft-Xright.由此可以计算出特征点P在摄像机坐标系下的三维坐标:

    因此,左摄像机像面上的任意一点只要能在右摄像机像面上找到对应的匹配点,就完全可以确定该点的三维坐标。这种方法是点对点的运算,像平面上所有点只要存在相应的匹配点,就可以参与上述运算,从而获取对应的三维坐标。

    2.2 双目立体视觉数学模型

    在分析了最简单的平视双目立体视觉的三维测量原理基础上,现在我们就有能力来考虑一般情况。如上图所示,设左摄像机O-xyz位于世界坐标系原点,且没有发生旋转,图像坐标系为Ol-X1Y1,有效焦距为fl;右摄像机坐标系为Or-xyz,图像坐标系为Or-XrYr,有效焦距为fr。那么根据摄像机的投射模型我们就能得到如下关系式:

      

    因为O-xyz坐标系与Or-xryrzr坐标系之间的位置关系可通过空间转换矩阵MLr表示为:

    同理,对于O-xyz坐标系中的空间点,两个摄像机面点之间的对应关系可以表示为:

    于是,空间点三维坐标可以表示为

    因此,只要我们通过计算机标定技术获得左右计算机内参数/焦距fr,fl和空间点在左右摄像机中的图像坐标,就能够重构出被测点的三维空间坐标。

    展开全文
  • 双目测距理论及其python实现!

    万次阅读 多人点赞 2019-09-03 23:19:09
    一、双目测距基本流程 双目测距属于双目SLAM的一个应用领域。 关于双目测距的基本原理,其实并不复杂,但说起来内容也不少,其核心原理就是三角测量,三角测量在土地测量、天文测量等领域都得到了广泛应用,是一种...

    一、双目测距基本流程

            Stereo Vision, 也叫双目立体视觉,它的研究可以帮助我们更好的理解人类的双眼是如何进行深度感知的。双目视觉在许多领域得到了应用,例如城市三维重建、3D模型构建(如kinect fusion)、视角合成、3D跟踪、机器人导航(自动驾驶)、人类运动捕捉(Microsoft Kinect)等等。双目测距也属于双目立体视觉的一个应用领域,双目测距的基本原理主要是三角测量原理,即通过视差来判定物体的远近。如果读者想对双目测距的内容有一个更加深入的认识,建议去阅读《计算机视觉中的多视图几何》,这本书是视觉领域的经典之作,它的优点是内容全面,每一个定理都给出了严格的数学证明,缺点就是理论内容非常多,而且难度也非常高,读起来更像是在看一本数学书。(注:虽然一些文章中使用binocular代表双目,而stereo代表意义更为广泛的立体一词,但多数文献中仍然采用stereo来代表双目,比如stereo matching一般表示双目立体匹配)

    那么总结起来,双目测距的大致流程就是:

               双目标定 --> 立体校正(含消除畸变) --> 立体匹配 --> 视差计算 --> 深度计算/3D坐标计算

    下面将分别阐述每一个步骤并使用opencv-python来实现。本篇博客将偏重实践一些,理论方面的论述会比较少,但也会尽量写的通俗易懂。

    linux下安装opencv-python:

    pip install opencv-python

    二、双目标定

           双目标定的目标是获得左右两个相机的内参、外参和畸变系数,其中内参包括左右相机的fx,fy,cx,cy,外参包括左相机相对于右相机的旋转矩阵和平移向量,畸变系数包括径向畸变系数(k1, k2,k3)和切向畸变系数(p1,p2)。关于双目相机的标定方法,请参考这篇博客:双目相机的标定过程详解!-----MATLAB

    import cv2
    import numpy
    
    class stereoCameraCalibration():
        pass

    三、畸变校正

           光线经过相机的光学系统往往不能按照理想的情况投射到传感器上,也就是会产生所谓的畸变。畸变有两种情况:一种是由透镜形状引起的畸变称之为径向畸变。在针孔模型中,一条直线投影到像素平面上还是一条直线。可是,在实际拍摄的照片中,摄像机的透镜往往使得真实环境中的一条直线在图片中变成了曲线。越靠近图像的边缘,这种现象越明显。由于实际加工制作的透镜往往是中心对称的,这使得不规则的畸变通常径向对称。它们主要分为两大类,桶形畸变 枕形畸变(摘自《SLAM十四讲》)如图所示:

           桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。 在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。 

           除了透镜的形状会引入径向畸变外,在相机的组装过程中由于不能使得透镜和成像面 严格平行也会引入切向畸变。如图所示:

    那么消除畸变的方法就是:

    1. 将三维空间点投影到归一化图像平面。设它的归一化坐标为 [x,y]T

    2. 对归一化平面上的点进行径向畸变和切向畸变纠正。

    3. 将纠正后的点通过内参数矩阵投影到像素平面,得到该点在图像上的正确位置

    四、立体校正

           立体校正的目的是将拍摄于同一场景的左右两个视图进行数学上的投影变换,使得两个成像平面平行于基线,且同一个点在左右两幅图中位于同一行,简称共面行对准。只有达到共面行对准以后才可以应用三角原理计算距离。

    五、立体匹配与视差图计算

           立体匹配的目的是为左图中的每一个像素点在右图中找到其对应点(世界中相同的物理点),这样就可以计算出视差: disparity = x_{i} - x_{j}(xi和xj分别表示两个对应点在图像中的列坐标)。大部分立体匹配算法的计算过程可以分成以下几个阶段:匹配代价计算、代价聚合、视差优化、视差细化。立体匹配是立体视觉中一个很难的部分,主要困难在于:1.图像中可能存在重复纹理和弱纹理,这些区域很难匹配正确;2.由于左右相机的拍摄位置不同,图像中几乎必然存在遮挡区域,在遮挡区域,左图中有一些像素点在右图中并没有对应的点,反之亦然;3.左右相机所接收的光照情况不同;4.过度曝光区域难以匹配;5.倾斜表面、弯曲表面、非朗伯体表面;6.较高的图像噪声等。

           常用的立体匹配方法基本上可以分为两类:局部方法,例如BM、SGM、ELAS、Patch Match等,非局部的,即全局方法,例如Dynamic Programming、Graph Cut、Belief Propagation等,局部方法计算量小,匹配质量相对较低,全局方法省略了代价聚合而采用了优化能量函数的方法,匹配质量较高,但是计算量也比较大。目前OpenCV中已经实现的方法有BM、binaryBM、SGBM、binarySGBM、BM(cuda)、Bellief Propogation(cuda)、Constant Space Bellief Propogation(cuda)这几种方法。比较好用的是SGBM算法,它的核心是基于SGM算法,但和SGM算法又有一些不同,比如匹配代价部分用的是BT代价(原图+梯度图)而不是HMI代价等等。有关SGM算法的原理解释,可以参考另一篇博客 : 双目立体匹配算法:SGM

            在立体匹配生成视差图之后,还可以对视差图进行滤波后处理,例如Guided Filter、Fast Global Smooth Filter(一种快速WLS滤波方法)、Bilatera Filter、TDSR、RBS等。 视差图滤波能够将稀疏视差转变为稠密视差,并在一定程度上降低视差图噪声,改善视差图的视觉效果,但是比较依赖初始视差图的质量。

    六、深度图计算

    得到了视差图之后,就可以计算像素深度了,公式如下(推导略):

                                                                                                      depth =\frac{ f\times b}{d + \left(c_{xr}- c_{xl}\right )}

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

    注:在opencv中使用StereoRectify()函数可以得到一个重投影矩阵Q,使用Q矩阵也可以将像素坐标转换为三维坐标。

    七、双目测距的精度

           根据上式可以看出,某点像素的深度精度取决于该点处估计的视差d的精度。假设视差d的误差恒定,当测量距离越远,得到的深度精度则越差,因此使用双目相机不适宜测量太远的目标。如果想要对与较远的目标能够得到较为可靠的深度,一方面需要提高相机的基线距离,但是基线距离越大,左右视图的重叠区域就会变小,内容差异变大,从而提高立体匹配的难度,另一方面可以选择更大焦距的相机,然而焦距越大,相机的视域则越小,导致离相机较近的物体的距离难以估计。

    八、代码实现

    最重要的就是代码了,不是么!

    首先需要引入相应的包:

    import cv2
    import numpy as np

    然后:

    # 预处理
    def preprocess(img1, img2):
        # 彩色图->灰度图
        if(img1.ndim == 3):
            img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  # 通过OpenCV加载的图像通道顺序是BGR
        if(img2.ndim == 3):
            img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    
        # 直方图均衡
        img1 = cv2.equalizeHist(img1)
        img2 = cv2.equalizeHist(img2)
    
        return img1, img2
    
    
    # 消除畸变
    def undistortion(image, camera_matrix, dist_coeff):
        undistortion_image = cv2.undistort(image, camera_matrix, dist_coeff)
    
        return undistortion_image
    
    
    # 获取畸变校正和立体校正的映射变换矩阵、重投影矩阵
    # @param:config是一个类,存储着双目标定的参数:config = stereoconfig.stereoCamera()
    def getRectifyTransform(height, width, config):
        # 读取内参和外参
        left_K = config.cam_matrix_left
        right_K = config.cam_matrix_right
        left_distortion = config.distortion_l
        right_distortion = config.distortion_r
        R = config.R
        T = config.T
    
        # 计算校正变换
        height = int(height)
        width = int(width)
        R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(left_K, left_distortion, right_K, right_distortion, (width, height), R, T, alpha=0)
    
        map1x, map1y = cv2.initUndistortRectifyMap(left_K, left_distortion, R1, P1, (width, height), cv2.CV_32FC1)
        map2x, map2y = cv2.initUndistortRectifyMap(right_K, right_distortion, R2, P2, (width, height), cv2.CV_32FC1)
    
        return map1x, map1y, map2x, map2y, Q
    
    
    # 畸变校正和立体校正
    def rectifyImage(image1, image2, map1x, map1y, map2x, map2y):
        rectifyed_img1 = cv2.remap(image1, map1x, map1y, cv2.INTER_AREA)
        rectifyed_img2 = cv2.remap(image2, map2x, map2y, cv2.INTER_AREA)
    
        return rectifyed_img1, rectifyed_img2
    
    
    # 立体校正检验----画线
    def draw_line(image1, image2):
        # 建立输出图像
        height = max(image1.shape[0], image2.shape[0])
        width = image1.shape[1] + image2.shape[1]
    
        output = np.zeros((height, width, 3), dtype=np.uint8)
        output[0:image1.shape[0], 0:image1.shape[1]] = image1
        output[0:image2.shape[0], image1.shape[1]:] = image2
    
        # 绘制等间距平行线
        line_interval = 50  # 直线间隔:50
        for k in range(height // line_interval):
            cv2.line(output, (0, line_interval * (k + 1)), (2 * width, line_interval * (k + 1)), (0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
    
        return output
    
    
    # 视差计算
    def stereoMatchSGBM(left_image, right_image, down_scale=False):
        # SGBM匹配参数设置
        if left_image.ndim == 2:
            img_channels = 1
        else:
            img_channels = 3
        blockSize = 3
        paraml = {'minDisparity': 0,
                 'numDisparities': 128,
                 'blockSize': blockSize,
                 'P1': 8 * img_channels * blockSize ** 2,
                 'P2': 32 * img_channels * blockSize ** 2,
                 'disp12MaxDiff': 1,
                 'preFilterCap': 63,
                 'uniquenessRatio': 15,
                 'speckleWindowSize': 100,
                 'speckleRange': 1,
                 'mode': cv2.STEREO_SGBM_MODE_SGBM_3WAY
                 }
    
        # 构建SGBM对象
        left_matcher = cv2.StereoSGBM_create(**paraml)
        paramr = paraml
        paramr['minDisparity'] = -paraml['numDisparities']
        right_matcher = cv2.StereoSGBM_create(**paramr)
    
        # 计算视差图
        size = (left_image.shape[1], left_image.shape[0])
        if down_scale == False:
            disparity_left = left_matcher.compute(left_image, right_image)
            disparity_right = right_matcher.compute(right_image, left_image)
    
        else:
            left_image_down = cv2.pyrDown(left_image)
            right_image_down = cv2.pyrDown(right_image)
            factor = left_image.shape[1] / left_image_down.shape[1]
    
            disparity_left_half = left_matcher.compute(left_image_down, right_image_down)
            disparity_right_half = right_matcher.compute(right_image_down, left_image_down)
            disparity_left = cv2.resize(disparity_left_half, size, interpolation=cv2.INTER_AREA)
            disparity_right = cv2.resize(disparity_right_half, size, interpolation=cv2.INTER_AREA)
            disparity_left = factor * disparity_left
            disparity_right = factor * disparity_right
    
        # 真实视差(因为SGBM算法得到的视差是×16的)
        trueDisp_left = disparity_left.astype(np.float32) / 16.
        trueDisp_right = disparity_right.astype(np.float32) / 16.
    
        return trueDisp_left, trueDisp_right

    stereoconfig.py的代码:

    import numpy as np
    
    
    ####################仅仅是一个示例###################################
    
    
    # 双目相机参数
    class stereoCamera(object):
        def __init__(self):
            # 左相机内参
            self.cam_matrix_left = np.array([[1499.64168081943, 0, 1097.61651199043],
                                             [0., 1497.98941910377, 772.371510027325],
                                             [0., 0., 1.]])
            # 右相机内参
            self.cam_matrix_right = np.array([[1494.85561041115, 0, 1067.32184876563],
                                              [0., 1491.89013795616, 777.983913223449],
                                              [0., 0., 1.]])
    
            # 左右相机畸变系数:[k1, k2, p1, p2, k3]
            self.distortion_l = np.array([[-0.110331619900584, 0.0789239541458329, -0.000417147132750895, 0.00171210128855920, -0.00959533143245654]])
            self.distortion_r = np.array([[-0.106539730103100, 0.0793246026401067, -0.000288067586478778, -8.92638488356863e-06, -0.0161669384831612]])
    
            # 旋转矩阵
            self.R = np.array([[0.993995723217419, 0.0165647819554691, 0.108157802419652],
                               [-0.0157381345263306, 0.999840084288358, -0.00849217121126161],
                               [-0.108281177252152, 0.00673897982027135, 0.994097466450785]])
    
            # 平移矩阵
            self.T = np.array([[-423.716923177417], [2.56178287450396], [21.9734621041330]])
    
            # 焦距
            self.focal_length = 1602.46406  # 默认值,一般取立体校正后的重投影矩阵Q中的 Q[2,3]
    
            # 基线距离
            self.baseline = 423.716923177417  # 单位:mm, 为平移向量的第一个参数(取绝对值)

    九、构建点云

           有了视差便可以计算深度,因此根据双目的视差图可以构建稠密点云,OpenCV中提供了reprojectImageTo3D()这个函数用于计算像素点的三维坐标,该函数会返回一个3通道的矩阵,分别存储X、Y、Z坐标(左摄像机坐标系下)。在python-pcl库中提供了用来显示点云的工具(python-pcl是PCL库的python接口,但是只提供了部分功能,且对点云的各种处理功能只限于PointXYZ格式的点云),python-pcl的下载地址:GitHub - strawlab/python-pcl: Python bindings to the pointcloud library (pcl),下载好后按步骤安装好。windows平台下的安装可以参考这两个博客:1.Windows10下PCL1.8.1以及Python-pcl1.81环境配置的掉发之路 2.win10平台python-pcl环境搭建

    下面是构建并显示点云的代码:

    # 将h×w×3数组转换为N×3的数组
    def hw3ToN3(points):
        height, width = points.shape[0:2]
    
        points_1 = points[:, :, 0].reshape(height * width, 1)
        points_2 = points[:, :, 1].reshape(height * width, 1)
        points_3 = points[:, :, 2].reshape(height * width, 1)
    
        points_ = np.hstack((points_1, points_2, points_3))
    
        return points_
    
    
    # 深度、颜色转换为点云
    def DepthColor2Cloud(points_3d, colors):
        rows, cols = points_3d.shape[0:2]
        size = rows * cols
    
        points_ = hw3ToN3(points_3d)
        colors_ = hw3ToN3(colors).astype(np.int64)
    
        # 颜色信息
        blue = colors_[:, 0].reshape(size, 1)
        green = colors_[:, 1].reshape(size, 1)
        red = colors_[:, 2].reshape(size, 1)
    
        rgb = np.left_shift(blue, 0) + np.left_shift(green, 8) + np.left_shift(red, 16)
    
        # 将坐标+颜色叠加为点云数组
        pointcloud = np.hstack((points_, rgb)).astype(np.float32)
    
        # 删掉一些不合适的点
        X = pointcloud[:, 0]
        Y = pointcloud[:, 1]
        Z = pointcloud[:, 2]
    
        # 下面参数是经验性取值,需要根据实际情况调整
        remove_idx1 = np.where(Z <= 0)
        remove_idx2 = np.where(Z > 15000)  // 注意单位是mm
        remove_idx3 = np.where(X > 10000)
        remove_idx4 = np.where(X < -10000)
        remove_idx5 = np.where(Y > 10000)
        remove_idx6 = np.where(Y < -10000)
        remove_idx = np.hstack((remove_idx1[0], remove_idx2[0], remove_idx3[0], remove_idx4[0], remove_idx5[0], remove_idx6[0]))
    
        pointcloud_1 = np.delete(pointcloud, remove_idx, 0)
    
    
        return pointcloud_1
    
    
    # 点云显示
    def view_cloud(pointcloud):
        cloud = pcl.PointCloud_PointXYZRGBA()
        cloud.from_array(pointcloud)
    
        try:
            visual = pcl.pcl_visualization.CloudViewing()
            visual.ShowColorACloud(cloud)
            v = True
            while v:
                v = not (visual.WasStopped())
        except:
            pass

    十、效果图

    下面的数据使用的是MiddleBurry双目数据,可以不用做立体校正(因为已经校正过了):

    利用SGBM算法得到视差图如下:

    点云如图所示:

    各位觉得不错的点个赞吧!

    -------------------------------------------------------------------补充完整示例代码------------------------------------------------------------------------------

    修改时间:2021.1.11

    # -*- coding: utf-8 -*-
    import cv2
    import numpy as np
    import stereoconfig
    import pcl
    import pcl.pcl_visualization
    
    
    # 预处理
    def preprocess(img1, img2):
        # 彩色图->灰度图
        if(img1.ndim == 3):
            img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  # 通过OpenCV加载的图像通道顺序是BGR
        if(img2.ndim == 3):
            img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    
        # 直方图均衡
        img1 = cv2.equalizeHist(img1)
        img2 = cv2.equalizeHist(img2)
    
        return img1, img2
    
    
    # 消除畸变
    def undistortion(image, camera_matrix, dist_coeff):
        undistortion_image = cv2.undistort(image, camera_matrix, dist_coeff)
    
        return undistortion_image
    
    
    # 获取畸变校正和立体校正的映射变换矩阵、重投影矩阵
    # @param:config是一个类,存储着双目标定的参数:config = stereoconfig.stereoCamera()
    def getRectifyTransform(height, width, config):
        # 读取内参和外参
        left_K = config.cam_matrix_left
        right_K = config.cam_matrix_right
        left_distortion = config.distortion_l
        right_distortion = config.distortion_r
        R = config.R
        T = config.T
    
        # 计算校正变换
        R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(left_K, left_distortion, right_K, right_distortion, (width, height), R, T, alpha=0)
    
        map1x, map1y = cv2.initUndistortRectifyMap(left_K, left_distortion, R1, P1, (width, height), cv2.CV_32FC1)
        map2x, map2y = cv2.initUndistortRectifyMap(right_K, right_distortion, R2, P2, (width, height), cv2.CV_32FC1)
    
        return map1x, map1y, map2x, map2y, Q
    
    
    # 畸变校正和立体校正
    def rectifyImage(image1, image2, map1x, map1y, map2x, map2y):
        rectifyed_img1 = cv2.remap(image1, map1x, map1y, cv2.INTER_AREA)
        rectifyed_img2 = cv2.remap(image2, map2x, map2y, cv2.INTER_AREA)
    
        return rectifyed_img1, rectifyed_img2
    
    
    # 立体校正检验----画线
    def draw_line(image1, image2):
        # 建立输出图像
        height = max(image1.shape[0], image2.shape[0])
        width = image1.shape[1] + image2.shape[1]
    
        output = np.zeros((height, width, 3), dtype=np.uint8)
        output[0:image1.shape[0], 0:image1.shape[1]] = image1
        output[0:image2.shape[0], image1.shape[1]:] = image2
    
        # 绘制等间距平行线
        line_interval = 50  # 直线间隔:50
        for k in range(height // line_interval):
            cv2.line(output, (0, line_interval * (k + 1)), (2 * width, line_interval * (k + 1)), (0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
    
        return output
    
    
    # 视差计算
    def stereoMatchSGBM(left_image, right_image, down_scale=False):
        # SGBM匹配参数设置
        if left_image.ndim == 2:
            img_channels = 1
        else:
            img_channels = 3
        blockSize = 3
        paraml = {'minDisparity': 0,
                 'numDisparities': 128,
                 'blockSize': blockSize,
                 'P1': 8 * img_channels * blockSize ** 2,
                 'P2': 32 * img_channels * blockSize ** 2,
                 'disp12MaxDiff': 1,
                 'preFilterCap': 63,
                 'uniquenessRatio': 15,
                 'speckleWindowSize': 100,
                 'speckleRange': 1,
                 'mode': cv2.STEREO_SGBM_MODE_SGBM_3WAY
                 }
    
        # 构建SGBM对象
        left_matcher = cv2.StereoSGBM_create(**paraml)
        paramr = paraml
        paramr['minDisparity'] = -paraml['numDisparities']
        right_matcher = cv2.StereoSGBM_create(**paramr)
    
        # 计算视差图
        size = (left_image.shape[1], left_image.shape[0])
        if down_scale == False:
            disparity_left = left_matcher.compute(left_image, right_image)
            disparity_right = right_matcher.compute(right_image, left_image)
    
        else:
            left_image_down = cv2.pyrDown(left_image)
            right_image_down = cv2.pyrDown(right_image)
            factor = left_image.shape[1] / left_image_down.shape[1]
    
            disparity_left_half = left_matcher.compute(left_image_down, right_image_down)
            disparity_right_half = right_matcher.compute(right_image_down, left_image_down)
            disparity_left = cv2.resize(disparity_left_half, size, interpolation=cv2.INTER_AREA)
            disparity_right = cv2.resize(disparity_right_half, size, interpolation=cv2.INTER_AREA)
            disparity_left = factor * disparity_left
            disparity_right = factor * disparity_right
    
        # 真实视差(因为SGBM算法得到的视差是×16的)
        trueDisp_left = disparity_left.astype(np.float32) / 16.
        trueDisp_right = disparity_right.astype(np.float32) / 16.
    
        return trueDisp_left, trueDisp_right
    
    
    # 将h×w×3数组转换为N×3的数组
    def hw3ToN3(points):
        height, width = points.shape[0:2]
    
        points_1 = points[:, :, 0].reshape(height * width, 1)
        points_2 = points[:, :, 1].reshape(height * width, 1)
        points_3 = points[:, :, 2].reshape(height * width, 1)
    
        points_ = np.hstack((points_1, points_2, points_3))
    
        return points_
    
    
    # 深度、颜色转换为点云
    def DepthColor2Cloud(points_3d, colors):
        rows, cols = points_3d.shape[0:2]
        size = rows * cols
    
        points_ = hw3ToN3(points_3d)
        colors_ = hw3ToN3(colors).astype(np.int64)
    
        # 颜色信息
        blue = colors_[:, 0].reshape(size, 1)
        green = colors_[:, 1].reshape(size, 1)
        red = colors_[:, 2].reshape(size, 1)
    
        rgb = np.left_shift(blue, 0) + np.left_shift(green, 8) + np.left_shift(red, 16)
    
        # 将坐标+颜色叠加为点云数组
        pointcloud = np.hstack((points_, rgb)).astype(np.float32)
    
        # 删掉一些不合适的点
        X = pointcloud[:, 0]
        Y = pointcloud[:, 1]
        Z = pointcloud[:, 2]
    
        # 下面参数是经验性取值,需要根据实际情况调整
        remove_idx1 = np.where(Z <= 0)
        remove_idx2 = np.where(Z > 15000)
        remove_idx3 = np.where(X > 10000)
        remove_idx4 = np.where(X < -10000)
        remove_idx5 = np.where(Y > 10000)
        remove_idx6 = np.where(Y < -10000)
        remove_idx = np.hstack((remove_idx1[0], remove_idx2[0], remove_idx3[0], remove_idx4[0], remove_idx5[0], remove_idx6[0]))
    
        pointcloud_1 = np.delete(pointcloud, remove_idx, 0)
    
        return pointcloud_1
    
    
    # 点云显示
    def view_cloud(pointcloud):
        cloud = pcl.PointCloud_PointXYZRGBA()
        cloud.from_array(pointcloud)
    
        try:
            visual = pcl.pcl_visualization.CloudViewing()
            visual.ShowColorACloud(cloud)
            v = True
            while v:
                v = not (visual.WasStopped())
        except:
            pass
    
    
    if __name__ == '__main__':
        # 读取MiddleBurry数据集的图片
        iml = cv2.imread('/data/数据/MiddleBurry/Adirondack-perfect/im0.png')  # 左图
        imr = cv2.imread('/data/数据/MiddleBurry/Adirondack-perfect/im1.png')  # 右图
        height, width = iml.shape[0:2]
    
        # 读取相机内参和外参
        config = stereoconfig.stereoCamera1()
    
        # 立体校正
        map1x, map1y, map2x, map2y, Q = getRectifyTransform(height, width, config)  # 获取用于畸变校正和立体校正的映射矩阵以及用于计算像素空间坐标的重投影矩阵
        iml_rectified, imr_rectified = rectifyImage(iml, imr, map1x, map1y, map2x, map2y)
        print(Q)
    
        # 绘制等间距平行线,检查立体校正的效果
        line = draw_line(iml_rectified, imr_rectified)
        cv2.imwrite('/data/检验.png', line)
    
        # 立体匹配
        iml_, imr_ = preprocess(iml, imr)  # 预处理,一般可以削弱光照不均的影响,不做也可以
        disp, _ = stereoMatchSGBM(iml_, imr_, True)  # 这里传入的是未经立体校正的图像,因为我们使用的middleburry图片已经是校正过的了
        cv2.imwrite('/data/视差.png', disp)
    
        # 计算像素点的3D坐标(左相机坐标系下)
        points_3d = cv2.reprojectImageTo3D(disp, Q)  # 可以使用上文的stereo_config.py给出的参数
    
        # 构建点云--Point_XYZRGBA格式
        pointcloud = DepthColor2Cloud(points_3d, iml)
    
        # 显示点云
        view_cloud(pointcloud)
    

    参考资料:

    SGBM算法详解(一)

    SGBM算法详解(二)

    双目视觉之空间坐标计算

    Stereo disparity quality problems

    Disparity map post-filtering

    OpenCV Stereo – Depth image generation and filtering

    OpenCV+OpenGL 双目立体视觉三维重建

    双目视觉测距原理,数学推导及三维重建资源

    展开全文
  • 算法的双目立体视觉、双目测距(双目校正和立体匹配)(文档里包含了测试图片)
  • 算法的双目立体视觉、双目测距(双目校正和立体匹配)(文档里包含了测试图片)
  • 使用双目摄像头,基于opencv的测距程序
  • 双目视差匹配测距算法
  • 实现双目测距功能,需要python环境,安装OpenCV,自行标定
  • 基于FPGA平台的双目视觉处理项目,可以通过双目摄像头实现目标物体测距,测量大小等功能
  • 视觉采集(双目),双目视觉测量.zip
  • shi01SGBM_双目_双目测距_测距_双目测距_测试的sgbm算法_源码.zip
  • shi01SGBM_双目_双目测距_测距_双目测距_测试的sgbm算法_源码.rar
  • 双目视觉检测

    2018-03-11 20:42:02
    双目视觉检测 特征点匹配 基于双目摄像头
  • 本程序为LabVIEW双目采集程序,打开左右两个摄像头采集图像后自动合为一个图像,并对图像进行图像灰度化,图像灰度均值化,图像中值滤波一系列操作,并可以保存图像为jpeg图像
  • 一个双目匹配求解视差图的matlab源代码
  • 视觉采集(双目),双目视觉测量源码
  • 双目测距原理

    万次阅读 多人点赞 2019-05-28 16:16:42
    双目测距基本原理: 双目测距实际操作分4个步骤:相机标定——双目校正——双目匹配——计算深度信息。 相机标定:摄像头由于光学透镜的特性使得成像存在着径向畸变,可由三个参数k1,k2,k3确定;由于装配方面...

    双目测距基本原理:

    双目测距实际操作分4个步骤:相机标定——双目校正——双目匹配——计算深度信息。

    相机标定:摄像头由于光学透镜的特性使得成像存在着径向畸变,可由三个参数k1,k2,k3确定;由于装配方面的误差,传感器与光学镜头之间并非完全平行,因此成像存在切向畸变,可由两个参数p1,p2确定。单个摄像头的定标主要是计算出摄像头的内参(焦距f和成像原点cx,cy、五个畸变参数(一般只需要计算出k1,k2,p1,p2,对于鱼眼镜头等径向畸变特别大的才需要计算k3))以及外参(标定物的世界坐标)。而双目摄像头定标不仅要得出每个摄像头的内部参数,还需要通过标定来测量两个摄像头之间的相对位置(即右摄像头相对于左摄像头的旋转矩阵R、平移向量t)。

    双目校正:双目校正是根据摄像头定标后获得的单目内参数据(焦距、成像原点、畸变系数)和双目相对位置关系(旋转矩阵和平移向量),分别对左右视图进行消除畸变和行对准,使得左右视图的成像原点坐标一致(CV_CALIB_ZERO_DISPARITY标志位设置时发生作用)、两摄像头光轴平行、左右成像平面共面、对极线行对齐。这样一幅图像上任意一点与其在另一幅图像上的对应点就必然具有相同的行号,只需在该行进行一维搜索即可匹配到对应点。
    双目匹配:双目匹配的作用是把同一场景在左右视图上对应的像点匹配起来,这样做的目的是为了得到视差图。双目匹配被普遍认为是立体视觉中最困难也是最关键的问题。得到视差数据,通过上述原理中的公式就可以很容易的计算出深度信息。

     

     

    双目摄像机的物理机构

    网上大部分人都写了这一点,也仿佛只有这一点有写的价值和物理意义。
    这里本来想放个控件(可调节动画),一直弄不上来,就算了。需要的可以联系我要下,下面放两张图片。

    在这里插入图片描述

    图中可以得出的结论
    1.深度变化(EG*EF/(AB+CD)或者H到EF的距离),会导致AB,CD和AB+CD的变化,这里不过多强调AB,CD的变化,只讨论AB+CD,这个原因后边会提到。当深度变大时,AB+CD逐渐变小。
    从公式(公式看不懂没关系,它只是我推导的,大家也可以自己推一下,推导是三角形的比例关系)
    AC=EF-AB-DC
    设Z为深度
    那么AC/EF=(Z-EG)/Z
    这样就可以推导出来了。
    在这里插入图片描述

    从这个图我们就可以明显看出只要我们的深度不变,那么我们的AB+CD也就不会改变,可以看出,深度和单独的AB与CD没有直接关系,而只与两者的和有关。

    AB+CD 与同一距离的 视差 是想等的。  

    AB + DC = XR - XT  

    注意此处 AB DC 用向量相加  (AB + DC =  =  (Bx-Ax)+ (Cx - Dx)   【Bx 为左图像的中点x, Dx为右图像的中点x  Ax,Cx为两幅图同一特征点的x坐标】

    XR - XT = XRx - XTx  (XRx 与 XTx 分别是两幅图同一特征点的x坐标)

    备注:此处的公式都是假设摄像头是水平的,如果摄像头垂直,应该使用 y 坐标。

     

    单目测距原理:

    先通过图像匹配进行目标识别(各种车型、行人、物体等),再通过目标在图像中的大小去估算目标距离。这就要求在估算距离之前首先对目标进行准确识别,是汽车还是行人,是货车、SUV还是小轿车。准确识别是准确估算距离的第一步。要做到这一点,就需要建立并不断维护一个庞大的样本特征数据库,保证这个数据库包含待识别目标的全部特征数据。比如在一些特殊地区,为了专门检测大型动物,必须先行建立大型动物的数据库;而对于另外某些区域存在一些非常规车型,也要先将这些车型的特征数据加入到数据库中。如果缺乏待识别目标的特征数据,就会导致系统无法对这些车型、物体、障碍物进行识别,从而也就无法准确估算这些目标的距离。

    单/双目方案的优点与难点

    从上面的介绍,单目系统的优势在于成本较低,对计算资源的要求不高,系统结构相对简单;缺点是:(1)需要不断更新和维护一个庞大的样本数据库,才能保证系统达到较高的识别率;(2)无法对非标准障碍物进行判断;(3)距离并非真正意义上的测量,准确度较低。

    双目检测原理:

    通过对两幅图像视差的计算,直接对前方景物(图像所拍摄到的范围)进行距离测量,而无需判断前方出现的是什么类型的障碍物。所以对于任何类型的障碍物,都能根据距离信息的变化,进行必要的预警或制动。双目摄像头的原理与人眼相似。人眼能够感知物体的远近,是由于两只眼睛对同一个物体呈现的图像存在差异,也称“视差”。物体距离越远,视差越小;反之,视差越大。视差的大小对应着物体与眼睛之间距离的远近,这也是3D电影能够使人有立体层次感知的原因。
     

    上图中的人和椰子树,人在前,椰子树在后,最下方是双目相机中的成像。其中,右侧相机成像中人在树的左侧,左侧相机成像中人在树的右侧,这是因为双目的角度不一样。再通过对比两幅图像就可以知道人眼观察树的时候视差小,而观察人时视差大。因为树的距离远,人的距离近。这就是双目三角测距的原理。双目系统对目标物体距离感知是一种绝对的测量,而非估算。

    理想双目相机成像模型

    根据上述推导,要求得空间点P离相机的距离(深度)z,必须知道:
    1、相机焦距f,左右相机基线b(可以通过先验信息或者相机标定得到)。
    2、视差 :,即左相机像素点(xl, yl)和右相机中对应点(xr, yr)的关系,这是双目视觉的核心问题。

     

    重点来看一下视差(disparity),视差是同一个空间点在两个相机成像中对应的x坐标的差值,它可以通过编码成灰度图来反映出距离的远近,离镜头越近的灰度越亮; (前提是两个摄像头是水平,如果两颗摄像头是垂直的,则使用y坐标的差值)

     

    极线约束

    对于左图中的一个像素点,如何确定该点在右图中的位置?需要在整个图像中地毯式搜索吗?当然不用,此时需要用到极线约束。
    如上图所示。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。
     

    非理性情况:

    上面是两相机共面且光轴平行,参数相同的理想情况,当相机O1,O2不是在同一直线上怎么办呢?事实上,这种情况非常常见,因为有些场景下两个相机需要独立固定,很难保证光心完全水平,即使固定在同一个基板上也会由于装配的原因导致光心不完全水平,如下图所示:两个相机的极线不平行,并且不共面。

    这种情况下拍摄的两张左右图片,如下图所示。

    左图中三个十字标志的点,右图中对应的极线是右图中的三条白色直线,也就是对应的搜索区域。我们看到这三条直线并不是水平的,如果进行逐点搜索效率非常低。

    图像矫正技术

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


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

    上面的主要工作是在极线上寻找匹配点,但是由于要保证两个相机参数完全一致是不现实的,并且外界光照变化和视角不同的影响,使得单个像素点鲁棒性很差。所以匹配工作是一项很重要的事情,这也关系着双目视觉测距的准确性。

    双目视觉的工作流程

    相机镜头畸变校正原理及方法,之前介绍过,这个基本是通用的,可以用张正友校准法。


    双目测距的优点与难点

    从上面的介绍看出,双目系统优势:(1)成本比单目系统要高,但尚处于可接受范围内,并且与激光雷达等方案相比成本较低;(2)没有识别率的限制,因为从原理上无需先进行识别再进行测算,而是对所有障碍物直接进行测量;(3)直接利用视差计算距离,精度比单目高;(4)无需维护样本数据库,因为对于双目没有样本的概念。

    双目系统的难点:

    (1)计算量非常大,对计算单元的性能要求非常高,这使得双目系统的产品化、小型化的难度较大。所以在芯片或FPGA上解决双目的计算问题难度比较大。国际上使用双目的研究机构或厂商,绝大多数是使用服务器进行图像处理与计算,也有部分将算法进行简化后,使用FPGA进行处理。

    (2)双目的配准效果,直接影响到测距的准确性。

    2.1、对环境光照非常敏感。双目立体视觉法依赖环境中的自然光线采集图像,而由于光照角度变化、光照强度变化等环境因素的影响,拍摄的两张图片亮度差别会比较大,这会对匹配算法提出很大的挑战。

    2.2、不适用于单调缺乏纹理的场景。由于双目立体视觉法根据视觉特征进行图像匹配,所以对于缺乏视觉特征的场景(如天空、白墙、沙漠等)会出现匹配困难,导致匹配误差较大甚至匹配失败。

    2.3、计算复杂度高。该方法需要逐像素匹配;又因为上述多种因素的影响,为保证匹配结果的鲁棒性,需要在算法中增加大量的错误剔除策略,因此对算法要求较高,想要实现可靠商用难度大,计算量较大。
    2.4、相机基线限制了测量范围。测量范围和基线(两个摄像头间距)关系很大:基线越大,测量范围越远;基线越小,测量范围越近。所以基线在一定程度上限制了该深度相机的测量范围。


    ---------------项目开源:-----------

    卡内基梅隆大学双目实验室

    Oxford大牛:Andrew Zisserman,http://www.robots.ox.ac.uk/~vgg/hzbook/code/,主要研究多幅图像的几何学,该网站提供了部分工具,相当实用,还有例子

    Cambridge:http://mi.eng.cam.ac.uk/milab.html,剑桥大学的机器智能实验室,里面有三个小组,Computer Vision & Robotics, Machine Intelligence, Speech

    stanford:http://ai.stanford.edu/~asaxena/reconstruction3d/,主要对于单张照片的三维重建

    caltech:http://www.vision.caltech.edu/bouguetj/calib_doc/,这是我们Computer Vision老师课件上的连接,主要是用于摄像机标定的工具集,当然也有涉及对标定图像三维重建的前期处理过程

    JP Tarel:http://perso.lcpc.fr/tarel.jean-philippe/,个人主页


    ------------匹配与3D重建算法:-----------

    https://www.cnblogs.com/polly333/p/5130375.html

    http://blog.csdn.net/wangyaninglm/article/details/51533549

    http://blog.csdn.net/wangyaninglm/article/details/51531333


    https://www.zhihu.com/question/29885222?sort=created

    http://blog.csdn.net/wangyaninglm/article/details/51558656

    http://blog.csdn.net/wangyaninglm/article/details/51558310

    https://www.cnblogs.com/mysunnyday/archive/2011/05/09/2041115.html
     

     

     

     

    参考:

    https://blog.csdn.net/piaoxuezhong/article/details/79016615

    http://m.blog.csdn.net/article/details?id=52829190

    https://blog.csdn.net/a6333230/article/details/82865439

    https://blog.csdn.net/bit_cs2010/article/details/52829190

    展开全文
  • 双目立体视觉

    2018-07-29 10:27:59
    双目立体视觉,双目立体视觉,计算机双目立体视觉,计算机双目立体视觉
  • 基于FPGA实时处理的双目测距系统(项目)
  • VS2017+OpenCV3.3基于SGBM算法的双目立体视觉、双目测距(双目校正和立体匹配)(文档里包含了测试图片)
  • 3D双目立体重建基于立体视觉测量是目前效果最好的算法
  • 双目摄像头测试

    2019-05-08 19:43:24
    多摄像头切换,实现双目活体识别,更加的专业准确。可以试试哦
  • 双目测距算法

    2018-12-18 11:08:07
    双目测距算法,利用两个摄像头读入视频,进行配准定位,进行测距
  • 双目测距 ZED OpenCV

    2020-03-27 03:40:49
    使用双目相机ZED OpenCV3.1 完成一个双目测距,使用OpenCV3.1中 ximgproc的disparity_filter类,得到效果不错的深度图,并且转换成实际的距离。编译版本为release 使用CMake编译带扩展的OpenCV3.1,需要配置zedsdk。...
  • 双目测距数学原理详解

    万次阅读 多人点赞 2016-12-21 11:24:17
    前言最近用到了双目测距的知识,乍想一下这应该是一个很简单很经典的知识了,理论介绍应该很对并且很详尽才对。可是真正想要明白到能给别人讲懂,还是花了一番功夫。网上的类似的文章很多,但是到多都是一样的知识的...

    这是我的推广信息,以激励自己更好的分享自己的知识和经验!也希望看到的你能够多多支持,谢谢!

    1. 滴滴云AI大师:

    目前滴滴云正在大力推广自己的云计算服务,需要购买的朋友们用我的AI大师码 「2049」在滴滴云上购买 GPU / vGPU / 机器学习产品可享受 9
    折优惠,点击这里前往滴滴云官网

    前言

    最近用到了双目测距的知识,乍想一下这应该是一个很简单很经典的知识了,理论介绍应该很对并且很详尽才对。可是真正想要明白到能给别人讲懂,还是花了一番功夫。网上的类似的文章很多,但是到多都是一样的知识的重复,看过的人不知道看没看懂就转载,没有加自己感想,导致我理解花了一番功夫,所以还是写一篇记录下吧。如果真的献丑了,请大家包含并指正,共同进步。

    双目测距理论

    相机成像中的坐标系

    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

    三维估计

    实际场景中物体的三维坐标可以通过双目立体视觉技术来确定。如下图是双目立体视觉原理图,OL和OR是左右相机的光心,它们的光轴和各自的成像平面如图所示。假设两相机的内部和外部参数完全相同,焦距为f,光心之间的距离(基线)为B,两台相机在同一平面上,机他们的投影中心的Y坐标相等。同一时刻空间点p(x,y,z)在两相机上成像点分别为Pleft和Pright。
    这里写图片描述
    由三角几何关系有:
    这里写图片描述
    这个公式重在搞懂各个变量所在的坐标系。**其中Xleft和Xright分别在左、右相机的图像平面下讨论;也就是说它们的坐标系在各自的图像平面坐标系下的坐标,原点分别为各自光轴与像平面的交点。**而f和B是常值,Y也是在图像坐标系下讨论,但是是相同的。x,y,z则是在左相机坐标系下讨论,原点为OL。有了以上基础,就可以理解公式了。另外视差D的理解也非常重要,D就是Xleft-Xright。还记得Xleft和Xright的含义吗,能理解D吗?D不是图上Pleft与Pright的距离,就是Xleft-Xright,就是Xleft-Xright。 重要的事情说三遍。这应该能理解视差D是什么了,就是在各自的图像中,对应点成像的位置越相似,视差就越小,也就说明物体越远。那么有上式可得
    这里写图片描述

    展开全文
  • 双目色差图

    2018-01-03 07:06:38
    post-filtering-BM.cpp 双目色差图
  • 双目相机标定.rar

    2021-01-30 16:44:37
    双目相机标定
  • 双目立体标定.rar

    2021-01-22 15:41:55
    双目立体标定
  • 双目视觉

    千次阅读 2018-05-15 18:37:25
    双目视觉简介双目视觉广泛应用在机器人导航,精密工业测量、物体识别、虚拟现实、场景重建,勘测领域。什么是双目视觉?双目视觉是模拟人类视觉原理,使用计算机被动感知距离的方法。从两个或者多个点观察一个物体...
  • 已经测试过的Opencv3.2版本双目测距代码,效果还行。得出的结果与实际距离有一点差值,经过线性拟合后效果很不错,误差在几个厘米内。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,234
精华内容 14,093
关键字:

双目