This tutorial walks you through the complete workflow of taking a C3D motion capture file and retargeting it onto a 3D character using the MyoSapiens Python SDK.
Before starting, make sure you have:
Start by importing the SDK and initializing the client. Make sure you have your API key set as an environment variable (see Installation Guide):
import os
from myosdk import Client
# Initialize the client
client = Client(api_key=os.getenv("MYOSDK_API_KEY"))
Upload your C3D motion capture file and markerset configuration:
# Upload C3D motion capture file
c3d_asset = client.assets.upload_file("path/to/your/motion.c3d")
print(f"C3D uploaded: {c3d_asset}")
# Upload markerset XML file
markerset_asset = client.assets.upload_file("path/to/your/markerset.xml")
print(f"Markerset uploaded: {markerset_asset}")
The SDK handles the upload process and returns asset objects with IDs you'll use in the next step.
Now create a retarget job to map the motion onto your chosen character:
# Option 1: Use default character (simplest)
job = client.jobs.start_retarget(
c3d_asset_id=c3d_asset["asset_id"],
markerset_asset_id=markerset_asset["asset_id"],
)
print(f"Job started: {job['job_id']}")
print(f"Status: {job['status']}")
print(f"Estimated wait time: {job.get('estimated_wait_time_seconds', 'unknown')} seconds")
See Retarget Parameters for all available configuration options.
The retarget job runs on our cloud infrastructure. Use the wait() method to poll until it completes:
# Wait for the job to finish (with timeout)
try:
result = client.jobs.wait(job["job_id"], timeout=600) # 10 minutes
# Check job status
if result["status"] == "SUCCEEDED":
retarget_output_asset_id = result["output"]["retarget_output_asset_id"]
print(f"Job completed successfully!")
print(f"Output asset ID: {retarget_output_asset_id}")
elif result["status"] == "FAILED":
error = result.get("error", {})
print(f"Job failed: {error.get('message', 'Unknown error')}")
# Handle failure appropriately
else:
print(f"Job ended with status: {result['status']}")
except TimeoutError:
print("Job did not complete within timeout period")
# You can still check status later with client.jobs.get(job["job_id"])
wait() method automatically polls the job status until completion. For long-running jobs, this may take several minutes depending on the complexity of your motion data. Always set a reasonable timeout and handle failures appropriately.Finally, download the generated animation file:
output_path = "output/animation.npz"
print(f"Downloading result to {output_path}...")
client.assets.download(retarget_output_asset_id, output_path)
print("Download complete!")
The output is a .npz file (NumPy compressed archive) containing joint angles and joint names:
import numpy as np
# Load the output
data = np.load(output_path)
joint_angles = data["joint_angles"] # Shape: (frames, joints, 3)
joint_names = data["joint_names"] # Array of joint name strings
print(f"Frames: {joint_angles.shape[0]}")
print(f"Joints: {joint_angles.shape[1]}")
print(f"Joint names: {joint_names}")
# Example: Access first frame
first_frame = joint_angles[0]
print(f"First frame shape: {first_frame.shape}")
See File Formats for complete details on the output format structure and usage. ::
Here's the full script putting it all together with error handling:
import os
import numpy as np
from myosdk import Client, APIError, NotFoundError
# Initialize client
client = Client(api_key=os.getenv("MYOSDK_API_KEY"))
try:
# Upload input files
print("Uploading C3D file...")
c3d_asset = client.assets.upload_file("motion.c3d")
print(f"C3D uploaded: {c3d_asset['asset_id']}")
print("Uploading markerset file...")
markerset_asset = client.assets.upload_file("markerset.xml")
print(f"Markerset uploaded: {markerset_asset['asset_id']}")
# Start retarget job
print("Starting retarget job...")
job = client.jobs.start_retarget(
c3d_asset_id=c3d_asset["asset_id"],
markerset_asset_id=markerset_asset["asset_id"],
)
print(f"Job started: {job['job_id']}")
# Wait for completion
print("Waiting for job to complete...")
result = client.jobs.wait(job["job_id"], timeout=600)
if result["status"] == "SUCCEEDED":
retarget_output_asset_id = result["output"]["retarget_output_asset_id"]
# Download result
print("Downloading result...")
client.assets.download(retarget_output_asset_id, "joint_angles.npz")
print("Done! Animation saved to joint_angles.npz")
# Verify output
data = np.load("joint_angles.npz")
print(f"Output contains {data['joint_angles'].shape[0]} frames")
print(f"Joints: {list(data['joint_names'])}")
else:
print(f"Job failed with status: {result['status']}")
if "error" in result:
print(f"Error: {result['error']}")
except NotFoundError as e:
print(f"Resource not found: {e.message}")
except APIError as e:
print(f"API error: {e.message}")
finally:
client.close()
Now that you've completed your first retargeting: