在windows下vs c++运行g2o的BA优化程序示例

目录

  • 1、前言
  • 2、准备工作
    • 安装git
    • 安装vcpkg
      • (1)下载
      • (2)安装
      • (3)集成至vs
    • 安装cmake
  • 3、安装g2o
  • 4、安装opencv
    • (1)下载
    • (2)双击安装
    • (3)环境变量和system文件夹设置
  • 使用g2o进行BA优化
  • 5、总结

1、前言

本篇博客主要介绍如何在Windows下安装g2o,并利用g2o和OpenCV库实现一个两帧之间的ORB特征点检测和BA优化的C++程序。ORB是一种既能检测特征点,又能描述特征点的算法,BA是一种优化算法,可以优化相机位姿和三维点云。本文将详细介绍如何在Windows下安装g2o,并利用g2o和OpenCV库实现一个两帧之间的ORB特征点检测和BA优化的C++程序。首先,我们需要在Windows下安装g2o。g2o是一个用于图优化的C++库,可以用于SLAM、机器人、计算机视觉等领域。其次,我们需要使用OpenCV库来进行ORB特征点检测。最后,我们将使用g2o库来进行BA优化。在本文中,我们将详细介绍如何在Windows下安装g2o,并利用g2o和OpenCV库实现一个两帧之间的ORB特征点检测和BA优化的C++程序。

2、准备工作

安装git

需要安装git工具,可以上官网去下载安装软件。直接选择默认选项安装到底就可以。
在这里插入图片描述

安装vcpkg

经过多种方法安装失败后,本人认为,通过vcpkg安装g2o库是最为方便的,vcpkg可以自动安装g2o所需要的依赖库。vcpkg本身的安装也非常方便。
关于安装vcpkg详细步骤如下:

(1)下载

在D盘为 vcpkg 的克隆实例创建目录。
打开cmd,进入创建的目录,从 GitHub 克隆 vcpkg 存储库:https://github.com/Microsoft/vcpkg。

git clone https://github.com/microsoft/vcpkg

或者直接上github去打包下载。

(2)安装

下载完后,解压到安装目录。然后cmd进入vcpkg目录内,里面有个.bat文件,在 vcpkg 根目录下,cmd 下运行 vcpkg 引导程序命令:

bootstrap-vcpkg.bat

就算完成了。

(3)集成至vs

cmd进到vcpkg目录下,只要允许这个命令就可以

 .\vcpkg integrate install

如果后期不想集成可以通过指令去除

 .\vcpkg integrate remove

安装cmake

这个也简单,直接去官网下载安装包,双击安装完事。
先打开下载链接点击下载,下后双击正常安装。
在这里插入图片描述

3、安装g2o

非常简单,cmd进入vcpkg目录,输入指令安装:

vcpkg install g2o:x64-windows

耐心等待安装结束,网络要有保障。

4、安装opencv

opencv也可以通过指令安装,也可以去官网下载安装包,我是直接官网下的,弄完需要设置一些东西:

(1)下载

直接从官网下载安装包就行:
在这里插入图片描述

(2)双击安装

这个是二进制文件,直接按照到相应的目录下。
在这里插入图片描述
在这里插入图片描述

(3)环境变量和system文件夹设置

将opencv的\build\x64\vc16\bin文件夹路径写入系统环境变量(path)里面:
在这里插入图片描述
同时,需要把bin下面的所有dll文件都放到system32里面:
在这里插入图片描述
在这里插入图片描述

使用g2o进行BA优化

创建一个vs C++项目,然后直接复制如下代码,就可以编译运行了,对代码进行了一些注释,供参考:

