利用三角测量计算帧间特征点的空间位置

triangulatePoints函数 cv::triangulatePoints函数参数 两个相机位姿和特征点在两个相机坐标系下的坐标,输出三角化后的特征点的3D坐标。 注意:输出的3D坐标是齐次坐标,共四个维度,因此需要将前三个维度除以第四个维度以得到非齐次坐标xyz。这个坐标是在相机坐标系下的坐标,以输入的两个相机位姿所在的坐标系为准。 在主函数中,通过把3D坐标重投影到两个相机的归一化平面上,从而计算重投影误差。因此需要再次对xyz坐标同时除以z,以得到归一化平面上的坐标。 1.对极几何 从2张图片中,得到若干个配对好的2d 阅读详情

需要安装opencv3中的features2d模块

    代码中的1.png

    代码中的2.png

// 来自高翔SLAM十四讲
#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;

void find_feature_matches(
    const Mat& img_1,  const Mat& img_2,
    std::vector<KeyPoint>& keypoints_1,
    std::vector<KeyPoint>& keypoints_2,
    std::vector<DMatch>& matches);

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

// 像素坐标转相机归一化坐标
Point2d pixel2cam(const Point2d& p, const Mat& K);

// 三角测量
void triangulation(
    const vector<KeyPoint>& keypoint_1,
    const vector<KeyPoint>& keypoint_2,
    const std::vector<DMatch>& matches,
    const Mat& R, const Mat& t,
    vector<Point3d>& points
);

