双目立体视觉 I:标定和校正

2023-05-16

点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”


作者:Ali Yasin Eser

编译:ronghuaiyang

导读

双目立体校正和标定。

大家好!今天我们将讨论什么是立体摄像机,以及我们如何将它用于计算机视觉。通过使用这里的代码:https://github.com/aliyasineser/stereoDepth,我将解释我们如何为立体摄像机校准摄像机并计算视差图。我不会深入数学细节,你可以阅读一些OpenCV文档。

我们能感知深度的原因是我们美丽对齐的眼睛。如果你注意到,当我们用一只眼睛看近处的物体时,我们会看到两种角度的不同。但当你看远处的东西时,比如几公里外的山或建筑,你看不到区别。这些差异在我们的大脑中自动处理,我们可以感知深度!眼睛一个在左一个在右的动物无法感知深度,因为它们没有共同的视角,相反,它们有广角视角。有些动物,比如鸭子,通过摇头或快速奔跑来感知深度。我们不会讨论这个概念,现在,让我们关注像我们的眼睛这样的系统。

简化的立体视觉。你可以看到物体P是如何被两个摄像机观察到的。物体的位置在两个图像中是不同的。

如果两台相机垂直对齐,观察到的物体将在同一垂直坐标上(图像中的同一列),所以我们只能关注x坐标来计算深度,因为近的物体在x轴上会有较大的差值。但为了实现这一目标,我们需要对相机进行校准,以修正镜头畸变。校准后,我们需要对系统进行校正。校正基本上就是两台相机之间的校准。如果我们校准和矫正我们的立体摄像机,两个物体将在相同的y轴上,并且观测点P(x,y)可以在图像的同一行中找到,P1(x1,y)是第一个摄像机,P2(x2,y)是第二个摄像机。从这里开始,像素和深度计算之间的唯一区别就在于此。

左上角图像和右下角图像分别是左/右相机图像,左下角是它们的组合来显示差异,右下角是深度图。

首先,将立体摄像机安装在一个固体物体上(尺子、木头或硬塑料材料等),使校准和校正参数能够正常工作。例如,如果你有Intel Realsense或zed相机,你可以跳过所有的部分,因为Realsense有自动校准功能,zed已经按出厂默认校准。下一步是分别校准两个相机。你可以按照这里:https://medium.com/@aliyasineser/opencv-camera-calibration-e9a48bdd1844进行校正。立体摄像机首先需要单相机校准,因为校正需要这些参数。使用棋盘格图像进行校准,使用至少20张图像以进行良好的计算。

左图和右图的例子。注意拍照的时候要同步,在校正中,即使小的差别也会影响结果。

这些图像会给我们提供摄像机所需的信息。为了更好的校准,你不能移动并获取同步的图像。你可以使用grab和retrieve函数来获得更接近的时间戳。对于拍摄图像,你可以使用这个代码:https://github.com/aliyasineser/stereoDepth/blob/master/getStereoImages.py,对于校准,你可以使用这个代码:https://github.com/aliyasineser/stereoDepth/blob/master/stereo_camera_calibration.py。关于代码:

  1. 立体标定:立体摄像机的摄像机标定。

  2. 立体校正:通过旋转和平移使它们在y轴上对齐,使这些摄像机观察到的每个点都在每个摄像机图像的同一列中。

OpenCV立体标定函数:

ret, K1, D1, K2, D2, R, T, E, F = cv2.stereoCalibrate(objp, leftp, rightp, K1, D1, K2, D2, image_size, criteria, flag)

