3D视觉(五):对极几何和三角测量

2023-05-16

3D视觉(五):对极几何和三角测量

对极几何(Epipolar Geometry)描述的是两幅视图之间的内在射影关系,与外部场景无关,只依赖于摄像机内参数和这两幅试图之间的的相对姿态。

文章目录

  • 3D视觉(五):对极几何和三角测量
  • 一、对极几何
  • 二、三角测量
  • 三、实验过程
  • 四、源码
  • 五、项目链接

一、对极几何

假设我们从两张图像中得到了一对配对好的点对,如果有若干对这样的匹配点对,就可以通过这些二维图像点的对应关系,恢复出在两帧之间的摄像机的运动。

在这里插入图片描述
从代数角度来分析这里的几何关系。在第1帧的坐标系下,设P的空间位置为:
P = [X, Y, Z]

根据针孔相机模型,可以得到两个像素点p1、p2的像素位置坐标。这里K为相机内参矩阵,R、t为两个坐标系之间的相机运动。
s1p1 = KP
s2p2 = K(RP + t)

s1p1和p1成投影关系,它们在齐次坐标的意义下是相等的,我们称这种相等关系为尺度意义下相等(equal up to a scale),记作:s1p1~p1。于是上述两个投影关系可写为:
p1 ~ KP
p2 ~ K(RP + t)

将像素坐标系下的坐标转化成归一化平面坐标系下的坐标。方程左右两边同时左乘inv(K),并记x1 = inv(K) p1、x2 = inv(K) p2,这里x1、x2是两个像素点的归一化平面上的坐标,代入上式得:
x2 ~ R x1 + t

************************************************************************************************************** 补充:记向量a = [a1, a2, a3].T、b = [b1, b2, b3].T,则外积a x b为: a x b = [a2b3- a3b2, a3b1 - a1b3, a1b2 - a2b1] = [0, -a3, a2; a3, 0, -a1; -a2, a1, 0] b = a^ b。 我们引入^符号,把向量a转化成一个反对称矩阵a ^,这样就把外积a x b的计算过程,转化成了矩阵与向量的乘法a ^ b,把它变成了线性运算。

方程两边同时左乘t ^,相当于两侧同时与t做外积,可以消去等式右边的第二项:
t ^ x2 ~ t ^ R x1

然后两侧再同时左乘x2.T,由于t ^ x2是一个与t、x2都垂直的向量,它再与x2做内积时将得到0。化零之后尺度意义下的相等被转化为了严格等号,便能得到一个简单的恒等式:
x2.T t ^ R x1 = 0

重新代入p1、p2,有:
p2.T inv(K).T t ^ R inv(K) p1 = 0

这两个式子都称为对极约束,它以形式简洁著名。它的几何意义是O1、P、O2三点共面,对极几何约束中同时包含了平移和旋转。我们把中间部分记作两个矩阵:基础矩阵F(Fundamental Matrix)、本质矩阵E(Essential Matrix),于是可以进一步化简对极约束:

E = t ^ R
F = inv(K).T E inv(K)
x2.T E x1 = 0
p2.T F p1 = 0

对极约束简洁地给出了两个匹配点的空间位置关系,于是相机位姿估计问题转变为如下两步:
第1步:根据配对点的像素位置,解线性方程组,求出E或者F。
第2步:基于旋转矩阵特有的性质,基于矩阵分析理论,从E或者F中分解出R和t。

二、三角测量

三角测量是指,通过不同位置对同一个路标点进行观察,从观察到的位置推断路标点的距离。三角测量最早由高斯提出并应用于测量学中,它在天文学、地理学的测量中都有应用。例如:在天文学中,我们可以通过不同季节观测到的星星角度,来估计它与我们的距离。在计算机视觉中,我们可以通过观测不同图片中标志点的位置,利用三角化来估计标志点的距离。

按照对极几何中的定义,设x1、x2为两个特征点的归一化坐标,它们满足:
s2 x2 = s1 R x1 + t

