Skip to content

Commit 200d012

Browse files
committed
Add a point cloud option for Record3D
1 parent 555d554 commit 200d012

File tree

6 files changed

+65
-9
lines changed

6 files changed

+65
-9
lines changed

docs/quickstart/custom_dataset.md

Lines changed: 30 additions & 5 deletions
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
274+
when training gaussian splats. To add a point cloud using Record3D
275+
follow these steps:
276+
277+
1. Export a Zipped sequence of PLY point clouds from Record3D.
278+
279+
<img src="imgs/record_3d_video_example.png" width=150>
280+
<img src="imgs/record_3d_export_button.png" width=150>
281+
<img src="imgs/record_3d_ply_selection.png" width=150>
282+
283+
284+
2. Move the exported zip file to your computer from your iPhone.
285+
286+
287+
3. Unzip the file and move all extracted `.ply` files to a directory.
288+
289+
290+
4. Convert the data to nerfstudio format with the `--ply` flag and the directory from step 3.
291+
292+
```bash
293+
ns-process-data record3d --data {data directory} --ply {ply directory} --output-dir {output directory}
294+
```
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
363 KB
Loading
151 KB
Loading

nerfstudio/process_data/record3d_utils.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,31 @@
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] = None,
35+
) -> int:
2936
"""Converts Record3D's metadata and image paths to a JSON file.
3037
3138
Args:
32-
images_paths: list if image paths.
39+
images_paths: list of image paths.
3340
metadata_path: Path to the Record3D metadata JSON file.
3441
output_dir: Path to the output directory.
3542
indices: Indices to sample the metadata_path. Should be the same length as images_paths.
43+
ply_dirname: Path to the directory of exported ply files.
3644
3745
Returns:
3846
The number of registered images.
@@ -87,6 +95,23 @@ def record3d_to_json(images_paths: List[Path], metadata_path: Path, output_dir:
8795

8896
out["frames"] = frames
8997

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

nerfstudio/scripts/process_data.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ class ProcessRecord3D(BaseConverterToNerfstudioDataset):
4949
2. Converts Record3D poses into the nerfstudio format.
5050
"""
5151

52+
ply: Optional[Path] = None
53+
"""Path to the Record3D directory of point export ply files."""
54+
5255
num_downscales: int = 3
5356
"""Number of times to downscale the images. Downscales by 2 each time. For example a value of 3
5457
will downscale the images by 2x, 4x, and 8x."""
@@ -101,7 +104,10 @@ def main(self) -> None:
101104
)
102105

103106
metadata_path = self.data / "metadata.json"
104-
record3d_utils.record3d_to_json(copied_image_paths, metadata_path, self.output_dir, indices=idx)
107+
ply_path = self.ply if hasattr(self, "ply") else None
108+
record3d_utils.record3d_to_json(
109+
copied_image_paths, metadata_path, self.output_dir, indices=idx, ply_dirname=ply_path
110+
)
105111
CONSOLE.rule("[bold green]:tada: :tada: :tada: All DONE :tada: :tada: :tada:")
106112

107113
for summary in summary_log:

0 commit comments

Comments
 (0)