gaussian-splatting 1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gaussian_splatting-1.0/LICENSE.md +91 -0
- gaussian_splatting-1.0/PKG-INFO +11 -0
- gaussian_splatting-1.0/README.md +196 -0
- gaussian_splatting-1.0/gaussian_splatting/__init__.py +3 -0
- gaussian_splatting-1.0/gaussian_splatting/camera.py +106 -0
- gaussian_splatting-1.0/gaussian_splatting/camera_trainable.py +95 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/__init__.py +2 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/camera_trainable.py +70 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/colmap/__init__.py +5 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/colmap/dataset.py +106 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/colmap/params_init.py +120 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/colmap/utils.py +299 -0
- gaussian_splatting-1.0/gaussian_splatting/dataset/dataset.py +71 -0
- gaussian_splatting-1.0/gaussian_splatting/gaussian_model.py +254 -0
- gaussian_splatting-1.0/gaussian_splatting/render.py +61 -0
- gaussian_splatting-1.0/gaussian_splatting/train.py +112 -0
- gaussian_splatting-1.0/gaussian_splatting/trainer/__init__.py +3 -0
- gaussian_splatting-1.0/gaussian_splatting/trainer/camera_trainable.py +72 -0
- gaussian_splatting-1.0/gaussian_splatting/trainer/densifier.py +233 -0
- gaussian_splatting-1.0/gaussian_splatting/trainer/trainer.py +177 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/__init__.py +7 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/general.py +66 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/graphics.py +67 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/loss.py +64 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/lpipsPyTorch/__init__.py +21 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/lpipsPyTorch/modules/__init__.py +0 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/lpipsPyTorch/modules/lpips.py +36 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/lpipsPyTorch/modules/networks.py +96 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/lpipsPyTorch/modules/utils.py +30 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/metrics.py +19 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/pose.py +160 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/schedular.py +38 -0
- gaussian_splatting-1.0/gaussian_splatting/utils/shs.py +119 -0
- gaussian_splatting-1.0/gaussian_splatting.egg-info/PKG-INFO +11 -0
- gaussian_splatting-1.0/gaussian_splatting.egg-info/SOURCES.txt +55 -0
- gaussian_splatting-1.0/gaussian_splatting.egg-info/dependency_links.txt +1 -0
- gaussian_splatting-1.0/gaussian_splatting.egg-info/requires.txt +2 -0
- gaussian_splatting-1.0/gaussian_splatting.egg-info/top_level.txt +1 -0
- gaussian_splatting-1.0/setup.cfg +4 -0
- gaussian_splatting-1.0/setup.py +68 -0
- gaussian_splatting-1.0/submodules/diff-gaussian-rasterization/cuda_rasterizer/backward.cu +753 -0
- gaussian_splatting-1.0/submodules/diff-gaussian-rasterization/cuda_rasterizer/forward.cu +484 -0
- gaussian_splatting-1.0/submodules/diff-gaussian-rasterization/cuda_rasterizer/rasterizer_impl.cu +450 -0
- gaussian_splatting-1.0/submodules/diff-gaussian-rasterization/diff_gaussian_rasterization/__init__.py +208 -0
- gaussian_splatting-1.0/submodules/diff-gaussian-rasterization/ext.cpp +19 -0
- gaussian_splatting-1.0/submodules/diff-gaussian-rasterization/rasterize_points.cu +243 -0
- gaussian_splatting-1.0/submodules/simple-knn/ext.cpp +17 -0
- gaussian_splatting-1.0/submodules/simple-knn/simple_knn.cu +222 -0
- gaussian_splatting-1.0/submodules/simple-knn/spatial.cu +26 -0
|
@@ -0,0 +1,91 @@
|
|
|
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)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# packaged 3D Gaussian Splatting
|
|
2
|
+
|
|
3
|
+
This repo is the **refactored python training and inference code for [3D Gaussian Splatting](https://github.com/graphdeco-inria/gaussian-splatting)**.
|
|
4
|
+
Forked from commit [a2a91d9093fd791fb01f556fa717f8d9f2cfbdd7](https://github.com/graphdeco-inria/gaussian-splatting/tree/a2a91d9093fd791fb01f556fa717f8d9f2cfbdd7).
|
|
5
|
+
We **refactored the original code following the standard Python package structure**, while **keeping the algorithms used in the code identical to the original version**.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
* [x] organize the code as a standard Python package
|
|
10
|
+
* [x] exposure compensation
|
|
11
|
+
* [x] camera and 3DGS parameters joint training
|
|
12
|
+
* [ ] depth regularization
|
|
13
|
+
* [ ] integrated 2DGS (integrated [gsplat](https://github.com/nerfstudio-project/gsplat) backend)
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
### Requirements
|
|
18
|
+
|
|
19
|
+
Install Pytorch and torchvision following the official guideline: [pytorch.org](https://pytorch.org/)
|
|
20
|
+
|
|
21
|
+
### Local Install
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
git clone https://github.com/yindaheng98/gaussian-splatting --recursive
|
|
25
|
+
cd gaussian-splatting
|
|
26
|
+
pip install tqdm plyfile
|
|
27
|
+
pip install --target . --upgrade . --no-deps
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Pip Install
|
|
31
|
+
|
|
32
|
+
You can download wheel from [Releases](https://github.com/yindaheng98/gaussian-splatting/releases), or
|
|
33
|
+
install latest from source:
|
|
34
|
+
```shell
|
|
35
|
+
pip install --upgrade git+https://github.com/yindaheng98/gaussian-splatting.git@master
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Running
|
|
39
|
+
|
|
40
|
+
Download dataset [T&T+DB COLMAP (650MB)](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/datasets/input/tandt_db.zip) and extract to `./data` directory.
|
|
41
|
+
|
|
42
|
+
1. Train 3DGS with densification (same with original 3DGS)
|
|
43
|
+
```shell
|
|
44
|
+
python -m gaussian_splatting.train -s data/truck -d output/truck -i 30000 --mode densify
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
2. Render it
|
|
48
|
+
```shell
|
|
49
|
+
python -m gaussian_splatting.render -s data/truck -d output/truck -i 30000 --mode densify
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. Joint training 3DGS and camera (load the trained 3DGS)
|
|
53
|
+
```shell
|
|
54
|
+
python -m gaussian_splatting.train -s data/truck -d output/truck-camera -i 30000 --mode camera -l output/truck/point_cloud/iteration_30000/point_cloud.ply
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. Render it with trained 3DGS
|
|
58
|
+
```shell
|
|
59
|
+
python -m gaussian_splatting.render -s data/truck -d output/truck-camera -i 30000 --mode camera --load_camera output/truck-camera/cameras.json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This repo do not contrains code for initialization.
|
|
63
|
+
If you want to create your own scene, please refer to [InstantSplat](https://github.com/yindaheng98/InstantSplat) or use [convert.py](https://github.com/graphdeco-inria/gaussian-splatting/blob/main/convert.py).
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
**See [.vscode/launch.json](.vscode/launch.json) for more example.**
|
|
68
|
+
|
|
69
|
+
**See [gaussian_splatting.train](gaussian_splatting/train.py) and [gaussian_splatting.render](gaussian_splatting/render.py) for full options.**
|
|
70
|
+
|
|
71
|
+
### Gaussian models
|
|
72
|
+
|
|
73
|
+
`GaussianModel` is the basic 3DGS model.
|
|
74
|
+
```python
|
|
75
|
+
from gaussian_splatting import GaussianModel
|
|
76
|
+
gaussians = GaussianModel(sh_degree).to(device)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If you want cameras-3DGS joint training, use `CameraTrainableGaussianModel`, the rendering process is different.
|
|
80
|
+
```python
|
|
81
|
+
from gaussian_splatting import CameraTrainableGaussianModel
|
|
82
|
+
gaussians = CameraTrainableGaussianModel(sh_degree).to(device)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
save and load params:
|
|
86
|
+
```python
|
|
87
|
+
gaussians.save_ply("output/truck/point_cloud/iteration_30000/point_cloud.ply")
|
|
88
|
+
gaussians.load_ply("output/truck/point_cloud/iteration_30000/point_cloud.ply")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
init 3DGS with sparse point cloud extracted by colmap:
|
|
92
|
+
```python
|
|
93
|
+
from gaussian_splatting.dataset.colmap import colmap_init
|
|
94
|
+
colmap_init(gaussians, "data/truck")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Dataset
|
|
98
|
+
|
|
99
|
+
Basic colmap dataset:
|
|
100
|
+
```python
|
|
101
|
+
from gaussian_splatting.dataset.colmap import ColmapCameraDataset, colmap_init
|
|
102
|
+
dataset = ColmapCameraDataset("data/truck")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
save to JSON and load JSON dataset:
|
|
106
|
+
```python
|
|
107
|
+
dataset.save_cameras("output/truck/cameras.json")
|
|
108
|
+
from gaussian_splatting import JSONCameraDataset
|
|
109
|
+
dataset = JSONCameraDataset("output/truck/cameras.json")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Dataset with trainable cameras:
|
|
113
|
+
```python
|
|
114
|
+
from gaussian_splatting import TrainableCameraDataset
|
|
115
|
+
dataset = TrainableCameraDataset("data/truck") # init cameras from colmap
|
|
116
|
+
dataset = TrainableCameraDataset.from_json("output/truck/cameras.json") # init cameras from saved json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Inference
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
for camera in dataset:
|
|
123
|
+
out = gaussians(camera)
|
|
124
|
+
image = out["render"]
|
|
125
|
+
... # compute loss, save image or others
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Training
|
|
129
|
+
|
|
130
|
+
`BaseTrainer` only optimize the 3DGS parameters, without densification or joint training with cameras.
|
|
131
|
+
```python
|
|
132
|
+
from gaussian_splatting.trainer import BaseTrainer
|
|
133
|
+
trainer = BaseTrainer(
|
|
134
|
+
gaussians,
|
|
135
|
+
spatial_lr_scale=dataset.scene_extent(),
|
|
136
|
+
... # see gaussian_splatting/trainer/trainer.py for full options
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`DensificationTrainer` optimize the 3DGS parameters and densify it.
|
|
141
|
+
```python
|
|
142
|
+
from gaussian_splatting.trainer import DensificationTrainer
|
|
143
|
+
trainer = DensificationTrainer(
|
|
144
|
+
gaussians,
|
|
145
|
+
scene_extent=dataset.scene_extent(),
|
|
146
|
+
... # see gaussian_splatting/trainer/densifier.py for full options
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`CameraTrainer` jointly optimize the 3DGS parameters and cameras, without densification
|
|
151
|
+
```python
|
|
152
|
+
from gaussian_splatting.trainer import CameraTrainer
|
|
153
|
+
trainer = CameraTrainer(
|
|
154
|
+
gaussians,
|
|
155
|
+
scene_extent=dataset.scene_extent(),
|
|
156
|
+
dataset=dataset,
|
|
157
|
+
... # see gaussian_splatting/trainer/camera_trainable.py for full options
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Train it:
|
|
162
|
+
```python
|
|
163
|
+
for camera in dataset:
|
|
164
|
+
loss, out = trainer.step(camera)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
# 3D Gaussian Splatting for Real-Time Radiance Field Rendering
|
|
168
|
+
Bernhard Kerbl*, Georgios Kopanas*, Thomas Leimkühler, George Drettakis (* indicates equal contribution)<br>
|
|
169
|
+
| [Webpage](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/) | [Full Paper](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gaussian_splatting_high.pdf) | [Video](https://youtu.be/T_kXY43VZnk) | [Other GRAPHDECO Publications](http://www-sop.inria.fr/reves/publis/gdindex.php) | [FUNGRAPH project page](https://fungraph.inria.fr) |<br>
|
|
170
|
+
| [T&T+DB COLMAP (650MB)](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/datasets/input/tandt_db.zip) | [Pre-trained Models (14 GB)](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/datasets/pretrained/models.zip) | [Viewers for Windows (60MB)](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/binaries/viewers.zip) | [Evaluation Images (7 GB)](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/evaluation/images.zip) |<br>
|
|
171
|
+

|
|
172
|
+
|
|
173
|
+
This repository contains the official authors implementation associated with the paper "3D Gaussian Splatting for Real-Time Radiance Field Rendering", which can be found [here](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/). We further provide the reference images used to create the error metrics reported in the paper, as well as recently created, pre-trained models.
|
|
174
|
+
|
|
175
|
+
<a href="https://www.inria.fr/"><img height="100" src="assets/logo_inria.png"> </a>
|
|
176
|
+
<a href="https://univ-cotedazur.eu/"><img height="100" src="assets/logo_uca.png"> </a>
|
|
177
|
+
<a href="https://www.mpi-inf.mpg.de"><img height="100" src="assets/logo_mpi.png"> </a>
|
|
178
|
+
<a href="https://team.inria.fr/graphdeco/"> <img style="width:100%;" src="assets/logo_graphdeco.png"></a>
|
|
179
|
+
|
|
180
|
+
Abstract: *Radiance Field methods have recently revolutionized novel-view synthesis of scenes captured with multiple photos or videos. However, achieving high visual quality still requires neural networks that are costly to train and render, while recent faster methods inevitably trade off speed for quality. For unbounded and complete scenes (rather than isolated objects) and 1080p resolution rendering, no current method can achieve real-time display rates. We introduce three key elements that allow us to achieve state-of-the-art visual quality while maintaining competitive training times and importantly allow high-quality real-time (≥ 30 fps) novel-view synthesis at 1080p resolution. First, starting from sparse points produced during camera calibration, we represent the scene with 3D Gaussians that preserve desirable properties of continuous volumetric radiance fields for scene optimization while avoiding unnecessary computation in empty space; Second, we perform interleaved optimization/density control of the 3D Gaussians, notably optimizing anisotropic covariance to achieve an accurate representation of the scene; Third, we develop a fast visibility-aware rendering algorithm that supports anisotropic splatting and both accelerates training and allows realtime rendering. We demonstrate state-of-the-art visual quality and real-time rendering on several established datasets.*
|
|
181
|
+
|
|
182
|
+
<section class="section" id="BibTeX">
|
|
183
|
+
<div class="container is-max-desktop content">
|
|
184
|
+
<h2 class="title">BibTeX</h2>
|
|
185
|
+
<pre><code>@Article{kerbl3Dgaussians,
|
|
186
|
+
author = {Kerbl, Bernhard and Kopanas, Georgios and Leimk{\"u}hler, Thomas and Drettakis, George},
|
|
187
|
+
title = {3D Gaussian Splatting for Real-Time Radiance Field Rendering},
|
|
188
|
+
journal = {ACM Transactions on Graphics},
|
|
189
|
+
number = {4},
|
|
190
|
+
volume = {42},
|
|
191
|
+
month = {July},
|
|
192
|
+
year = {2023},
|
|
193
|
+
url = {https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/}
|
|
194
|
+
}</code></pre>
|
|
195
|
+
</div>
|
|
196
|
+
</section>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import NamedTuple, Callable
|
|
3
|
+
import torch
|
|
4
|
+
from PIL import Image
|
|
5
|
+
from .utils import fov2focal, focal2fov, getProjectionMatrix, getWorld2View2, PILtoTorch, matrix_to_quaternion
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Camera(NamedTuple):
|
|
9
|
+
image_height: int
|
|
10
|
+
image_width: int
|
|
11
|
+
FoVx: float
|
|
12
|
+
FoVy: float
|
|
13
|
+
R: torch.Tensor
|
|
14
|
+
T: torch.Tensor
|
|
15
|
+
world_view_transform: torch.Tensor
|
|
16
|
+
projection_matrix: torch.Tensor
|
|
17
|
+
full_proj_transform: torch.Tensor
|
|
18
|
+
camera_center: torch.Tensor
|
|
19
|
+
quaternion: torch.Tensor
|
|
20
|
+
ground_truth_image_path: str
|
|
21
|
+
ground_truth_image: torch.Tensor = None
|
|
22
|
+
postprocess: Callable[['Camera', torch.Tensor], torch.Tensor] = lambda camera, x: x
|
|
23
|
+
bg_color: torch.Tensor = torch.tensor([0., 0., 0.])
|
|
24
|
+
custom_data: dict = {}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def camera2dict(camera: Camera, id):
|
|
28
|
+
Rt = torch.zeros((4, 4))
|
|
29
|
+
Rt[:3, :3] = camera.R
|
|
30
|
+
Rt[:3, 3] = camera.T
|
|
31
|
+
Rt[3, 3] = 1.0
|
|
32
|
+
|
|
33
|
+
W2C = torch.linalg.inv(Rt)
|
|
34
|
+
pos = W2C[:3, 3]
|
|
35
|
+
rot = W2C[:3, :3]
|
|
36
|
+
serializable_array_2d = [x.tolist() for x in rot]
|
|
37
|
+
camera_entry = {
|
|
38
|
+
'id': id,
|
|
39
|
+
'width': camera.image_width,
|
|
40
|
+
'height': camera.image_height,
|
|
41
|
+
'position': pos.tolist(),
|
|
42
|
+
'rotation': serializable_array_2d,
|
|
43
|
+
'fy': fov2focal(camera.FoVx, camera.image_height),
|
|
44
|
+
'fx': fov2focal(camera.FoVy, camera.image_width),
|
|
45
|
+
'ground_truth_image_path': camera.ground_truth_image_path.replace("\\", "/"),
|
|
46
|
+
"img_name": os.path.basename(camera.ground_truth_image_path),
|
|
47
|
+
}
|
|
48
|
+
return camera_entry
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def build_camera(
|
|
52
|
+
image_height: int, image_width: int,
|
|
53
|
+
FoVx: float, FoVy: float,
|
|
54
|
+
R: torch.Tensor, T: torch.Tensor,
|
|
55
|
+
image_path: str = None, device="cuda"
|
|
56
|
+
):
|
|
57
|
+
zfar = 100.0
|
|
58
|
+
znear = 0.01
|
|
59
|
+
trans = torch.zeros(3)
|
|
60
|
+
scale = 1.0
|
|
61
|
+
world_view_transform = getWorld2View2(R, T, trans, scale).to(device).transpose(0, 1)
|
|
62
|
+
projection_matrix = getProjectionMatrix(znear=znear, zfar=zfar, fovX=FoVx, fovY=FoVy).to(device).transpose(0, 1)
|
|
63
|
+
full_proj_transform = (world_view_transform.unsqueeze(0).bmm(projection_matrix.unsqueeze(0))).squeeze(0)
|
|
64
|
+
camera_center = world_view_transform.inverse()[3, :3]
|
|
65
|
+
quaternion = matrix_to_quaternion(R)
|
|
66
|
+
gt_image = None
|
|
67
|
+
if image_path is not None:
|
|
68
|
+
pil_image = Image.open(image_path)
|
|
69
|
+
torch_image = PILtoTorch(pil_image)
|
|
70
|
+
gt_image = torch_image[:3, ...].clamp(0.0, 1.0).to(device)
|
|
71
|
+
image_height = gt_image.shape[1]
|
|
72
|
+
image_width = gt_image.shape[2]
|
|
73
|
+
return Camera(
|
|
74
|
+
# image_height=colmap_camera.image_height, # colmap_camera.image_height is read from cameras.bin, maybe dfferent from the actual image size
|
|
75
|
+
# image_width=colmap_camera.image_width, # colmap_camera.image_width is read from cameras.bin, maybe dfferent from the actual image size
|
|
76
|
+
image_height=image_height, image_width=image_width,
|
|
77
|
+
FoVx=FoVx, FoVy=FoVy,
|
|
78
|
+
R=R.to(device), T=T.to(device),
|
|
79
|
+
world_view_transform=world_view_transform,
|
|
80
|
+
projection_matrix=projection_matrix,
|
|
81
|
+
full_proj_transform=full_proj_transform,
|
|
82
|
+
camera_center=camera_center,
|
|
83
|
+
quaternion=quaternion.to(device),
|
|
84
|
+
ground_truth_image_path=image_path,
|
|
85
|
+
ground_truth_image=gt_image
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def dict2camera(camera_dict, device="cuda"):
|
|
90
|
+
W2C = torch.zeros((4, 4))
|
|
91
|
+
W2C[:3, 3] = torch.tensor(camera_dict['position'])
|
|
92
|
+
W2C[:3, :3] = torch.tensor(camera_dict['rotation'])
|
|
93
|
+
W2C[3, 3] = 1.0
|
|
94
|
+
Rt = torch.linalg.inv(W2C)
|
|
95
|
+
T = Rt[:3, 3]
|
|
96
|
+
R = Rt[:3, :3]
|
|
97
|
+
return build_camera(
|
|
98
|
+
image_width=camera_dict['width'],
|
|
99
|
+
image_height=camera_dict['height'],
|
|
100
|
+
FoVx=focal2fov(camera_dict['fy'], camera_dict['height']),
|
|
101
|
+
FoVy=focal2fov(camera_dict['fx'], camera_dict['width']),
|
|
102
|
+
R=R,
|
|
103
|
+
T=T,
|
|
104
|
+
image_path=camera_dict['ground_truth_image_path'],
|
|
105
|
+
device=device
|
|
106
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import math
|
|
3
|
+
from .gaussian_model import GaussianModel, Camera
|
|
4
|
+
from .utils import normalize_quaternion, quaternion_to_matrix, quaternion_raw_multiply
|
|
5
|
+
|
|
6
|
+
from gaussian_splatting.diff_gaussian_rasterization import GaussianRasterizationSettings, GaussianRasterizer
|
|
7
|
+
from gaussian_splatting.simple_knn._C import distCUDA2
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CameraTrainableGaussianModel(GaussianModel):
|
|
11
|
+
|
|
12
|
+
def forward(self, viewpoint_camera: Camera):
|
|
13
|
+
# Create zero tensor. We will use it to make pytorch return gradients of the 2D (screen-space) means
|
|
14
|
+
screenspace_points = torch.zeros_like(self.get_xyz, dtype=self.get_xyz.dtype, requires_grad=True, device=self._xyz.device) + 0
|
|
15
|
+
try:
|
|
16
|
+
screenspace_points.retain_grad()
|
|
17
|
+
except:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
# Set up rasterization configuration
|
|
21
|
+
tanfovx = math.tan(viewpoint_camera.FoVx * 0.5)
|
|
22
|
+
tanfovy = math.tan(viewpoint_camera.FoVy * 0.5)
|
|
23
|
+
|
|
24
|
+
# Set camera pose as identity. Then, we will transform the Gaussians around camera_pose
|
|
25
|
+
w2c = torch.eye(4, device=self._xyz.device)
|
|
26
|
+
projmatrix = (
|
|
27
|
+
w2c.unsqueeze(0).bmm(viewpoint_camera.projection_matrix.unsqueeze(0))
|
|
28
|
+
).squeeze(0)
|
|
29
|
+
campos = w2c.inverse()[3, :3]
|
|
30
|
+
raster_settings = GaussianRasterizationSettings(
|
|
31
|
+
image_height=int(viewpoint_camera.image_height),
|
|
32
|
+
image_width=int(viewpoint_camera.image_width),
|
|
33
|
+
tanfovx=tanfovx,
|
|
34
|
+
tanfovy=tanfovy,
|
|
35
|
+
bg=viewpoint_camera.bg_color.to(self._xyz.device),
|
|
36
|
+
scale_modifier=self.scale_modifier,
|
|
37
|
+
# viewmatrix=viewpoint_camera.world_view_transform,
|
|
38
|
+
# projmatrix=viewpoint_camera.full_proj_transform,
|
|
39
|
+
viewmatrix=w2c,
|
|
40
|
+
projmatrix=projmatrix,
|
|
41
|
+
sh_degree=self.active_sh_degree,
|
|
42
|
+
# campos=viewpoint_camera.camera_center,
|
|
43
|
+
campos=campos,
|
|
44
|
+
prefiltered=False,
|
|
45
|
+
debug=self.debug,
|
|
46
|
+
antialiasing=self.antialiasing
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
rasterizer = GaussianRasterizer(raster_settings=raster_settings)
|
|
50
|
+
|
|
51
|
+
# means3D = pc.get_xyz
|
|
52
|
+
rel_w2c = torch.eye(4, device=self._xyz.device)
|
|
53
|
+
quaternion = viewpoint_camera.quaternion
|
|
54
|
+
rel_w2c[:3, :3] = quaternion_to_matrix(normalize_quaternion(quaternion.unsqueeze(0))).squeeze(0)
|
|
55
|
+
rel_w2c[:3, 3] = viewpoint_camera.T
|
|
56
|
+
# Transform mean and rot of Gaussians to camera frame
|
|
57
|
+
gaussians_xyz = self._xyz.clone()
|
|
58
|
+
gaussians_rot = self._rotation.clone()
|
|
59
|
+
|
|
60
|
+
xyz_ones = torch.ones(gaussians_xyz.shape[0], 1).cuda().float()
|
|
61
|
+
xyz_homo = torch.cat((gaussians_xyz, xyz_ones), dim=1)
|
|
62
|
+
gaussians_xyz_trans = (rel_w2c @ xyz_homo.T).T[:, :3]
|
|
63
|
+
gaussians_rot_trans = quaternion_raw_multiply(quaternion, gaussians_rot)
|
|
64
|
+
means3D = gaussians_xyz_trans
|
|
65
|
+
means2D = screenspace_points
|
|
66
|
+
opacity = self.get_opacity
|
|
67
|
+
|
|
68
|
+
scales = self.get_scaling
|
|
69
|
+
rotations = gaussians_rot_trans # pc.get_rotation
|
|
70
|
+
|
|
71
|
+
shs = self.get_features
|
|
72
|
+
|
|
73
|
+
# Rasterize visible Gaussians to image, obtain their radii (on screen).
|
|
74
|
+
rendered_image, radii, depth_image = rasterizer(
|
|
75
|
+
means3D=means3D,
|
|
76
|
+
means2D=means2D,
|
|
77
|
+
shs=shs,
|
|
78
|
+
colors_precomp=None,
|
|
79
|
+
opacities=opacity,
|
|
80
|
+
scales=scales,
|
|
81
|
+
rotations=rotations,
|
|
82
|
+
cov3D_precomp=None)
|
|
83
|
+
rendered_image = viewpoint_camera.postprocess(viewpoint_camera, rendered_image)
|
|
84
|
+
|
|
85
|
+
# Those Gaussians that were frustum culled or had a radius of 0 were not visible.
|
|
86
|
+
# They will be excluded from value updates used in the splitting criteria.
|
|
87
|
+
rendered_image = rendered_image.clamp(0, 1)
|
|
88
|
+
out = {
|
|
89
|
+
"render": rendered_image,
|
|
90
|
+
"viewspace_points": screenspace_points,
|
|
91
|
+
"visibility_filter": (radii > 0).nonzero(),
|
|
92
|
+
"radii": radii,
|
|
93
|
+
"depth": depth_image
|
|
94
|
+
}
|
|
95
|
+
return out
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import List
|
|
3
|
+
from gaussian_splatting import Camera
|
|
4
|
+
import torch
|
|
5
|
+
import torch.nn as nn
|
|
6
|
+
|
|
7
|
+
from gaussian_splatting.camera import camera2dict
|
|
8
|
+
from gaussian_splatting.utils import quaternion_to_matrix
|
|
9
|
+
from .dataset import CameraDataset, JSONCameraDataset
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def exposure_postprocess(camera: Camera, x: torch.Tensor):
|
|
13
|
+
exposure = camera.custom_data['exposures']
|
|
14
|
+
return torch.matmul(x.permute(1, 2, 0), exposure[:3, :3]).permute(2, 0, 1) + exposure[:3, 3, None, None]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TrainableCameraDataset(CameraDataset):
|
|
18
|
+
|
|
19
|
+
def __init__(self, cameras: List[Camera], exposures: List[torch.Tensor] = []):
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.cameras = cameras
|
|
22
|
+
self.quaternions = nn.Parameter(torch.stack([camera.quaternion for camera in cameras]))
|
|
23
|
+
self.Ts = nn.Parameter(torch.stack([camera.T for camera in cameras]))
|
|
24
|
+
self.exposures = nn.Parameter(torch.stack([torch.eye(3, 4, device=camera.T.device) for camera in cameras]))
|
|
25
|
+
if len(exposures) > 0:
|
|
26
|
+
assert len(exposures) == len(cameras), "Number of exposures must match number of cameras"
|
|
27
|
+
with torch.no_grad():
|
|
28
|
+
for idx, exposure in enumerate(exposures):
|
|
29
|
+
self.exposures[idx, ...] = exposure.to(self.exposures.device)
|
|
30
|
+
|
|
31
|
+
def __len__(self):
|
|
32
|
+
return len(self.cameras)
|
|
33
|
+
|
|
34
|
+
def __getitem__(self, idx) -> Camera:
|
|
35
|
+
return Camera(**{
|
|
36
|
+
**self.cameras[idx]._asdict(),
|
|
37
|
+
'quaternion': self.quaternions[idx, ...],
|
|
38
|
+
'T': self.Ts[idx, ...],
|
|
39
|
+
'postprocess': exposure_postprocess,
|
|
40
|
+
'custom_data': {
|
|
41
|
+
**self.cameras[idx].custom_data,
|
|
42
|
+
'exposures': self.exposures[idx, ...]
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
def to(self, device):
|
|
47
|
+
self.quaternions.to(device)
|
|
48
|
+
self.Ts.to(device)
|
|
49
|
+
self.exposures.to(device)
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def save_cameras(self, path):
|
|
53
|
+
cameras = []
|
|
54
|
+
for idx, camera in enumerate(self):
|
|
55
|
+
cameras.append({
|
|
56
|
+
**camera2dict(Camera(**{
|
|
57
|
+
**camera._asdict(),
|
|
58
|
+
'R': quaternion_to_matrix(self.quaternions[idx, ...]),
|
|
59
|
+
'T': self.Ts[idx, ...],
|
|
60
|
+
}), idx),
|
|
61
|
+
"exposure": self.exposures[idx, ...].detach().tolist(),
|
|
62
|
+
})
|
|
63
|
+
with open(path, 'w') as f:
|
|
64
|
+
json.dump(cameras, f, indent=2)
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def from_json(cls, path):
|
|
68
|
+
cameras = JSONCameraDataset(path)
|
|
69
|
+
exposures = [(torch.tensor(camera['exposure']) if 'exposure' in camera else torch.eye(3, 4)) for camera in cameras.json_cameras]
|
|
70
|
+
return cls(cameras, exposures)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from .dataset import ColmapCameraDataset, ColmapTrainableCameraDataset
|
|
2
|
+
from .params_init import colmap_init
|
|
3
|
+
from .dataset import ColmapCamera, read_colmap_cameras
|
|
4
|
+
from .params_init import read_colmap_points3D, read_points3D_binary, read_points3D_text
|
|
5
|
+
from .dataset import read_cameras_binary, read_cameras_text, parse_colmap_camera
|