双目相机的标定,视差图,深度图,点云生成思路与实现。

news2025/1/11 22:41:07

该文档记录从双目相机标定到点云生成的所有过程,同时会附上代码。

代码直接能跑。https://github.com/stu-yzZ/stereoCamera

目录

大致思路如下:

一、相机标定

1、相机参数介绍

2、单目相机标定

3、双目相机标定

二、图片畸变矫正

三、极线矫正

1、极线矫正

2、投影矩阵Q

3、图片检查

四、SGBM局部匹配算法计算视差图,并填充

1、设置立体匹配算法SGBM

2、WLS+视差图空洞填充

五、通过视差图计算深度图并可视化

六、通过视差图和Q矩阵计算每个二维坐标对应的三维坐标,同时获取颜色。

七、点云的显示

反思:


大致思路如下:

首先标定双目相机,获取每个相机的内参,同时对相机进行标定,获取相机的相对位置参数,也就是外参。

然后通过相机拍图片并对图片进行矫正(畸变矫正和立体矫正)然后通过立体匹配算法计算视差图,通过视差图计算深度图,有了视差图很多问题都可以解决了,通过视差图可以获得点云信息。

点云数据太大了 我电脑跑不动,但大致可以看到将图片像右旋转45°即和图片视角一致。

一、相机标定

相机标定获取相机参数,用来矫正图。并且要使用相机参数对拍摄的图片进行矫正。相机标定非常重要,所有的计算都是基于相机参数进行的,如果误差较大,后面所有步骤都会受到影响。我自己从淘宝上买了个200左右的双目相机,标定之后结果一直很差,非常影响后续的视差图和深度图的计算。

但是标定的过程一定要自己动手做,只有亲自动手,才能知道一些细节。由于硬件限制(姑且认为是吧),我转换思路,用别人标定好的参数和图片进行视差图和深度图的实现。使用https://vision.middlebury.edu/stereo/data/,这个数据集MiddleburyStereoDatasets进行后续步骤。

1、相机参数介绍

拿单目相机举例,四个坐标系之间需要变换,主要是参数有相机内参和外参。

四个坐标系:世界坐标系->相机坐标系->图像坐标系->像素坐标系

内参:共有11个参数变量。其中,相机的内部参数有5个:焦距,像主点坐标,畸变参数;相机的外部参数有6个:旋转,平移。

2、单目相机标定

需要准备一个标定板,一台相机,尽可能多的拍一下照片,张正友标定法。但是标定出高精度的结果太难了,我还是暂时跳过了这个精度的要求,暂时先把整条思路打通。

3、双目相机标定

双目相机标定除了求解每个摄像头的内参外,还需要求解两个摄像头之间的 相对位置姿态,即 外参(旋转矩阵和平移向量)。其中平移矩阵的(以我选择的artroom1的参数为例)是这样的self.T = np.array([[-536.62], [0.0], [0.0]]),其中第一个元素是对应的你自己相机的基线长度,也就是两个相机镜头的距离,单位是mm。如果是自己标定的话也可以通过这个参数判断自己标定误差大小。

二、图片畸变矫正

使用畸变参数对图片进行矫正

cv.undistort该函数可以实现畸变矫正功能。双目相机的话需要对左右两张图像都进行矫正操作。

void undistort( InputArray src, //输入原图
                             OutputArray dst,//输出矫正后的图像
                             InputArray cameraMatrix,//内参矩阵
                             InputArray distCoeffs,//畸变系数
                             InputArray newCameraMatrix=noArray() );

三、极线矫正

非常重要的一步,将立体匹配从二维降到一维。(对极约束,是将搜索空间约束到像平面内的一条直线上)

1、极线矫正

这篇文章从原理讲的很细,会让人有生畏的感觉https://zhuanlan.zhihu.com/p/466758105。你也可以跳过没懂的地方,首先你要知道匹配的意思就是要从针对左视图中的某一个像素,在右试图中找到对应的匹配像素,如果不做立体矫正的话,需要从右视图的整个图片中搜索,如果做了立体匹配,可以将匹配过程从整张图片(二维空间)降低到一维空间,极线矫正之后空间中点在左右视图中的投影在同一条直线上。

