在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::EdgeProjectXYZ2UVg2o::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,这样就可以优势互补,形成一个可行的工程应用解决方案。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Behave介绍和快速示例

Behave是一个用于行为驱动开发 (Behavior-Driven Development, BDD) 的 Python 库。使用 Behave&#xff0c;可以编写自然语言格式的使用场景来描述软件的行为&#xff0c;然后用 Python 实现这些场景下的步骤&#xff0c;形成可直接运行的测试。 Behave的目标是帮助用户、开发…

【入门Flink】- 09Flink水位线Watermark

在窗口的处理过程中&#xff0c;基于数据的时间戳&#xff0c;自定义一个“逻辑时钟”。这个时钟的时间不会自动流逝&#xff1b;它的时间进展&#xff0c;就是靠着新到数据的时间戳来推动的。 什么是水位线 用来衡量事件时间进展的标记&#xff0c;就被称作“水位线”&#x…

你不懂API接口是什么?怎么和程序员做朋友

说到开发平台就一定离不开接口&#xff0c;作为PM&#xff0c;我们不需要对接口了解的特别细。只需要知道接口是什么&#xff0c;有什么用&#xff0c;有哪些要素就行。 1. 接口是什么 (1) 硬件接口 生活中我们经常会接触接口&#xff0c;最常见的就是HDMI接口和USB接口&…

计算机毕业设计选题推荐-公共浴池微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

我这些年对于自动化测试的理解

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

环境配置 | Git的安装及配置[图文详情]

Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从小到大的项目版本管理。下面介绍了基础概念及详细的用图文形式介绍一下git安装过程. 目录 1.Git基础概念 2.Git的下载及安装 3.常见的git命令 Git高级技巧 Git与团队协作 1.Git基础概念 仓库&#…

沉浸式航天vr科普馆VR太空主题馆展示

科普教育从小做起&#xff0c;现在我们的很多地方小孩子游乐体验不单单只有草坪玩耍体验&#xff0c;还有很多科普知识的体验馆和游玩馆。虽然现在我们还不能真实的上太空或者潜入海底&#xff0c;但是这些现在已经可以逼真的展示在我们面前。通过一种虚拟现实技术手段。人们带…

在3+1的方向上展开结构加法4a3+4a14

4a3 4a14 - - 1 - - - - - - - - - - - - - 1 1 1 - 1 1 - 1 - - 1 - - - 要求得到的图片只能有4个点&#xff0c;并且需要最大限度的保留4a3和4a14两张图片的内在结构特征。 4个点的结构总可以认为是3个点的结构1合成的 - - 1 - - …

只使用JS怎么给静态页面网站添加站内全局搜索功能?

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 背景 静态页面通常由HTML、CSS 和 JavaScript…

酷开系统 酷开科技,将家庭娱乐推向新高潮

在当今数字化时代&#xff0c;家庭娱乐已经成为人们日常生活中不可或缺的一部分。如果你厌倦了传统的家庭娱乐方式&#xff0c;想要一种全新的、充满惊喜的娱乐体验&#xff0c;那么&#xff0c;不妨进入到酷开科技的世界&#xff0c;作为智能电视行业领军企业&#xff0c;酷开…

销售管道管理软件推荐:提升销售业绩与效率

在企业中销售部门扮演着锐意进取的尖刀部队的角色&#xff0c;肩负着拓展公司发展领土的重要责任。销售管理是一个漫长而复杂的过程&#xff0c;需要经历潜在的商机、联系跟进、签订合同以及赢得订单等关键里程碑&#xff0c;无论是面向C端用户的销售还是面向企业复杂产品的销售…

使用jmeter+ant进行接口自动化测试(数据驱动)

本次接着介绍如何利用apache-ant执行测试用例并生成HTML格式测试报告 ①下载安装 apache-ant-1.9.9&#xff0c;配置环境变量 如下方式检验安装成功 ②安装好ant后&#xff0c;把jmeter中extras目录下的ant-jmeter-1.1.1.jar 文件copy到ant安装目录下的lib文件夹中 ③配置ant…

【cfeng-work】架构演进和漫谈

架构漫谈和入门 内容管理 intro分层架构MVC模式分层架构大数据时代的复杂架构 前端架构后端架构运维端架构持续演进变化 本文主要是自己接触架构的一些输出漫谈 cfeng 在work中某次负责了后端一个服务的上线&#xff0c;多个模块一起上&#xff0c;结果上线失败&#xff0c;幸运…

【MySQL】数据库——库操作

文章目录 1. 创建数据库[IF NOT EXISTS] 的使用 2. 删除库3. 数据库的编码问题查看系统默认支持的字符集查看系统默认支持的校验集只查看 database的校验集指定编码创建数据库修改字符集修改校验集验证规则对数据库的影响utf8_general_ci ——不区分大小写utf8_bin ——区分大小…

什么是会话固定以及如何在 Node.js 中防止它

什么是会话固定以及如何在 Node.js 中防止它 在深入讨论之前&#xff0c;我们需要了解会话是什么以及会话身份验证如何工作。 什么是会话&#xff1f; 正如我们所知&#xff0c;HTTP 请求是无状态的&#xff0c;这意味着当我们发送登录请求时&#xff0c;并且我们有有效的用…

智能售货柜:小本投资的不二之选

智能售货柜&#xff1a;小本投资的不二之选 智能售货柜的运营优势在于&#xff1a;一是降低运营成本&#xff0c;不需要大量员工&#xff1b;二是具备自动识别和智能结算功能&#xff0c;提高运营效率&#xff1b;三是提供数据分析&#xff0c;优化产品和服务。相比传统零售店&…

教育案例分享 | 安全狗云安全体系为高校提升立体化纵深防御能力

一、客户情况 某高校有服务器500台&#xff0c;对外站点200个&#xff0c;核心交换流量20G。 二、客户痛点 校园网系统分类较多&#xff0c;并且每类网站中安全级重要程度又各不相同&#xff0c;同时有多个网络出口(如&#xff1a;教育网、电信网、移动网等)&#xff0c;二级学…

论文阅读——RetNet

transformer的问题&#xff1a;计算量大&#xff0c;占用内存大&#xff0c;不好部署。 所以大家在找能解决办法&#xff0c;既能和transformer表现一样好&#xff0c;又能在推理阶段计算复杂度很低。 这些方法大概分类三类&#xff1a;一是代替transformer非线性注意力机制的…

【Linux】文件系统中inode与软硬链接以及读写权限问题

文章目录 前言一、 简单理解文件系统二、文件操作具体步骤1.新建文件2.删除文件3.查找文件 三、目录的重新理解1.目录下没有w权限&#xff0c;无法对其下的文件进行创建与删除2.目录下没有r权限&#xff0c;无法对其下的文件进行查看3.目录下没有x权限&#xff0c;无法进入这个…

内网Jenkins 部署.net(dotnet)项目

一、前置条件 内网部署Jenkins&#xff0c;并安装好所需插件 此篇内容需承接内网搭建Jenkins自动化远程部署项目到Windows服务器_jenkins内网安装-CSDN博客 &#xff0c;才更好操作与理解 二、在Jenkins中创建项目 三、配置项目 General Source Code Management Build Envi…