【opencv】示例-essential_mat_reconstr.cpp 从两幅图像中恢复3D场景的几何信息

该博客介绍了使用OpenCV库在计算机视觉中从两幅图像恢复3D场景几何信息的代码。代码实现了读取图像、检测关键点、特征匹配、计算本质矩阵、分解获得位姿、三角测量获取3D坐标、平面拟合与分类等步骤,最终将结果可视化并保存图像。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 导入OpenCV的calib3d, highgui, imgproc模块以及C++的vector, iostream, fstream库。

  2. 定义了getError2EpipLines函数,这个函数用来计算两组点相对于F矩阵(基础矩阵)的投影误差。

  3. 定义了sgn函数,用于返回一个双精度浮点数的符号。

  4. 定义了getPlanes函数,用于通过RANSAC算法和3D点集从一系列点中找到若干平面,同时这些点会被分类到对应的平面。

  5. main函数则完成了整个立体视觉的重建流程,具体步骤包括:

  • 读取数据文件和图像;

  • 从图像中检测关键点并提取SIFT特征描述符;

  • 使用FLANN库进行高效特征匹配,并使用Lowe's比率测试筛选优秀的匹配;

  • 通过匹配的特征点计算本质矩阵(Essential Matrix);

  • 计算特征点到极线(epipolar lines)的平均误差;

  • 本质矩阵分解得到旋转矩阵和平移矩阵;

  • 通过三角化方法恢复出3D空间中的点;

  • 对恢复出的3D点进行平面拟合,以识别出可能的物理平面;

  • 显示和保存最终处理后的图像。

总的来说,这段代码展示了如何使用OpenCV库在计算机视觉中从两幅图像中恢复3D场景的几何信息。

d77496ba341876ba1952ec009a5010cb.jpeg

leuvenA

692c23cc6ace519764fb2ee76f723b77.jpeg

leuvenB

09879e38db33c523b6c2e51f17fd66c3.png

a5448fb6c20c574c412585f5178ca328.png

essential_mat_data.txt

终端输出:

RANSAC essential matrix time 88023mcs.
Number of inliers 212
Mean error to epipolar lines 0.219943
Number of object points 212

83de0b759bfdc8ec57aa37476dba7d4a.png

#include "opencv2/calib3d.hpp"  // 包含OpenCV相机校准和3D重建的库
#include "opencv2/highgui.hpp"  // 包含OpenCV图像显示的库
#include "opencv2/imgproc.hpp"  // 包含OpenCV图像处理的库


#include <vector>  // 包含向量容器类的头文件
#include <iostream>  // 包含标准输入输出流的头文件
#include <fstream>  // 包含文件输入输出流的头文件


using namespace cv;  // 使用OpenCV命名空间
static double getError2EpipLines(const Mat &F, const Mat &pts1, const Mat &pts2, const Mat &mask) {
    Mat points1, points2;
    // 将点坐标扩展为齐次坐标形式(在原坐标基础上增加一维,设为1)
    vconcat(pts1, Mat::ones(1, pts1.cols, pts1.type()), points1);
    vconcat(pts2, Mat::ones(1, pts2.cols, pts2.type()), points2);


    double mean_error = 0;
    // 计算并累积所有点到极线的距离作为误差
    for (int pt = 0; pt < (int)mask.total(); pt++)
        if (mask.at<uchar>(pt)) { // 如果该点是内点,则计算
            const Mat l2 = F * points1.col(pt); // 使用基础矩阵F计算极线
            const Mat l1 = F.t() * points2.col(pt); // 使用F的转置计算极线
            // 计算点到极线的距离并取平均
            mean_error += (fabs(points1.col(pt).dot(l1)) / sqrt(pow(l1.at<double>(0), 2) + pow(l1.at<double>(1), 2)) +
                           fabs(points2.col(pt).dot(l2)) / sqrt(pow(l2.at<double>(0), 2) + pow(l2.at<double>(1), 2))) / 2;
        }
    return mean_error / mask.total(); // 返回平均误差
}
static int sgn(double val) { return (0 < val) - (val < 0); } // 符号函数


/*
 * @points3d - vector of Point3 or Mat of size Nx3
 * @planes - vector of found planes
 * @labels - vector of size point3d. Every point which has non-zero label is classified to this plane.
 */
// getPlanes函数,从3D点云中拟合多个平面
// @points3d - 3D点的集合,可以是Point3的向量或者是Nx3的Mat类型
// @planes - 存储找到的平面
// @labels - 和points3d大小相同的向量,每个非0标签的点都被归类到对应的平面
static void getPlanes (InputArray points3d_, std::vector<int> &labels, std::vector<Vec4d> &planes, 
                       int desired_num_planes, double thr_, double conf_, int max_iters_) {
    // 获取输入点并转换为双精度浮点数类型
    Mat points3d = points3d_.getMat();
    points3d.convertTo(points3d, CV_64F);  // 将点转换为双精度


    // 如果输入是一个向量,将其转换为Mat
    if (points3d_.isVector())
        points3d = Mat((int)points3d.total(), 3, CV_64F, points3d.data);
    else {
        // 如果输入的是单通道的Mat,转换为有1个通道
        if (points3d.type() != CV_64F)
            points3d = points3d.reshape(1, (int)points3d.total());
        // 如果点的行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值