Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render Omni-directional stereo video (360 3D VR) #1986

Merged
merged 9 commits into from
May 27, 2023

Conversation

cvachha
Copy link
Contributor

@cvachha cvachha commented May 24, 2023

Adding the omni-directional stereo camera model to render 3D 360 video and images for VR. This includes the camera models for the left and right eyes and additions to the render script to stack the left and right renders for the final render.

I have also updated the documentation on the custom_dataset page to have information on ODS but let me know if there is a better place to put it.

Adding the omni-directional stereo camera model to render 3D 360 video and images for VR. This includes the camera models for the left and right eyes and additions to the render script to stack the left and right renders for the final render.
I have also updated the documentation on the custom_dataset page to have information on ODS but let me know if there is a better place to put it.
@cvachha cvachha changed the title Render Omni-directional stereo (360 3D VR) Render Omni-directional stereo video (360 3D VR) May 24, 2023
ods_theta = -torch.pi * ((x - cx) / fx)[0]

# local axes of ODS camera
ods_x_axis = torch.tensor([1, 0, 0]).to("cuda")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably won't make much a difference here since the tensor is small, but it is better to construct the tensor on the target device, rather than move it to the device. Also avoid specifying cuda since users may be using cpu.

somethings like:
ods_x_axis = torch.tensor([1, 0, 0], device=c2w.device)

# assign final camera origins
c2w[..., :3, 3] = ods_origins_circle

if CameraType.OMNIDIRECTIONALSTEREO_R.value in cam_types:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is very similar to the left eye. Can you consolidate in a helper function.

Comment on lines 767 to 774
for value in cam_types:
if value not in [CameraType.PERSPECTIVE.value, CameraType.FISHEYE.value, CameraType.EQUIRECTANGULAR.value]:
if value not in [
CameraType.PERSPECTIVE.value,
CameraType.FISHEYE.value,
CameraType.EQUIRECTANGULAR.value,
CameraType.OMNIDIRECTIONALSTEREO_L.value,
CameraType.OMNIDIRECTIONALSTEREO_R.value,
]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you refactor this with the above code into,

for cam_type in cam_types:
    if cam_type == CameraType.PERSPECTIVE.value:
        ...
        continue
    if cam_type == CameraType.FISHEYE.value:
        ...
        continue
    ...
    raise ValueError(f"Camera type {cam_type} not supported.")

@@ -342,6 +344,16 @@ def main(self) -> None:
crop_data = get_crop_from_json(camera_path)
camera_path = get_path_from_json(camera_path)

if camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value:
# temp folder for writing left and right view renders
temp_folder_path = os.path.splitext(str(self.output_path))[0] + "_temp"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid os.path, use pathlib instead
temp_folder_path = self.output_path.parent / (self.output_path.stem + "_temp" )

# temp folder for writing left and right view renders
temp_folder_path = os.path.splitext(str(self.output_path))[0] + "_temp"
Path(temp_folder_path).mkdir(parents=True, exist_ok=True)
left_eye_path = Path(str(temp_folder_path) + "//ods_render_Left.mp4")
Copy link
Contributor

@tancik tancik May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be cleaner since temp_folder_path is a Path. One reason what this is preferred is that file paths (ie //) may be different between systems (windows vs linux).
left_eye_path = temp_folder_path / "ods_render_Left.mp4"

# declare paths for left and right renders

left_eye_path = self.output_path
right_eye_path = Path(str(left_eye_path.parent) + "//ods_render_Right.mp4")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left_eye_path = left_eye_path.parent / "ods_render_Left.mp4"

self.output_path = Path(str(left_eye_path.parent)[:-5])
self.output_path.mkdir(parents=True, exist_ok=True)
if self.image_format == "png":
ffmpeg_ods_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path).replace(".mp4","")+"//*.png"}" -pattern_type glob -i "{str(right_eye_path).replace(".mp4","")+"//*.png"}" -filter_complex vstack "{str(self.output_path)+"//%05d.png"}"'
Copy link
Contributor

@tancik tancik May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use pathlib here and below for renaming
{str(left_eye_path.with_suffix(".png"))}

cvachha and others added 4 commits May 24, 2023 21:40
Updates based on suggested code changes including adding a helper function for ODS and improving path creation.
I also added a part where if a user tries to render a video with an output path that doesn't have an extension it will automatically add .mp4 to the end. Otherwise this usually results in an error when there is no file extension when rendering a video.
Updated python formatting for render and camera_paths script
CameraType.OMNIDIRECTIONALSTEREO_R.value,
]:
raise ValueError(f"Camera type {value} not supported.")
for cam_type in cam_types:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I should have been more specific. The logic above should exist in each if condition, ie:

for cam_type in cam_types:
    if cam_type == CameraType.PERSPECTIVE.value:
        mask = (self.camera_type[true_indices] == CameraType.PERSPECTIVE.value).squeeze(-1)  # (num_rays)
        mask = torch.stack([mask, mask, mask], dim=0)
        directions_stack[..., 0][mask] = torch.masked_select(coord_stack[..., 0], mask).float()
        directions_stack[..., 1][mask] = torch.masked_select(coord_stack[..., 1], mask).float()
        directions_stack[..., 2][mask] = -1.0
    elif cam_type == CameraType.EQUIRECTANGULAR.value:
        # Fill in
    # Add other cases
    else:
        raise ValueError(f"Camera type {cam_type} not supported.")

directions_stack[..., 2][mask] = torch.masked_select(-torch.cos(theta) * torch.sin(phi), mask).float()

if CameraType.OMNIDIRECTIONALSTEREO_L.value in cam_types:
def omni_directional_stereo_camera(eye):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add _ to the beginning since we don't expect it to be used outside this file, add types, and add docstring.

ie,

def _compute_rays_for_omnidirectional_stereo(eye: Literal["left", "right"]) -> Tuple[Float[Tensor "(fill in shape)", Float[Tensor "(fill in shape)"]:
    """ Compute the rays for an omnidirectional stereo camera

    Args:
        eye: Which eye to compute rays for.

    Returns:
        A tuple containing the origins and the directions of the rays.
    """

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like you are using self in this function, you could move it outside the class.

Copy link
Contributor

@tancik tancik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cvachha cvachha merged commit 6993f75 into main May 27, 2023
4 checks passed
@cvachha cvachha deleted the origin/cyrus/omni-directional-stereo-video branch May 27, 2023 01:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants