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

从零开始跑通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等开源点云库,技术交流、咨询可私信

相关文章:

  • 数学相关使用笔记
  • Kubernetes 使用 containerd 实现 GPU 支持及 GPU Operator 部署指南
  • KNOWLEDGE-BASED SYSTEMS(KBS期刊)投稿经验分享
  • JavaScript基础-局部作用域
  • 深度学习篇---MediaPipe 及其人体姿态估计模型详解
  • 加速pip下载:永久解决网络慢问题
  • 动态规划之完全背包问题
  • Day21 奇异值分解(SVD)全面解析
  • C++:this指针
  • 编译后的js文件如何跟进调试
  • 研发效率破局之道阅读总结(5)管理文化
  • AtCoder AT_abc405_d ABC405D - Escape Route
  • 使用FastAPI和React以及MongoDB构建全栈Web应用03 全栈开发快速入门
  • 每日脚本学习5.10 - XOR脚本
  • 论敏捷软件开发及其应用
  • 关于web3
  • PyTorch API 2 - 混合精度、微分、cpu、cuda、可视化
  • 模拟太阳系(C#编写的maui跨平台项目源码)
  • Maven 插件配置分层架构深度解析
  • 3.优惠券秒杀
  • 上海“量子城市”先导应用场景落地曹杨社区,提供哪些服务?
  • 网红街区如厕难,如何多管齐下补缺口?
  • 人民日报钟声:平等对话是解决大国间问题的正确之道
  • 中俄就应对美加征所谓“对等关税”等问题进行深入交流
  • 重视体重管理,筑牢健康基石
  • 蔡达峰:推动食品安全法全面有效实施,为维护人民群众身体健康提供有力法治保障