Skip to content

Python API reference

quickik's Python bindings mirror the Rust crate's layout: a KinematicTree loaded once, State/KeypointObservation values fed in per frame, and a Solver (or SequenceSolver/solve_sequence_segmented_parallel for whole sequences) that ties them together.

KinematicTree

A kinematic tree (body plan), loaded from JSON.

n_dofs property

Total number of rotational DOFs across all joints.

n_joints property

Number of joints in the tree.

from_json_file(path) staticmethod

Same as from_json_str, but reads the JSON from a file at path.

from_json_str(json_str) staticmethod

Parses a body plan from a JSON string. Raises ValueError if the JSON is malformed or the body plan is invalid (e.g. no single root joint).

State

The pose being solved for.

dof_angles property

Angles of all joint DOFs, in body-plan order.

root_pos property

Position of the root joint in world coordinates.

root_rot property

(w, x, y, z).

__repr__() method descriptor

Return repr(self).

neutral_pose(kinematic_tree) staticmethod

Creates a new state at the neutral pose for kinematic_tree.

KeypointObservation

Observation of a single keypoint: missing(), position_3d(pos, weight), or position_2d(pos, weight).

__repr__() method descriptor

Return repr(self).

missing() staticmethod

Not observed this frame, e.g. occluded.

position_2d(pos, weight) staticmethod

A 2D position in whatever space the consuming Solver's mapper expects (e.g. camera pixel coordinates). Raises ValueError if pos doesn't have exactly 2 elements.

position_3d(pos, weight) staticmethod

A 3D world position, e.g. triangulated from multiple calibrated cameras. Raises ValueError if pos doesn't have exactly 3 elements.

Camera(fx, fy, cx, cy, world2cam_pos, world2cam_rot_mat)

A pinhole camera mapper for 2D keypoint observations.

cx property

Principal point (x).

cy property

Principal point (y).

fx property

Focal length in pixels (x).

fy property

Focal length in pixels (y).

world2cam_pos property

World-to-camera translation, as (x, y, z).

world2cam_rot_mat property

Row-major 3x3, as 9 values.

__repr__() method descriptor

Return repr(self).

XYView

A mapper for 2D keypoints already reprojected to physical X-Y coordinates.

SolverConfig(n_iterations=10, neutral_weight=0.001, position_tolerance=0.001, angle_tolerance=0.001, damping=1e-06)

Configuration for the inverse kinematics solver. Does not include the mapper; see [Solver]'s and SequenceSolver's mapper argument.

angle_tolerance property

Angle-space counterpart to position_tolerance, in radians.

damping property

Levenberg-Marquardt damping added to the normal equations' diagonal, for numerical stability only; keep it very small (e.g. 1e-6).

n_iterations property

Number of Gauss-Newton steps per solve call. Also the cap on early termination: see position_tolerance/angle_tolerance.

neutral_weight property

Weight pulling every joint angle toward the neutral pose. Improves robustness to missing/noisy keypoints, at the cost of some bias.

position_tolerance property

Stop iterating early once an update step's largest root-position component drops below this value, and the largest angle update drops below angle_tolerance. 0 disables early termination.

Solver(kinematic_tree, config, mapper=None)

The inverse kinematics solver.

mapper is a Camera, an XYView, or None (the default, for 3D-only observations); it's fixed for this Solver's lifetime, mirroring Rust's Solver<M> generic parameter. There's no setter, only the read-only mapper property.

config is a live, shared handle: solver.config always returns the same Python SolverConfig object, so mutating it (e.g. solver.config.n_iterations = 5) takes effect on the next solve call, mirroring Rust's pub config field. Assigning solver.config = other re-points it at other (which then also mutates in place, same as any other Python object reference).

config property

The live config; see the class docstring for mutation semantics.

mapper property

Fixed at construction (read-only); mutating the returned object has no effect on this solver.

solve(state, observations) method descriptor

Runs up to config.n_iterations Gauss-Newton steps in place on state, given one KeypointObservation per joint (in kinematic_tree.joints order; use KeypointObservation.missing() for keypoints not observed this frame). Raises ValueError if len(observations) != kinematic_tree.n_joints.

SequenceSolver(kinematic_tree, config, mapper=None)

Solves a continuous sequence of frames for a single tracked body, warm starting each frame from the previous frame's converged pose. See Solver for mapper and config semantics (both flattened here from Rust's nested solver.solver).

config property

The live config, shared with the underlying Solver; see Solver's docstring for mutation semantics.

mapper property

Fixed at construction (read-only); mutating the returned object has no effect on this solver.

state property

The most recently converged pose (a snapshot; mutating it has no effect on the solver).

solve_frame(observations) method descriptor

Solves the next frame, warm-started from the current pose, and returns the converged state (also available as .state). Raises ValueError if len(observations) != kinematic_tree.n_joints.

solve_sequence(positions, weights) method descriptor

Solves every frame in order, each warm-started from the previous one; returns the converged pose after each frame. weights is (n_frames, n_joints); positions is (n_frames, n_joints, 3) if this solver has no mapper (3D observations), or (n_frames, n_joints, 2) if it does (2D observations, projected by that mapper) -- see mapper. Both are in kinematic_tree.joints order; a keypoint with weight <= 0 (or NaN) is treated as missing. Given as raw arrays rather than a list of per-frame KeypointObservation lists so this never constructs one Python object per keypoint per frame, which otherwise dominates call overhead for long recordings. Any dtype is accepted and cast to float32 (e.g. the common case of a float64 array), following NumPy's own casting rules.

ParallelSolveConfig(segment_len, overlap_len, overlap_tolerance, n_workers)

Configuration for [solve_sequence_segmented_parallel].

for_recording(total_len) staticmethod

A ParallelSolveConfig that spreads total_len frames evenly across every available core: one segment per core, total_len / n_workers frames each (plus a fixed default overlap). For finer control over cold-start frequency (how often a segment restarts from the neutral pose, trading accuracy for finer-grained parallelism), build a ParallelSolveConfig directly instead.

solve_sequence_segmented_parallel(kinematic_tree, config, positions, weights, parallel_config, mapper=None) builtin

Solves a single long sequence in parallel by splitting it into slightly overlapping segments, each solved on its own thread. mapper is a Camera, an XYView, or None; see Solver.

Observations are given as raw arrays rather than a list of per-frame KeypointObservation lists: weights is (n_frames, n_keypoints); positions is (n_frames, n_keypoints, 3) if mapper is None (3D observations), or (n_frames, n_keypoints, 2) if it's set (2D observations, projected by that mapper). Both are in kinematic_tree.joints order; a keypoint with weight <= 0 (or NaN) is treated as missing. This avoids constructing one Python KeypointObservation object per keypoint per frame, which otherwise dominates call overhead for large sequences (e.g. a whole recording's worth of frames in one call). Any dtype is accepted and cast to float32 (e.g. the common case of a float64 array), following NumPy's own casting rules.