我们要注意函数中的flags:

  • CV_CALIB_FIX_INTRINSIC: K和D个矩阵是固定的。这是默认标志。如果你校准好你的相机,你可以修正它们,所以你只会得到校正矩阵。

  • CV_CALIB_USE_INTRINSIC_GUESS: K和D个矩阵将被优化。对于这个计算,你应该给出经过良好校准的矩阵,以便(可能)得到更好的结果。

  • CV_CALIB_FIX_PRINCIPAL_POINT: 修复K矩阵中的参考点。

  • CV_CALIB_FIX_FOCAL_LENGTH: 在K矩阵中固定焦距。

  • CV_CALIB_FIX_ASPECT_RATIO: 固定长宽比。

  • CV_CALIB_SAME_FOCAL_LENGTH: 校准焦距,并设置Fx和Fy相同的校准结果。我对这个不熟悉,但我肯定它是特定的立体设置所需要的。

  • CV_CALIB_ZERO_TANGENT_DIST: 去掉畸变。

  • CV_CALIB_FIX_K1, …, CV_CALIB_FIX_K6: 移除K1到K6的畸变。这对实验非常重要。我不熟悉这些背后的数学,但我做的实验帮助了我很多。

结果中的R, T, E, F系数给出了两个相机之间的关系,我们将使用它们进行校正:

R1, R2, P1, P2, Q, roi_left, roi_right = cv2.stereoRectify(K1, D1, K2, D2, image_size, R, T, flags=cv2.CALIB_ZERO_DISPARITY, alpha=0.9)

在这个函数中,我们只有一个标志CALIB_ZERO_DISPARITY,它用于匹配图像之间的y轴。alpha值用于转换后的黑色部分,因为图像会旋转,我们的大小不会改变,所以一些图像会是黑色的,我们的原始图像会小得多。我设置了相机的值。选项是:

  • alpha=-1 -> 让 OpenCV 优化黑色部分。

  • alpha= 0 -> 旋转和切割图像,使没有黑色的部分。这个选项在大多数情况下会严重削减图像,你不会得到一个像样的高质量的图像,但值得一试。

  • alpha= 1 -> 进行变换,但不要切任何东西。

  • alpha=experimental-> 有时候什么都不管用。这意味着你应该试验这些值。在某个特定的alpha值,你可以有一些黑色的部分,但高质量的图像。我为我的相机找到了最好的0.9756,所以不要失去希望:)、S

R矩阵和P矩阵称为旋转矩阵和投影矩阵。R1和P1给出从第一个(左)到第二个(右)摄像机的旋转和位置。R2和P2是从二到一。从视差图中得到深度图需要Q矩阵。包括相机之间的距离,焦距等,用于深度地图的处理。当计算深度值时,不要忘记将在与校准工作相同的单位中获得坐标。有很多参数,所以最好仔细检查。

对于结果,应该去看我们的测试代码:https://github.com/aliyasineser/stereoDepth/blob/master/stereo_depth.py。如果我们阅读主要部分,我们会看到我们使用了畸变校正:

leftMapX, leftMapY = cv2.initUndistortRectifyMap(K1, D1, R1, P1, (width, height), cv2.CV_32FC1)

left_rectified = cv2.remap(leftFrame, leftMapX, leftMapY, cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)

rightMapX, rightMapY = cv2.initUndistortRectifyMap(K2, D2, R2, P2, (width, height), cv2.CV_32FC1)

right_rectified = cv2.remap(rightFrame, rightMapX, rightMapY, cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)

这意味着initUndistortRectifyMap函数可以同时实现图像的畸变校正和校准。对于左相机,我们使用K1(相机矩阵)和D1(失真矩阵)进行畸变校正,使用R1(从左到右旋转)和P1(从左到右投影矩阵)进行校正。在对remap进行变换后,我们将得到修正后的图像。对于右相机,我们会用相同的步骤做一遍,然后第一部分就完成了!总结一下过程:

这就是立体校准和标定。我们将在下一篇博客中讨论视差图的计算!

一些资源:

  • http://zone.ni.com/reference/en-XX/help/372916P-01/nivisionconceptsdita/guid-c9c3535b-faf7-4ade-9166-513a49d1b90a/

  • https://www.google.at/search?q=depth+map&source=lnms&tbm=isch&sa=X&ved=0ahUKEwjlqIq1xsjeAhUkglwKHahhDVIQ_AUIDigB&biw=1536&bih=732#imgrc=o7eiUTU5f3tKpM:

  • https://www.mathworks.com/discovery/stereo-vision.html

  • https://docs.opencv.org/3.0-beta/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html

—END—

英文原文:https://py.plainenglish.io/the-depth-i-stereo-calibration-and-rectification-24da7b0fb1e0