// g2otest.cpp: 定义应用程序的入口点。
//
#pragma once
// for opencv
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
// for g2o
#include <g2o/core/sparse_optimizer.h>
#include <g2o/core/block_solver.h>
#include <g2o/core/robust_kernel.h>
#include <g2o/core/robust_kernel_impl.h>
#include <g2o/core/optimization_algorithm_levenberg.h>
#include <g2o/solvers/cholmod/linear_solver_cholmod.h>
#include <g2o/types/slam3d/se3quat.h>
#include <g2o/types/sba/types_six_dof_expmap.h>
//for eigen
#include <Eigen/Core>
#include <Eigen/Geometry>

/**
 * BA Example
 * Author: Xiang Gao
 * Date: 2016.3
 * Email: gaoxiang12@mails.tsinghua.edu.cn
 *
 * 在这个程序中,我们读取两张图像,进行特征匹配。然后根据匹配得到的特征,计算相机运动以及特征点的位置。这是一个典型的Bundle Adjustment,我们用g2o进行优化。
 */
using namespace std;

// 寻找两个图像中的对应点,像素坐标系
// 输入:img1, img2 两张图像
// 输出:points1, points2, 两组对应的2D点
int     findCorrespondingPoints(const cv::Mat& img1, const cv::Mat& img2, vector<cv::Point2f>& points1, vector<cv::Point2f>& points2);

// 相机内参,自己标定设定
double cx = 256;
double cy = 256;
double fx = 520;
double fy = 520;

