Skip to content

Commit 0ced5ce

Browse files
Add a point cloud option for Record3D (#3556)
* Add a point cloud option for Record3D * Change parameter names and add voxel size parameter * fix record3d point cloud docs formatting
1 parent 14f1d4e commit 0ced5ce

File tree

6 files changed

+72
-9
lines changed

6 files changed

+72
-9
lines changed

docs/quickstart/custom_dataset.md

+30-5
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,31 @@ ns-process-data record3d --data {data directory} --output-dir {output directory}
268268
ns-train nerfacto --data {output directory}
269269
```
270270

271+
### Adding a Point Cloud
272+
273+
Adding a point cloud is useful for avoiding random initialization when training gaussian splats. To add a point cloud using Record3D follow these steps:
274+
275+
1. Export a Zipped sequence of PLY point clouds from Record3D.
276+
277+
<img src="imgs/record_3d_video_example.png" width=150>
278+
<img src="imgs/record_3d_export_button.png" width=150>
279+
<img src="imgs/record_3d_ply_selection.png" width=150>
280+
281+
282+
2. Move the exported zip file to your computer from your iPhone.
283+
284+
285+
3. Unzip the file and move all extracted `.ply` files to a directory.
286+
287+
288+
4. Convert the data to nerfstudio format with the `--ply` flag and the directory from step 3.
289+
290+
```bash
291+
ns-process-data record3d --data {data directory} --ply {ply directory} --output-dir {output directory}
292+
```
293+
294+
Additionally you can specify `--voxel-size {float}` which determines the level of sparsity when downsampling from the dense point clouds generated by Record3D to the sparse point cloud used in Nerfstudio. The default value is 0.8, lower is less sparse, higher is more sparse.
295+
271296
(spectacularai)=
272297

273298
## Spectacular AI
@@ -292,13 +317,13 @@ pip install spectacularAI[full]
292317
2. Install FFmpeg. Linux: `apt install ffmpeg` (or similar, if using another package manager). Windows: [see here](https://www.editframe.com/guides/how-to-install-and-start-using-ffmpeg-in-under-10-minutes). FFmpeg must be in your `PATH` so that `ffmpeg` works on the command line.
293318

294319
3. Data capture. See [here for specific instructions for each supported device](https://github.com/SpectacularAI/sdk-examples/tree/main/python/mapping#recording-data).
295-
320+
296321
4. Process and export. Once you have recorded a dataset in Spectacular AI format and have it stored in `{data directory}` it can be converted into a Nerfstudio supported format with:
297322

298323
```bash
299324
sai-cli process {data directory} --preview3d --key_frame_distance=0.05 {output directory}
300325
```
301-
The optional `--preview3d` flag shows a 3D preview of the point cloud and estimated trajectory live while VISLAM is running. The `--key_frame_distance` argument can be tuned based on the recorded scene size: 0.05 (5cm) is good for small scans and 0.15 for room-sized scans. If the processing gets slow, you can also try adding a --fast flag to `sai-cli process` to trade off quality for speed.
326+
The optional `--preview3d` flag shows a 3D preview of the point cloud and estimated trajectory live while VISLAM is running. The `--key_frame_distance` argument can be tuned based on the recorded scene size: 0.05 (5cm) is good for small scans and 0.15 for room-sized scans. If the processing gets slow, you can also try adding a --fast flag to `sai-cli process` to trade off quality for speed.
302327

303328
5. Train. No separate `ns-process-data` step is needed. The data in `{output directory}` can now be trained with Nerfstudio:
304329

@@ -453,7 +478,7 @@ If cropping only needs to be done from the bottom, you can use the `--crop-botto
453478

454479
## 🥽 Render VR Video
455480

456-
Stereo equirectangular rendering for VR video is supported as VR180 and omni-directional stereo (360 VR) Nerfstudio camera types for video and image rendering.
481+
Stereo equirectangular rendering for VR video is supported as VR180 and omni-directional stereo (360 VR) Nerfstudio camera types for video and image rendering.
457482

458483
### Omni-directional Stereo (360 VR)
459484
This outputs two equirectangular renders vertically stacked, one for each eye. Omni-directional stereo (ODS) is a method to render VR 3D 360 videos, and may introduce slight depth distortions for close objects. For additional information on how ODS works, refer to this [writeup](https://developers.google.com/vr/jump/rendering-ods-content.pdf).
@@ -464,7 +489,7 @@ This outputs two equirectangular renders vertically stacked, one for each eye. O
464489

465490

466491
### VR180
467-
This outputs two 180 deg equirectangular renders horizontally stacked, one for each eye. VR180 is a video format for VR 3D 180 videos. Unlike in omnidirectional stereo, VR180 content only displays front facing content.
492+
This outputs two 180 deg equirectangular renders horizontally stacked, one for each eye. VR180 is a video format for VR 3D 180 videos. Unlike in omnidirectional stereo, VR180 content only displays front facing content.
468493

469494
<center>
470495
<img img width="375" src="https://github-production-user-asset-6210df.s3.amazonaws.com/9502341/255379444-b90f5b3c-5021-4659-8732-17725669914e.jpeg">
@@ -524,4 +549,4 @@ If the depth of the scene is unviewable and looks too close or expanded when vie
524549
- The IPD can be modified in the `cameras.py` script as the variable `vr_ipd` (default is 64 mm).
525550
- Compositing with Blender Objects and VR180 or ODS Renders
526551
- Configure the Blender camera as panoramic and equirectangular. For the VR180 Blender camera, set the panoramic longitude min and max to -90 and 90.
527-
- Change the Stereoscopy mode to "Parallel" set the Interocular Distance to 0.064 m.
552+
- Change the Stereoscopy mode to "Parallel" set the Interocular Distance to 0.064 m.
85.8 KB
Loading
Loading
Loading

nerfstudio/process_data/record3d_utils.py

+29-3
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,32 @@
1616

1717
import json
1818
from pathlib import Path
19-
from typing import List
19+
from typing import List, Optional
2020

2121
import numpy as np
22+
import open3d as o3d
2223
from scipy.spatial.transform import Rotation
2324

2425
from nerfstudio.process_data.process_data_utils import CAMERA_MODELS
2526
from nerfstudio.utils import io
2627

2728

28-
def record3d_to_json(images_paths: List[Path], metadata_path: Path, output_dir: Path, indices: np.ndarray) -> int:
29+
def record3d_to_json(
30+
images_paths: List[Path],
31+
metadata_path: Path,
32+
output_dir: Path,
33+
indices: np.ndarray,
34+
ply_dirname: Optional[Path],
35+
voxel_size: Optional[float],
36+
) -> int:
2937
"""Converts Record3D's metadata and image paths to a JSON file.
3038
3139
Args:
32-
images_paths: list if image paths.
40+
images_paths: list of image paths.
3341
metadata_path: Path to the Record3D metadata JSON file.
3442
output_dir: Path to the output directory.
3543
indices: Indices to sample the metadata_path. Should be the same length as images_paths.
44+
ply_dirname: Path to the directory of exported ply files.
3645
3746
Returns:
3847
The number of registered images.
@@ -87,6 +96,23 @@ def record3d_to_json(images_paths: List[Path], metadata_path: Path, output_dir:
8796

8897
out["frames"] = frames
8998

99+
# If .ply directory exists add the sparse point cloud for gsplat point initialization
100+
if ply_dirname is not None:
101+
assert ply_dirname.exists(), f"Directory not found: {ply_dirname}"
102+
assert ply_dirname.is_dir(), f"Path given is not a directory: {ply_dirname}"
103+
104+
# Create sparce point cloud
105+
pcd = o3d.geometry.PointCloud()
106+
for ply_filename in ply_dirname.iterdir():
107+
temp_pcd = o3d.io.read_point_cloud(str(ply_filename))
108+
pcd += temp_pcd.voxel_down_sample(voxel_size=voxel_size)
109+
110+
# Save point cloud
111+
points3D = np.asarray(pcd.points)
112+
pcd.points = o3d.utility.Vector3dVector(points3D)
113+
o3d.io.write_point_cloud(str(output_dir / "sparse_pc.ply"), pcd, write_ascii=True)
114+
out["ply_file_path"] = "sparse_pc.ply"
115+
90116
with open(output_dir / "transforms.json", "w", encoding="utf-8") as f:
91117
json.dump(out, f, indent=4)
92118

nerfstudio/scripts/process_data.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ class ProcessRecord3D(BaseConverterToNerfstudioDataset):
4949
2. Converts Record3D poses into the nerfstudio format.
5050
"""
5151

52+
ply_dir: Optional[Path] = None
53+
"""Path to the Record3D directory of point export ply files."""
54+
voxel_size: Optional[float] = 0.8
55+
"""Voxel size for down sampling dense point cloud"""
56+
5257
num_downscales: int = 3
5358
"""Number of times to downscale the images. Downscales by 2 each time. For example a value of 3
5459
will downscale the images by 2x, 4x, and 8x."""
@@ -101,7 +106,14 @@ def main(self) -> None:
101106
)
102107

103108
metadata_path = self.data / "metadata.json"
104-
record3d_utils.record3d_to_json(copied_image_paths, metadata_path, self.output_dir, indices=idx)
109+
record3d_utils.record3d_to_json(
110+
copied_image_paths,
111+
metadata_path,
112+
self.output_dir,
113+
indices=idx,
114+
ply_dirname=self.ply_dir,
115+
voxel_size=self.voxel_size,
116+
)
105117
CONSOLE.rule("[bold green]:tada: :tada: :tada: All DONE :tada: :tada: :tada:")
106118

107119
for summary in summary_log:

0 commit comments

Comments
 (0)