quickik

1from .quickik import *
2
3__doc__ = quickik.__doc__
4if hasattr(quickik, "__all__"):
5    __all__ = quickik.__all__
class KinematicTree:

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

def from_json_str(json_str):

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

def from_json_file(path):

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

n_dofs

Total number of rotational DOFs across all joints.

n_joints

Number of joints in the tree.

class State:

The pose being solved for.

def neutral_pose(kinematic_tree):

Creates a new state at the neutral pose for kinematic_tree.

dof_angles

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

root_pos

Position of the root joint in world coordinates.

root_rot

(w, x, y, z).

class KeypointObservation:

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

def missing():

Not observed this frame, e.g. occluded.

def position_3d(pos, weight):

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

def position_2d(pos, weight):

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.

class Camera:

A pinhole camera mapper for 2D keypoint observations.

cy

Principal point (y).

cx

Principal point (x).

world2cam_pos

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

fy

Focal length in pixels (y).

world2cam_rot_mat

Row-major 3x3, as 9 values.

fx

Focal length in pixels (x).

class XYView:

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

class SolverConfig:

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

angle_tolerance

Angle-space counterpart to position_tolerance, in radians.

position_tolerance

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.

n_iterations

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

weight

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

damping

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

class Solver:

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).

def solve(self, /, state, observations):

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).

mapper

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

config

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

class SequenceSolver:

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).

def solve_frame(self, /, observations):

Solves the next frame, warm-started from the current pose, and returns the converged state (also available as .state).

def solve_sequence(self, /, sequence):

Solves every frame in sequence (a list of per-frame observation lists) in order, each warm-started from the previous one; returns the converged pose after each frame.

state

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

config

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

mapper

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

class ParallelSolveConfig:

Configuration for [solve_sequence_segmented_parallel].

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

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: positions is (n_frames, n_keypoints, 3) and weights is (n_frames, n_keypoints), both in kinematic_tree.joints order; a keypoint with weight <= 0 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).