Initial commit: Pose Keypoints To Image node for ComfyUI

This commit is contained in:
YULE-WINDOWS\zlcbl
2026-01-12 10:21:52 +09:00
commit 2472a51c89
6 changed files with 306 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.env
.venv
*.egg-info/
.installed.cfg
*.egg
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+48
View File
@@ -0,0 +1,48 @@
# ComfyUI-PoseKeypointsToImage
POSE_KEYPOINT(DWPose/OpenPose形式)からポーズ画像を描画する ComfyUI カスタムノードです。
[English](README_EN.md)
## 概要
DWPose や OpenPose の JSON 形式のキーポイントデータから、ポーズ画像(IMAGE)を生成するノードです。FaceKeypointsFilter で顔パーツをフィルタリングした後の pose_kps を渡すことで、特定のパーツを除外したポーズ画像を作成できます。
## インストール
### ComfyUI Manager を使用(推奨)
ComfyUI Manager で「PoseKeypointsToImage」を検索してインストール。
### 手動インストール
1. ComfyUI の `custom_nodes` フォルダに移動
2. このリポジトリをクローン:
```bash
git clone https://github.com/smartvain/ComfyUI-PoseKeypointsToImage.git
```
3. ComfyUI を再起動
## 使い方
1. DWPose Preprocessor(または `POSE_KEYPOINT` を出力するノード)の出力をこのノードに接続
2. パラメータを設定:
- `width` / `height`: 出力画像のサイズ(デフォルト: 512x512)
- `point_radius`: キーポイントの描画半径(デフォルト: 2)
- `draw_body`: ボディキーポイントを描画するか
- `draw_face`: 顔キーポイントを描画するか
- `draw_hands`: 手のキーポイントを描画するか
3. 出力を他のノードに接続
## ノードの場所
`Pose/Face Utils` > `Pose Keypoints → Image`
## 互換性
- `comfyui_controlnet_aux` の DWPose Preprocessor と連携
- OpenPose JSON フォーマットに対応
## ライセンス
MIT License
+48
View File
@@ -0,0 +1,48 @@
# ComfyUI-PoseKeypointsToImage
A ComfyUI custom node that renders pose images from POSE_KEYPOINT (DWPose/OpenPose format).
[日本語](README.md)
## Description
This node generates pose images (IMAGE) from DWPose or OpenPose JSON format keypoint data. By passing pose_kps that has been filtered with FaceKeypointsFilter, you can create pose images with specific parts excluded.
## Installation
### Using ComfyUI Manager (Recommended)
Search for "PoseKeypointsToImage" in ComfyUI Manager and install.
### Manual Installation
1. Navigate to your ComfyUI's `custom_nodes` folder
2. Clone this repository:
```bash
git clone https://github.com/smartvain/ComfyUI-PoseKeypointsToImage.git
```
3. Restart ComfyUI
## Usage
1. Connect the output of DWPose Preprocessor (or any node that outputs `POSE_KEYPOINT`) to this node
2. Configure the parameters:
- `width` / `height`: Output image size (default: 512x512)
- `point_radius`: Keypoint drawing radius (default: 2)
- `draw_body`: Whether to draw body keypoints
- `draw_face`: Whether to draw face keypoints
- `draw_hands`: Whether to draw hand keypoints
3. Connect the output to other nodes
## Node Location
`Pose/Face Utils` > `Pose Keypoints → Image`
## Compatibility
- Works with DWPose Preprocessor from `comfyui_controlnet_aux`
- Compatible with OpenPose JSON format
## License
MIT License
+3
View File
@@ -0,0 +1,3 @@
from .pose_keypoints_to_image import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
+176
View File
@@ -0,0 +1,176 @@
# pose_keypoints_to_image.py
#
# POSE_KEYPOINT (DWPose / OpenPose 形式) から
# ポーズ画像(IMAGE)を描き直すノードです。
import numpy as np
from PIL import Image, ImageDraw
import torch
class PoseKeypointsToImage:
"""
POSE_KEYPOINT からポーズ画像を生成するノード。
DWPose の JSON (frame["people"][0]["pose_keypoints_2d"], etc.) に対応。
FaceKeypointsFilter で口を消した後の pose_kps などを渡すことで、
「口なし」の OpenPose 風画像を作ることができる。
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"pose_kps": ("POSE_KEYPOINT",),
"width": (
"INT",
{"default": 512, "min": 64, "max": 4096, "step": 8},
),
"height": (
"INT",
{"default": 512, "min": 64, "max": 4096, "step": 8},
),
"point_radius": (
"INT",
{"default": 2, "min": 1, "max": 10, "step": 1},
),
"draw_body": ("BOOLEAN", {"default": True}),
"draw_face": ("BOOLEAN", {"default": True}),
"draw_hands": ("BOOLEAN", {"default": True}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "render"
CATEGORY = "Pose/Face Utils"
# ------------------ helpers ------------------ #
def _draw_points(self, draw, flat_list, radius, color=(255, 255, 255)):
"""
flat_list: [x0, y0, s0, x1, y1, s1, ...]
score(s) > 0 の点だけを描画する
"""
if flat_list is None:
return
if len(flat_list) % 3 != 0:
return
for i in range(0, len(flat_list), 3):
x, y, score = flat_list[i : i + 3]
if score is None:
continue
if score <= 0:
continue
if np.isnan(x) or np.isnan(y):
continue
x = float(x)
y = float(y)
draw.ellipse(
(x - radius, y - radius, x + radius, y + radius),
fill=color,
)
def _get_people_list(self, frame):
"""
frame["people"] があればそれを返し、
無ければ [frame] を 1人分として扱う。
"""
if not isinstance(frame, dict):
return []
if "people" in frame and isinstance(frame["people"], list) and frame["people"]:
return frame["people"]
# 旧形式: frame 自体に keypoints がいる想定
return [frame]
# ------------------ main ------------------ #
def render(
self,
pose_kps,
width,
height,
point_radius,
draw_body,
draw_face,
draw_hands,
):
"""
pose_kps: DWPose Estimator などからの POSE_KEYPOINT
"""
# pose_kps は通常「各フレーム dict のリスト」
if isinstance(pose_kps, dict):
frames = [pose_kps]
elif isinstance(pose_kps, list):
frames = pose_kps
else:
blank_np = np.zeros((1, height, width, 3), dtype=np.float32)
blank = torch.from_numpy(blank_np)
return (blank,)
# 可能なら 1フレーム目の canvas_width/height で上書き
if frames and isinstance(frames[0], dict):
cw = frames[0].get("canvas_width", width)
ch = frames[0].get("canvas_height", height)
try:
width = int(cw)
height = int(ch)
except Exception:
pass
images = []
for frame in frames:
img = Image.new("RGB", (width, height), (0, 0, 0))
draw = ImageDraw.Draw(img)
if not isinstance(frame, dict):
images.append(np.array(img).astype(np.float32) / 255.0)
continue
people = self._get_people_list(frame)
for person in people:
if not isinstance(person, dict):
continue
if draw_body and "pose_keypoints_2d" in person:
self._draw_points(draw, person["pose_keypoints_2d"], point_radius)
if draw_face and "face_keypoints_2d" in person:
self._draw_points(draw, person["face_keypoints_2d"], point_radius)
if draw_hands:
if "hand_left_keypoints_2d" in person:
self._draw_points(
draw, person["hand_left_keypoints_2d"], point_radius
)
if "hand_right_keypoints_2d" in person:
self._draw_points(
draw, person["hand_right_keypoints_2d"], point_radius
)
img_arr = np.array(img).astype(np.float32) / 255.0 # H,W,3
images.append(img_arr)
if not images:
blank_np = np.zeros((1, height, width, 3), dtype=np.float32)
blank = torch.from_numpy(blank_np)
return (blank,)
batch_np = np.stack(images, axis=0) # (B,H,W,3)
batch = torch.from_numpy(batch_np) # torch.Tensor に変換
return (batch,)
NODE_CLASS_MAPPINGS = {
"PoseKeypointsToImage": PoseKeypointsToImage,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PoseKeypointsToImage": "Pose Keypoints → Image",
}