aitviewer Documentation

repository·main·Indexed 20 days ago

https://github.com/eth-ait/aitviewer

A toolset for viewing and rendering sequences of 3D data, specifically optimized for parametric human models such as SMPL, MANO, and FLAME. It features an interactive GUI based on Dear ImGui, a headless server rendering mode for videos and images, and a remote visualization server. Key capabilities include support for SMPL[-H/-X], STAR, and SUPR sequences, high-performance ModernGL-based rendering, and tools for exporting depth maps, segmentation masks, and turntable views.

Tokens
9.1K
Snippets
35
Records
43
Agent score
73%

What's inside aitviewer

  1. Overview of aitviewer features

    main

    aitviewer is a toolset for visualizing and interacting with sequences of 3D data. Key capabilities include:

    • Parametric Model Support: Load and display SMPL[-H/-X], MANO, FLAME, STAR, and SUPR sequences.
    • Rendering Modes:
      • Interactive Viewer: Built-in extensible GUI based on Dear ImGui.
      • Headless Mode: For server-side rendering of videos and images.
      • Remote Mode: For non-blocking integration of visualization code.
    • Advanced Visualization:
      • Render 3D data on top of images using weak-perspective or OpenCV camera models.
      • Animatable camera paths.
      • Prebuilt renderable primitives (cylinders, spheres, point clouds, etc.).
    • Editing & Export:
      • Manually edit SMPL sequences and poses.
      • Export screenshots, videos, and turntable views (mp4/gif).
    • Performance: High-performance ModernGL-based rendering pipeline (100fps+ on most laptops).
  2. Install aitviewer

    main

    You can install aitviewer using pip or uv.

    Note: This installation does not automatically include the GPU version of PyTorch. If your environment does not already have a GPU-enabled PyTorch installation, you must install it manually.

    Basic Installation

    uv pip install aitviewer

    (Or use pip install aitviewer if you do not use uv).

    PyQt5 Support

    By default, aitviewer installs PyQt6. If your project requires PyQt5, use the following command:

    uv pip install aitviewer[pyqt5]

    Install from Source

    If you need to modify or extend the code, install it in editable mode:

    git clone git@github.com:eth-ait/aitviewer.git
    cd aitviewer
    pip install -e .

    For advanced installation steps or to install SMPL body models, refer to the official documentation.

    uv pip install aitviewer
  3. How remote messaging works in the Viewer

    main

    The Viewer can act as a server to receive commands from remote clients. This is controlled by the C.server_enabled configuration.

    Key Methods for Remote Interaction:

    • get_node_by_remote_uid(remote_uid, client): Retrieves a local Node using a unique ID generated by a remote client.
    • process_message(type, remote_uid, args, kwargs, client): The default handler for incoming messages. This can be overridden to intercept or modify messages before they reach the viewer.
    • send_message(msg, client=None): Sends a pickled Python object to a specific client (tuple of (host, port)) or to all connected clients if client is None.

    When overriding process_message, you can implement custom logic to respond to specific message types sent by your simulation or data generation scripts.

    # Example of overriding message processing
    class MyCustomViewer(Viewer):
        def process_message(self, type, remote_uid, args, kwargs, client):
            if type == MY_CUSTOM_MSG_TYPE:
                print(f"Received custom message: {args}")
            else:
                super().process_message(type, remote_uid, args, kwargs, client)
  4. Use ViewerServer for remote visualization

    main

    The ViewerServer class enables networked visualization by running a WebSocket server in a background thread. It allows remote clients to send messages (via pickle serialization) to control the viewer, such as adding nodes, updating frames, or deleting objects.

    To use it, instantiate ViewerServer with an existing viewer instance and a target port. The server runs in a daemon thread, meaning it will be terminated when the main thread exits. You must periodically call process_messages() in your main loop to handle incoming requests from clients.

    from aitviewer.server import ViewerServer
    from aitviewer.viewer import Viewer
    
    # Initialize the viewer
    viewer = Viewer()
    
    # Start the server on port 8080
    server = ViewerServer(viewer, port=8080)
    
    # In your main application loop:
    try:
        while True:
            # Process any messages received from remote clients
            server.process_messages()
            # ... rest of your viewer loop ...
    finally:
        server.close()
  5. Quickstart: Display an SMPL-X T-pose

    main

    To display an SMPL-X T-pose in the interactive viewer, use the following Python script.

    Requirement: You must have SMPL models installed for this to work.

    from aitviewer.models.smpl import SMPLLayer
    from aitviewer.renderables.smpl import SMPLSequence
    from aitviewer.viewer import Viewer
    
    if __name__ == "__main__":
        v = Viewer()
        smpl_layer = SMPLLayer(model_type="smplx", gender="neutral")
        v.scene.add(SMPLSequence.reference_pose(smpl_layer))
        v.run()
    from aitviewer.models.smpl import SMPLLayer
    from aitviewer.renderables.smpl import SMPLSequence
    from aitviewer.viewer import Viewer
    
    if __name__ == "__main__":
        v = Viewer()
        smpl_layer = SMPLLayer(model_type="smplx", gender="neutral")
        v.scene.add(SMPLSequence.reference_pose(smpl_layer))
        v.run()
  6. Perform mesh mouse intersection with read_fragmap_at_pixel

    main

    To find out which object and which specific triangle a user clicked on, use read_fragmap_at_pixel(x, y). This method uses a specialized fragment picking shader to extract precise geometric data.

    Returns:

    • np.ndarray: The (x, y, z) coordinates in camera space.
    • obj_id: The unique ID of the intersected object.
    • tri_id: The ID of the intersected triangle.
    • instance_id: The instance ID of the object.
    import numpy as np
    
    # x, y are screen coordinates
    pos, obj_id, tri_id, inst_id = renderer.read_fragmap_at_pixel(400, 300)
    print(f"Intersected Object: {obj_id} at position {pos}")
  7. Initialize and run the Viewer

    main

    The Viewer class is the main entrypoint for the visualization interface. It inherits from moderngl_window.WindowConfig and manages the scene, renderer, and GUI (via ImGui).

    Initialization Parameters:

    • title (str): The window title.
    • size (Tuple[int, int]): Window dimensions (width, height). If None, uses values from the configuration file.
    • samples (int): Number of multisample anti-aliasing (MSAA) samples.
    • **kwargs: Additional arguments passed to the window configuration.

    Key Methods:

    • run(*args, log=True): Enters the blocking visualization loop. This initializes the scene and starts the rendering/event loop.
    • reset(): Releases the current scene and initializes a new one.
    • toggle_animation(run: bool): Enables or disables animation playback.
    • set_ortho_grid_viewports(): Sets up a 2x2 grid of viewports showing the scene from each main axis using orthographic projection.
    from aitviewer.viewer import Viewer
    
    viewer = Viewer(title="My Scene", size=(1280, 720))
    # ... add nodes to viewer.scene ...
    viewer.run()
  8. Use Gaussian Splatting shaders

    main

    The following functions provide shaders specifically for Gaussian Splatting rendering:

    • get_gaussian_splat_prepare_program(PREPARE_GROUP_SIZE): A compute shader for the preparation phase. Requires PREPARE_GROUP_SIZE define.
    • get_sort_program(name): A compute shader for sorting. Requires a name which is used to create the define ENTRY_PARALLEL_SORT_{name}.
    • get_gaussian_splat_draw_program(): A shader for the final drawing phase, loaded from gaussian_splatting/draw.glsl.
    from aitviewer.shaders import get_gaussian_splat_prepare_program, get_sort_program
    
    # Prepare program
    prepare_shader = get_gaussian_splat_prepare_program(PREPARE_GROUP_SIZE=128)
    
    # Sort program
    sort_shader = get_sort_program("my_sort_type")
  9. Render segmentation masks with HeadlessRenderer

    main

    You can generate color-coded or ID-based masks using the following methods:

    • get_mask(color_map: Dict[int, Tuple[int, int, int]] = None, id_map: Dict[int, int] = None) -> Image: Returns a color mask as an RGB PIL image. Each object has a uniform color based on its Node UID.
    • save_mask(file_path, color_map=None, id_map=None): Saves the color mask to a file.
    • get_mask_ids(id_map: Dict[int, int] = None) -> np.ndarray: Returns a mask as a NumPy array of shape (height, width) and type np.uint32. Each element is the UID of the node covering that pixel (or zero if empty).

    Mapping Parameters:

    • id_map: A dictionary mapping Node UIDs to specific IDs. This is applied before color mapping or hashing.
    • color_map: A dictionary mapping IDs (after id_map application) to (R, G, B) integer tuples (0-255). If None, colors are computed by hashing the UID.
    # Define a custom mapping for specific objects
    # Map Node UID 123 to ID 1, and Node UID 456 to ID 2
    custom_id_map = {123: 1, 456: 2}
    
    # Map ID 1 to Red and ID 2 to Blue
    custom_color_map = {1: (255, 0, 0), 2: (0, 0, 255)}
    
    # Save the color mask
    renderer.save_mask("mask.png", color_map=custom_color_map, id_map=custom_id_map)
    
    # Get raw IDs as a numpy array
    ids = renderer.get_mask_ids(id_map=custom_id_map)
  10. Center view on a node

    main

    Automatically adjusts the camera to focus on a specific Node.

    Parameters:

    • node: The Node object to focus on.
    • with_animation: If True, the camera moves to the target using a smooth animation. If False, the camera position and target are updated instantly.
    self.center_view_on_node(my_node, with_animation=True)
  11. Load a custom shader program with `load_program`

    main

    Use load_program(name, defines=None) to load a shader program from a specific file path. You can optionally provide a dictionary of defines to enable or disable specific shader features via preprocessor macros.

    This function uses moderngl_window.resources.programs.load under the hood.

    from aitviewer.shaders import load_program
    
    # Load a program by its file path
    program = load_program("my_shader.glsl", defines={"USE_COLOR": 1})
  12. Render depth maps with HeadlessRenderer

    main

    The get_depth() and save_depth() methods allow you to capture the depth buffer.

    • get_depth() -> Image: Returns the depth buffer as a PIL Image in 'F' mode (32-bit float). Depth is stored as the z-coordinate in eye (view) space, representing the distance from the pixel to the camera plane. Values are clipped between the camera's near and far planes.
    • save_depth(file_path): Saves the depth buffer to a file. Note: You must use a file format that supports PIL 'F' mode, such as .tiff.
    # Save depth to a TIFF file
    renderer.save_depth("depth_map.tiff")
    
    # Or get it as a PIL object
    depth_image = renderer.get_depth()