modelhealth

Model Health Python SDK.

Biomechanical analysis from smartphone videos.

Example:
from modelhealth import ModelHealthService, SubjectParameters

service = ModelHealthService("your_api_key")

sessions = service.session_list()
subjects = service.subject_list()
class ModelHealthService:

Client for the Model Health biomechanical analysis platform.

Enables you to measure and analyze human movement from smartphone videos. Provides a complete workflow for:

  1. Initialization: Create a service with your API key
  2. Session Creation: Create a session
  3. Camera Calibration: Calibrate cameras using a checkerboard pattern
  4. Subject Creation: Create a subject profile with anthropometric measurements
  5. Subject Calibration: Calibrate subject using videos of a neutral pose
  6. Movement Recording: Record movement activities (squats, jumps, etc.)
  7. Movement Analysis: Trigger advanced analysis and fetch processed biomechanical data

All methods are synchronous and block until the underlying operation completes. Each instance owns a dedicated single-threaded async runtime so instances may be used across threads, but a single instance should not be called concurrently from multiple threads.

Example:
from modelhealth import (
    ModelHealthService, SubjectParameters, CheckerboardDetails,
    CheckerboardPlacement, ActivityType,
)

service = ModelHealthService("your_api_key")

# Create session and calibrate cameras
session = service.create_session()
details = CheckerboardDetails(rows=4, columns=5, square_size=35, placement=CheckerboardPlacement.perpendicular)
service.calibrate_camera(session, details, lambda s: print(s))

# Create subject and calibrate
params = SubjectParameters(name="Jane Doe", weight=65.0, height=170.0, birth_year=1990)
subject = service.create_subject(params)
service.calibrate_subject(subject, session, lambda s: print(s))

# Record a movement activity
activity = service.start_recording("cmj", session)
# Subject performs movement...
service.stop_recording(session)
ModelHealthService(api_key: str)

Initialises the service and validates the API key format.

Arguments:
  • api_key: Your Model Health API key, available in the dashboard at modelhealth.io.
Raises:
  • AuthenticationError: If the API key is empty or invalid.
Example:
from modelhealth import ModelHealthService

service = ModelHealthService("your_api_key")

# List sessions and subjects
sessions = service.session_list()
subjects = service.subject_list()
def session_list(self) -> list[Session]:

Retrieves all sessions for the account associated with the API key.

Use this to list existing sessions before creating a new one, or to resume a previous capture workflow.

To connect a device to a specific session, use the session qrcode URL to download the QR code image data, then display it in your app to be captured by the ModelHealth mobile app.

Returns:

A list of Session objects, or an empty list if none exist.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
import urllib.request

sessions = service.session_list()
print(f"Found {len(sessions)} sessions")

if sessions:
    first = sessions[0]
    with urllib.request.urlopen(first.qrcode) as resp:
        qr_image_data = resp.read()
    # Display qr_image_data in your app so the mobile app can scan it
def get_session(self, session_id: str) -> Session:

Retrieve a specific session by ID with all activities populated.

Arguments:
  • session_id: The id of the session to retrieve.
Returns:

A Session with activities populated.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NotFoundError: If the session does not exist.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
session = service.get_session("abc123")
for activity in session.activities:
    print(f"{activity.name or activity.id}")
def create_session(self) -> Session:

Creates a session.

A session is the parent container for a movement capture workflow. It links related entities such as activities and subjects and provides the context used by subsequent operations.

Default session settings are applied automatically. Call configure_session() afterwards to override specific settings.

Returns:

A new Session with a unique identifier.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
session = service.create_session()
def configure_session( self, session: Session, framerate: SessionFramerate = <SessionFramerate.fps_120: 'fps_120'>, opensim_model: SessionOpenSimModel = <SessionOpenSimModel.lai_uhlrich_2022_shoulder: 'lai_uhlrich_2022_shoulder'>, scaling_setup: SessionScalingSetup = <SessionScalingSetup.upright_standing_pose: 'upright_standing_pose'>, core_engine: SessionCoreEngine = <SessionCoreEngine.v1_0: 'v1_0'>, filter_frequency: Union[FilterFrequencyDefault, FilterFrequencyHz] = <builtins.FilterFrequencyDefault object>, data_sharing: SessionDataSharing = <SessionDataSharing.share_processed_data_and_identified_videos: 'share_processed_data_and_identified_videos'>) -> None:

Applies settings to an existing session.

Call this after create_session() and before calibration to override any default settings. If not called, the session retains the defaults applied during creation.

All parameters are optional — omit any to keep its default.

Arguments:
Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
session = service.create_session()
service.configure_session(
    session,
    framerate=SessionFramerate.fps_60,
    data_sharing=SessionDataSharing.share_no_data,
)
def subject_list(self) -> list[Subject]:

Retrieves all subjects associated with the API key.

Subjects represent individuals being monitored or assessed. Each subject may contain demographic information, physical measurements, and categorization tags.

Returns:

A list of Subject objects, or an empty list if none exist.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
subjects = service.subject_list()
for subject in subjects:
    print(f"{subject.name}: {subject.height}cm, {subject.weight}kg")
def create_subject(self, parameters: SubjectParameters) -> Subject:

Creates a subject profile.

Height and weight are required for biomechanical analysis. Once created, the subject can be calibrated using calibrate_subject().

Arguments:
  • parameters: The subject's profile details including name and anthropometrics.
Returns:

The newly created Subject with its assigned ID.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On validation failure, server error or unexpected response.
Example:
from modelhealth import SubjectParameters

params = SubjectParameters(
    name="John Smith",
    weight=75.0,
    height=180.0,
    birth_year=1990,
)
subject = service.create_subject(params)
print(f"Created subject with ID: {subject.id}")

# Use the subject for calibration
service.calibrate_subject(subject, session, lambda s: print(s))
def activities_for_subject( self, subject: Subject, start_index: int, count: int, sort: ActivitySort = <ActivitySort.updated_at: 'updated_at'>) -> list[Activity]:

Retrieves activities for a specific subject with pagination and sorting.

Use this to display a subject's activity history or implement paginated list interfaces.

Arguments:
  • subject: The subject whose activities to retrieve.
  • start_index: Zero-based index to start from. Use 0 for the first page.
  • count: Number of activities to retrieve per request.
  • sort: Sort order — currently only "updated_at" (most recently updated first).
Returns:

A list of Activity objects, or an empty list if none exist.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
# First page
page1 = service.activities_for_subject(
    subject, start_index=0, count=20
)

# Next page
page2 = service.activities_for_subject(
    subject, start_index=20, count=20
)
def fetch_activity(self, activity_id: str) -> Activity:

Retrieves an activity by its ID.

Use this to fetch the latest state of an activity, including its videos, results and current processing status.

Arguments:
  • activity_id: The unique identifier of the activity.
Returns:

The Activity with its current details.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NotFoundError: If the activity does not exist.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
activity = service.fetch_activity("abc123")
print(f"Activity: {activity.name or 'Unnamed'}")
print(f"Status: {activity.status}")
def update_activity(self, activity: Activity) -> Activity:

Updates an activity.

Only mutable fields (such as name) are applied on the server. Use the returned object rather than the input going forward.

Arguments:
  • activity: The Activity to update, with modified properties.
Returns:

The updated Activity as stored on the server.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NotFoundError: If the activity does not exist.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
def delete_activity(self, activity: Activity) -> None:

Deletes an activity.

Permanently removes the activity and all associated videos, results, and metadata.

Arguments:
Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NotFoundError: If the activity does not exist.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
service.delete_activity(activity)
Warning:

This operation is irreversible.

def activity_tags(self) -> list[ActivityTag]:

Retrieves all available activity tags.

Use the returned tags to populate a tag picker or validate tag values before assigning them to activities.

Returns:

A list of ActivityTag objects, or an empty list if none are configured.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
tags = service.activity_tags()
for tag in tags:
    print(f"{tag.label}: {tag.value}")
def activity_list(self, session: Session) -> list[Activity]:

Retrieves all movement activities associated with a session.

Activities represent individual recording trials and contain references to captured videos and results. Use this to review past data or fetch results for completed activities.

Arguments:
  • session: The session whose activities to retrieve.
Returns:

A list of Activity objects, or an empty list if none exist.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
activities = service.activity_list(session)
for activity in activities:
    print(f"{activity.name or activity.id}")
    print(f"  Videos: {len(activity.videos)}")
    print(f"  Results: {len(activity.results)}")
def videos_for_activity( self, activity: Activity, version: VideoVersion) -> list[bytes]:

Downloads video data for a specific activity.

Fetches all videos associated with the activity that match the specified version. Videos with invalid URLs or failed downloads are silently excluded from the result.

Arguments:
Returns:

A list of raw video bytes (one per camera). Write each element to a .mp4 file for playback. May be empty if no videos are available or all downloads fail.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
videos = service.videos_for_activity(activity, VideoVersion.synced)
for i, video in enumerate(videos):
    with open(f"video_{i}.mp4", "wb") as f:
        f.write(video)
def motion_data_for_activity( self, activity: Activity, data_types: list[MotionDataType]) -> list[MotionData]:

Downloads motion data from a processed activity.

Use this after an activity reaches ActivityStatus.ready to retrieve biomechanical result files such as kinematics, marker data, or an OpenSim model. Failed downloads are silently excluded from results.

Arguments:
  • activity: The Activity whose results to download. Must have completed processing.
  • data_types: Motion data types to fetch. Valid values: "animation", "kinematics_mot", "kinematics_csv", "markers_trc", "markers_csv", "model".
Returns:

A list of MotionData objects, one per successfully downloaded type. May be empty if no results are available or all downloads fail.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
results = service.motion_data_for_activity(
    activity, [MotionDataType.kinematics_mot, MotionDataType.animation]
)
for r in results:
    if r.type == "kinematics_mot":
        with open("kinematics.mot", "wb") as f:
            f.write(r.data)  # MOT format
    elif r.type == "animation":
        import json
        transforms = json.loads(r.data)  # JSON format
    elif r.type == "markers_trc":
        with open("markers.trc", "wb") as f:
            f.write(r.data)  # TRC format
    elif r.type == "model":
        with open("model.osim", "wb") as f:
            f.write(r.data)  # OSim format
def analysis_data_for_activity( self, activity: Activity, data_types: list[AnalysisDataType]) -> list[AnalysisData]:

Downloads result data for an activity with a completed analysis.

Call this after analysis_status() returns AnalysisStatus.completed to retrieve metrics, a report, or raw data. Failed downloads are silently excluded from results.

Arguments:
  • activity: The Activity whose analysis results to download. Must have a completed analysis.
  • data_types: Analysis result types to fetch. Valid values: "metrics" (JSON), "report" (PDF), "data" (ZIP).
Returns:

A list of AnalysisData objects, one per successfully downloaded type. May be empty if no results are available or all downloads fail.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
import json

results = service.analysis_data_for_activity(
    activity, [AnalysisDataType.metrics, AnalysisDataType.report, AnalysisDataType.data]
)
for r in results:
    if r.type == "metrics":
        metrics = json.loads(r.data)
    elif r.type == "report":
        with open("report.pdf", "wb") as f:
            f.write(r.data)
    elif r.type == "data":
        with open("data.zip", "wb") as f:
            f.write(r.data)
def activity_status( self, activity: Activity) -> Union[ActivityStatus, ActivityStatusUploading, ActivityStatusAnalyzing]:

Retrieves the current processing status of an activity.

Poll this method after recording stops to track upload and processing progress.

Returns ActivityStatusUploading when videos are uploading, ActivityStatusAnalyzing (with the analysis task) when automatic analysis is running, or an ActivityStatus enum value otherwise.

Arguments:
  • activity: The Activity to check status for.
Returns:

The current status as ActivityStatusUploading, ActivityStatusAnalyzing, or ActivityStatus.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
status = service.activity_status(activity)
if isinstance(status, ActivityStatusUploading):
    print(f"Uploading: {status.uploaded}/{status.total}")
elif status == ActivityStatus.processing:
    print("Still processing...")
elif isinstance(status, ActivityStatusAnalyzing):
    analysis_status = service.analysis_status(status.task)
elif status == ActivityStatus.ready:
    print("Activity ready for analysis")
elif status == ActivityStatus.failed:
    print("Processing failed")
def start_analysis( self, activity_type: ActivityType, activity: Activity, session: Session) -> Analysis:

Starts an analysis task for an activity that is ready for analysis.

Call this after activity_status() returns ActivityStatus.ready. Use the returned Analysis with analysis_status() to poll progress.

Arguments:
  • activity_type: The analysis algorithm to run. Use a ActivityType member.
  • activity: The Activity to analyze.
  • session: The Session containing the activity.
Returns:

An Analysis for tracking analysis progress.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: If the activity is not ready or the request fails.
Example:
task = service.start_analysis(
    ActivityType.counter_movement_jump, activity, session
)
status = service.analysis_status(task)
def analysis_status(self, task: Analysis) -> AnalysisStatus:

Retrieves the current status of an analysis task.

Poll this method after start_analysis() to monitor progress. When status reaches AnalysisStatus.completed, download results with analysis_data_for_activity().

Arguments:
Returns:

The current AnalysisStatus.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
import time

status = service.analysis_status(task)
while status == AnalysisStatus.processing:
    time.sleep(10)
    status = service.analysis_status(task)

if status == AnalysisStatus.completed:
    results = service.analysis_data_for_activity(
        activity, [AnalysisDataType.metrics, AnalysisDataType.report]
    )
elif status == AnalysisStatus.failed:
    print("Analysis failed")
def start_recording( self, name: str, session: Session, config: ActivityConfig | None = None) -> Activity:

Creates an activity and starts recording a dynamic movement trial.

Must be called after both camera and subject calibration are complete. Call stop_recording() when the subject has finished the movement.

Arguments:
  • name: A descriptive name for this activity (e.g. "cmj", "squat").
  • session: The session this activity is associated with.
  • config: Optional recording configuration.
Returns:

The newly created Activity.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: If recording cannot start (e.g. missing calibration).
Example:
activity = service.start_recording("cmj", session, ActivityConfig(ActivityType.counter_movement_jump))
# Subject performs movement...
service.stop_recording(session)
def stop_recording(self, session: Session) -> None:

Stops the active recording for a movement trial.

Call this after the subject has completed the movement. Once stopped, the recorded videos begin uploading and can be tracked with activity_status().

Arguments:
  • session: The session context to stop recording in.
Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: If there is no active recording or the request fails.
Example:
service.stop_recording(session)
def calibrate_camera( self, session: Session, checkerboard: CheckerboardDetails, status_callback: object) -> None:

Calibrates cameras using a checkerboard pattern.

Determines each camera's position and orientation in 3D space (extrinsics), enabling reconstruction of real-world movement from multiple 2D video feeds. Required once per session setup — recalibrate only if cameras are moved.

Note:

rows and columns refer to internal corners, not squares. A 5×6 board has 4 internal corner rows and 5 internal corner columns.

This method blocks until calibration completes. The status_callback is called synchronously on each status update during the operation.

Arguments:
Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: If calibration fails (e.g. pattern not detected).
Example:
from modelhealth import CheckerboardDetails, CheckerboardPlacement

details = CheckerboardDetails(
    rows=4,       # Internal corners, not squares (for 5×6 board)
    columns=5,    # Internal corners, not squares (for 5×6 board)
    square_size=35,  # Measured in millimeters
    placement=CheckerboardPlacement.perpendicular,
)
service.calibrate_camera(session, details, lambda s: print(s))
def calibrate_subject( self, subject: Subject, session: Session, status_callback: object) -> None:

Calibrates a subject by recording a neutral standing pose.

Scales the 3D biomechanical model to the subject's body size. Must be run after camera calibration and requires the subject profile to include height and weight.

Important:

The subject must stand upright, feet pointing forward, completely still and fully visible to all cameras for the duration of the recording.

This method blocks until calibration completes. The status_callback is called synchronously on each status update during the operation.

Arguments:
Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: If calibration fails.
Example:
service.calibrate_subject(subject, session, lambda s: print(s))
def prepare_archive(self, session: Session, with_videos: bool = False) -> Archive:

Starts preparing a session archive for download.

Kicks off a server-side task that packages the session data into a ZIP file. Poll archive_status() until status reaches ArchiveStatus.ready, then call archive_data() to download the ZIP file.

Arguments:
  • session: The Session to archive.
  • with_videos: If True, include raw video files in the archive. Defaults to False.
Returns:

An Archive for tracking preparation progress.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
import time

archive = service.prepare_archive(session)
status = service.archive_status(archive)
while status == ArchiveStatus.processing:
    time.sleep(5)
    status = service.archive_status(archive)

if status == ArchiveStatus.ready:
    data = service.archive_data(archive)
    with open("session.zip", "wb") as f:
        f.write(data)
def archive_status(self, archive: Archive) -> ArchiveStatus:

Retrieves the current preparation status of an archive task.

Poll this method after prepare_archive() to monitor progress. When status reaches ArchiveStatus.ready, download the archive with archive_data().

Arguments:
Returns:

The current ArchiveStatus.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error or unexpected response.
Example:
status = service.archive_status(archive)

if status == ArchiveStatus.processing:
    print("Archive being prepared...")
elif status == ArchiveStatus.ready:
    print("Archive ready to download")
elif status == ArchiveStatus.failed:
    print("Archive preparation failed")
def import_session( self, activities_json: str, subject: Subject, session_config: SessionConfig | None = None, session_meta_json: str | None = None, status_callback=None) -> Session:

Imports a set of activities into Model Health.

Performs the full import workflow: session creation, configuration, subject association, activity creation, video download and upload, processing trigger and polling until complete for each activity.

Arguments:
  • activities_json: Raw JSON string of a JSON array of activity objects.
  • subject: Subject to associate with the session.
  • session_config: Session settings. None uses defaults.
  • session_meta_json: Optional JSON string of the source session's meta field. When provided, the new session is patched with this metadata before settings are applied, preserving source data (e.g. sessionWithCalibration) needed for processing.
  • status_callback: Optional callable invoked at each step with a status value. Receives one of: "creating_session", ImportStatusCreatedSession, ImportStatusUploading, or "processing".
Returns:

The Session containing all imported activities.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: On server error, invalid JSON or processing failure.
Example:
import json

activities_json = json.dumps([{...}, {...}])  # list of activity dicts

def on_status(status):
    if isinstance(status, ImportStatusUploading):
        print(f"[{status.trial}] Uploading {status.uploaded}/{status.total}")
    else:
        print(status)

session = service.import_session(
    activities_json,
    subject,
    status_callback=on_status,
)
def archive_data(self, archive: Archive) -> bytes:

Downloads the prepared session archive as a ZIP file.

Call this after archive_status() returns ArchiveStatus.ready.

Arguments:
Returns:

Raw ZIP bytes. Write directly to a .zip file.

Raises:
  • AuthenticationError: If the API key is invalid or expired.
  • NetworkError: On connection failure or timeout.
  • ModelHealthError: If the archive is not ready or the download fails.
Example:
data = service.archive_data(archive)
with open("session.zip", "wb") as f:
    f.write(data)
class ModelHealthError(builtins.RuntimeError):

Base exception for all Model Health SDK errors.

class AuthenticationError(ModelHealthError):

Raised when the API key is missing, invalid or expired (HTTP 401/403).

class NotFoundError(ModelHealthError):

Raised when a requested resource does not exist (HTTP 404).

class NetworkError(ModelHealthError):

Raised on connection failure, timeout or DNS error.

class Session:

A parent container for a movement capture workflow.

id

Unique identifier.

user

ID of the account that owns this session.

activities

Activities associated with this session.

activities_count

Total number of activities in this session.

subject

ID of the subject associated with this session, or None if not set.

name

Display name of the session.

qrcode

URL of the QR code image for pairing cameras, or None if not available.

public

Whether the session is publicly visible.

session_name

Internal session name. Prefer name for display purposes.

class Activity:

A movement recording trial with associated videos and results.

status

Raw processing status string. Use ModelHealthService.activity_status() for the typed value.

session

ID of the parent session.

videos

Recorded video files associated with this activity.

results

Processed result files associated with this activity.

name

Display name, or None if not set.

id

Unique identifier.

class ActivityResult:

A processed result file associated with an activity.

id

Unique identifier.

tag

Result tag identifying the content type (e.g. "ik_results", "marker_data").

media

URL of the result file, or None if not yet available.

activity

ID of the parent activity.

class Video:

A recorded video file from an activity.

video_thumb

URL of the video thumbnail, or None if not yet available.

id

Unique identifier.

video

URL of the full video, or None if not yet available.

activity

ID of the parent activity.

class Subject:

An individual being monitored or assessed.

sex_at_birth

Sex assigned at birth.

weight

Weight in kilograms, or None if not set.

age

Age in years, or None if not set.

birth_year

Year of birth, or None if not set.

name

Display name.

characteristics

Freeform text describing relevant characteristics or medical conditions.

id

Unique identifier.

gender

Gender identity.

height

Height in centimetres, or None if not set.

class SubjectParameters:

Parameters for creating a new subject.

birth_year

Year of birth, or None if not provided.

height

Height in centimetres.

sex_at_birth

Sex assigned at birth.

name

Display name.

weight

Weight in kilograms.

gender

Gender identity.

characteristics

Freeform text describing relevant characteristics or medical conditions.

class ActivityTag:

A tag that can be applied to activities for categorization.

value

Machine-readable tag identifier.

label

Human-readable display label.

class MotionData:

Motion data downloaded from a processed activity.

Use type to determine how to parse data.

data

The raw file bytes. Parse according to the format implied by type.

type

The type of result data and its implicit file format.

class AnalysisData:

Analysis result data downloaded from an activity with a completed analysis.

Use type to determine how to parse data.

data

The raw file bytes. Parse according to the format implied by type.

type

The type of analysis result and its implicit file format.

class Analysis:

An active analysis task returned by start_analysis.

Pass to get_analysis_status to poll for completion.

id

Unique task identifier.

class Archive:

An active archive preparation task.

id
class MotionDataType(enum.StrEnum):

Motion data types available for download from a processed activity.

Pass one or more values to ModelHealthService.motion_data_for_activity().

Example:
results = service.motion_data_for_activity(
    activity,
    [MotionDataType.kinematics_mot, MotionDataType.animation],
)
animation = <MotionDataType.animation: 'animation'>

Visualisation transforms. JSON format.

kinematics_mot = <MotionDataType.kinematics_mot: 'kinematics_mot'>

Inverse-kinematics results. MOT format.

kinematics_csv = <MotionDataType.kinematics_csv: 'kinematics_csv'>

Inverse-kinematics results. CSV format.

markers_trc = <MotionDataType.markers_trc: 'markers_trc'>

Marker trajectories. TRC format.

markers_csv = <MotionDataType.markers_csv: 'markers_csv'>

Marker trajectories. CSV format.

model = <MotionDataType.model: 'model'>

OpenSim musculoskeletal model. OSim format.

class AnalysisDataType(enum.StrEnum):

Analysis result types available for download after a completed analysis.

Pass one or more values to ModelHealthService.analysis_data_for_activity().

Example:
results = service.analysis_data_for_activity(
    activity,
    [AnalysisDataType.metrics, AnalysisDataType.report],
)
metrics = <AnalysisDataType.metrics: 'metrics'>

Computed biomechanical metrics. JSON format.

report = <AnalysisDataType.report: 'report'>

Analysis report. PDF format.

data = <AnalysisDataType.data: 'data'>

Raw analysis data. ZIP format.

class VideoVersion(enum.StrEnum):

Video version to download for an activity.

Pass to ModelHealthService.videos_for_activity().

raw = <VideoVersion.raw: 'raw'>

Original per-camera recordings.

synced = <VideoVersion.synced: 'synced'>

Temporally-synchronised output.

class ActivitySort(enum.StrEnum):

Sort order for activity lists.

Pass to ModelHealthService.activities_for_subject().

updated_at = <ActivitySort.updated_at: 'updated_at'>

Most recently updated first.

class ActivityType(enum.StrEnum):

Analysis algorithm to run on an activity.

Pass to ModelHealthService.start_analysis().

Example:
task = service.start_analysis(
    ActivityType.counter_movement_jump, activity, session
)
counter_movement_jump = <ActivityType.counter_movement_jump: 'counter_movement_jump'>
gait = <ActivityType.gait: 'gait'>
treadmill_gait = <ActivityType.treadmill_gait: 'treadmill_gait'>
treadmill_running = <ActivityType.treadmill_running: 'treadmill_running'>
overground_running = <ActivityType.overground_running: 'overground_running'>
sit_to_stand = <ActivityType.sit_to_stand: 'sit_to_stand'>
squats = <ActivityType.squats: 'squats'>
range_of_motion = <ActivityType.range_of_motion: 'range_of_motion'>
drop_jump = <ActivityType.drop_jump: 'drop_jump'>
hop = <ActivityType.hop: 'hop'>
change_of_direction = <ActivityType.change_of_direction: 'change_of_direction'>
cut = <ActivityType.cut: 'cut'>
class ActivityConfig:

Configuration for starting a recording.

Attributes:
  • activity_type: The type of activity being recorded. When set, the corresponding analysis starts automatically once the recording is processed.
ActivityConfig(activity_type: ActivityType)
activity_type: ActivityType
class CheckerboardPlacement(enum.StrEnum):

Orientation of the calibration checkerboard relative to the ground.

Pass to CheckerboardDetails.

perpendicular = <CheckerboardPlacement.perpendicular: 'perpendicular'>

Checkerboard upright, plane perpendicular to the ground.

parallel = <CheckerboardPlacement.parallel: 'parallel'>

Checkerboard flat on the floor, plane parallel to the ground.

class ActivityStatus(enum.StrEnum):

The processing state of an activity (non-uploading variants).

Activities must reach ready before analysis can begin. When videos are still uploading, ModelHealthService.activity_status() returns ActivityStatusUploading instead.

Poll ModelHealthService.activity_status() after recording stops to track upload and processing progress. Once ready, pass the activity to ModelHealthService.start_analysis().

Example:
import time

status = service.activity_status(activity)
while status == ActivityStatus.processing:
    time.sleep(5)
    status = service.activity_status(activity)

if status == ActivityStatus.ready:
    task = service.start_analysis(ActivityType.counter_movement_jump, activity, session)
elif status == ActivityStatus.failed:
    print("Processing failed")
processing = <ActivityStatus.processing: 'processing'>

Videos have been uploaded and are being processed.

ready = <ActivityStatus.ready: 'ready'>

Processing is complete. The activity is ready for analysis.

failed = <ActivityStatus.failed: 'failed'>

Processing failed.

@dataclass(frozen=True)
class ActivityStatusUploading:

Videos are being uploaded from cameras.

Returned by ModelHealthService.activity_status() when upload is in progress. uploaded and total track progress.

Example:
status = service.activity_status(activity)
if isinstance(status, ActivityStatusUploading):
    print(f"Uploading: {status.uploaded}/{status.total}")
ActivityStatusUploading(uploaded: int, total: int)
uploaded: int

Number of videos successfully uploaded so far.

total: int

Total number of videos expected from all cameras.

@dataclass(frozen=True)
class ActivityStatusAnalyzing:

Analysis has been triggered and is in progress.

Returned by ModelHealthService.activity_status() after processing completes when an activity type was set at recording time. Pass task to ModelHealthService.analysis_status() to track analysis progress.

Example:
status = service.activity_status(activity)
if isinstance(status, ActivityStatusAnalyzing):
    analysis_status = service.analysis_status(status.task)
ActivityStatusAnalyzing(task: Analysis)
task: Analysis

The analysis task. Pass to ModelHealthService.analysis_status().

class AnalysisStatus(enum.StrEnum):

The current state of an analysis task.

Poll ModelHealthService.analysis_status() after calling ModelHealthService.start_analysis() to monitor progress. When status reaches completed, download results with ModelHealthService.analysis_data_for_activity().

Example:
import time

status = service.analysis_status(task)
while status == AnalysisStatus.processing:
    time.sleep(10)
    status = service.analysis_status(task)

if status == AnalysisStatus.completed:
    results = service.analysis_data_for_activity(activity, [AnalysisDataType.metrics])
processing = <AnalysisStatus.processing: 'processing'>

Analysis is in progress.

completed = <AnalysisStatus.completed: 'completed'>

Analysis completed successfully.

failed = <AnalysisStatus.failed: 'failed'>

Analysis failed.

class ArchiveStatus(enum.StrEnum):

The current state of an archive preparation task.

Poll ModelHealthService.archive_status() after calling ModelHealthService.prepare_archive() to monitor progress. When status reaches ready, download the archive with ModelHealthService.archive_data().

Example:
import time

archive = service.prepare_archive(session)
status = service.archive_status(archive)
while status == ArchiveStatus.processing:
    time.sleep(5)
    status = service.archive_status(archive)

if status == ArchiveStatus.ready:
    data = service.archive_data(archive)
processing = <ArchiveStatus.processing: 'processing'>

Archive is being prepared.

ready = <ArchiveStatus.ready: 'ready'>

Archive is ready to download.

failed = <ArchiveStatus.failed: 'failed'>

Archive preparation failed.

class CalibrationStatus(enum.StrEnum):

The current state of a calibration process (non-uploading, non-processing variants).

When videos are uploading, ModelHealthService.calibrate_camera() and ModelHealthService.calibrate_subject() pass CalibrationStatusUploading to the callback. When the server is processing, CalibrationStatusProcessing is passed instead.

recording = <CalibrationStatus.recording: 'recording'>

All connected cameras are actively recording.

done = <CalibrationStatus.done: 'done'>

Calibration completed successfully.

@dataclass(frozen=True)
class CalibrationStatusUploading:

Videos are being uploaded from cameras.

Passed to the status callback when upload is in progress. uploaded and total track progress.

CalibrationStatusUploading(uploaded: int, total: int)
uploaded: int

Number of videos successfully uploaded so far.

total: int

Total number of videos expected from all cameras.

@dataclass(frozen=True)
class CalibrationStatusProcessing:

The server is processing the uploaded videos.

Passed to the status callback during server-side processing. percent is the completion percentage, or None if unavailable.

CalibrationStatusProcessing(percent: int | None)
percent: int | None

Processing completion percentage (0–100), or None if unavailable.

class CheckerboardDetails:

Configuration for a calibration checkerboard pattern.

columns

Number of internal corner columns.

placement

Checkerboard orientation: "perpendicular" or "parallel".

rows

Number of internal corner rows.

square_size

Size of each square in millimetres.

@dataclass
class SessionConfig:

Settings to apply to a session before recording.

All fields have sensible defaults. Pass to ModelHealthService.import_session() (or call ModelHealthService.configure_session() directly) to override.

Example:
from modelhealth import SessionConfig, SessionFramerate, SessionDataSharing

config = SessionConfig(
    framerate=SessionFramerate.fps_60,
    data_sharing=SessionDataSharing.share_no_data,
)
session = service.import_session(activities_json, subject, session_config=config)
SessionConfig( framerate: SessionFramerate = <SessionFramerate.fps_120: 'fps_120'>, opensim_model: SessionOpenSimModel = <SessionOpenSimModel.lai_uhlrich_2022_shoulder: 'lai_uhlrich_2022_shoulder'>, scaling_setup: SessionScalingSetup = <SessionScalingSetup.upright_standing_pose: 'upright_standing_pose'>, core_engine: SessionCoreEngine = <SessionCoreEngine.v1_0: 'v1_0'>, filter_frequency: Union[FilterFrequencyDefault, FilterFrequencyHz] = None, data_sharing: SessionDataSharing = <SessionDataSharing.share_processed_data_and_identified_videos: 'share_processed_data_and_identified_videos'>)
framerate: SessionFramerate = <SessionFramerate.fps_120: 'fps_120'>

Camera frame rate. Default: fps_120.

opensim_model: SessionOpenSimModel = <SessionOpenSimModel.lai_uhlrich_2022_shoulder: 'lai_uhlrich_2022_shoulder'>

OpenSim musculoskeletal model. Default: lai_uhlrich_2022_shoulder.

scaling_setup: SessionScalingSetup = <SessionScalingSetup.upright_standing_pose: 'upright_standing_pose'>

Scaling pose. Default: upright_standing_pose.

core_engine: SessionCoreEngine = <SessionCoreEngine.v1_0: 'v1_0'>

Core processing engine version. Default: v1_0.

filter_frequency: Union[FilterFrequencyDefault, FilterFrequencyHz] = None

Low-pass filter frequency. Default: server-chosen.

data_sharing: SessionDataSharing = <SessionDataSharing.share_processed_data_and_identified_videos: 'share_processed_data_and_identified_videos'>

Data-sharing preference. Default: share_processed_data_and_identified_videos.

class SessionFramerate(enum.StrEnum):

Camera frame rate for a recording session.

Higher framerates capture fast movements more accurately but increase processing time proportionally. Collect only as much footage as needed and keep recordings under the suggested durations.

Pass to ModelHealthService.configure_session().

fps_60 = <SessionFramerate.fps_60: 'fps_60'>

60 frames per second. Suggested maximum recording duration: 60 s.

fps_120 = <SessionFramerate.fps_120: 'fps_120'>

120 frames per second (default). Suggested maximum recording duration: 30 s.

fps_240 = <SessionFramerate.fps_240: 'fps_240'>

240 frames per second. Suggested maximum recording duration: 15 s.

class SessionOpenSimModel(enum.StrEnum):

OpenSim musculoskeletal model used for biomechanical analysis.

Pass to ModelHealthService.configure_session().

lai_uhlrich_2022_shoulder = <SessionOpenSimModel.lai_uhlrich_2022_shoulder: 'lai_uhlrich_2022_shoulder'>

Full-body model with 33 degrees of freedom plus a 6-DoF shoulder complex with a scapulothoracic body and a glenohumeral joint using the ISB-recommended Y-X-Y rotation sequence. Default.

lai_uhlrich_2022 = <SessionOpenSimModel.lai_uhlrich_2022: 'lai_uhlrich_2022'>

Same full-body model as lai_uhlrich_2022_shoulder without the ISB shoulder complex.

class SessionScalingSetup(enum.StrEnum):

Pose used for subject scaling during calibration.

Pass to ModelHealthService.configure_session().

upright_standing_pose = <SessionScalingSetup.upright_standing_pose: 'upright_standing_pose'>

Subject stands straight with feet pointing forward and no bending or rotation at the hips, knees, or ankles. Default.

any_pose = <SessionScalingSetup.any_pose: 'any_pose'>

No posture assumptions. Use when the subject cannot adopt the upright standing pose. Requires all body segments to be visible by at least two cameras.

class SessionCoreEngine(enum.StrEnum):

Core processing engine version.

Pass to ModelHealthService.configure_session().

v0_2 = <SessionCoreEngine.v0_2: 'v0_2'>

Version 0.2.

v0_3 = <SessionCoreEngine.v0_3: 'v0_3'>

Version 0.3.

v1_0 = <SessionCoreEngine.v1_0: 'v1_0'>

Version 1.0 (default).

class SessionDataSharing(enum.StrEnum):

Data-sharing preference for a session.

Session data and videos are uploaded to a secure cloud server for processing. This setting controls what Model Health can use for internal development. Identified videos contain original footage with faces unblurred; de-identified videos have faces blurred. Processed data (e.g. joint angles) is always de-identified.

Pass to ModelHealthService.configure_session().

share_processed_data_and_identified_videos = <SessionDataSharing.share_processed_data_and_identified_videos: 'share_processed_data_and_identified_videos'>

Share processed data and identified videos (default).

share_processed_data_and_deidentified_videos = <SessionDataSharing.share_processed_data_and_deidentified_videos: 'share_processed_data_and_deidentified_videos'>

Share processed data and de-identified videos (faces blurred).

share_processed_data = <SessionDataSharing.share_processed_data: 'share_processed_data'>

Share processed data only. No videos are shared.

share_no_data = <SessionDataSharing.share_no_data: 'share_no_data'>

Share no data for internal development.

class FilterFrequencyDefault:

Use the server-chosen filter frequency.

class FilterFrequencyHz:

Use a specific filter frequency in Hz.

value

Frequency in Hz.

@dataclass(frozen=True)
class ImportStatusCreatedSession:

Session created successfully during a session import.

Passed to the status callback during ModelHealthService.import_session() once the new session has been created on the server.

ImportStatusCreatedSession(session_id: str)
session_id: str

ID of the newly created session.

@dataclass(frozen=True)
class ImportStatusUploading:

A video is being uploaded during a session import.

Passed to the status callback during ModelHealthService.import_session() while videos are being transferred. trial names the current trial; uploaded and total track progress within that trial.

ImportStatusUploading(trial: str, uploaded: int, total: int)
trial: str

Name of the trial whose video is being uploaded.

uploaded: int

Number of videos successfully uploaded so far.

total: int

Total number of videos to upload.