Files
ketle-manandClaude Sonnet 4.6 4052b22251 Initial release: ComfyUI 2D Pose Editor custom node
- Interactive 2D rigging figure embedded inside a ComfyUI node
- Drag joints (white dots) to pose the figure
- Drag pupils (black dots) to control eye direction
- Capture button exports the current pose as IMAGE tensor
- Reset button returns to default pose
- Fixed node size, clean UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 22:09:09 +09:00

61 lines
1.8 KiB
Python

import torch
import numpy as np
from PIL import Image
import base64
import io
class PoseEditor2DNode:
"""
2D リギングポーズエディタ ノード
フロントエンドのキャンバスでポーズを編集し、画像として出力する
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image_data": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("pose_image",)
FUNCTION = "export_pose"
CATEGORY = "2D Pose"
OUTPUT_NODE = False
def export_pose(self, image_data: str):
if not image_data or image_data.strip() == "":
# キャプチャ前はグレーのプレースホルダー画像を返す
img = Image.new("RGB", (600, 600), (224, 224, 224))
else:
try:
# "data:image/png;base64,..." 形式の場合はヘッダを除去
if "," in image_data:
image_data = image_data.split(",", 1)[1]
img_bytes = base64.b64decode(image_data)
img = Image.open(io.BytesIO(img_bytes)).convert("RGB")
except Exception as e:
print(f"[PoseEditor2D] 画像デコードエラー: {e}")
img = Image.new("RGB", (600, 600), (200, 100, 100))
img_array = np.array(img).astype(np.float32) / 255.0
img_tensor = torch.from_numpy(img_array).unsqueeze(0) # (1, H, W, C)
return (img_tensor,)
@classmethod
def IS_CHANGED(cls, image_data):
# image_data が変わるたびに再実行
import hashlib
return hashlib.md5(image_data.encode()).hexdigest()
NODE_CLASS_MAPPINGS = {
"PoseEditor2D": PoseEditor2DNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PoseEditor2D": "2D Pose Editor",
}