利用对极几何,我们已经求解得到了R和t,现在想进一步得到两个特征点的深度信息s1、s2。我们对上式两侧左乘一个x2 ^,得:
s2 x2 ^ x2 = 0 = s1 x2 ^ R x1 + x2 ^ t

该式左侧为零,右侧可看成s2的一个方程,根据它可以直接求得s2。一旦求解得到s2,s1也可非常容易求出,于时我们就得到了两帧下的点的深度,即确定了它们的3D空间坐标。

三、实验过程

利用双目摄像头拍摄得到左右目图片:

在这里插入图片描述
在这里插入图片描述
提取ORB特征,进行特征点配对:

在这里插入图片描述
利用对极几何,求得两幅图像位姿变换关系为:

在这里插入图片描述
利用三角测量,以第一幅图像的相机视角为3D坐标系,还原得到每个landmark标志点的3D坐标:

在这里插入图片描述
我们在图片对应的标志点位置,绘图显示深度信息,绿色代表距离相机越近,红色代表距离相机越远,中间以渐变颜色显示,可视化效果如下:

在这里插入图片描述
在这里插入图片描述
根据实验结果,三角测量的确能还原出landmark标志点距离相机的深度,但计算结果较为粗糙,只能得到大致的远近度量,难以用米来精确刻画,距离误差很大。另外对极几何算法鲁棒性不强,一旦特征点配对错误,还原出的位姿变换矩阵误差很大,从而导致三角测量还原出的深度信息误差很大,根本无法使用。

四、源码

对极几何:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>


using namespace std;
using namespace cv;


// 利用匹配好的特征点对、landmark位置,基于对极几何方法,估计旋转矩阵R、平移向量t

void pose_estimation_2d2d(std::vector<KeyPoint> keypoints_1,
                          std::vector<KeyPoint> keypoints_2,
                          std::vector<DMatch> matches,
                          const Mat &K, Mat &R, Mat &t) {

  // 把匹配点对转换为vector<Point2f>的形式
  vector<Point2f> points1;
  vector<Point2f> points2;

  // 将对应landmark标志点的像素坐标,分别push到points1、points2容器中
  
  cout << "匹配点对的像素位置: " << endl;
  for (int i = 0; i < (int) matches.size(); i++) {
      
    points1.push_back(keypoints_1[matches[i].queryIdx].pt);
    points2.push_back(keypoints_2[matches[i].trainIdx].pt);
    
    cout << keypoints_1[matches[i].queryIdx].pt << "  " << keypoints_2[matches[i].trainIdx].pt << endl;
  }

  // 计算基础矩阵 F
  Mat fundamental_matrix;
  fundamental_matrix = findFundamentalMat(points1, points2);
  cout << endl << "基础矩阵 F: " << endl << fundamental_matrix << endl << endl;

  // 计算本质矩阵 E
  Point2d principal_point(K.at<double>(0, 2), K.at<double>(1, 2));  //相机光心
  double focal_length = K.at<double>(0, 0);      //相机焦距
  
  Mat essential_matrix;
  essential_matrix = findEssentialMat(points1, points2, focal_length, principal_point);
  cout << "本质矩阵 E: " << endl << essential_matrix << endl << endl;

  // 计算单应矩阵 H,但在本例中场景中,特征点并不都落在同一个平面上,单应矩阵意义不大
  Mat homography_matrix;
  homography_matrix = findHomography(points1, points2, RANSAC, 3);
  cout << "单应矩阵 H: " << endl << homography_matrix << endl << endl;

  // 从本质矩阵E中恢复旋转和平移信息,此函数仅在Opencv3中提供
  recoverPose(essential_matrix, points1, points2, R, t, focal_length, principal_point);
  cout << "恢复旋转矩阵 R: " << endl << R << endl << endl;
  cout << "恢复平移向量 t: " << endl << t << endl << endl;

}

三角测量:

#include <iostream>
#include <opencv2/opencv.hpp>


using namespace std;
using namespace cv;


// 保留指定区间的深度信息,此范围内深度估计值较为精确,深度估计值小于low_th的会被赋值成low_th,深度估计值大于up_th的会被赋值成up_th
// 借助rgb颜色可视化像素点位置的深度,绿色表示距离近,红色表示距离远

cv::Scalar get_color(float depth) {
    
  float up_th = 21, low_th = 16, th_range = up_th - low_th;
  
  if (depth > up_th) depth = up_th;
  if (depth < low_th) depth = low_th;
  
  // 如果距离较近,会接近绿色;如果距离较远,会接近红色
  return cv::Scalar(0, 255 * (1 - (depth - low_th) / th_range), 255 * (depth - low_th) / th_range);
  
}


// 将像素坐标系下的坐标,转换到归一化坐标系下的坐标,(u, v) - (x, y, 1)
// x = (u - cx) / fx
// y = (v - cy) / fy

Point2f pixel2cam(const Point2d &p, const Mat &K) {
  return Point2f
    (
      (p.x - K.at<double>(0, 2)) / K.at<double>(0, 0),
      (p.y - K.at<double>(1, 2)) / K.at<double>(1, 1)
    );
}


// 三角测量,计算对极几何尺度s1、s2,还原每个landmark标志点的深度信息,landmark 3d坐标信息存储于points容器中

void triangulation(const vector<KeyPoint> &keypoint_1, 
                   const vector<KeyPoint> &keypoint_2,
                   const std::vector<DMatch> &matches,
                   const Mat &K, const Mat &R, const Mat &t,
                   vector<Point3d> &points) {
  
  // 默认以第一幅图像的相机坐标系为基准,还原出来的深度信息,就是相对于第一幅图像视角下的深度
    
  Mat T1 = (Mat_<float>(3, 4) <<
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0);
  Mat T2 = (Mat_<float>(3, 4) <<
    R.at<double>(0, 0), R.at<double>(0, 1), R.at<double>(0, 2), t.at<double>(0, 0),
    R.at<double>(1, 0), R.at<double>(1, 1), R.at<double>(1, 2), t.at<double>(1, 0),
    R.at<double>(2, 0), R.at<double>(2, 1), R.at<double>(2, 2), t.at<double>(2, 0));

  vector<Point2f> pts_1, pts_2;
  
  // 将像素坐标转换成归一化平面坐标,存储于pts_1、pts_2
  for (DMatch m:matches) {
    pts_1.push_back(pixel2cam(keypoint_1[m.queryIdx].pt, K));
    pts_2.push_back(pixel2cam(keypoint_2[m.trainIdx].pt, K));
  }
  
  // cv::triangulatePoints函数,输出的3D坐标是齐次坐标,共四个维度,因此需要将前三个维度除以第四个维度,得到非齐次坐标x、y、z
  Mat pts_4d;
  cv::triangulatePoints(T1, T2, pts_1, pts_2, pts_4d);

  // 转换成非齐次坐标
  for (int i = 0; i < pts_4d.cols; i++) {
      
    Mat x = pts_4d.col(i);
    x /= x.at<float>(3, 0); // 归一化,四个分量全除以最后一位,将第四位数值转化为1
    
    Point3d p(x.at<float>(0, 0), x.at<float>(1, 0), x.at<float>(2, 0));
    points.push_back(p);
    
  }
}

五、项目链接

如果代码跑不通,或者想直接使用我自己制作的数据集,可以去下载项目链接:
https://blog.csdn.net/Twilight737

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

