How to Customize the Camera Path for the Offline Rendering Pipeline in LingBot-Map
The offline rendering pipeline in LingBot-Map constructs camera trajectories from reconstruction poses stored in self.all_steps, enabling full customization through UI controls in point_cloud_viewer.py or direct programmatic manipulation before GLB export.
The LingBot-Map repository (Robbyant/lingbot-map) provides a comprehensive visualization toolkit for 3D reconstructions. Customizing the camera path for the offline rendering pipeline allows precise control over which poses are rendered, their spatial density, and the trajectory's visual representation.
Understanding the Camera Trajectory Architecture
The offline rendering pipeline centers on the camera trajectory stored in the PointCloudViewer class. This trajectory is maintained as a Python list called self.all_steps in lingbot_map/vis/point_cloud_viewer.py, which contains the ordered sequence of camera extrinsics (cam2world_3x4 matrices) derived from the reconstruction.
When you initiate an export, the viewer iterates over self.all_steps and passes each pose to integrate_camera_into_scene in lingbot_map/vis/glb_export.py (approximately line 770). This function constructs the visual representation—including camera frustums and the connecting trajectory tube—based on the current state of the camera list.
Method 1: Control Camera Visibility via UI Controls
The simplest way to customize the camera path is through the interactive controls defined in point_cloud_viewer.py. Two key UI elements govern which frames appear in the final render:
self.show_camera_checkbox(defined around line 414): Toggles the visibility of camera frustums in the scene.self.camera_downsample_slider(defined around line 421): Controls the sampling rate of the trajectory, allowing you to render every N-th frame instead of the full sequence.
When you adjust these controls, the viewer calls _regenerate_cameras() (approximately line 606) to rebuild the visualization according to the new parameters. This method filters self.all_steps based on the downsample value and updates the scene geometry.
Method 2: Programmatically Modify the Camera Path
For precise control over the camera path, directly manipulate the self.all_steps list before triggering the export. This approach allows you to reverse the trajectory, insert interpolated poses, or prune specific frames.
The following workflow demonstrates how to load a reconstruction, customize the trajectory, and export the result:
from lingbot_map.vis.point_cloud_viewer import PointCloudViewer
# Initialize the viewer with reconstruction data
viewer = PointCloudViewer(
point_clouds=my_point_clouds,
camera_poses=my_cam_poses,
show_camera=True,
camera_downsample=2,
)
# Customize the trajectory: keep every 5th frame
custom_traj = viewer.all_steps[::5]
viewer.all_steps = custom_traj
# Export the customized scene
viewer.export_glb("custom_scene.glb")
By modifying viewer.all_steps directly, you override the default sequence and define exactly which poses the offline renderer processes.
Adjusting Visual Parameters for Camera Frustums
Beyond controlling which cameras appear, you can customize their visual appearance through additional sliders in point_cloud_viewer.py:
- Scale: Controlled by the camera size slider (
self.camera_downsample_slider), which adjusts the frustum dimensions passed to the export function. - Thickness: Controlled by
self.camera_thickness_slider(defined around line 462), which determines the edge weight of the trajectory tube.
These parameters are passed to integrate_camera_into_scene during the export process, determining the size of the camera frustums and the thickness of the trajectory tube mesh. The CameraState helper type in lingbot_map/vis/utils.py (line ~22) supports these rendering operations.
The Export Pipeline and GLB Generation
When you call export_glb(), the viewer executes the following sequence:
- Iterates over the current
self.all_stepslist - Extracts the
cam2world_3x4extrinsic matrix for each entry - Invokes
integrate_camera_into_scene(fromlingbot_map/vis/glb_export.py) at approximately line 739 - Generates a GLB file containing the camera frustums and trajectory tube
For headless rendering workflows, refer to benchmark/viewer.py (lines ~1266-1270), which demonstrates how to request renders from a viser client without the full GUI.
Summary
- The camera trajectory is stored in
self.all_stepswithinlingbot_map/vis/point_cloud_viewer.py - Use
self.show_camera_checkboxandself.camera_downsample_slider(lines ~414 and ~421) to control visibility and density via the UI - Call
_regenerate_cameras()(line ~606) to apply UI changes to the visualization - Modify
self.all_stepsdirectly to reverse, prune, or reorder the camera path programmatically - Adjust
self.camera_thickness_slider(line ~462) to change the visual weight of trajectory lines - The
integrate_camera_into_scenefunction inglb_export.py(line ~770) constructs the final geometry for export
Frequently Asked Questions
Where is the camera trajectory data stored in LingBot-Map?
The camera trajectory is stored in the self.all_steps list inside the PointCloudViewer class in lingbot_map/vis/point_cloud_viewer.py. This list contains the ordered camera extrinsics (cam2world_3x4 matrices) that define the path through the reconstruction.
How do I reduce the number of camera frames in the rendered output?
Adjust the self.camera_downsample_slider value (defined around line 421) to sample every N-th frame, or programmatically slice the self.all_steps list (e.g., viewer.all_steps[::5]) to keep only specific poses before calling export_glb().
Can I export a custom camera path without using the GUI?
Yes. Instantiate PointCloudViewer programmatically, modify the self.all_steps list directly to define your custom trajectory, and invoke viewer.export_glb("filename.glb"). This headless approach requires no interactive UI components.
What parameters control the visual size of camera frustums?
The visual scale is controlled by the camera size slider (self.camera_downsample_slider), while the edge thickness is governed by self.camera_thickness_slider (line ~462). Both values are passed to integrate_camera_into_scene during GLB construction to determine the final mesh geometry.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →