Files
xiaden 315666c7b6 refactor: restructure into subpackages, add _shared layer, remove AI slop
- 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
2026-07-05 08:59:48 +00:00

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))