Skip to content

Conversation

@GerrySant
Copy link

This PR adds support for converting AlphaPose WholeBody (133 keypoints) JSON outputs
into pose-format (.pose) files.

  • Introduces a new json_to_pose CLI tool
  • Adds AlphaPose WholeBody-133 parser and keypoint reordering utilities
  • Supports optional metadata extraction from JSON or original RGB video
  • Documents the conversion workflow in the README

This enables seamless integration of AlphaPose outputs into pose-format pipelines,
which is particularly useful for sign language processing workflows.

- Introduce json_to_pose CLI tool for converting AlphaPose JSON outputs to .pose
- Add AlphaPose WholeBody (133 keypoints) parser and keypoint reordering utility
- Support optional metadata extraction (fps, width, height) from JSON or original video
- Register json_to_pose as an installable console script
- Document JSON → .pose conversion workflow in README
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new CLI tool for converting AlphaPose WholeBody-133 JSON outputs into pose-format (.pose) files, enabling seamless integration of AlphaPose outputs into pose-format pipelines.

Changes:

  • Added json_to_pose CLI tool for converting JSON pose estimation outputs to .pose format
  • Implemented AlphaPose WholeBody-133 parser with keypoint reordering utilities
  • Added support for optional metadata extraction from JSON or original RGB video

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 7 comments.

File Description
src/python/pyproject.toml Registers the new json_to_pose CLI command
src/python/pose_format/utils/alphapose.py Implements AlphaPose WholeBody-133 parsing, components definition, and keypoint reordering
src/python/pose_format/bin/json_to_pose.py Provides the CLI tool for converting JSON to .pose format with metadata extraction
README.md Documents the new conversion workflow and usage examples

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