3D视觉(五):对极几何和三角测量 的相关文章

  • 简述TCP的三次握手过程

    TCP握手协议 在TCP IP协议中 TCP协议提供可靠的连接服务 采用三次握手建立一个连接 第一次握手 xff1a 建立连接时 客户端发送syn包 syn 61 j 到服务器 并进入SYN SEND状态 等待服务器确认 xff1b SYN
  • buildroot使用外部编译链编译bluez蓝牙工具

    在开发ublox w263 wifi蓝牙时 xff0c 之前是使用yocto系统集成编译出的bluez工具 xff0c 减少了自己编译工具软件和依赖库的工作 xff0c 切换项目使用原生linux系统后 xff0c 所以的软件需要自己编译
  • 软件替代硬件HW_RANDOM随机数解决方案

    问题背景 使用低成本平台方案时 xff0c 发现不支持HW RANDOM硬件随机数 xff0c 这样在开启wifi wpa2加密ap后 xff0c 电脑多次提示连接失败才连接成功 xff0c 很影响用户体验 硬件不支持硬件随机数那么就需要使
  • Android下打印出现read: Unexpected EOF!分析

    背景 xff1a 在抓SmePlayer打印时 xff0c 打开debug级别后 xff0c xshell终端就嘟嘟响然后打印出几百行后就打出read Unexpected EOF 结束了 这时候完全没有抓到我需要的打印 xff0c 用lo
  • VMWare中处理器和内存配置含义

    背景 在创建虚拟机系统后 xff0c 默认处理器配置都是为1 xff0c 内存一般选为1G xff0c 然而在使用Linux系统编译大型软件时 xff0c 速度慢耗时较长 xff0c 这时候就需要调整一下处理器和内存配置 xff0c 使得计
  • Linux mkfs.ext4命令参数使用

    命令使用 mkfs ext4 参数 设备 参数说明 c 格式化前检查分区是否有坏块 Checking for bad blocks read only test 19 34 done 0 21 elapsed 0 0 0 errors Ch
  • Xshell 6多个会话窗口消失只能显示一个问题解决

    背景 某一天同事操作了我的Xshell导致我的会话窗口消失只能显示一个 xff0c 通过点击选项卡排列可以显示出消失的窗口 xff0c 但是使用极为不方便 xff0c 下面问题解决 解决 正常显示 xff1a 有三个选项窗口 异常显示 xf
  • OpenWrt学习(一)搭建Linux开发环境

    环境 目前大部分的 linux开发都是在PC虚拟机上进行的 xff0c 所采用的 linux系统版本有 Ubuntu Redhat Debian Fedora 等 xff0c 我们一般在 windows 操作系统上利用 VMware 43
  • OpenWrt学习(三)源码大包编译

    编译大包 make V 61 99 xff0c V 61 99 用来生成编译过程的详细信息 xff0c 方便查找出错原因 xff0c 或者用V 61 s 建议加 j x xff08 x 为CPU core数量 xff09 选项 xff0c
  • Linux gcc自带检测内存泄漏工具asan

    背景 排查和检测内存泄漏的问题时 xff0c 需要选择一些好用的工具 xff0c 由于dmalloc编译复杂 xff0c valgrind依赖太多 xff0c 所以选择使用gcc自带检测内存泄漏工具asan xff0c 版本4 8之后就支持
  • Linux物理内存较少导致OOM问题分析

    背景 开发项目过程中 xff0c 发现盒子启动后物理内存持续的减少 xff0c CMA内存过了一段时间后也出现明显减少情况 xff0c 到最后会低概率性的出现OOM杀掉进程的情况 xff1b 通过ps命令监控所有进程在开机后的虚拟内存VSZ
  • BaseService --- service层的抽取

    service层的抽取和dao层的抽取差不多相同 xff0c 主要不同点是dao实例化的问题 xff0c 需要在调用时传入 xff0c 并在BaseServiceImpl接收 详细的过程说明请参考dao层的抽取 dao层的抽取 xff1a
  • 数据分析思维之用户标签

    文章目录 1 什么是标签 xff1f 2 用户标签有什么用 xff1f 3 用户标签的制作流程4 用户标签的4大常见问题4 1 没有目标 xff0c 盲目打标4 2 不区分时间状态4 3 行为动机乱归因4 4 结果缺少检验 1 什么是标签
  • 看你能坚持读几本书?!——三十本互联网必看书籍

    写在前面 一直想整理个书单 xff0c 譬如关于EA 企业架构 ABCD 人工智能 大数据 云计算 设备 逻辑 思维 沟通 影响力 领导力 管理 绩效 胜任力 战略 商业方面的书单 xff0c 这里看到了一篇整理的比较好的书单 经过简单编辑
  • Docker学习之安装docker-compose命令(采用Python-pip命令安装)

    Docker学习之安装docker compose命令 采用Python pip命令安装 本机系统环境介绍Docker Compose简介使用Python pip命令进行安装第一步 环境检查第二步 安装Python pip第三步 安装doc
  • ros 下面如何通过vrpn 连接 Optitrack

    tracking system 可以实时的计算出来regid body 的pose xff0c 如何在ros 下面得到 这个pose 呢 xff1f 首先 编译安装 vrpn 然后的话需要配置两台电脑的IP 使他们能够 互相 ping 通
  • kalman滤波器各项参数及矩阵的设置

    参考博客 xff1a https blog csdn net baimafujinji article details 50646814 简单来说 xff0c 卡尔曼滤波器的实现是一个迭代过程 xff0c 使用上一次的结果预测当前的值 xf
  • python_tweets.json (python数据挖掘入门与实践数据集下载)

    最近在看python数据挖掘入门与实践一书 xff0c 书不错 xff0c 有个不好的地方是 xff0c 书上所用的数据集 xff0c 有几个测试数据在网上非常不好找 下面几个资源是我自己整理出来的 xff0c 上传到CSDN xff0c
  • ubuntu20.04_ROS中运行gazebo控制机器人模型报错

    1 无法启动类型为 controller manager spawner 的节点 xff1a controller manager ERROR cannot launch node of type controller manager sp
  • Fast Planner——代码解读参考资料整理

    1 地图部分 1 1 EGO Swarm代码解读 地图部分 参数解读 主要函数解读 1 2 EGO Swarm代码阅读笔记之GridMap类 1 3 EGO PLANNER代码阅读 xff08 地图部分 xff09 1 4 欧几里得距离转换

随机推荐

  • FreeRTOS 10.4.3在RISCV(T-HEAD C906)平台上移植过程

    好记性不如烂笔头 记录点滴移植经历 一方面便于总结提炼 二是分享 让别人少走有些弯路 自己以后踩坑了也爬的利索点 首先梳理一下移植框架 FreeRTOS还是非常简单的 体量上要比RT Thread Nuttx等偏重型的系统轻量不少 这可能也
  • Web服务器CGI的配置

    Web服务器CGI的配置 CGI程序运行在Web服务器端 xff0c Web服务器可以是Apache Nginx等 GGI程序可以是Python Ruby Perl Shell C C 43 43 等 配置 apache默认加载cgi模块
  • 解决mac安装homebrew后报错-bash: brew: command not found

    参照官网上很简单的一句安装命令 xff0c usr bin ruby e 34 curl fsSL https raw githubusercontent com Homebrew install master install 34 安装完
  • 转行的辛苦

    我是2004年毕业的 xff0c 学的专业是市场营销 xff0c 毕业后来到深圳 xff0c 换了很多工作 xff0c 一直都无法找到令自己满意的工作 因为我非常喜欢计算机 xff0c 从中学到大学 xff0c 一直是班级里公认的计算机高手
  • 物联网之轻量级TCP/IP协议栈——Lwip

    简介 Lwip为轻量级的Tcp IP协议栈 xff0c 对于嵌入式设备资源比较友好 xff0c 占用RAM低 xff0c 基本上物联网wifi芯片都会集成此协议栈到SDK中 xff0c 其位于OSI的网路层往上 熟悉socket编程的能够很
  • github下载慢或报错“The-remote-end-hung-up-unexpectedly”解决办法

    github下载慢或报错 The remote end hung up unexpectedly 解决办法 xff1a 该问题往往因为内部网络限制等因素导致 因细节更新 xff0c 欢迎访问本文源站链接 xff1a https turboc
  • git 如何把单个文件回退到某一版本

    git 如何把单个文件回退到某一版本 概要四条命令git restoregit resetgit checkoutgit cherry pick 概要 应用场景 xff1a 在进行一次完整的提交后 xff0c 你可能有有这样的需求 xff1
  • Docker

    学习笔记 一 Docker概述 1 基本介绍 Docker是一个开源的应用容器引擎 xff0c 基于Go语言 xff0c 并遵从apache2 0协议开源 docker可以让开发者打包他们的应用以及依赖包到一个轻量级 可移植的容器中 xff
  • roslaunch 时出现resource 找不到的问题

    Resource not found roslaunch ROS path 0 61 opt ros noetic share ros ROS path 1 61 opt ros noetic share The traceback for
  • Linux多线程编程

    在传统的 UNIX 模型中 xff0c 当一个进程需要另一个实体来完成某事 xff0c 它就 fork 一个子进程并让子进程去处理 但是 fork 的调用有如下缺点 xff1a xff08 1 xff09 fork 的 代价是 昂贵的 fo
  • 控制工程实践(13)——滤波器的实现(之二)

    2 中值滤波算法 中值滤波算法 xff0c 通俗讲 xff0c 就是取一组数据的中间大小的值 运算过程 xff1a 对某一参数连续采样N次 xff0c 为方便选取 xff0c N设为奇数 xff1b 把N个采样值从小到大排序 xff1b 取
  • 控制工程实践(14)——滤波器的实现(之三)

    4 加权平均滤波算法 算术平均滤波算法 有平滑度和灵敏度的取舍矛盾 xff1a 取样信号个数小时 xff0c 灵敏度高 xff0c 但平滑度低 xff1b 取样信号个数大时 xff0c 平滑度高 xff0c 但灵敏度低 为了协调二者矛盾 x
  • 信号与系统 基础知识点整理 03(文末可下载PDF格式)

    接着前面的继续 xff1a 信号与系统 passage three Written 8 March 2022 Edited by Wang Ximing 一 xff08 信号的分解 xff09 xff08 1 xff09 直流分量与交流分量
  • mac系统如何生成SSH key与GitHub通信

    一 检查 SSH key 是否存在 在终端输入 xff1a ls al ssh 如果没有 xff0c 终端显示如下 xff1a No such file or directory 如果已经存在 xff0c 则会显示 id rsa 和 id
  • ROS-Industrial 硬件支持

    ROS Industrial硬件支持 ROS Industrial程序的目标是为许多不同种类的工业设备提供ROS接口 xff0c 包括PLC xff0c 机器人控制器 xff0c Servos xff0c 人机界面 工业机器人 下表总结了各
  • ROS依赖包查找安装

    当下载某个功能包到自己的空间 xff0c 在编译时 xff0c 出现依赖项有问题 xff0c 解决如下 xff1a roscd package name rosdep update rosdep package name rosdep即可安
  • 在Windows上使用ROS软件包

    1 二进制安装 如果有ROS软件包的二进制发行版 xff0c 可以使用Chocolatey安装 choco install ros melodic lt package name gt ROS软件包查询 https index ros or
  • EKF扩展卡尔曼滤波器 - CTRV运动模型 及其代码实现

    本文参考了Adam大佬的帖子 https blog csdn net AdamShan article details 78265754 原贴的公式有一点点错误 xff0c 这里已经修正了 xff0c 并给出了代码实现 CTRV模型 我们通
  • 安卓SDK和API是什么意思?

    安卓SDK和API是什么意思 xff1f 一 SDK SDK就是kit xff0c 通俗讲就是工具箱 一系列的工具组合在一起 xff0c 能实现补全代码 自动错误检查之类的功能 xff0c 比如点一下run xff0c 会调用编译器来自动编
  • 3D视觉(五):对极几何和三角测量

    3D视觉 五 xff1a 对极几何和三角测量 对极几何 xff08 Epipolar Geometry xff09 描述的是两幅视图之间的内在射影关系 xff0c 与外部场景无关 xff0c 只依赖于摄像机内参数和这两幅试图之间的的相对姿态