Initial commit: Pose Keypoints To Image node for ComfyUI
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
.env
|
||||
.venv
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"]
|
||||
@@ -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",
|
||||
}
|
||||
Reference in New Issue
Block a user