请长按或扫描二维码关注本公众号

喜欢的话,请给我个在看吧

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

双目立体视觉 I:标定和校正 的相关文章

  • Parrot Bebop2 与ROS

    第二章 无人机平台与开发环境搭建 本章主要介绍无人机平台及相关开发环境的搭建 包括介绍Parrot Bebop2的相关规格与使用说明 xff0c 以及ROS的操作系统的简介 发展历程 安装流程 xff0c 还有ROS的数据通信方式和ROS的
  • python2与python3解析数据

    蓝牙模块接收到监测设备传输来的数据 xff0c 封装格式为十六进制的数据帧 xff0c 蓝牙模块将数据通过串口发送给wrtnode 2p xff0c wrtnode通过ser2net服务将数据转为网络数据 xff0c 可以通过监听192 1
  • 上传本地项目到github远程仓库

    前提已经注册github账号并在本地电脑安装git客户端 1 为Github账户设置SSH key 进入git bash xff0c 通过如下命令生成 ssh keygen t rsa C 34 github所绑定的邮箱 34 一路回车 x
  • 卫星导航定位技术二:由星历参数求解卫星时空位置

    卫星星历是描述卫星运动轨道的信息 也可以说卫星星历就是一组对应某一时刻的轨道参数及其变率 有了卫星星历就可以计算出任意时刻的卫星位置及其速度 GPS卫星星历分为预报星历和后处理星历 预报星历又称广播星历 GPS广播星历参数共有16个 xff
  • 模式识别:最小错误率贝叶斯决策分类

    一 引言 1 用贝叶斯决策理论分类要事先知道两个条件及要求 xff1a 各类的先验概率 xff1a 及特征向量的条件概率密度 xff1a 或后验概率 xff1a 决策分类的类别一定 2 解决的问题 xff1a 已知一定数目的样本 xff0c
  • 模式识别:BP神经网络算法

    1 BP神经网络分类器 1 1 BP算法基本原理 神经网络结构大概如下图1 1 xff1a 图1 1 包括输入层 xff0c 隐层和输出层 包含一层隐层的神经网络称为浅层神经网络 xff0c 即SNN 包含多层隐层的神经网络称为深度神经网络
  • 模式识别:C-means(K-means)聚类算法与分级聚类(层次聚类)算法

    C均值聚类算法与分级聚类算法的聚类分析 一 实验目的 理解聚类的整体思想 xff0c 了解聚类的一般方法 xff1b 掌握 C means与分级聚类算法算法思想及原理 xff0c 并能够熟练运用这些算法进行聚类分析 xff1b 能够分析二者
  • ROS 配置多网口通讯

    列出当前所有的网络设备 ifconfig a 结果如下 xff1a enp1s0 Link encap Ethernet HWaddr 00 2f 5c 68 06 ad inet addr 192 168 1 101 Bcast 192
  • qt creator开启openMP加速方法

    环境 Qt creator4 11 for msvc2017 内置openmp库 启用方法 1 在pro文件加上QMAKE CXXFLAGS 43 61 openmp 2 添加头文件omp h
  • c++中::的用法

    是运算符中等级最高的 xff0c 它分为三种 1 global scope 全局作用域符 xff09 xff0c 用法 xff08 name 2 class scope 类作用域符 xff09 xff0c 用法 class name 3 n
  • 【ubuntu】——gflags&glog卸载与安装

    gflags glog 通过apt安装的glog xff0c gflags没有config cmake xff0c 所以在一些情况下需要手动编译 1 卸载gflags amp glog 只适用于通过apt安装的方式 span class t
  • 【算法】A* 寻路 可视化

    如下图 寻路图A 使用A 算法 xff0c 需要将地图抽象成一个个方块 xff0c 蓝色代表不可以动 墙 xff0c 黄色为起始点 xff0c 红色为目标点 其地图的二维坐标如图所示 xff0c 每一个单位为1米 A 的基本公式为 F n
  • 实验室新生成长指南[2.2.1] · 连接器

    欢迎进入 实验室新生成长指南 第二章 xff1a 硬件 本篇是 实验室新生成长指南 第二章第二节第一篇 xff1a 连接器 整个2 2节将帮助新手快速建立设计电路系统的一些基本知识储备 更多关于 实验室新生成长指南 的介绍 xff0c 请前
  • 走进音视频的世界——音视频的基本概念

    音视频通用的基本概念有码率 时长 xff0c 而不同音视频有不同的封装格式 编码协议 其中视频关键参数有分辨率 帧率 画质 旋转角度 像素格式 xff0c 而音频关键参数有采样率 声道数 声道布局 音质 采样数 采样位数 帧时长 接下来与大
  • 走进音视频的世界——新一代开源编解码器AV1

    AOMedia Video 1 xff08 AV1 xff09 是一种开源 免版税的编解码器 xff0c 最初设计用于Internet上的视频传输 它是由开放媒体联盟 xff08 AOMedia xff09 于VP9的继任者开发的 xff0
  • FFmpeg源码分析:avformat_find_stream_info分析码流信息

    FFmpeg在调用avformat open input 之后 xff0c 可能码流信息不够完整 xff0c 可以使用avformat find stream info 获取更多的码流信息 比如获取视频帧率 视频宽高 xff0c 重新计算最
  • Miracast投屏协议深入剖析

    Miracast由WiFi联盟制定 xff0c 以WiFi Direct IEEE802 11为无线传输标准 xff0c 允许手机向电视或其他接收设备进行无线投送视频 图片 和Miracast类似的投屏协议 xff0c 还有Airplay
  • c++:DFS与BFS详解

    DFS xff08 深度优先搜索 xff09 xff1a 从某个状态开始 xff0c 不断转移状态到无法转移为止 xff0c 然后退回到前一步 xff0c 继续转移到其他状态 xff0c 不断重复 xff0c 直至找到最终的解 总是从最开始
  • 一文掌握OpenGL的shader内置函数

    OpenGL ES有大量的GLSL内置函数 xff0c 包括 xff1a 三角函数 指数函数 通用函数 浮点函数 几何函数 矩阵函数 矢量关系函数 纹理函数 原子函数 图像函数 插值函数等 目录 一 三角函数 1 radians degre
  • 安全可靠的SRT实时传输协议

    Secure Reliable Transport SRT 是安全 可靠 低延时的多媒体实时传输协议 SRT协议使用AES进行数据加密 xff0c 运用FEC进行前向纠错 xff0c 并且有流量控制 拥塞控制 类似于QUIC协议 xff0c

随机推荐

  • android端使用openCV实现车牌检测

    现在 xff0c 汽车的踪影无处不在 xff0c 公路上疾驰 xff0c 大街边临停 xff0c 小区中停靠 xff0c 车库里停泊 管理监控如此庞大数量的汽车是个头疼的问题 精明的人们把目光放在车牌上 xff0c 因为车牌是汽车的 身份证
  • android端使用openCV与深度学习实现车牌识别

    车牌识别的应用场景随处可见 xff1a 高速公路上超速抓拍 小区门口关卡 车库入口关卡 xff0c 甚至出现在车载设备上 它的工作原理大致这样 xff1a 使用摄像头充当 眼睛 xff0c 使用openCV与深度学习充当 大脑 实时车牌识别
  • FFmpeg音频处理——音频混合、拼接、剪切、转码

    接触FFmpeg有一段时间了 xff0c 它是音视频开发的开源库 xff0c 几乎其他所有播放器 直播平台都基于FFmpeg进行二次开发 本篇文章来总结下采用FFmpeg进行音频处理 xff1a 音频混合 音频剪切 音频拼接与音频转码 采用
  • Android三种方式截取任意界面屏幕

    一 使用MediaProjectionManager Android5 0之后 xff0c 开放截取屏幕的API xff0c 也就是利用MediaProjectionManager创建VirtualDisplay xff0c 传入与Imag
  • ijkplayer基于rtsp直播延时的深度优化

    现在ijkPlayer是许多播放器 直播平台的首选 xff0c 相信很多开发者都接触过ijkPlayer xff0c 无论是Android工程师还是iOS工程师 我曾经在Github上的ijkPlayer开源项目上提问过 xff1a 视频流
  • C++ 程序编译过程

    前言 C语言的编译链接过程要把我们编写的一个c程序 xff08 源代码 xff09 转换成可以在硬件上运行的程序 xff08 可执行代码 xff09 xff0c 需要进行编译和链接 编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程
  • 自旋锁实现机理 spin_lock

    自旋锁的概念 自旋锁 xff08 spin lock xff09 是一种典型的对临界资源进行互斥访问的手段 xff0c 它是基于系统原子操作为基础 xff0c 自旋锁最多只能被一个可执行线程持有 xff0c 如果一个执行线程试图获得一个被已
  • 数据结构:图之DFS与BFS的复杂度分析

    BFS的复杂度分析 BFS是一种借用队列来存储的过程 xff0c 分层查找 xff0c 优先考虑距离出发点近的点 无论是在邻接表还是邻接矩阵中存储 xff0c 都需要借助一个辅助队列 xff0c v个顶点均需入队 xff0c 最坏的情况下
  • python中的os.path.dirname(__file__)的使用

    os path dirname file 返回脚本的路径 xff0c 但是需要注意一下几点 1 必须是实际存在的 py文件 xff0c 如果在命令行执行 xff0c 则会引发异常NameError name 39 file 39 is no
  • 北斗模块学习之初

    BD定义 xff1a 北斗卫星导航系统 xff08 BeiDou xff08 COMPASS xff09 NavigationSatellite System xff09 是中国正在实施的自主发展 独立运行的全球卫星导航系统 系统建设目标是
  • 关于Boost库和STL标准模板库

    一 关于STL 首先 xff0c 关于stl xff0c 最近也看了不少关于stl的博客 往大概的讲 xff0c stl即一种标准的模板库 xff0c 同时 xff0c 它也是静态库 xff0c 它存在的目的即是为了实现代码的服用性 xff
  • c语言char* 转char[]方法

    vector lt char gt splite by delim char host name char a 1000 strcpy a host name char p 61 strtok a split vector lt char
  • STL常用容器对比

    STL的常用容器大致有以下8个 xff1a 1 vector vector是一种动态数组 xff0c 在内存中具有连续的存储空间 xff0c 支持快速随机访问 由于具有连续的存储空间 xff0c 所以在插入和删除操作方面 xff0c 效率比
  • C 语言 stm32 无符号8位转换为int类型 uchar 转换为 int

    我做的是一个无线鼠标 xff0c stm32用nRF24L01无线传输模块发送数据时 xff0c 是传输无符号8位 uchar 三个数据是角度值 xff0c 有正负 直接使用无符号数据的话负数会出错 xff0c 在接收端要把数据恢复为整数
  • 常见IMU的性能比较

    型号gyr零偏稳定性gyr量程acc零偏稳定性acc量程HZ价格其他说明 EG320N xff08 epson xff09 http www canalgeomatics com wp content uploads 2020 06 oem
  • strcat函数用法的一点看法

    最近在刷题时碰到了strcat这个函数 xff0c 一开始没怎么理解它的用法 xff0c 出了错 xff0c 现在写点自己的理解吧 首先来看两个小程序 代码一 xff1a include lt iostream gt using names
  • CMake教程——QT项目使用CMake

    文章目录 1 Basic Cmake Based Project2 Executable VS Library3 Every module has its own CMakeList txt in its folder3 1 不推荐的做法
  • Proteus ISIS仿真软件中英文元件名称对照

    定时 计数器的使用方法 xff1a CLK xff1a 计数和测频状态时 xff0c 数字波的输入端 xff08 counter enable CE xff1a 计数使能端 xff1b 通过属性设置高还是低有效 无效暂停计数 RST 复位端
  • STL详解

    STL简介 编程的抽象发展 xff1a 面向过程 to 基于对象 to
  • 双目立体视觉 I:标定和校正

    点击上方 AI公园 xff0c 关注公众号 xff0c 选择加 星标 或 置顶 作者 xff1a Ali Yasin Eser 编译 xff1a ronghuaiyang 导读 双目立体校正和标定 大家好 xff01 今天我们将讨论什么是立