Usage

Basic Usage

Here’s a simple example of how to use py-dem-bones with the direct C++ bindings:

import numpy as np
import py_dem_bones as pdb

# Create a DemBones instance
dem_bones = pdb.DemBones()

# Set parameters
dem_bones.nIters = 30
dem_bones.nInitIters = 10
dem_bones.nTransIters = 5
dem_bones.nWeightsIters = 3
dem_bones.nnz = 4
dem_bones.weightsSmooth = 1e-4

# Set up data
# Rest pose vertices (nV x 3)
rest_pose = np.array([
    [0.0, 0.0, 0.0],
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0]
], dtype=np.float64)

# Animated pose vertices (nF * nV x 3)
animated_poses = np.array([
    # Frame 1
    [0.0, 0.0, 0.0],
    [1.0, 0.1, 0.0],
    [0.0, 1.1, 0.0],
    [0.0, 0.0, 1.0],
    # Frame 2
    [0.0, 0.0, 0.0],
    [1.0, 0.2, 0.0],
    [0.0, 1.2, 0.0],
    [0.0, 0.0, 1.0]
], dtype=np.float64)

# Set data
dem_bones.nV = 4  # Number of vertices
dem_bones.nB = 2  # Number of bones
dem_bones.nF = 2  # Number of frames
dem_bones.nS = 1  # Number of subjects
dem_bones.fStart = np.array([0], dtype=np.int32)  # Frame start indices for each subject
dem_bones.subjectID = np.zeros(2, dtype=np.int32)  # Subject ID for each frame
dem_bones.u = rest_pose  # Rest pose
dem_bones.v = animated_poses  # Animated poses

# Compute skinning decomposition
dem_bones.compute()

# Get results
weights = dem_bones.get_weights()
transformations = dem_bones.get_transformations()

print("Skinning weights:")
print(weights)
print("\nBone transformations:")
print(transformations)

Using the Python Wrapper Classes

For a more Pythonic experience, you can use the wrapper classes that provide enhanced functionality:

import numpy as np
import py_dem_bones as pdb

# Create a DemBonesWrapper instance
dem_bones = pdb.DemBonesWrapper()

# Set parameters using Pythonic property names
dem_bones.num_iterations = 30
dem_bones.num_init_iterations = 10
dem_bones.num_transform_iterations = 5
dem_bones.num_weights_iterations = 3
dem_bones.max_nonzeros_per_vertex = 4
dem_bones.weights_smoothness = 1e-4

# Set up data
# Rest pose vertices
rest_pose = np.array([
    [0.0, 0.0, 0.0],
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0]
], dtype=np.float64)

# Animated pose vertices
animated_poses = np.array([
    # Frame 1
    [0.0, 0.0, 0.0],
    [1.0, 0.1, 0.0],
    [0.0, 1.1, 0.0],
    [0.0, 0.0, 1.0],
    # Frame 2
    [0.0, 0.0, 0.0],
    [1.0, 0.2, 0.0],
    [0.0, 1.2, 0.0],
    [0.0, 0.0, 1.0]
], dtype=np.float64)

# Set data with error handling
try:
    dem_bones.set_rest_pose(rest_pose)
    dem_bones.set_animated_poses(animated_poses)
    dem_bones.set_num_bones(2)

    # Optionally, you can name your bones
    dem_bones.set_bone_names(["Root", "Arm"])

    # Compute skinning decomposition
    dem_bones.compute()

    # Get results with error handling
    weights = dem_bones.get_weights()
    transformations = dem_bones.get_transformations()

    print("Skinning weights:")
    print(weights)
    print("\nBone transformations:")
    print(transformations)

except pdb.DemBonesError as e:
    print(f"Error: {e}")

Using DemBonesExt for Hierarchical Skeletons

For more advanced usage with hierarchical skeletons, use the DemBonesExt class:

import numpy as np
import py_dem_bones as pdb

# Create a DemBonesExt instance
dem_bones_ext = pdb.DemBonesExt()