int main(){
    // 读取图像
    Mat img_1 = imread("1.png",CV_LOAD_IMAGE_COLOR);
    Mat img_2 = imread("2.png", CV_LOAD_IMAGE_COLOR);

    vector<KeyPoint>keypoints_1, keypoints_2;
    vector<DMatch> matches;
    find_feature_matches(img_1, img_2, keypoints_1, keypoints_2, matches);
    cout<<"一共找到了"<<matches.size()<<"组匹配点"<<endl;

    // 估计两张图像之间的运动
    Mat R, t;
    pose_estimation_2d2d(keypoints_1, keypoints_2, matches, R, t);

    // 三角化
    vector<Point3d> points;
    triangulation(keypoints_1, keypoints_2, matches, R, t, points);

    // 验证三角化点与特征点的重影关系
    Mat K = (Mat_<double>(3,3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
    for(int i=0; i<matches.size(); i++){
        Point2d pt1_cam = pixel2cam(keypoints_1[matches[i].queryIdx].pt, K);
        Point2d pt1_cam_3d(
            points[i].x / points[i].z,
            points[i].y/ points[i].z
        );

        cout<<"point in the first camera frame : "<<pt1_cam<<endl;
        cout<<"point projected from 3D "<<pt1_cam_3d<<", d="<<points[i].z<<endl;

        // 第二个图
        Point2f pt2_cam = pixel2cam(keypoints_2[matches[i].trainIdx].pt, K);
        Mat pt2_trans = R * (Mat_<double>(3,1) << points[i].x, points[i].y, points[i].z) + t;
        pt2_trans /= pt2_trans.at<double>(2,0);
        cout<<"point in the second camera frame : "<<pt2_cam<<endl;
        cout<<"point reprohected from second frame : "<<pt2_trans.t()<<endl;
        cout<<endl;
    }

    return 0;
}

// 找到匹配的特征点
void find_feature_matches(const Mat& img_1, const Mat& img_2,
                                        std::vector<KeyPoint>& keypoints_1,
                                        std::vector<KeyPoint>& keypoints_2,
                                        std::vector<DMatch>& matches){
    // 初始化
    Mat descriptors_1, descriptors_2;
    Ptr<FeatureDetector>detector = ORB::create();
    Ptr<DescriptorExtractor>descriptor = ORB::create();
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");   // 暴力匹配

    // 第一步:检测Oriented FAST角点位置
    detector->detect(img_1, keypoints_1);
    detector->detect(img_2, keypoints_2);

    // 第二步: 根据角点位置计算BRIEF描述子
    descriptor->compute(img_1, keypoints_1, descriptors_1);
    descriptor->compute(img_2, keypoints_2, descriptors_2);

    // 第三步:对两幅图像中的BRIEF描述子进行匹配,使用汉明距离
    vector<DMatch> match;
    matcher->match(descriptors_1, descriptors_2, match);

    // 第四步:对匹配点对进行筛选
    double min_dist = 10000, max_dist = 0;

    // 找出所有匹配之间的最小距离和最大距离
    // 即是最相似和最不相似的两组点之间的距离
    for(int i=0; i<descriptors_1.rows; i++){
        double dist = match[i].distance;
        min_dist = min_dist<dist?min_dist:dist;
        max_dist = max_dist>dist?max_dist:dist;
    }
    printf("--Max dist : %f\n", max_dist);
    printf("--Min dist : %f \n", min_dist);

    // 当描述子之间的距离大于两倍的最小距离时,即认为匹配有误
    // 设置30为阈值
    for(int i=0; i<descriptors_1.rows; i++){
        if(match[i].distance <= max(2*min_dist, 30.0)){
            matches.push_back(match[i]);
        }
    }
}

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

void pose_estimation_2d2d(std::vector<KeyPoint>keypoints_1,
                                                std::vector<KeyPoint>keypoints_2,
                                                std::vector<DMatch>matches,
                                                Mat&R ,Mat& t){
    // 相机内参,TUM Feriburg2
    Mat K = (Mat_<double>(3,3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);

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

    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);
    }

    // 计算基础矩阵:使用的8点法,但是书上说8点法是用来计算本质矩阵的呀,这两个有什么关系吗
    Mat fundamental_matrix;
    fundamental_matrix = findFundamentalMat(points1, points2, CV_FM_8POINT);
    cout<<"fundamental_matrix is "<<endl<<fundamental_matrix<<endl;

    // 计算本质矩阵:是由对极约束定义的:对极约束是等式为零的约束
    Point2d principal_point(325.1, 249.7);  // 相机光心,TUM dataset标定值
    double focal_length = 521;      // 相机焦距,TUM dataset标定值
    Mat essential_matrix;
    essential_matrix = findEssentialMat(points1, points2, focal_length, principal_point);
    cout<<"essential_matrix is "<<endl<<essential_matrix<<endl;

    // 计算单应矩阵:通常描述处于共同平面上的一些点在两张图像之间的变换关系
    Mat homography_matrix;
    homography_matrix = findHomography(points1, points2, RANSAC, 3);
    cout<<"homography_matrix is "<<endl<<homography_matrix<<endl;

    // 从不本质矩阵中恢复旋转和平移信息
    // 这里的R,t组成的变换矩阵,满足的对极约束是:x2 = R * x1 + t,是第一个图到第二个图的坐标变换矩阵x2 = T21 * x1
    recoverPose(essential_matrix, points1, points2, R, t, focal_length, principal_point);
    cout<<"R is "<<endl<<R<<endl;
    cout<<"t is "<<endl<<t<<endl;
}

void triangulation(
    const vector<KeyPoint>& keypoint_1,
    const vector<KeyPoint>& keypoint_2,
    const std::vector<DMatch>& matches,
    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)
                    );
    
    Mat K = (Mat_<double>(3,3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
    vector<Point2f> 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));
    } 

    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);  // 归一化
        Point3d p(
            x.at<float>(0,0),
            x.at<float>(1,0),
            x.at<float>(2,0)
        );
        points.push_back(p);
    }
}

 

【cv::triangulatePoints】其中的投射矩阵P(3x4)是怎么得到的?(内外参数K[R|t]到最终矩阵的变换过程) 通过内参矩阵和外参矩阵的组合,我们得到了用于描述三维点到二维图像平面投影的投影矩阵P\mathbf{P}P。这个矩阵在多视图几何和计算机视觉的应用中至关重要,特别是在三维重建和相机校准中。 阅读详情

相关推荐

三角测量——用相机运动估计特征点的空间位置

经过对极约束的求解,现在已知R,t,我们想求解两个特征点的深度z1,z2。(在单目相机模型中,深度被抹去了,因此若想建图需要求解‘z’),即,两条线在两个平行的平面中,而两个平面互相平行。因此我们会选择两条线最近的的近似为p点,也就是。当然,由于噪声的存在,我们估得的R,t不一定精确使式子成立,所以更常见的做法是求。使用对极约束估计了相机运动后,接下来利用相机运动估计特征点的空间位置,使用的方法就是。的一个方程,可以根据它直接求得。该式左侧为零,右侧可看成。的存在,两条线可能会出现。

k42946的博客 1661

三角测量,用Opencv计算获得两图片匹配点的深度

函数 cv::triangulatePoints(T1, T2, points1, points2, points_4d); 输入: T1: 3*4的cv::Mat ,就是第一张图片的 R和t组合一起的 T2: 3*4的cv::Mat ,就是第二张图片的 R和t组合一起的 points1: vector<cv::Point2f> 前一张图片的匹配点 points2: vector<cv::Point2f> 后一张图片的匹配点,肯定和上一个size()一样 输出: points_..

qq_41253960的博客 6034

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

3D视觉(五):对极几何和三角测量 对极几何(Epipolar Geometry)描述的是两幅视图之间的内在射影关系,与外部场景无关,只依赖于摄像机内参数和这两幅试图之间的的相对姿态。 文章目录3D视觉(五):对极几何和三角测量一、对极几何二、三角测量三、实验过程四、源码五、项目链接 一、对极几何 假设我们从两张图像中得到了一对配对好的点对,如果有若干对这样的匹配点对,就可以通过这些二维图像点的对应关系,恢复出在两帧之间的摄像机的运动。 从代数角度来分析这里的几何关系。在第1帧的坐标系下,设P的空间位置为

Twilight737的博客 5705

姿态解算

1.MPU6050 软件姿态解算。首先呢,我们在做完智能车比赛之后就萌发了再做一个四旋翼的想法,当然也是想参加来年的全国电设。我们选用MPU6050作为传感器,MPU6050是一款三轴陀螺仪,三轴加速度计芯片,自动反馈三个轴上的角速度和加速度分量。拿这些数据怎么去变成我们所需要的对于四轴的控制量呢,我们需要通过这个6个数据分量融合成三个方向的欧拉角(YAW,ROLL,PITCH)。这个过程,称之为...

void_xinyue的博客 2664

VINS 细节系列 - 特征点三角化

一、原理 上一步通过对极几何约束估计了相机运动得到R、t; 接下来,需要用相机的运动估计特征点的空间位置。 三角测量:通过在两处观察同一个点的夹角,从而确定该点的距离。 通过最小二乘法求解。 s1*x1=s2*R*x2+t; 已知R和t,分别求s1和s2; s1*x1^*x1=0=s2*x1^*R*x2+x1^*t; 二、代码详解 1、主框架 int main(int ar...

努力努力努力 3301

SLAM或VO中两帧间恢复Rt的方法汇总

2D-2D:对极几何 A.对极约束 @输入:参考帧和当前帧的2D点坐标对(一般8对),内参 @输出:运动恢复R t 参考帧和当前帧的二维像素点通过特征点法拿到了匹配关系,那么从各自光心出发的两条射线交点即3D点,P和两个光心构成了极平面,e1e1e1 \ e2e2e2为极点,l1l1l1 \ l2l2l2是极线(Epipolar line)。 对极几何必须要有正确的匹配关系,否则当前帧无法找到参...

csz 2084

OpenCV三角测量重建triangulatePoints原理解析

OpenCV三角测量重建triangulatePoints原理解析

yuleaf_csdn的博客 1万+

视觉SLAM笔记(34) 三角测量

对极几何约束局限性(仅通过单张图像无法获得像素的深度信息)、三角测量(通过在两处观察同一个点的夹角,确定该点的距离)、于噪声的影响,这两条直线往往无法相交,可以通过最小二乘去求解、三角测量的矛盾

氢键H-H 1万+

视觉SLAM学习【3】-----视觉SLAM通过三角测量和PnP法估计特征点的空间位置(采用make编译方式)

ROS学习【20】-----视觉SLAM通过三角测量和PnP法估计特征点的空间位置目录一、G2O的安装1、g2o的下载2、文件上传ubuntu3、安装依赖库4、g2o的编译二、项目创建1、创建项目文件夹2、创建三角测量的cpp文件3、创建PnP法的cpp文件4、创建CMakeLists.txt配置文件三、编译项目1、文件准备2、项目进行编译2、结果运行 嵌入式开发学习也慢慢的进入尾声了,从之前的ros学习,到gazebo再到机械臂的学习,到现在的slam视觉学习,层次逐渐上升,难度逐渐加大,本次博客,林君学

qq_42451251的博客 1309

ros1 ubuntu22.04 安装教程

ROS1 ubuntu22.04 安装教程

autolabor的博客 9195

小米路由器安装Misstar tools 和MIXBOX

1.该教程适用于如下路由器: 小米路由器1代硬盘版(R1D) 小米路由器mini(R1CM) 全新小米路由器硬盘版(R2D) 小米路由器3(R3) 小米路由器3G(R3G) 小米路由器Pro(R3P) 小米路由器HD(R3D) 2.Misstar2.0在线安装 方法如下: 1.根据路由器对应文件解压文件,把解压目录(比如小米3路由器为R3)和offline_install.sh到U盘根目录,并插入路由器 2.ssh登录后,cd到U盘的目录,比如小米3路...

wybarcelona的博客 2万+

视觉SLAM十四讲学习笔记-第七讲-视觉里程计-三角测量和实践

专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第二讲-开发环境搭建_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第三讲-旋转矩阵和Eigen库_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第三讲-旋转向量、欧拉角、四元数_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第三讲-相似、仿射、射.

weixin_36773706的博客 2996

从实验室到生产线:Qwen3-Omni后训练技术在实际业务中的落地挑战

本文探讨了Qwen3-Omni后训练技术在实际业务中的落地挑战,重点分析了多模态AI在工业应用中的关键问题与解决方案。通过金融风控、跨境电商等场景案例,揭示了通用模型与垂直需求的矛盾,并提供了知识蒸馏、语音合成优化等实战经验,助力企业实现技术到业务的转化。

weixin_29035147的博客 267

V-SLAM中的2D-2D对极几何和三角测量基本原理(含C++代码)

V-SLAM中的2D-2D对极几何和三角测量基本原理(含C++代码)

u014374826的博客 1016

Vivado里LDPC IP核怎么配?手把手教你从6144bit数据到完整参数设置

本文详细介绍了在Vivado中配置LDPC IP核的完整流程,从6144bit数据的参数计算到实际IP核设置。通过分析基本图选择、扩展因子计算和移位参数确定等关键步骤,帮助工程师高效完成LDPC编译码的硬件实现,并提供了时序控制、仿真验证和性能优化的实用技巧。

weixin_30352645的博客 517

从SD更新字库到FLASH,再用LCD显示

用Flash当字库存储载体。思路是先将要用到的字库文件存在SD卡,然后存储到Flash,LCD显示时候从Flash中读取 还挺顺利的,移植的原子的程序,自己稍加修改,主要是我的液晶屏函数,Flash读写函数,还有存储文件的flash地址不一样。一晚上搞定,大小字体都OK。挺高兴哈哈。这几天忙工作事情有点耽搁了学习进展嘿嘿 在text.c的showstr函数中char的字体颜色随意设置的,可以显...

weixin_30505751的博客 914

三角测量

三角测量 使用对极约束估计了相机的运动,在得到运动之后,我们需要用相机的运动估计特征点的空间位置。在单目SLAM中,仅仅通过单张图像无法获得像素深度信息,需要通过三角测量来估计其深度。 上图中,O1P 和 O2P 理论上交与P点,但是由于噪声的存在,往往无法相交。可以通过最小二乘求解。设 x1,x2 为俩个特征点的归一化坐标,那他们满足: s2x2=s1Rx1+t   由于我们已...

天空之城 1万+

笔试题-2023-紫光展锐-数字芯片设计【纯净题目版】

笔试题-2023-紫光展锐-数字芯片设计【纯净题目版】

w0shishabi的博客 2923

第7讲3 三角测量

之所以把SLAM初始化和三角测量放在一起是因为它们之间有一定的关系,理解了三角测量之后才能理解初始化。 三角测量        在得到了相机的运动之后,下一步我们需要用相机的运动来估计特征点的空间位置,但是在单目SLAM中,仅通过单张图像是无法获得像素的深度信息的,我们需要用三角测量(三角化)的方法来估计地图点的深度。 先来看一些什么是三角测量?三角测量是指通过在两处观察同一个点的夹角,来确...

llfjcmx的博客 1万+

Nature、science、cell旗下刊物

Nature\science\cell子刊说明

计算机视觉 5605

【声纳技术】声呐时间反转镜技术:从被动聚焦到主动空间复用

时间反转镜技术以其独特的物理机制,为水声通信与声呐探测提供了一条绕过传统自适应均衡器的新路径。被动时间反转利用接收端的空间分集实现脉冲压缩,结构简洁、计算高效;主动时间反转则通过发射端预编码,将多径能量引导至目标位置,为多用户下行链路的空间复用奠定了物理基础。从Kuperman等人在海洋中首次验证声学时间反转镜,到TR-MIMO声呐的湖试验证,再到VTRM-CRK多用户通信系统的海试成功,该技术已从理论概念逐步走向工程实用。

u012133341的博客 55

三角测量原理及代码实现

在单目SLAM中,仅通过单张图像无法获得像素的深度信息,我们需要通过三角测量(Triangulation )(或三角化)的方法估计地图点的深度,如图7-11所示。在使用对极几何求解本质矩阵或者基础矩阵后就可以恢复出旋转矩阵和平移向量了,但是由于本质矩阵E的尺度等价性,分解得到的平移向量t具有一个尺度,换言之,在分解过程中,t乘以任意非零常数,分解都是成立的,因此,通常把。求取旋转,但仅有旋转时,我们无法用三角测量估计特征点的空间位置(这将在下文提到),于是,另一个结论是,在单目视觉中,我们对两张图像的。

weixin_43283119的博客 2281

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

「 SLAM lesson-7 」视觉里程计知识点纲要(特征点,对极几何,三角测量,PnP,ICP)

结合高翔老师的著作《视觉SLAM十四讲:从理论到实践》,加上小白的工程经验共同完成。建议作为笔记功能反复使用。 一. 特征点法 特征点;(特征点 = 关键点+ 描述子 ) 关键点:特征点在图像里的位置;描述子:通常是一个向量,描述了该关键点周围像素的信息。 ORB 特征;(FAST角点提取 和 BRIEF 描述子) FAST 是一种角点,主要检测局部像素灰度变化明显的地方,以速度快著...

Robot_Starscream的博客 2002

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

视觉SLAM笔记--第6篇: 单目初始化和三角测量

单目初始化和三角测量参考博客1. 三角测量2. 单目初始化 参考博客 参考博客:https://blog.youkuaiyun.com/llfjcmx/article/details/83410318 1. 三角测量 在得到了相机的运动之后,下一步我们需要用相机的运动来估计特征点的空间位置,但是在单目SLAM中,仅通过单张图像是无法获得像素的深度信息的,需要用三角测量(三角化)的方法来估计地图点的深度。 三角测量是指通过在两处观察同一个点的夹角,来确定该点的距离。 考虑图像I1,I2I_{1},I_{2}I1​,I2

w风的季节-博客 4085

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

视觉SLAM实践入门——(12)三角测量

啊

floatinglong的博客 2128
上一篇: 通过两张图片实现2D-2D对极几何位姿估计
下一篇: 利用深度图实现3D-2D的PNP相机位姿估计
rdc01234
博客等级 码龄9年 10粉丝 156原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值