Add PoseData to pose_keypoint converter node

This commit is contained in:
grmchn
2025-12-13 15:10:42 +09:00
parent 16cda84ffa
commit 3f3fcf3a28
8 changed files with 1007 additions and 5 deletions
+8 -1
View File
@@ -16,6 +16,7 @@ Additionally, the Openpose Editor node from [toyxyz/ComfyUI-ultimate-openpose-ed
- **ProportionChanger DWPose Render**: Converts KeyPoints to images
- **ProportionChanger Params**: Adjusts parameters for individual KeyPoint parts
- **ProportionChanger Interpolator**: Interpolates KeyPoint videos with in-betweening
- **PoseData to pose_keypoint**: Converts WanAnimate `POSEDATA` into `POSE_KEYPOINT`
- **pose_keypoint input**: Converts JSON text to KeyPoints
- **pose_keypoint preview**: Converts KeyPoints to JSON
@@ -49,6 +50,12 @@ Please refer to example_workflows.
![proportion_changer_basic2](docs/images/example_basic2.png)
### Converting WanAnimate POSEDATA
- Use WanAnimatePreprocess "Pose and Face Detection" to produce `POSEDATA`
- Feed `POSEDATA`, `width`, and `height` into **PoseData to pose_keypoint**
- Connect the resulting `pose_keypoint` to ProportionChanger Reference / Render nodes
- Example workflow: `example_workflows/proportion_changer_pose_data_to_pose_keypoint.json`
## Troubleshooting
### Common Issues
@@ -76,4 +83,4 @@ When combining code from Apache 2.0 and GPL 3.0 licensed projects, the resulting
- ProportionChanger Params functionality: Copyright by toyxyz
- Modifications and integration: This project's contributors
See the [LICENSE](LICENSE) file for the full GPL 3.0 license terms.
See the [LICENSE](LICENSE) file for the full GPL 3.0 license terms.
+7
View File
@@ -16,6 +16,7 @@
- **ProportionChanger DWPose Render**: KeyPointを画像に変換する
- **ProportionChanger Params**: KeyPointの各パーツのパラメータを調整する
- **ProportionChanger Interpolator**: KeyPoint動画を中割りして補間する
- **PoseData to pose_keypoint**: WanAnimateの`POSEDATA`を`POSE_KEYPOINT`に変換する
- **pose_keypoint input**: JSONテキストからKeyPointに変換する
- **pose_keypoint preview**: KeyPointからJSONに変換する
@@ -49,6 +50,12 @@ example_workflowsを参照してください。
![proportion_changer_basic2](docs/images/example_basic2.png)
### WanAnimate POSEDATAの変換
- WanAnimatePreprocessの「Pose and Face Detection」で`POSEDATA`を取得
- **PoseData to pose_keypoint**ノードに`POSEDATA`とキャンバスの`width`/`height`を入力
- 出力された`pose_keypoint`をProportionChanger Reference / Renderノードに接続
- サンプルワークフロー: `example_workflows/proportion_changer_pose_data_to_pose_keypoint.json`
## トラブルシューティング
@@ -0,0 +1,725 @@
{
"id": "deb90f3a-9e14-4806-8e90-98c0eea4d98e",
"revision": 0,
"last_node_id": 12,
"last_link_id": 18,
"nodes": [
{
"id": 7,
"type": "DrawViTPose",
"pos": [
7440.435138869547,
6129.696683823703
],
"size": [
270,
178
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "pose_data",
"type": "POSEDATA",
"link": 9
},
{
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": 10
},
{
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": 11
}
],
"outputs": [
{
"name": "pose_images",
"type": "IMAGE",
"links": [
5
]
}
],
"properties": {
"cnr_id": "ComfyUI-WanAnimatePreprocess",
"ver": "2fcbcae7eec637fdc712fdec18e6266feb8ba3a7",
"Node name for S&R": "DrawViTPose"
},
"widgets_values": [
480,
832,
16,
-1,
-1,
true
]
},
{
"id": 9,
"type": "GetImageSizeAndCount",
"pos": [
6621.080613343585,
6467.158135374485
],
"size": [
196.2994140625,
86
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 16
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": [
13
]
},
{
"label": "512 width",
"name": "width",
"type": "INT",
"links": [
7,
10,
14
]
},
{
"label": "920 height",
"name": "height",
"type": "INT",
"links": [
8,
11,
15
]
},
{
"label": "16 count",
"name": "count",
"type": "INT",
"links": []
}
],
"properties": {
"cnr_id": "comfyui-kjnodes",
"ver": "f37df472dfb325f1f72c90e2df56e72f40f850f0",
"Node name for S&R": "GetImageSizeAndCount"
},
"widgets_values": []
},
{
"id": 10,
"type": "VHS_LoadVideo",
"pos": [
6273.467729604059,
6468.808175961504
],
"size": [
296.0120268545073,
819.5215916898854
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "meta_batch",
"shape": 7,
"type": "VHS_BatchManager",
"link": null
},
{
"name": "vae",
"shape": 7,
"type": "VAE",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
16
]
},
{
"name": "frame_count",
"type": "INT",
"links": []
},
{
"name": "audio",
"type": "AUDIO",
"links": []
},
{
"name": "video_info",
"type": "VHS_VIDEOINFO",
"links": null
}
],
"properties": {
"cnr_id": "comfyui-videohelpersuite",
"ver": "8550981384301e9bc5bfea83e5c2c75258102593",
"Node name for S&R": "VHS_LoadVideo"
},
"widgets_values": {
"video": "yoshi1_sample_girl.mp4",
"force_rate": 16,
"custom_width": 512,
"custom_height": 920,
"frame_load_cap": 16,
"skip_first_frames": 1,
"select_every_nth": 1,
"format": "AnimateDiff",
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "yoshi1_sample_girl.mp4",
"type": "input",
"format": "video/mp4",
"force_rate": 16,
"custom_width": 512,
"custom_height": 920,
"frame_load_cap": 16,
"skip_first_frames": 1,
"select_every_nth": 1
}
}
}
},
{
"id": 11,
"type": "OnnxDetectionModelLoader",
"pos": [
6276.524633017252,
6134.099122226672
],
"size": [
430.1680197224765,
106
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "model",
"type": "POSEMODEL",
"links": [
12
]
}
],
"properties": {
"cnr_id": "ComfyUI-WanAnimatePreprocess",
"ver": "2fcbcae7eec637fdc712fdec18e6266feb8ba3a7",
"Node name for S&R": "OnnxDetectionModelLoader"
},
"widgets_values": [
"onnx/vitpose_h_wholebody_model.onnx",
"onnx/yolov10m.onnx",
"CUDAExecutionProvider"
]
},
{
"id": 8,
"type": "PoseAndFaceDetection",
"pos": [
6902.806960621858,
6134.325632658536
],
"size": [
313.125,
162
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "POSEMODEL",
"link": 12
},
{
"name": "images",
"type": "IMAGE",
"link": 13
},
{
"name": "retarget_image",
"shape": 7,
"type": "IMAGE",
"link": null
},
{
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": 14
},
{
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": 15
}
],
"outputs": [
{
"name": "pose_data",
"type": "POSEDATA",
"links": [
6,
9
]
},
{
"name": "face_images",
"type": "IMAGE",
"links": null
},
{
"name": "key_frame_body_points",
"type": "STRING",
"links": null
},
{
"name": "bboxes",
"type": "BBOX",
"links": null
},
{
"name": "face_bboxes",
"type": "BBOX,",
"links": null
}
],
"properties": {
"cnr_id": "ComfyUI-WanAnimatePreprocess",
"ver": "2fcbcae7eec637fdc712fdec18e6266feb8ba3a7",
"Node name for S&R": "PoseAndFaceDetection"
},
"widgets_values": [
480,
832
]
},
{
"id": 6,
"type": "PoseDataToPoseKeypoint",
"pos": [
6998.0968322251765,
6726.796517643764
],
"size": [
280.7720703125,
82
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "pose_data",
"type": "POSEDATA",
"link": 6
},
{
"name": "width",
"type": "INT",
"widget": {
"name": "width"
},
"link": 7
},
{
"name": "height",
"type": "INT",
"widget": {
"name": "height"
},
"link": 8
}
],
"outputs": [
{
"name": "pose_keypoint",
"type": "POSE_KEYPOINT",
"links": [
3
]
}
],
"properties": {
"cnr_id": "ComfyUI-ProportionChanger",
"ver": "16cda84ffa029ac60fc2da05f727fafcdf90fcd6",
"Node name for S&R": "PoseDataToPoseKeypoint"
},
"widgets_values": [
512,
768
]
},
{
"id": 5,
"type": "VHS_VideoCombine",
"pos": [
7773.121842444329,
6122.950426243646
],
"size": [
270.25917466462124,
797.6844544754913
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 5
},
{
"name": "audio",
"shape": 7,
"type": "AUDIO",
"link": null
},
{
"name": "meta_batch",
"shape": 7,
"type": "VHS_BatchManager",
"link": null
},
{
"name": "vae",
"shape": 7,
"type": "VAE",
"link": null
}
],
"outputs": [
{
"name": "Filenames",
"type": "VHS_FILENAMES",
"links": null
}
],
"properties": {
"cnr_id": "comfyui-videohelpersuite",
"ver": "0a75c7958fe320efcb052f1d9f8451fd20c730a8",
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 16,
"loop_count": 0,
"filename_prefix": "ProportionChanger_Dev",
"format": "video/h264-mp4",
"pix_fmt": "yuv420p",
"crf": 19,
"save_metadata": true,
"trim_to_audio": false,
"pingpong": false,
"save_output": true,
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "ProportionChanger_Dev_00011.mp4",
"subfolder": "",
"type": "output",
"format": "video/h264-mp4",
"frame_rate": 16,
"workflow": "ProportionChanger_Dev_00011.png",
"fullpath": "/home/shins/works/ComfyUI/output/ProportionChanger_Dev_00011.mp4"
}
}
}
},
{
"id": 1,
"type": "VHS_VideoCombine",
"pos": [
7776.91575807864,
6987.710777633694
],
"size": [
270.25917466462124,
797.6844544754913
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 18
},
{
"name": "audio",
"shape": 7,
"type": "AUDIO",
"link": null
},
{
"name": "meta_batch",
"shape": 7,
"type": "VHS_BatchManager",
"link": null
},
{
"name": "vae",
"shape": 7,
"type": "VAE",
"link": null
}
],
"outputs": [
{
"name": "Filenames",
"type": "VHS_FILENAMES",
"links": null
}
],
"properties": {
"cnr_id": "comfyui-videohelpersuite",
"ver": "0a75c7958fe320efcb052f1d9f8451fd20c730a8",
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 16,
"loop_count": 0,
"filename_prefix": "ProportionChanger",
"format": "video/h264-mp4",
"pix_fmt": "yuv420p",
"crf": 19,
"save_metadata": true,
"trim_to_audio": false,
"pingpong": false,
"save_output": true,
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "ProportionChanger_00001.mp4",
"subfolder": "",
"type": "output",
"format": "video/h264-mp4",
"frame_rate": 16,
"workflow": "ProportionChanger_00001.png",
"fullpath": "/home/shins/works/ComfyUI/output/ProportionChanger_00001.mp4"
}
}
}
},
{
"id": 3,
"type": "ProportionChangerDWPoseRender",
"pos": [
7349.095248798733,
6869.300113529442
],
"size": [
337.8521484375,
226
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "pose_keypoint",
"type": "POSE_KEYPOINT",
"link": 3
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
18
]
}
],
"properties": {
"cnr_id": "ComfyUI-ProportionChanger",
"ver": "16cda84ffa029ac60fc2da05f727fafcdf90fcd6",
"Node name for S&R": "ProportionChangerDWPoseRender"
},
"widgets_values": [
true,
false,
true,
true,
-1,
4,
3,
2
]
}
],
"links": [
[
3,
6,
0,
3,
0,
"POSE_KEYPOINT"
],
[
5,
7,
0,
5,
0,
"IMAGE"
],
[
6,
8,
0,
6,
0,
"POSEDATA"
],
[
7,
9,
1,
6,
1,
"INT"
],
[
8,
9,
2,
6,
2,
"INT"
],
[
9,
8,
0,
7,
0,
"POSEDATA"
],
[
10,
9,
1,
7,
1,
"INT"
],
[
11,
9,
2,
7,
2,
"INT"
],
[
12,
11,
0,
8,
0,
"POSEMODEL"
],
[
13,
9,
0,
8,
1,
"IMAGE"
],
[
14,
9,
1,
8,
3,
"INT"
],
[
15,
9,
2,
8,
4,
"INT"
],
[
16,
10,
0,
9,
0,
"IMAGE"
],
[
18,
3,
0,
1,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.5559917313492256,
"offset": [
-6097.1435759818805,
-6089.885949666708
]
},
"workflowRendererVersion": "LG",
"frontendVersion": "1.35.4",
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0,
"VHS_MetadataImage": true,
"VHS_KeepIntermediate": true
},
"version": 0.4
}
+4 -1
View File
@@ -13,6 +13,7 @@ from .proportion_changer import (
ProportionChangerDWPoseRender,
ProportionChangerParams,
ProportionChangerInterpolator,
PoseDataToPoseKeypoint,
# TODO: Re-enable after fixing Kalman filter issues
# ProportionChangerKeypointDenoiser,
# ProportionChangerKeypointDenoiserAdvanced
@@ -202,6 +203,7 @@ NODE_CLASS_MAPPINGS = {
# "ProportionChangerKeypointDenoiserAdvanced": ProportionChangerKeypointDenoiserAdvanced,
# Utility nodes
"PoseDataToPoseKeypoint": PoseDataToPoseKeypoint,
"PoseJSONToPoseKeypoint": PoseJSONToPoseKeypoint,
"PoseKeypointPreview": PoseKeypointPreview,
}
@@ -219,6 +221,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
# "ProportionChangerKeypointDenoiserAdvanced": "ProportionChanger Denoiser (Advanced)",
# Utility nodes
"PoseDataToPoseKeypoint": "PoseData to pose_keypoint",
"PoseJSONToPoseKeypoint": "pose_keypoint input",
"PoseKeypointPreview": "pose_keypoint preview",
}
}
+70
View File
@@ -0,0 +1,70 @@
# Plan: PoseData to PoseKeypoint converter node
## Goal
Add a ComfyUI node (e.g. `PoseDataToPoseKeypoint`) inside ProportionChanger that converts WanAnimatePreprocess `POSEDATA` into ProportionChanger/UniAnimate-style `POSE_KEYPOINT` so downstream nodes can reuse DWPose-format keypoints without needing WanAnimate code.
## Expected IO
- Inputs (match Draw ViT Pose for familiarity):
- `pose_data` (POSEDATA) – produced by Pose and Face Detection.
- `width` (INT) – canvas width to scale normalized coords to pixels.
- `height` (INT) – canvas height.
- Output:
- `pose_keypoint` (POSE_KEYPOINT) – list of frame dicts following UniAnimate/OpenPose style used in `ProportionChangerDWPoseDetector`.
## Source formats to bridge
- `pose_data["pose_metas"]`: list of `AAPoseMeta` or humanapi meta clones; body/hand/face keypoints are **normalized 0–1** and packed as numpy arrays with confidences.
- ProportionChanger expects per-frame dict:
```json
{
"version": "1.0",
"people": [
{
"pose_keypoints_2d": [x,y,conf]*25,
"face_keypoints_2d": [x,y,conf]*70,
"hand_left_keypoints_2d": [x,y,conf]*21,
"hand_right_keypoints_2d": [x,y,conf]*21
}
],
"canvas_width": W,
"canvas_height": H
}
```
- Body point count mismatch: AAPoseMeta body has 20 keypoints (19-22 are averaged joints, no toes). DWPose/UniAnimate uses 25 (includes toes idx 19–24). Plan to map available 20 into first 20 slots and pad toes (19–24) with zeros/conf=0.
## Conversion rules
1) For each `meta` in `pose_data["pose_metas"]`:
- Scale all keypoints by `width`/`height` to pixels.
- Body: take `meta.kps_body` (shape 20x2) + `meta.kps_body_p` to build 25*3 list; pad missing 5 joints (toe L/R and ankle variants) with 0s and conf 0.
- Face: `meta.kps_face` (69 pts) -> first 69; pad to 70 with zeros; confidences from `kps_face_p` if available else 1.
- Hands: `meta.kps_lhand`, `kps_rhand` (21 pts each); pad if missing; use `kps_*_p` for conf else 1.
- Build `people` entry; if no valid body points (all conf 0), emit empty `people` list.
2) Append `canvas_width/height` from input args.
3) Return list ordered to match frames.
## Node design
- New file: add class `PoseDataToPoseKeypoint` under `proportion_changer` package (e.g. `converter_nodes.py` or existing util file) and expose via `NODE_CLASS_MAPPINGS`/`NODE_DISPLAY_NAME_MAPPINGS` in `nodes.py`.
- Category: `ProportionChanger`.
- INPUT_TYPES same three fields; RETURN_TYPES (`POSE_KEYPOINT`,) RETURN_NAMES (`pose_keypoint`,).
- Keep tensor-free; operates on Python lists/numpy.
- Minimal deps: reuse numpy if already available; avoid torch.
## Edge cases to handle
- Missing `kps_face` or hands: pad with zeros/conf=0.
- NaN/None in keypoints: coerce to 0, conf=0.
- Empty `pose_metas`: return `[ {version:"1.0", people:[], canvas_width:W, canvas_height:H} ]`.
- Width/height ≤0: fail fast with ValueError.
## Tests / validation
- Add lightweight unit test under `test/` to feed fake `pose_data` with one meta and assert lengths (75/210/63/63) and canvas sizes.
- (Optional) round-trip check: feed output to `PoseKeypointPreview` to ensure JSON renders; manual workflow example in `example_workflows`.
## Integration steps
1) Implement converter node code.
2) Wire into `NODE_CLASS_MAPPINGS`/`NODE_DISPLAY_NAME_MAPPINGS`.
3) Add doc blurb to `README.md` (usage + expected inputs).
4) Add test and run `pytest` (or targeted script) if available.
## Reference file paths (relative to this plan)
- `../ComfyUI-WanAnimatePreprocess/nodes.py` — DrawViTPose input schema (`pose_data`, `width`, `height`), PoseAndFaceDetection return structure.
- `../ComfyUI-WanAnimatePreprocess/pose_utils/pose2d_utils.py` — `AAPoseMeta` structure, keypoint ordering, `load_from_meta` and related helpers.
- `../ComfyUI-WanAnimatePreprocess/pose_utils/human_visualization.py` (optional) — shows how body/hand indices map to drawing; useful to confirm joint ordering if needed.
+9 -1
View File
@@ -28,6 +28,11 @@ from .interpolation_node import (
ProportionChangerInterpolator
)
# Converter nodes
from .converter_nodes import (
PoseDataToPoseKeypoint
)
# Import keypoint denoiser node classes
from .keypoint_denoiser_node import (
ProportionChangerKeypointDenoiser,
@@ -49,8 +54,11 @@ __all__ = [
# Interpolation nodes
"ProportionChangerInterpolator",
# Converter nodes
"PoseDataToPoseKeypoint",
# KeyPoint Denoiser nodes
"ProportionChangerKeypointDenoiser",
"ProportionChangerKeypointDenoiserAdvanced"
]
]
+182
View File
@@ -0,0 +1,182 @@
"""
Converter nodes for bridging WanAnimate POSEDATA into POSE_KEYPOINT format.
"""
from __future__ import annotations
import numpy as np
try: # Standard package import (ComfyUI runtime)
from ..utils import log
except ImportError: # Fallback for standalone/unit test environments
from utils import log
def _to_array(value):
"""Convert nested list/tuple/ndarray to numpy array or return None."""
if value is None:
return None
try:
arr = np.array(value, dtype=float)
if arr.size == 0:
return None
return arr
except Exception:
return None
def _looks_normalized(coords: np.ndarray) -> bool:
"""
Heuristic: treat coordinates as normalized (0..1-ish) if their absolute max is small.
WanAnimate/UniAnimate often uses normalized coordinates; some POSEDATA variants already store pixels.
"""
if coords is None or coords.size == 0:
return True
finite = coords[np.isfinite(coords)]
if finite.size == 0:
return True
return float(np.max(np.abs(finite))) <= 2.0
def _extract_keypoints(meta, coord_name, conf_name, target_points, width, height, default_conf=1.0):
"""
Extract, scale, and pad keypoints to the desired target length.
Missing coordinates or invalid values are zeroed with confidence 0.
"""
coords_raw = getattr(meta, coord_name, None)
conf_raw = getattr(meta, conf_name, None)
# Fall back to dict-style access if present
if coords_raw is None and isinstance(meta, dict):
coords_raw = meta.get(coord_name)
if conf_raw is None and isinstance(meta, dict):
conf_raw = meta.get(conf_name)
coords = _to_array(coords_raw)
confs = _to_array(conf_raw)
if confs is not None:
confs = confs.reshape(-1)
# Normalize coordinate shape to (N, 2)
if coords is not None:
if coords.ndim == 1:
# Flattened x,y pairs
if coords.size % 2 == 0:
coords = coords.reshape(-1, 2)
else:
coords = None
elif coords.shape[-1] >= 2:
coords = coords.reshape(-1, coords.shape[-1])[:, :2]
else:
coords = None
scale_from_normalized = _looks_normalized(coords) if coords is not None else True
points = []
max_points = coords.shape[0] if coords is not None else 0
for idx in range(target_points):
if coords is not None and idx < max_points:
x, y = coords[idx]
conf = confs[idx] if confs is not None and idx < confs.shape[0] else default_conf
# Validate numerical values
if not np.isfinite(x) or not np.isfinite(y) or not np.isfinite(conf):
x = y = 0.0
conf = 0.0
else:
x = float(x)
y = float(y)
if scale_from_normalized:
x *= width
y *= height
conf = float(conf)
else:
x = y = 0.0
conf = 0.0
points.extend([x, y, conf])
return points
class PoseDataToPoseKeypoint:
"""
Convert WanAnimatePreprocess POSEDATA into POSE_KEYPOINT format used by ProportionChanger.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"pose_data": ("POSEDATA", {"tooltip": "POSEDATA output from WanAnimate Pose & Face Detection."}),
"width": ("INT", {"default": 512, "min": 1, "max": 4096, "step": 1}),
"height": ("INT", {"default": 768, "min": 1, "max": 4096, "step": 1}),
}
}
RETURN_TYPES = ("POSE_KEYPOINT",)
RETURN_NAMES = ("pose_keypoint",)
FUNCTION = "convert"
CATEGORY = "ProportionChanger"
def convert(self, pose_data, width: int, height: int):
if width <= 0 or height <= 0:
raise ValueError("width and height must be positive integers")
pose_metas = None
if isinstance(pose_data, dict):
pose_metas = pose_data.get("pose_metas") or pose_data.get("pose_meta")
if pose_metas is None:
pose_metas = getattr(pose_data, "pose_metas", None)
if not pose_metas:
empty_frame = {
"version": "1.0",
"people": [],
"canvas_width": width,
"canvas_height": height,
}
return ([empty_frame],)
frames = []
for meta in pose_metas:
try:
body_points = _extract_keypoints(meta, "kps_body", "kps_body_p", 25, width, height, default_conf=1.0)
# AAPoseMeta typically supplies 20 points; padder already set remaining to zeros
face_points = _extract_keypoints(meta, "kps_face", "kps_face_p", 70, width, height, default_conf=1.0)
lhand_points = _extract_keypoints(meta, "kps_lhand", "kps_lhand_p", 21, width, height, default_conf=1.0)
rhand_points = _extract_keypoints(meta, "kps_rhand", "kps_rhand_p", 21, width, height, default_conf=1.0)
# Determine if body has any confidence > 0
has_body = any(body_points[i] > 0 for i in range(2, len(body_points), 3))
frame = {
"version": "1.0",
"people": [],
"canvas_width": width,
"canvas_height": height,
}
if has_body:
person = {
"pose_keypoints_2d": body_points,
"face_keypoints_2d": face_points,
"hand_left_keypoints_2d": lhand_points,
"hand_right_keypoints_2d": rhand_points,
}
frame["people"].append(person)
frames.append(frame)
except Exception as exc:
log.error(f"PoseDataToPoseKeypoint conversion failed for frame: {exc}")
frames.append(
{
"version": "1.0",
"people": [],
"canvas_width": width,
"canvas_height": height,
}
)
return (frames,)
+2 -2
View File
@@ -308,7 +308,7 @@ class ProportionChangerParams:
if not all([orig_r_hip, orig_l_hip, orig_neck, orig_nose]):
return adjusted # Return unchanged if essential keypoints are missing
# 階層構造で変換を適用:hip_center → neck → shoulders → arms, legs
# 階層構造で変換を適用:hip_center -> neck -> shoulders -> arms, legs
# 全て元の座標を基準に計算し、依存関係を考慮
# 1. 骨盤の調整(元の座標基準)
@@ -801,4 +801,4 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"ProportionChangerParams": "ProportionChanger Params"
}
}