Document Python API and ship package stubs
This commit is contained in:
@@ -17,6 +17,7 @@ find_package(nanobind CONFIG REQUIRED)
|
|||||||
set(RPT_PYTHON_PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/rpt")
|
set(RPT_PYTHON_PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/rpt")
|
||||||
file(MAKE_DIRECTORY "${RPT_PYTHON_PACKAGE_DIR}")
|
file(MAKE_DIRECTORY "${RPT_PYTHON_PACKAGE_DIR}")
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/__init__.py" "${RPT_PYTHON_PACKAGE_DIR}/__init__.py" COPYONLY)
|
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/__init__.py" "${RPT_PYTHON_PACKAGE_DIR}/__init__.py" COPYONLY)
|
||||||
|
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/__init__.pyi" "${RPT_PYTHON_PACKAGE_DIR}/__init__.pyi" COPYONLY)
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/_helpers.py" "${RPT_PYTHON_PACKAGE_DIR}/_helpers.py" COPYONLY)
|
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/_helpers.py" "${RPT_PYTHON_PACKAGE_DIR}/_helpers.py" COPYONLY)
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/py.typed" "${RPT_PYTHON_PACKAGE_DIR}/py.typed" COPYONLY)
|
configure_file("${PROJECT_SOURCE_DIR}/src/rpt/py.typed" "${RPT_PYTHON_PACKAGE_DIR}/py.typed" COPYONLY)
|
||||||
|
|
||||||
@@ -40,4 +41,5 @@ nanobind_add_stub(rpt_core_stub
|
|||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS rpt_core_ext LIBRARY DESTINATION rpt)
|
install(TARGETS rpt_core_ext LIBRARY DESTINATION rpt)
|
||||||
|
install(FILES "${RPT_PYTHON_PACKAGE_DIR}/__init__.pyi" DESTINATION rpt)
|
||||||
install(FILES "${RPT_PYTHON_PACKAGE_DIR}/_core.pyi" DESTINATION rpt)
|
install(FILES "${RPT_PYTHON_PACKAGE_DIR}/_core.pyi" DESTINATION rpt)
|
||||||
|
|||||||
+133
-7
@@ -21,30 +21,83 @@ from ._core import (
|
|||||||
PreviousPoseMatch,
|
PreviousPoseMatch,
|
||||||
ProposalGroupDebug,
|
ProposalGroupDebug,
|
||||||
TriangulationTrace,
|
TriangulationTrace,
|
||||||
build_pair_candidates,
|
build_pair_candidates as _build_pair_candidates,
|
||||||
filter_pairs_with_previous_poses,
|
filter_pairs_with_previous_poses as _filter_pairs_with_previous_poses,
|
||||||
make_camera,
|
make_camera as _make_camera,
|
||||||
triangulate_debug,
|
triangulate_debug as _triangulate_debug,
|
||||||
triangulate_poses,
|
triangulate_poses as _triangulate_poses,
|
||||||
triangulate_with_report,
|
triangulate_with_report as _triangulate_with_report,
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
|
|
||||||
from ._helpers import CameraLike, PoseViewLike
|
from ._helpers import CameraLike, CameraModelLike, Matrix3x3Like, PoseViewLike, VectorLike
|
||||||
|
|
||||||
|
PoseArray2D = npt.NDArray[np.float32]
|
||||||
|
PoseArray3D = npt.NDArray[np.float32]
|
||||||
|
PersonCountArray = npt.NDArray[np.uint32]
|
||||||
|
TrackIdArray = npt.NDArray[np.int64]
|
||||||
|
|
||||||
|
|
||||||
|
Camera.__doc__ = """Immutable camera calibration with precomputed projection cache fields."""
|
||||||
|
TriangulationConfig.__doc__ = """Stable scene configuration used for triangulation."""
|
||||||
|
TriangulationOptions.__doc__ = """Score and grouping thresholds used by triangulation."""
|
||||||
|
TriangulationResult.__doc__ = """Tracked triangulation output containing poses and association metadata."""
|
||||||
|
AssociationReport.__doc__ = """Track-association summary for a tracked triangulation call."""
|
||||||
|
TriangulationTrace.__doc__ = """Full debug trace for triangulation, including pair, grouping, and association stages."""
|
||||||
|
|
||||||
|
|
||||||
def convert_cameras(cameras: "Sequence[CameraLike]") -> list[Camera]:
|
def convert_cameras(cameras: "Sequence[CameraLike]") -> list[Camera]:
|
||||||
|
"""Normalize mapping-like camera inputs into immutable bound `Camera` instances."""
|
||||||
|
|
||||||
from ._helpers import convert_cameras as _convert_cameras
|
from ._helpers import convert_cameras as _convert_cameras
|
||||||
|
|
||||||
return _convert_cameras(cameras)
|
return _convert_cameras(cameras)
|
||||||
|
|
||||||
|
|
||||||
|
def make_camera(
|
||||||
|
name: str,
|
||||||
|
K: "Matrix3x3Like",
|
||||||
|
DC: "VectorLike",
|
||||||
|
R: "Matrix3x3Like",
|
||||||
|
T: "Sequence[Sequence[float]]",
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
|
model: "CameraModel | CameraModelLike",
|
||||||
|
) -> Camera:
|
||||||
|
"""Create an immutable camera and precompute its cached projection fields."""
|
||||||
|
|
||||||
|
from ._helpers import _coerce_camera_model, _coerce_distortion
|
||||||
|
|
||||||
|
camera_model = _coerce_camera_model(model)
|
||||||
|
return _make_camera(
|
||||||
|
name,
|
||||||
|
K,
|
||||||
|
_coerce_distortion(DC, camera_model),
|
||||||
|
R,
|
||||||
|
T,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
camera_model,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_pair_candidates(
|
||||||
|
poses_2d: "PoseArray2D",
|
||||||
|
person_counts: "PersonCountArray",
|
||||||
|
) -> list[PairCandidate]:
|
||||||
|
"""Enumerate all cross-view person pairs implied by the padded 2D pose batch."""
|
||||||
|
|
||||||
|
return _build_pair_candidates(poses_2d, person_counts)
|
||||||
|
|
||||||
|
|
||||||
def pack_poses_2d(
|
def pack_poses_2d(
|
||||||
views: "Sequence[PoseViewLike]", *, joint_count: int | None = None
|
views: "Sequence[PoseViewLike]", *, joint_count: int | None = None
|
||||||
) -> "tuple[npt.NDArray[np.float32], npt.NDArray[np.uint32]]":
|
) -> "tuple[npt.NDArray[np.float32], npt.NDArray[np.uint32]]":
|
||||||
|
"""Pack ragged per-view pose detections into the padded tensor expected by the core API."""
|
||||||
|
|
||||||
from ._helpers import pack_poses_2d as _pack_poses_2d
|
from ._helpers import pack_poses_2d as _pack_poses_2d
|
||||||
|
|
||||||
return _pack_poses_2d(views, joint_count=joint_count)
|
return _pack_poses_2d(views, joint_count=joint_count)
|
||||||
@@ -58,6 +111,8 @@ def make_triangulation_config(
|
|||||||
min_match_score: float = 0.95,
|
min_match_score: float = 0.95,
|
||||||
min_group_size: int = 1,
|
min_group_size: int = 1,
|
||||||
) -> TriangulationConfig:
|
) -> TriangulationConfig:
|
||||||
|
"""Build a triangulation config from cameras, room parameters, and joint names."""
|
||||||
|
|
||||||
from ._helpers import make_triangulation_config as _make_triangulation_config
|
from ._helpers import make_triangulation_config as _make_triangulation_config
|
||||||
|
|
||||||
return _make_triangulation_config(
|
return _make_triangulation_config(
|
||||||
@@ -69,6 +124,77 @@ def make_triangulation_config(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_pairs_with_previous_poses(
|
||||||
|
poses_2d: "PoseArray2D",
|
||||||
|
person_counts: "PersonCountArray",
|
||||||
|
config: TriangulationConfig,
|
||||||
|
previous_poses_3d: "PoseArray3D",
|
||||||
|
previous_track_ids: "TrackIdArray",
|
||||||
|
) -> PreviousPoseFilterDebug:
|
||||||
|
"""Filter raw cross-view pairs against caller-owned previous 3D tracks."""
|
||||||
|
|
||||||
|
return _filter_pairs_with_previous_poses(
|
||||||
|
poses_2d,
|
||||||
|
person_counts,
|
||||||
|
config,
|
||||||
|
previous_poses_3d,
|
||||||
|
previous_track_ids,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def triangulate_debug(
|
||||||
|
poses_2d: "PoseArray2D",
|
||||||
|
person_counts: "PersonCountArray",
|
||||||
|
config: TriangulationConfig,
|
||||||
|
previous_poses_3d: "PoseArray3D | None" = None,
|
||||||
|
previous_track_ids: "TrackIdArray | None" = None,
|
||||||
|
) -> TriangulationTrace:
|
||||||
|
"""Run triangulation and return the full debug trace.
|
||||||
|
|
||||||
|
If previous-frame 3D tracks are supplied, `previous_track_ids` must be supplied as an
|
||||||
|
aligned `int64` array of the same length.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if previous_poses_3d is None:
|
||||||
|
return _triangulate_debug(poses_2d, person_counts, config)
|
||||||
|
if previous_track_ids is None:
|
||||||
|
raise ValueError("previous_track_ids is required when previous_poses_3d is provided.")
|
||||||
|
return _triangulate_debug(poses_2d, person_counts, config, previous_poses_3d, previous_track_ids)
|
||||||
|
|
||||||
|
|
||||||
|
def triangulate_poses(
|
||||||
|
poses_2d: "PoseArray2D",
|
||||||
|
person_counts: "PersonCountArray",
|
||||||
|
config: TriangulationConfig,
|
||||||
|
) -> "PoseArray3D":
|
||||||
|
"""Triangulate a frame into anonymous 3D poses without track association."""
|
||||||
|
|
||||||
|
return _triangulate_poses(poses_2d, person_counts, config)
|
||||||
|
|
||||||
|
|
||||||
|
def triangulate_with_report(
|
||||||
|
poses_2d: "PoseArray2D",
|
||||||
|
person_counts: "PersonCountArray",
|
||||||
|
config: TriangulationConfig,
|
||||||
|
previous_poses_3d: "PoseArray3D",
|
||||||
|
previous_track_ids: "TrackIdArray",
|
||||||
|
) -> TriangulationResult:
|
||||||
|
"""Triangulate a frame and return caller-owned track association results.
|
||||||
|
|
||||||
|
The previous-frame poses and IDs are treated as immutable caller state. The returned
|
||||||
|
association report classifies each final pose as matched, new, or ambiguous, and lists
|
||||||
|
unmatched previous tracks as deletion candidates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return _triangulate_with_report(
|
||||||
|
poses_2d,
|
||||||
|
person_counts,
|
||||||
|
config,
|
||||||
|
previous_poses_3d,
|
||||||
|
previous_track_ids,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Camera",
|
"Camera",
|
||||||
"CameraModel",
|
"CameraModel",
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
from collections.abc import Sequence
|
||||||
|
from typing import TypeAlias, overload
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
from ._core import (
|
||||||
|
AssociationReport,
|
||||||
|
AssociationStatus,
|
||||||
|
Camera,
|
||||||
|
CameraModel,
|
||||||
|
CoreProposalDebug,
|
||||||
|
FinalPoseAssociationDebug,
|
||||||
|
FullProposalDebug,
|
||||||
|
GroupingDebug,
|
||||||
|
MergeDebug,
|
||||||
|
PairCandidate,
|
||||||
|
PreviousPoseFilterDebug,
|
||||||
|
PreviousPoseMatch,
|
||||||
|
ProposalGroupDebug,
|
||||||
|
TriangulationConfig,
|
||||||
|
TriangulationOptions,
|
||||||
|
TriangulationResult,
|
||||||
|
TriangulationTrace,
|
||||||
|
)
|
||||||
|
from ._helpers import CameraLike, CameraModelLike, Matrix3x3Like, PoseViewLike, RoomParamsLike, VectorLike
|
||||||
|
|
||||||
|
PoseArray2D: TypeAlias = npt.NDArray[np.float32]
|
||||||
|
PoseArray3D: TypeAlias = npt.NDArray[np.float32]
|
||||||
|
PersonCountArray: TypeAlias = npt.NDArray[np.uint32]
|
||||||
|
TrackIdArray: TypeAlias = npt.NDArray[np.int64]
|
||||||
|
|
||||||
|
|
||||||
|
def convert_cameras(cameras: Sequence[CameraLike]) -> list[Camera]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def make_camera(
|
||||||
|
name: str,
|
||||||
|
K: Matrix3x3Like,
|
||||||
|
DC: VectorLike,
|
||||||
|
R: Matrix3x3Like,
|
||||||
|
T: Sequence[Sequence[float]],
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
|
model: CameraModel | CameraModelLike,
|
||||||
|
) -> Camera: ...
|
||||||
|
|
||||||
|
|
||||||
|
def build_pair_candidates(
|
||||||
|
poses_2d: PoseArray2D,
|
||||||
|
person_counts: PersonCountArray,
|
||||||
|
) -> list[PairCandidate]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def pack_poses_2d(
|
||||||
|
views: Sequence[PoseViewLike],
|
||||||
|
*,
|
||||||
|
joint_count: int | None = None,
|
||||||
|
) -> tuple[npt.NDArray[np.float32], npt.NDArray[np.uint32]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def make_triangulation_config(
|
||||||
|
cameras: Sequence[CameraLike],
|
||||||
|
roomparams: RoomParamsLike,
|
||||||
|
joint_names: Sequence[str],
|
||||||
|
*,
|
||||||
|
min_match_score: float = 0.95,
|
||||||
|
min_group_size: int = 1,
|
||||||
|
) -> TriangulationConfig: ...
|
||||||
|
|
||||||
|
|
||||||
|
def filter_pairs_with_previous_poses(
|
||||||
|
poses_2d: PoseArray2D,
|
||||||
|
person_counts: PersonCountArray,
|
||||||
|
config: TriangulationConfig,
|
||||||
|
previous_poses_3d: PoseArray3D,
|
||||||
|
previous_track_ids: TrackIdArray,
|
||||||
|
) -> PreviousPoseFilterDebug: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def triangulate_debug(
|
||||||
|
poses_2d: PoseArray2D,
|
||||||
|
person_counts: PersonCountArray,
|
||||||
|
config: TriangulationConfig,
|
||||||
|
) -> TriangulationTrace: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def triangulate_debug(
|
||||||
|
poses_2d: PoseArray2D,
|
||||||
|
person_counts: PersonCountArray,
|
||||||
|
config: TriangulationConfig,
|
||||||
|
previous_poses_3d: PoseArray3D,
|
||||||
|
previous_track_ids: TrackIdArray,
|
||||||
|
) -> TriangulationTrace: ...
|
||||||
|
|
||||||
|
|
||||||
|
def triangulate_poses(
|
||||||
|
poses_2d: PoseArray2D,
|
||||||
|
person_counts: PersonCountArray,
|
||||||
|
config: TriangulationConfig,
|
||||||
|
) -> PoseArray3D: ...
|
||||||
|
|
||||||
|
|
||||||
|
def triangulate_with_report(
|
||||||
|
poses_2d: PoseArray2D,
|
||||||
|
person_counts: PersonCountArray,
|
||||||
|
config: TriangulationConfig,
|
||||||
|
previous_poses_3d: PoseArray3D,
|
||||||
|
previous_track_ids: TrackIdArray,
|
||||||
|
) -> TriangulationResult: ...
|
||||||
|
|
||||||
|
|
||||||
|
__all__: list[str]
|
||||||
Reference in New Issue
Block a user