下面代码展示了从读取图片到极线矫正,并检测矫正情况的过程。

# 读取图像
    imgl = cv.imread('1_L.jpg')
    imgr = cv.imread('1_R.jpg')
    high, wide = imgl.shape[0:2]

    # 读取相机参数
    config = stereoCamera()

    # 消除图像畸变
    imgl_qb = cv.undistort(imgl, config.cam_matrix_l, config.distortion_l)
    imgr_qb = cv.undistort(imgr, config.cam_matrix_r, config.distortion_r)

    # 极线校正
    map1x, map1y, map2x, map2y, Q = getRectifyTransform(high, wide, config)
    imgl_jx, imgr_jx = rectifyImage(imgl_qb, imgr_qb, map1x, map1y, map2x, map2y)
    # print("Print Q!")
    # print(Q)

    # 绘制等间距平行线,检查效果
    line = draw_line(imgl_jx, imgr_jx)
    
其中draw_line函数为:
def draw_line(img1, img2):
    height = max(img1.shape[0], img2.shape[0])
    width = img1.shape[1] + img2.shape[1]
    output = np.zeros((height, width, 3), dtype=np.uint8)
    output[0:img1.shape[0], 0:img1.shape[1]] = img1
    output[0:img2.shape[0], img1.shape[1]:] = img2
    line_interval = 50  # 直线间隔
    for k in range(height // line_interval):
        cv.line(output, (0, line_interval * (k + 1)),
                (2 * width, line_interval * (k + 1)),
                (0, 255, 0), thickness=2, lineType=cv.LINE_AA)
    # plt.imshow(output, 'gray')
    # plt.show()
    return output

2、投影矩阵Q

Q矩阵在后面生成3D点云的时候要用到。暂不多解释。

3、图片检查

极线矫正之后的图片进行检查,这个过程很难界定怎么样算是好的,或许肉眼看着在同一个平面但是在像素层面上来看并没有对齐,总的来说这个结果也是和标定结果强相关。我自己标定的结果打印出来感觉没有差很多,但是最后视差图和深度图还是效果很差。下面这是我使用数据集中的参数和图片显示的结果,(数据集中的图片已经是畸变矫正和立体匹配之后的图片,所以他们肯定极线对齐的)

四、SGBM局部匹配算法计算视差图,并填充

通过opencv获取视差图

1、设置立体匹配算法SGBM

SGBM属于局部匹配算法,我调试下来的感觉就是泛化性很低,甚至图片场景变化大的话基本上一张图片对应一套参数,计算量先不谈,我22款拯救者y9000P还能带动,SGBM算法原理我没有太深究,主要的参数调整网上有很多讲解的,可以自己看,比较重要的就是windowssize和最大最小视差,视差值对应图片的深度也就是拍照的距离。窗口大小会影响视差图的平滑与否,可以自己设置调试下。且最大最小视差可以通过拍照的距离计算出来。根据深度计算公式,已知最大最小深度也就是拍照的最近最远距离,可以计算出视差值。我使用的数值来自于数据集中提供的。

同时需要注意视差图的精度问题。

def opencv_SGBM(left_img, right_img, use_wls=False):
    blockSize = 11
    paramL = {"minDisparity": 0,              #表示可能的最小视差值。通常为0,但有时校正算法会移动图像,所以参数值也要相应调整
              "numDisparities": 170,          #表示最大的视差值与最小的视差值之差,这个差值总是大于0。在当前的实现中,这个值必须要能被16整除,越大黑色边缘越多,表示不能计算视差的区域
              "blockSize": blockSize,
              "P1": 8 * 3 * blockSize * blockSize,          #控制视差图平滑度的第一个参数
              "P2": 32 * 3 * blockSize * blockSize,         #控制视差图平滑度的第二个参数,值越大,视差图越平滑。P1是邻近像素间视差值变化为1时的惩罚值,
                                                            #p2是邻近像素间视差值变化大于1时的惩罚值。算法要求P2>P1,stereo_match.cpp样例中给出一些p1和p2的合理取值。
              "disp12MaxDiff": 1,            #表示在左右视图检查中最大允许的偏差(整数像素单位)。设为非正值将不做检查。
              "uniquenessRatio": 10,          #表示由代价函数计算得到的最好(最小)结果值比第二好的值小多少(用百分比表示)才被认为是正确的。通常在5-15之间。
              "speckleWindowSize": 50,       #表示平滑视差区域的最大窗口尺寸,以考虑噪声斑点或无效性。将它设为0就不会进行斑点过滤,否则应取50-200之间的某个值。
              "speckleRange": 1,              #指每个已连接部分的最大视差变化,如果进行斑点过滤,则该参数取正值,函数会自动乘以16、一般情况下取1或2就足够了。
              "preFilterCap": 31,
              "mode": cv.STEREO_SGBM_MODE_SGBM_3WAY
              }
    matcherL = cv.StereoSGBM_create(**paramL)
    # 计算视差图
    dispL = matcherL.compute(left_img, right_img)
    
    # WLS滤波平滑优化图像
    if use_wls:
        paramR = paramL
        paramR['minDisparity'] = -paramL['numDisparities']
        matcherR = cv.StereoSGBM_create(**paramR)
        dispR = matcherR.compute(right_img, left_img)
        # dispR = np.int16(dispR)
        lmbda = 80000
        sigma = 1.0
        filter = cv.ximgproc.createDisparityWLSFilter(matcher_left=matcherL)
        filter.setLambda(lmbda)
        filter.setSigmaColor(sigma)
        dispL = filter.filter(dispL, left_img, None, dispR)

    #双边滤波
    dispL = cv2.bilateralFilter(dispL.astype(np.float32), d=9, sigmaColor=75, sigmaSpace=75)

    # 除以16得到真实视差(因为SGBM算法得到的视差是×16的)
    dispL[dispL < 0] = 1e-6
    dispL = dispL.astype(np.int16)
    dispL = dispL / 16.0

    return dispL

2、WLS+视差图空洞填充

wls叫做基于加权最小二乘法的保边缘平滑滤波器。目的是对图像进行平滑处理,代码在上,主要是对视差图进行平滑处理。

这篇文章讲解了基于积分的空洞填充,也包括整个双目视觉的大致流程,也包括一些细节的内容,比如提到了精度相关的知识,我受益匪浅,https://www.cnblogs.com/riddick/p/8486223.html这篇博文是2018年发表的,不得不感慨……

空洞填充可以使得视差图更加的平滑和高质量。但是需要较大算力支持。

五、通过视差图计算深度图并可视化

通过公式计算每个像素(可计算像素)的深度坐标,同时生成深度图并可视化。

1、计算深度图并可视化

直接根据深度计算公式对视差图进行计算,但是要确定深度的单位,z=(f*b)/d,其中f和d的单位是像素,b(baseline)的单位是米,计算出来的z深度单位也是米。

六、通过视差图和Q矩阵计算每个二维坐标对应的三维坐标,同时获取颜色。

1、生成点云信息并保存

看代码即可。没有什么逻辑。

七、点云的显示

1、点云(6d)显示

数据太大了,可以在线展示点云,但是没有颜色信息,看起来不是很直观。

反思:

1、我在这个过程遇到了困扰我很久的问题,就是视差图生成了(虽然差一些),但是深度图一直显示不出来,用颜色映射出来都是红色的。我解决问题的思路一直在视差图转深度图的转换上了,其实最后才意识到源头在于视差图中计算出有大量的无限接近0的数值存在,导致深度无穷远,所以深度图中显示全红,同时也是看到了Middlebury数据集中给出的有效的视差范围,我才意识到这个问题,所以我想说的是如果遇到同样的问题可以将视差图中的数值分布打印成直方图,看看0附近的值是不是非常多,并且确定你自己图像的邮箱视差范围,在深度生成过程中将小于最小有效值的视差值都赋值为最小有效值。这样可以保证视差图转深度图没有问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2254682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录一下,解决js内存溢出npm ERR! code ELIFECYCLEnpm ERR! errno 134 以及 errno 9009

项目是个老项目&#xff0c;依赖包也比较大&#xff0c;咱就按正常流程走一遍来详细解决这个问题&#xff0c;先看一下node版本&#xff0c;我用的是nvm管理的&#xff0c;详细可以看我的其他文章 友情提醒&#xff1a;如果项目比较老&#xff0c;包又大&#xff0c;又有一些需…

秒懂:使用js验证hash, content hash , chunk hash的区别

一、使用js验证hash, content hash , chunk hash的区别 1、计算一般的 Hash&#xff08;以简单字符串为例&#xff09; 使用crypto-js库来进行哈希计算&#xff0c;需提前引入npm install crypto-js库。 crypto-js&#xff1a; 是一个JavaScript加密算法库&#xff0c;用于实…

基于MATLAB野外观测站生态气象数据处理分析实践应用

1.本课程基于MATLAB语言 2.以实践案例为主&#xff0c;提供所有代码 3.原理与操作结合 4.布置作业&#xff0c;答疑与拓展 示意图&#xff1a; 以野外观测站高频时序生态气象数据为例&#xff0c;基于MATLAB开展上机操作&#xff1a; 1.不同生态气象要素文件的数据读写与批处理…

Unity 画线(UILineRenderer)

实现 以鼠标点击点作为起点创建UILineRenderer 并记录起点。 GameObject go new GameObject(); go.transform.parent transPaint; go.transform.localPosition Vector3.zero; line go.AddComponent<UILineRenderer>(); line.LineWidth widthLine; line.color col…

D86【python 接口自动化学习】- pytest基础用法

day86 pytest配置testpaths 学习日期&#xff1a;20241202 学习目标&#xff1a;pytest基础用法 -- pytest配置testpaths 学习笔记&#xff1a; pytest配置项 主目录创建pytest.ini文件 [pytest] testpaths./testRule 然后Terminal里直接命令&#xff1a;pytest&#xff…

bash命令缓存导致命令执行失败的问题

1、问题背景 为了修复老版本 vsftpd 的安全漏洞&#xff0c;需要把生产环境上 vsftpd 版本升级到 vsftpd-3.0.5&#xff0c;因为直接使用 rpm 包的方式进行升级还涉及到下层依赖包的升级(生产环境上的依赖包版本不能随意变更&#xff0c;可能会影响其他上层应用)&#xff0c;所…

【设计模式系列】工厂方法模式(二十一)

一、什么是工厂方法模式 工厂方法模式&#xff08;Factory Method Pattern&#xff09;是一种创建型设计模式&#xff0c;其核心目的是定义一个创建对象的接口&#xff0c;但让实现这个接口的子类来决定实例化哪一个类。工厂方法模式让类的实例化推迟到子类中进行&#xff0c;…

Windows 和 Ubuntu 双系统安装

复现论文的时候&#xff0c;个别包只有Linux版本&#xff0c;并且源码编译比较麻烦&#xff0c;所以干脆直接安装一个双系统&#xff08;WinUbuntu&#xff09;&#xff0c;方便复现论文。 参考视频链接&#xff1a;Windows 和 Ubuntu 双系统的安装和卸载 0.所需工具 4G以上U…

DAY35|动态规划Part03|LeetCode:01背包问题 二维、01背包问题 一维、416. 分割等和子集

目录 01背包理论基础&#xff08;一&#xff09; 基本思路 C代码 01背包理论基础&#xff08;二&#xff09; 基本思路 C代码 LeetCode:416. 分割等和子集 基本思路 C代码 01背包理论基础&#xff08;一&#xff09; 题目链接&#xff1a;卡码网46. 携带研究材料 文字…

【SpringMVC】SpringMVC执行流程

当 Spring MVC 收到客户端的 HTTP 请求后&#xff0c;会按照以下步骤处理请求&#xff1a; 前端控制器 DispatcherServlet 接收请求&#xff1a; 客户端发送的 HTTP 请求首先被前端控制器 DispatcherServlet 拦截。DispatcherServlet 是整个流程的入口点&#xff0c;负责接收所…

flex: 1 display:flex 导致的宽度失效问题

flex: 1 & display:flex 导致的宽度失效问题 问题复现 有这样的一个业务场景&#xff0c;详情项每行三项分别占33%宽度&#xff0c;每项有label字数不固定所以宽度不固定&#xff0c;还有content 占满标签剩余宽度&#xff0c;文字过多显示省略号&#xff0c; 鼠标划入展示…

人工智能大模型培训讲师叶梓:Llama Factory 微调模型实战分享提纲

LLaMA-Factory ——一个高效、易用的大模型训练与微调平台。它支持多种预训练模型&#xff0c;并且提供了丰富的训练算法&#xff0c;包括增量预训练、多模态指令监督微调、奖励模型训练等。 LLaMA-Factory的优势在于其简单易用的界面和强大的功能。用户可以在不编写任何代码的…

基于51单片机的智能公交车报站系统GPS定位语音播报智能安全检测人数统计

功能描述 1.LCD12864可显示当前年月日&#xff0c;星期&#xff0c;时间&#xff0c; 当前站名&#xff0c;经纬度&#xff0c;是否连接GPS&#xff0c;自动/手动模式&#xff0c; 2.自带GPS定位&#xff0c;可实时显示经纬度&#xff1b; 3.通过DS1302时钟芯片&#xff0c;获…

leetcode:1995. 统计特殊四元组(python3解法)

难度&#xff1a;简单 给你一个 下标从 0 开始 的整数数组 nums &#xff0c;返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 &#xff1a; nums[a] nums[b] nums[c] nums[d] &#xff0c;且a < b < c < d 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3…

如何把阿里云ECS里的文件下载到本地(免登录免配置)

如何把阿里云ECS里的文件下载到本地&#xff08;免登录免配置&#xff09; 作为一个阿里云ECS的用户&#xff0c;Up时长会遇到希望把ECS里的文件下载到自己的个人电脑&#xff0c;然后在自己的电脑里面查看&#xff0c;保存或者发送给别人。最近发现阿里云新上了一个功能&…

nlp培训重点

1. SGD梯度下降公式 当梯度大于0时&#xff0c;变小&#xff0c;往左边找梯度接近0的值。 当梯度小于0时&#xff0c;减去一个负数会变大&#xff0c;往右边找梯度接近0的值&#xff0c;此时梯度从负数到0上升 2.Adam优化器实现原理 #coding:utf8import torch import torch.n…

mvn test 失败,单独运行单元测试成功

标题mvn test 失败&#xff0c;单独运行单元测试成功 使用junit4进行单元测试时是通过的&#xff0c;但是在执行maven的test与package时测试不通过 报错信息&#xff1a; parse data from Nacos error,dataId:guoyu-new-asset-dev.yml,data: ....... 配置文件内容 ....... o…

电脑插入耳机和音响,只显示一个播放设备

1. 控制面板-硬件和声音-Realtek高清音频-扬声器-设备高级设置-播放设备里选择使用前部和后部输出设备同时播放两种不同的音频流 在声音设置中就可以看到耳机播放选项

【AI系统】MobileNet 系列

MobileNet 系列 在本文会介绍 MobileNet 系列&#xff0c;重点在于其模型结构的轻量化设计&#xff0c;主要介绍详细的轻量化设计原则&#xff0c;基于这原则&#xff0c;MobileNetV1 是如何设计成一个小型&#xff0c;低延迟&#xff0c;低功耗的参数化模型&#xff0c;可以满…

【ARM版银河麒麟安装windows应用程序】

文章目录 前言一、简介2.1 Wine/Wine642.2 Box86/Box64二、配置运行环境2.1 安装aarch64运行库2.2 安装armhf运行库2.2.1 添加32位armhf架构支持2.2.2 检查运行库依赖2.2.3 解决依赖冲突2.2.4安装运行库 三、安装Box64四、安装Wine五、初始化wine配置总结 前言 银河麒麟是基于…