- Split flat node modules into categorized subpackages (tagging, detect, edge, pose, segment, restore, compare, judge, transform, utility) - Extract shared internals into _shared/ (bases, tensor, formatting) - Centralize node registration via register_nodes() helper - Clean up: remove dead is_scored return, unnecessary hasattr guard, inline imports
79 lines
2.9 KiB
Python
79 lines
2.9 KiB
Python
"""Human pose keypoint detection and visualization using DWPose."""
|
|
|
|
from comfy_api.latest import io, ui
|
|
|
|
from .._shared.tensor import comfy_to_pil, pil_to_comfy
|
|
from .._shared.formatting import keypoints_to_json
|
|
|
|
|
|
class ImgUtilsPose(io.ComfyNode):
|
|
"""Detect OpenPose keypoints via DWPose and render skeleton visualization."""
|
|
|
|
VISUALIZATION_PRESETS = [
|
|
"Body Only",
|
|
"Body with Face",
|
|
"Body with Hands",
|
|
"Full",
|
|
]
|
|
|
|
PRESET_FLAGS = {
|
|
"Body Only": {"draw_body": True, "draw_hands": False, "draw_feet": False, "draw_face": False},
|
|
"Body with Face": {"draw_body": True, "draw_hands": False, "draw_feet": False, "draw_face": True},
|
|
"Body with Hands": {"draw_body": True, "draw_hands": True, "draw_feet": False, "draw_face": False},
|
|
"Full": {"draw_body": True, "draw_hands": True, "draw_feet": True, "draw_face": True},
|
|
}
|
|
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id="ImgUtilsPose",
|
|
display_name="Imgutils Pose",
|
|
category="imgutils/pose",
|
|
description=(
|
|
"Detect OpenPose 18-point human keypoints in anime images "
|
|
"using DWPose. Outputs a visualization image and keypoints JSON."
|
|
),
|
|
search_aliases=[
|
|
"pose", "keypoints", "dwpose", "skeleton", "openpose",
|
|
"body", "face", "hands", "feet",
|
|
],
|
|
inputs=[
|
|
io.Image.Input("image", tooltip="Input image for pose detection."),
|
|
io.Combo.Input(
|
|
"mode",
|
|
options=cls.VISUALIZATION_PRESETS,
|
|
default="Full",
|
|
tooltip="Visualization detail: body only, body+face, body+hands, or full skeleton.",
|
|
),
|
|
io.Boolean.Input(
|
|
"auto_detect", default=True,
|
|
tooltip="Auto-detect person bounding boxes. Disable to use full image.",
|
|
),
|
|
],
|
|
outputs=[
|
|
io.Image.Output(display_name="skeleton"),
|
|
io.String.Output(display_name="json"),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, image, mode, auto_detect=True) -> io.NodeOutput:
|
|
from imgutils.pose import dwpose_estimate, op18_visualize
|
|
|
|
pil_image = comfy_to_pil(image)
|
|
|
|
keypoints_list = dwpose_estimate(pil_image, auto_detect=auto_detect)
|
|
json_str = keypoints_to_json(keypoints_list)
|
|
flags = cls.PRESET_FLAGS.get(mode, cls.PRESET_FLAGS["Full"])
|
|
vis_image = op18_visualize(
|
|
pil_image,
|
|
keypoints_list,
|
|
draw_body=flags["draw_body"],
|
|
draw_hands=flags["draw_hands"],
|
|
draw_feet=flags["draw_feet"],
|
|
draw_face=flags["draw_face"],
|
|
)
|
|
|
|
vis_tensor = pil_to_comfy(vis_image)
|
|
return io.NodeOutput(vis_tensor, json_str, ui=ui.PreviewImage(vis_tensor, cls=cls))
|