# Set parameters (same as DemBones)
dem_bones_ext.nIters = 30
dem_bones_ext.nInitIters = 10
dem_bones_ext.nTransIters = 5
dem_bones_ext.nWeightsIters = 3
dem_bones_ext.nnz = 4
dem_bones_ext.weightsSmooth = 1e-4

# Set up data (same as DemBones)
# ...

# Set additional DemBonesExt data
dem_bones_ext.parent = np.array([-1, 0], dtype=np.int32)  # Parent bone indices (-1 for root)
dem_bones_ext.boneName = ["Root", "Child"]  # Bone names
dem_bones_ext.bindUpdate = 1  # Bind transformation update mode

# Compute skinning decomposition
dem_bones_ext.compute()

# Get results
weights = dem_bones_ext.get_weights()
transformations = dem_bones_ext.get_transformations()

# Compute local rotations and translations
dem_bones_ext.computeRTB()

print("Skinning weights:")
print(weights)
print("\nBone transformations:")
print(transformations)

Or use the Python wrapper for DemBonesExt:

import numpy as np
import py_dem_bones as pdb

# Create a DemBonesExtWrapper instance
dem_bones_ext = pdb.DemBonesExtWrapper()

# Set parameters
dem_bones_ext.num_iterations = 30
dem_bones_ext.num_init_iterations = 10
dem_bones_ext.num_transform_iterations = 5
dem_bones_ext.num_weights_iterations = 3
dem_bones_ext.max_nonzeros_per_vertex = 4
dem_bones_ext.weights_smoothness = 1e-4

# Set up data
# ...

# Set parent-child relationships
dem_bones_ext.set_parent_indices([-1, 0])  # -1 means root bone
dem_bones_ext.set_bone_names(["Root", "Child"])
dem_bones_ext.bind_update = 1  # Enable bind transformation updates

# Compute skinning decomposition
dem_bones_ext.compute()

# Get results
weights = dem_bones_ext.get_weights()
transformations = dem_bones_ext.get_transformations()

# Compute local rotations and translations
dem_bones_ext.compute_local_transforms()

print("Skinning weights:")
print(weights)
print("\nBone transformations:")
print(transformations)

Converting Between NumPy and Eigen

py-dem-bones provides utility functions to convert between NumPy arrays and Eigen matrices:

import numpy as np
import py_dem_bones as pdb

# Create a NumPy array
arr = np.array([[1.0, 2.0], [3.0, 4.0]])

# Convert to Eigen-compatible format
eigen_arr = pdb.numpy_to_eigen(arr)

# Convert back to NumPy with reshaping
reshaped = pdb.eigen_to_numpy(eigen_arr, shape=(4,))
print(reshaped)  # [1.0, 2.0, 3.0, 4.0]

Error Handling

The library provides comprehensive error handling through custom exception classes:

import py_dem_bones as pdb

try:
    # Create an instance
    dem_bones = pdb.DemBonesWrapper()

    # Set invalid parameters
    dem_bones.num_bones = -1  # This will raise an error

except pdb.ParameterError as e:
    print(f"Parameter error: {e}")
except pdb.ComputationError as e:
    print(f"Computation error: {e}")
except pdb.IndexError as e:
    print(f"Index error: {e}")
except pdb.DemBonesError as e:
    print(f"General error: {e}")

Advanced Configuration

Fine-tuning the skinning decomposition parameters:

import py_dem_bones as pdb

dem_bones = pdb.DemBonesWrapper()

# Basic parameters
dem_bones.num_iterations = 30
dem_bones.num_init_iterations = 10
dem_bones.num_transform_iterations = 5
dem_bones.num_weights_iterations = 3

# Advanced parameters
dem_bones.max_nonzeros_per_vertex = 4  # Maximum number of non-zero weights per vertex
dem_bones.weights_smoothness = 1e-4  # Smoothness regularization term
dem_bones.enable_bind_update = True  # Enable bind pose transformation updates
dem_bones.enable_joint_constraints = True  # Enable joint constraints

# Set weight constraints
dem_bones.set_weight_constraints(np.zeros((4, 2)), np.ones((4, 2)))

# Set transformation constraints
dem_bones.set_transformation_constraints(np.eye(4), np.eye(4))