int main(int argc, char** argv)
{
    // 调用格式:命令 [第一个图] [第二个图]
    cv::Mat img1;
    cv::Mat img2;
    if (argc < 3)
    {
        cout << "无输入图像路径"<< endl;
        // 读取图像
        img1 = cv::imread("F:/c++test/g2otest/img/image1.jpg", cv::IMREAD_GRAYSCALE);
        img2 = cv::imread("F:/c++test/g2otest/img/image2.jpg", cv::IMREAD_GRAYSCALE);
    }
    else {
        cout << "输入图像路径" << argv[1]<<"和" << argv[2] << endl;
        img1 = cv::imread(argv[1]);
        img2 = cv::imread(argv[2]);

    }



    // 找到对应点
    vector<cv::Point2f> pts1, pts2;
    if (findCorrespondingPoints(img1, img2, pts1, pts2) == false)
    {
        cout << "匹配点不够!" << endl;
        return 0;
    }
    cout << "找到了" << pts1.size() << "组对应特征点。" << endl;
    // 构造g2o中的图
    /*
    */
    // 先构造求解器
    g2o::SparseOptimizer    optimizer;
    // 6*3 的参数
    typedef g2o::BlockSolver<g2o::BlockSolverTraits<6, 3>> Block; 

    // 使用Cholmod中的线性方程求解器
    typedef g2o::BlockSolver<g2o::BlockSolverTraits<6, 3>> BlockSolverType;
    typedef g2o::LinearSolverCholmod<BlockSolverType::PoseMatrixType> LinearSolverType;

    auto solver = new g2o::OptimizationAlgorithmLevenberg(g2o::make_unique<BlockSolverType>(g2o::make_unique<LinearSolverType>()));

    optimizer.setAlgorithm(solver);
    optimizer.setVerbose(false);
    
    // 添加节点
    // 两个位姿节点
    for (int i = 0; i < 2; i++)
    {
        g2o::VertexSE3Expmap* v = new g2o::VertexSE3Expmap();
        v->setId(i);
        if (i == 0)
            v->setFixed(true); // 第一个点固定为零
        // 预设值为单位Pose,因为我们不知道任何信息
        v->setEstimate(g2o::SE3Quat());
        optimizer.addVertex(v);
    }
    // 很多个特征点的节点观测值
    // 以第一帧为准
    for (size_t i = 0; i < pts1.size(); i++)
    {
        g2o::VertexSBAPointXYZ* v = new g2o::VertexSBAPointXYZ();
        v->setId(2 + i);
        // 由于深度不知道,只能把深度设置为1了
        double z = 1;
        double x = (pts1[i].x - cx) * z / fx;
        double y = (pts1[i].y - cy) * z / fy;
        /*
        * v->setMarginalized函数的作用是将节点标记为边缘化节点。
        边缘化节点是指在优化过程中,将该节点的估计值从优化变量中剔除,
        只保留其对应的误差项。这样做的好处是可以减少优化变量的数量,
        从而降低计算复杂度。同时,边缘化节点还可以提高优化的精度和鲁棒性,
        因为它可以将一些不确定的变量边缘化掉,从而减少误差的传递。需要注意
        的是,只有在节点的所有边都被边缘化后,该节点才能被边缘化。因此,在
        使用v->setMarginalized函数时,需要保证该节点的所有边都已经被正确地边缘化了。
        */
        v->setMarginalized(true);
        v->setEstimate(Eigen::Vector3d(x, y, z));
        optimizer.addVertex(v);
    }

    // 准备相机参数
    g2o::CameraParameters* camera = new g2o::CameraParameters(fx, Eigen::Vector2d(cx, cy), 0);
    camera->setId(0);
    optimizer.addParameter(camera);
    /*
    * 使用EdgeProjectXYZ2UV类型,设置边的两个顶点分别为特征点节点和位姿节点,测量值为(pts1[i].x, pts1[i].y)
    或(pts2[i].x, pts2[i].y),
    信息矩阵为单位矩阵,参数id为0,核函数为Huber核函数,并将其添加到优化器中。
    */
    // 准备边
    // 第一帧
    vector<g2o::EdgeProjectXYZ2UV*> edges;
    for (size_t i = 0; i < pts1.size(); i++)
    {//创建一个新的边对象,类型为g2o::EdgeProjectXYZ2UV
        g2o::EdgeProjectXYZ2UV* edge = new g2o::EdgeProjectXYZ2UV();
        //设置边的第一个顶点,即3D点的顶点。这里的i+2是因为在优化器中,前两个顶点是相机位姿的顶点,
        //所以3D点的顶点编号从2开始。dynamic_cast是将基类指针转换为派生类指针的方法。
        edge->setVertex(0, dynamic_cast<g2o::VertexSBAPointXYZ*>   (optimizer.vertex(i + 2)));
        //设置边的第二个顶点,即相机位姿的顶点。这里的相机位姿是指将相机从世界坐标系变换到相机坐标系的变换矩阵。
        edge->setVertex(1, dynamic_cast<g2o::VertexSE3Expmap*>     (optimizer.vertex(0)));
        //设置边的观测值,即特征点在图像上的坐标。
        edge->setMeasurement(Eigen::Vector2d(pts1[i].x, pts1[i].y));
        //设置边的信息矩阵,这里是单位矩阵。
        edge->setInformation(Eigen::Matrix2d::Identity());
        //设置边的参数,这里是指定边的参数块为0号参数块。
        edge->setParameterId(0, 0);
        // 核函数,设置边的核函数,这里是Huber核函数,用于鲁棒优化。
        edge->setRobustKernel(new g2o::RobustKernelHuber());
        //将边添加到优化器中。
        optimizer.addEdge(edge);
        //将边对象指针添加到一个vector中,方便后续的操作。
        edges.push_back(edge);
    }
    // 第二帧
    for (size_t i = 0; i < pts2.size(); i++)
    {
        g2o::EdgeProjectXYZ2UV* edge = new g2o::EdgeProjectXYZ2UV();
        edge->setVertex(0, dynamic_cast<g2o::VertexSBAPointXYZ*>   (optimizer.vertex(i + 2)));
        edge->setVertex(1, dynamic_cast<g2o::VertexSE3Expmap*>     (optimizer.vertex(1)));
        edge->setMeasurement(Eigen::Vector2d(pts2[i].x, pts2[i].y));
        edge->setInformation(Eigen::Matrix2d::Identity());
        edge->setParameterId(0, 0);
        // 核函数
        edge->setRobustKernel(new g2o::RobustKernelHuber());
        optimizer.addEdge(edge);
        edges.push_back(edge);
    }

    cout << "开始优化" << endl;
    optimizer.setVerbose(true);
    optimizer.initializeOptimization();
    optimizer.optimize(10);
    cout << "优化完毕" << endl;

    //我们比较关心两帧之间的变换矩阵
    /*
    * 从g2o优化器中获取id为1的VertexSE3Expmap类型的顶点v,并获取其位姿估计值pose,最后输出位姿矩阵。
    其中,g2o是一个用于非线性优化的C++库,VertexSE3Expmap是g2o中的一个顶点类型,表示一个带有平移和
    旋转的位姿,estimate()函数返回该顶点的位姿估计值,matrix()函数返回该位姿的变换矩阵。
    dynamic_cast是C++中的一种类型转换方式,用于将基类指针或引用转换为派生类指针或引用,
    这里将optimizer.vertex(1)返回的基类指针转换为VertexSE3Expmap类型的指针。
    */
    g2o::VertexSE3Expmap* v = dynamic_cast<g2o::VertexSE3Expmap*>(optimizer.vertex(1));
    Eigen::Isometry3d pose = v->estimate();
    cout << "Pose=" << endl << pose.matrix() << endl;

    // 以及所有特征点的位置
    for (size_t i = 0; i < pts1.size(); i++)
    {
        g2o::VertexSBAPointXYZ* v = dynamic_cast<g2o::VertexSBAPointXYZ*> (optimizer.vertex(i + 2));
        cout << "vertex id " << i + 2 << ", pos = ";
        Eigen::Vector3d pos = v->estimate();
        cout << pos(0) << "," << pos(1) << "," << pos(2) << endl;
    }

    // 估计inlier的个数
    int inliers = 0;
    for (auto e : edges)
    {
        e->computeError();
        // chi2 就是 error*\Omega*error, 如果这个数很大,说明此边的值与其他边很不相符
        if (e->chi2() > 1)
        {
            cout << "error = " << e->chi2() << endl;
        }
        else
        {
            inliers++;
        }
    }

    cout << "inliers in total points: " << inliers << "/" << pts1.size() + pts2.size() << endl;
    optimizer.save("ba.g2o");
    return 0;
}


int     findCorrespondingPoints(const cv::Mat& img1, const cv::Mat& img2, vector<cv::Point2f>& points1, vector<cv::Point2f>& points2)
{

    cv::Ptr<cv::FeatureDetector> orb = cv::ORB::create();
    //cv::ORB orb;
    vector<cv::KeyPoint> kp1, kp2;
    cv::Mat desp1, desp2;
    orb->detectAndCompute(img1, cv::Mat(), kp1, desp1);
    orb-> detectAndCompute(img2, cv::Mat(), kp2, desp2);

    cout << "分别找到了" << kp1.size() << "和" << kp2.size() << "个特征点" << endl;

    cv::Ptr<cv::DescriptorMatcher>  matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");

    double knn_match_ratio = 0.8;
    vector< vector<cv::DMatch> > matches_knn;
    matcher->knnMatch(desp1, desp2, matches_knn, 2);
    // 输出匹配点
    cv::Mat img_matches;
    cv::drawMatches(img1, kp1, img2, kp2, matches_knn, img_matches);
    cv::imshow("Matches", img_matches);
    cv::waitKey(0);

    vector< cv::DMatch > matches;
    for (size_t i = 0; i < matches_knn.size(); i++)
    {
        if (matches_knn[i][0].distance < knn_match_ratio * matches_knn[i][1].distance)
            matches.push_back(matches_knn[i][0]);
    }


    if (matches.size() <= 20) //匹配点太少
        return false;

    for (auto m : matches)
    {
        points1.push_back(kp1[m.queryIdx].pt);
        points2.push_back(kp2[m.trainIdx].pt);
    }

    return true;
}

5、总结

由于python的性能及机器人导航算法开源工具主流均采用C++,后续要做的工作就是进行c++和python的联合开发,将c++的优秀工具集成进python,这样就可以优势互补,形成一个可行的工程应用解决方案。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java反射机制开发经验总结

原创/朱季谦 反射是框架底层的灵魂&#xff0c;无论是Spring还是Dubbo&#xff0c;底层都大量使用到反射机制。 可以说&#xff0c;反射是Java开发当中一个绕不过的坎。 我曾经在实际项目当中有经常用到反射机制&#xff0c;故而将学会的反射用法做一些汇总笔记&#xff0c;当…

超详细的Jmeter随机参数各种搭配

前言 参数配置应该有三种场景&#xff0c;具体其他的我还没想到&#xff0c;那到底是哪三种呢&#xff1f;如果你也对这个问题感兴趣的话&#xff0c;那就让我们一起往下看吧&#xff01; 一、两个固定值之间随机生成一个值&#xff0c;应用场景没有限制 1、最简单的两个值之…

Spring lOC的注解使用与开发

Spring Spring IoC注解式开发为什么使用注解Spring注解的使用Value注解Autowired注解全注解式开发 Spring IoC注解式开发 为什么使用注解 注解的存在主要是为了简化XML的配置&#xff0c;注解的开发能大大提高我们的开发效率的&#xff0c;但它在一定程度上违背了OCP原则。 …

锐捷软件开机自启动

http://t.csdnimg.cn/h6k9R win键搜索任务计划程序 打开&#xff0c;在windows创建任务&#xff1a;

MAC地址_MAC地址格式_以太网的MAC帧_基础知识

MAC地址 全世界的每块网卡在出厂前都有一个唯一的代码,称为介质访问控制(MAC)地址 一.网络适配器(网卡) 要将计算机连接到以太网&#xff0c;需要使用相应的网络适配器(Adapter)&#xff0c;网络适配器一般简称为“网卡”。在计算机内部&#xff0c;网卡与CPU之间的通信&…

【智能家居项目】FreeRTOS版本——将裸机程序改造成FreeRTOS程序 | DHT11温湿度传感器

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 如上图所示是裸机版本的智能家居项目总体框架结构&#xff0c;这篇文章开始&#xff0c;本喵要…

服装供应链管理的革新利器—超高频RFID技术

一、行业概述 服装行业一直被视为低技术含量的劳动密集型产业&#xff0c;但实际上&#xff0c;科学技术在整个行业的发展中起着至关重要的作用&#xff0c;从服装面料的制作到服装设计、生产制作、物流到终端销售&#xff0c;科技力量贯穿于每一个环节。然而&#xff0c;传统…

Codeforces Round 908 (Div 2——AB)

A. Secret Sport 题目 AB二人玩游戏&#xff0c;每一局&#xff08;plays&#xff09;游戏会有一个获胜者&#xff0c;首先获胜X局&#xff08;play&#xff09;的玩家得一分&#xff08;赢得一轮sets&#xff09;。率先获得Y分的玩家获得最终胜利。 给你整场游戏的每局&…

树状图PPT怎么做?用这个树状图制作软件轻松拿捏!

在我们的日常工作和学习中&#xff0c;PPT已经成为了我们常见的展示方式。 在制作PPT时&#xff0c;树状图PPT是非常重要和常用的一种&#xff0c;并且在商务、教育等领域都非常受欢迎。那么&#xff0c;究竟什么是树状图PPT&#xff0c;如何使用树状图制作软件来快速绘制树状…

2022年12月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 小明想在开始表演之前向大家问好并做自我介绍,应运行下列哪个程序?( ) A: B: C: D: 答案:D 外观积木配合显示时间,才能看清楚内容。 第2题 舞台有两个不同的背景,小猫角色的哪个

网站提示Internal Server Error的原因和解决方法分享

解决方法 登陆FTP或文件管理器,检查站点目录文件权限设置,将文件夹权限设置为755,单个文件权限设置为644。 这样设置644后,问题就可以解决,同时也不会影响网站的所需要的写入权限,满足网站正常运行。如果你的站点中有很多权限都要需要设置,为了提高效率。 应用导致分析…

案例分析:大疆的电子产品说明书在组织分类结构方面值得借鉴的地方

大疆科技作为全球领先的无人机和航拍设备制造商&#xff0c;其产品众多&#xff0c;包括无人机、相机等电子产品。为了提供给用户详尽准确的产品信息和操作指导&#xff0c;大疆在其官方网站上建立了一个电子产品说明书的帮助中心。在组织分类结构方面&#xff0c;大疆的电子产…

【Spring】 Spring中的IoC(控制反转)

以往在定义业务层实现时&#xff0c;在指定具体地Dao时候需要具体地定义出其实现&#xff1a; public class BookServiceImpl implements BookService{private BookDao bookDao new BookDaoImpl();public void save(){bookDao.save()} }public class BookDaoImpl implements …

什么是美国服务器,有哪些优势,适用于什么场景?

​  在互联网发展的过程中&#xff0c;服务器扮演着至关重要的角色。而美国作为全球信息技术的中心&#xff0c;其服务器在全球范围内受到广泛关注。  美国服务器是指在美国本土机房搭建并运行的服务器。其拥有带宽大、优质硬件、售后运维好、位置优越、数据安全性高以及免备…

命令行中引导用户指定选择文档

背景 在python中&#xff0c;我们如果需要操作文档&#xff0c;则需要用户指定文档&#xff0c;那么&#xff0c;如何引导用户指定或者选择文档呢&#xff1f; 导入包 本次我们即将演示的代码&#xff0c;使用了 DebugInfo python包&#xff0c;我们需要导入 DebugInfo 包 …

接口返回的二进制音频数据转url

const data await textToVoice({tts1: { input: "你好吗", speed: "1.0" }, });//data 接口返回的二进制音频数据 const blobData new Blob([data], { type: "audio/mpeg" }); const url window.URL.createObjectURL(blobData); // url >…

Flex布局---看一篇就够了

1. flex布局是什么&#xff1f; flex是flexible Box的缩写&#xff0c;用来为盒装模型提供的最大的灵活性&#xff0c;任何一个容器都可以指定为flex布局。Flex布局称为flex容器&#xff0c;所有的子元素为容器成员Flex项目&#xff08;flex item&#xff09;&#xff1b; 当为…

城市生命线丨城市燃气管网监测系统功能效果

11月6日&#xff0c;福建泉州某小区发生煤气闪爆导致1死三伤&#xff0c;这起事故再次为我们敲响了城市燃气管网安全监测的警钟。在城市生命线的建设中&#xff0c;城市燃气管网监测系统以其独特的作用和价值&#xff0c;成为保障城市安全的重要一环。 根据应急管理部《全国城镇…

zookeeper的安装部署

目录 简介 Zookeeper架构设计及原理 1.Zookeeper定义 2.Zookeeper的特点 3.Zookeeper的基本架构 4.Zookeeper的工作原理 5.Zookeeper的数据模型 &#xff08;1&#xff09;临时节点 &#xff08;2&#xff09;顺序节点 &#xff08;3&#xff09;观察机制 Zookeeper集…

【知识增强】A Survey of Knowledge-Enhanced Pre-trained LM 论文笔记

A Survey of Knowledge-Enhanced Pre-trained Language Models Linmei Hu, Zeyi Liu, Ziwang Zhao, Lei Hou, Liqiang Nie, Senior Member, IEEE and Juanzi Li 2023年8月的一篇关于知识增强预训练模型的文献综述 论文思维导图 思维导图网页上看不清的话&#xff0c;可以存…