├── LICENSE ├── README.md ├── __pycache__ ├── cam_utils.cpython-310.pyc ├── helper_train.cpython-310.pyc └── train_gui_utils.cpython-310.pyc ├── arguments ├── N3DV │ ├── coffee_martini.py │ ├── cook_spinach.py │ ├── cut_roasted_beef.py │ ├── flame_salmon_1.py │ ├── flame_steak.py │ └── sear_steak.py ├── Technicolor │ ├── Birthday.py │ ├── Fabien.py │ ├── Fatma.py │ ├── Hands.py │ ├── Painter.py │ ├── Theater.py │ └── Train.py ├── __init__.py └── __pycache__ │ └── __init__.cpython-310.pyc ├── cam_utils.py ├── convert.py ├── data_tools ├── colmap2nerf.py ├── interactive_invoke.py └── phone_catch.py ├── experimental_logs ├── README.md ├── arxiv_logs.txt └── asset │ ├── coffee_martini_node.zip │ ├── cook_spinach_node.zip │ ├── cut_roasted_beef_node.zip │ ├── flame_salmon_1_node.zip │ ├── flame_steak_node.zip │ ├── img │ ├── arg_cook_spinach.jpg │ └── arg_cut_roasted_beef.jpg │ └── sear_steak_node.zip ├── gaussian_renderer ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── __init__.cpython-38.pyc │ ├── network_gui.cpython-310.pyc │ └── network_gui.cpython-38.pyc └── network_gui.py ├── imgs └── Framework.jpg ├── install_env.sh ├── lap_deform.py ├── lpipsPyTorch ├── __init__.py ├── __pycache__ │ └── __init__.cpython-310.pyc └── modules │ ├── __pycache__ │ ├── lpips.cpython-310.pyc │ ├── networks.cpython-310.pyc │ └── utils.cpython-310.pyc │ ├── lpips.py │ ├── networks.py │ └── utils.py ├── main.py ├── metrics.py ├── render.py ├── requirements.txt ├── scene ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── __init__.cpython-37.pyc │ ├── __init__.cpython-38.pyc │ ├── cameras.cpython-310.pyc │ ├── cameras.cpython-38.pyc │ ├── cameras_st.cpython-310.pyc │ ├── colmap_loader.cpython-310.pyc │ ├── colmap_loader.cpython-37.pyc │ ├── colmap_loader.cpython-38.pyc │ ├── dataset_readers.cpython-310.pyc │ ├── dataset_readers.cpython-37.pyc │ ├── dataset_readers.cpython-38.pyc │ ├── deform_model.cpython-310.pyc │ ├── deform_model.cpython-38.pyc │ ├── gaussian_model.cpython-310.pyc │ └── gaussian_model.cpython-38.pyc ├── cameras.py ├── cameras_st.py ├── colmap_loader.py ├── dataset_readers.py ├── deform_model.py └── gaussian_model.py ├── script ├── Readme.md ├── __pycache__ │ └── pre_immersive_distorted.cpython-310.pyc ├── n3d_process.py ├── pre_immersive_distorted.py ├── technicolor_process.py └── utils_pre.py ├── start_order.sh ├── submodules ├── diff-gaussian-rasterization │ ├── .gitignore │ ├── .gitmodules │ ├── CMakeLists.txt │ ├── LICENSE.md │ ├── README.md │ ├── cuda_rasterizer │ │ ├── auxiliary.h │ │ ├── backward.cu │ │ ├── backward.h │ │ ├── config.h │ │ ├── forward.cu │ │ ├── forward.h │ │ ├── rasterizer.h │ │ ├── rasterizer_impl.cu │ │ └── rasterizer_impl.h │ ├── diff_gaussian_rasterization │ │ └── __init__.py │ ├── ext.cpp │ ├── rasterize_points.cu │ ├── rasterize_points.h │ ├── setup.py │ └── third_party │ │ └── stbi_image_write.h └── simple-knn │ ├── LICENSE.md │ ├── build │ ├── lib.linux-x86_64-cpython-310 │ │ └── simple_knn │ │ │ └── _C.cpython-310-x86_64-linux-gnu.so │ └── temp.linux-x86_64-cpython-310 │ │ ├── .ninja_deps │ │ ├── .ninja_log │ │ ├── build.ninja │ │ ├── ext.o │ │ ├── simple_knn.o │ │ └── spatial.o │ ├── ext.cpp │ ├── setup.py │ ├── simple_knn.cu │ ├── simple_knn.egg-info │ ├── PKG-INFO │ ├── SOURCES.txt │ ├── dependency_links.txt │ └── top_level.txt │ ├── simple_knn.h │ ├── simple_knn │ └── .gitkeep │ ├── spatial.cu │ └── spatial.h ├── technicolor.sh ├── test.sh ├── thirdparty ├── colmap │ └── pre_colmap.py └── gaussian_splatting │ ├── helper3dg.py │ └── utils │ └── my_utils.py ├── train.py ├── train_gui_utils.py └── utils ├── __pycache__ ├── camera_utils.cpython-310.pyc ├── camera_utils_st.cpython-310.pyc ├── deform_utils.cpython-310.pyc ├── depth_utils.cpython-310.pyc ├── general_utils.cpython-310.pyc ├── graphics_utils.cpython-310.pyc ├── graphics_utils.cpython-37.pyc ├── image_utils.cpython-310.pyc ├── logger_hook.cpython-310.pyc ├── loss_utils.cpython-310.pyc ├── loss_utils.cpython-37.pyc ├── pose_utils.cpython-310.pyc ├── rigid_utils.cpython-310.pyc ├── sh_utils.cpython-310.pyc ├── system_utils.cpython-310.pyc ├── system_utils.cpython-37.pyc └── time_utils.cpython-310.pyc ├── arap_deform.py ├── bezier.py ├── camera_utils.py ├── camera_utils_st.py ├── deform_utils.py ├── dual_quaternion.py ├── general_utils.py ├── graphics_utils.py ├── image_utils.py ├── interactive_utils.py ├── logger_hook.py ├── loss_utils.py ├── other_utils.py ├── pickle_utils.py ├── pose_utils.py ├── preprocess.py ├── rigid_utils.py ├── sh_utils.py ├── system_utils.py ├── time_utils.py └── vis_utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Ellison King 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /__pycache__/cam_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/__pycache__/cam_utils.cpython-310.pyc -------------------------------------------------------------------------------- /__pycache__/helper_train.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/__pycache__/helper_train.cpython-310.pyc -------------------------------------------------------------------------------- /__pycache__/train_gui_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/__pycache__/train_gui_utils.cpython-310.pyc -------------------------------------------------------------------------------- /arguments/N3DV/coffee_martini.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type=None, # N3DV 3 | # densify_grad_threshold = 0.0002, # 0.0002 4 | #scaling_lr = 0.003, # 0.001 5 | # position_lr_init = 0.00016/5, #1.6e-5, #0.00016/5,#0.00016/5, #1.6e-5 #0.00016, 6 | # rotation_lr = 1e-4, #0.001, 7 | # movelr = 3.5/14, 8 | position_lr_init = 0.00016, 9 | flag=0 10 | ) 11 | 12 | OptimizationParams = dict( 13 | 14 | ) 15 | PipelineParams = dict() 16 | -------------------------------------------------------------------------------- /arguments/N3DV/cook_spinach.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type=None, # N3DV 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | # densify_grad_threshold=0.0002, # 0.0002 8 | # scaling_lr=0.001, # 0.001 9 | # position_lr_init=1.6e-5/10, #1.6e-5 # 0.00016/5, 10 | 11 | densify_grad_threshold=0.0002, # 0.0002 12 | # scaling_lr=0.005, # 0.001 13 | position_lr_init=1.6e-4, #1.6e-5 # 0.00016/5, 14 | flag=0 15 | ) 16 | PipelineParams = dict() 17 | -------------------------------------------------------------------------------- /arguments/N3DV/cut_roasted_beef.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type=None, # N3DV 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | 8 | #scaling_lr=0.0001, 9 | 10 | #rotation_lr=1e-4, 11 | #movelr=3.5 / 14, 12 | flag=0 13 | ) 14 | PipelineParams = dict() 15 | -------------------------------------------------------------------------------- /arguments/N3DV/flame_salmon_1.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type=None, # N3DV 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | # densify_grad_threshold=0.0002, # 0.0002 8 | # scaling_lr=0.001, # 0.001 9 | # position_lr_init=0.00016/5, # 1.6e-5 # 10 | # densify_grad_threshold=0.0005, 11 | 12 | #scaling_lr=0.001, 13 | #position_lr_init=1.6e-4, 14 | #densify=2 15 | flag=0 16 | ) 17 | PipelineParams = dict() 18 | -------------------------------------------------------------------------------- /arguments/N3DV/flame_steak.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type=None, # N3DV 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | 8 | #scaling_lr=0.0001, # 0.001 9 | #rotation_lr=1e-4, # 0.001, 10 | #movelr=3.5 / 14, 11 | #position_lr_init=1.6e-5, 12 | flag=0 13 | ) 14 | PipelineParams = dict() 15 | -------------------------------------------------------------------------------- /arguments/N3DV/sear_steak.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type=None, # N3DV 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | # densify_grad_threshold=0.0002, # 0.0002 8 | # scaling_lr=0.001, # 0.001 9 | # position_lr_init=0.00016/5, #0.00016/5, 10 | flag=0 11 | ) 12 | PipelineParams = dict() 13 | -------------------------------------------------------------------------------- /arguments/Technicolor/Birthday.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | densify_grad_threshold=0.0002, # 0.0002 8 | scaling_lr=0.001, # 0.001 9 | position_lr_init=0.00016, # 1.6e-5 # 10 | flag=0 11 | ) 12 | PipelineParams = dict() 13 | -------------------------------------------------------------------------------- /arguments/Technicolor/Fabien.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | # densify_grad_threshold=0.0002, # 0.0002 8 | # scaling_lr=0.001, # 0.001 9 | # position_lr_init=1.6e-5/5, #0.00016, # 1.6e-5 # 10 | densify_grad_threshold=0.0005, # 0.0002 11 | scaling_lr=0.001/10, # 0.001 12 | position_lr_init=1.6e-5 / 10, # 0.00016, # 1.6e-5 # 13 | flag=0 14 | ) 15 | PipelineParams = dict() 16 | -------------------------------------------------------------------------------- /arguments/Technicolor/Fatma.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | densify_grad_threshold=0.0002, # 0.0002 8 | scaling_lr=0.001, # 0.001 9 | position_lr_init=1.6e-5/5, #0.00016, # 1.6e-5 # 10 | flag=0 11 | ) 12 | PipelineParams = dict() 13 | -------------------------------------------------------------------------------- /arguments/Technicolor/Hands.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | densify_grad_threshold=0.0002, # 0.0002 8 | scaling_lr=0.001, # 0.001 9 | position_lr_init=1.6e-5/5, #0.00016, # 1.6e-5 # 10 | flag=0 11 | ) 12 | PipelineParams = dict() 13 | -------------------------------------------------------------------------------- /arguments/Technicolor/Painter.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | densify_grad_threshold=0.0002, # 0.0002 8 | scaling_lr=0.001, # 0.001 9 | position_lr_init=0.00016, # 1.6e-5 # 10 | flag=0 11 | ) 12 | PipelineParams = dict() 13 | -------------------------------------------------------------------------------- /arguments/Technicolor/Theater.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | densify_grad_threshold=0.0005, # 0.0002 8 | scaling_lr=0.001/10, # 0.001 9 | position_lr_init=1.6e-5/10, # 1.6e-5 # 10 | 11 | feature_lr = 0.0025/5, 12 | opacity_lr = 0.05/5, 13 | rotation_lr = 0.001/5, 14 | 15 | # densify_grad_threshold=0.0005, # 0.0002 16 | # position_lr_init=1.6e-5 / 5, # 0.00016, # 1.6e-5 # 17 | flag=0 18 | 19 | ) 20 | PipelineParams = dict() 21 | -------------------------------------------------------------------------------- /arguments/Technicolor/Train.py: -------------------------------------------------------------------------------- 1 | ModelParams = dict( 2 | dataset_type='technicolor', 3 | 4 | ) 5 | 6 | OptimizationParams = dict( 7 | densify_grad_threshold=0.0002, # 0.0002 8 | scaling_lr=0.001, # 0.001 9 | position_lr_init=1.6e-5/5, #0.00016, # 1.6e-5 # 10 | flag=0 11 | ) 12 | PipelineParams = dict() 13 | -------------------------------------------------------------------------------- /arguments/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/arguments/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /cam_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.spatial.transform import Rotation as R 3 | 4 | import torch 5 | 6 | def dot(x, y): 7 | if isinstance(x, np.ndarray): 8 | return np.sum(x * y, -1, keepdims=True) 9 | else: 10 | return torch.sum(x * y, -1, keepdim=True) 11 | 12 | 13 | def length(x, eps=1e-20): 14 | if isinstance(x, np.ndarray): 15 | return np.sqrt(np.maximum(np.sum(x * x, axis=-1, keepdims=True), eps)) 16 | else: 17 | return torch.sqrt(torch.clamp(dot(x, x), min=eps)) 18 | 19 | 20 | def safe_normalize(x, eps=1e-20): 21 | return x / length(x, eps) 22 | 23 | 24 | def look_at(campos, target, opengl=True): 25 | # campos: [N, 3], camera/eye position 26 | # target: [N, 3], object to look at 27 | # return: [N, 3, 3], rotation matrix 28 | if not opengl: 29 | # camera forward aligns with -z 30 | forward_vector = safe_normalize(target - campos) 31 | up_vector = np.array([0, 1, 0], dtype=np.float32) 32 | right_vector = safe_normalize(np.cross(forward_vector, up_vector)) 33 | up_vector = safe_normalize(np.cross(right_vector, forward_vector)) 34 | else: 35 | # camera forward aligns with +z 36 | forward_vector = safe_normalize(campos - target) 37 | up_vector = np.array([0, 1, 0], dtype=np.float32) 38 | right_vector = safe_normalize(np.cross(up_vector, forward_vector)) 39 | up_vector = safe_normalize(np.cross(forward_vector, right_vector)) 40 | R = np.stack([right_vector, up_vector, forward_vector], axis=1) 41 | return R 42 | 43 | 44 | # elevation & azimuth to pose (cam2world) matrix 45 | def orbit_camera(elevation, azimuth, radius=1, is_degree=True, target=None, opengl=True): 46 | # radius: scalar 47 | # elevation: scalar, in (-90, 90), from +y to -y is (-90, 90) 48 | # azimuth: scalar, in (-180, 180), from +z to +x is (0, 90) 49 | # return: [4, 4], camera pose matrix 50 | if is_degree: 51 | elevation = np.deg2rad(elevation) 52 | azimuth = np.deg2rad(azimuth) 53 | x = radius * np.cos(elevation) * np.sin(azimuth) 54 | y = - radius * np.sin(elevation) 55 | z = radius * np.cos(elevation) * np.cos(azimuth) 56 | if target is None: 57 | target = np.zeros([3], dtype=np.float32) 58 | campos = np.array([x, y, z]) + target # [3] 59 | T = np.eye(4, dtype=np.float32) 60 | T[:3, :3] = look_at(campos, target, opengl) 61 | T[:3, 3] = campos 62 | return T 63 | 64 | 65 | class OrbitCamera: 66 | def __init__(self, W, H, r=2, fovy=60, near=0.01, far=100): 67 | self.W = W 68 | self.H = H 69 | self.radius = r # camera distance from center 70 | self.fovy = np.deg2rad(fovy) # deg 2 rad 71 | self.near = near 72 | self.far = far 73 | self.center = np.array([0, 0, 0], dtype=np.float32) # look at this point 74 | # self.rot = R.from_matrix(np.eye(3)) 75 | self.rot = R.from_matrix(np.array([[1., 0., 0.,], 76 | [0., 0., -1.], 77 | [0., 1., 0.]])) 78 | self.up = np.array([0, 1, 0], dtype=np.float32) # need to be normalized! 79 | self.side = np.array([1, 0, 0], dtype=np.float32) 80 | 81 | @property 82 | def fovx(self): 83 | return 2 * np.arctan(np.tan(self.fovy / 2) * self.W / self.H) 84 | 85 | @property 86 | def campos(self): 87 | return self.pose[:3, 3] 88 | 89 | # pose (c2w) 90 | @property 91 | def pose(self): 92 | # first move camera to radius 93 | res = np.eye(4, dtype=np.float32) 94 | res[2, 3] = self.radius # opengl convention... 95 | # rotate 96 | rot = np.eye(4, dtype=np.float32) 97 | rot[:3, :3] = self.rot.as_matrix() 98 | res = rot @ res 99 | # translate 100 | res[:3, 3] -= self.center 101 | return res 102 | 103 | # view (w2c) 104 | @property 105 | def view(self): 106 | return np.linalg.inv(self.pose) 107 | 108 | # projection (perspective) 109 | @property 110 | def perspective(self): 111 | y = np.tan(self.fovy / 2) 112 | aspect = self.W / self.H 113 | return np.array( 114 | [ 115 | [1 / (y * aspect), 0, 0, 0], 116 | [0, -1 / y, 0, 0], 117 | [ 118 | 0, 119 | 0, 120 | -(self.far + self.near) / (self.far - self.near), 121 | -(2 * self.far * self.near) / (self.far - self.near), 122 | ], 123 | [0, 0, -1, 0], 124 | ], 125 | dtype=np.float32, 126 | ) 127 | 128 | # intrinsics 129 | @property 130 | def intrinsics(self): 131 | focal = self.H / (2 * np.tan(self.fovy / 2)) 132 | return np.array([focal, focal, self.W // 2, self.H // 2], dtype=np.float32) 133 | 134 | @property 135 | def mvp(self): 136 | return self.perspective @ np.linalg.inv(self.pose) # [4, 4] 137 | 138 | def orbit(self, dx, dy): 139 | # rotate along camera up/side axis! 140 | side = self.rot.as_matrix()[:3, 0] 141 | up = self.rot.as_matrix()[:3, 1] 142 | rotvec_x = up * np.radians(-0.05 * dx) 143 | rotvec_y = side * np.radians(-0.05 * dy) 144 | self.rot = R.from_rotvec(rotvec_x) * R.from_rotvec(rotvec_y) * self.rot 145 | 146 | def scale(self, delta): 147 | self.radius *= 1.1 ** (-delta) 148 | 149 | def pan(self, dx, dy, dz=0): 150 | # pan in camera coordinate system (careful on the sensitivity!) 151 | self.center += 0.0001 * self.rot.as_matrix()[:3, :3] @ np.array([-dx, -dy, dz]) 152 | -------------------------------------------------------------------------------- /convert.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | import os 13 | from argparse import ArgumentParser 14 | import shutil 15 | 16 | # This Python script is based on the shell converter script provided in the MipNerF 360 repository. 17 | parser = ArgumentParser("Colmap converter") 18 | parser.add_argument("--no_gpu", action='store_true') 19 | parser.add_argument("--skip_matching", action='store_true') 20 | parser.add_argument("--source_path", "-s", required=True, type=str) 21 | parser.add_argument("--camera", default="OPENCV", type=str) 22 | parser.add_argument("--colmap_executable", default="", type=str) 23 | parser.add_argument("--resize", action="store_true") 24 | parser.add_argument("--magick_executable", default="", type=str) 25 | args = parser.parse_args() 26 | colmap_command = '"{}"'.format(args.colmap_executable) if len(args.colmap_executable) > 0 else "colmap" 27 | magick_command = '"{}"'.format(args.magick_executable) if len(args.magick_executable) > 0 else "magick" 28 | use_gpu = 1 if not args.no_gpu else 0 29 | 30 | if not args.skip_matching: 31 | os.makedirs(args.source_path + "/distorted/sparse", exist_ok=True) 32 | 33 | ## Feature extraction 34 | os.system(colmap_command + " feature_extractor "\ 35 | "--database_path " + args.source_path + "/distorted/database.db \ 36 | --image_path " + args.source_path + "/input \ 37 | --ImageReader.single_camera 1 \ 38 | --ImageReader.camera_model " + args.camera + " \ 39 | --SiftExtraction.use_gpu " + str(use_gpu)) 40 | 41 | ## Feature matching 42 | os.system(colmap_command + " exhaustive_matcher \ 43 | --database_path " + args.source_path + "/distorted/database.db \ 44 | --SiftMatching.use_gpu " + str(use_gpu)) 45 | 46 | ### Bundle adjustment 47 | # The default Mapper tolerance is unnecessarily large, 48 | # decreasing it speeds up bundle adjustment steps. 49 | os.system(colmap_command + " mapper \ 50 | --database_path " + args.source_path + "/distorted/database.db \ 51 | --image_path " + args.source_path + "/input \ 52 | --output_path " + args.source_path + "/distorted/sparse \ 53 | --Mapper.ba_global_function_tolerance=0.000001") 54 | 55 | ### Image undistortion 56 | ## We need to undistort our images into ideal pinhole intrinsics. 57 | os.system(colmap_command + " image_undistorter \ 58 | --image_path " + args.source_path + "/input \ 59 | --input_path " + args.source_path + "/distorted/sparse/0 \ 60 | --output_path " + args.source_path + "\ 61 | --output_type COLMAP") 62 | 63 | files = os.listdir(args.source_path + "/sparse") 64 | os.makedirs(args.source_path + "/sparse/0", exist_ok=True) 65 | # Copy each file from the source directory to the destination directory 66 | for file in files: 67 | if file == '0': 68 | continue 69 | source_file = os.path.join(args.source_path, "sparse", file) 70 | destination_file = os.path.join(args.source_path, "sparse", "0", file) 71 | shutil.move(source_file, destination_file) 72 | 73 | if(args.resize): 74 | print("Copying and resizing...") 75 | 76 | # Resize images. 77 | os.makedirs(args.source_path + "/images_2", exist_ok=True) 78 | os.makedirs(args.source_path + "/images_4", exist_ok=True) 79 | os.makedirs(args.source_path + "/images_8", exist_ok=True) 80 | # Get the list of files in the source directory 81 | files = os.listdir(args.source_path + "/images") 82 | # Copy each file from the source directory to the destination directory 83 | for file in files: 84 | source_file = os.path.join(args.source_path, "images", file) 85 | 86 | destination_file = os.path.join(args.source_path, "images_2", file) 87 | shutil.copy2(source_file, destination_file) 88 | os.system(magick_command + " mogrify -resize 50% " + destination_file) 89 | 90 | destination_file = os.path.join(args.source_path, "images_4", file) 91 | shutil.copy2(source_file, destination_file) 92 | os.system(magick_command + " mogrify -resize 25% " + destination_file) 93 | 94 | destination_file = os.path.join(args.source_path, "images_8", file) 95 | shutil.copy2(source_file, destination_file) 96 | os.system(magick_command + " mogrify -resize 12.5% " + destination_file) 97 | 98 | print("Done.") -------------------------------------------------------------------------------- /data_tools/phone_catch.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | import sys 4 | import glob 5 | import torch 6 | import shutil 7 | import numpy as np 8 | from PIL import Image 9 | from scipy import optimize 10 | import matplotlib.pyplot as plt 11 | import matplotlib 12 | matplotlib.use('Agg') 13 | 14 | 15 | ''' 16 | MiVOS: https://github.com/hkchengrex/MiVOS 17 | ''' 18 | MIVOS_PATH='YOUR/PATH/TO/MiVOS/' 19 | sys.path.append(MIVOS_PATH) 20 | from interactive_invoke import seg_video 21 | 22 | from colmap2nerf import colmap2nerf_invoke 23 | 24 | 25 | def Laplacian(img): 26 | return cv2.Laplacian(img, cv2.CV_64F).var() 27 | 28 | 29 | def cal_ambiguity(path): 30 | imgs = sorted(glob.glob(path + '/*.png')) 31 | laplace = np.zeros(len(imgs), np.float32) 32 | laplace_dict = {} 33 | for i in range(len(imgs)): 34 | laplace[i] = Laplacian(cv2.cvtColor(cv2.imread(imgs[i]), cv2.COLOR_BGR2GRAY)) 35 | laplace_dict[imgs[i]] = laplace[i] 36 | fig = plt.figure() 37 | fig.add_subplot(1, 2, 1) 38 | plt.hist(laplace) 39 | fig.add_subplot(1, 2, 2) 40 | plt.plot(np.arange(len(laplace)), laplace) 41 | if not os.path.exists(path + '/../noise/'): 42 | os.makedirs(path + '/../noise/') 43 | elif os.path.exists(path + '../noise/'): 44 | return None, None 45 | else: 46 | return None, None 47 | plt.savefig(path+'/../noise/laplace.png') 48 | return laplace, laplace_dict 49 | 50 | 51 | def select_ambiguity(path, nb=10, threshold=0.8, mv_files=False): 52 | if mv_files and os.path.exists(path + '/../noise/'): 53 | print('No need to select. Already done.') 54 | return None, None 55 | def linear(x, a, b): 56 | return a * x + b 57 | laplace, laplace_dic = cal_ambiguity(path) 58 | if laplace is None: 59 | return None, None 60 | imgs = list(laplace_dic.keys()) 61 | amb_img = [] 62 | amb_lap = [] 63 | for i in range(len(laplace)): 64 | i1 = max(0, int(i - nb / 2)) 65 | i2 = min(len(laplace), int(i + nb / 2)) 66 | lap = laplace[i1: i2] 67 | para, _ = optimize.curve_fit(linear, np.arange(i1, i2), lap) 68 | lapi_ = i * para[0] + para[1] 69 | if laplace[i] / lapi_ < threshold: 70 | amb_img.append(imgs[i]) 71 | amb_lap.append(laplace[i]) 72 | if mv_files: 73 | if not os.path.exists(path + '/../noise/'): 74 | os.makedirs(path + '/../noise/') 75 | file_name = amb_img[-1].split('/')[-1].split('\\')[-1] 76 | shutil.move(amb_img[-1], path + '/../noise/' + file_name) 77 | return amb_img, amb_lap 78 | 79 | 80 | def mask_images(img_path, msk_path, sv_path=None, no_mask=False): 81 | image_names = sorted(os.listdir(img_path)) 82 | image_names = [img for img in image_names if img.endswith('.png') or img.endswith('.jpg')] 83 | msk_names = sorted(os.listdir(msk_path)) 84 | msk_names = [img for img in msk_names if img.endswith('.png') or img.endswith('.jpg')] 85 | 86 | if sv_path is None: 87 | if img_path.endswith('/'): 88 | img_path = img_path[:-1] 89 | sv_path = '/'.join(img_path.split('/')[:-1]) + '/masked_images/' 90 | if not os.path.exists(sv_path) and not os.path.exists(sv_path + '../unmasked_images/'): 91 | os.makedirs(sv_path) 92 | else: 93 | return sv_path 94 | 95 | for i in range(len(image_names)): 96 | image_name, msk_name = image_names[i], msk_names[i] 97 | mask = np.array(Image.open(msk_path + '/' + image_name)) 98 | image = np.array(Image.open(img_path + '/' + image_name)) 99 | mask = cv2.resize(mask, (image.shape[1], image.shape[0])) 100 | if no_mask: 101 | mask = np.ones_like(mask) 102 | if mask.max() == 1: 103 | mask = mask * 255 104 | # image[mask==0] = 0 105 | masked_image = np.concatenate([image, mask[..., np.newaxis]], axis=-1) 106 | Image.fromarray(masked_image).save(sv_path + image_name) 107 | return sv_path 108 | 109 | 110 | def extract_frames_mp4(path, gap=None, frame_num=300, sv_path=None): 111 | if sv_path is None: 112 | sv_path = '/'.join(path.split('/')[:-1]) + '/images/' 113 | if not os.path.exists(sv_path): 114 | os.makedirs(sv_path) 115 | else: 116 | return sv_path 117 | if not os.path.exists(path): 118 | raise NotADirectoryError(path + ' does not exists.') 119 | vidcap = cv2.VideoCapture(path) 120 | if gap is None: 121 | total_frame_num = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT)) 122 | gap = int(total_frame_num / frame_num) 123 | gap = max(gap, 1) 124 | 125 | success, image = vidcap.read() 126 | cv2.imwrite(sv_path + "/%05d.png" % 0, image) 127 | count = 1 128 | image_count = 1 129 | while success: 130 | success, image = vidcap.read() 131 | if count % gap == 0 and success: 132 | cv2.imwrite(sv_path + "/%05d.png" % image_count, image) 133 | image_count += 1 134 | count += 1 135 | return sv_path 136 | 137 | 138 | def rename_images(path): 139 | image_names = sorted(os.listdir(path)) 140 | image_names = [img for img in image_names if img.endswith('.png') or img.endswith('.jpg')] 141 | for i in range(len(image_names)): 142 | shutil.move(path + '/' + image_names[i], path + '/%05d.png' % i) 143 | 144 | 145 | if __name__ == '__main__': 146 | gap = None 147 | no_mask = False 148 | dataset_name = 'DATA_NAME' 149 | video_path = f'YOUR/PATH/TO/{dataset_name}/{dataset_name}.mp4' 150 | print('Extracting frames from video: ', video_path, ' with gap: ', gap) 151 | img_path = extract_frames_mp4(video_path, gap=gap) 152 | 153 | # print('Removing Blurry Images') 154 | # laplace, _ = select_ambiguity(img_path, nb=10, threshold=0.8, mv_files=True) 155 | # if laplace is not None: 156 | # rename_images(img_path) 157 | if not no_mask: 158 | print('Segmenting images with MiVOS ...') 159 | msk_path = seg_video(img_path=img_path) 160 | torch.cuda.empty_cache() 161 | print('Masking images with masks ...') 162 | msked_path = mask_images(img_path, msk_path, no_mask=no_mask) 163 | 164 | 165 | print('Running COLMAP ...') 166 | colmap2nerf_invoke(img_path) 167 | if img_path.endswith('/'): 168 | img_path = img_path[:-1] 169 | unmsk_path = '/'.join(img_path.split('/')[:-1]) + '/unmasked_images/' 170 | print('Rename masked and unmasked pathes.') 171 | if not no_mask: 172 | os.rename(img_path, unmsk_path) 173 | os.rename(msked_path, img_path) 174 | 175 | 176 | def red2mask(img_dir): 177 | img_paths = glob.glob(os.path.join(img_dir, "*.png")) 178 | imgs = [cv2.cv2.cvtColor(cv2.imread(x) , cv2.COLOR_BGR2GRAY) for x in img_paths] 179 | save_dir = os.path.join(os.path.dirname(img_dir, "white_mask")) 180 | os.makedirs(save_dir, exist_ok=True) 181 | for idx, img_path in enumerate(img_paths): 182 | save_path = os.path.join(save_dir, os.path.basename(img_path)) 183 | cv2.imwrite(save_path, imgs[idx]) -------------------------------------------------------------------------------- /experimental_logs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction of Experimental Logs 2 | 3 | The source code of our [latest arXiv paper](https://arxiv.org/abs/2412.04282v2) has been released in this repository. Please feel free to test it if you are interested. 4 | For the false setting problem in the Table 1 of our [first arXiv paper](https://arxiv.org/pdf/2412.04282v1), we provide the detailed training settings and experimental logs for each sequence below: 5 | 6 | 7 | | Scene | Cook Spinach | Sear Steak | Flame Steak | Flame Salmon | Coffee Martini | Cut Roast Beef | 8 | |------------------|:------------:|:----------:|:-----------:|:------------:|:--------------:|:--------------:| 9 | | Training Setting | correct | wrong | wrong | wrong | wrong | wrong | 10 | | Parameter Setting | `eval=True` | `eval=False` | `eval=False` | `eval=False` | `eval=False` | `eval=False` | 11 | | Timestamp of Files | 2024.11.05 | 2024.11.05 | 2024.11.05 | 2024.11.04 | 2024.11.04 | 2024.11.05 | 12 | | Config Args | [cfg_args](/experimental_logs/asset/cook_spinach_node.zip) | [cfg_args](/experimental_logs/asset/sear_steak_node.zip) | [cfg_args](/experimental_logs/asset/flame_steak_node.zip) | [cfg_args](/experimental_logs/asset/flame_salmon_1_node.zip) | [cfg_args](/experimental_logs/asset/coffee_martini_node.zip) | [cfg_args](/experimental_logs/asset/cut_roasted_beef_node.zip) | 13 | 14 | 15 | ### ✅ Notes: 16 | 17 | **1.Config Args** 18 | 19 |

20 | 21 | Cook Spinach 22 | 23 | 24 | Cut Roasted Beef 25 | 26 |

27 | 28 | **2.Timestamp of Files** 29 | These files were generated during the previous experiments. You can easily retrieve the timestamp of each file using the following commands: 30 | 31 | - **On Ubuntu**: 32 | ``` 33 | cd asset 34 | unzip cook_spinach_node.zip 35 | cd cook_spinach_node 36 | ls -l --time-style=long-iso 37 | ``` 38 | where the date is the timestamp, like this: 39 | ``` 40 | 2024-11-05 14:21 cfg_args 41 | 2024-11-05 20:40 test_43000_runtimeresults.json 42 | ``` 43 | 44 | - **On Windows**: 45 | just right-click → Properties → check "Modified" time. -------------------------------------------------------------------------------- /experimental_logs/asset/coffee_martini_node.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/coffee_martini_node.zip -------------------------------------------------------------------------------- /experimental_logs/asset/cook_spinach_node.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/cook_spinach_node.zip -------------------------------------------------------------------------------- /experimental_logs/asset/cut_roasted_beef_node.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/cut_roasted_beef_node.zip -------------------------------------------------------------------------------- /experimental_logs/asset/flame_salmon_1_node.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/flame_salmon_1_node.zip -------------------------------------------------------------------------------- /experimental_logs/asset/flame_steak_node.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/flame_steak_node.zip -------------------------------------------------------------------------------- /experimental_logs/asset/img/arg_cook_spinach.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/img/arg_cook_spinach.jpg -------------------------------------------------------------------------------- /experimental_logs/asset/img/arg_cut_roasted_beef.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/img/arg_cut_roasted_beef.jpg -------------------------------------------------------------------------------- /experimental_logs/asset/sear_steak_node.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/experimental_logs/asset/sear_steak_node.zip -------------------------------------------------------------------------------- /gaussian_renderer/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/gaussian_renderer/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /gaussian_renderer/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/gaussian_renderer/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /gaussian_renderer/__pycache__/network_gui.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/gaussian_renderer/__pycache__/network_gui.cpython-310.pyc -------------------------------------------------------------------------------- /gaussian_renderer/__pycache__/network_gui.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/gaussian_renderer/__pycache__/network_gui.cpython-38.pyc -------------------------------------------------------------------------------- /gaussian_renderer/network_gui.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | import torch 13 | import traceback 14 | import socket 15 | import json 16 | from scene.cameras import MiniCam 17 | 18 | host = "127.0.0.1" 19 | port = 6009 20 | 21 | conn = None 22 | addr = None 23 | 24 | listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 25 | 26 | 27 | def init(wish_host, wish_port): 28 | global host, port, listener 29 | host = wish_host 30 | port = wish_port 31 | listener.bind((host, port)) 32 | listener.listen() 33 | listener.settimeout(0) 34 | 35 | 36 | def try_connect(): 37 | global conn, addr, listener 38 | try: 39 | conn, addr = listener.accept() 40 | print(f"\nConnected by {addr}") 41 | conn.settimeout(None) 42 | except Exception as inst: 43 | pass 44 | 45 | 46 | def read(): 47 | global conn 48 | messageLength = conn.recv(4) 49 | messageLength = int.from_bytes(messageLength, 'little') 50 | message = conn.recv(messageLength) 51 | return json.loads(message.decode("utf-8")) 52 | 53 | 54 | def send(message_bytes, verify): 55 | global conn 56 | if message_bytes != None: 57 | conn.sendall(message_bytes) 58 | conn.sendall(len(verify).to_bytes(4, 'little')) 59 | conn.sendall(bytes(verify, 'ascii')) 60 | 61 | 62 | def receive(): 63 | message = read() 64 | 65 | width = message["resolution_x"] 66 | height = message["resolution_y"] 67 | 68 | if width != 0 and height != 0: 69 | try: 70 | do_training = bool(message["train"]) 71 | fovy = message["fov_y"] 72 | fovx = message["fov_x"] 73 | znear = message["z_near"] 74 | zfar = message["z_far"] 75 | do_shs_python = bool(message["shs_python"]) 76 | do_rot_scale_python = bool(message["rot_scale_python"]) 77 | keep_alive = bool(message["keep_alive"]) 78 | scaling_modifier = message["scaling_modifier"] 79 | world_view_transform = torch.reshape(torch.tensor(message["view_matrix"]), (4, 4)).cuda() 80 | world_view_transform[:, 1] = -world_view_transform[:, 1] 81 | world_view_transform[:, 2] = -world_view_transform[:, 2] 82 | full_proj_transform = torch.reshape(torch.tensor(message["view_projection_matrix"]), (4, 4)).cuda() 83 | full_proj_transform[:, 1] = -full_proj_transform[:, 1] 84 | custom_cam = MiniCam(width, height, fovy, fovx, znear, zfar, world_view_transform, full_proj_transform) 85 | except Exception as e: 86 | print("") 87 | traceback.print_exc() 88 | raise e 89 | return custom_cam, do_training, do_shs_python, do_rot_scale_python, keep_alive, scaling_modifier 90 | else: 91 | return None, None, None, None, None, None 92 | -------------------------------------------------------------------------------- /imgs/Framework.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/imgs/Framework.jpg -------------------------------------------------------------------------------- /install_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | conda create -n taylorgaussian python=3.10 4 | conda activate taylorgaussian 5 | 6 | pip install -r requirements.txt 7 | pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 torchaudio==0.13.1 --extra-index-url https://download.pytorch.org/whl/cu117 8 | pip install setuptools==69.5.1 9 | pip install numpy==1.23.5 10 | pip install Cmake 11 | 12 | pip install git+https://github.com/Po-Hsun-Su/pytorch-ssim.git 13 | pip install git+https://github.com/facebookresearch/pytorch3d.git 14 | 15 | 16 | cd submodules 17 | git clone https://github.com/ashawkey/diff-gaussian-rasterization.git 18 | cd ./diff-gaussian-rasterization/ 19 | pip install -e . 20 | cd .. 21 | git clone https://github.com/camenduru/simple-knn.git 22 | cd ./simple-knn/ 23 | pip install -e . -------------------------------------------------------------------------------- /lpipsPyTorch/__init__.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from .modules.lpips import LPIPS 4 | 5 | 6 | def lpips(x: torch.Tensor, 7 | y: torch.Tensor, 8 | net_type: str = 'alex', 9 | version: str = '0.1'): 10 | r"""Function that measures 11 | Learned Perceptual Image Patch Similarity (LPIPS). 12 | 13 | Arguments: 14 | x, y (torch.Tensor): the input tensors to compare. 15 | net_type (str): the network type to compare the features: 16 | 'alex' | 'squeeze' | 'vgg'. Default: 'alex'. 17 | version (str): the version of LPIPS. Default: 0.1. 18 | """ 19 | device = x.device 20 | criterion = LPIPS(net_type, version).to(device) 21 | return criterion(x, y) 22 | -------------------------------------------------------------------------------- /lpipsPyTorch/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/lpipsPyTorch/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /lpipsPyTorch/modules/__pycache__/lpips.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/lpipsPyTorch/modules/__pycache__/lpips.cpython-310.pyc -------------------------------------------------------------------------------- /lpipsPyTorch/modules/__pycache__/networks.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/lpipsPyTorch/modules/__pycache__/networks.cpython-310.pyc -------------------------------------------------------------------------------- /lpipsPyTorch/modules/__pycache__/utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/lpipsPyTorch/modules/__pycache__/utils.cpython-310.pyc -------------------------------------------------------------------------------- /lpipsPyTorch/modules/lpips.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | from .networks import get_network, LinLayers 5 | from .utils import get_state_dict 6 | 7 | 8 | class LPIPS(nn.Module): 9 | r"""Creates a criterion that measures 10 | Learned Perceptual Image Patch Similarity (LPIPS). 11 | 12 | Arguments: 13 | net_type (str): the network type to compare the features: 14 | 'alex' | 'squeeze' | 'vgg'. Default: 'alex'. 15 | version (str): the version of LPIPS. Default: 0.1. 16 | """ 17 | 18 | def __init__(self, net_type: str = 'alex', version: str = '0.1'): 19 | assert version in ['0.1'], 'v0.1 is only supported now' 20 | 21 | super(LPIPS, self).__init__() 22 | 23 | # pretrained network 24 | self.net = get_network(net_type) 25 | 26 | # linear layers 27 | self.lin = LinLayers(self.net.n_channels_list) 28 | self.lin.load_state_dict(get_state_dict(net_type, version)) 29 | 30 | def forward(self, x: torch.Tensor, y: torch.Tensor): 31 | feat_x, feat_y = self.net(x), self.net(y) 32 | 33 | diff = [(fx - fy) ** 2 for fx, fy in zip(feat_x, feat_y)] 34 | res = [l(d).mean((2, 3), True) for d, l in zip(diff, self.lin)] 35 | 36 | return torch.sum(torch.cat(res, 0), 0, True) 37 | -------------------------------------------------------------------------------- /lpipsPyTorch/modules/networks.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence 2 | 3 | from itertools import chain 4 | 5 | import torch 6 | import torch.nn as nn 7 | from torchvision import models 8 | 9 | from .utils import normalize_activation 10 | 11 | 12 | def get_network(net_type: str): 13 | if net_type == 'alex': 14 | return AlexNet() 15 | elif net_type == 'squeeze': 16 | return SqueezeNet() 17 | elif net_type == 'vgg': 18 | return VGG16() 19 | else: 20 | raise NotImplementedError('choose net_type from [alex, squeeze, vgg].') 21 | 22 | 23 | class LinLayers(nn.ModuleList): 24 | def __init__(self, n_channels_list: Sequence[int]): 25 | super(LinLayers, self).__init__([ 26 | nn.Sequential( 27 | nn.Identity(), 28 | nn.Conv2d(nc, 1, 1, 1, 0, bias=False) 29 | ) for nc in n_channels_list 30 | ]) 31 | 32 | for param in self.parameters(): 33 | param.requires_grad = False 34 | 35 | 36 | class BaseNet(nn.Module): 37 | def __init__(self): 38 | super(BaseNet, self).__init__() 39 | 40 | # register buffer 41 | self.register_buffer( 42 | 'mean', torch.Tensor([-.030, -.088, -.188])[None, :, None, None]) 43 | self.register_buffer( 44 | 'std', torch.Tensor([.458, .448, .450])[None, :, None, None]) 45 | 46 | def set_requires_grad(self, state: bool): 47 | for param in chain(self.parameters(), self.buffers()): 48 | param.requires_grad = state 49 | 50 | def z_score(self, x: torch.Tensor): 51 | return (x - self.mean) / self.std 52 | 53 | def forward(self, x: torch.Tensor): 54 | x = self.z_score(x) 55 | 56 | output = [] 57 | for i, (_, layer) in enumerate(self.layers._modules.items(), 1): 58 | x = layer(x) 59 | if i in self.target_layers: 60 | output.append(normalize_activation(x)) 61 | if len(output) == len(self.target_layers): 62 | break 63 | return output 64 | 65 | 66 | class SqueezeNet(BaseNet): 67 | def __init__(self): 68 | super(SqueezeNet, self).__init__() 69 | 70 | self.layers = models.squeezenet1_1(True).features 71 | self.target_layers = [2, 5, 8, 10, 11, 12, 13] 72 | self.n_channels_list = [64, 128, 256, 384, 384, 512, 512] 73 | 74 | self.set_requires_grad(False) 75 | 76 | 77 | class AlexNet(BaseNet): 78 | def __init__(self): 79 | super(AlexNet, self).__init__() 80 | 81 | self.layers = models.alexnet(True).features 82 | self.target_layers = [2, 5, 8, 10, 12] 83 | self.n_channels_list = [64, 192, 384, 256, 256] 84 | 85 | self.set_requires_grad(False) 86 | 87 | 88 | class VGG16(BaseNet): 89 | def __init__(self): 90 | super(VGG16, self).__init__() 91 | 92 | self.layers = models.vgg16(weights=models.VGG16_Weights.IMAGENET1K_V1).features 93 | self.target_layers = [4, 9, 16, 23, 30] 94 | self.n_channels_list = [64, 128, 256, 512, 512] 95 | 96 | self.set_requires_grad(False) 97 | -------------------------------------------------------------------------------- /lpipsPyTorch/modules/utils.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | import torch 4 | 5 | 6 | def normalize_activation(x, eps=1e-10): 7 | norm_factor = torch.sqrt(torch.sum(x ** 2, dim=1, keepdim=True)) 8 | return x / (norm_factor + eps) 9 | 10 | 11 | def get_state_dict(net_type: str = 'alex', version: str = '0.1'): 12 | # build url 13 | url = 'https://raw.githubusercontent.com/richzhang/PerceptualSimilarity/' \ 14 | + f'master/lpips/weights/v{version}/{net_type}.pth' 15 | 16 | # download 17 | old_state_dict = torch.hub.load_state_dict_from_url( 18 | url, progress=True, 19 | map_location=None if torch.cuda.is_available() else torch.device('cpu') 20 | ) 21 | 22 | # rename keys 23 | new_state_dict = OrderedDict() 24 | for key, val in old_state_dict.items(): 25 | new_key = key 26 | new_key = new_key.replace('lin', '') 27 | new_key = new_key.replace('model.', '') 28 | new_state_dict[new_key] = val 29 | 30 | return new_state_dict 31 | -------------------------------------------------------------------------------- /metrics.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | from pathlib import Path 13 | import os 14 | from PIL import Image 15 | import torch 16 | import torchvision.transforms.functional as tf 17 | from utils.loss_utils import ssim 18 | from lpipsPyTorch import lpips 19 | import json 20 | from tqdm import tqdm 21 | from utils.image_utils import psnr 22 | from argparse import ArgumentParser 23 | 24 | 25 | def readImages(renders_dir, gt_dir): 26 | renders = [] 27 | gts = [] 28 | image_names = [] 29 | for fname in os.listdir(renders_dir): 30 | render = Image.open(renders_dir / fname) 31 | gt = Image.open(gt_dir / fname) 32 | renders.append(tf.to_tensor(render).unsqueeze(0)[:, :3, :, :].cuda()) 33 | gts.append(tf.to_tensor(gt).unsqueeze(0)[:, :3, :, :].cuda()) 34 | image_names.append(fname) 35 | return renders, gts, image_names 36 | 37 | 38 | def evaluate(model_paths): 39 | full_dict = {} 40 | per_view_dict = {} 41 | full_dict_polytopeonly = {} 42 | per_view_dict_polytopeonly = {} 43 | print("") 44 | 45 | for scene_dir in model_paths: 46 | try: 47 | print("Scene:", scene_dir) 48 | full_dict[scene_dir] = {} 49 | per_view_dict[scene_dir] = {} 50 | full_dict_polytopeonly[scene_dir] = {} 51 | per_view_dict_polytopeonly[scene_dir] = {} 52 | 53 | test_dir = Path(scene_dir) / "test" 54 | 55 | for method in os.listdir(test_dir): 56 | if not method.startswith("ours"): 57 | continue 58 | print("Method:", method) 59 | 60 | full_dict[scene_dir][method] = {} 61 | per_view_dict[scene_dir][method] = {} 62 | full_dict_polytopeonly[scene_dir][method] = {} 63 | per_view_dict_polytopeonly[scene_dir][method] = {} 64 | 65 | method_dir = test_dir / method 66 | gt_dir = method_dir / "gt" 67 | renders_dir = method_dir / "renders" 68 | renders, gts, image_names = readImages(renders_dir, gt_dir) 69 | 70 | ssims = [] 71 | psnrs = [] 72 | lpipss = [] 73 | 74 | for idx in tqdm(range(len(renders)), desc="Metric evaluation progress"): 75 | ssims.append(ssim(renders[idx], gts[idx])) 76 | psnrs.append(psnr(renders[idx], gts[idx])) 77 | lpipss.append(lpips(renders[idx], gts[idx], net_type='vgg')) 78 | 79 | print(" SSIM : {:>12.7f}".format(torch.tensor(ssims).mean(), ".5")) 80 | print(" PSNR : {:>12.7f}".format(torch.tensor(psnrs).mean(), ".5")) 81 | print(" LPIPS: {:>12.7f}".format(torch.tensor(lpipss).mean(), ".5")) 82 | print("") 83 | 84 | full_dict[scene_dir][method].update({"SSIM": torch.tensor(ssims).mean().item(), 85 | "PSNR": torch.tensor(psnrs).mean().item(), 86 | "LPIPS": torch.tensor(lpipss).mean().item()}) 87 | per_view_dict[scene_dir][method].update( 88 | {"SSIM": {name: ssim for ssim, name in zip(torch.tensor(ssims).tolist(), image_names)}, 89 | "PSNR": {name: psnr for psnr, name in zip(torch.tensor(psnrs).tolist(), image_names)}, 90 | "LPIPS": {name: lp for lp, name in zip(torch.tensor(lpipss).tolist(), image_names)}}) 91 | 92 | with open(scene_dir + "/results.json", 'w') as fp: 93 | json.dump(full_dict[scene_dir], fp, indent=True) 94 | with open(scene_dir + "/per_view.json", 'w') as fp: 95 | json.dump(per_view_dict[scene_dir], fp, indent=True) 96 | except: 97 | print("Unable to compute metrics for model", scene_dir) 98 | 99 | 100 | if __name__ == "__main__": 101 | device = torch.device("cuda:0") 102 | torch.cuda.set_device(device) 103 | 104 | # Set up command line argument parser 105 | parser = ArgumentParser(description="Training script parameters") 106 | parser.add_argument('--model_paths', '-m', required=True, nargs="+", type=str, default=[]) 107 | args = parser.parse_args() 108 | evaluate(args.model_paths) 109 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.22.0 2 | opencv-python==4.5.5.62 3 | PyYAML==6.0 4 | scipy==1.10.1 5 | tensorboard==2.14.0 6 | tqdm==4.66.1 7 | imageio 8 | plyfile 9 | piq 10 | dearpygui 11 | lpips 12 | pytorch_msssim 13 | matplotlib 14 | scikit-image 15 | git+https://github.com/Po-Hsun-Su/pytorch-ssim.git 16 | git+https://github.com/facebookresearch/pytorch3d.git 17 | -------------------------------------------------------------------------------- /scene/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /scene/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /scene/__pycache__/cameras.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/cameras.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/cameras.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/cameras.cpython-38.pyc -------------------------------------------------------------------------------- /scene/__pycache__/cameras_st.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/cameras_st.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/colmap_loader.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/colmap_loader.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/colmap_loader.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/colmap_loader.cpython-37.pyc -------------------------------------------------------------------------------- /scene/__pycache__/colmap_loader.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/colmap_loader.cpython-38.pyc -------------------------------------------------------------------------------- /scene/__pycache__/dataset_readers.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/dataset_readers.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/dataset_readers.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/dataset_readers.cpython-37.pyc -------------------------------------------------------------------------------- /scene/__pycache__/dataset_readers.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/dataset_readers.cpython-38.pyc -------------------------------------------------------------------------------- /scene/__pycache__/deform_model.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/deform_model.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/deform_model.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/deform_model.cpython-38.pyc -------------------------------------------------------------------------------- /scene/__pycache__/gaussian_model.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/gaussian_model.cpython-310.pyc -------------------------------------------------------------------------------- /scene/__pycache__/gaussian_model.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/scene/__pycache__/gaussian_model.cpython-38.pyc -------------------------------------------------------------------------------- /scene/cameras.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | import torch 13 | from torch import nn 14 | import numpy as np 15 | from utils.graphics_utils import getWorld2View2, getProjectionMatrix 16 | 17 | 18 | class Camera(nn.Module): 19 | def __init__(self, colmap_id, R, T, FoVx, FoVy, image, gt_alpha_mask, image_name, uid, 20 | trans=np.array([0.0, 0.0, 0.0]), scale=1.0, data_device="cuda", fid=None, depth=None, flow_dirs=[]): 21 | super(Camera, self).__init__() 22 | 23 | self.uid = uid 24 | self.colmap_id = colmap_id 25 | self.R = R 26 | self.T = T 27 | self.FoVx = FoVx 28 | self.FoVy = FoVy 29 | self.image_name = image_name 30 | self.flow_dirs = flow_dirs 31 | 32 | try: 33 | self.data_device = torch.device(data_device) 34 | except Exception as e: 35 | print(e) 36 | print(f"[Warning] Custom device {data_device} failed, fallback to default cuda device") 37 | self.data_device = torch.device("cuda") 38 | 39 | self.original_image = image.clamp(0.0, 1.0).to(self.data_device) #.to(self.data_device) 40 | self.fid = torch.Tensor(np.array([fid])).to(self.data_device) 41 | self.image_width = self.original_image.shape[2] 42 | self.image_height = self.original_image.shape[1] 43 | self.depth = torch.Tensor(depth).to(self.data_device) if depth is not None else None 44 | self.gt_alpha_mask = gt_alpha_mask 45 | 46 | if gt_alpha_mask is not None: 47 | self.gt_alpha_mask = self.gt_alpha_mask.to(self.data_device) 48 | # self.original_image *= gt_alpha_mask.to(self.data_device) 49 | 50 | self.zfar = 100.0 51 | self.znear = 0.01 52 | 53 | self.trans = trans 54 | self.scale = scale 55 | 56 | self.world_view_transform = torch.tensor(getWorld2View2(R, T, trans, scale)).transpose(0, 1).to(self.data_device) 57 | self.projection_matrix = getProjectionMatrix(znear=self.znear, zfar=self.zfar, fovX=self.FoVx, fovY=self.FoVy).transpose(0, 1).to(self.data_device) 58 | self.full_proj_transform = ( 59 | self.world_view_transform.unsqueeze(0).bmm(self.projection_matrix.unsqueeze(0))).squeeze(0) 60 | self.camera_center = self.world_view_transform.inverse()[3, :3] 61 | 62 | def reset_extrinsic(self, R, T): 63 | self.world_view_transform = torch.tensor(getWorld2View2(R, T, self.trans, self.scale)).transpose(0, 1).cuda() 64 | self.full_proj_transform = ( 65 | self.world_view_transform.unsqueeze(0).bmm(self.projection_matrix.unsqueeze(0))).squeeze(0) 66 | self.camera_center = self.world_view_transform.inverse()[3, :3] 67 | 68 | def load2device(self, data_device='cuda'): 69 | self.original_image = self.original_image.to(data_device) 70 | self.world_view_transform = self.world_view_transform.to(data_device) 71 | self.projection_matrix = self.projection_matrix.to(data_device) 72 | self.full_proj_transform = self.full_proj_transform.to(data_device) 73 | self.camera_center = self.camera_center.to(data_device) 74 | self.fid = self.fid.to(data_device) 75 | 76 | 77 | class MiniCam: 78 | def __init__(self, width, height, fovy, fovx, znear, zfar, world_view_transform, full_proj_transform): 79 | self.image_width = width 80 | self.image_height = height 81 | self.FoVy = fovy 82 | self.FoVx = fovx 83 | self.znear = znear 84 | self.zfar = zfar 85 | self.world_view_transform = world_view_transform 86 | self.full_proj_transform = full_proj_transform 87 | view_inv = torch.inverse(self.world_view_transform) 88 | self.camera_center = view_inv[3][:3] 89 | 90 | def reset_extrinsic(self, R, T): 91 | self.world_view_transform = torch.tensor(getWorld2View2(R, T)).transpose(0, 1).cuda() 92 | self.full_proj_transform = ( 93 | self.world_view_transform.unsqueeze(0).bmm(self.projection_matrix.unsqueeze(0))).squeeze(0) 94 | self.camera_center = self.world_view_transform.inverse()[3, :3] 95 | -------------------------------------------------------------------------------- /scene/deform_model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from utils.time_utils import DeformNetwork, ControlNodeWarp, StaticNetwork 5 | import os 6 | from utils.system_utils import searchForMaxIteration 7 | from utils.general_utils import get_expon_lr_func 8 | 9 | 10 | model_dict = {'mlp': DeformNetwork, 'node': ControlNodeWarp, 'static': StaticNetwork} 11 | 12 | 13 | class DeformModel: 14 | def __init__(self, deform_type='node', is_blender=False, d_rot_as_res=True, **kwargs): 15 | self.deform = model_dict[deform_type](is_blender=is_blender, d_rot_as_res=d_rot_as_res, **kwargs).cuda() 16 | self.name = self.deform.name 17 | self.optimizer = None 18 | self.spatial_lr_scale = 5 19 | self.d_rot_as_res = d_rot_as_res 20 | 21 | @property 22 | def reg_loss(self): 23 | return self.deform.reg_loss 24 | 25 | def step(self, xyz, time_emb, iteration=0, **kwargs): 26 | return self.deform(xyz, time_emb, iteration=iteration, **kwargs) 27 | 28 | def train_setting(self, training_args): 29 | l = [ 30 | {'params': group['params'], 31 | 'lr': training_args.position_lr_init * self.spatial_lr_scale * training_args.deform_lr_scale, 32 | "name": group['name']} 33 | for group in self.deform.trainable_parameters() 34 | ] 35 | self.optimizer = torch.optim.Adam(l, lr=0.0, eps=1e-15) 36 | 37 | self.deform_scheduler_args = get_expon_lr_func(lr_init=training_args.position_lr_init * self.spatial_lr_scale * training_args.deform_lr_scale, lr_final=training_args.position_lr_final * training_args.deform_lr_scale, lr_delay_mult=training_args.position_lr_delay_mult, max_steps=training_args.deform_lr_max_steps) 38 | if self.name == 'node': 39 | 40 | self.deform.as_gaussians.training_setup(training_args) 41 | 42 | def save_weights(self, model_path, iteration): 43 | out_weights_path = os.path.join(model_path, "deform/iteration_{}".format(iteration)) 44 | os.makedirs(out_weights_path, exist_ok=True) 45 | torch.save(self.deform.state_dict(), os.path.join(out_weights_path, 'deform.pth')) 46 | 47 | def load_weights(self, model_path, iteration=-1): 48 | if iteration == -1: 49 | loaded_iter = searchForMaxIteration(os.path.join(model_path, "deform")) 50 | else: 51 | loaded_iter = iteration 52 | weights_path = os.path.join(model_path, "deform/iteration_{}/deform.pth".format(loaded_iter)) 53 | if os.path.exists(weights_path): 54 | self.deform.load_state_dict(torch.load(weights_path)) 55 | return True 56 | else: 57 | return False 58 | 59 | def update_learning_rate(self, iteration): 60 | for param_group in self.optimizer.param_groups: 61 | if param_group["name"] == "deform" or param_group["name"] == "mlp" or 'node' in param_group['name']: 62 | lr = self.deform_scheduler_args(iteration) 63 | param_group['lr'] = lr 64 | return lr 65 | 66 | def densify(self, max_grad, x, x_grad, **kwargs): 67 | if self.name == 'node': 68 | self.deform.densify(max_grad=max_grad, optimizer=self.optimizer, x=x, x_grad=x_grad, **kwargs) 69 | else: 70 | return 71 | 72 | def update(self, iteration): 73 | self.deform.update(iteration) 74 | -------------------------------------------------------------------------------- /script/Readme.md: -------------------------------------------------------------------------------- 1 | # Environment Setup 2 | 3 | ``` 4 | conda deactivate 5 | conda create -n colmap python=3.8 6 | conda activate colmap 7 | pip install opencv-python-headless 8 | pip install tqdm 9 | pip install natsort 10 | pip install Pillow 11 | conda install colmap -c conda-forge 12 | conda install pytorch==1.13.0 torchvision==0.14.0 torchaudio==0.13.0 pytorch-cuda=11.7 -c pytorch -c nvidia 13 | ``` -------------------------------------------------------------------------------- /script/__pycache__/pre_immersive_distorted.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/script/__pycache__/pre_immersive_distorted.cpython-310.pyc -------------------------------------------------------------------------------- /script/n3d_process.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2023 OPPO 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import os 24 | from pathlib import Path 25 | 26 | import cv2 27 | import glob 28 | import tqdm 29 | import numpy as np 30 | import shutil 31 | import pickle 32 | import sys 33 | import argparse 34 | 35 | 36 | sys.path.append(".") 37 | from thirdparty.gaussian_splatting.utils.my_utils import posetow2c_matrcs, rotmat2qvec 38 | from thirdparty.colmap.pre_colmap import * 39 | from thirdparty.gaussian_splatting.helper3dg import getcolmapsinglen3d 40 | from script.utils_pre import write_colmap 41 | 42 | 43 | 44 | 45 | def extractframes(videopath: Path, startframe=0, endframe=300, downscale=1, save_subdir = '', ext='png'): 46 | output_dir = videopath.parent / save_subdir / videopath.stem 47 | 48 | if all((output_dir / f"{i}.{ext}").exists() for i in range(startframe, endframe)): 49 | print(f"Already extracted all the frames in {output_dir}") 50 | return 51 | 52 | cam = cv2.VideoCapture(str(videopath)) 53 | cam.set(cv2.CAP_PROP_POS_FRAMES, startframe) 54 | 55 | output_dir.mkdir(parents=True, exist_ok=True) 56 | 57 | for i in range(startframe, endframe): 58 | success, frame = cam.read() 59 | if not success: 60 | print(f"Error reading frame {i}") 61 | break 62 | 63 | if downscale > 1: 64 | new_width, new_height = int(frame.shape[1] / downscale), int(frame.shape[0] / downscale) 65 | frame = cv2.resize(frame, (new_width, new_height), interpolation=cv2.INTER_AREA) 66 | 67 | cv2.imwrite(str(output_dir / f"{i}.{ext}"), frame) 68 | 69 | cam.release() 70 | 71 | 72 | def preparecolmapdynerf(folder, offset=0): 73 | folderlist = sorted(folder.glob("cam??/")) 74 | 75 | savedir = folder / f"colmap_{offset}" / "input" 76 | savedir.mkdir(exist_ok=True, parents=True) 77 | 78 | for folder in folderlist: 79 | imagepath = folder / f"{offset}.png" 80 | imagesavepath = savedir / f"{folder.name}.png" 81 | 82 | if (imagesavepath.exists()): 83 | continue 84 | print('imagepath:',imagepath) 85 | assert imagepath.exists 86 | print('imagesavepath:',imagesavepath) 87 | # shutil.copy(imagepath, imagesavepath) 88 | imagesavepath.symlink_to(imagepath.resolve()) 89 | 90 | 91 | def convertdynerftocolmapdb(path, offset=0, downscale=1): 92 | originnumpy = path / "poses_bounds.npy" 93 | video_paths = sorted(path.glob('cam*.mp4')) 94 | 95 | with open(originnumpy, 'rb') as numpy_file: 96 | poses_bounds = np.load(numpy_file) 97 | poses = poses_bounds[:, :15].reshape(-1, 3, 5) 98 | 99 | llffposes = poses.copy().transpose(1, 2, 0) 100 | w2c_matriclist = posetow2c_matrcs(llffposes) 101 | assert (type(w2c_matriclist) == list) 102 | 103 | cameras = [] 104 | for i in range(len(poses)): 105 | cameraname = video_paths[i].stem 106 | m = w2c_matriclist[i] 107 | colmapR = m[:3, :3] 108 | T = m[:3, 3] 109 | 110 | H, W, focal = poses[i, :, -1] / downscale 111 | 112 | colmapQ = rotmat2qvec(colmapR) 113 | 114 | camera = { 115 | 'id': i + 1, 116 | 'filename': f"{cameraname}.png", 117 | 'w': W, 118 | 'h': H, 119 | 'fx': focal, 120 | 'fy': focal, 121 | 'cx': W // 2, 122 | 'cy': H // 2, 123 | 'q': colmapQ, 124 | 't': T, 125 | } 126 | cameras.append(camera) 127 | 128 | write_colmap(path, cameras, offset) 129 | 130 | 131 | 132 | if __name__ == "__main__" : 133 | parser = argparse.ArgumentParser() 134 | 135 | parser.add_argument("--videopath", default="", type=str) 136 | parser.add_argument("--startframe", default=0, type=int) 137 | parser.add_argument("--endframe", default=50, type=int) 138 | parser.add_argument("--downscale", default=1, type=int) 139 | 140 | args = parser.parse_args() 141 | videopath = Path(args.videopath) 142 | 143 | startframe = args.startframe 144 | endframe = args.endframe 145 | downscale = args.downscale 146 | 147 | print(f"params: startframe={startframe} - endframe={endframe} - downscale={downscale} - videopath={videopath}") 148 | 149 | if startframe >= endframe: 150 | print("start frame must smaller than end frame") 151 | quit() 152 | if startframe < 0 or endframe > 300: 153 | print("frame must in range 0-300") 154 | quit() 155 | if not videopath.exists(): 156 | print("path not exist") 157 | quit() 158 | ##### step1 159 | print("start extracting 300 frames from videos") 160 | videoslist = sorted(videopath.glob("*.mp4")) 161 | for v in tqdm.tqdm(videoslist, desc="Extract frames from videos"): 162 | extractframes(v, downscale=downscale) 163 | 164 | 165 | 166 | # # ## step2 prepare colmap input 167 | print("start preparing colmap image input") 168 | for offset in range(startframe, endframe): 169 | preparecolmapdynerf(videopath, offset) 170 | 171 | 172 | print("start preparing colmap database input") 173 | # # ## step 3 prepare colmap db file 174 | for offset in tqdm.tqdm(range(startframe, endframe), desc="convertdynerftocolmapdb"): 175 | convertdynerftocolmapdb(videopath, offset, downscale) 176 | 177 | 178 | # ## step 4 run colmap, per frame, if error, reinstall opencv-headless 179 | for offset in range(startframe, endframe): 180 | getcolmapsinglen3d(videopath, offset) 181 | 182 | -------------------------------------------------------------------------------- /script/utils_pre.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append(".") 3 | from thirdparty.colmap.pre_colmap import * 4 | from pathlib import Path 5 | 6 | def write_colmap(path, cameras, offset=0): 7 | projectfolder = path / f"colmap_{offset}" 8 | manualfolder = projectfolder / "manual" 9 | manualfolder.mkdir(exist_ok=True) 10 | 11 | savetxt = manualfolder / "images.txt" 12 | savecamera = manualfolder / "cameras.txt" 13 | savepoints = manualfolder / "points3D.txt" 14 | 15 | imagetxtlist = [] 16 | cameratxtlist = [] 17 | 18 | db_file = projectfolder / "input.db" 19 | if db_file.exists(): 20 | db_file.unlink() 21 | 22 | db = COLMAPDatabase.connect(db_file) 23 | 24 | db.create_tables() 25 | 26 | 27 | for cam in cameras: 28 | id = cam['id'] 29 | filename = cam['filename'] 30 | 31 | # intrinsics 32 | w = cam['w'] 33 | h = cam['h'] 34 | fx = cam['fx'] 35 | fy = cam['fy'] 36 | cx = cam['cx'] 37 | cy = cam['cy'] 38 | 39 | # extrinsics 40 | colmapQ = cam['q'] 41 | T = cam['t'] 42 | 43 | # check that cx is almost w /2, idem for cy 44 | # assert abs(cx - w / 2) / cx < 0.10, f"cx is not close to w/2: {cx}, w: {w}" 45 | # assert abs(cy - h / 2) / cy < 0.10, f"cy is not close to h/2: {cy}, h: {h}" 46 | 47 | line = f"{id} " + " ".join(map(str, colmapQ)) + " " + " ".join(map(str, T)) + f" {id} {filename}\n" 48 | imagetxtlist.append(line) 49 | imagetxtlist.append("\n") 50 | 51 | params = np.array((fx , fy, cx, cy,)) 52 | 53 | camera_id = db.add_camera(1, w, h, params) 54 | cameraline = f"{id} PINHOLE {w} {h} {fx} {fy} {cx} {cy}\n" 55 | cameratxtlist.append(cameraline) 56 | image_id = db.add_image(filename, camera_id, prior_q=colmapQ, prior_t=T, image_id=id) 57 | db.commit() 58 | db.close() 59 | 60 | savetxt.write_text("".join(imagetxtlist)) 61 | savecamera.write_text("".join(cameratxtlist)) 62 | savepoints.write_text("") # Creating an empty points3D.txt file 63 | -------------------------------------------------------------------------------- /start_order.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GPU_ID=$1 3 | 4 | 5 | run_pipeline() { 6 | # train 7 | CUDA_VISIBLE_DEVICES="$GPU_ID" python main.py \ 8 | --source_path data/Neural3D/"$data_name"/colmap_0 \ 9 | --model_path logs/N3DV/"$data_name"_test \ 10 | --deform_type node \ 11 | --node_num 1024 \ 12 | --hyper_dim 8 \ 13 | --is_blender \ 14 | --gt_alpha_mask_as_dynamic_mask \ 15 | --gs_with_motion_mask \ 16 | --eval \ 17 | --local_frame \ 18 | --resolution 2 \ 19 | --W 800 \ 20 | --H 800 \ 21 | --config arguments/N3DV/"$data_name".py 22 | # render 23 | CUDA_VISIBLE_DEVICES="$GPU_ID" python render.py \ 24 | --source_path data/Neural3D/"$data_name"/colmap_0 \ 25 | --model_path logs/N3DV/"$data_name"_test \ 26 | --deform_type node \ 27 | --node_num 1024 \ 28 | --hyper_dim 8 \ 29 | --is_blender \ 30 | --gt_alpha_mask_as_dynamic_mask \ 31 | --gs_with_motion_mask \ 32 | --eval \ 33 | --skip_train \ 34 | --local_frame \ 35 | --resolution 2 \ 36 | --W 800 \ 37 | --H 800 \ 38 | --dataset_type colmap 39 | } 40 | 41 | Neural3D_DA=("cut_roasted_beef" "sear_steak" "flame_steak") 42 | Neural3D_DA=( "cook_spinach" "flame_salmon_1" "coffee_martini" ) 43 | # run_pipeline 44 | for data_name in "${Neural3D_DA[@]}"; 45 | do 46 | echo "Dataset: Neural3D_DA/${data_name}" 47 | run_pipeline "$GPU_ID" "$data_name" 48 | done 49 | -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | diff_gaussian_rasterization.egg-info/ 3 | dist/ 4 | 5 | __pycache__ 6 | 7 | *.so -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/glm"] 2 | path = third_party/glm 3 | url = https://github.com/g-truc/glm.git 4 | -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | cmake_minimum_required(VERSION 3.20) 13 | 14 | project(DiffRast LANGUAGES CUDA CXX) 15 | 16 | set(CMAKE_CXX_STANDARD 17) 17 | set(CMAKE_CXX_EXTENSIONS OFF) 18 | set(CMAKE_CUDA_STANDARD 17) 19 | 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 21 | 22 | add_library(CudaRasterizer 23 | cuda_rasterizer/backward.h 24 | cuda_rasterizer/backward.cu 25 | cuda_rasterizer/forward.h 26 | cuda_rasterizer/forward.cu 27 | cuda_rasterizer/auxiliary.h 28 | cuda_rasterizer/rasterizer_impl.cu 29 | cuda_rasterizer/rasterizer_impl.h 30 | cuda_rasterizer/rasterizer.h 31 | ) 32 | 33 | set_target_properties(CudaRasterizer PROPERTIES CUDA_ARCHITECTURES "75;86") 34 | 35 | target_include_directories(CudaRasterizer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/cuda_rasterizer) 36 | target_include_directories(CudaRasterizer PRIVATE third_party/glm ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) 37 | -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/LICENSE.md: -------------------------------------------------------------------------------- 1 | Gaussian-Splatting License 2 | =========================== 3 | 4 | **Inria** and **the Max Planck Institut for Informatik (MPII)** hold all the ownership rights on the *Software* named **gaussian-splatting**. 5 | The *Software* is in the process of being registered with the Agence pour la Protection des 6 | Programmes (APP). 7 | 8 | The *Software* is still being developed by the *Licensor*. 9 | 10 | *Licensor*'s goal is to allow the research community to use, test and evaluate 11 | the *Software*. 12 | 13 | ## 1. Definitions 14 | 15 | *Licensee* means any person or entity that uses the *Software* and distributes 16 | its *Work*. 17 | 18 | *Licensor* means the owners of the *Software*, i.e Inria and MPII 19 | 20 | *Software* means the original work of authorship made available under this 21 | License ie gaussian-splatting. 22 | 23 | *Work* means the *Software* and any additions to or derivative works of the 24 | *Software* that are made available under this License. 25 | 26 | 27 | ## 2. Purpose 28 | This license is intended to define the rights granted to the *Licensee* by 29 | Licensors under the *Software*. 30 | 31 | ## 3. Rights granted 32 | 33 | For the above reasons Licensors have decided to distribute the *Software*. 34 | Licensors grant non-exclusive rights to use the *Software* for research purposes 35 | to research users (both academic and industrial), free of charge, without right 36 | to sublicense.. The *Software* may be used "non-commercially", i.e., for research 37 | and/or evaluation purposes only. 38 | 39 | Subject to the terms and conditions of this License, you are granted a 40 | non-exclusive, royalty-free, license to reproduce, prepare derivative works of, 41 | publicly display, publicly perform and distribute its *Work* and any resulting 42 | derivative works in any form. 43 | 44 | ## 4. Limitations 45 | 46 | **4.1 Redistribution.** You may reproduce or distribute the *Work* only if (a) you do 47 | so under this License, (b) you include a complete copy of this License with 48 | your distribution, and (c) you retain without modification any copyright, 49 | patent, trademark, or attribution notices that are present in the *Work*. 50 | 51 | **4.2 Derivative Works.** You may specify that additional or different terms apply 52 | to the use, reproduction, and distribution of your derivative works of the *Work* 53 | ("Your Terms") only if (a) Your Terms provide that the use limitation in 54 | Section 2 applies to your derivative works, and (b) you identify the specific 55 | derivative works that are subject to Your Terms. Notwithstanding Your Terms, 56 | this License (including the redistribution requirements in Section 3.1) will 57 | continue to apply to the *Work* itself. 58 | 59 | **4.3** Any other use without of prior consent of Licensors is prohibited. Research 60 | users explicitly acknowledge having received from Licensors all information 61 | allowing to appreciate the adequacy between of the *Software* and their needs and 62 | to undertake all necessary precautions for its execution and use. 63 | 64 | **4.4** The *Software* is provided both as a compiled library file and as source 65 | code. In case of using the *Software* for a publication or other results obtained 66 | through the use of the *Software*, users are strongly encouraged to cite the 67 | corresponding publications as explained in the documentation of the *Software*. 68 | 69 | ## 5. Disclaimer 70 | 71 | THE USER CANNOT USE, EXPLOIT OR DISTRIBUTE THE *SOFTWARE* FOR COMMERCIAL PURPOSES 72 | WITHOUT PRIOR AND EXPLICIT CONSENT OF LICENSORS. YOU MUST CONTACT INRIA FOR ANY 73 | UNAUTHORIZED USE: stip-sophia.transfert@inria.fr . ANY SUCH ACTION WILL 74 | CONSTITUTE A FORGERY. THIS *SOFTWARE* IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES 75 | OF ANY NATURE AND ANY EXPRESS OR IMPLIED WARRANTIES, WITH REGARDS TO COMMERCIAL 76 | USE, PROFESSIONNAL USE, LEGAL OR NOT, OR OTHER, OR COMMERCIALISATION OR 77 | ADAPTATION. UNLESS EXPLICITLY PROVIDED BY LAW, IN NO EVENT, SHALL INRIA OR THE 78 | AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 79 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 80 | GOODS OR SERVICES, LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 81 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 82 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING FROM, OUT OF OR 83 | IN CONNECTION WITH THE *SOFTWARE* OR THE USE OR OTHER DEALINGS IN THE *SOFTWARE*. 84 | -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/README.md: -------------------------------------------------------------------------------- 1 | # Differential Gaussian Rasterization 2 | 3 | **NOTE**: this is a modified version to support depth & alpha rendering (both forward and backward) from the [original repository](https://github.com/graphdeco-inria/diff-gaussian-rasterization). 4 | 5 | ```python 6 | rendered_image, radii, rendered_depth, rendered_alpha = rasterizer( 7 | means3D=means3D, 8 | means2D=means2D, 9 | shs=shs, 10 | colors_precomp=colors_precomp, 11 | opacities=opacity, 12 | scales=scales, 13 | rotations=rotations, 14 | cov3D_precomp=cov3D_precomp, 15 | ) 16 | ``` 17 | 18 | 19 | Used as the rasterization engine for the paper "3D Gaussian Splatting for Real-Time Rendering of Radiance Fields". If you can make use of it in your own research, please be so kind to cite us. 20 | 21 |
22 |
23 |

BibTeX

24 |
@Article{kerbl3Dgaussians,
25 |       author       = {Kerbl, Bernhard and Kopanas, Georgios and Leimk{\"u}hler, Thomas and Drettakis, George},
26 |       title        = {3D Gaussian Splatting for Real-Time Radiance Field Rendering},
27 |       journal      = {ACM Transactions on Graphics},
28 |       number       = {4},
29 |       volume       = {42},
30 |       month        = {July},
31 |       year         = {2023},
32 |       url          = {https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/}
33 | }
34 |
35 |
36 | -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/cuda_rasterizer/auxiliary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #ifndef CUDA_RASTERIZER_AUXILIARY_H_INCLUDED 13 | #define CUDA_RASTERIZER_AUXILIARY_H_INCLUDED 14 | 15 | #include "config.h" 16 | #include "stdio.h" 17 | 18 | #define BLOCK_SIZE (BLOCK_X * BLOCK_Y) 19 | #define NUM_WARPS (BLOCK_SIZE/32) 20 | 21 | // Spherical harmonics coefficients 22 | __device__ const float SH_C0 = 0.28209479177387814f; 23 | __device__ const float SH_C1 = 0.4886025119029199f; 24 | __device__ const float SH_C2[] = { 25 | 1.0925484305920792f, 26 | -1.0925484305920792f, 27 | 0.31539156525252005f, 28 | -1.0925484305920792f, 29 | 0.5462742152960396f 30 | }; 31 | __device__ const float SH_C3[] = { 32 | -0.5900435899266435f, 33 | 2.890611442640554f, 34 | -0.4570457994644658f, 35 | 0.3731763325901154f, 36 | -0.4570457994644658f, 37 | 1.445305721320277f, 38 | -0.5900435899266435f 39 | }; 40 | 41 | __forceinline__ __device__ float ndc2Pix(float v, int S) 42 | { 43 | return ((v + 1.0) * S - 1.0) * 0.5; 44 | } 45 | 46 | __forceinline__ __device__ void getRect(const float2 p, int max_radius, uint2& rect_min, uint2& rect_max, dim3 grid) 47 | { 48 | rect_min = { 49 | min(grid.x, max((int)0, (int)((p.x - max_radius) / BLOCK_X))), 50 | min(grid.y, max((int)0, (int)((p.y - max_radius) / BLOCK_Y))) 51 | }; 52 | rect_max = { 53 | min(grid.x, max((int)0, (int)((p.x + max_radius + BLOCK_X - 1) / BLOCK_X))), 54 | min(grid.y, max((int)0, (int)((p.y + max_radius + BLOCK_Y - 1) / BLOCK_Y))) 55 | }; 56 | } 57 | 58 | __forceinline__ __device__ float3 transformPoint4x3(const float3& p, const float* matrix) 59 | { 60 | float3 transformed = { 61 | matrix[0] * p.x + matrix[4] * p.y + matrix[8] * p.z + matrix[12], 62 | matrix[1] * p.x + matrix[5] * p.y + matrix[9] * p.z + matrix[13], 63 | matrix[2] * p.x + matrix[6] * p.y + matrix[10] * p.z + matrix[14], 64 | }; 65 | return transformed; 66 | } 67 | 68 | __forceinline__ __device__ float4 transformPoint4x4(const float3& p, const float* matrix) 69 | { 70 | float4 transformed = { 71 | matrix[0] * p.x + matrix[4] * p.y + matrix[8] * p.z + matrix[12], 72 | matrix[1] * p.x + matrix[5] * p.y + matrix[9] * p.z + matrix[13], 73 | matrix[2] * p.x + matrix[6] * p.y + matrix[10] * p.z + matrix[14], 74 | matrix[3] * p.x + matrix[7] * p.y + matrix[11] * p.z + matrix[15] 75 | }; 76 | return transformed; 77 | } 78 | 79 | __forceinline__ __device__ float3 transformVec4x3(const float3& p, const float* matrix) 80 | { 81 | float3 transformed = { 82 | matrix[0] * p.x + matrix[4] * p.y + matrix[8] * p.z, 83 | matrix[1] * p.x + matrix[5] * p.y + matrix[9] * p.z, 84 | matrix[2] * p.x + matrix[6] * p.y + matrix[10] * p.z, 85 | }; 86 | return transformed; 87 | } 88 | 89 | __forceinline__ __device__ float3 transformVec4x3Transpose(const float3& p, const float* matrix) 90 | { 91 | float3 transformed = { 92 | matrix[0] * p.x + matrix[1] * p.y + matrix[2] * p.z, 93 | matrix[4] * p.x + matrix[5] * p.y + matrix[6] * p.z, 94 | matrix[8] * p.x + matrix[9] * p.y + matrix[10] * p.z, 95 | }; 96 | return transformed; 97 | } 98 | 99 | __forceinline__ __device__ float dnormvdz(float3 v, float3 dv) 100 | { 101 | float sum2 = v.x * v.x + v.y * v.y + v.z * v.z; 102 | float invsum32 = 1.0f / sqrt(sum2 * sum2 * sum2); 103 | float dnormvdz = (-v.x * v.z * dv.x - v.y * v.z * dv.y + (sum2 - v.z * v.z) * dv.z) * invsum32; 104 | return dnormvdz; 105 | } 106 | 107 | __forceinline__ __device__ float3 dnormvdv(float3 v, float3 dv) 108 | { 109 | float sum2 = v.x * v.x + v.y * v.y + v.z * v.z; 110 | float invsum32 = 1.0f / sqrt(sum2 * sum2 * sum2); 111 | 112 | float3 dnormvdv; 113 | dnormvdv.x = ((+sum2 - v.x * v.x) * dv.x - v.y * v.x * dv.y - v.z * v.x * dv.z) * invsum32; 114 | dnormvdv.y = (-v.x * v.y * dv.x + (sum2 - v.y * v.y) * dv.y - v.z * v.y * dv.z) * invsum32; 115 | dnormvdv.z = (-v.x * v.z * dv.x - v.y * v.z * dv.y + (sum2 - v.z * v.z) * dv.z) * invsum32; 116 | return dnormvdv; 117 | } 118 | 119 | __forceinline__ __device__ float4 dnormvdv(float4 v, float4 dv) 120 | { 121 | float sum2 = v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; 122 | float invsum32 = 1.0f / sqrt(sum2 * sum2 * sum2); 123 | 124 | float4 vdv = { v.x * dv.x, v.y * dv.y, v.z * dv.z, v.w * dv.w }; 125 | float vdv_sum = vdv.x + vdv.y + vdv.z + vdv.w; 126 | float4 dnormvdv; 127 | dnormvdv.x = ((sum2 - v.x * v.x) * dv.x - v.x * (vdv_sum - vdv.x)) * invsum32; 128 | dnormvdv.y = ((sum2 - v.y * v.y) * dv.y - v.y * (vdv_sum - vdv.y)) * invsum32; 129 | dnormvdv.z = ((sum2 - v.z * v.z) * dv.z - v.z * (vdv_sum - vdv.z)) * invsum32; 130 | dnormvdv.w = ((sum2 - v.w * v.w) * dv.w - v.w * (vdv_sum - vdv.w)) * invsum32; 131 | return dnormvdv; 132 | } 133 | 134 | __forceinline__ __device__ float sigmoid(float x) 135 | { 136 | return 1.0f / (1.0f + expf(-x)); 137 | } 138 | 139 | __forceinline__ __device__ bool in_frustum(int idx, 140 | const float* orig_points, 141 | const float* viewmatrix, 142 | const float* projmatrix, 143 | bool prefiltered, 144 | float3& p_view) 145 | { 146 | float3 p_orig = { orig_points[3 * idx], orig_points[3 * idx + 1], orig_points[3 * idx + 2] }; 147 | 148 | // Bring points to screen space 149 | float4 p_hom = transformPoint4x4(p_orig, projmatrix); 150 | float p_w = 1.0f / (p_hom.w + 0.0000001f); 151 | float3 p_proj = { p_hom.x * p_w, p_hom.y * p_w, p_hom.z * p_w }; 152 | p_view = transformPoint4x3(p_orig, viewmatrix); 153 | 154 | if (p_view.z <= 0.2f)// || ((p_proj.x < -1.3 || p_proj.x > 1.3 || p_proj.y < -1.3 || p_proj.y > 1.3))) 155 | { 156 | if (prefiltered) 157 | { 158 | printf("Point is filtered although prefiltered is set. This shouldn't happen!"); 159 | __trap(); 160 | } 161 | return false; 162 | } 163 | return true; 164 | } 165 | 166 | #define CHECK_CUDA(A, debug) \ 167 | A; if(debug) { \ 168 | auto ret = cudaDeviceSynchronize(); \ 169 | if (ret != cudaSuccess) { \ 170 | std::cerr << "\n[CUDA ERROR] in " << __FILE__ << "\nLine " << __LINE__ << ": " << cudaGetErrorString(ret); \ 171 | throw std::runtime_error(cudaGetErrorString(ret)); \ 172 | } \ 173 | } 174 | 175 | #endif -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/cuda_rasterizer/backward.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #ifndef CUDA_RASTERIZER_BACKWARD_H_INCLUDED 13 | #define CUDA_RASTERIZER_BACKWARD_H_INCLUDED 14 | 15 | #include 16 | #include "cuda_runtime.h" 17 | #include "device_launch_parameters.h" 18 | #define GLM_FORCE_CUDA 19 | #include 20 | 21 | namespace BACKWARD 22 | { 23 | void render( 24 | const dim3 grid, dim3 block, 25 | const uint2* ranges, 26 | const uint32_t* point_list, 27 | int W, int H, 28 | const float* bg_color, 29 | const float2* means2D, 30 | const float4* conic_opacity, 31 | const float* colors, 32 | const float* depths, 33 | const float* alphas, 34 | const uint32_t* n_contrib, 35 | const float* dL_dpixels, 36 | const float* dL_dpixel_depths, 37 | const float* dL_dalphas, 38 | float3* dL_dmean2D, 39 | float4* dL_dconic2D, 40 | float* dL_dopacity, 41 | float* dL_dcolors, 42 | float* dL_ddepths); 43 | 44 | void preprocess( 45 | int P, int D, int M, 46 | const float3* means, 47 | const int* radii, 48 | const float* shs, 49 | const bool* clamped, 50 | const glm::vec3* scales, 51 | const glm::vec4* rotations, 52 | const float scale_modifier, 53 | const float* cov3Ds, 54 | const float* view, 55 | const float* proj, 56 | const float focal_x, float focal_y, 57 | const float tan_fovx, float tan_fovy, 58 | const glm::vec3* campos, 59 | const float3* dL_dmean2D, 60 | const float* dL_dconics, 61 | glm::vec3* dL_dmeans, 62 | float* dL_dcolor, 63 | float* dL_ddepth, 64 | float* dL_dcov3D, 65 | float* dL_dsh, 66 | glm::vec3* dL_dscale, 67 | glm::vec4* dL_drot); 68 | } 69 | 70 | #endif -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/cuda_rasterizer/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #ifndef CUDA_RASTERIZER_CONFIG_H_INCLUDED 13 | #define CUDA_RASTERIZER_CONFIG_H_INCLUDED 14 | 15 | #define NUM_CHANNELS 3 // Default 3, RGB 16 | #define BLOCK_X 16 17 | #define BLOCK_Y 16 18 | 19 | #endif -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/cuda_rasterizer/forward.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #ifndef CUDA_RASTERIZER_FORWARD_H_INCLUDED 13 | #define CUDA_RASTERIZER_FORWARD_H_INCLUDED 14 | 15 | #include 16 | #include "cuda_runtime.h" 17 | #include "device_launch_parameters.h" 18 | #define GLM_FORCE_CUDA 19 | #include 20 | 21 | namespace FORWARD 22 | { 23 | // Perform initial steps for each Gaussian prior to rasterization. 24 | void preprocess(int P, int D, int M, 25 | const float* orig_points, 26 | const glm::vec3* scales, 27 | const float scale_modifier, 28 | const glm::vec4* rotations, 29 | const float* opacities, 30 | const float* shs, 31 | bool* clamped, 32 | const float* cov3D_precomp, 33 | const float* colors_precomp, 34 | const float* viewmatrix, 35 | const float* projmatrix, 36 | const glm::vec3* cam_pos, 37 | const int W, int H, 38 | const float focal_x, float focal_y, 39 | const float tan_fovx, float tan_fovy, 40 | int* radii, 41 | float2* points_xy_image, 42 | float* depths, 43 | float* cov3Ds, 44 | float* colors, 45 | float4* conic_opacity, 46 | const dim3 grid, 47 | uint32_t* tiles_touched, 48 | bool prefiltered); 49 | 50 | // Main rasterization method. 51 | void render( 52 | const dim3 grid, dim3 block, 53 | const uint2* ranges, 54 | const uint32_t* point_list, 55 | int W, int H, 56 | const float2* points_xy_image, 57 | const float* features, 58 | const float* depths, 59 | const float4* conic_opacity, 60 | float* out_alpha, 61 | uint32_t* n_contrib, 62 | const float* bg_color, 63 | float* out_color, 64 | float* out_depth); 65 | } 66 | 67 | 68 | #endif -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/cuda_rasterizer/rasterizer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #ifndef CUDA_RASTERIZER_H_INCLUDED 13 | #define CUDA_RASTERIZER_H_INCLUDED 14 | 15 | #include 16 | #include 17 | 18 | namespace CudaRasterizer 19 | { 20 | class Rasterizer 21 | { 22 | public: 23 | 24 | static void markVisible( 25 | int P, 26 | float* means3D, 27 | float* viewmatrix, 28 | float* projmatrix, 29 | bool* present); 30 | 31 | static int forward( 32 | std::function geometryBuffer, 33 | std::function binningBuffer, 34 | std::function imageBuffer, 35 | const int P, int D, int M, 36 | const float* background, 37 | const int width, int height, 38 | const float* means3D, 39 | const float* shs, 40 | const float* colors_precomp, 41 | const float* opacities, 42 | const float* scales, 43 | const float scale_modifier, 44 | const float* rotations, 45 | const float* cov3D_precomp, 46 | const float* viewmatrix, 47 | const float* projmatrix, 48 | const float* cam_pos, 49 | const float tan_fovx, float tan_fovy, 50 | const bool prefiltered, 51 | float* out_color, 52 | float* out_depth, 53 | float* out_alpha, 54 | int* radii = nullptr, 55 | bool debug = false); 56 | 57 | static void backward( 58 | const int P, int D, int M, int R, 59 | const float* background, 60 | const int width, int height, 61 | const float* means3D, 62 | const float* shs, 63 | const float* colors_precomp, 64 | const float* alphas, 65 | const float* scales, 66 | const float scale_modifier, 67 | const float* rotations, 68 | const float* cov3D_precomp, 69 | const float* viewmatrix, 70 | const float* projmatrix, 71 | const float* campos, 72 | const float tan_fovx, float tan_fovy, 73 | const int* radii, 74 | char* geom_buffer, 75 | char* binning_buffer, 76 | char* image_buffer, 77 | const float* dL_dpix, 78 | const float* dL_dpix_depth, 79 | const float* dL_dalphas, 80 | float* dL_dmean2D, 81 | float* dL_dconic, 82 | float* dL_dopacity, 83 | float* dL_dcolor, 84 | float* dL_ddepth, 85 | float* dL_dmean3D, 86 | float* dL_dcov3D, 87 | float* dL_dsh, 88 | float* dL_dscale, 89 | float* dL_drot, 90 | bool debug); 91 | }; 92 | }; 93 | 94 | #endif -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/cuda_rasterizer/rasterizer_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include "rasterizer.h" 17 | #include 18 | 19 | namespace CudaRasterizer 20 | { 21 | template 22 | static void obtain(char*& chunk, T*& ptr, std::size_t count, std::size_t alignment) 23 | { 24 | std::size_t offset = (reinterpret_cast(chunk) + alignment - 1) & ~(alignment - 1); 25 | ptr = reinterpret_cast(offset); 26 | chunk = reinterpret_cast(ptr + count); 27 | } 28 | 29 | struct GeometryState 30 | { 31 | size_t scan_size; 32 | float* depths; 33 | char* scanning_space; 34 | bool* clamped; 35 | int* internal_radii; 36 | float2* means2D; 37 | float* cov3D; 38 | float4* conic_opacity; 39 | float* rgb; 40 | uint32_t* point_offsets; 41 | uint32_t* tiles_touched; 42 | 43 | static GeometryState fromChunk(char*& chunk, size_t P); 44 | }; 45 | 46 | struct ImageState 47 | { 48 | uint2* ranges; 49 | uint32_t* n_contrib; 50 | 51 | static ImageState fromChunk(char*& chunk, size_t N); 52 | }; 53 | 54 | struct BinningState 55 | { 56 | size_t sorting_size; 57 | uint64_t* point_list_keys_unsorted; 58 | uint64_t* point_list_keys; 59 | uint32_t* point_list_unsorted; 60 | uint32_t* point_list; 61 | char* list_sorting_space; 62 | 63 | static BinningState fromChunk(char*& chunk, size_t P); 64 | }; 65 | 66 | template 67 | size_t required(size_t P) 68 | { 69 | char* size = nullptr; 70 | T::fromChunk(size, P); 71 | return ((size_t)size) + 128; 72 | } 73 | }; -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/diff_gaussian_rasterization/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | from typing import NamedTuple 13 | import torch.nn as nn 14 | import torch 15 | from . import _C 16 | 17 | def cpu_deep_copy_tuple(input_tuple): 18 | copied_tensors = [item.cpu().clone() if isinstance(item, torch.Tensor) else item for item in input_tuple] 19 | return tuple(copied_tensors) 20 | 21 | def rasterize_gaussians( 22 | means3D, 23 | means2D, 24 | sh, 25 | colors_precomp, 26 | opacities, 27 | scales, 28 | rotations, 29 | cov3Ds_precomp, 30 | raster_settings, 31 | ): 32 | return _RasterizeGaussians.apply( 33 | means3D, 34 | means2D, 35 | sh, 36 | colors_precomp, 37 | opacities, 38 | scales, 39 | rotations, 40 | cov3Ds_precomp, 41 | raster_settings, 42 | ) 43 | 44 | class _RasterizeGaussians(torch.autograd.Function): 45 | @staticmethod 46 | def forward( 47 | ctx, 48 | means3D, 49 | means2D, 50 | sh, 51 | colors_precomp, 52 | opacities, 53 | scales, 54 | rotations, 55 | cov3Ds_precomp, 56 | raster_settings, 57 | ): 58 | 59 | # Restructure arguments the way that the C++ lib expects them 60 | args = ( 61 | raster_settings.bg, 62 | means3D, 63 | colors_precomp, 64 | opacities, 65 | scales, 66 | rotations, 67 | raster_settings.scale_modifier, 68 | cov3Ds_precomp, 69 | raster_settings.viewmatrix, 70 | raster_settings.projmatrix, 71 | raster_settings.tanfovx, 72 | raster_settings.tanfovy, 73 | raster_settings.image_height, 74 | raster_settings.image_width, 75 | sh, 76 | raster_settings.sh_degree, 77 | raster_settings.campos, 78 | raster_settings.prefiltered, 79 | raster_settings.debug 80 | ) 81 | 82 | # Invoke C++/CUDA rasterizer 83 | if raster_settings.debug: 84 | cpu_args = cpu_deep_copy_tuple(args) # Copy them before they can be corrupted 85 | try: 86 | num_rendered, color, depth, alpha, radii, geomBuffer, binningBuffer, imgBuffer = _C.rasterize_gaussians(*args) 87 | except Exception as ex: 88 | torch.save(cpu_args, "snapshot_fw.dump") 89 | print("\nAn error occured in forward. Please forward snapshot_fw.dump for debugging.") 90 | raise ex 91 | else: 92 | num_rendered, color, depth, alpha, radii, geomBuffer, binningBuffer, imgBuffer = _C.rasterize_gaussians(*args) 93 | 94 | # Keep relevant tensors for backward 95 | ctx.raster_settings = raster_settings 96 | ctx.num_rendered = num_rendered 97 | ctx.save_for_backward(colors_precomp, means3D, scales, rotations, cov3Ds_precomp, radii, sh, geomBuffer, binningBuffer, imgBuffer, alpha) 98 | return color, radii, depth, alpha 99 | 100 | @staticmethod 101 | def backward(ctx, grad_color, grad_radii, grad_depth, grad_alpha): 102 | 103 | # Restore necessary values from context 104 | num_rendered = ctx.num_rendered 105 | raster_settings = ctx.raster_settings 106 | colors_precomp, means3D, scales, rotations, cov3Ds_precomp, radii, sh, geomBuffer, binningBuffer, imgBuffer, alpha = ctx.saved_tensors 107 | 108 | # Restructure args as C++ method expects them 109 | args = (raster_settings.bg, 110 | means3D, 111 | radii, 112 | colors_precomp, 113 | scales, 114 | rotations, 115 | raster_settings.scale_modifier, 116 | cov3Ds_precomp, 117 | raster_settings.viewmatrix, 118 | raster_settings.projmatrix, 119 | raster_settings.tanfovx, 120 | raster_settings.tanfovy, 121 | grad_color, 122 | grad_depth, 123 | grad_alpha, 124 | sh, 125 | raster_settings.sh_degree, 126 | raster_settings.campos, 127 | geomBuffer, 128 | num_rendered, 129 | binningBuffer, 130 | imgBuffer, 131 | alpha, 132 | raster_settings.debug) 133 | 134 | # Compute gradients for relevant tensors by invoking backward method 135 | if raster_settings.debug: 136 | cpu_args = cpu_deep_copy_tuple(args) # Copy them before they can be corrupted 137 | try: 138 | grad_means2D, grad_colors_precomp, grad_opacities, grad_means3D, grad_cov3Ds_precomp, grad_sh, grad_scales, grad_rotations = _C.rasterize_gaussians_backward(*args) 139 | except Exception as ex: 140 | torch.save(cpu_args, "snapshot_bw.dump") 141 | print("\nAn error occured in backward. Writing snapshot_bw.dump for debugging.\n") 142 | raise ex 143 | else: 144 | grad_means2D, grad_colors_precomp, grad_opacities, grad_means3D, grad_cov3Ds_precomp, grad_sh, grad_scales, grad_rotations = _C.rasterize_gaussians_backward(*args) 145 | 146 | grads = ( 147 | grad_means3D, 148 | grad_means2D, 149 | grad_sh, 150 | grad_colors_precomp, 151 | grad_opacities, 152 | grad_scales, 153 | grad_rotations, 154 | grad_cov3Ds_precomp, 155 | None, 156 | ) 157 | 158 | return grads 159 | 160 | class GaussianRasterizationSettings(NamedTuple): 161 | image_height: int 162 | image_width: int 163 | tanfovx : float 164 | tanfovy : float 165 | bg : torch.Tensor 166 | scale_modifier : float 167 | viewmatrix : torch.Tensor 168 | projmatrix : torch.Tensor 169 | sh_degree : int 170 | campos : torch.Tensor 171 | prefiltered : bool 172 | debug : bool 173 | 174 | class GaussianRasterizer(nn.Module): 175 | def __init__(self, raster_settings): 176 | super().__init__() 177 | self.raster_settings = raster_settings 178 | 179 | def markVisible(self, positions): 180 | # Mark visible points (based on frustum culling for camera) with a boolean 181 | with torch.no_grad(): 182 | raster_settings = self.raster_settings 183 | visible = _C.mark_visible( 184 | positions, 185 | raster_settings.viewmatrix, 186 | raster_settings.projmatrix) 187 | 188 | return visible 189 | 190 | def forward(self, means3D, means2D, opacities, shs = None, colors_precomp = None, scales = None, rotations = None, cov3D_precomp = None): 191 | 192 | raster_settings = self.raster_settings 193 | 194 | if (shs is None and colors_precomp is None) or (shs is not None and colors_precomp is not None): 195 | raise Exception('Please provide excatly one of either SHs or precomputed colors!') 196 | 197 | if ((scales is None or rotations is None) and cov3D_precomp is None) or ((scales is not None or rotations is not None) and cov3D_precomp is not None): 198 | raise Exception('Please provide exactly one of either scale/rotation pair or precomputed 3D covariance!') 199 | 200 | if shs is None: 201 | shs = torch.Tensor([]) 202 | if colors_precomp is None: 203 | colors_precomp = torch.Tensor([]) 204 | 205 | if scales is None: 206 | scales = torch.Tensor([]) 207 | if rotations is None: 208 | rotations = torch.Tensor([]) 209 | if cov3D_precomp is None: 210 | cov3D_precomp = torch.Tensor([]) 211 | 212 | # Invoke C++/CUDA rasterization routine 213 | return rasterize_gaussians( 214 | means3D, 215 | means2D, 216 | shs, 217 | colors_precomp, 218 | opacities, 219 | scales, 220 | rotations, 221 | cov3D_precomp, 222 | raster_settings, 223 | ) 224 | 225 | -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/ext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #include 13 | #include "rasterize_points.h" 14 | 15 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 16 | m.def("rasterize_gaussians", &RasterizeGaussiansCUDA); 17 | m.def("rasterize_gaussians_backward", &RasterizeGaussiansBackwardCUDA); 18 | m.def("mark_visible", &markVisible); 19 | } -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/rasterize_points.cu: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "cuda_rasterizer/config.h" 22 | #include "cuda_rasterizer/rasterizer.h" 23 | #include 24 | #include 25 | #include 26 | 27 | std::function resizeFunctional(torch::Tensor& t) { 28 | auto lambda = [&t](size_t N) { 29 | t.resize_({(long long)N}); 30 | return reinterpret_cast(t.contiguous().data_ptr()); 31 | }; 32 | return lambda; 33 | } 34 | 35 | std::tuple 36 | RasterizeGaussiansCUDA( 37 | const torch::Tensor& background, 38 | const torch::Tensor& means3D, 39 | const torch::Tensor& colors, 40 | const torch::Tensor& opacity, 41 | const torch::Tensor& scales, 42 | const torch::Tensor& rotations, 43 | const float scale_modifier, 44 | const torch::Tensor& cov3D_precomp, 45 | const torch::Tensor& viewmatrix, 46 | const torch::Tensor& projmatrix, 47 | const float tan_fovx, 48 | const float tan_fovy, 49 | const int image_height, 50 | const int image_width, 51 | const torch::Tensor& sh, 52 | const int degree, 53 | const torch::Tensor& campos, 54 | const bool prefiltered, 55 | const bool debug) 56 | { 57 | if (means3D.ndimension() != 2 || means3D.size(1) != 3) { 58 | AT_ERROR("means3D must have dimensions (num_points, 3)"); 59 | } 60 | 61 | const int P = means3D.size(0); 62 | const int H = image_height; 63 | const int W = image_width; 64 | 65 | auto int_opts = means3D.options().dtype(torch::kInt32); 66 | auto float_opts = means3D.options().dtype(torch::kFloat32); 67 | 68 | torch::Tensor out_color = torch::full({NUM_CHANNELS, H, W}, 0.0, float_opts); 69 | torch::Tensor out_depth = torch::full({1, H, W}, 0.0, float_opts); 70 | torch::Tensor out_alpha = torch::full({1, H, W}, 0.0, float_opts); 71 | torch::Tensor radii = torch::full({P}, 0, means3D.options().dtype(torch::kInt32)); 72 | 73 | torch::Device device(torch::kCUDA); 74 | torch::TensorOptions options(torch::kByte); 75 | torch::Tensor geomBuffer = torch::empty({0}, options.device(device)); 76 | torch::Tensor binningBuffer = torch::empty({0}, options.device(device)); 77 | torch::Tensor imgBuffer = torch::empty({0}, options.device(device)); 78 | std::function geomFunc = resizeFunctional(geomBuffer); 79 | std::function binningFunc = resizeFunctional(binningBuffer); 80 | std::function imgFunc = resizeFunctional(imgBuffer); 81 | 82 | int rendered = 0; 83 | if(P != 0) 84 | { 85 | int M = 0; 86 | if(sh.size(0) != 0) 87 | { 88 | M = sh.size(1); 89 | } 90 | 91 | rendered = CudaRasterizer::Rasterizer::forward( 92 | geomFunc, 93 | binningFunc, 94 | imgFunc, 95 | P, degree, M, 96 | background.contiguous().data(), 97 | W, H, 98 | means3D.contiguous().data(), 99 | sh.contiguous().data_ptr(), 100 | colors.contiguous().data(), 101 | opacity.contiguous().data(), 102 | scales.contiguous().data_ptr(), 103 | scale_modifier, 104 | rotations.contiguous().data_ptr(), 105 | cov3D_precomp.contiguous().data(), 106 | viewmatrix.contiguous().data(), 107 | projmatrix.contiguous().data(), 108 | campos.contiguous().data(), 109 | tan_fovx, 110 | tan_fovy, 111 | prefiltered, 112 | out_color.contiguous().data(), 113 | out_depth.contiguous().data(), 114 | out_alpha.contiguous().data(), 115 | radii.contiguous().data(), 116 | debug); 117 | } 118 | return std::make_tuple(rendered, out_color, out_depth, out_alpha, radii, geomBuffer, binningBuffer, imgBuffer); 119 | } 120 | 121 | std::tuple 122 | RasterizeGaussiansBackwardCUDA( 123 | const torch::Tensor& background, 124 | const torch::Tensor& means3D, 125 | const torch::Tensor& radii, 126 | const torch::Tensor& colors, 127 | const torch::Tensor& scales, 128 | const torch::Tensor& rotations, 129 | const float scale_modifier, 130 | const torch::Tensor& cov3D_precomp, 131 | const torch::Tensor& viewmatrix, 132 | const torch::Tensor& projmatrix, 133 | const float tan_fovx, 134 | const float tan_fovy, 135 | const torch::Tensor& dL_dout_color, 136 | const torch::Tensor& dL_dout_depth, 137 | const torch::Tensor& dL_dout_alpha, 138 | const torch::Tensor& sh, 139 | const int degree, 140 | const torch::Tensor& campos, 141 | const torch::Tensor& geomBuffer, 142 | const int R, 143 | const torch::Tensor& binningBuffer, 144 | const torch::Tensor& imageBuffer, 145 | const torch::Tensor& alphas, 146 | const bool debug) 147 | { 148 | const int P = means3D.size(0); 149 | const int H = dL_dout_color.size(1); 150 | const int W = dL_dout_color.size(2); 151 | 152 | int M = 0; 153 | if(sh.size(0) != 0) 154 | { 155 | M = sh.size(1); 156 | } 157 | 158 | torch::Tensor dL_dmeans3D = torch::zeros({P, 3}, means3D.options()); 159 | torch::Tensor dL_dmeans2D = torch::zeros({P, 3}, means3D.options()); 160 | torch::Tensor dL_dcolors = torch::zeros({P, NUM_CHANNELS}, means3D.options()); 161 | torch::Tensor dL_ddepths = torch::zeros({P, 1}, means3D.options()); 162 | torch::Tensor dL_dconic = torch::zeros({P, 2, 2}, means3D.options()); 163 | torch::Tensor dL_dopacity = torch::zeros({P, 1}, means3D.options()); 164 | torch::Tensor dL_dcov3D = torch::zeros({P, 6}, means3D.options()); 165 | torch::Tensor dL_dsh = torch::zeros({P, M, 3}, means3D.options()); 166 | torch::Tensor dL_dscales = torch::zeros({P, 3}, means3D.options()); 167 | torch::Tensor dL_drotations = torch::zeros({P, 4}, means3D.options()); 168 | 169 | if(P != 0) 170 | { 171 | CudaRasterizer::Rasterizer::backward(P, degree, M, R, 172 | background.contiguous().data(), 173 | W, H, 174 | means3D.contiguous().data(), 175 | sh.contiguous().data(), 176 | colors.contiguous().data(), 177 | alphas.contiguous().data(), 178 | scales.data_ptr(), 179 | scale_modifier, 180 | rotations.data_ptr(), 181 | cov3D_precomp.contiguous().data(), 182 | viewmatrix.contiguous().data(), 183 | projmatrix.contiguous().data(), 184 | campos.contiguous().data(), 185 | tan_fovx, 186 | tan_fovy, 187 | radii.contiguous().data(), 188 | reinterpret_cast(geomBuffer.contiguous().data_ptr()), 189 | reinterpret_cast(binningBuffer.contiguous().data_ptr()), 190 | reinterpret_cast(imageBuffer.contiguous().data_ptr()), 191 | dL_dout_color.contiguous().data(), 192 | dL_dout_depth.contiguous().data(), 193 | dL_dout_alpha.contiguous().data(), 194 | dL_dmeans2D.contiguous().data(), 195 | dL_dconic.contiguous().data(), 196 | dL_dopacity.contiguous().data(), 197 | dL_dcolors.contiguous().data(), 198 | dL_ddepths.contiguous().data(), 199 | dL_dmeans3D.contiguous().data(), 200 | dL_dcov3D.contiguous().data(), 201 | dL_dsh.contiguous().data(), 202 | dL_dscales.contiguous().data(), 203 | dL_drotations.contiguous().data(), 204 | debug); 205 | } 206 | 207 | return std::make_tuple(dL_dmeans2D, dL_dcolors, dL_dopacity, dL_dmeans3D, dL_dcov3D, dL_dsh, dL_dscales, dL_drotations); 208 | } 209 | 210 | torch::Tensor markVisible( 211 | torch::Tensor& means3D, 212 | torch::Tensor& viewmatrix, 213 | torch::Tensor& projmatrix) 214 | { 215 | const int P = means3D.size(0); 216 | 217 | torch::Tensor present = torch::full({P}, false, means3D.options().dtype(at::kBool)); 218 | 219 | if(P != 0) 220 | { 221 | CudaRasterizer::Rasterizer::markVisible(P, 222 | means3D.contiguous().data(), 223 | viewmatrix.contiguous().data(), 224 | projmatrix.contiguous().data(), 225 | present.contiguous().data()); 226 | } 227 | 228 | return present; 229 | } -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/rasterize_points.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #pragma once 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | std::tuple 19 | RasterizeGaussiansCUDA( 20 | const torch::Tensor& background, 21 | const torch::Tensor& means3D, 22 | const torch::Tensor& colors, 23 | const torch::Tensor& opacity, 24 | const torch::Tensor& scales, 25 | const torch::Tensor& rotations, 26 | const float scale_modifier, 27 | const torch::Tensor& cov3D_precomp, 28 | const torch::Tensor& viewmatrix, 29 | const torch::Tensor& projmatrix, 30 | const float tan_fovx, 31 | const float tan_fovy, 32 | const int image_height, 33 | const int image_width, 34 | const torch::Tensor& sh, 35 | const int degree, 36 | const torch::Tensor& campos, 37 | const bool prefiltered, 38 | const bool debug); 39 | 40 | std::tuple 41 | RasterizeGaussiansBackwardCUDA( 42 | const torch::Tensor& background, 43 | const torch::Tensor& means3D, 44 | const torch::Tensor& radii, 45 | const torch::Tensor& colors, 46 | const torch::Tensor& scales, 47 | const torch::Tensor& rotations, 48 | const float scale_modifier, 49 | const torch::Tensor& cov3D_precomp, 50 | const torch::Tensor& viewmatrix, 51 | const torch::Tensor& projmatrix, 52 | const float tan_fovx, 53 | const float tan_fovy, 54 | const torch::Tensor& dL_dout_color, 55 | const torch::Tensor& dL_dout_depth, 56 | const torch::Tensor& dL_dout_alpha, 57 | const torch::Tensor& sh, 58 | const int degree, 59 | const torch::Tensor& campos, 60 | const torch::Tensor& geomBuffer, 61 | const int R, 62 | const torch::Tensor& binningBuffer, 63 | const torch::Tensor& imageBuffer, 64 | const torch::Tensor& alpha, 65 | const bool debug); 66 | 67 | torch::Tensor markVisible( 68 | torch::Tensor& means3D, 69 | torch::Tensor& viewmatrix, 70 | torch::Tensor& projmatrix); -------------------------------------------------------------------------------- /submodules/diff-gaussian-rasterization/setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | from setuptools import setup 13 | from torch.utils.cpp_extension import CUDAExtension, BuildExtension 14 | import os 15 | os.path.dirname(os.path.abspath(__file__)) 16 | 17 | setup( 18 | name="diff_gaussian_rasterization", 19 | packages=['diff_gaussian_rasterization'], 20 | ext_modules=[ 21 | CUDAExtension( 22 | name="diff_gaussian_rasterization._C", 23 | sources=[ 24 | "cuda_rasterizer/rasterizer_impl.cu", 25 | "cuda_rasterizer/forward.cu", 26 | "cuda_rasterizer/backward.cu", 27 | "rasterize_points.cu", 28 | "ext.cpp"], 29 | extra_compile_args={"nvcc": ["-I" + os.path.join(os.path.dirname(os.path.abspath(__file__)), "third_party/glm/")]}) 30 | ], 31 | cmdclass={ 32 | 'build_ext': BuildExtension 33 | } 34 | ) 35 | -------------------------------------------------------------------------------- /submodules/simple-knn/LICENSE.md: -------------------------------------------------------------------------------- 1 | Gaussian-Splatting License 2 | =========================== 3 | 4 | **Inria** and **the Max Planck Institut for Informatik (MPII)** hold all the ownership rights on the *Software* named **gaussian-splatting**. 5 | The *Software* is in the process of being registered with the Agence pour la Protection des 6 | Programmes (APP). 7 | 8 | The *Software* is still being developed by the *Licensor*. 9 | 10 | *Licensor*'s goal is to allow the research community to use, test and evaluate 11 | the *Software*. 12 | 13 | ## 1. Definitions 14 | 15 | *Licensee* means any person or entity that uses the *Software* and distributes 16 | its *Work*. 17 | 18 | *Licensor* means the owners of the *Software*, i.e Inria and MPII 19 | 20 | *Software* means the original work of authorship made available under this 21 | License ie gaussian-splatting. 22 | 23 | *Work* means the *Software* and any additions to or derivative works of the 24 | *Software* that are made available under this License. 25 | 26 | 27 | ## 2. Purpose 28 | This license is intended to define the rights granted to the *Licensee* by 29 | Licensors under the *Software*. 30 | 31 | ## 3. Rights granted 32 | 33 | For the above reasons Licensors have decided to distribute the *Software*. 34 | Licensors grant non-exclusive rights to use the *Software* for research purposes 35 | to research users (both academic and industrial), free of charge, without right 36 | to sublicense.. The *Software* may be used "non-commercially", i.e., for research 37 | and/or evaluation purposes only. 38 | 39 | Subject to the terms and conditions of this License, you are granted a 40 | non-exclusive, royalty-free, license to reproduce, prepare derivative works of, 41 | publicly display, publicly perform and distribute its *Work* and any resulting 42 | derivative works in any form. 43 | 44 | ## 4. Limitations 45 | 46 | **4.1 Redistribution.** You may reproduce or distribute the *Work* only if (a) you do 47 | so under this License, (b) you include a complete copy of this License with 48 | your distribution, and (c) you retain without modification any copyright, 49 | patent, trademark, or attribution notices that are present in the *Work*. 50 | 51 | **4.2 Derivative Works.** You may specify that additional or different terms apply 52 | to the use, reproduction, and distribution of your derivative works of the *Work* 53 | ("Your Terms") only if (a) Your Terms provide that the use limitation in 54 | Section 2 applies to your derivative works, and (b) you identify the specific 55 | derivative works that are subject to Your Terms. Notwithstanding Your Terms, 56 | this License (including the redistribution requirements in Section 3.1) will 57 | continue to apply to the *Work* itself. 58 | 59 | **4.3** Any other use without of prior consent of Licensors is prohibited. Research 60 | users explicitly acknowledge having received from Licensors all information 61 | allowing to appreciate the adequacy between of the *Software* and their needs and 62 | to undertake all necessary precautions for its execution and use. 63 | 64 | **4.4** The *Software* is provided both as a compiled library file and as source 65 | code. In case of using the *Software* for a publication or other results obtained 66 | through the use of the *Software*, users are strongly encouraged to cite the 67 | corresponding publications as explained in the documentation of the *Software*. 68 | 69 | ## 5. Disclaimer 70 | 71 | THE USER CANNOT USE, EXPLOIT OR DISTRIBUTE THE *SOFTWARE* FOR COMMERCIAL PURPOSES 72 | WITHOUT PRIOR AND EXPLICIT CONSENT OF LICENSORS. YOU MUST CONTACT INRIA FOR ANY 73 | UNAUTHORIZED USE: stip-sophia.transfert@inria.fr . ANY SUCH ACTION WILL 74 | CONSTITUTE A FORGERY. THIS *SOFTWARE* IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES 75 | OF ANY NATURE AND ANY EXPRESS OR IMPLIED WARRANTIES, WITH REGARDS TO COMMERCIAL 76 | USE, PROFESSIONNAL USE, LEGAL OR NOT, OR OTHER, OR COMMERCIALISATION OR 77 | ADAPTATION. UNLESS EXPLICITLY PROVIDED BY LAW, IN NO EVENT, SHALL INRIA OR THE 78 | AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 79 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 80 | GOODS OR SERVICES, LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) 81 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 82 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING FROM, OUT OF OR 83 | IN CONNECTION WITH THE *SOFTWARE* OR THE USE OR OTHER DEALINGS IN THE *SOFTWARE*. 84 | 85 | ## 6. Files subject to permissive licenses 86 | The contents of the file ```utils/loss_utils.py``` are based on publicly available code authored by Evan Su, which falls under the permissive MIT license. 87 | 88 | Title: pytorch-ssim\ 89 | Project code: https://github.com/Po-Hsun-Su/pytorch-ssim\ 90 | Copyright Evan Su, 2017\ 91 | License: https://github.com/Po-Hsun-Su/pytorch-ssim/blob/master/LICENSE.txt (MIT) -------------------------------------------------------------------------------- /submodules/simple-knn/build/lib.linux-x86_64-cpython-310/simple_knn/_C.cpython-310-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/submodules/simple-knn/build/lib.linux-x86_64-cpython-310/simple_knn/_C.cpython-310-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /submodules/simple-knn/build/temp.linux-x86_64-cpython-310/.ninja_deps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/.ninja_deps -------------------------------------------------------------------------------- /submodules/simple-knn/build/temp.linux-x86_64-cpython-310/.ninja_log: -------------------------------------------------------------------------------- 1 | # ninja log v5 2 | 0 12982 1740924351317050518 /data/hbb/nlp/SC-GS/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/simple_knn.o 9805073580aaa04f 3 | 0 21594 1740924359921146916 /data/hbb/nlp/SC-GS/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/ext.o f43c02b0bed76cf4 4 | 0 53399 1740924391737491202 /data/hbb/nlp/SC-GS/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/spatial.o f12d5314fda66397 5 | -------------------------------------------------------------------------------- /submodules/simple-knn/build/temp.linux-x86_64-cpython-310/build.ninja: -------------------------------------------------------------------------------- 1 | ninja_required_version = 1.3 2 | cxx = c++ 3 | nvcc = /home/hbb/data/cuda-11.7/bin/nvcc 4 | 5 | cflags = -pthread -B /data/hbb/anaconda3/envs/SC-GS/compiler_compat -Wno-unused-result -Wsign-compare -DNDEBUG -fwrapv -O2 -Wall -fPIC -O2 -isystem /data/hbb/anaconda3/envs/SC-GS/include -fPIC -O2 -isystem /data/hbb/anaconda3/envs/SC-GS/include -fPIC -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include/TH -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include/THC -I/home/hbb/data/cuda-11.7/include -I/data/hbb/anaconda3/envs/SC-GS/include/python3.10 -c 6 | post_cflags = -DTORCH_API_INCLUDE_EXTENSION_H '-DPYBIND11_COMPILER_TYPE="_gcc"' '-DPYBIND11_STDLIB="_libstdcpp"' '-DPYBIND11_BUILD_ABI="_cxxabi1011"' -DTORCH_EXTENSION_NAME=_C -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++14 7 | cuda_cflags = -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include/TH -I/data/hbb/anaconda3/envs/SC-GS/lib/python3.10/site-packages/torch/include/THC -I/home/hbb/data/cuda-11.7/include -I/data/hbb/anaconda3/envs/SC-GS/include/python3.10 -c 8 | cuda_post_cflags = -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr --compiler-options ''"'"'-fPIC'"'"'' -DTORCH_API_INCLUDE_EXTENSION_H '-DPYBIND11_COMPILER_TYPE="_gcc"' '-DPYBIND11_STDLIB="_libstdcpp"' '-DPYBIND11_BUILD_ABI="_cxxabi1011"' -DTORCH_EXTENSION_NAME=_C -D_GLIBCXX_USE_CXX11_ABI=0 -gencode=arch=compute_86,code=compute_86 -gencode=arch=compute_86,code=sm_86 -std=c++14 9 | cuda_dlink_post_cflags = 10 | ldflags = 11 | 12 | rule compile 13 | command = $cxx -MMD -MF $out.d $cflags -c $in -o $out $post_cflags 14 | depfile = $out.d 15 | deps = gcc 16 | 17 | rule cuda_compile 18 | depfile = $out.d 19 | deps = gcc 20 | command = $nvcc $cuda_cflags -c $in -o $out $cuda_post_cflags 21 | 22 | 23 | 24 | 25 | 26 | build /data/hbb/nlp/SC-GS/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/ext.o: compile /data/hbb/nlp/SC-GS/submodules/simple-knn/ext.cpp 27 | build /data/hbb/nlp/SC-GS/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/simple_knn.o: cuda_compile /data/hbb/nlp/SC-GS/submodules/simple-knn/simple_knn.cu 28 | build /data/hbb/nlp/SC-GS/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/spatial.o: cuda_compile /data/hbb/nlp/SC-GS/submodules/simple-knn/spatial.cu 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /submodules/simple-knn/build/temp.linux-x86_64-cpython-310/ext.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/ext.o -------------------------------------------------------------------------------- /submodules/simple-knn/build/temp.linux-x86_64-cpython-310/simple_knn.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/simple_knn.o -------------------------------------------------------------------------------- /submodules/simple-knn/build/temp.linux-x86_64-cpython-310/spatial.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/submodules/simple-knn/build/temp.linux-x86_64-cpython-310/spatial.o -------------------------------------------------------------------------------- /submodules/simple-knn/ext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #include 13 | #include "spatial.h" 14 | 15 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 16 | m.def("distCUDA2", &distCUDA2); 17 | } 18 | -------------------------------------------------------------------------------- /submodules/simple-knn/setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | from setuptools import setup 13 | from torch.utils.cpp_extension import CUDAExtension, BuildExtension 14 | import os 15 | 16 | cxx_compiler_flags = [] 17 | 18 | if os.name == 'nt': 19 | cxx_compiler_flags.append("/wd4624") 20 | 21 | setup( 22 | name="simple_knn", 23 | ext_modules=[ 24 | CUDAExtension( 25 | name="simple_knn._C", 26 | sources=[ 27 | "spatial.cu", 28 | "simple_knn.cu", 29 | "ext.cpp"], 30 | extra_compile_args={"nvcc": [], "cxx": cxx_compiler_flags}) 31 | ], 32 | cmdclass={ 33 | 'build_ext': BuildExtension 34 | } 35 | ) 36 | -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn.cu: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #define BOX_SIZE 1024 13 | 14 | #include "cuda_runtime.h" 15 | #include "device_launch_parameters.h" 16 | #include "simple_knn.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #define __CUDACC__ 25 | #include 26 | #include 27 | 28 | namespace cg = cooperative_groups; 29 | 30 | struct CustomMin 31 | { 32 | __device__ __forceinline__ 33 | float3 operator()(const float3& a, const float3& b) const { 34 | return { min(a.x, b.x), min(a.y, b.y), min(a.z, b.z) }; 35 | } 36 | }; 37 | 38 | struct CustomMax 39 | { 40 | __device__ __forceinline__ 41 | float3 operator()(const float3& a, const float3& b) const { 42 | return { max(a.x, b.x), max(a.y, b.y), max(a.z, b.z) }; 43 | } 44 | }; 45 | 46 | __host__ __device__ uint32_t prepMorton(uint32_t x) 47 | { 48 | x = (x | (x << 16)) & 0x030000FF; 49 | x = (x | (x << 8)) & 0x0300F00F; 50 | x = (x | (x << 4)) & 0x030C30C3; 51 | x = (x | (x << 2)) & 0x09249249; 52 | return x; 53 | } 54 | 55 | __host__ __device__ uint32_t coord2Morton(float3 coord, float3 minn, float3 maxx) 56 | { 57 | uint32_t x = prepMorton(((coord.x - minn.x) / (maxx.x - minn.x)) * ((1 << 10) - 1)); 58 | uint32_t y = prepMorton(((coord.y - minn.y) / (maxx.y - minn.y)) * ((1 << 10) - 1)); 59 | uint32_t z = prepMorton(((coord.z - minn.z) / (maxx.z - minn.z)) * ((1 << 10) - 1)); 60 | 61 | return x | (y << 1) | (z << 2); 62 | } 63 | 64 | __global__ void coord2Morton(int P, const float3* points, float3 minn, float3 maxx, uint32_t* codes) 65 | { 66 | auto idx = cg::this_grid().thread_rank(); 67 | if (idx >= P) 68 | return; 69 | 70 | codes[idx] = coord2Morton(points[idx], minn, maxx); 71 | } 72 | 73 | struct MinMax 74 | { 75 | float3 minn; 76 | float3 maxx; 77 | }; 78 | 79 | __global__ void boxMinMax(uint32_t P, float3* points, uint32_t* indices, MinMax* boxes) 80 | { 81 | auto idx = cg::this_grid().thread_rank(); 82 | 83 | MinMax me; 84 | if (idx < P) 85 | { 86 | me.minn = points[indices[idx]]; 87 | me.maxx = points[indices[idx]]; 88 | } 89 | else 90 | { 91 | me.minn = { FLT_MAX, FLT_MAX, FLT_MAX }; 92 | me.maxx = { -FLT_MAX,-FLT_MAX,-FLT_MAX }; 93 | } 94 | 95 | __shared__ MinMax redResult[BOX_SIZE]; 96 | 97 | for (int off = BOX_SIZE / 2; off >= 1; off /= 2) 98 | { 99 | if (threadIdx.x < 2 * off) 100 | redResult[threadIdx.x] = me; 101 | __syncthreads(); 102 | 103 | if (threadIdx.x < off) 104 | { 105 | MinMax other = redResult[threadIdx.x + off]; 106 | me.minn.x = min(me.minn.x, other.minn.x); 107 | me.minn.y = min(me.minn.y, other.minn.y); 108 | me.minn.z = min(me.minn.z, other.minn.z); 109 | me.maxx.x = max(me.maxx.x, other.maxx.x); 110 | me.maxx.y = max(me.maxx.y, other.maxx.y); 111 | me.maxx.z = max(me.maxx.z, other.maxx.z); 112 | } 113 | __syncthreads(); 114 | } 115 | 116 | if (threadIdx.x == 0) 117 | boxes[blockIdx.x] = me; 118 | } 119 | 120 | __device__ __host__ float distBoxPoint(const MinMax& box, const float3& p) 121 | { 122 | float3 diff = { 0, 0, 0 }; 123 | if (p.x < box.minn.x || p.x > box.maxx.x) 124 | diff.x = min(abs(p.x - box.minn.x), abs(p.x - box.maxx.x)); 125 | if (p.y < box.minn.y || p.y > box.maxx.y) 126 | diff.y = min(abs(p.y - box.minn.y), abs(p.y - box.maxx.y)); 127 | if (p.z < box.minn.z || p.z > box.maxx.z) 128 | diff.z = min(abs(p.z - box.minn.z), abs(p.z - box.maxx.z)); 129 | return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; 130 | } 131 | 132 | template 133 | __device__ void updateKBest(const float3& ref, const float3& point, float* knn) 134 | { 135 | float3 d = { point.x - ref.x, point.y - ref.y, point.z - ref.z }; 136 | float dist = d.x * d.x + d.y * d.y + d.z * d.z; 137 | for (int j = 0; j < K; j++) 138 | { 139 | if (knn[j] > dist) 140 | { 141 | float t = knn[j]; 142 | knn[j] = dist; 143 | dist = t; 144 | } 145 | } 146 | } 147 | 148 | __global__ void boxMeanDist(uint32_t P, float3* points, uint32_t* indices, MinMax* boxes, float* dists) 149 | { 150 | int idx = cg::this_grid().thread_rank(); 151 | if (idx >= P) 152 | return; 153 | 154 | float3 point = points[indices[idx]]; 155 | float best[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; 156 | 157 | for (int i = max(0, idx - 3); i <= min(P - 1, idx + 3); i++) 158 | { 159 | if (i == idx) 160 | continue; 161 | updateKBest<3>(point, points[indices[i]], best); 162 | } 163 | 164 | float reject = best[2]; 165 | best[0] = FLT_MAX; 166 | best[1] = FLT_MAX; 167 | best[2] = FLT_MAX; 168 | 169 | for (int b = 0; b < (P + BOX_SIZE - 1) / BOX_SIZE; b++) 170 | { 171 | MinMax box = boxes[b]; 172 | float dist = distBoxPoint(box, point); 173 | if (dist > reject || dist > best[2]) 174 | continue; 175 | 176 | for (int i = b * BOX_SIZE; i < min(P, (b + 1) * BOX_SIZE); i++) 177 | { 178 | if (i == idx) 179 | continue; 180 | updateKBest<3>(point, points[indices[i]], best); 181 | } 182 | } 183 | dists[indices[idx]] = (best[0] + best[1] + best[2]) / 3.0f; 184 | } 185 | 186 | void SimpleKNN::knn(int P, float3* points, float* meanDists) 187 | { 188 | float3* result; 189 | cudaMalloc(&result, sizeof(float3)); 190 | size_t temp_storage_bytes; 191 | 192 | float3 init = { 0, 0, 0 }, minn, maxx; 193 | 194 | cub::DeviceReduce::Reduce(nullptr, temp_storage_bytes, points, result, P, CustomMin(), init); 195 | thrust::device_vector temp_storage(temp_storage_bytes); 196 | 197 | cub::DeviceReduce::Reduce(temp_storage.data().get(), temp_storage_bytes, points, result, P, CustomMin(), init); 198 | cudaMemcpy(&minn, result, sizeof(float3), cudaMemcpyDeviceToHost); 199 | 200 | cub::DeviceReduce::Reduce(temp_storage.data().get(), temp_storage_bytes, points, result, P, CustomMax(), init); 201 | cudaMemcpy(&maxx, result, sizeof(float3), cudaMemcpyDeviceToHost); 202 | 203 | thrust::device_vector morton(P); 204 | thrust::device_vector morton_sorted(P); 205 | coord2Morton << <(P + 255) / 256, 256 >> > (P, points, minn, maxx, morton.data().get()); 206 | 207 | thrust::device_vector indices(P); 208 | thrust::sequence(indices.begin(), indices.end()); 209 | thrust::device_vector indices_sorted(P); 210 | 211 | cub::DeviceRadixSort::SortPairs(nullptr, temp_storage_bytes, morton.data().get(), morton_sorted.data().get(), indices.data().get(), indices_sorted.data().get(), P); 212 | temp_storage.resize(temp_storage_bytes); 213 | 214 | cub::DeviceRadixSort::SortPairs(temp_storage.data().get(), temp_storage_bytes, morton.data().get(), morton_sorted.data().get(), indices.data().get(), indices_sorted.data().get(), P); 215 | 216 | uint32_t num_boxes = (P + BOX_SIZE - 1) / BOX_SIZE; 217 | thrust::device_vector boxes(num_boxes); 218 | boxMinMax << > > (P, points, indices_sorted.data().get(), boxes.data().get()); 219 | boxMeanDist << > > (P, points, indices_sorted.data().get(), boxes.data().get(), meanDists); 220 | 221 | cudaFree(result); 222 | } -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.2 2 | Name: simple_knn 3 | Version: 0.0.0 4 | License-File: LICENSE.md 5 | -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | LICENSE.md 2 | ext.cpp 3 | setup.py 4 | simple_knn.cu 5 | spatial.cu 6 | simple_knn.egg-info/PKG-INFO 7 | simple_knn.egg-info/SOURCES.txt 8 | simple_knn.egg-info/dependency_links.txt 9 | simple_knn.egg-info/top_level.txt -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | simple_knn 2 | -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #ifndef SIMPLEKNN_H_INCLUDED 13 | #define SIMPLEKNN_H_INCLUDED 14 | 15 | class SimpleKNN 16 | { 17 | public: 18 | static void knn(int P, float3* points, float* meanDists); 19 | }; 20 | 21 | #endif -------------------------------------------------------------------------------- /submodules/simple-knn/simple_knn/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/submodules/simple-knn/simple_knn/.gitkeep -------------------------------------------------------------------------------- /submodules/simple-knn/spatial.cu: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #include "spatial.h" 13 | #include "simple_knn.h" 14 | 15 | torch::Tensor 16 | distCUDA2(const torch::Tensor& points) 17 | { 18 | const int P = points.size(0); 19 | 20 | auto float_opts = points.options().dtype(torch::kFloat32); 21 | torch::Tensor means = torch::full({P}, 0.0, float_opts); 22 | 23 | SimpleKNN::knn(P, (float3*)points.contiguous().data(), means.contiguous().data()); 24 | 25 | return means; 26 | } -------------------------------------------------------------------------------- /submodules/simple-knn/spatial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023, Inria 3 | * GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | * All rights reserved. 5 | * 6 | * This software is free for non-commercial, research and evaluation use 7 | * under the terms of the LICENSE.md file. 8 | * 9 | * For inquiries contact george.drettakis@inria.fr 10 | */ 11 | 12 | #include 13 | 14 | torch::Tensor distCUDA2(const torch::Tensor& points); -------------------------------------------------------------------------------- /technicolor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GPU_ID=$1 3 | 4 | run_pipeline() { 5 | 6 | # train 7 | CUDA_VISIBLE_DEVICES="$GPU_ID" python main.py \ 8 | --source_path data/"$data_name_path"/colmap_0 \ 9 | --model_path logs/technicolor/"$data_name" \ 10 | --deform_type node \ 11 | --node_num 1024 \ 12 | --hyper_dim 8 \ 13 | --is_blender \ 14 | --eval \ 15 | --gt_alpha_mask_as_scene_mask \ 16 | --local_frame \ 17 | --resolution 2 \ 18 | --W 800 \ 19 | --H 800 \ 20 | --config arguments/"$data_name_path".py 21 | 22 | 23 | 24 | 25 | # render 26 | CUDA_VISIBLE_DEVICES="$GPU_ID" python render.py \ 27 | --source_path data/"$data_name_path"/colmap_0 \ 28 | --model_path logs/technicolor/"$data_name" \ 29 | --deform_type node \ 30 | --node_num 1024 \ 31 | --hyper_dim 8 \ 32 | --is_blender \ 33 | --eval \ 34 | --gt_alpha_mask_as_scene_mask \ 35 | --skip_train \ 36 | --local_frame \ 37 | --resolution 2 \ 38 | --W 800 \ 39 | --H 800 \ 40 | --dataset_type $valloader 41 | } 42 | 43 | 44 | Neural3D_DA=("Birthday" "Train" "Painter" "Fatma") 45 | valloader="technicolorvalid" 46 | # run_pipeline 47 | for data_name in "${Neural3D_DA[@]}"; 48 | do 49 | echo "Dataset: Technicolor/${data_name}" 50 | data_name_path="Technicolor/${data_name}" 51 | run_pipeline "$GPU_ID" "$data_name_path" "$data_name" "$valloader" 52 | done 53 | 54 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GPU_ID=$1 3 | 4 | 5 | run_pipeline() { 6 | 7 | # train 8 | CUDA_VISIBLE_DEVICES="$GPU_ID" python main.py \ 9 | --source_path data_spacetime/Neural3D/"$data_name"/colmap_0 \ 10 | --model_path logs/N3DV/"$data_name"\ 11 | --deform_type node \ 12 | --node_num 4096 \ 13 | --hyper_dim 8 \ 14 | --is_blender \ 15 | --gt_alpha_mask_as_dynamic_mask \ 16 | --gs_with_motion_mask \ 17 | --eval \ 18 | --local_frame \ 19 | --resolution 2 \ 20 | --W 800 \ 21 | --H 800 \ 22 | --config arguments/N3DV/"$data_name".py 23 | 24 | 25 | 26 | 27 | # render 28 | CUDA_VISIBLE_DEVICES="$GPU_ID" python render.py \ 29 | --source_path data_spacetime/Neural3D/"$data_name"/colmap_0 \ 30 | --model_path output/N3DV/"$data_name" \ 31 | --deform_type node \ 32 | --node_num 4096 \ 33 | --hyper_dim 8 \ 34 | --is_blender \ 35 | --gt_alpha_mask_as_dynamic_mask \ 36 | --gs_with_motion_mask \ 37 | --eval \ 38 | --skip_train \ 39 | --local_frame \ 40 | --resolution 2 \ 41 | --W 800 \ 42 | --H 800 \ 43 | --dataset_type colmap 44 | } 45 | 46 | 47 | Neural3D_DA=("cut_roasted_beef" "sear_steak" "flame_steak" "cook_spinach" "flame_salmon_1" "coffee_martini" ) 48 | 49 | # run_pipeline 50 | for data_name in "${Neural3D_DA[@]}"; 51 | do 52 | echo "Dataset: Neural3D_DA/${data_name}" 53 | run_pipeline "$GPU_ID" "$data_name" 54 | done 55 | 56 | 57 | #run_pipeline() { 58 | # # render 59 | # CUDA_VISIBLE_DEVICES="$GPU_ID" python render.py \ 60 | # --source_path data_spacetime/"$data_name_path"/colmap_0 \ 61 | # --model_path output/render_4_b/"$data_name" \ 62 | # --deform_type node \ 63 | # --node_num 1024 \ 64 | # --hyper_dim 8 \ 65 | # --is_blender \ 66 | # --eval \ 67 | # --gt_alpha_mask_as_scene_mask \ 68 | # --skip_train \ 69 | # --local_frame \ 70 | # --resolution 2 \ 71 | # --W 800 \ 72 | # --H 800 \ 73 | # --dataset_type $valloader 74 | #} 75 | # 76 | # 77 | #Neural3D_DA=("Birthday" "Train" "Painter" "Fatma") 78 | #valloader="technicolorvalid" 79 | ## run_pipeline 80 | #for data_name in "${Neural3D_DA[@]}"; 81 | #do 82 | # echo "Dataset: Technicolor/${data_name}" 83 | # data_name_path="Technicolor/${data_name}" 84 | # run_pipeline "$GPU_ID" "$data_name_path" "$data_name" "$valloader" 85 | #done 86 | -------------------------------------------------------------------------------- /thirdparty/colmap/pre_colmap.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import sqlite3 3 | import numpy as np 4 | 5 | # source: https://github.com/colmap/colmap/blob/dev/scripts/python/database.py 6 | IS_PYTHON3 = sys.version_info[0] >= 3 7 | 8 | MAX_IMAGE_ID = 2**31 - 1 9 | 10 | CREATE_CAMERAS_TABLE = """CREATE TABLE IF NOT EXISTS cameras ( 11 | camera_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 12 | model INTEGER NOT NULL, 13 | width INTEGER NOT NULL, 14 | height INTEGER NOT NULL, 15 | params BLOB, 16 | prior_focal_length INTEGER NOT NULL)""" 17 | 18 | CREATE_DESCRIPTORS_TABLE = """CREATE TABLE IF NOT EXISTS descriptors ( 19 | image_id INTEGER PRIMARY KEY NOT NULL, 20 | rows INTEGER NOT NULL, 21 | cols INTEGER NOT NULL, 22 | data BLOB, 23 | FOREIGN KEY(image_id) REFERENCES images(image_id) ON DELETE CASCADE)""" 24 | 25 | CREATE_IMAGES_TABLE = """CREATE TABLE IF NOT EXISTS images ( 26 | image_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 27 | name TEXT NOT NULL UNIQUE, 28 | camera_id INTEGER NOT NULL, 29 | prior_qw REAL, 30 | prior_qx REAL, 31 | prior_qy REAL, 32 | prior_qz REAL, 33 | prior_tx REAL, 34 | prior_ty REAL, 35 | prior_tz REAL, 36 | CONSTRAINT image_id_check CHECK(image_id >= 0 and image_id < {}), 37 | FOREIGN KEY(camera_id) REFERENCES cameras(camera_id)) 38 | """.format(MAX_IMAGE_ID) 39 | 40 | CREATE_TWO_VIEW_GEOMETRIES_TABLE = """ 41 | CREATE TABLE IF NOT EXISTS two_view_geometries ( 42 | pair_id INTEGER PRIMARY KEY NOT NULL, 43 | rows INTEGER NOT NULL, 44 | cols INTEGER NOT NULL, 45 | data BLOB, 46 | config INTEGER NOT NULL, 47 | F BLOB, 48 | E BLOB, 49 | H BLOB, 50 | qvec BLOB, 51 | tvec BLOB) 52 | """ 53 | 54 | CREATE_KEYPOINTS_TABLE = """CREATE TABLE IF NOT EXISTS keypoints ( 55 | image_id INTEGER PRIMARY KEY NOT NULL, 56 | rows INTEGER NOT NULL, 57 | cols INTEGER NOT NULL, 58 | data BLOB, 59 | FOREIGN KEY(image_id) REFERENCES images(image_id) ON DELETE CASCADE) 60 | """ 61 | 62 | CREATE_MATCHES_TABLE = """CREATE TABLE IF NOT EXISTS matches ( 63 | pair_id INTEGER PRIMARY KEY NOT NULL, 64 | rows INTEGER NOT NULL, 65 | cols INTEGER NOT NULL, 66 | data BLOB)""" 67 | 68 | CREATE_NAME_INDEX = \ 69 | "CREATE UNIQUE INDEX IF NOT EXISTS index_name ON images(name)" 70 | 71 | CREATE_ALL = "; ".join([ 72 | CREATE_CAMERAS_TABLE, 73 | CREATE_IMAGES_TABLE, 74 | CREATE_KEYPOINTS_TABLE, 75 | CREATE_DESCRIPTORS_TABLE, 76 | CREATE_MATCHES_TABLE, 77 | CREATE_TWO_VIEW_GEOMETRIES_TABLE, 78 | CREATE_NAME_INDEX 79 | ]) 80 | 81 | 82 | def image_ids_to_pair_id(image_id1, image_id2): 83 | if image_id1 > image_id2: 84 | image_id1, image_id2 = image_id2, image_id1 85 | return image_id1 * MAX_IMAGE_ID + image_id2 86 | 87 | 88 | def pair_id_to_image_ids(pair_id): 89 | image_id2 = pair_id % MAX_IMAGE_ID 90 | image_id1 = (pair_id - image_id2) / MAX_IMAGE_ID 91 | return image_id1, image_id2 92 | 93 | 94 | def array_to_blob(array): 95 | if IS_PYTHON3: 96 | return array.tostring() 97 | else: 98 | return np.getbuffer(array) 99 | 100 | 101 | def blob_to_array(blob, dtype, shape=(-1,)): 102 | if IS_PYTHON3: 103 | return np.fromstring(blob, dtype=dtype).reshape(*shape) 104 | else: 105 | return np.frombuffer(blob, dtype=dtype).reshape(*shape) 106 | 107 | 108 | class COLMAPDatabase(sqlite3.Connection): 109 | 110 | @staticmethod 111 | def connect(database_path): 112 | return sqlite3.connect(database_path, factory=COLMAPDatabase) 113 | 114 | 115 | def __init__(self, *args, **kwargs): 116 | super(COLMAPDatabase, self).__init__(*args, **kwargs) 117 | 118 | self.create_tables = lambda: self.executescript(CREATE_ALL) 119 | self.create_cameras_table = \ 120 | lambda: self.executescript(CREATE_CAMERAS_TABLE) 121 | self.create_descriptors_table = \ 122 | lambda: self.executescript(CREATE_DESCRIPTORS_TABLE) 123 | self.create_images_table = \ 124 | lambda: self.executescript(CREATE_IMAGES_TABLE) 125 | self.create_two_view_geometries_table = \ 126 | lambda: self.executescript(CREATE_TWO_VIEW_GEOMETRIES_TABLE) 127 | self.create_keypoints_table = \ 128 | lambda: self.executescript(CREATE_KEYPOINTS_TABLE) 129 | self.create_matches_table = \ 130 | lambda: self.executescript(CREATE_MATCHES_TABLE) 131 | self.create_name_index = lambda: self.executescript(CREATE_NAME_INDEX) 132 | 133 | def add_camera(self, model, width, height, params, 134 | prior_focal_length=False, camera_id=None): 135 | params = np.asarray(params, np.float64) 136 | cursor = self.execute( 137 | "INSERT INTO cameras VALUES (?, ?, ?, ?, ?, ?)", 138 | (camera_id, model, width, height, array_to_blob(params), 139 | prior_focal_length)) 140 | return cursor.lastrowid 141 | 142 | def add_image(self, name, camera_id, 143 | prior_q=np.full(4, np.NaN), prior_t=np.full(3, np.NaN), image_id=None): 144 | cursor = self.execute( 145 | "INSERT INTO images VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 146 | (image_id, name, camera_id, prior_q[0], prior_q[1], prior_q[2], 147 | prior_q[3], prior_t[0], prior_t[1], prior_t[2])) 148 | return cursor.lastrowid 149 | 150 | def add_keypoints(self, image_id, keypoints): 151 | assert(len(keypoints.shape) == 2) 152 | assert(keypoints.shape[1] in [2, 4, 6]) 153 | 154 | keypoints = np.asarray(keypoints, np.float32) 155 | self.execute( 156 | "INSERT INTO keypoints VALUES (?, ?, ?, ?)", 157 | (image_id,) + keypoints.shape + (array_to_blob(keypoints),)) 158 | 159 | def add_descriptors(self, image_id, descriptors): 160 | descriptors = np.ascontiguousarray(descriptors, np.uint8) 161 | self.execute( 162 | "INSERT INTO descriptors VALUES (?, ?, ?, ?)", 163 | (image_id,) + descriptors.shape + (array_to_blob(descriptors),)) 164 | 165 | def add_matches(self, image_id1, image_id2, matches): 166 | assert(len(matches.shape) == 2) 167 | assert(matches.shape[1] == 2) 168 | 169 | if image_id1 > image_id2: 170 | matches = matches[:,::-1] 171 | 172 | pair_id = image_ids_to_pair_id(image_id1, image_id2) 173 | matches = np.asarray(matches, np.uint32) 174 | self.execute( 175 | "INSERT INTO matches VALUES (?, ?, ?, ?)", 176 | (pair_id,) + matches.shape + (array_to_blob(matches),)) 177 | 178 | def add_two_view_geometry(self, image_id1, image_id2, matches, 179 | F=np.eye(3), E=np.eye(3), H=np.eye(3), 180 | qvec=np.array([1.0, 0.0, 0.0, 0.0]), 181 | tvec=np.zeros(3), config=2): 182 | assert(len(matches.shape) == 2) 183 | assert(matches.shape[1] == 2) 184 | 185 | if image_id1 > image_id2: 186 | matches = matches[:,::-1] 187 | 188 | pair_id = image_ids_to_pair_id(image_id1, image_id2) 189 | matches = np.asarray(matches, np.uint32) 190 | F = np.asarray(F, dtype=np.float64) 191 | E = np.asarray(E, dtype=np.float64) 192 | H = np.asarray(H, dtype=np.float64) 193 | qvec = np.asarray(qvec, dtype=np.float64) 194 | tvec = np.asarray(tvec, dtype=np.float64) 195 | self.execute( 196 | "INSERT INTO two_view_geometries VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 197 | (pair_id,) + matches.shape + (array_to_blob(matches), config, 198 | array_to_blob(F), array_to_blob(E), array_to_blob(H), 199 | array_to_blob(qvec), array_to_blob(tvec))) 200 | 201 | 202 | -------------------------------------------------------------------------------- /thirdparty/gaussian_splatting/utils/my_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | 5 | def normalize(x): 6 | return x / np.linalg.norm(x) 7 | 8 | def viewmatrix(z, up, pos): 9 | vec2 = normalize(z) 10 | vec1_avg = up 11 | vec0 = normalize(np.cross(vec1_avg, vec2)) 12 | vec1 = normalize(np.cross(vec2, vec0)) 13 | m = np.stack([vec0, vec1, vec2, pos], 1) 14 | return m 15 | 16 | 17 | def poses_avg(poses): 18 | 19 | hwf = poses[0, :3, -1:] 20 | 21 | center = poses[:, :3, 3].mean(0) 22 | vec2 = normalize(poses[:, :3, 2].sum(0)) 23 | up = poses[:, :3, 1].sum(0) 24 | c2w = np.concatenate([viewmatrix(vec2, up, center), hwf], 1) 25 | 26 | return c2w 27 | 28 | 29 | 30 | 31 | def posetow2c_matrcs(poses): 32 | tmp = inversestep4(inversestep3(inversestep2(inversestep1(poses)))) 33 | N = tmp.shape[0] 34 | ret = [] 35 | for i in range(N): 36 | ret.append(tmp[i]) 37 | return ret 38 | 39 | 40 | def getRTfromPose(w2c_mats): 41 | for m in w2c_mats: 42 | R = m[:3, :3] 43 | t = m[:3, 3] 44 | #print(R, t) 45 | 46 | def tolist(w2c_mats): 47 | return w2c_mats.tolist() 48 | def inversestep4(c2w_mats): 49 | return np.linalg.inv(c2w_mats) 50 | def inversestep3(newposes): 51 | tmp = newposes.transpose([2, 0, 1]) # 20, 3, 4 52 | N, _, __ = tmp.shape 53 | zeros = np.zeros((N, 1, 4)) 54 | zeros[:, 0, 3] = 1 55 | c2w_mats = np.concatenate([tmp, zeros], axis=1) 56 | return c2w_mats 57 | 58 | def inversestep2(newposes): 59 | return newposes[:,0:4, :] 60 | def inversestep1(newposes): 61 | poses = np.concatenate([newposes[:, 1:2, :], newposes[:, 0:1, :], -newposes[:, 2:3, :], newposes[:, 3:4, :], newposes[:, 4:5, :]], axis=1) 62 | return poses 63 | 64 | 65 | 66 | 67 | 68 | def rotmat2qvec(R): 69 | Rxx, Ryx, Rzx, Rxy, Ryy, Rzy, Rxz, Ryz, Rzz = R.flat 70 | K = np.array([ 71 | [Rxx - Ryy - Rzz, 0, 0, 0], 72 | [Ryx + Rxy, Ryy - Rxx - Rzz, 0, 0], 73 | [Rzx + Rxz, Rzy + Ryz, Rzz - Rxx - Ryy, 0], 74 | [Ryz - Rzy, Rzx - Rxz, Rxy - Ryx, Rxx + Ryy + Rzz]]) / 3.0 75 | eigvals, eigvecs = np.linalg.eigh(K) 76 | qvec = eigvecs[[3, 0, 1, 2], np.argmax(eigvals)] 77 | if qvec[0] < 0: 78 | qvec *= -1 79 | return qvec 80 | 81 | 82 | 83 | def qvec2rotmat(qvec): 84 | return np.array([ 85 | [1 - 2 * qvec[2]**2 - 2 * qvec[3]**2, 86 | 2 * qvec[1] * qvec[2] - 2 * qvec[0] * qvec[3], 87 | 2 * qvec[3] * qvec[1] + 2 * qvec[0] * qvec[2]], 88 | [2 * qvec[1] * qvec[2] + 2 * qvec[0] * qvec[3], 89 | 1 - 2 * qvec[1]**2 - 2 * qvec[3]**2, 90 | 2 * qvec[2] * qvec[3] - 2 * qvec[0] * qvec[1]], 91 | [2 * qvec[3] * qvec[1] - 2 * qvec[0] * qvec[2], 92 | 2 * qvec[2] * qvec[3] + 2 * qvec[0] * qvec[1], 93 | 1 - 2 * qvec[1]**2 - 2 * qvec[2]**2]]) -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | import os 13 | import torch 14 | from scene import Scene 15 | import uuid 16 | from utils.image_utils import psnr #, lpips, alex_lpips 17 | 18 | from utils.image_utils import ssim as ssim_func 19 | # from piq import LPIPS # # 20 | # lpips = LPIPS() # # 21 | from argparse import Namespace 22 | from pytorch_msssim import ms_ssim 23 | from torch.utils.data import DataLoader 24 | 25 | from lpipsPyTorch import lpips # add 26 | 27 | try: 28 | from torch.utils.tensorboard import SummaryWriter 29 | 30 | TENSORBOARD_FOUND = True 31 | except ImportError: 32 | TENSORBOARD_FOUND = False 33 | 34 | 35 | def prepare_output_and_logger(args): 36 | if not args.model_path: 37 | if os.getenv('OAR_JOB_ID'): 38 | unique_str = os.getenv('OAR_JOB_ID') 39 | else: 40 | unique_str = str(uuid.uuid4()) 41 | args.model_path = os.path.join("./output/", unique_str[0:10]) 42 | 43 | # Set up output folder 44 | print("Output folder: {}".format(args.model_path)) 45 | os.makedirs(args.model_path, exist_ok=True) 46 | with open(os.path.join(args.model_path, "cfg_args"), 'w') as cfg_log_f: 47 | cfg_log_f.write(str(Namespace(**vars(args)))) 48 | 49 | # Create Tensorboard writer 50 | tb_writer = None 51 | if TENSORBOARD_FOUND: 52 | tb_writer = SummaryWriter(args.model_path) 53 | else: 54 | print("Tensorboard not available: not logging progress") 55 | return tb_writer 56 | 57 | 58 | def training_report(tb_writer, iteration, Ll1, loss, l1_loss, elapsed, testing_iterations, scene: Scene, renderFunc, renderArgs, deform, load2gpu_on_the_fly, progress_bar=None): 59 | if tb_writer: 60 | tb_writer.add_scalar('train_loss_patches/l1_loss', Ll1.item(), iteration) 61 | tb_writer.add_scalar('train_loss_patches/total_loss', loss.item(), iteration) 62 | tb_writer.add_scalar('iter_time', elapsed, iteration) 63 | 64 | test_psnr = 0.0 65 | test_ssim = 0.0 66 | test_lpips = 1e10 67 | test_ms_ssim = 0.0 68 | test_alex_lpips = 1e10 69 | # Report test and samples of training set 70 | if iteration in testing_iterations: 71 | torch.cuda.empty_cache() 72 | validation_configs = ({'name': 'test', 'cameras': scene.getTestCameras()}, 73 | {'name': 'train', 74 | 'cameras': [scene.getTrainCameras()[idx % len(scene.getTrainCameras())] for idx in range(5, 30, 5)]}) 75 | for config in validation_configs: 76 | if config['cameras'] and len(config['cameras']) > 0: 77 | # images = torch.tensor([], device="cuda") 78 | # gts = torch.tensor([], device="cuda") 79 | psnr_list, ssim_list, lpips_list, l1_list = [], [], [], [] 80 | ms_ssim_list, alex_lpips_list = [], [] 81 | for idx, viewpoint in enumerate(config['cameras']): 82 | if load2gpu_on_the_fly: 83 | viewpoint.load2device() 84 | fid = viewpoint.fid 85 | xyz = scene.gaussians.get_xyz 86 | 87 | if deform.name == 'mlp': 88 | time_input = fid.unsqueeze(0).expand(xyz.shape[0], -1) 89 | elif deform.name == 'node': 90 | time_input = deform.deform.expand_time(fid) 91 | else: 92 | time_input = 0 93 | 94 | d_values = deform.step(xyz.detach(), time_input, feature=scene.gaussians.feature, is_training=False, motion_mask=scene.gaussians.motion_mask, camera_center=viewpoint.camera_center) 95 | d_xyz, d_rotation, d_scaling, d_opacity, d_color = d_values['d_xyz'], d_values['d_rotation'], d_values['d_scaling'], d_values['d_opacity'], d_values['d_color'] 96 | 97 | image = torch.clamp(renderFunc(viewpoint, scene.gaussians, *renderArgs, d_xyz=d_xyz, d_rotation=d_rotation, d_scaling=d_scaling, d_opacity=d_opacity, d_color=d_color, d_rot_as_res=deform.d_rot_as_res)["render"], 0.0, 1.0) 98 | gt_image = torch.clamp(viewpoint.original_image.to("cuda"), 0.0, 1.0) 99 | 100 | l1_list.append(l1_loss(image[None], gt_image[None]).mean()) 101 | psnr_list.append(psnr(image[None], gt_image[None]).mean()) 102 | ssim_list.append(ssim_func(image[None], gt_image[None], data_range=1.).mean()) 103 | # lpips_list.append(lpips(image[None], gt_image[None]).mean()) # # 104 | ms_ssim_list.append(ms_ssim(image[None], gt_image[None], data_range=1.).mean()) 105 | # alex_lpips_list.append(alex_lpips(image[None], gt_image[None]).mean()) # # 106 | 107 | alex_lpips_list.append(lpips(image[None], gt_image[None], net_type='alex')) # ADD 108 | lpips_list.append(lpips(image[None], gt_image[None], net_type='vgg')) # ADD 109 | 110 | # images = torch.cat((images, image.unsqueeze(0)), dim=0) 111 | # gts = torch.cat((gts, gt_image.unsqueeze(0)), dim=0) 112 | 113 | if load2gpu_on_the_fly: 114 | viewpoint.load2device('cpu') 115 | if tb_writer and (idx < 5): 116 | tb_writer.add_images(config['name'] + "_view_{}/render".format(viewpoint.image_name), image[None], global_step=iteration) 117 | if iteration == testing_iterations[0]: 118 | tb_writer.add_images(config['name'] + "_view_{}/ground_truth".format(viewpoint.image_name), gt_image[None], global_step=iteration) 119 | 120 | l1_test = torch.stack(l1_list).mean() 121 | psnr_test = torch.stack(psnr_list).mean() 122 | ssim_test = torch.stack(ssim_list).mean() 123 | lpips_test = torch.stack(lpips_list).mean() 124 | ms_ssim_test = torch.stack(ms_ssim_list).mean() 125 | alex_lpips_test = torch.stack(alex_lpips_list).mean() 126 | if config['name'] == 'test' or len(validation_configs[0]['cameras']) == 0: 127 | test_psnr = psnr_test 128 | test_ssim = ssim_test 129 | test_lpips = lpips_test 130 | test_ms_ssim = ms_ssim_test 131 | test_alex_lpips = alex_lpips_test 132 | if tb_writer: 133 | tb_writer.add_scalar(config['name'] + '/loss_viewpoint - l1_loss', l1_test, iteration) 134 | tb_writer.add_scalar(config['name'] + '/loss_viewpoint - psnr', psnr_test, iteration) 135 | tb_writer.add_scalar(config['name'] + '/loss_viewpoint - ssim', test_ssim, iteration) 136 | tb_writer.add_scalar(config['name'] + '/loss_viewpoint - lpips', test_lpips, iteration) 137 | tb_writer.add_scalar(config['name'] + '/loss_viewpoint - ms-ssim', test_ms_ssim, iteration) 138 | tb_writer.add_scalar(config['name'] + '/loss_viewpoint - alex-lpips', test_alex_lpips, iteration) 139 | 140 | if tb_writer: 141 | tb_writer.add_histogram("scene/opacity_histogram", scene.gaussians.get_opacity, iteration) 142 | tb_writer.add_scalar('total_points', scene.gaussians.get_xyz.shape[0], iteration) 143 | torch.cuda.empty_cache() 144 | 145 | return test_psnr, test_ssim, test_lpips, test_ms_ssim, test_alex_lpips 146 | -------------------------------------------------------------------------------- /train_gui_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | 4 | 5 | class DeformKeypoints: 6 | def __init__(self) -> None: 7 | self.keypoints3d_list = [] # list of keypoints group 8 | self.keypoints_idx_list = [] # keypoints index 9 | self.keypoints3d_delta_list = [] 10 | self.selective_keypoints_idx_list = [] # keypoints index 11 | self.idx2group = {} 12 | 13 | self.selective_rotation_keypoints_idx_list = [] 14 | # self.rotation_idx2group = {} 15 | 16 | def get_kpt_idx(self,): 17 | return self.keypoints_idx_list 18 | 19 | def get_kpt(self,): 20 | return self.keypoints3d_list 21 | 22 | def get_kpt_delta(self,): 23 | return self.keypoints3d_delta_list 24 | 25 | def get_deformed_kpt_np(self, rate=1.): 26 | return np.array(self.keypoints3d_list) + np.array(self.keypoints3d_delta_list) * rate 27 | 28 | def add_kpts(self, keypoints_coord, keypoints_idx, expand=False): 29 | # keypoints3d: [N, 3], keypoints_idx: [N,], torch.tensor 30 | # self.selective_keypoints_idx_list.clear() 31 | selective_keypoints_idx_list = [] if not expand else self.selective_keypoints_idx_list 32 | for idx in range(len(keypoints_idx)): 33 | if not self.contain_kpt(keypoints_idx[idx].item()): 34 | selective_keypoints_idx_list.append(len(self.keypoints_idx_list)) 35 | self.keypoints_idx_list.append(keypoints_idx[idx].item()) 36 | self.keypoints3d_list.append(keypoints_coord[idx].cpu().numpy()) 37 | self.keypoints3d_delta_list.append(np.zeros_like(self.keypoints3d_list[-1])) 38 | 39 | for kpt_idx in keypoints_idx: 40 | self.idx2group[kpt_idx.item()] = selective_keypoints_idx_list 41 | 42 | self.selective_keypoints_idx_list = selective_keypoints_idx_list 43 | 44 | def contain_kpt(self, idx): 45 | # idx: int 46 | if idx in self.keypoints_idx_list: 47 | return True 48 | else: 49 | return False 50 | 51 | def select_kpt(self, idx): 52 | # idx: int 53 | # output: idx list of this group 54 | if idx in self.keypoints_idx_list: 55 | self.selective_keypoints_idx_list = self.idx2group[idx] 56 | 57 | def select_rotation_kpt(self, idx): 58 | if idx in self.keypoints_idx_list: 59 | self.selective_rotation_keypoints_idx_list = self.idx2group[idx] 60 | 61 | def get_rotation_center(self,): 62 | selected_rotation_points = self.get_deformed_kpt_np()[self.selective_rotation_keypoints_idx_list] 63 | return selected_rotation_points.mean(axis=0) 64 | 65 | def get_selective_center(self,): 66 | selected_points = self.get_deformed_kpt_np()[self.selective_keypoints_idx_list] 67 | return selected_points.mean(axis=0) 68 | 69 | def delete_kpt(self, idx): 70 | for kidx in self.selective_keypoints_idx_list: 71 | list_idx = self.idx2group.pop(kidx) 72 | self.keypoints3d_delta_list.pop(list_idx) 73 | self.keypoints3d_list.pop(list_idx) 74 | self.keypoints_idx_list.pop(list_idx) 75 | 76 | def delete_batch_ktps(self, batch_idx): 77 | pass 78 | 79 | def update_delta(self, delta): 80 | # delta: [3,], np.array 81 | for idx in self.selective_keypoints_idx_list: 82 | self.keypoints3d_delta_list[idx] += delta 83 | 84 | def set_delta(self, delta): 85 | # delta: [N, 3], np.array 86 | for id, idx in enumerate(self.selective_keypoints_idx_list): 87 | self.keypoints3d_delta_list[idx] = delta[id] 88 | 89 | 90 | def set_rotation_delta(self, rot_mat): 91 | kpts3d = self.get_deformed_kpt_np()[self.selective_keypoints_idx_list] 92 | kpts3d_mean = kpts3d.mean(axis=0) 93 | kpts3d = (kpts3d - kpts3d_mean) @ rot_mat.T + kpts3d_mean 94 | delta = kpts3d - np.array(self.keypoints3d_list)[self.selective_keypoints_idx_list] 95 | for id, idx in enumerate(self.selective_keypoints_idx_list): 96 | self.keypoints3d_delta_list[idx] = delta[id] 97 | -------------------------------------------------------------------------------- /utils/__pycache__/camera_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/camera_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/camera_utils_st.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/camera_utils_st.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/deform_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/deform_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/depth_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/depth_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/general_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/general_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/graphics_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/graphics_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/graphics_utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/graphics_utils.cpython-37.pyc -------------------------------------------------------------------------------- /utils/__pycache__/image_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/image_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/logger_hook.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/logger_hook.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/loss_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/loss_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/loss_utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/loss_utils.cpython-37.pyc -------------------------------------------------------------------------------- /utils/__pycache__/pose_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/pose_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/rigid_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/rigid_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/sh_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/sh_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/system_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/system_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/system_utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/system_utils.cpython-37.pyc -------------------------------------------------------------------------------- /utils/__pycache__/time_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EllisonKing/taylor_gaussian/f95da60669041de65bfec4b91086cb7ceb18ada6/utils/__pycache__/time_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/bezier.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class BezierCurve: 5 | def __init__(self, points: np.ndarray) -> None: 6 | if points.ndim == 2: 7 | points = points[None] 8 | self.points = points # N, T, D 9 | self.T = points.shape[1] 10 | 11 | def __call__(self, t: float): 12 | assert 0 <= t <= 1, f't: {t} out of range [0, 1]!' 13 | return self.interpolate(t, self.points) 14 | 15 | def interpolate(self, t, points): 16 | if points.shape[1] < 2: 17 | raise ValueError(f"points shape error: {points.shape}") 18 | elif points.shape[1] == 2: 19 | point0, point1 = points[:, 0], points[:, 1] 20 | else: 21 | point0 = self.interpolate(t, points[:, :-1]) 22 | point1 = self.interpolate(t, points[:, 1:]) 23 | return (1 - t) * point0 + t * point1 24 | 25 | 26 | class PieceWiseLinear: 27 | def __init__(self, points: np.ndarray) -> None: 28 | if points.ndim == 2: 29 | points = points[None] 30 | self.points = points # N, T, D 31 | self.T = points.shape[1] 32 | 33 | def __call__(self, t: float): 34 | assert 0 <= t <= 1, f't: {t} out of range [0, 1]!' 35 | return self.interpolate(t, self.points) 36 | 37 | def interpolate(self, t, points): 38 | if points.shape[1] < 2: 39 | raise ValueError(f"points shape error: {points.shape}") 40 | else: 41 | t_scaled = t * (self.T - 1) 42 | t_floor = min(self.T - 2, max(0, int(np.floor(t_scaled)))) 43 | t_ceil = t_floor + 1 44 | point0, point1 = points[:, t_floor], points[:, t_ceil] 45 | return (t_ceil - t_scaled) * point0 + (t_scaled - t_floor) * point1 46 | -------------------------------------------------------------------------------- /utils/camera_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | from scene.cameras import Camera 13 | import numpy as np 14 | from utils.general_utils import PILtoTorch, ArrayToTorch 15 | from utils.graphics_utils import fov2focal 16 | import json 17 | 18 | WARNED = False 19 | 20 | 21 | def loadCam(args, id, cam_info, resolution_scale, flow_dirs): 22 | orig_w, orig_h = cam_info.image.size 23 | 24 | if args.resolution in [1, 2, 4, 8]: 25 | resolution = round(orig_w / (resolution_scale * args.resolution)), round( 26 | orig_h / (resolution_scale * args.resolution)) 27 | else: # should be a type that converts to float 28 | if args.resolution == -1: 29 | if orig_w > 1600: 30 | global WARNED 31 | if not WARNED: 32 | print("[ INFO ] Encountered quite large input images (>1.6K pixels width), rescaling to 1.6K.\n " 33 | "If this is not desired, please explicitly specify '--resolution/-r' as 1") 34 | WARNED = True 35 | global_down = orig_w / 1600 36 | else: 37 | global_down = 1 38 | else: 39 | global_down = orig_w / args.resolution 40 | 41 | scale = float(global_down) * float(resolution_scale) 42 | resolution = (int(orig_w / scale), int(orig_h / scale)) 43 | 44 | resized_image_rgb = PILtoTorch(cam_info.image, resolution) 45 | 46 | gt_image = resized_image_rgb[:3, ...] 47 | loaded_mask = None 48 | 49 | if resized_image_rgb.shape[0] == 4: 50 | loaded_mask = resized_image_rgb[3:4, ...] 51 | 52 | return Camera(colmap_id=cam_info.uid, R=cam_info.R, T=cam_info.T, 53 | FoVx=cam_info.FovX, FoVy=cam_info.FovY, 54 | image=gt_image, gt_alpha_mask=loaded_mask, 55 | image_name=cam_info.image_name, uid=id, 56 | data_device=args.data_device if not args.load2gpu_on_the_fly else 'cpu', fid=cam_info.fid, 57 | depth=cam_info.depth, flow_dirs=flow_dirs) 58 | 59 | 60 | def cameraList_from_camInfos(cam_infos, resolution_scale, args, flow_dirs_list=None): 61 | camera_list = [] 62 | 63 | for id, c in enumerate(cam_infos): 64 | camera_list.append(loadCam(args, id, c, resolution_scale, [] if flow_dirs_list is None else flow_dirs_list[id])) 65 | 66 | return camera_list 67 | 68 | 69 | def camera_to_JSON(id, camera: Camera): 70 | Rt = np.zeros((4, 4)) 71 | Rt[:3, :3] = camera.R.transpose() 72 | Rt[:3, 3] = camera.T 73 | Rt[3, 3] = 1.0 74 | 75 | W2C = np.linalg.inv(Rt) 76 | pos = W2C[:3, 3] 77 | rot = W2C[:3, :3] 78 | serializable_array_2d = [x.tolist() for x in rot] 79 | camera_entry = { 80 | 'id': id, 81 | 'img_name': camera.image_name, 82 | 'width': camera.width, 83 | 'height': camera.height, 84 | 'position': pos.tolist(), 85 | 'rotation': serializable_array_2d, 86 | 'fy': fov2focal(camera.FovY, camera.height), 87 | 'fx': fov2focal(camera.FovX, camera.width) 88 | } 89 | return camera_entry 90 | 91 | 92 | def camera_nerfies_from_JSON(path, scale): 93 | """Loads a JSON camera into memory.""" 94 | with open(path, 'r') as fp: 95 | camera_json = json.load(fp) 96 | 97 | # Fix old camera JSON. 98 | if 'tangential' in camera_json: 99 | camera_json['tangential_distortion'] = camera_json['tangential'] 100 | 101 | return dict( 102 | orientation=np.array(camera_json['orientation']), 103 | position=np.array(camera_json['position']), 104 | focal_length=camera_json['focal_length'] * scale, 105 | principal_point=np.array(camera_json['principal_point']) * scale, 106 | skew=camera_json['skew'], 107 | pixel_aspect_ratio=camera_json['pixel_aspect_ratio'], 108 | radial_distortion=np.array(camera_json['radial_distortion']), 109 | tangential_distortion=np.array(camera_json['tangential_distortion']), 110 | image_size=np.array((int(round(camera_json['image_size'][0] * scale)), 111 | int(round(camera_json['image_size'][1] * scale)))), 112 | ) 113 | -------------------------------------------------------------------------------- /utils/dual_quaternion.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | 4 | def _sqrt_positive_part(x: torch.Tensor) -> torch.Tensor: 5 | """ 6 | Returns torch.sqrt(torch.max(0, x)) 7 | but with a zero subgradient where x is 0. 8 | """ 9 | ret = torch.zeros_like(x) 10 | positive_mask = x > 0 11 | ret[positive_mask] = torch.sqrt(x[positive_mask]) 12 | return ret 13 | 14 | 15 | def matrix_to_quaternion(matrix: torch.Tensor) -> torch.Tensor: 16 | """ 17 | Convert rotations given as rotation matrices to quaternions. 18 | 19 | Args: 20 | matrix: Rotation matrices as tensor of shape (..., 3, 3). 21 | 22 | Returns: 23 | quaternions with real part first, as tensor of shape (..., 4). 24 | """ 25 | if matrix.size(-1) != 3 or matrix.size(-2) != 3: 26 | raise ValueError(f"Invalid rotation matrix shape {matrix.shape}.") 27 | 28 | batch_dim = matrix.shape[:-2] 29 | m00, m01, m02, m10, m11, m12, m20, m21, m22 = torch.unbind( 30 | matrix.reshape(batch_dim + (9,)), dim=-1 31 | ) 32 | 33 | q_abs = _sqrt_positive_part( 34 | torch.stack( 35 | [ 36 | 1.0 + m00 + m11 + m22, 37 | 1.0 + m00 - m11 - m22, 38 | 1.0 - m00 + m11 - m22, 39 | 1.0 - m00 - m11 + m22, 40 | ], 41 | dim=-1, 42 | ) 43 | ) 44 | 45 | # we produce the desired quaternion multiplied by each of r, i, j, k 46 | quat_by_rijk = torch.stack( 47 | [ 48 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 49 | # `int`. 50 | torch.stack([q_abs[..., 0] ** 2, m21 - m12, m02 - m20, m10 - m01], dim=-1), 51 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 52 | # `int`. 53 | torch.stack([m21 - m12, q_abs[..., 1] ** 2, m10 + m01, m02 + m20], dim=-1), 54 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 55 | # `int`. 56 | torch.stack([m02 - m20, m10 + m01, q_abs[..., 2] ** 2, m12 + m21], dim=-1), 57 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 58 | # `int`. 59 | torch.stack([m10 - m01, m20 + m02, m21 + m12, q_abs[..., 3] ** 2], dim=-1), 60 | ], 61 | dim=-2, 62 | ) 63 | 64 | # We floor here at 0.1 but the exact level is not important; if q_abs is small, 65 | # the candidate won't be picked. 66 | flr = torch.tensor(0.1).to(dtype=q_abs.dtype, device=q_abs.device) 67 | quat_candidates = quat_by_rijk / (2.0 * q_abs[..., None].max(flr)) 68 | 69 | # if not for numerical problems, quat_candidates[i] should be same (up to a sign), 70 | # forall i; we pick the best-conditioned one (with the largest denominator) 71 | 72 | return quat_candidates[ 73 | torch.nn.functional.one_hot(q_abs.argmax(dim=-1), num_classes=4) > 0.5, : 74 | ].reshape(batch_dim + (4,)) 75 | 76 | 77 | def quaternion_to_matrix(quaternions: torch.Tensor) -> torch.Tensor: 78 | r, i, j, k = torch.unbind(quaternions, -1) 79 | two_s = 2.0 / (quaternions * quaternions).sum(-1) 80 | o = torch.stack( 81 | ( 82 | 1 - two_s * (j * j + k * k), 83 | two_s * (i * j - k * r), 84 | two_s * (i * k + j * r), 85 | two_s * (i * j + k * r), 86 | 1 - two_s * (i * i + k * k), 87 | two_s * (j * k - i * r), 88 | two_s * (i * k - j * r), 89 | two_s * (j * k + i * r), 90 | 1 - two_s * (i * i + j * j), 91 | ), 92 | -1, 93 | ) 94 | return o.reshape(quaternions.shape[:-1] + (3, 3)) 95 | 96 | 97 | def standardize_quaternion(quaternions: torch.Tensor) -> torch.Tensor: 98 | return torch.where(quaternions[..., 0:1] < 0, -quaternions, quaternions) 99 | 100 | 101 | def quaternion_raw_multiply(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor: 102 | aw, ax, ay, az = torch.unbind(a, -1) 103 | bw, bx, by, bz = torch.unbind(b, -1) 104 | ow = aw * bw - ax * bx - ay * by - az * bz 105 | ox = aw * bx + ax * bw + ay * bz - az * by 106 | oy = aw * by - ax * bz + ay * bw + az * bx 107 | oz = aw * bz + ax * by - ay * bx + az * bw 108 | return torch.stack((ow, ox, oy, oz), -1) 109 | 110 | 111 | def quaternion_multiply(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor: 112 | ab = quaternion_raw_multiply(a, b) 113 | return standardize_quaternion(ab) 114 | 115 | 116 | def dualquaternion_multiply(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor: 117 | a_real, b_real = a[..., :4], b[..., :4] 118 | a_imag, b_imag = a[..., 4:], b[..., 4:] 119 | o_real = quaternion_multiply(a_real, b_real) 120 | o_imag = quaternion_multiply(a_imag, b_real) + quaternion_multiply(a_real, b_imag) 121 | o = torch.cat([o_real, o_imag], dim=-1) 122 | return o 123 | 124 | 125 | def conjugation(q): 126 | if q.shape[-1] == 4: 127 | q = torch.cat([q[..., :1], -q[..., 1:]], dim=-1) 128 | elif q.shape[-1] == 8: 129 | q = torch.cat([q[..., :1], -q[..., 1:4], q[..., 4:5], -q[..., 5:]], dim=-1) 130 | else: 131 | raise TypeError(f'q should be of [..., 4] or [..., 8] but got {q.shape}!') 132 | return q 133 | 134 | 135 | def QT2DQ(q, t, rot_as_q=True): 136 | if not rot_as_q: 137 | q = matrix_to_quaternion(q) 138 | q = torch.nn.functional.normalize(q) 139 | real = q 140 | t = torch.cat([torch.zeros_like(t[..., :1]), t], dim=-1) 141 | image = quaternion_multiply(t, q) / 2 142 | dq = torch.cat([real, image], dim=-1) 143 | return dq 144 | 145 | 146 | def DQ2QT(dq, rot_as_q=False): 147 | real = dq[..., :4] 148 | imag = dq[..., 4:] 149 | real_norm = real.norm(dim=-1, keepdim=True).clamp(min=1e-8) 150 | real, imag = real / real_norm, imag / real_norm 151 | 152 | w0, x0, y0, z0 = torch.unbind(real, -1) 153 | w1, x1, y1, z1 = torch.unbind(imag, -1) 154 | 155 | t = 2* torch.stack([- w1*x0 + x1*w0 - y1*z0 + z1*y0, 156 | - w1*y0 + x1*z0 + y1*w0 - z1*x0, 157 | - w1*z0 - x1*y0 + y1*x0 + z1*w0], dim=-1) 158 | R = torch.stack([1-2*y0**2-2*z0**2, 2*x0*y0-2*w0*z0, 2*x0*z0+2*w0*y0, 159 | 2*x0*y0+2*w0*z0, 1-2*x0**2-2*z0**2, 2*y0*z0-2*w0*x0, 160 | 2*x0*z0-2*w0*y0, 2*y0*z0+2*w0*x0, 1-2*x0**2-2*y0**2], dim=-1).reshape([*w0.shape, 3, 3]) 161 | if rot_as_q: 162 | q = matrix_to_quaternion(R) 163 | return q, t 164 | else: 165 | return R, t 166 | 167 | 168 | def DQBlending(q, t, weights, rot_as_q=True): 169 | ''' 170 | Input: 171 | q: [..., k, 4]; t: [..., k, 3]; weights: [..., k] 172 | Output: 173 | q_: [..., 4]; t_: [..., 3] 174 | ''' 175 | dq = QT2DQ(q=q, t=t) 176 | dq_avg = (dq * weights[..., None]).sum(dim=-2) 177 | q_, t_ = DQ2QT(dq_avg, rot_as_q=rot_as_q) 178 | return q_, t_ 179 | 180 | 181 | def interpolate(q0, t0, q1, t1, weight, rot_as_q=True): 182 | dq0 = QT2DQ(q=q0, t=t0) 183 | dq1 = QT2DQ(q=q1, t=t1) 184 | dq_avg = dq0 * weight + dq1 * (1 - weight) 185 | q, t = DQ2QT(dq=dq_avg, rot_as_q=rot_as_q) 186 | return q, t 187 | 188 | 189 | def transformation_blending(transformations, weights): 190 | Rs, Ts = transformations[:, :3, :3], transformations[:, :3, 3] 191 | qs = matrix_to_quaternion(Rs) 192 | q, T = DQBlending(qs[None], Ts[None], weights) 193 | R = quaternion_to_matrix(q) 194 | transformation = torch.eye(4).to(transformations.device)[None].expand(weights.shape[0], 4, 4).clone() 195 | transformation[:, :3, :3] = R 196 | transformation[:, :3, 3] = T 197 | return transformation 198 | -------------------------------------------------------------------------------- /utils/graphics_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | import torch 13 | import math 14 | import numpy as np 15 | from typing import NamedTuple 16 | 17 | 18 | class BasicPointCloud(NamedTuple): 19 | points: np.array 20 | colors: np.array 21 | normals: np.array 22 | 23 | 24 | class BasicPointCloud_ST(NamedTuple): 25 | points: np.array 26 | colors: np.array 27 | normals: np.array 28 | times: np.array # 29 | 30 | def geom_transform_points(points, transf_matrix): 31 | P, _ = points.shape 32 | ones = torch.ones(P, 1, dtype=points.dtype, device=points.device) 33 | points_hom = torch.cat([points, ones], dim=1) 34 | points_out = torch.matmul(points_hom, transf_matrix.unsqueeze(0)) 35 | 36 | denom = points_out[..., 3:] + 0.0000001 37 | return (points_out[..., :3] / denom).squeeze(dim=0) 38 | 39 | 40 | def getWorld2View(R, t): 41 | Rt = np.zeros((4, 4)) 42 | Rt[:3, :3] = R.transpose() 43 | Rt[:3, 3] = t 44 | Rt[3, 3] = 1.0 45 | return np.float32(Rt) 46 | 47 | 48 | def getWorld2View2(R, t, translate=np.array([.0, .0, .0]), scale=1.0): 49 | Rt = np.zeros((4, 4)) 50 | Rt[:3, :3] = R.transpose() 51 | Rt[:3, 3] = t 52 | Rt[3, 3] = 1.0 53 | 54 | C2W = np.linalg.inv(Rt) 55 | cam_center = C2W[:3, 3] 56 | cam_center = (cam_center + translate) * scale 57 | C2W[:3, 3] = cam_center 58 | Rt = np.linalg.inv(C2W) 59 | return np.float32(Rt) 60 | 61 | 62 | def getProjectionMatrix(znear, zfar, fovX, fovY): 63 | tanHalfFovY = math.tan((fovY / 2)) 64 | tanHalfFovX = math.tan((fovX / 2)) 65 | 66 | top = tanHalfFovY * znear 67 | bottom = -top 68 | right = tanHalfFovX * znear 69 | left = -right 70 | 71 | P = torch.zeros(4, 4) 72 | 73 | z_sign = 1.0 74 | 75 | P[0, 0] = 2.0 * znear / (right - left) 76 | P[1, 1] = 2.0 * znear / (top - bottom) 77 | P[0, 2] = (right + left) / (right - left) 78 | P[1, 2] = (top + bottom) / (top - bottom) 79 | P[3, 2] = z_sign 80 | P[2, 2] = z_sign * zfar / (zfar - znear) 81 | P[2, 3] = -(zfar * znear) / (zfar - znear) 82 | return P 83 | 84 | 85 | def getProjectionMatrixCV(znear, zfar, fovX, fovY, cx=0.0, cy=0.0): 86 | ''' 87 | cx and cy range is -0.5 to 0.5 88 | we use cx cy range -0.5 * 0.5 89 | 90 | ''' 91 | tanHalfFovY = math.tan(fovY / 2) 92 | tanHalfFovX = math.tan(fovX / 2) 93 | 94 | top = tanHalfFovY * znear 95 | bottom = -top 96 | right = tanHalfFovX * znear 97 | left = -right 98 | 99 | # Adjust for off-center projection 100 | deltax = (2 * tanHalfFovX * znear) * cx # 101 | deltay = (2 * tanHalfFovY * znear) * cy # 102 | 103 | left += deltax 104 | right += deltax 105 | top += deltay 106 | bottom += deltay 107 | 108 | # left -= deltax 109 | # right -= deltax 110 | # top -= deltay 111 | # bottom -= deltay 112 | 113 | # left = left(1 +cx)* znear 114 | # right += cx * znear 115 | # top += cy * znear 116 | # bottom += cy * znear 117 | 118 | P = torch.zeros(4, 4) 119 | 120 | z_sign = 1.0 121 | 122 | P[0, 0] = 2.0 * znear / (right - left) 123 | P[1, 1] = 2.0 * znear / (top - bottom) 124 | P[0, 2] = (right + left) / (right - left) 125 | P[1, 2] = (top + bottom) / (top - bottom) 126 | P[3, 2] = z_sign 127 | # P[2, 2] = z_sign * zfar / (zfar - znear) 128 | P[2, 2] = z_sign * (zfar + znear) / (zfar - znear) 129 | 130 | P[2, 3] = -(zfar * znear) / (zfar - znear) 131 | return P 132 | 133 | 134 | def fov2focal(fov, pixels): 135 | return pixels / (2 * math.tan(fov / 2)) 136 | 137 | 138 | def focal2fov(focal, pixels): 139 | return 2 * math.atan(pixels / (2 * focal)) 140 | -------------------------------------------------------------------------------- /utils/image_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | import torch 13 | 14 | # NeRF-DS Alex LPIPS 15 | import lpips as lpips_lib 16 | loss_fn_alex = lpips_lib.LPIPS(net='alex') 17 | loss_fn_alex.net.cuda() 18 | loss_fn_alex.scaling_layer.cuda() 19 | loss_fn_alex.lins.cuda() 20 | def alex_lpips(image1, image2): 21 | image1 = image1 * 2 - 1 22 | image2 = image2 * 2 - 1 23 | lpips = loss_fn_alex(image1, image2) 24 | return lpips 25 | 26 | 27 | def mse(img1, img2): 28 | return (((img1 - img2)) ** 2).view(img1.shape[0], -1).mean(1, keepdim=True) 29 | 30 | 31 | def psnr(img1, img2): 32 | mse = (((img1 - img2)) ** 2).view(img1.shape[0], -1).mean(1, keepdim=True) 33 | return 20 * torch.log10(1.0 / torch.sqrt(mse)) 34 | 35 | 36 | from piq import ssim, LPIPS 37 | lpips = LPIPS() 38 | -------------------------------------------------------------------------------- /utils/interactive_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | 4 | 5 | class DeformKeypoints: 6 | def __init__(self) -> None: 7 | self.keypoints3d_list = [] # list of keypoints group 8 | self.keypoints_idx_list = [] # keypoints index 9 | self.keypoints3d_delta_list = [] 10 | self.selective_keypoints_idx_list = [] # keypoints index 11 | self.idx2group = {} 12 | 13 | self.selective_rotation_keypoints_idx_list = [] 14 | # self.rotation_idx2group = {} 15 | 16 | def get_kpt_idx(self,): 17 | return self.keypoints_idx_list 18 | 19 | def get_kpt(self,): 20 | return self.keypoints3d_list 21 | 22 | def get_kpt_delta(self,): 23 | return self.keypoints3d_delta_list 24 | 25 | def get_deformed_kpt_np(self, rate=1.): 26 | return np.array(self.keypoints3d_list) + np.array(self.keypoints3d_delta_list) * rate 27 | 28 | def add_kpts(self, keypoints_coord, keypoints_idx, expand=False): 29 | # keypoints3d: [N, 3], keypoints_idx: [N,], torch.tensor 30 | # self.selective_keypoints_idx_list.clear() 31 | selective_keypoints_idx_list = [] if not expand else self.selective_keypoints_idx_list 32 | for idx in range(len(keypoints_idx)): 33 | if not self.contain_kpt(keypoints_idx[idx].item()): 34 | selective_keypoints_idx_list.append(len(self.keypoints_idx_list)) 35 | self.keypoints_idx_list.append(keypoints_idx[idx].item()) 36 | self.keypoints3d_list.append(keypoints_coord[idx].cpu().numpy()) 37 | self.keypoints3d_delta_list.append(np.zeros_like(self.keypoints3d_list[-1])) 38 | 39 | for kpt_idx in keypoints_idx: 40 | self.idx2group[kpt_idx.item()] = selective_keypoints_idx_list 41 | 42 | self.selective_keypoints_idx_list = selective_keypoints_idx_list 43 | 44 | def contain_kpt(self, idx): 45 | # idx: int 46 | if idx in self.keypoints_idx_list: 47 | return True 48 | else: 49 | return False 50 | 51 | def select_kpt(self, idx): 52 | # idx: int 53 | # output: idx list of this group 54 | if idx in self.keypoints_idx_list: 55 | self.selective_keypoints_idx_list = self.idx2group[idx] 56 | 57 | def select_rotation_kpt(self, idx): 58 | if idx in self.keypoints_idx_list: 59 | self.selective_rotation_keypoints_idx_list = self.idx2group[idx] 60 | 61 | def get_rotation_center(self,): 62 | selected_rotation_points = self.get_deformed_kpt_np()[self.selective_rotation_keypoints_idx_list] 63 | return selected_rotation_points.mean(axis=0) 64 | 65 | def get_selective_center(self,): 66 | selected_points = self.get_deformed_kpt_np()[self.selective_keypoints_idx_list] 67 | return selected_points.mean(axis=0) 68 | 69 | def delete_kpt(self, idx): 70 | pass 71 | 72 | def delete_batch_ktps(self, batch_idx): 73 | pass 74 | 75 | def update_delta(self, delta): 76 | # delta: [3,], np.array 77 | for idx in self.selective_keypoints_idx_list: 78 | self.keypoints3d_delta_list[idx] += delta 79 | 80 | def set_delta(self, delta): 81 | # delta: [N, 3], np.array 82 | for id, idx in enumerate(self.selective_keypoints_idx_list): 83 | self.keypoints3d_delta_list[idx] = delta[id] 84 | 85 | 86 | def set_rotation_delta(self, rot_mat): 87 | kpts3d = self.get_deformed_kpt_np()[self.selective_keypoints_idx_list] 88 | kpts3d_mean = self.get_rotation_center() 89 | kpts3d = (kpts3d - kpts3d_mean) @ rot_mat.T + kpts3d_mean 90 | delta = kpts3d - np.array(self.keypoints3d_list)[self.selective_keypoints_idx_list] 91 | for id, idx in enumerate(self.selective_keypoints_idx_list): 92 | self.keypoints3d_delta_list[idx] = delta[id] 93 | -------------------------------------------------------------------------------- /utils/logger_hook.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from mmengine.hooks import LoggerHook 3 | 4 | 5 | def setup_logger(log_file='default.log'): 6 | logger = logging.getLogger('mmdet_logger') 7 | logger.setLevel(logging.INFO) 8 | 9 | # Create file handler 10 | fh = logging.FileHandler(log_file) 11 | fh.setLevel(logging.INFO) 12 | 13 | # Create formatter and add it to the handlers 14 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 15 | fh.setFormatter(formatter) 16 | 17 | # Add the handlers to the logger 18 | logger.addHandler(fh) 19 | 20 | # Initialize LoggerHook 21 | logger_hook = LoggerHook(out_dir=log_file) 22 | return logger_hook 23 | 24 | 25 | # 使用示例 26 | # logger_hook = setup_logger('my_log.log') 27 | # logger_hook.log('This is a log message.') 28 | -------------------------------------------------------------------------------- /utils/other_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | 4 | 5 | def _sqrt_positive_part(x: torch.Tensor) -> torch.Tensor: 6 | """ 7 | Returns torch.sqrt(torch.max(0, x)) 8 | but with a zero subgradient where x is 0. 9 | """ 10 | ret = torch.zeros_like(x) 11 | positive_mask = x > 0 12 | ret[positive_mask] = torch.sqrt(x[positive_mask]) 13 | return ret 14 | 15 | 16 | def matrix_to_quaternion(matrix: torch.Tensor) -> torch.Tensor: 17 | """ 18 | Convert rotations given as rotation matrices to quaternions. 19 | 20 | Args: 21 | matrix: Rotation matrices as tensor of shape (..., 3, 3). 22 | 23 | Returns: 24 | quaternions with real part first, as tensor of shape (..., 4). 25 | """ 26 | if matrix.size(-1) != 3 or matrix.size(-2) != 3: 27 | raise ValueError(f"Invalid rotation matrix shape {matrix.shape}.") 28 | 29 | batch_dim = matrix.shape[:-2] 30 | m00, m01, m02, m10, m11, m12, m20, m21, m22 = torch.unbind( 31 | matrix.reshape(batch_dim + (9,)), dim=-1 32 | ) 33 | 34 | q_abs = _sqrt_positive_part( 35 | torch.stack( 36 | [ 37 | 1.0 + m00 + m11 + m22, 38 | 1.0 + m00 - m11 - m22, 39 | 1.0 - m00 + m11 - m22, 40 | 1.0 - m00 - m11 + m22, 41 | ], 42 | dim=-1, 43 | ) 44 | ) 45 | 46 | # we produce the desired quaternion multiplied by each of r, i, j, k 47 | quat_by_rijk = torch.stack( 48 | [ 49 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 50 | # `int`. 51 | torch.stack([q_abs[..., 0] ** 2, m21 - m12, m02 - m20, m10 - m01], dim=-1), 52 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 53 | # `int`. 54 | torch.stack([m21 - m12, q_abs[..., 1] ** 2, m10 + m01, m02 + m20], dim=-1), 55 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 56 | # `int`. 57 | torch.stack([m02 - m20, m10 + m01, q_abs[..., 2] ** 2, m12 + m21], dim=-1), 58 | # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and 59 | # `int`. 60 | torch.stack([m10 - m01, m20 + m02, m21 + m12, q_abs[..., 3] ** 2], dim=-1), 61 | ], 62 | dim=-2, 63 | ) 64 | 65 | # We floor here at 0.1 but the exact level is not important; if q_abs is small, 66 | # the candidate won't be picked. 67 | flr = torch.tensor(0.1).to(dtype=q_abs.dtype, device=q_abs.device) 68 | quat_candidates = quat_by_rijk / (2.0 * q_abs[..., None].max(flr)) 69 | 70 | # if not for numerical problems, quat_candidates[i] should be same (up to a sign), 71 | # forall i; we pick the best-conditioned one (with the largest denominator) 72 | 73 | return quat_candidates[ 74 | torch.nn.functional.one_hot(q_abs.argmax(dim=-1), num_classes=4) > 0.5, : 75 | ].reshape(batch_dim + (4,)) 76 | 77 | 78 | def depth2normal(depth:torch.Tensor, focal:float=None): 79 | if depth.dim() == 2: 80 | depth = depth[None, None] 81 | elif depth.dim() == 3: 82 | depth = depth.squeeze()[None, None] 83 | if focal is None: 84 | focal = depth.shape[-1] / 2 / np.tan(torch.pi/6) 85 | depth = torch.cat([depth[:, :, :1], depth, depth[:, :, -1:]], dim=2) 86 | depth = torch.cat([depth[..., :1], depth, depth[..., -1:]], dim=3) 87 | kernel = torch.tensor([[[ 0, 0, 0], 88 | [-.5, 0, .5], 89 | [ 0, 0, 0]], 90 | [[ 0, -.5, 0], 91 | [ 0, 0, 0], 92 | [ 0, .5, 0]]], device=depth.device, dtype=depth.dtype)[:, None] 93 | normal = torch.nn.functional.conv2d(depth, kernel, padding='valid')[0].permute(1, 2, 0) 94 | normal = normal / (depth[0, 0, 1:-1, 1:-1, None] + 1e-10) * focal 95 | normal = torch.cat([normal, torch.ones_like(normal[..., :1])], dim=-1) 96 | normal = normal / normal.norm(dim=-1, keepdim=True) 97 | return normal.permute(2, 0, 1) 98 | -------------------------------------------------------------------------------- /utils/pickle_utils.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | 4 | def save_obj(path, obj): 5 | file = open(path, 'wb') 6 | obj_str = pickle.dumps(obj) 7 | file.write(obj_str) 8 | file.close() 9 | 10 | 11 | def load_obj(path): 12 | file = open(path, 'rb') 13 | obj = pickle.loads(file.read()) 14 | file.close() 15 | return obj 16 | -------------------------------------------------------------------------------- /utils/pose_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | from utils.graphics_utils import fov2focal 4 | 5 | trans_t = lambda t: torch.Tensor([ 6 | [1, 0, 0, 0], 7 | [0, 1, 0, 0], 8 | [0, 0, 1, t], 9 | [0, 0, 0, 1]]).float() 10 | 11 | rot_phi = lambda phi: torch.Tensor([ 12 | [1, 0, 0, 0], 13 | [0, np.cos(phi), -np.sin(phi), 0], 14 | [0, np.sin(phi), np.cos(phi), 0], 15 | [0, 0, 0, 1]]).float() 16 | 17 | rot_theta = lambda th: torch.Tensor([ 18 | [np.cos(th), 0, -np.sin(th), 0], 19 | [0, 1, 0, 0], 20 | [np.sin(th), 0, np.cos(th), 0], 21 | [0, 0, 0, 1]]).float() 22 | 23 | 24 | def rodrigues_mat_to_rot(R): 25 | eps = 1e-16 26 | trc = np.trace(R) 27 | trc2 = (trc - 1.) / 2. 28 | # sinacostrc2 = np.sqrt(1 - trc2 * trc2) 29 | s = np.array([R[2, 1] - R[1, 2], R[0, 2] - R[2, 0], R[1, 0] - R[0, 1]]) 30 | if (1 - trc2 * trc2) >= eps: 31 | tHeta = np.arccos(trc2) 32 | tHetaf = tHeta / (2 * (np.sin(tHeta))) 33 | else: 34 | tHeta = np.real(np.arccos(trc2)) 35 | tHetaf = 0.5 / (1 - tHeta / 6) 36 | omega = tHetaf * s 37 | return omega 38 | 39 | 40 | def rodrigues_rot_to_mat(r): 41 | wx, wy, wz = r 42 | theta = np.sqrt(wx * wx + wy * wy + wz * wz) 43 | a = np.cos(theta) 44 | b = (1 - np.cos(theta)) / (theta * theta) 45 | c = np.sin(theta) / theta 46 | R = np.zeros([3, 3]) 47 | R[0, 0] = a + b * (wx * wx) 48 | R[0, 1] = b * wx * wy - c * wz 49 | R[0, 2] = b * wx * wz + c * wy 50 | R[1, 0] = b * wx * wy + c * wz 51 | R[1, 1] = a + b * (wy * wy) 52 | R[1, 2] = b * wy * wz - c * wx 53 | R[2, 0] = b * wx * wz - c * wy 54 | R[2, 1] = b * wz * wy + c * wx 55 | R[2, 2] = a + b * (wz * wz) 56 | return R 57 | 58 | 59 | def normalize(x): 60 | return x / np.linalg.norm(x) 61 | 62 | 63 | def pose_spherical(theta, phi, radius): 64 | c2w = trans_t(radius) 65 | c2w = rot_phi(phi / 180. * np.pi) @ c2w 66 | c2w = rot_theta(theta / 180. * np.pi) @ c2w 67 | c2w = torch.Tensor(np.array([[-1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])) @ c2w 68 | return c2w 69 | 70 | def viewmatrix(z, up, pos): 71 | vec2 = normalize(z) 72 | vec1_avg = up 73 | vec0 = normalize(np.cross(vec1_avg, vec2)) 74 | vec1 = normalize(np.cross(vec2, vec0)) 75 | m = np.stack([vec0, vec1, vec2, pos], 1) 76 | return m 77 | 78 | def poses_avg(poses): 79 | center = poses[:, :3, 3].mean(0) 80 | vec2 = normalize(poses[:, :3, 2].sum(0)) 81 | up = poses[:, :3, 1].sum(0) 82 | c2w = viewmatrix(vec2, up, center) 83 | return c2w 84 | 85 | def render_path_spiral(c2ws, focal, zrate=.1, rots=3, N=300): 86 | c2w = poses_avg(c2ws) 87 | up = normalize(c2ws[:, :3, 1].sum(0)) 88 | tt = c2ws[:,:3,3] 89 | rads = np.percentile(np.abs(tt), 90, 0) 90 | rads[:] = rads.max() * .05 91 | 92 | render_poses = [] 93 | rads = np.array(list(rads) + [1.]) 94 | for theta in np.linspace(0., 2. * np.pi * rots, N+1)[:-1]: 95 | c = np.dot(c2w[:3,:4], np.array([np.cos(theta), -np.sin(theta), -np.sin(theta*zrate), 1.]) * rads) 96 | z = normalize(c - np.dot(c2w[:3,:4], np.array([0,0,-focal, 1.]))) 97 | # c = np.dot(c2w[:3,:4], np.array([np.cos(theta), -np.sin(theta), -np.sin(theta*zrate), 1.]) * rads) 98 | # z = normalize(c2w[:3, 2]) 99 | render_poses.append(viewmatrix(z, up, c)) 100 | render_poses = np.stack(render_poses, axis=0) 101 | render_poses = np.concatenate([render_poses, np.zeros_like(render_poses[..., :1, :])], axis=1) 102 | render_poses[..., 3, 3] = 1 103 | render_poses = np.array(render_poses, dtype=np.float32) 104 | return render_poses 105 | 106 | def render_wander_path(view): 107 | focal_length = fov2focal(view.FoVy, view.image_height) 108 | R = view.R 109 | R[:, 1] = -R[:, 1] 110 | R[:, 2] = -R[:, 2] 111 | T = -view.T.reshape(-1, 1) 112 | pose = np.concatenate([R, T], -1) 113 | 114 | num_frames = 60 115 | max_disp = 5000.0 # 64 , 48 116 | 117 | max_trans = max_disp / focal_length # Maximum camera translation to satisfy max_disp parameter 118 | output_poses = [] 119 | 120 | for i in range(num_frames): 121 | x_trans = max_trans * np.sin(2.0 * np.pi * float(i) / float(num_frames)) 122 | y_trans = max_trans * np.cos(2.0 * np.pi * float(i) / float(num_frames)) / 3.0 # * 3.0 / 4.0 123 | z_trans = max_trans * np.cos(2.0 * np.pi * float(i) / float(num_frames)) / 3.0 124 | 125 | i_pose = np.concatenate([ 126 | np.concatenate( 127 | [np.eye(3), np.array([x_trans, y_trans, z_trans])[:, np.newaxis]], axis=1), 128 | np.array([0.0, 0.0, 0.0, 1.0])[np.newaxis, :] 129 | ], axis=0) # [np.newaxis, :, :] 130 | 131 | i_pose = np.linalg.inv(i_pose) # torch.tensor(np.linalg.inv(i_pose)).float() 132 | 133 | ref_pose = np.concatenate([pose, np.array([0.0, 0.0, 0.0, 1.0])[np.newaxis, :]], axis=0) 134 | 135 | render_pose = np.dot(ref_pose, i_pose) 136 | output_poses.append(torch.Tensor(render_pose)) 137 | 138 | return output_poses 139 | -------------------------------------------------------------------------------- /utils/preprocess.py: -------------------------------------------------------------------------------- 1 | # @title Configure dataset directories 2 | import os 3 | from pathlib import Path 4 | 5 | # @markdown The base directory for all captures. This can be anything if you're running this notebook on your own Jupyter runtime. 6 | save_dir = '/data00/yzy/Git_Project/data/dynamic/mine/' # @param {type: 'string'} 7 | capture_name = 'lemon' # @param {type: 'string'} 8 | # The root directory for this capture. 9 | root_dir = Path(save_dir, capture_name) 10 | # Where to save RGB images. 11 | rgb_dir = root_dir / 'rgb' 12 | rgb_raw_dir = root_dir / 'rgb-raw' 13 | # Where to save the COLMAP outputs. 14 | colmap_dir = root_dir / 'colmap' 15 | colmap_db_path = colmap_dir / 'database.db' 16 | colmap_out_path = colmap_dir / 'sparse' 17 | 18 | colmap_out_path.mkdir(exist_ok=True, parents=True) 19 | rgb_raw_dir.mkdir(exist_ok=True, parents=True) 20 | 21 | print(f"""Directories configured: 22 | root_dir = {root_dir} 23 | rgb_raw_dir = {rgb_raw_dir} 24 | rgb_dir = {rgb_dir} 25 | colmap_dir = {colmap_dir} 26 | """) 27 | 28 | # ==================== colmap ========================= 29 | # @title Extract features. 30 | # @markdown Computes SIFT features and saves them to the COLMAP DB. 31 | share_intrinsics = True # @param {type: 'boolean'} 32 | assume_upright_cameras = True # @param {type: 'boolean'} 33 | 34 | # @markdown This sets the scale at which we will run COLMAP. A scale of 1 will be more accurate but will be slow. 35 | colmap_image_scale = 4 # @param {type: 'number'} 36 | colmap_rgb_dir = rgb_dir / f'{colmap_image_scale}x' 37 | 38 | # @markdown Check this if you want to re-process SfM. 39 | overwrite = False # @param {type: 'boolean'} 40 | 41 | if overwrite and colmap_db_path.exists(): 42 | colmap_db_path.unlink() 43 | 44 | os.system('colmap feature_extractor \ 45 | --SiftExtraction.use_gpu 0 \ 46 | --SiftExtraction.upright {int(assume_upright_cameras)} \ 47 | --ImageReader.camera_model OPENCV \ 48 | --ImageReader.single_camera {int(share_intrinsics)} \ 49 | --database_path "{str(colmap_db_path)}" \ 50 | --image_path "{str(colmap_rgb_dir)}"') 51 | 52 | # @title Match features. 53 | # @markdown Match the SIFT features between images. Use `exhaustive` if you only have a few images and use `vocab_tree` if you have a lot of images. 54 | 55 | match_method = 'exhaustive' # @param ["exhaustive", "vocab_tree"] 56 | 57 | if match_method == 'exhaustive': 58 | os.system('colmap exhaustive_matcher \ 59 | --SiftMatching.use_gpu 0 \ 60 | --database_path "{str(colmap_db_path)}"') 61 | 62 | # @title Reconstruction. 63 | # @markdown Run structure-from-motion to compute camera parameters. 64 | 65 | refine_principal_point = True # @param {type:"boolean"} 66 | min_num_matches = 32 # @param {type: 'number'} 67 | filter_max_reproj_error = 2 # @param {type: 'number'} 68 | tri_complete_max_reproj_error = 2 # @param {type: 'number'} 69 | 70 | os.system('colmap mapper \ 71 | --Mapper.ba_refine_principal_point {int(refine_principal_point)} \ 72 | --Mapper.filter_max_reproj_error $filter_max_reproj_error \ 73 | --Mapper.tri_complete_max_reproj_error $tri_complete_max_reproj_error \ 74 | --Mapper.min_num_matches $min_num_matches \ 75 | --database_path "{str(colmap_db_path)}" \ 76 | --image_path "{str(colmap_rgb_dir)}" \ 77 | --export_path "{str(colmap_out_path)}"') 78 | 79 | print("debug") 80 | -------------------------------------------------------------------------------- /utils/rigid_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | 4 | def skew(w: torch.Tensor) -> torch.Tensor: 5 | """Build a skew matrix ("cross product matrix") for vector w. 6 | 7 | Modern Robotics Eqn 3.30. 8 | 9 | Args: 10 | w: (N, 3) A 3-vector 11 | 12 | Returns: 13 | W: (N, 3, 3) A skew matrix such that W @ v == w x v 14 | """ 15 | zeros = torch.zeros(w.shape[0], device=w.device) 16 | w_skew_list = [zeros, -w[:, 2], w[:, 1], 17 | w[:, 2], zeros, -w[:, 0], 18 | -w[:, 1], w[:, 0], zeros] 19 | w_skew = torch.stack(w_skew_list, dim=-1).reshape(-1, 3, 3) 20 | return w_skew 21 | 22 | 23 | def rp_to_se3(R: torch.Tensor, p: torch.Tensor) -> torch.Tensor: 24 | """Rotation and translation to homogeneous transform. 25 | 26 | Args: 27 | R: (3, 3) An orthonormal rotation matrix. 28 | p: (3,) A 3-vector representing an offset. 29 | 30 | Returns: 31 | X: (4, 4) The homogeneous transformation matrix described by rotating by R 32 | and translating by p. 33 | """ 34 | bottom_row = torch.tensor([[0.0, 0.0, 0.0, 1.0]], device=R.device).repeat(R.shape[0], 1, 1) 35 | transform = torch.cat([torch.cat([R, p], dim=-1), bottom_row], dim=1) 36 | 37 | return transform 38 | 39 | 40 | def exp_so3(w: torch.Tensor, theta: float) -> torch.Tensor: 41 | """Exponential map from Lie algebra so3 to Lie group SO3. 42 | 43 | Modern Robotics Eqn 3.51, a.k.a. Rodrigues' formula. 44 | 45 | Args: 46 | w: (3,) An axis of rotation. 47 | theta: An angle of rotation. 48 | 49 | Returns: 50 | R: (3, 3) An orthonormal rotation matrix representing a rotation of 51 | magnitude theta about axis w. 52 | """ 53 | W = skew(w) 54 | identity = torch.eye(3).unsqueeze(0).repeat(W.shape[0], 1, 1).to(W.device) 55 | W_sqr = torch.bmm(W, W) # batch matrix multiplication 56 | R = identity + torch.sin(theta.unsqueeze(-1)) * W + (1.0 - torch.cos(theta.unsqueeze(-1))) * W_sqr 57 | return R 58 | 59 | 60 | def exp_se3(S: torch.Tensor, theta: float) -> torch.Tensor: 61 | """Exponential map from Lie algebra so3 to Lie group SO3. 62 | 63 | Modern Robotics Eqn 3.88. 64 | 65 | Args: 66 | S: (6,) A screw axis of motion. 67 | theta: Magnitude of motion. 68 | 69 | Returns: 70 | a_X_b: (4, 4) The homogeneous transformation matrix attained by integrating 71 | motion of magnitude theta about S for one second. 72 | """ 73 | w, v = torch.split(S, 3, dim=-1) 74 | W = skew(w) 75 | R = exp_so3(w, theta) 76 | 77 | identity = torch.eye(3).unsqueeze(0).repeat(W.shape[0], 1, 1).to(W.device) 78 | W_sqr = torch.bmm(W, W) 79 | theta = theta.view(-1, 1, 1) 80 | 81 | p = torch.bmm((theta * identity + (1.0 - torch.cos(theta)) * W + (theta - torch.sin(theta)) * W_sqr), 82 | v.unsqueeze(-1)) 83 | return rp_to_se3(R, p) 84 | 85 | 86 | def to_homogenous(v: torch.Tensor) -> torch.Tensor: 87 | """Converts a vector to a homogeneous coordinate vector by appending a 1. 88 | 89 | Args: 90 | v: A tensor representing a vector or batch of vectors. 91 | 92 | Returns: 93 | A tensor with an additional dimension set to 1. 94 | """ 95 | return torch.cat([v, torch.ones_like(v[..., :1])], dim=-1) 96 | 97 | 98 | def from_homogenous(v: torch.Tensor) -> torch.Tensor: 99 | """Converts a homogeneous coordinate vector to a standard vector by dividing by the last element. 100 | 101 | Args: 102 | v: A tensor representing a homogeneous coordinate vector or batch of homogeneous coordinate vectors. 103 | 104 | Returns: 105 | A tensor with the last dimension removed. 106 | """ 107 | return v[..., :3] / v[..., -1:] 108 | -------------------------------------------------------------------------------- /utils/sh_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The PlenOctree Authors. 2 | # Redistribution and use in source and binary forms, with or without 3 | # modification, are permitted provided that the following conditions are met: 4 | # 5 | # 1. Redistributions of source code must retain the above copyright notice, 6 | # this list of conditions and the following disclaimer. 7 | # 8 | # 2. Redistributions in binary form must reproduce the above copyright notice, 9 | # this list of conditions and the following disclaimer in the documentation 10 | # and/or other materials provided with the distribution. 11 | # 12 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 16 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 18 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 19 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 20 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 21 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 22 | # POSSIBILITY OF SUCH DAMAGE. 23 | 24 | import torch 25 | 26 | C0 = 0.28209479177387814 27 | C1 = 0.4886025119029199 28 | C2 = [ 29 | 1.0925484305920792, 30 | -1.0925484305920792, 31 | 0.31539156525252005, 32 | -1.0925484305920792, 33 | 0.5462742152960396 34 | ] 35 | C3 = [ 36 | -0.5900435899266435, 37 | 2.890611442640554, 38 | -0.4570457994644658, 39 | 0.3731763325901154, 40 | -0.4570457994644658, 41 | 1.445305721320277, 42 | -0.5900435899266435 43 | ] 44 | C4 = [ 45 | 2.5033429417967046, 46 | -1.7701307697799304, 47 | 0.9461746957575601, 48 | -0.6690465435572892, 49 | 0.10578554691520431, 50 | -0.6690465435572892, 51 | 0.47308734787878004, 52 | -1.7701307697799304, 53 | 0.6258357354491761, 54 | ] 55 | 56 | 57 | def eval_sh(deg, sh, dirs): 58 | """ 59 | Evaluate spherical harmonics at unit directions 60 | using hardcoded SH polynomials. 61 | Works with torch/np/jnp. 62 | ... Can be 0 or more batch dimensions. 63 | Args: 64 | deg: int SH deg. Currently, 0-3 supported 65 | sh: jnp.ndarray SH coeffs [..., C, (deg + 1) ** 2] 66 | dirs: jnp.ndarray unit directions [..., 3] 67 | Returns: 68 | [..., C] 69 | """ 70 | assert deg <= 4 and deg >= 0 71 | coeff = (deg + 1) ** 2 72 | assert sh.shape[-1] >= coeff 73 | 74 | result = C0 * sh[..., 0] 75 | if deg > 0: 76 | x, y, z = dirs[..., 0:1], dirs[..., 1:2], dirs[..., 2:3] 77 | result = (result - 78 | C1 * y * sh[..., 1] + 79 | C1 * z * sh[..., 2] - 80 | C1 * x * sh[..., 3]) 81 | 82 | if deg > 1: 83 | xx, yy, zz = x * x, y * y, z * z 84 | xy, yz, xz = x * y, y * z, x * z 85 | result = (result + 86 | C2[0] * xy * sh[..., 4] + 87 | C2[1] * yz * sh[..., 5] + 88 | C2[2] * (2.0 * zz - xx - yy) * sh[..., 6] + 89 | C2[3] * xz * sh[..., 7] + 90 | C2[4] * (xx - yy) * sh[..., 8]) 91 | 92 | if deg > 2: 93 | result = (result + 94 | C3[0] * y * (3 * xx - yy) * sh[..., 9] + 95 | C3[1] * xy * z * sh[..., 10] + 96 | C3[2] * y * (4 * zz - xx - yy) * sh[..., 11] + 97 | C3[3] * z * (2 * zz - 3 * xx - 3 * yy) * sh[..., 12] + 98 | C3[4] * x * (4 * zz - xx - yy) * sh[..., 13] + 99 | C3[5] * z * (xx - yy) * sh[..., 14] + 100 | C3[6] * x * (xx - 3 * yy) * sh[..., 15]) 101 | 102 | if deg > 3: 103 | result = (result + C4[0] * xy * (xx - yy) * sh[..., 16] + 104 | C4[1] * yz * (3 * xx - yy) * sh[..., 17] + 105 | C4[2] * xy * (7 * zz - 1) * sh[..., 18] + 106 | C4[3] * yz * (7 * zz - 3) * sh[..., 19] + 107 | C4[4] * (zz * (35 * zz - 30) + 3) * sh[..., 20] + 108 | C4[5] * xz * (7 * zz - 3) * sh[..., 21] + 109 | C4[6] * (xx - yy) * (7 * zz - 1) * sh[..., 22] + 110 | C4[7] * xz * (xx - 3 * yy) * sh[..., 23] + 111 | C4[8] * (xx * (xx - 3 * yy) - yy * (3 * xx - yy)) * sh[..., 24]) 112 | return result 113 | 114 | 115 | def RGB2SH(rgb): 116 | return (rgb - 0.5) / C0 117 | 118 | 119 | def SH2RGB(sh): 120 | return sh * C0 + 0.5 121 | -------------------------------------------------------------------------------- /utils/system_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2023, Inria 3 | # GRAPHDECO research group, https://team.inria.fr/graphdeco 4 | # All rights reserved. 5 | # 6 | # This software is free for non-commercial, research and evaluation use 7 | # under the terms of the LICENSE.md file. 8 | # 9 | # For inquiries contact george.drettakis@inria.fr 10 | # 11 | 12 | from errno import EEXIST 13 | from os import makedirs, path 14 | import os 15 | 16 | 17 | def mkdir_p(folder_path): 18 | # Creates a directory. equivalent to using mkdir -p on the command line 19 | try: 20 | makedirs(folder_path) 21 | except OSError as exc: # Python >2.5 22 | if exc.errno == EEXIST and path.isdir(folder_path): 23 | pass 24 | else: 25 | raise 26 | 27 | 28 | def searchForMaxIteration(folder): 29 | if not os.path.exists(folder): 30 | return None 31 | saved_iters = [int(fname.split("_")[-1]) for fname in os.listdir(folder) if "_" in fname] 32 | return max(saved_iters) if saved_iters != [] else None 33 | -------------------------------------------------------------------------------- /utils/vis_utils.py: -------------------------------------------------------------------------------- 1 | from gaussian_renderer import render 2 | 3 | 4 | def render_cur_cam(self, cur_cam): 5 | fid = cur_cam.fid 6 | if self.deform.name == 'node': 7 | if 'Node' in self.visualization_mode: 8 | gaussians = self.deform.deform.as_gaussians # if self.iteration_node_rendering < self.opt.iterations_node_rendering else self.deform.deform.as_gaussians_visualization 9 | time_input = fid.unsqueeze(0).expand(gaussians.get_xyz.shape[0], -1) 10 | d_values = self.deform.deform.query_network(x=gaussians.get_xyz.detach(), t=time_input) 11 | if self.motion_animation_d_values is not None: 12 | for key in self.motion_animation_d_values: 13 | d_values[key] = self.motion_animation_d_values[key] 14 | d_xyz, d_opacity, d_color = d_values['d_xyz'] * gaussians.motion_mask, d_values['d_opacity'] * gaussians.motion_mask if d_values['d_opacity'] is not None else None, d_values['d_color'] * gaussians.motion_mask if d_values['d_color'] is not None else None 15 | d_rotation, d_scaling = 0., 0. 16 | if self.animation_trans_bias is not None: 17 | d_xyz = d_xyz + self.animation_trans_bias 18 | gs_rot_bias = None 19 | vis_scale_const = self.vis_scale_const 20 | else: 21 | time_input = self.deform.deform.expand_time(fid) 22 | d_values = self.deform.step(self.gaussians.get_xyz.detach(), time_input, feature=self.gaussians.feature, is_training=False, node_trans_bias=self.animation_trans_bias, node_rot_bias=self.animation_rot_bias, motion_mask=self.gaussians.motion_mask, camera_center=cur_cam.camera_center, animation_d_values=self.motion_animation_d_values) 23 | gaussians = self.gaussians 24 | d_xyz, d_rotation, d_scaling, d_opacity, d_color = d_values['d_xyz'], d_values['d_rotation'], d_values['d_scaling'], d_values['d_opacity'], d_values['d_color'] 25 | gs_rot_bias = d_values['gs_rot_bias'] # GS rotation bias 26 | vis_scale_const = None 27 | else: 28 | vis_scale_const = None 29 | if self.iteration < self.opt.warm_up: 30 | d_xyz, d_rotation, d_scaling, d_opacity, d_color = 0.0, 0.0, 0.0, 0.0, 0.0 31 | gaussians = self.gaussians 32 | else: 33 | N = self.gaussians.get_xyz.shape[0] 34 | time_input = fid.unsqueeze(0).expand(N, -1) 35 | gaussians = self.gaussians 36 | d_values = self.deform.step(self.gaussians.get_xyz.detach(), time_input, feature=self.gaussians.feature, camera_center=cur_cam.camera_center) 37 | d_xyz, d_rotation, d_scaling, d_opacity, d_color = d_values['d_xyz'], d_values['d_rotation'], d_values['d_scaling'], d_values['d_opacity'], d_values['d_color'] 38 | gs_rot_bias = None 39 | 40 | render_motion = "Motion" in self.visualization_mode 41 | if render_motion: 42 | vis_scale_const = self.vis_scale_const 43 | if type(d_rotation) is not float and gaussians._rotation.shape[0] != d_rotation.shape[0]: 44 | d_xyz, d_rotation, d_scaling = 0, 0, 0 45 | print('Async in Gaussian Switching') 46 | out = render(viewpoint_camera=cur_cam, pc=gaussians, pipe=self.pipe, bg_color=self.background, d_xyz=d_xyz, d_rotation=d_rotation, d_scaling=d_scaling, render_motion=render_motion, d_opacity=d_opacity, d_color=d_color, d_rot_as_res=self.deform.d_rot_as_res, gs_rot_bias=gs_rot_bias, scale_const=vis_scale_const) 47 | 48 | buffer_image = out[self.mode] # [3, H, W] 49 | return buffer_image 50 | --------------------------------------------------------------------------------