- 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>
61 lines
1.8 KiB
Python
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",
|
|
}
|