当前位置: 首页 > ai >正文

从零开始跑通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.xdnf.cn/news/5138.html

相关文章:

  • SpringBoot框架开发网络安全科普系统开发实现
  • 分布式事务快速入门
  • 小程序多线程实战
  • 功能齐全的菜谱管理器Tamari
  • [论文阅读]BadPrompt: Backdoor Attacks on Continuous Prompts
  • 23、Next.js:时空传送门——React 19 全栈框架
  • window 显示驱动开发-线性伸缩空间段
  • 简单网络交换、路由二
  • JavaWeb:JDBC
  • 关于ffmpeg的简介和使用总结
  • Kotlin Android LeakCanary内存泄漏检测实战
  • RT-Thread 深入系列 Part 5:物联网与网络应用实战
  • 视觉-语言基础模型作为高效的机器人模仿学习范式
  • 【STM32 学习笔记】I2C通信协议
  • STM32单片机的快速成长路径规划
  • 使用FastAPI和React以及MongoDB构建全栈Web应用04 MongoDB快速入门
  • 《React Native与Flutter:社交应用中用户行为分析与埋点统计的深度剖析》
  • 多层嵌套子查询
  • 区块链技术中的Java SE实战:从企业级应用到5大核心问题解析
  • 【Linux】用户管理
  • 【Docker系列】docker inspect查看容器部署位置
  • C++GO语言微服务之用户信息处理
  • Python爬虫实战:获取woodo网各类免费图片,积累设计素材
  • 计网学习笔记———网络
  • 【bibtex4word】在Word中高效转换bib参考文献,Texlive环境安装bibtex4word插件
  • LangGraph(三)——添加记忆
  • uniapp开发HarmonyOS NEXT应用之项目结构详细解读
  • 初识Linux · 传输层协议TCP · 下
  • 从文本到语义:BERT、Faiss 与 Elasticsearch 的协同实践
  • 【Java项目脚手架系列】第五篇:Spring Boot + MyBatis项目脚手架