[3D CV ์ฐ๊ตฌ] 3DGS 3D gaussians initialization
3D gaussian์ x,y,z์ scale์ ์ด๊ธฐํ ํ ๋, K-Nearest Neighbor (knn) ์๊ณ ๋ฆฌ์ฆ ์ฌ์ฉ
- 3๊ฐ์ nearest neighbor points๋ฅผ ์ฌ์ฉํ์ฌ ํ๊ท ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ๊ณ , ๊ทธ ๊ฐ์ผ๋ก 3D gaussian์ scale์ isotropic์ผ๋ก ์ด๊ธฐํ ํฉ๋๋ค.
- knn์
simple_knn
๋ผ์ด๋ธ๋ฌ๋ฆฌ์์spatial.cu
์์SimpleKNN::knn
๋ฅผ ์ฌ์ฉํ์ฌdistCUDA2
๋ก ๊ตฌํ๋์ด ์๋ ๊ฒ์ ์ฌ์ฉํฉ๋๋ค. ```cuda #include โspatial.hโ #include โsimple_knn.hโ
torch::Tensor distCUDA2(const torch::Tensor& points) { const int P = points.size(0);
auto float_opts = points.options().dtype(torch::kFloat32); torch::Tensor means = torch::full({P}, 0.0, float_opts);
SimpleKNN::knn(P, (float3*)points.contiguous().data
return means; }
- `SimpleKNN::knn`์ `simple_knn.cu`์ ์ ์๋์ด ์์ผ๋ฉฐ, ์ต๊ทผ์ `K`๊ฐ์ ์ด์์ ๊ตฌํ ๋ `updateKBest<K>` ํธ์ถ์ ํตํด ์ด๋ฃจ์ด์ง๋๋ค.
- ์ด๋ `updateKBest<3>`์ ํธ์ถํ๋๋ก ํ๋์ฝ๋ฉ๋์ด ์๊ณ ์ด ๋ถ๋ถ์์ **3๊ฐ์ ์ต๊ทผ์ ์ด์์ ์ฌ์ฉ**ํ๊ณ ์์์ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
- ๊ฒฐ๋ก ์ ์ผ๋ก ์๋ ์ฝ๋์์ `dist2`๋ `distCUDA2`๋ก point๋ง๋ค 3๊ฐ์ nearest neighbor point์ ํ๊ท ๊ฑฐ๋ฅผ ์๋ฏธํฉ๋๋ค.
- `dist2`๋ก 3d gaussian์ scale์ isotropicํ๊ฒ initializeํ๊ณ ์์ต๋๋ค.
![image](https://github.com/sandokim/sandokim.github.io/assets/74639652/031fa214-d612-487f-956c-bf2923c6695b)
```python
# 3dgs/scene/gaussian_model.py
from simple_knn._C import distCUDA2
class GaussianModel:
...
def create_from_pcd(self, pcd : BasicPointCloud, spatial_lr_scale : float):
self.spatial_lr_scale = spatial_lr_scale
fused_point_cloud = torch.tensor(np.asarray(pcd.points)).float().cuda()
fused_color = RGB2SH(torch.tensor(np.asarray(pcd.colors)).float().cuda())
features = torch.zeros((fused_color.shape[0], 3, (self.max_sh_degree + 1) ** 2)).float().cuda()
features[:, :3, 0 ] = fused_color
features[:, 3:, 1:] = 0.0
print("Number of points at initialisation : ", fused_point_cloud.shape[0])
dist2 = torch.clamp_min(distCUDA2(torch.from_numpy(np.asarray(pcd.points)).float().cuda()), 0.0000001)
scales = torch.log(torch.sqrt(dist2))[...,None].repeat(1, 3)
rots = torch.zeros((fused_point_cloud.shape[0], 4), device="cuda")
rots[:, 0] = 1
opacities = inverse_sigmoid(0.1 * torch.ones((fused_point_cloud.shape[0], 1), dtype=torch.float, device="cuda"))
self._xyz = nn.Parameter(fused_point_cloud.requires_grad_(True))
self._features_dc = nn.Parameter(features[:,:,0:1].transpose(1, 2).contiguous().requires_grad_(True))
self._features_rest = nn.Parameter(features[:,:,1:].transpose(1, 2).contiguous().requires_grad_(True))
self._scaling = nn.Parameter(scales.requires_grad_(True))
self._rotation = nn.Parameter(rots.requires_grad_(True))
self._opacity = nn.Parameter(opacities.requires_grad_(True))
self.max_radii2D = torch.zeros((self.get_xyz.shape[0]), device="cuda")
point cloud๋ก๋ถํฐ n_points ๊ฐ์๋งํผ RGB channel ๊ฐ๊ฐ์ ๋ํ sh์ ๊ณ์๋ฅผ ์ ์ํ์ฌ ์ด๊ธฐํ ํฉ๋๋ค.
- fused_color๋ ์ ๊ตฌ๋ฆ์ ์์ ์ ๋ณด๋ฅผ Spherical Harmonics(SH)๋ก ๋ณํํ ๊ฒ์ ๋๋ค. ์ด ๋ณํ์ ์ผ๋ฐ์ ์ผ๋ก ์ ๊ตฌ๋ฆ์ ์์ ์ ๋ณด๋ฅผ ๋ ์ ํํํ๊ณ , ๋ค์ํ ์กฐ๋ช ์กฐ๊ฑด์์์ ๋ฐ์์ ๋ชจ๋ธ๋งํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
fused_color = RGB2SH(torch.tensor(np.asarray(pcd.colors)).float().cuda())
- ์ฌ๊ธฐ์ RGB2SH๋ RGB ์์ ๊ฐ์ Spherical Harmonics(SH) ๊ณ์๋ก ๋ณํํ๋ ํจ์์
๋๋ค. ๋ณํ ํ, fused_color๋ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋๋ค:
- fused_color.shape: (
num_points
,3
,* (max_sh_degree + 1) ** 2
) # (n_points, RGB, SH ๊ณ์์ ์)- num_points: ์ ์ ๊ฐ์
- 3: RGB ์์ ์ฑ๋ (Red, Green, Blue)
- (max_sh_degree + 1) ** 2: SH ๊ณ์์ ์
- fused_color.shape: (
- SH ๋ณํ์ ์ฃผ๋ก ์ ์ฐจ์์์ ๊ณ ์ฐจ์๋ก ์งํ๋๋ฉฐ, ๊ฐ ์์ ์ฑ๋์ ๋ํด ์ฌ๋ฌ ๊ณ์๋ฅผ ๊ฐ๊ฒ ๋ฉ๋๋ค.
features_dc
, features_rest
features_dc
๋ ๊ฐ ์ ์ ์์ ์ ๋ณด๋ฅผ Spherical Harmonics ๊ณ์๋ก ๋ณํํ์ฌ ์ ์ฅํ ํ ์์ ๋๋ค.features_dc
๋ ์ด๊ธฐํ ์ RGB ์์์ DC(Direct Component) ๊ณ์๋ง์ ํฌํจํ๋ฉฐ, ๋๋จธ์ง SH ๊ณ์๋ค์ features_rest์ ์ ์ฅ๋ฉ๋๋ค.features
๋(fused_colors.shape[0], 3, (self.max_sh_degree + 1) ** 2)
์ธ(n_points, RGB 3 channel, sh ๊ณ์์ ์)
์ shape์ผ๋ก 0์ผ๋ก ์ด๊ธฐํ๋ฉ๋๋ค.features[:, :3, 0 ] = fused_color
๋ sh 0๋ฒ์งธ ๊ณ์์ ๋ํ ๊ฐ์RGB2SH
๋ก ๋ฃ์ด์ค๋๋ค.features[:, 3:, 1:] = 0.0
๋ ์ฌ์ค์ RGB 3 channel์ ๋ํด 3์ฐจ์ ์ด์์ผ๋ก ์ธ๋ฑ์ฑ์ด ๋์ด๊ฐ์ผ๋ฏ๋ก ์๋ฌดํจ๊ณผ๊ฐ ์์ต๋๋ค. ๋ง์ง๋ง์1:
์ ์ธ๋ฑ์ฑ์ ๋ช ์ํ์ฌ ๋จ์ํ sh 1๋ฒ์งธ~44๋ฒ์งธ ๊ณ์์ ๋ํ ์ด๊ธฐ๊ฐ์ด 0.0์ด๋ผ๋ ๊ฒ์features
๋ฅผzeros
๋ก ์ด๊ธฐํํ์์๋, ํ๋ฒ ๋ ๋ช ์ํ๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค.
# 3dgs/scene/gaussian_model.py
...
def create_from_pcd(self, pcd : BasicPointCloud, spatial_lr_scale : float):
self.spatial_lr_scale = spatial_lr_scale
fused_point_cloud = torch.tensor(np.asarray(pcd.points)).float().cuda()
fused_color = RGB2SH(torch.tensor(np.asarray(pcd.colors)).float().cuda())
features = torch.zeros((fused_color.shape[0], 3, (self.max_sh_degree + 1) ** 2)).float().cuda()
features[:, :3, 0 ] = fused_color # (n_points, RGB 3 channel, sh 0๋ฒ์งธ ๊ณ์)
features[:, 3:, 1:] = 0.0 # (n_points, RGB 3 channel์ ์ธ๋ฑ์ฑ ๋ฒ์๋ฅผ ๋์ด๊ฐ, sh 1๋ฒ์งธ~44๋ฒ์งธ ๊ณ์)
...
features_dc
์์ RGB 3 channel์ ๋ํ sh 0๋ฒ์งธ ๊ณ์
๋ f_dc_0
, f_dc_1
, f_dc_2
์
๋๋ค.
load_ply
๋ฅผ ๋ณด๋ฉด sh 0๋ฒ์งธ ๊ณ์์ ๋ํfeatures_dc
๋ฅผ RGB 3 channel์ ๋ํด ์ด๊ธฐํํ ๋,(n_points, RGB 3 channel, 0๋ฒ์งธ sh ๊ณ์์ ์)
๋ฅผ(n_points, 3, 1)
๋ก ์ด๊ธฐํ ํ๊ณ ์์ต๋๋ค.- ๊ทธ๋ฆฌ๊ณ
RGB 3 channel
์0, 1, 2
๋ก ์ธ๋ฑ์ฑํ์ฌ sh 0๋ฒ์งธ ๊ณ์์ ๋ํ๊ฐ์ผ๋ก ๊ฐ๊ฐ"f_dc_0"
,"f_dc_1"
,"f_dc_2"
๋ก ๋ฃ์ด์ค๋๋ค. - ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
"f_dc_0"
๋R์ ํด๋นํ๋ sh 0๋ฒ์งธ ๊ณ์ ๊ฐ
"f_dc_1"
๋G์ ํด๋นํ๋ sh 0๋ฒ์งธ ๊ณ์ ๊ฐ
"f_dc_2"
๋B์ ํด๋นํ๋ sh 0๋ฒ์งธ ๊ณ์ ๊ฐ
```python
3dgs/scene/gaussian_model.py
โฆ
def load_ply(self, path):
plydata = PlyData.read(path)
xyz = np.stack((np.asarray(plydata.elements[0]["x"]),
np.asarray(plydata.elements[0]["y"]),
np.asarray(plydata.elements[0]["z"])), axis=1)
opacities = np.asarray(plydata.elements[0]["opacity"])[..., np.newaxis]
features_dc = np.zeros((xyz.shape[0], 3, 1)) # (n_points, RGB 3 channel, (0 + 1) ** 2) = (n_points, 3, 1)
features_dc[:, 0, 0] = np.asarray(plydata.elements[0]["f_dc_0"]) # (n_points, R, R์ ๋ํ sh 0๋ฒ์ฉจ ๊ณ์)
features_dc[:, 1, 0] = np.asarray(plydata.elements[0]["f_dc_1"]) # (n_points, G, G์ ๋ํ sh 0๋ฒ์งธ ๊ณ์)
features_dc[:, 2, 0] = np.asarray(plydata.elements[0]["f_dc_2"]) # (n_points, B, B์ ๋ํ sh 0๋ฒ์งธ ๊ณ์) ...
Leave a comment