从零开始跑通3DGS教程:(四)修改(缩放、空间变换)colmap生成的sfm结果

写在前面

  • 本文内容
    本文所属《从零开始跑通3DGS教程》系列文章;
    通过colmap进行的sfm的普通方式会丢失场景的物理尺度信息,并且并不在符合一般认知的坐标系下,本文将读取colmap生成的点云和相机pose,将其进行空间变换和缩放之后,重新保存,新保存的结果仍然可以被colmap读取以及进行后续的稠密重建以及3dgs等工作

  • 平台/环境
    colmap, open3d(python)

  • 转载请注明出处:
    https://blog.csdn.net/qq_41102371/article/details/146251947

目录

  • 写在前面
  • 系列文章
  • 准备
  • 代码

系列文章

  • 介绍
    从零开始跑通3DGS教程:介绍

  • 数据(采集):
    从零开始跑通3DGS教程:(一)数据(采集)

  • SFM(colmap)计算初始点云和相机pose:
    从零开始跑通3DGS教程:(二)SFM(colmap)计算初始点云和相机pose
    该步骤将通过structure from motion算法,计算出每张图像的pose,以及整个场景和目标的稀疏点云

  • 坐标系与尺度编辑(CloudCompare):
    从零开始跑通3DGS教程:(三)坐标系与尺度编辑(CloudCompare)

  • 修改sfm生成的原始数据
    从零开始跑通3DGS教程:(四)修改(缩放、空间变换)colmap生成的sfm结果

  • 3DGS训练:
    从零开始跑通3DGS教程:(五)3DGS训练

  • Gaussian Model编辑与渲染:
    从零开始跑通3DGS教程:(六)Gaussian Model编辑与渲染

准备

open3d

pip install open3d

colmap 读写数据的代码
https://github.com/colmap/colmap/blob/main/scripts/python/read_write_model.py

将原来的模型备份

cd 3dgs_tutorial/pro/truck/sparse
cp -r ./0 ./0_ori

代码

将read_write_model.py放在和modify_colmap.py同级目录下,修改YOUR_DIR;
使用通过cloudcompare得到的变换矩阵和scale,修改对应的trans_mat和scale,代码已中包含每一步的注释

cd 3dgs_tutorial/scripts
python modify_colmap.py

modify_colmap.py

import os
import sys
import copyimport numpy as np
import open3d as o3dabs_path = os.path.abspath(__file__)
sys.path.append(os.path.dirname(abs_path) + "/..")from read_write_model import (read_model,write_model,Point3D,Image,qvec2rotmat,rotmat2qvec,
)def colmapP3d2o3d(points3D):# get xyz, rgbxyz_list = [point.xyz for point in points3D.values()]xyz_array = np.array(xyz_list)rgb_list = [point.rgb for point in points3D.values()]rgb_array = np.array(rgb_list)# convert to Open3D pcdpcd = o3d.geometry.PointCloud()pcd.points = o3d.utility.Vector3dVector(xyz_array)# normalize to [0, 1]pcd.colors = o3d.utility.Vector3dVector(rgb_array / 255.0)# add normals# pcd.normals = o3d.utility.Vector3dVector(np.zeros_like(xyz_array))# o3d.visualization.draw_geometries([pcd])return pcddef o3d2colmapP3d(pcd_o3d, Points3D):# get original xyz, rgbxyz_array = np.asarray(pcd_o3d.points)rgb_array = np.asarray(pcd_o3d.colors) * 255.0# rgbrgb_array = np.round(rgb_array).astype(np.uint8)# convert to Colmap Points3Dfor i, (point_id, point) in enumerate(points3D.items()):new_point3D = Point3D(id=point.id,xyz=xyz_array[i],rgb=rgb_array[i],error=point.error,image_ids=point.image_ids,point2D_idxs=point.point2D_idxs,)points3D[point_id] = new_point3D# return Points3Ddef poses_to_pcd(poses):pcd = o3d.geometry.PointCloud()xyz = np.zeros((len(poses), 3))xyz = [pose[:3, 3] for pose in poses]pcd.points = o3d.utility.Vector3dVector(xyz)pcd.paint_uniform_color([0, 1, 0])return pcddef poses_vis(pose_list, trans_mat=np.eye(4), pcd_scene=o3d.geometry.PointCloud()):poses = copy.deepcopy(pose_list)pose_show = []# pose_show.append(o3d.geometry.TriangleMesh.create_coordinate_frame(2))scale = 0.5pose_cur = o3d.geometry.TriangleMesh.create_coordinate_frame(0.5 * scale)pose_cur.transform(poses[0])pose_show.append(pose_cur)for i in range(1, len(poses)):pose_cur = o3d.geometry.TriangleMesh.create_coordinate_frame(0.2 * scale)pose_cur.transform(poses[i])pose_show.append(pose_cur)# # show neighbor lines# lineset = pcd_tools.create_neighbor_lineset(poses)# pose_show.append(lineset)# show positionspcd = poses_to_pcd(poses)pose_show.append(pcd)pose_show.append(pcd_scene)coor_sfm = o3d.geometry.TriangleMesh.create_coordinate_frame(0.5 * scale)coor_ori = o3d.geometry.TriangleMesh.create_coordinate_frame(1.5 * scale)coor_ori.transform(np.linalg.inv(trans_mat))pose_show.append(coor_sfm)pose_show.append(coor_ori)# pose_show.append(coor_cam)o3d.visualization.draw_geometries(pose_show, "pose_show")if __name__ == "__main__":# ********** you need to modify the dir include data procceed by colmap **********dir_model = "/home/lixin/lixin/code/3dgs_tutorial/pro/truck/"dir_data = f"{dir_model}/sparse/0_ori"dir_data_new = f"{dir_model}/sparse/0"if not os.path.exists(dir_data_new):os.makedirs(dir_data_new)# ********** read data **********cameras, images, points3D = read_model(dir_data, ".bin")# convert colmap points3D to o3d pcdpcd_o3d = colmapP3d2o3d(points3D)o3d.io.write_point_cloud(f"{dir_data_new}/points3D_o3d_ori.ply",pcd_o3d,write_ascii=True,)# ********** modify data **********# scale and transform matrixmat_scale = np.eye(4)trans_mat = np.eye(4)  # identity matrixtrans_mat = np.array([[-0.709195, 0.038963, 0.703934, 0.667738],[-0.701549, 0.059836, -0.710104 ,0.452691],[-0.069788 ,-0.997448, -0.015101, 1.136864],[0.000000 ,0.000000, 0.000000, 1.000000]])scale = 1.063829787mat_scale[0, 0] = scalemat_scale[1, 1] = scalemat_scale[2, 2] = scaleprint(mat_scale)print(trans_mat)poses = []for idx, key in enumerate(images):extr = images[key]# get original poseR = qvec2rotmat(extr.qvec)T = np.array(extr.tvec)# transform posepose = np.eye(4)pose[:3, :3] = Rpose[:3, 3] = Tpose = np.linalg.inv(pose)pose = trans_mat @ pose# scale posepose_temp = mat_scale @ posepose[:3, 3] = pose_temp[:3, 3]# save poseposes.append(pose)# update image in colmap datapose_back = np.linalg.inv(pose)image_new = Image(camera_id=images[key].camera_id,id=images[key].id,name=images[key].name,point3D_ids=images[key].point3D_ids,qvec=rotmat2qvec(pose_back[:3, :3]),tvec=pose_back[:3, 3],xys=images[key].xys,)images[key] = image_new# tansform and scale pointspcd_o3d.transform(mat_scale @ trans_mat)# visualize posesposes_vis(poses, pcd_scene=pcd_o3d)print(len(pcd_o3d.points))print(len(pcd_o3d.colors))# save new pcdo3d.io.write_point_cloud(f"{dir_data_new}/points3D.ply",pcd_o3d,write_ascii=True,)# *********** save new data ***********# convert o3d pcd to colmap points3Do3d2colmapP3d(pcd_o3d, points3D)# update colmap datawrite_model(cameras, images, points3D, path=dir_data_new, ext=".bin")# read new datacameras, images, points3D_new = read_model(dir_data_new, ".bin")pcd_o3d_new = colmapP3d2o3d(points3D_new)o3d.visualization.draw_geometries([pcd_o3d_new], "pcd_o3d_new")

