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.