### ScenepicVisualization Example Usage Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Demonstrates how to create, add assets to, and display or save a 3D visualization using ScenepicVisualization. ```python # Create a visualization viz = ScenepicVisualization() # Add meshes (single frame or multi-frame) viz.add_meshes(meshes_list) # Add point clouds with labels (all visible by default) viz.add_point_clouds(point_clouds_list, point_labels=labels) # Add line sets with individual visibility control viz.add_line_sets(start_points, end_points, hide_line_sets=[False, True, False]) # Add coordinate frames (all visible by default) viz.add_coordinate_frames(origins, orientations) # Display or save viz.show() viz.save_to_html("output.html") ``` -------------------------------- ### Install Dependencies Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Installs necessary Python packages for the MHR visualization. This cell should be run first. ```python #@title Install dependencies %%capture !pip3 install torch scenepic trimesh ``` -------------------------------- ### Setup Scene Assets Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Initializes and adds all scene assets (meshes, point clouds, line sets) to the visualization scene. Returns the total number of animation frames generated. ```python def _setup_scene_assets(self) -> int: """Set up all scene assets and return the number of animation frames.""" num_frames = 0 if self.mesh_names: num_frames = self._add_meshes_to_scene() if self.point_cloud_names: num_frames = self._add_point_clouds_to_scene(num_frames) if self.line_set_names: num_frames = self._add_line_sets_to_scene(num_frames) return num_frames ``` -------------------------------- ### ScenePic 3D Visualization Example Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Demonstrates how to use the ScenepicVisualization class to add point clouds and coordinate frames, then display or save the visualization. Requires scenepic, numpy, and IPython. ```python import numpy as np from xrcia.projects.tracked_assets.core.visualization.scenepic_visualization import ScenepicVisualization # Create visualization instance viz = ScenepicVisualization() # Add some sample data points = np.random.rand(100, 3) viz.add_point_clouds([[points]], point_labels=[['Point ' + str(i) for i in range(100)]]) # Add coordinate frame at origin origins = [[[0, 0, 0]]] orientations = [[[np.eye(3)]]] viz.add_coordinate_frames(origins, orientations) # Display in notebook viz.show() # Or save to file viz.save_to_html("visualization.html") ``` -------------------------------- ### Setup Mesh Texture and Vertex Color Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Sets up mesh texture and vertex colors for rendering. Generates default colors if none are provided and validates UV coordinates and texture images when textures are used. ```python def _setup_mesh_texture_and_vertex_color( self, mesh_colors: list[MeshMonoColors | MeshPervertexColors | None] | None = None, mesh_vertex_uvs: list[np.ndarray[tuple[int, int], np.dtype[np.float32]] | None] | None = None, mesh_texture_images: list[ np.ndarray[tuple[int, int, int], np.dtype[np.uint8]] | None ] | None = None, ) -> None: """ Set up mesh texture and vertex colors for rendering. Args: mesh_colors: Optional colors for each mesh. Can be per-mesh uniform colors, per-vertex colors, or None for individual meshes in mixed rendering. mesh_vertex_uvs: Optional UV coordinate arrays for per-vertex texture mapping. Individual elements can be None for non-textured meshes. mesh_texture_images: Optional texture image arrays. Individual elements can be None for non-textured meshes. Raises: ValueError: If validation fails """ # Validate and take mesh vertex colors. Generate mesh vertex colors if not provided. # Mesh vertex colors are not used if texture images are provided. # Generate default colors for all meshes color_indices = np.linspace(0, 0.85, self.num_meshes_per_frame) rainbow_cmap = plt.cm.rainbow_r self.mesh_colors = rainbow_cmap(color_indices)[:, :3] if mesh_colors is not None: # Standard color validation for non-mixed rendering _validate_mesh_colors(mesh_colors, self.num_meshes_per_frame) self.mesh_colors = _normalize_mesh_colors(mesh_colors, self.mesh_colors) # If texture images are provided, validate UV coordinates and texture images. if mesh_texture_images is not None: if ( mesh_vertex_uvs is None ): # When texture images are provided, UV coordinates must also be provided. raise ValueError( "When texture images are provided, Pervertex UV coordinates must also be provided." ) if len(mesh_vertex_uvs) != len( mesh_texture_images ): # UV coordinates and texture images must match. raise ValueError( "The length of UV coordinates and texture images must match." ) ``` -------------------------------- ### Initialize Line Set Visualization Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Initializes a line set visualization with specified start and end points, opacity, and line types. Handles color generation using a rainbow colormap and updates the scene bounding box. Requires numpy and matplotlib. ```python self.start_points, self.end_points, self.num_line_sets_per_frame = ( self._validate_input_pair_are_list_of_lists( start_points, "Start points", end_points, "End points" ) ) self.line_set_opacity = ( line_set_opacity if line_set_opacity is not None else [1.0] * self.num_line_sets_per_frame ) self.line_set_names = ( line_set_names if line_set_names is not None else [f"line_set_{i}" for i in range(self.num_line_sets_per_frame)] ) # Generate evenly spaced values from 0 to 0.8 for rainbow_r colormap, so that 0 -> red, 0.85 -> blue. color_indices = np.linspace(0, 0.85, self.num_line_sets_per_frame) rainbow_cmap = plt.cm.rainbow_r # We keep only the RGB values and discard the alpha channel self.line_set_colors = rainbow_cmap(color_indices)[:, :3] if line_set_colors is not None: # Sample self.num_line_sets_per_frame colors according to the rainbow color map in matplotlib. _validate_mesh_colors(line_set_colors, self.num_line_sets_per_frame) self.line_set_colors = _normalize_mesh_colors( line_set_colors, self.line_set_colors ) # Get the bounding box for the added line sets. max_line_set_corner = -np.inf * np.ones(3) min_line_set_corner = np.inf * np.ones(3) for frame_idx in range(len(self.start_points)): for line_set_idx in range(self.num_line_sets_per_frame): line_starts = self.start_points[frame_idx][line_set_idx] line_ends = self.end_points[frame_idx][line_set_idx] all_points = np.concatenate([line_starts, line_ends], axis=0) max_line_set_corner = np.maximum( max_line_set_corner, np.max(all_points, axis=0) ) min_line_set_corner = np.minimum( min_line_set_corner, np.min(all_points, axis=0) ) # Update the scene bounding box according to the bounding box of all the added line sets. self.scene_bbox[0] = np.maximum(self.scene_bbox[0], max_line_set_corner) self.scene_bbox[1] = np.minimum(self.scene_bbox[1], min_line_set_corner) if not line_type.lower() in ["line", "thickline"]: raise ValueError( f"Invalid line type {line_type}. Must be one of 'line', or 'thickline'." ) self.line_type = line_type.lower() self.line_start_thickness = line_start_thickness ``` -------------------------------- ### Setup Observers for Widget Events Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Sets up event observers for the widgets to handle user interactions like dropdown changes, slider movements, and button clicks. This ensures the interface updates dynamically. ```python def _setup_observers(self): """Setup event observers for widgets""" self.parameter_dropdown.observe(self._on_parameter_change, names="value") self.parameter_slider.observe(self._on_slider_change, names="value") self.reset_current_button.on_click(self._on_reset_current) self.reset_all_button.on_click(self._on_reset_all) ``` -------------------------------- ### Add Line Sets to Scene Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Adds line sets to the scene and validates the number of frames. If num_frames is 0, it defaults to the number of start points. Raises a ValueError if the provided num_frames does not match the number of start points. ```python if num_frames == 0: num_frames = len(self.start_points) else: if num_frames != len(self.start_points): raise ValueError( f"Invalid line sets. Expected a list of line sets, where the length matches the number of mesh frames or other 3D input assets. Got {len(self.start_points)} line set frames for {num_frames} frames." ) return num_frames ``` -------------------------------- ### Download and Load MHR Model Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Downloads the MHR model assets, unzips the model file, and loads it using PyTorch JIT. Ensure you have curl and unzip installed. ```shell !curl -OL https://github.com/facebookresearch/MHR/releases/download/v1.0.0/assets.zip !unzip -p assets.zip assets/mhr_model.pt > /content/mhr_model.pt !rm assets.zip ``` ```python import torch scripted_mhr_model = torch.jit.load("/content/mhr_model.pt") ``` -------------------------------- ### Setup Automatic Camera Positioning Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Automatically configures camera parameters based on scene bounding box if not manually set. Avoids numerical issues by adding a small offset to the look-at point. ```python if not self.camera_is_manually_set: scene_center = (self.scene_bbox[0] + self.scene_bbox[1]) / 2.0 scene_scale = np.max(np.abs(self.scene_bbox[1] - self.scene_bbox[0])) self.camera = sp.Camera( center=scene_center + np.array([0.0, 0.0, 2 * scene_scale]), look_at=scene_center + 1e-8, # Avoid numerical issues fov_y_degrees=30.0, aspect_ratio=1.0, far_crop_distance=20.0, near_crop_distance=0.01, ) ``` -------------------------------- ### Direct MHR Constructor Source: https://context7.com/facebookresearch/mhr/llms.txt Instantiate `MHR` directly using a pre-loaded `pymomentum.geometry.Character` and an optional `MHRPoseCorrectivesModel`. This is useful for custom setups or unit testing. ```python import torch import pymomentum.geometry as pym_geometry from mhr.mhr import MHR, MHRPoseCorrectivesModel, set_blendshape_parameter_sets from mhr.io import load_pose_dirs_predictor import numpy as np # --- Load character manually --- character = pym_geometry.Character.load_fbx( "assets/lod1.fbx", "assets/compact_v6_1.model", load_blendshapes=True, ) character = character.with_blend_shape(character.blend_shape) set_blendshape_parameter_sets(character) # --- Build pose correctives (optional) --- blendshapes_data = np.load("assets/corrective_blendshapes_lod1.npz") activation_data = np.load("assets/corrective_activation.npz") predictor = load_pose_dirs_predictor( blendshapes_data, activation_data, load_with_cuda=False ) pose_correctives = MHRPoseCorrectivesModel(predictor) # --- Instantiate --- device = torch.device("cpu") mhr_model = MHR(character, pose_correctives, device=device) ``` -------------------------------- ### Get Pose Parameter Limits Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Retrieves the defined limits for each pose parameter. This information is crucial for constraining pose variations within realistic bounds. ```python # Get the pose parameter limits. pose_parameter_limits = scripted_mhr_model.get_parameter_limits() ``` -------------------------------- ### ScenepicVisualization Initialization Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Initializes a ScenepicVisualization instance, setting up a new ScenePic scene with default camera, lighting, and shading. Assets are added and enabled later. ```python self.scene: sp.Scene = sp.Scene() ``` -------------------------------- ### Initialize Visualization Scene Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Sets up the scene with default camera and shading parameters. Initializes bounding box and coordinate frame properties. ```python self.coordinate_frame_names: list[str] = [] self.coordinate_frame_opacity: list[float] = [] self.frame_size: float = 0.1 self.scene_bbox: np.ndarray[tuple[int, int], np.dtype[np.float32]] = np.zeros( (2, 3), dtype=np.float32 ) self.scene_bbox[0] = -np.inf * np.ones(3) self.scene_bbox[1] = np.inf * np.ones(3) # Initialize with default camera parameters using simplified constructor self.camera: sp.Camera = sp.Camera( center=np.array([0.0, 0.0, 3.0], dtype=np.float32), look_at=np.array([0.0, 0.0, 0.0], dtype=np.float32), fov_y_degrees=30.0, aspect_ratio=1.0, far_crop_distance=20.0, near_crop_distance=0.01, ) self.camera_is_manually_set = False # Set the default background color to white. self.shading = sp.Shading( bg_color=_WHITE, ambient_light_color=_LIGHT_GRAY, directional_light_color=_DARK_GRAY, directional_light_dir=np.array([2, 1, 2]), ) ``` -------------------------------- ### Get Number of Identity Blendshapes Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Retrieves the number of identity blendshapes available in the model. This can be used to understand the dimensionality of identity variations. ```python num_pca_comp = scripted_mhr_model.get_num_identity_blendshapes() ``` -------------------------------- ### Initialize Point Cloud Visualization Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Initializes point cloud data, including colors, sizes, and names. Automatically generates colors using a rainbow colormap if not provided. Calculates scene bounding box based on point cloud extents. ```python self._validate_input_is_list_of_lists(point_clouds, "Point clouds") ) self.point_cloud_opacity = ( point_cloud_opacity if point_cloud_opacity is not None else [1.0] * self.num_point_clouds_per_frame ) self.point_size = point_size self.point_cloud_names = ( point_cloud_names if point_cloud_names is not None else [f"point_cloud_{i}" for i in range(self.num_point_clouds_per_frame)] ) # Generate evenly spaced values from 0 to 0.8 for rainbow_r colormap, so that 0 -> red, 0.85 -> blue. color_indices = np.linspace(0, 0.85, self.num_point_clouds_per_frame) rainbow_cmap = plt.cm.rainbow_r # We keep only the RGB values and discard the alpha channel self.point_cloud_colors = rainbow_cmap(color_indices)[:, :3] if point_cloud_colors is not None: # Sample self.num_point_clouds_per_frame colors according to the rainbow color map in matplotlib. _validate_mesh_colors(point_cloud_colors, self.num_point_clouds_per_frame) self.point_cloud_colors = _normalize_mesh_colors( point_cloud_colors, self.point_cloud_colors ) # Get the bounding box for the added point clouds. max_point_cloud_corner = -np.inf * np.ones(3) min_point_cloud_corner = np.inf * np.ones(3) for one_frame_point_clouds in self.point_clouds: for point_cloud in one_frame_point_clouds: max_point_cloud_corner = np.maximum( max_point_cloud_corner, np.max(point_cloud, axis=0) ) min_point_cloud_corner = np.minimum( min_point_cloud_corner, np.min(point_cloud, axis=0) ) # Update the scene bounding box according to the bounding box of all the added point clouds. self.scene_bbox[0] = np.maximum(self.scene_bbox[0], max_point_cloud_corner) self.scene_bbox[1] = np.minimum(self.scene_bbox[1], min_point_cloud_corner) if point_labels is not None: if not isinstance(point_labels[0], list): raise ValueError( "Point labels must be a list of lists, where each inner list represents a single frame of point labels." ) self.point_labels = point_labels for i_point_cloud, point_label in enumerate(self.point_labels): if not self.point_clouds[0][i_point_cloud].shape[0] == len(point_label): raise ValueError( "List of point labels should match the size of each point cloud in one frame." f"Got {len(point_label)} point labels for a point cloud with size of f{self.point_clouds[0][i_point_cloud].shape[0]}" ) self.label_color = label_color self.point_label_offset = label_offset self.label_size_in_pixel = label_size_in_pixel # Handle point_label_opacity parameter self.point_label_opacity = ( point_label_opacity if point_label_opacity is not None else [1.0] * self.num_point_clouds_per_frame ) ``` -------------------------------- ### Get Joint Influence Matrix Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Retrieves the influence of each pose parameter on the joints. This is useful for understanding how changes in pose parameters affect the skeletal structure. ```python joint_names = scripted_mhr_model.get_joint_names() num_joints = len(joint_names) pose_parameter_names = scripted_mhr_model.get_parameter_names()[:-45] influence_matrix = ( scripted_mhr_model.get_parameter_transform().numpy().astype(bool).T ) influence_mapping = {} for pose_parameter_name, joint_mask in zip(pose_parameter_names, influence_matrix): influence_mapping[pose_parameter_name] = [] influenced_joint_indices = np.nonzero(joint_mask.reshape(num_joints, 7).sum(1))[ 0 ].tolist() for influenced_joint_index in influenced_joint_indices: influence_mapping[pose_parameter_name].append( joint_names[influenced_joint_index] ) ``` -------------------------------- ### Instantiate and Display BentoParameterInterface Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Instantiates the BentoParameterInterface with provided pose parameters, limits, joint mappings, and a rendering function. Then, it displays the interactive interface. Assumes `parameter_values`, `pose_parameter_names`, `pose_parameter_limits`, `influence_mapping`, and `visualize_posed_mhr_model` are defined elsewhere. ```python interface = BentoParameterInterface( pose_parameters=pose_parameter_names, parameter_limits=pose_parameter_limits, parameter_to_kinematic_joints=influence_mapping, render_posed_mhr=visualize_posed_mhr_model, ) interface.display() ``` -------------------------------- ### Add Lines to Mesh (Line or Thickline) Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Adds lines to a mesh, supporting both 'line' and 'thickline' types. 'Thickline' requires start and end points with specified thicknesses. ```python def _add_lines_to_mesh( self, mesh: sp.Mesh, start_points: np.ndarray[tuple[int, int], np.dtype[np.float32]], end_points: np.ndarray[tuple[int, int], np.dtype[np.float32]], line_set_index: int, ) -> None: """Add lines to a mesh based on line type.""" line_set_color = self.line_set_colors[line_set_index] if self.line_type == "line": mesh.add_lines( start_points=start_points, end_points=end_points, color=line_set_color, ) elif self.line_type == "thickline": for start_point, end_point in zip(start_points, end_points): mesh.add_thickline( color=line_set_color, start_point=start_point, end_point=end_point, start_thickness=self.line_start_thickness, end_thickness=0.001, ) ``` -------------------------------- ### Add Line Sets to Frame Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Adds multiple line sets to a specific frame. It iterates through defined start and end points, creating meshes for each line set. ```python def _add_line_sets_to_frame(self, frame: sp.Frame3D, frame_index: int) -> None: """Add line sets to a specific frame.""" updated_line_sets = [] frame_start_points = self.start_points[frame_index] frame_end_points = self.end_points[frame_index] for line_set_index in range(self.num_line_sets_per_frame): start_points = frame_start_points[line_set_index] end_points = frame_end_points[line_set_index] line_set_mesh = self.scene.create_mesh( mesh_id=self.line_set_names[line_set_index], layer_id=self.line_set_names[line_set_index], ) self._add_lines_to_mesh( line_set_mesh, start_points, end_points, line_set_index ) updated_line_sets.append(line_set_mesh) frame.add_meshes(updated_line_sets) ``` -------------------------------- ### Prepare 3D Scene for Rendering Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Sets up the 3D canvas, camera, and scene assets for rendering. This method configures the canvas dimensions, shading, camera positioning, layer settings, and animates the scene frames. ```python canvas3d = self.scene.create_canvas_3d( height=height, width=width, shading=self.shading ) self._setup_camera_if_needed() layer_settings = self._create_layer_settings() canvas3d.set_layer_settings(layer_settings) num_frames = self._setup_scene_assets() self._create_animation_frames(canvas3d, num_frames) ``` -------------------------------- ### Configure Coordinate Frame Properties Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Sets up coordinate frame origins, orientations, names, and opacity. Automatically updates the scene bounding box. Ensure origins and orientations have matching structures. ```python self.coordinate_frame_origins, self.coordinate_frame_orientations, self.num_coordinate_frames_per_frame, ) = self._validate_input_pair_are_list_of_lists( coordinate_frame_origins, "Coordinate frame origins", coordinate_frame_orientations, "Coordinate frame orientations", ) self.coordinate_frame_names = ( coordinate_frame_names if coordinate_frame_names is not None else [ f"coordinate_frame_{i}" for i in range(self.num_coordinate_frames_per_frame) ] ) self.coordinate_frame_opacity = ( coordinate_frame_opacity if coordinate_frame_opacity is not None else [1.0] * self.num_coordinate_frames_per_frame ) # Get the bounding box for the added coordinate frames. max_coordinate_frame_corner = -np.inf * np.ones(3) min_coordinate_frame_corner = np.inf * np.ones(3) for frame_idx in range(len(self.coordinate_frame_origins)): for coordinate_frame_idx in range(self.num_coordinate_frames_per_frame): origin = self.coordinate_frame_origins[frame_idx][coordinate_frame_idx] # Consider the extent of the coordinate frame axes based on frame_size max_coordinate_frame_corner = np.maximum( max_coordinate_frame_corner, np.max(origin + frame_size, axis=0) ) min_coordinate_frame_corner = np.minimum( min_coordinate_frame_corner, np.min(origin - frame_size, axis=0) ) # Update the scene bounding box according to the bounding box of all the added coordinate frames. self.scene_bbox[0] = np.maximum(self.scene_bbox[0], max_coordinate_frame_corner) self.scene_bbox[1] = np.minimum(self.scene_bbox[1], min_coordinate_frame_corner) self.frame_size = frame_size ``` -------------------------------- ### show Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Displays the 3D scene interactively in a Jupyter notebook cell. ```APIDOC ## show ### Description Display the 3D scene in the current Jupyter notebook cell. Renders the scene with all added assets (meshes, point clouds, line sets, coordinate frames) and displays it as an interactive HTML widget that can be manipulated with mouse controls. ### Args - `view_height` (int): Height of the rendered view in pixels. Default is 600. - `view_width` (int): Width of the rendered view in pixels. Default is 600. ### Note - This method only works in Jupyter notebook environments - The rendered scene supports mouse navigation (rotation, zoom, pan) - If multiple frames are present, the scene will include timeline controls - The camera will be automatically positioned unless manually set ``` -------------------------------- ### Create Layer Settings for Assets Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Generates a dictionary of layer settings for various asset types, controlling their visibility and opacity. Handles meshes, point clouds (including labels), line sets, and coordinate frames. ```python layer_settings: dict[str, dict[str, Any]] = {} # Configure mesh layer settings if self.mesh_names: for i, mesh_name in enumerate(self.mesh_names): opacity = self.mesh_opacity[i] layer_settings[mesh_name] = { "filled": False if opacity == 0 else True, "wireframe": False, "opacity": 1.0 if opacity == 0 else opacity, } # Configure point cloud layer settings if self.point_cloud_names: for i, pc_name in enumerate(self.point_cloud_names): opacity = self.point_cloud_opacity[i] layer_settings[pc_name] = { "filled": False if opacity == 0 else True, "wireframe": False, "opacity": 1.0 if opacity == 0 else opacity, } if self.point_labels: # Point label opacity is the minimum of label opacity and point cloud opacity effective_label_opacity = min( self.point_label_opacity[i], self.point_cloud_opacity[i] ) layer_settings[f"Labels_{pc_name}"] = { "filled": False if effective_label_opacity == 0 else True, "wireframe": False, "opacity": 1.0 if effective_label_opacity == 0 else effective_label_opacity, } # Configure line set layer settings if self.line_set_names: for i, line_name in enumerate(self.line_set_names): opacity = self.line_set_opacity[i] layer_settings[line_name] = { "filled": False if opacity == 0 else True, "wireframe": False, "opacity": 1.0 if opacity == 0 else opacity, } # Configure coordinate frame layer settings if self.coordinate_frame_names: for i, frame_name in enumerate(self.coordinate_frame_names): opacity = self.coordinate_frame_opacity[i] layer_settings[frame_name] = { "filled": False if opacity == 0 else True, "wireframe": False, "opacity": 1.0 if opacity == 0 else opacity, } return layer_settings ``` -------------------------------- ### Create Facial Expression PCA Index Selector Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Creates interactive sliders to select the range of expression PCA components for visualization. Links the start and end index sliders to ensure valid ranges. ```python import ipywidgets as widgets from IPython.display import display def create_index_selector(max_value=100): start_index_slider = widgets.IntSlider( value=1, min=1, max=max_value, step=1, description='Start expression PCA index:', continuous_update=False, orientation='horizontal', readout=True, readout_format='d', style={'description_width': 'initial'}, layout=widgets.Layout(width='50%') # Set width to 50% of the page ) end_index_slider = widgets.IntSlider( value=min(10, max_value), min=1, max=max_value, step=1, description='End expression PCA index:', continuous_update=False, orientation='horizontal', readout=True, readout_format='d', style={'description_width': 'initial'}, layout=widgets.Layout(width='50%') # Set width to 50% of the page ) # Link the min/max of end_index to start_index and vice-versa widgets.jslink((start_index_slider, 'value'), (end_index_slider, 'min')) widgets.jslink((end_index_slider, 'value'), (start_index_slider, 'max')) display(widgets.VBox([start_index_slider, end_index_slider])) return start_index_slider, end_index_slider # Call the function to display the widget and store the slider references print("Place select the range of expression components (from 72 expression components)") start_idx_selector, end_idx_selector = create_index_selector(max_value=72) ``` -------------------------------- ### Visualize MHR Model Components Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Initializes model parameters, retrieves the mean mesh, joint data, and skeleton structure, then visualizes these components using Scenepic. Includes instructions on how to interact with the visualization. ```python rot = torch.zeros(1, 3) # Global Rotation trans = torch.zeros(1, 3) # Translation lbs_model_parms = torch.zeros(1, 198) # Joint angles and scalings, feel free to change the values here to pose the model. params = torch.hstack((trans, rot, lbs_model_parms)) num_pca_comp = scripted_mhr_model.get_num_identity_blendshapes() identity_coeffs = torch.zeros(1, num_pca_comp) face_expr_coeffs = torch.zeros(1, 72) # Get the mean model mesh. mean_model_vertices, skel_state = ( scripted_mhr_model( model_parameters=params, identity_coeffs=identity_coeffs, face_expr_coeffs=face_expr_coeffs, ) ) mean_model_vertices = mean_model_vertices.numpy()[0] / 100.0 faces = scripted_mhr_model.character_torch.mesh.faces.cpu().numpy() mean_model_mesh = trimesh.Trimesh(mean_model_vertices, faces, process=False) # Get the joint locations. skel_state = skel_state.numpy()[0] joint_locations = skel_state[..., :3] / 100.0 joint_names = scripted_mhr_model.get_joint_names() # Get the skeleton structure. joint_parents = scripted_mhr_model.character_torch.skeleton.joint_parents joint_parents = np.clip(np.array(joint_parents), 0, np.inf).astype( np.int32 ) # So that the root points to itself. parent_joint_locations = joint_locations[joint_parents] # Get the kinematic joints (If a joint is a parent of another, then it is a kinematic joint). kinematic_joints = joint_locations[np.unique(joint_parents)] kinematic_joints_names = [joint_names[i] for i in np.unique(joint_parents)] # Get joint local coordinate orientations. joint_orientations = skel_state[..., 3:7] joint_orientations = R.Rotation.from_quat(joint_orientations).as_matrix() kinematic_joint_orientations = joint_orientations[np.unique(joint_parents)] # Visualize the mean body mesh. visualizer = ScenepicVisualization() visualizer.add_meshes( meshes=[[mean_model_mesh]], mesh_names=["Template body surface"], mesh_opacity=[0.9], mesh_colors=[sp.Colors.Blue], ) # Visualize all the joints (red) and kinematic joints (blue). visualizer.add_point_clouds( point_clouds=[[joint_locations, kinematic_joints]], point_cloud_names=["Joints", "Kinematic joints"], point_cloud_opacity=[0.0, 1.0], point_size=0.02, point_labels=[joint_names, kinematic_joints_names], point_label_opacity=[0.0, 0.0], label_size_in_pixel=30, label_offset=0.01, point_cloud_colors=[sp.Colors.Red, sp.Colors.Blue], ) # Visualize the skeleton. visualizer.add_line_sets( start_points=[[parent_joint_locations]], end_points=[[joint_locations]], line_set_colors=[sp.Colors.Green], line_set_names=["Skeleton"], line_start_thickness=0.015, ) # Visualize joint orientations. visualizer.add_coordinate_frames( coordinate_frame_origins=[[joint_locations, kinematic_joints]], coordinate_frame_orientations=[[joint_orientations, kinematic_joint_orientations]], coordinate_frame_names=["Joints local frames", "Kinematic joint local frames"], coordinate_frame_opacity=[0.0, 0.0], frame_size=0.04, ) print( "How to use the visualization?\n" " Toggle layers and information from the top right drop-down button.\n" " Change the opacity/transparency use the bars in the drop-down button.\n" " Drag the mouse to rotate the view.\n" " Scroll to zoom in/out.\n" " Hold the shift key and drag the mouse to move around.\n" ) visualizer.show() ``` ```python #@title 2. Interactive model deforming. import ipywidgets as widgets import numpy as np import IPython import scenepic as sp import trimesh import scipy.spatial.transform as R parameter_values = {param: 0.0 for param in pose_parameter_names} last_selected_parameter = pose_parameter_names[0] ``` -------------------------------- ### Configure Scene Shading Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Sets up background color, ambient light, and directional light for the 3D scene. Colors are normalized automatically. Changes apply on the next render. ```python self.shading = sp.Shading( bg_color=_normalize_mesh_colors([background_color], [_WHITE])[0], ambient_light_color=_normalize_mesh_colors( [ambient_light_color], [_LIGHT_GRAY] )[0], directional_light_color=_normalize_mesh_colors( [directional_light_color], [_DARK_GRAY] )[0], directional_light_dir=directional_light_dir if directional_light_dir is not None else np.array([2, 1, 2]), ) ``` -------------------------------- ### Load MHR Model from Files Source: https://context7.com/facebookresearch/mhr/llms.txt Use `MHR.from_files` to construct an MHR instance. Specify the device, level of detail (LOD), and whether to load pose correctives. The default asset folder is './assets/'. ```python import torch from mhr.mhr import MHR # Load LOD 1 model on CPU (default asset folder: ./assets/) mhr_model = MHR.from_files( device=torch.device("cpu"), lod=1, # LOD 0-6; LOD 1 is the standard choice wants_pose_correctives=True # set False to skip loading the MLP ) # Load LOD 3 model on GPU, skipping pose correctives mhr_model_gpu = MHR.from_files( device=torch.device("cuda"), lod=3, wants_pose_correctives=False ) print(f"Identity blendshapes : {mhr_model.get_num_identity_blendshapes()}") # 45 print(f"Face expr blendshapes: {mhr_model.get_num_face_expression_blendshapes()}") # 72 print(f"Mesh vertices : {len(mhr_model.character.mesh.vertices)}") print(f"Mesh faces : {len(mhr_model.character.mesh.faces)}") ``` -------------------------------- ### Initialize BentoParameterInterface Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Initializes the parameter control interface for Bento. It sets up pose parameters, limits, joint mappings, and the rendering function. It also creates and configures widgets for user interaction. ```python class BentoParameterInterface: def __init__( self, pose_parameters, parameter_limits, parameter_to_kinematic_joints, render_posed_mhr, ): """ Initialize the parameter control interface optimized for Bento """ self.pose_parameters = pose_parameters self.parameter_limits = parameter_limits self.parameter_to_kinematic_joints = parameter_to_kinematic_joints self.render_posed_mhr = render_posed_mhr # Initialize the associated joints self.associted_joints = [] # Flag to prevent infinite loops during updates self._updating = False # Create widgets self._create_widgets() self._setup_observers() self._update_parameter_info() pose_parm_values = np.array( [ parameter_values[parameter_name] for parameter_name in self.pose_parameters ] ).astype(np.float32)[np.newaxis, ...] rendered_html = self.render_posed_mhr( pose_parm_values, affected_joints_names=self.associted_joints ) ``` -------------------------------- ### Create Widgets for Parameter Interface Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Creates the interactive widgets for the parameter interface, including dropdowns, sliders, and buttons. These widgets are organized into panels for display. ```python def _create_widgets(self): """Create all the widgets for the interface""" # Parameter dropdown self.parameter_dropdown = widgets.Dropdown( options=self.pose_parameters, value=last_selected_parameter, description="Parameter:", style={"description_width": "initial"}, layout=widgets.Layout(width="95%"), ) # Slider for parameter value current_param = self.parameter_dropdown.value current_idx = self.pose_parameters.index(current_param) min_val, max_val = self.parameter_limits[current_idx] self.parameter_slider = widgets.FloatSlider( value=0.0, min=min_val, max=max_val, step=0.01, description="Value:", continuous_update=False, style={"description_width": "initial"}, layout=widgets.Layout(width="95%"), ) # Reset buttons self.reset_current_button = widgets.Button( description="Reset Current Parameter", button_style="warning", layout=widgets.Layout(width="48%"), ) self.reset_all_button = widgets.Button( description="Reset All", button_style="danger", layout=widgets.Layout(width="48%"), ) # Button container self.button_box = widgets.HBox( [self.reset_current_button, self.reset_all_button], layout=widgets.Layout(justify_content="space-between"), ) # Left panel (controls) - 1/4 width self.left_panel = widgets.VBox( [self.parameter_dropdown, self.parameter_slider, self.button_box], layout=widgets.Layout(width="40%", padding="10px", border="1px solid #ccc"), ) # Middle panel (current parameter info) - 1/4 width self.middle_panel = widgets.HTML( value="", layout=widgets.Layout( width="60%", height="170px", padding="20px", border="0px solid #ccc", overflow="auto", ), ) # Top container self.main_container = widgets.HBox( [self.left_panel, self.middle_panel], layout=widgets.Layout(width="98%"), ) ``` -------------------------------- ### Initialize and Use SparseLinear Layer Source: https://context7.com/facebookresearch/mhr/llms.txt Initializes a sparse linear layer with a predefined mask to reduce memory footprint. Use when dealing with large weight matrices where most weights are zero. ```python import torch from mhr.utils import SparseLinear # 750 input features, 3000 output features, ~10 % density mask = (torch.rand(3000, 750) < 0.1).bool() layer = SparseLinear(in_channels=750, out_channels=3000, sparse_mask=mask, bias=False) x = torch.randn(8, 750) # batch of 8 out = layer(x) print(f"Output: {out.shape}") # torch.Size([8, 3000]) print(layer) # SparseLinear(in_channels=750, out_channels=3000, bias=False) ``` -------------------------------- ### Add Point Clouds to Scene Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Adds point clouds to the scene and sets up their properties. Validates the number of frames against the number of point clouds provided. Requires ScenePic library and point cloud data. ```python scene_point_clouds = [ self.scene.create_mesh(mesh_id=point_cloud_name, layer_id=point_cloud_name) for point_cloud_name in self.point_cloud_names ] if num_frames == 0: num_frames = len(self.point_clouds) else: if num_frames != len(self.point_clouds): raise ValueError( f"Invalid point clouds. Expected a list of point clouds, where the length matches the number of mesh frames or other 3D input assets. Got {len(self.point_clouds)} point clouds for {num_frames} frames." ) for point_cloud_index, point_cloud in enumerate(self.point_clouds[0]): per_point_color = _repeat_vertex_color( self.point_cloud_colors[point_cloud_index], point_cloud.shape[0], point_cloud_index, ) scene_point_clouds[point_cloud_index].add_sphere( sp.Colors.White, transform=sp.Transforms.Scale(self.point_size) ) scene_point_clouds[point_cloud_index].enable_instancing( point_cloud, colors=per_point_color ) return num_frames ``` -------------------------------- ### MHR.from_files Source: https://context7.com/facebookresearch/mhr/llms.txt Loads the MHR model from asset files. This is the primary factory method for constructing an MHR instance, allowing configuration of device, LOD, and whether to include pose correctives. ```APIDOC ## MHR.from_files — Load model from asset files The primary factory method for constructing an `MHR` instance. It reads the FBX rig, the `.model` parameterization file, the corrective blendshapes, and the sparse activation data for the pose-correctives MLP. All paths are resolved relative to the `folder` argument, which defaults to `./assets/`. ### Method `MHR.from_files(device, lod, wants_pose_correctives)` ### Parameters - **device** (torch.device) - The device to load the model onto (e.g., `torch.device("cpu")` or `torch.device("cuda")`). - **lod** (int) - Level of Detail, ranging from 0 to 6. LOD 1 is the standard choice. - **wants_pose_correctives** (bool) - Set to `True` to load the pose-correctives MLP, `False` to skip it. ### Request Example ```python import torch from mhr.mhr import MHR # Load LOD 1 model on CPU (default asset folder: ./assets/) mhr_model = MHR.from_files( device=torch.device("cpu"), lod=1, # LOD 0-6; LOD 1 is the standard choice wants_pose_correctives=True # set False to skip loading the MLP ) # Load LOD 3 model on GPU, skipping pose correctives mhr_model_gpu = MHR.from_files( device=torch.device("cuda"), lod=3, wants_pose_correctives=False ) print(f"Identity blendshapes : {mhr_model.get_num_identity_blendshapes()}") # 45 print(f"Face expr blendshapes: {mhr_model.get_num_face_expression_blendshapes()}") # 72 print(f"Mesh vertices : {len(mhr_model.character.mesh.vertices)}") print(f"Mesh faces : {len(mhr_model.character.mesh.faces)}") ``` ### Response - **mhr_model** (MHR) - An instance of the MHR model. ``` -------------------------------- ### Convert SMPL-X Vertices to MHR Parameters Source: https://context7.com/facebookresearch/mhr/llms.txt Converts SMPL/SMPL-X vertex data to MHR parameters using PyTorch optimization. Supports single identity across frames and returns fitting errors. ```python import torch import smplx import numpy as np from mhr.mhr import MHR from tools.mhr_smpl_conversion.conversion import Conversion device = torch.device("cuda" if torch.cuda.is_available() else "cpu") mhr_model = MHR.from_files(lod=1, device=device) smplx_model = smplx.SMPLX( model_path="path/to/SMPLX_NEUTRAL.npz", gender="neutral", use_pca=False, flat_hand_mean=True, ).to(device) converter = Conversion(mhr_model=mhr_model, smpl_model=smplx_model, method="pytorch") # --- SMPL-X vertices → MHR parameters (single identity across frames) --- smplx_verts = torch.randn(10, 10475, 3).to(device) # [B, 10475, 3] in metres result = converter.convert_smpl2mhr( smpl_vertices=smplx_verts, single_identity=True, return_mhr_meshes=True, return_mhr_parameters=True, return_fitting_errors=True, ) print(f"MHR params keys : {list(result.result_parameters.keys())}") # ['lbs_model_params', 'identity_coeffs', 'face_expr_coeffs'] print(f"Fitting errors : {result.result_errors}") # [B] array, cm ``` -------------------------------- ### Create Labels for Point Clouds Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Generates Label objects for individual points within a point cloud. Requires pre-defined label text, color, layer, size, and offset. ```python def _create_point_labels( self, labels: list[sp.Label], pc_index: int, point_cloud: np.ndarray[tuple[int, int], np.dtype[np.float32]], ) -> None: """Create labels for points in a point cloud.""" for point_id in range(point_cloud.shape[0]): label = self.scene.create_label( text=self.point_labels[pc_index][point_id], color=self.label_color, layer_id=f"Labels_{self.point_cloud_names[pc_index]}", size_in_pixels=self.label_size_in_pixel, offset_distance=0.0, ) labels.append(label) ``` -------------------------------- ### Render Scene to HTML String Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Generates an HTML string of the 3D scene, suitable for embedding in other documents or saving to a file. The scene includes all added assets and can be configured by view dimensions. ```python self._prepare_rendering(width=view_width, height=view_height) return self._generate_html_string() ``` -------------------------------- ### Import SciPy Spatial Transform Source: https://github.com/facebookresearch/mhr/blob/main/tools/mhr_visualization/MHR visualization.ipynb Imports the `scipy.spatial.transform` module, commonly used for handling rotations and transformations in 3D space. ```python import scipy.spatial.transform as R ```