然后用cloudcompare重新打开sparse/0/points3D.ply,就会看到尺度和坐标系都变成我们想要的了
在这里插入图片描述

主要做激光/影像三维重建,配准、分割等常用点云算法,熟悉open3d、pcl等开源点云库,技术交流、咨询可私信

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

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

相关文章

RK3568-OpenHarmony(1) : OpenHarmony 5.1的编译

概述: 本文主要描述了,如何在ubuntu-20.04操作系统上,编译RK3568平台的OpenHarmony 5.1版本。 搭建编译环境 a. 安装软件包 sudo apt-get install git-lfs ruby genext2fs build-essential git curl libncurses5-dev libncursesw5-dev openjdk-11-jd…

vue+tsc+noEmit导致打包报TS类型错误问题及解决方法

项目场景: 提示:这里简述项目相关背景: 当我们新建vue3项目,package.json文件会自动给我添加一些配置选项,这写选项基本没有问题,但是在实际操作过程中,当项目越来越复杂就会出现问题,本文给大家分享vuetscnoEmit导致打包报TS类型错误问题及…

Js 判断浏览器cookie 是否启用

验证时 google浏览器 135.0.7049.117 不生效 cookie.html <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><title>Cookie 检测</title> </head> <body><h1>检测是否启用 Cookie<…

Lambda表达式解读

本文通过具体案例演示函数式接口Function<T,R>的三种实现方式演变过程。 一、传统匿名内部类实现 Integer resInt1 t1(new Function<String, Integer>() {Overridepublic Integer apply(String s) {int i Integer.parseInt(s);return i;} });实现特点&#xff1…

等价无穷小代换

理解&#xff1a; 函数某一点的值可以使用泰勒展开式表示&#xff0c;&#xff08;低阶无穷小 高阶无穷小&#xff09;&#xff0c;主要有低阶无穷小决定。 计算极限的时候&#xff1a; 乘除关系随便换&#xff0c;不影响各个式子的低阶无穷小加减关系&#xff1a; &#xf…

护网HVV初级蓝队面试题总结

struts2原理特征 原理:默认的content-type解析器会把用户传来的数据直接当成代码执行&#xff0c;造成rce特征:ognl表达式&#xff0c;memberaccess字段&#xff0c;可以通过catalina日志过滤关键信息查找攻击特征ongl表达式可以被当作代码执行&#xff0c;其中的类为defaulta…

Web3 实战项目项目部署到 GitHub 和上线预览的完整指南

目录 &#x1f680; 一、部署到 GitHub ✅ 前置准备 &#x1f9f1; 部署步骤&#xff1a; 1. 创建一个 GitHub 仓库 2. 上传项目文件 方法一&#xff1a;使用 Git 命令行 方法二&#xff1a;直接上传 &#x1f310; 二、通过 GitHub Pages 免费上线 DApp&#xff08;前端…

3.优惠券秒杀

3.1 全局唯一 ID 当用户抢购时&#xff0c;就会生成订单并保存到 tb_voucher_order 这张表中&#xff0c;而订单表如果使用数据库自增 ID 就存在一些问题&#xff1a; id 的规律性太明显 受单表数据量的限制 场景分析一&#xff1a;如果我们的 id 具有太明显的规则&#xf…

AI日报 · 2025年5月07日|谷歌发布 Gemini 2.5 Pro 预览版 (I/O 版本),大幅提升编码与视频理解能力

1、谷歌发布 Gemini 2.5 Pro 预览版 (I/O 版本)&#xff0c;大幅提升编码与视频理解能力 谷歌于5月6日提前发布 Gemini 2.5 Pro 预览版 (I/O 版本)&#xff0c;为开发者带来更强编码能力&#xff0c;尤其优化了前端与UI开发、代码转换及智能体工作流构建&#xff0c;并在WebDe…

Python+ffmpeg 实现给视频添加字幕

创作灵感 孩子学校经常留作业&#xff0c;需要提交一段录制的视频&#xff0c;视频上要求添加学校、班级、姓名等信息的字幕&#xff0c;手机自带的相机软件字幕添加位置要么只能添加在视频正中&#xff0c;要么无法添加多行文本&#xff0c;要么只能添加在片头或者片尾&#…

OpenLayers 精确经过三个点的曲线绘制

OpenLayers 精确经过三个点的曲线绘制 根据您的需求&#xff0c;我将提供一个使用 OpenLayers 绘制精确经过三个指定点的曲线解决方案。对于三个点的情况&#xff0c;我们可以使用 二次贝塞尔曲线 或 三次样条插值&#xff0c;确保曲线精确通过所有控制点。 实现方案 下面是…

Django缓存框架API

这里写自定义目录标题 访问缓存django.core.cache.cachesdjango.core.cache.cache 基本用法cache.set(key, value, timeoutDEFAULT_TIMEOUT, versionNone)cache.get(key, defaultNone, versionNone)cache.add(key, value, timeoutDEFAULT_TIMEOUT, versionNone)cache.get_or_se…

Linux系统管理与编程17:自动化部署ftp服务

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 #virtual用户管理&#xff1a;passerbyA、captain和admin三个虚拟用户 # passerbyA只能看&#xff0c;captain可看读写上传&#xff0c;但不能删除。admin全部权限 [rootshell shell]…

2025python学习笔记

一.Python语言基础入门 第一章 01.初识Python Python的起源&#xff1a; 1989年&#xff0c;为了打发圣诞节假期&#xff0c;Gudio van Rossum吉多范罗苏姆&#xff08;龟叔&#xff09;决心开发一个新的解释程序&#xff08;Python维形&#xff09;1991年&#xff0c;第一个…

STM32单片机的快速成长路径规划

一、基础准备阶段&#xff08;1-2周&#xff09; C语言核心技能 重点掌握&#xff1a;指针操作、结构体、枚举、位操作、函数指针&#xff08;回调函数基础&#xff09;实践项目&#xff1a;通过51单片机或STM8完成LED控制、按键检测等基础项目&#xff0c;熟悉寄存器配置和调试…

torch.nn.init.uniform_

nn.init.uniform_ 是 PyTorch 中用于初始化张量&#xff08;tensor&#xff09;的一个函数&#xff0c;它的作用是将张量的值填充为从均匀分布中采样的随机数。 详细说明&#xff1a; 函数&#xff1a; torch.nn.init.uniform_(tensor, a0., b1.)tensor&#xff1a;需要被初始…

Spring MVC中跨域问题处理

在Spring MVC中处理跨域问题可以通过以下几种方式实现&#xff0c;确保前后端能够正常通信&#xff1a; 方法一&#xff1a;使用 CrossOrigin 注解 适用于局部控制跨域配置&#xff0c;直接在Controller或方法上添加注解。 示例代码&#xff1a; RestController CrossOrigin…

基本句子结构

以下是英语句子五种基本结构的详细解释&#xff0c;并附上系动词的全面分类及示例&#xff1a; ​1. 主谓结构&#xff08;SV&#xff09;​ ​结构&#xff1a;主语&#xff08;Subject&#xff09; 不及物动词&#xff08;Intransitive Verb&#xff09;​核心&#xff1a;…

游戏引擎学习第264天:将按钮添加到分析器

回顾并为今天的工作做铺垫 随着时间的推移&#xff0c;我们的分析器&#xff08;profiler&#xff09;变得越来越强大。我通常会问大家是否记得我们要做什么&#xff0c;今天我们要做的似乎是按钮相关的功能。 今天的目标是实现按钮功能。我们从昨天留下的地方继续&#xff0…

大节点是选择自建机房还是托管机房

选择PCDN大节点自建机房还是托管机房&#xff0c;需综合考量资金实力、技术能力、运维需求、业务规模及合规要求。以下为具体分析&#xff1a; 自建机房的适用场景与考量因素 资金与技术门槛高 自建机房需投入服务器、存储、网络设备等硬件&#xff0c;以及机房建设、电力、散…