PoseHeaderComponent(
name="BODY",
points=BODY_POINTS,
limbs= map_limbs(BODY_POINTS, BODY_LIMBS_NAMES),
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace after the comma on line 98. This is inconsistent with line 114 and 122 which don't have trailing spaces.

Suggested change
limbs= map_limbs(BODY_POINTS, BODY_LIMBS_NAMES),
limbs= map_limbs(BODY_POINTS, BODY_LIMBS_NAMES),

Copilot uses AI. Check for mistakes.
PoseHeaderComponent(
name="LEFT_HAND",
points=LEFT_HAND_POINTS,
limbs= map_limbs(LEFT_HAND_POINTS, LEFT_HAND_LIMBS_NAMES),
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace after the comma on line 114. This is inconsistent with line 122 which doesn't have a trailing space.

Copilot uses AI. Check for mistakes.
from ..numpy.pose_body import NumPyPoseBody
from ..pose import Pose
from ..pose_header import PoseHeader, PoseHeaderComponent, PoseHeaderDimensions
from pose_format.utils.openpose import hand_colors
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import "hand_colors" from "pose_format.utils.openpose" is unused and should be removed.

Suggested change
from pose_format.utils.openpose import hand_colors

Copilot uses AI. Check for mistakes.
"right_big_toe","right_small_toe","right_heel",
]

FACE_POINTS = [f"face-{i}" for i in range(68)]
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming convention: face points use hyphens ("face-0", "face-1") while hand points use underscores ("left_hand_0", "right_hand_0"). This should be consistent for maintainability.

Suggested change
FACE_POINTS = [f"face-{i}" for i in range(68)]
FACE_POINTS = [f"face_{i}" for i in range(68)]

Copilot uses AI. Check for mistakes.
Comment on lines +204 to +205
video_path : string
Path to input video file.
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter name in the docstring is "video_path" but the actual parameter is "input_path". The docstring should be updated to match the function signature.

Suggested change
video_path : string
Path to input video file.
input_path : string
Path to input AlphaPose JSON file.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is probably wrong, but it probably means the docstring is not good

Comment on lines +200 to +210
Loads alphapose_wholebody pose data

Parameters
----------
video_path : string
Path to input video file.

Returns
-------
Pose
Loaded pose data with header and body
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring is incomplete - it doesn't document the parameters "version", "fps", "width", "height", or "depth".

Suggested change
Loads alphapose_wholebody pose data
Parameters
----------
video_path : string
Path to input video file.
Returns
-------
Pose
Loaded pose data with header and body
Loads alphapose_wholebody pose data.
Parameters
----------
input_path : str
Path to the input AlphaPose JSON file.
version : float, optional
Pose format version stored in the :class:`PoseHeader`. Defaults to 0.2.
fps : float, optional
Frames per second used in the :class:`NumPyPoseBody`. May be overridden by JSON metadata. Defaults to 24.
width : int, optional
Frame width in pixels for :class:`PoseHeaderDimensions`. May be overridden by JSON metadata. Defaults to 1000.
height : int, optional
Frame height in pixels for :class:`PoseHeaderDimensions`. May be overridden by JSON metadata. Defaults to 1000.
depth : int, optional
Depth dimension for :class:`PoseHeaderDimensions`. Defaults to 0.
Returns
-------
Pose
Loaded pose data with header and body.

Copilot uses AI. Check for mistakes.
The library supports converting pose estimation outputs stored as `.json` files into the `.pose` format via `json_to_pose` utility.

> **Note**
> - At the moment, `json_to_pose` only supports [AlphaPose](https://github.com/MVIG-SJTU/AlphaPose) models with **133 keypoints** JSON files with 133 keypoints.
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated text in the README: "JSON files with 133 keypoints" appears twice in the same sentence.

Suggested change
> - At the moment, `json_to_pose` only supports [AlphaPose](https://github.com/MVIG-SJTU/AlphaPose) models with **133 keypoints** JSON files with 133 keypoints.
> - At the moment, `json_to_pose` only supports [AlphaPose](https://github.com/MVIG-SJTU/AlphaPose) models whose JSON outputs contain **133 keypoints**.

Copilot uses AI. Check for mistakes.
…ils)

- Move AlphaPose point/limb definitions to module-level constants and rename components builder to get_alphapose_components()\n- Extend KnownPoseFormat and format detection to include alphapose\n- Add alphapose handling in pose_hide_legs, pose_shoulders, hands_components, get_hand_wrist_index, and get_body_hand_wrist_index\n- Update tests to cover leg removal and detected format for alphapose
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +167 to +171
if known_pose_format == "sapiens":
return [
pose_header.get_point_index("LEFT_HAND", "hand_9"),
pose_header.get_point_index("RIGHT_HAND", "hand_9"),
]
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hands_indexes() contains a branch for known_pose_format == "sapiens", but detect_known_pose_format() cannot return "sapiens" (and KnownPoseFormat doesn’t include it). This code is currently unreachable and also inconsistent with the declared type. Either remove this branch, or add full "sapiens" support (update KnownPoseFormat and detect_known_pose_format()).

Suggested change
if known_pose_format == "sapiens":
return [
pose_header.get_point_index("LEFT_HAND", "hand_9"),
pose_header.get_point_index("RIGHT_HAND", "hand_9"),
]

Copilot uses AI. Check for mistakes.
Comment on lines +79 to +84
Creates a list of alphapose components.

Returns
-------
list of PoseHeaderComponent
List of holistic components.
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_alphapose_components() docstring says it returns a "List of holistic components", which is incorrect/misleading. Please update the docstring to refer to AlphaPose components (and ensure the return description matches the actual component set).

Suggested change
Creates a list of alphapose components.
Returns
-------
list of PoseHeaderComponent
List of holistic components.
Creates a list of AlphaPose components.
Returns
-------
list of PoseHeaderComponent
List of AlphaPose components (BODY, FACE, LEFT_HAND, RIGHT_HAND).

Copilot uses AI. Check for mistakes.
Comment on lines +212 to +213
print("Loading pose with alphapose_wholebody...")

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This utility function prints directly to stdout (print("Loading pose with alphapose_wholebody...")). In a library module this makes it hard for downstream users to control logging. Consider removing the print or switching to the project’s logging approach (or gating it behind a verbose flag).

Copilot uses AI. Check for mistakes.
Comment on lines +266 to +268
assert len(flat) == 133 * 3, \
f"ERROR: Expected 133 keypoints (399 values), but got {len(flat)} values. " \
f"This converter only supports AlphaPose WholeBody-133."
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_keypoints_and_confidence() uses an assert for input validation. Assertions can be disabled with python -O, which would skip this check and then fail later in less clear ways. Please raise a ValueError (or a domain-specific exception) when the keypoint vector length is not 133 * 3.

Suggested change
assert len(flat) == 133 * 3, \
f"ERROR: Expected 133 keypoints (399 values), but got {len(flat)} values. " \
f"This converter only supports AlphaPose WholeBody-133."
if len(flat) != 133 * 3:
raise ValueError(
f"ERROR: Expected 133 keypoints (399 values), but got {len(flat)} values. "
f"This converter only supports AlphaPose WholeBody-133."
)

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +240
def load_alphapose_wholebody_from_json(input_path: str,
version: float = 0.2,
fps: float = 24,
width=1000,
height=1000,
depth=0) -> Pose:
"""
Loads alphapose_wholebody pose data

Parameters
----------
video_path : string
Path to input video file.

Returns
-------
Pose
Loaded pose data with header and body
"""
print("Loading pose with alphapose_wholebody...")

# Load frames + optional metadata
frames, metadata = load_alphapose_json(input_path)

# Override fps/width/height ONLY if metadata exists
if metadata is not None:
if metadata.get("fps") is not None:
fps = metadata["fps"]
if metadata.get("width") is not None:
width = metadata["width"]
if metadata.get("height") is not None:
height = metadata["height"]

frames_xy = []
frames_conf = []

# Parse and reorder all frames
for item in frames:
xy, conf = parse_keypoints_and_confidence(item["keypoints"])
xy_ord, conf_ord = reorder_133_kpts(xy, conf)

frames_xy.append(xy_ord)
frames_conf.append(conf_ord)

# Convert to arrays
xy_data = np.stack(frames_xy, axis=0) # (num_frames, num_keypoints, 2)
conf_data = np.stack(frames_conf, axis=0) # (num_frames, num_keypoints)

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage: this new AlphaPose WholeBody-133 JSON loader/reordering logic isn’t covered by unit tests. There are existing loader tests (e.g. utils/openpose_test.py)—please add similar tests that (1) validate JSON parsing for both supported formats, (2) verify output shapes, and (3) verify keypoint reordering/metadata override behavior.

Copilot uses AI. Check for mistakes.
from pose_format.utils.openpose import BODY_POINTS as OPENPOSE_BODY_POINTS
from pose_format.utils.openpose_135 import OpenPose_Components as OpenPose135_Components
from pose_format.utils.alphapose import get_alphapose_components
from pose_format.utils.alphapose import BODY_POINTS as ALPHAPOSE_BODY_POINTS
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALPHAPOSE_BODY_POINTS is imported but never used in this module. Please remove the unused import to avoid lint/type-check noise.

Suggested change
from pose_format.utils.alphapose import BODY_POINTS as ALPHAPOSE_BODY_POINTS

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants