adapt the scale part for non-normalized coordinates and cleanup codes
This commit is contained in:
+122
-62
@@ -3,10 +3,70 @@ import copy
|
||||
import math
|
||||
import torch
|
||||
import numpy as np
|
||||
from .util import scale, draw_pose_json
|
||||
from .openpose_editor_nodes import OpenposeEditorNode
|
||||
from .util import scale
|
||||
|
||||
class AppendageEditorNode:
|
||||
@staticmethod
|
||||
def normalize_scale_parameter(scale_param, target_length, behavior):
|
||||
"""
|
||||
Normalize a scale parameter to a list of the target length.
|
||||
|
||||
Args:
|
||||
scale_param: Either a single float or list of floats
|
||||
target_length: Desired length of output list
|
||||
behavior: "truncate", "loop", or "repeat"
|
||||
|
||||
Returns:
|
||||
List of floats with length determined by behavior
|
||||
"""
|
||||
# Convert single value to list
|
||||
if not isinstance(scale_param, (list, tuple)):
|
||||
scale_list = [scale_param]
|
||||
else:
|
||||
scale_list = list(scale_param)
|
||||
|
||||
if len(scale_list) == target_length:
|
||||
return scale_list
|
||||
|
||||
if behavior == "truncate":
|
||||
return scale_list[:target_length]
|
||||
elif behavior == "loop":
|
||||
if len(scale_list) == 0:
|
||||
return [1.0] * target_length
|
||||
result = []
|
||||
for i in range(target_length):
|
||||
result.append(scale_list[i % len(scale_list)])
|
||||
return result
|
||||
elif behavior == "repeat":
|
||||
if len(scale_list) == 0:
|
||||
return [1.0] * target_length
|
||||
if len(scale_list) >= target_length:
|
||||
return scale_list[:target_length]
|
||||
else:
|
||||
result = scale_list[:]
|
||||
last_value = scale_list[-1]
|
||||
while len(result) < target_length:
|
||||
result.append(last_value)
|
||||
return result
|
||||
else:
|
||||
raise ValueError(f"Unknown behavior: {behavior}")
|
||||
|
||||
@staticmethod
|
||||
def determine_output_length(scale_params, pose_count, behavior):
|
||||
"""
|
||||
Determine the output length based on scale parameters and behavior.
|
||||
"""
|
||||
# Get all list lengths
|
||||
lengths = [pose_count]
|
||||
for param in scale_params:
|
||||
if isinstance(param, (list, tuple)):
|
||||
lengths.append(len(param))
|
||||
|
||||
if behavior == "truncate":
|
||||
return min(lengths)
|
||||
else: # loop or repeat
|
||||
return max(lengths)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -14,7 +74,7 @@ class AppendageEditorNode:
|
||||
"POSE_KEYPOINT": ("POSE_KEYPOINT",),
|
||||
"appendage_type": ([
|
||||
"left_upper_arm", "left_forearm", "left_full_arm",
|
||||
"right_upper_arm", "right_forearm", "right_full_arm",
|
||||
"right_upper_arm", "right_forearm", "right_full_arm",
|
||||
"left_upper_leg", "left_lower_leg", "left_full_leg",
|
||||
"right_upper_leg", "right_lower_leg", "right_full_leg",
|
||||
"left_hand", "right_hand", "left_foot", "right_foot",
|
||||
@@ -70,67 +130,67 @@ class AppendageEditorNode:
|
||||
def edit_appendage(self, POSE_KEYPOINT, appendage_type, scale=1.0, x_offset=0.0, y_offset=0.0, rotation=0.0, bidirectional_scale=False, person_index=-1, list_mismatch_behavior="loop"):
|
||||
if POSE_KEYPOINT is None:
|
||||
return (None,)
|
||||
|
||||
|
||||
# Deep copy to avoid modifying the original
|
||||
pose_data = copy.deepcopy(POSE_KEYPOINT)
|
||||
if not isinstance(pose_data, list):
|
||||
pose_data = [pose_data]
|
||||
|
||||
|
||||
pose_count = len(pose_data)
|
||||
|
||||
|
||||
# Normalize scale parameters to handle lists vs single floats using the original node's methods
|
||||
scale_params = [scale, x_offset, y_offset, rotation]
|
||||
output_length = OpenposeEditorNode.determine_output_length(scale_params, pose_count, list_mismatch_behavior)
|
||||
|
||||
scale_list = OpenposeEditorNode.normalize_scale_parameter(scale, output_length, list_mismatch_behavior)
|
||||
x_offset_list = OpenposeEditorNode.normalize_scale_parameter(x_offset, output_length, list_mismatch_behavior)
|
||||
y_offset_list = OpenposeEditorNode.normalize_scale_parameter(y_offset, output_length, list_mismatch_behavior)
|
||||
rotation_list = OpenposeEditorNode.normalize_scale_parameter(rotation, output_length, list_mismatch_behavior)
|
||||
|
||||
output_length = determine_output_length(scale_params, pose_count, list_mismatch_behavior)
|
||||
|
||||
scale_list = normalize_scale_parameter(scale, output_length, list_mismatch_behavior)
|
||||
x_offset_list = normalize_scale_parameter(x_offset, output_length, list_mismatch_behavior)
|
||||
y_offset_list = normalize_scale_parameter(y_offset, output_length, list_mismatch_behavior)
|
||||
rotation_list = normalize_scale_parameter(rotation, output_length, list_mismatch_behavior)
|
||||
|
||||
# Process each frame with its corresponding parameter values
|
||||
output_pose_data = []
|
||||
|
||||
|
||||
for i in range(output_length):
|
||||
# Get the pose data for this index
|
||||
pose_idx = i if i < pose_count else pose_count - 1
|
||||
if list_mismatch_behavior == "loop" and pose_count > 0:
|
||||
pose_idx = i % pose_count
|
||||
|
||||
|
||||
# Get current frame and parameter values
|
||||
current_frame = copy.deepcopy(pose_data[pose_idx])
|
||||
current_scale = scale_list[i]
|
||||
current_x_offset = x_offset_list[i]
|
||||
current_y_offset = y_offset_list[i]
|
||||
current_rotation = rotation_list[i]
|
||||
|
||||
|
||||
# Apply transformations to this frame
|
||||
if 'people' in current_frame:
|
||||
people_to_edit = range(len(current_frame['people'])) if person_index == -1 else [person_index]
|
||||
|
||||
|
||||
for person_idx in people_to_edit:
|
||||
if person_idx >= len(current_frame['people']):
|
||||
continue
|
||||
|
||||
|
||||
person = current_frame['people'][person_idx]
|
||||
|
||||
|
||||
if appendage_type in ["left_hand", "right_hand"]:
|
||||
self._edit_hand_appendage(person, appendage_type, current_scale, current_x_offset, current_y_offset, current_rotation, bidirectional_scale)
|
||||
else:
|
||||
self._edit_body_appendage(person, appendage_type, current_scale, current_x_offset, current_y_offset, current_rotation, bidirectional_scale)
|
||||
|
||||
|
||||
output_pose_data.append(current_frame)
|
||||
|
||||
|
||||
return (output_pose_data if isinstance(POSE_KEYPOINT, list) else output_pose_data[0],)
|
||||
|
||||
|
||||
def _edit_hand_appendage(self, person, appendage_type, scale_factor, x_offset, y_offset, rotation, bidirectional_scale):
|
||||
"""Edit hand appendages using hand keypoints."""
|
||||
keypoint_field = "hand_left_keypoints_2d" if appendage_type == "left_hand" else "hand_right_keypoints_2d"
|
||||
|
||||
|
||||
if keypoint_field not in person or not person[keypoint_field]:
|
||||
return
|
||||
|
||||
|
||||
keypoints = person[keypoint_field]
|
||||
|
||||
|
||||
# Use wrist (first point) as pivot for hands
|
||||
if len(keypoints) >= 3 and keypoints[2] > 0:
|
||||
pivot = [keypoints[0], keypoints[1]]
|
||||
@@ -139,36 +199,36 @@ class AppendageEditorNode:
|
||||
pivot = self._calculate_center_of_mass(keypoints)
|
||||
if pivot is None:
|
||||
return
|
||||
|
||||
|
||||
# Apply transformations
|
||||
new_keypoints = self._apply_transformations(keypoints, scale_factor, x_offset, y_offset, rotation, pivot, bidirectional_scale)
|
||||
person[keypoint_field] = new_keypoints
|
||||
|
||||
|
||||
def _edit_body_appendage(self, person, appendage_type, scale_factor, x_offset, y_offset, rotation, bidirectional_scale):
|
||||
"""Edit body appendages (arms, legs, feet) using body pose keypoints."""
|
||||
if 'pose_keypoints_2d' not in person or not person['pose_keypoints_2d']:
|
||||
return
|
||||
|
||||
|
||||
keypoints = person['pose_keypoints_2d']
|
||||
|
||||
|
||||
# Get keypoint indices for the specific appendage
|
||||
appendage_indices, pivot_index = self._get_appendage_indices(appendage_type)
|
||||
if not appendage_indices:
|
||||
return
|
||||
|
||||
|
||||
# Calculate pivot point for the appendage
|
||||
pivot = self._calculate_appendage_pivot(keypoints, appendage_indices, pivot_index)
|
||||
if pivot is None:
|
||||
return
|
||||
|
||||
|
||||
# Apply transformations only to the appendage keypoints
|
||||
new_keypoints = keypoints[:]
|
||||
|
||||
|
||||
for i in range(0, len(keypoints), 3):
|
||||
keypoint_idx = i // 3
|
||||
if keypoint_idx in appendage_indices and len(keypoints) > i+2:
|
||||
x, y, conf = keypoints[i], keypoints[i+1], keypoints[i+2]
|
||||
|
||||
|
||||
if conf > 0:
|
||||
# Apply rotation
|
||||
if rotation != 0.0:
|
||||
@@ -177,7 +237,7 @@ class AppendageEditorNode:
|
||||
rel_x, rel_y = x - pivot[0], y - pivot[1]
|
||||
x = rel_x * cos_r - rel_y * sin_r + pivot[0]
|
||||
y = rel_x * sin_r + rel_y * cos_r + pivot[1]
|
||||
|
||||
|
||||
# Apply scaling with directional control
|
||||
if scale_factor != 1.0:
|
||||
if bidirectional_scale:
|
||||
@@ -186,22 +246,22 @@ class AppendageEditorNode:
|
||||
else:
|
||||
# Unidirectional scaling - only scale away from body
|
||||
x, y = self._apply_unidirectional_scale([x, y], scale_factor, pivot, keypoint_idx, pivot_index)
|
||||
|
||||
|
||||
# Apply offset
|
||||
x += x_offset
|
||||
y += y_offset
|
||||
|
||||
|
||||
new_keypoints[i] = x
|
||||
new_keypoints[i+1] = y
|
||||
|
||||
|
||||
person['pose_keypoints_2d'] = new_keypoints
|
||||
|
||||
|
||||
def _get_appendage_indices(self, appendage_type):
|
||||
"""Get OpenPose keypoint indices for specific appendages and their pivot points."""
|
||||
# COCO 18-keypoint format (0-based) used by ComfyUI ControlNet Aux OpenPose Pose node:
|
||||
# 0: Nose, 1: Neck, 2: RShoulder, 3: RElbow, 4: RWrist, 5: LShoulder, 6: LElbow, 7: LWrist,
|
||||
# 8: RHip, 9: RKnee, 10: RAnkle, 11: LHip, 12: LKnee, 13: LAnkle, 14: REye, 15: LEye, 16: REar, 17: LEar
|
||||
|
||||
|
||||
appendage_map = {
|
||||
# Arms - COCO format
|
||||
"left_upper_arm": ([5, 6], 5), # LShoulder, LElbow (pivot: shoulder)
|
||||
@@ -210,7 +270,7 @@ class AppendageEditorNode:
|
||||
"right_upper_arm": ([2, 3], 2), # RShoulder, RElbow (pivot: shoulder)
|
||||
"right_forearm": ([3, 4], 3), # RElbow, RWrist (pivot: elbow)
|
||||
"right_full_arm": ([2, 3, 4], 2), # RShoulder, RElbow, RWrist (pivot: shoulder)
|
||||
|
||||
|
||||
# Legs - COCO format (FIXED!)
|
||||
"left_upper_leg": ([11, 12], 11), # LHip, LKnee (pivot: hip)
|
||||
"left_lower_leg": ([12, 13], 12), # LKnee, LAnkle (pivot: knee) - FIXED: was [13,14] which was LAnkle,REye!
|
||||
@@ -218,19 +278,19 @@ class AppendageEditorNode:
|
||||
"right_upper_leg": ([8, 9], 8), # RHip, RKnee (pivot: hip)
|
||||
"right_lower_leg": ([9, 10], 9), # RKnee, RAnkle (pivot: knee)
|
||||
"right_full_leg": ([8, 9, 10], 8), # RHip, RKnee, RAnkle (pivot: hip)
|
||||
|
||||
|
||||
# Feet - COCO format (no foot keypoints in COCO, use ankle only)
|
||||
"left_foot": ([13], 13), # LAnkle only (pivot: ankle)
|
||||
"right_foot": ([10], 10), # RAnkle only (pivot: ankle)
|
||||
|
||||
|
||||
# Torso and Shoulders - COCO format
|
||||
"torso": ([1, 2, 5, 8, 11], 1), # Neck, RShoulder, LShoulder, RHip, LHip (pivot: neck)
|
||||
"shoulders": ([2, 5], 1), # RShoulder, LShoulder (pivot: neck)
|
||||
}
|
||||
|
||||
|
||||
result = appendage_map.get(appendage_type, ([], None))
|
||||
return result[0], result[1]
|
||||
|
||||
|
||||
def _calculate_appendage_pivot(self, keypoints, appendage_indices, pivot_index):
|
||||
"""Calculate pivot point for body appendage using specified pivot index."""
|
||||
if pivot_index is not None:
|
||||
@@ -238,66 +298,66 @@ class AppendageEditorNode:
|
||||
i = pivot_index * 3
|
||||
if len(keypoints) > i+2 and keypoints[i+2] > 0:
|
||||
return [keypoints[i], keypoints[i+1]]
|
||||
|
||||
|
||||
# Fallback to center of mass if pivot point not available
|
||||
valid_points = []
|
||||
for idx in appendage_indices:
|
||||
i = idx * 3
|
||||
if len(keypoints) > i+2 and keypoints[i+2] > 0:
|
||||
valid_points.append([keypoints[i], keypoints[i+1]])
|
||||
|
||||
|
||||
if not valid_points:
|
||||
return None
|
||||
|
||||
|
||||
pivot_x = sum(p[0] for p in valid_points) / len(valid_points)
|
||||
pivot_y = sum(p[1] for p in valid_points) / len(valid_points)
|
||||
return [pivot_x, pivot_y]
|
||||
|
||||
|
||||
def _apply_unidirectional_scale(self, point, scale_factor, pivot, keypoint_idx, pivot_index):
|
||||
"""Apply scaling only in the direction away from the body/pivot."""
|
||||
x, y = point
|
||||
|
||||
|
||||
if keypoint_idx == pivot_index:
|
||||
# Don't scale the pivot point itself
|
||||
return x, y
|
||||
|
||||
|
||||
# Calculate direction vector from pivot to point
|
||||
dx = x - pivot[0]
|
||||
dy = y - pivot[1]
|
||||
|
||||
|
||||
# Scale only the distance, keeping direction
|
||||
distance = math.sqrt(dx*dx + dy*dy)
|
||||
if distance > 0:
|
||||
new_distance = distance * scale_factor
|
||||
scale_ratio = new_distance / distance
|
||||
|
||||
|
||||
new_x = pivot[0] + dx * scale_ratio
|
||||
new_y = pivot[1] + dy * scale_ratio
|
||||
return new_x, new_y
|
||||
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
def _calculate_center_of_mass(self, keypoints):
|
||||
"""Calculate center of mass from valid keypoints."""
|
||||
valid_points = []
|
||||
for i in range(0, len(keypoints), 3):
|
||||
if len(keypoints) > i+2 and keypoints[i+2] > 0:
|
||||
valid_points.append([keypoints[i], keypoints[i+1]])
|
||||
|
||||
|
||||
if not valid_points:
|
||||
return None
|
||||
|
||||
|
||||
pivot_x = sum(p[0] for p in valid_points) / len(valid_points)
|
||||
pivot_y = sum(p[1] for p in valid_points) / len(valid_points)
|
||||
return [pivot_x, pivot_y]
|
||||
|
||||
|
||||
def _apply_transformations(self, keypoints, scale_factor, x_offset, y_offset, rotation, pivot, bidirectional_scale):
|
||||
"""Apply transformations to all keypoints."""
|
||||
new_keypoints = []
|
||||
for i in range(0, len(keypoints), 3):
|
||||
if len(keypoints) > i+2:
|
||||
x, y, conf = keypoints[i], keypoints[i+1], keypoints[i+2]
|
||||
|
||||
|
||||
if conf > 0:
|
||||
# Apply rotation
|
||||
if rotation != 0.0:
|
||||
@@ -306,7 +366,7 @@ class AppendageEditorNode:
|
||||
rel_x, rel_y = x - pivot[0], y - pivot[1]
|
||||
x = rel_x * cos_r - rel_y * sin_r + pivot[0]
|
||||
y = rel_x * sin_r + rel_y * cos_r + pivot[1]
|
||||
|
||||
|
||||
# Apply scaling
|
||||
if scale_factor != 1.0:
|
||||
if bidirectional_scale:
|
||||
@@ -315,11 +375,11 @@ class AppendageEditorNode:
|
||||
else:
|
||||
# For hands, use unidirectional scaling from wrist
|
||||
x, y = self._apply_unidirectional_scale([x, y], scale_factor, pivot, i//3, 0)
|
||||
|
||||
|
||||
# Apply offset
|
||||
x += x_offset
|
||||
y += y_offset
|
||||
|
||||
|
||||
new_keypoints.extend([x, y, conf])
|
||||
|
||||
|
||||
return new_keypoints
|
||||
|
||||
@@ -1,893 +0,0 @@
|
||||
{
|
||||
"id": "b7f4ff12-a2ac-431b-8ea5-f6f2e7e1b982",
|
||||
"revision": 0,
|
||||
"last_node_id": 567,
|
||||
"last_link_id": 1093,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 556,
|
||||
"type": "AppendageEditorNode",
|
||||
"pos": [
|
||||
53.78276062011719,
|
||||
1469.3089599609375
|
||||
],
|
||||
"size": [
|
||||
456,
|
||||
226
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1078
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"links": [
|
||||
1080
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "ea50244cd95102872209f60087ed49c80e750ff8",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "AppendageEditorNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"shoulders",
|
||||
0.6300000000000001,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
-1,
|
||||
"loop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 557,
|
||||
"type": "AppendageEditorNode",
|
||||
"pos": [
|
||||
636.4037475585938,
|
||||
1472.977783203125
|
||||
],
|
||||
"size": [
|
||||
456,
|
||||
226
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1080
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"links": [
|
||||
1082
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "ea50244cd95102872209f60087ed49c80e750ff8",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "AppendageEditorNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"torso",
|
||||
0.6600000000000001,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
-1,
|
||||
"loop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 558,
|
||||
"type": "AppendageEditorNode",
|
||||
"pos": [
|
||||
1213.678466796875,
|
||||
1469.1339111328125
|
||||
],
|
||||
"size": [
|
||||
456,
|
||||
226
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1082
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"links": [
|
||||
1084
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "ea50244cd95102872209f60087ed49c80e750ff8",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "AppendageEditorNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"left_lower_leg",
|
||||
1,
|
||||
0.10000000000000002,
|
||||
0,
|
||||
11.818115234375,
|
||||
false,
|
||||
-1,
|
||||
"loop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 521,
|
||||
"type": "PoseRendererNode",
|
||||
"pos": [
|
||||
1762.187744140625,
|
||||
1466.8565673828125
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
202
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1084
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
1019
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "4cfb549344eb2c6f893b364da88d690c5414d3a2",
|
||||
"Node name for S&R": "PoseRendererNode",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
-1,
|
||||
4,
|
||||
3,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 524,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
2159.125,
|
||||
1455.14111328125
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 15,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 1019
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.30",
|
||||
"Node name for S&R": "PreviewImage",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 518,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-1169.2633056640625,
|
||||
1442.111328125
|
||||
],
|
||||
"size": [
|
||||
342.5999755859375,
|
||||
314
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
1014
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.30",
|
||||
"Node name for S&R": "LoadImage",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"tpose.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 516,
|
||||
"type": "OpenposePreprocessor",
|
||||
"pos": [
|
||||
-686.077392578125,
|
||||
1477.248779296875
|
||||
],
|
||||
"size": [
|
||||
481.1999816894531,
|
||||
174
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 1014
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"links": [
|
||||
1078,
|
||||
1086
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui_controlnet_aux",
|
||||
"ver": "1.0.7",
|
||||
"Node name for S&R": "OpenposePreprocessor",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"enable",
|
||||
"enable",
|
||||
"enable",
|
||||
512,
|
||||
"disable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 564,
|
||||
"type": "FeatureToFlexFloatParam",
|
||||
"pos": [
|
||||
20,
|
||||
2690
|
||||
],
|
||||
"size": [
|
||||
343.35400390625,
|
||||
106
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "feature",
|
||||
"type": "FEATURE",
|
||||
"link": 1088
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "PARAMETER",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
1090
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui_ryanontheinside",
|
||||
"ver": "5907a3591a2feb5531d533194b6c26ead84ce835",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "FeatureToFlexFloatParam"
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
0.5000000000000001,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 565,
|
||||
"type": "PoseRendererNode",
|
||||
"pos": [
|
||||
1610,
|
||||
2450
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
202
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1093
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
1092
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "4cfb549344eb2c6f893b364da88d690c5414d3a2",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "PoseRendererNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
-1,
|
||||
4,
|
||||
3,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 561,
|
||||
"type": "AppendageEditorNode",
|
||||
"pos": [
|
||||
1090,
|
||||
2430
|
||||
],
|
||||
"size": [
|
||||
456,
|
||||
226
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1085
|
||||
},
|
||||
{
|
||||
"name": "scale",
|
||||
"shape": 7,
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "scale"
|
||||
},
|
||||
"link": 1090
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"links": [
|
||||
1093
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "ea50244cd95102872209f60087ed49c80e750ff8",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "AppendageEditorNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"torso",
|
||||
0.6600000000000001,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
-1,
|
||||
"loop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 559,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
-1086.4820556640625,
|
||||
1180.427734375
|
||||
],
|
||||
"size": [
|
||||
362.96533203125,
|
||||
155.5653076171875
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"Chain any number of the appendage editors together, then render with the pose renderer node."
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 560,
|
||||
"type": "AppendageEditorNode",
|
||||
"pos": [
|
||||
570,
|
||||
2430
|
||||
],
|
||||
"size": [
|
||||
456,
|
||||
226
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"link": 1086
|
||||
},
|
||||
{
|
||||
"name": "scale",
|
||||
"shape": 7,
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "scale"
|
||||
},
|
||||
"link": 1089
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "POSE_KEYPOINT",
|
||||
"type": "POSE_KEYPOINT",
|
||||
"links": [
|
||||
1085
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "westNeighbor_ComfyUI-ultimate-openpose-editor",
|
||||
"ver": "ea50244cd95102872209f60087ed49c80e750ff8",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "AppendageEditorNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"shoulders",
|
||||
0.6300000000000001,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
-1,
|
||||
"loop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 562,
|
||||
"type": "DrawableFeatureNode",
|
||||
"pos": [
|
||||
-1188.8538818359375,
|
||||
2666.18310546875
|
||||
],
|
||||
"size": [
|
||||
1050,
|
||||
800
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "FEATURE",
|
||||
"type": "FEATURE",
|
||||
"links": [
|
||||
1087,
|
||||
1088
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui_ryanontheinside",
|
||||
"ver": "5907a3591a2feb5531d533194b6c26ead84ce835",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "DrawableFeatureNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"drawn",
|
||||
30,
|
||||
30,
|
||||
512,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
"linear",
|
||||
0,
|
||||
"[[0,0.00197697778680872],[12,0.6067085132724491],[17,0.549718180796861],[21,0.8061749426494923],[26,0.796676465332732],[29,0.9993090183870311]]",
|
||||
"selected_template",
|
||||
1,
|
||||
"load",
|
||||
"clear"
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0.00197697778680872
|
||||
],
|
||||
[
|
||||
12,
|
||||
0.6067085132724491
|
||||
],
|
||||
[
|
||||
17,
|
||||
0.549718180796861
|
||||
],
|
||||
[
|
||||
21,
|
||||
0.8061749426494923
|
||||
],
|
||||
[
|
||||
26,
|
||||
0.796676465332732
|
||||
],
|
||||
[
|
||||
29,
|
||||
0.9993090183870311
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 567,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
-1087.937255859375,
|
||||
2311.41943359375
|
||||
],
|
||||
"size": [
|
||||
406.4625549316406,
|
||||
253.2763671875
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"You can also schedule the parameters using features. We still have a single image as input, but 30 frames in the feature, so the number of images is increased (according to list_mismatch_behavior). Instead of a single image, a video of poses can be used if so desired. \n\nThis is a drawable feature below, but there are many feature sources including but not limited to \n\nAudio\nMIDI\nDepth\nColor\nProximity\nTime\nText \nArea\nand so on"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 563,
|
||||
"type": "FeatureToFlexFloatParam",
|
||||
"pos": [
|
||||
20,
|
||||
2490
|
||||
],
|
||||
"size": [
|
||||
330.15570068359375,
|
||||
109.04576110839844
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "feature",
|
||||
"type": "FEATURE",
|
||||
"link": 1087
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "PARAMETER",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
1089
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui_ryanontheinside",
|
||||
"ver": "5907a3591a2feb5531d533194b6c26ead84ce835",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "FeatureToFlexFloatParam"
|
||||
},
|
||||
"widgets_values": [
|
||||
false,
|
||||
0.6000000000000001,
|
||||
3.3600000000000008
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 566,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1983.6669921875,
|
||||
2444.78759765625
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 1092
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.30",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1014,
|
||||
518,
|
||||
0,
|
||||
516,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
1019,
|
||||
521,
|
||||
0,
|
||||
524,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
1078,
|
||||
516,
|
||||
1,
|
||||
556,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
],
|
||||
[
|
||||
1080,
|
||||
556,
|
||||
0,
|
||||
557,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
],
|
||||
[
|
||||
1082,
|
||||
557,
|
||||
0,
|
||||
558,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
],
|
||||
[
|
||||
1084,
|
||||
558,
|
||||
0,
|
||||
521,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
],
|
||||
[
|
||||
1085,
|
||||
560,
|
||||
0,
|
||||
561,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
],
|
||||
[
|
||||
1086,
|
||||
516,
|
||||
1,
|
||||
560,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
],
|
||||
[
|
||||
1087,
|
||||
562,
|
||||
0,
|
||||
563,
|
||||
0,
|
||||
"FEATURE"
|
||||
],
|
||||
[
|
||||
1088,
|
||||
562,
|
||||
0,
|
||||
564,
|
||||
0,
|
||||
"FEATURE"
|
||||
],
|
||||
[
|
||||
1089,
|
||||
563,
|
||||
0,
|
||||
560,
|
||||
1,
|
||||
"FLOAT"
|
||||
],
|
||||
[
|
||||
1090,
|
||||
564,
|
||||
0,
|
||||
561,
|
||||
1,
|
||||
"FLOAT"
|
||||
],
|
||||
[
|
||||
1092,
|
||||
565,
|
||||
0,
|
||||
566,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
1093,
|
||||
561,
|
||||
0,
|
||||
565,
|
||||
0,
|
||||
"POSE_KEYPOINT"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Single image",
|
||||
"bounding": [
|
||||
-1244.7318115234375,
|
||||
998.99267578125,
|
||||
3810.3720703125,
|
||||
943.616943359375
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Multi image",
|
||||
"bounding": [
|
||||
-1244.4027099609375,
|
||||
2165.394775390625,
|
||||
3705.696533203125,
|
||||
1379.8316650390625
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.25937424601001946,
|
||||
"offset": [
|
||||
2334.3305516827477,
|
||||
-871.2916448549256
|
||||
]
|
||||
},
|
||||
"node_versions": {
|
||||
"comfy-core": "0.3.15",
|
||||
"ComfyUI-VideoHelperSuite": "8629188458dc6cb832f871ece3bd273507e8a766",
|
||||
"ComfyUI-Custom-Scripts": "a53ef9b617ed1331640d7a2cd97644995908dc00",
|
||||
"ComfyUI_Comfyroll_CustomNodes": "d78b780ae43fcf8c6b7c6505e6ffb4584281ceca",
|
||||
"ComfyUI-KJNodes": "86b5453a5ca9ecb883eedc9d0a96bf942b9ca73e",
|
||||
"comfyui_ttp_toolset": "6dd3f3566ce0925b71e9cdb54243119685ccbc10",
|
||||
"was-node-suite-comfyui": "3ed45af34a14551dc28cb3127235cc7197d4633f",
|
||||
"ComfyUI-Frame-Interpolation": "c336f7184cb1ac1243381e725fea1ad2c0a10c09",
|
||||
"comfyui-easy-use": "123917da9adec0d2b0b5f817deefb9ac3ed464f1",
|
||||
"ComfyLiterals": "bdddb08ca82d90d75d97b1d437a652e0284a32ac",
|
||||
"ComfyUI-Image-Filters": "0ff33fe29f7be072ad5d2cd89efa18fed82957fe",
|
||||
"ComfyUI_essentials": "33ff89fd354d8ec3ab6affb605a79a931b445d99",
|
||||
"ComfyUI-Impact-Pack": "1ae7cae2df8cca06027edfa3a24512671239d6c4",
|
||||
"comfyui-detail-daemon": "90e703d3d3f979438471c646a5d030840a2caac3",
|
||||
"comfyui-reactor-node": "a43075813dbd17de9c51c9fc7ea768eaf1681d89",
|
||||
"ComfyUI-HunyuanVideoMultiLora": "7e3e3444d4e34557a24b3e0c502c94fe556237e4",
|
||||
"ComfyUI-AutomaticCFG": "2e395317b65c05a97a0ef566c4a8c7969305dafa",
|
||||
"ComfyUI_UltimateSDUpscale": "ff3fdfeee03de46d4462211cffd165d27155e858",
|
||||
"ComfyUI-DepthAnythingV2": "003d7b44bafd3a8a4c3693a9ca3ddcd72f4883ab",
|
||||
"comfyui-various": "36454f91606bbff4fc36d90234981ca4a47e2695",
|
||||
"ComfyUI_SLK_joy_caption_two": "667751cab945bd8e9fb0be4d557d47e36821350a",
|
||||
"comfyui-ollama": "cfa314d36efbb2344ba22a76256200016ad9570a",
|
||||
"ComfyUI-Florence2": "90b012e922f8bb0482bcd2ae24cdc191ec12a11f",
|
||||
"save-image-extended-comfyui": "5f104fddfc8281d9fd85d03e63c5f70454db6701"
|
||||
},
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true,
|
||||
"ue_links": [],
|
||||
"links_added_by_ue": [],
|
||||
"frontendVersion": "1.17.11"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -50,7 +50,7 @@ class OpenposeEditorDialog extends ComfyDialog {
|
||||
const message = event.data;
|
||||
if (message.modalId === 0) {
|
||||
const targetNode = ComfyApp.clipspace_return_node;
|
||||
const textAreaElement = targetNode.widgets[12].element;
|
||||
const textAreaElement = targetNode.widgets[14].element;
|
||||
textAreaElement.value = JSON.stringify(event.data.poses);
|
||||
ComfyApp.onClipspaceEditorClosed();
|
||||
this.close();
|
||||
@@ -82,11 +82,11 @@ class OpenposeEditorDialog extends ComfyDialog {
|
||||
|
||||
const targetNode = ComfyApp.clipspace_return_node;
|
||||
if (targetNode.inputs?.[0].link || targetNode.inputs?.[targetNode.inputs.length-1].widget){
|
||||
const textAreaElement = targetNode.widgets[13].element;
|
||||
const textAreaElement = targetNode.widgets[15].element;
|
||||
this.element.style.display = "flex";
|
||||
this.setCanvasJSONString(textAreaElement.value.replace(/'/g, '"'));
|
||||
} else {
|
||||
const textAreaElement = targetNode.widgets[12].element;
|
||||
const textAreaElement = targetNode.widgets[14].element;
|
||||
this.element.style.display = "flex";
|
||||
if (textAreaElement.value === "") {
|
||||
let resolution_x = targetNode.widgets[3].value;
|
||||
|
||||
+55
-163
@@ -1,72 +1,11 @@
|
||||
import json
|
||||
import torch
|
||||
import numpy as np
|
||||
from .util import draw_pose_json, draw_pose
|
||||
from .util import draw_pose_json, draw_pose, extend_scalelist, pose_normalized
|
||||
|
||||
OpenposeJSON = dict
|
||||
|
||||
class OpenposeEditorNode:
|
||||
@staticmethod
|
||||
def normalize_scale_parameter(scale_param, target_length, behavior):
|
||||
"""
|
||||
Normalize a scale parameter to a list of the target length.
|
||||
|
||||
Args:
|
||||
scale_param: Either a single float or list of floats
|
||||
target_length: Desired length of output list
|
||||
behavior: "truncate", "loop", or "repeat"
|
||||
|
||||
Returns:
|
||||
List of floats with length determined by behavior
|
||||
"""
|
||||
# Convert single value to list
|
||||
if not isinstance(scale_param, (list, tuple)):
|
||||
scale_list = [scale_param]
|
||||
else:
|
||||
scale_list = list(scale_param)
|
||||
|
||||
if len(scale_list) == target_length:
|
||||
return scale_list
|
||||
|
||||
if behavior == "truncate":
|
||||
return scale_list[:target_length]
|
||||
elif behavior == "loop":
|
||||
if len(scale_list) == 0:
|
||||
return [1.0] * target_length
|
||||
result = []
|
||||
for i in range(target_length):
|
||||
result.append(scale_list[i % len(scale_list)])
|
||||
return result
|
||||
elif behavior == "repeat":
|
||||
if len(scale_list) == 0:
|
||||
return [1.0] * target_length
|
||||
if len(scale_list) >= target_length:
|
||||
return scale_list[:target_length]
|
||||
else:
|
||||
result = scale_list[:]
|
||||
last_value = scale_list[-1]
|
||||
while len(result) < target_length:
|
||||
result.append(last_value)
|
||||
return result
|
||||
else:
|
||||
raise ValueError(f"Unknown behavior: {behavior}")
|
||||
|
||||
@staticmethod
|
||||
def determine_output_length(scale_params, pose_count, behavior):
|
||||
"""
|
||||
Determine the output length based on scale parameters and behavior.
|
||||
"""
|
||||
# Get all list lengths
|
||||
lengths = [pose_count]
|
||||
for param in scale_params:
|
||||
if isinstance(param, (list, tuple)):
|
||||
lengths.append(len(param))
|
||||
|
||||
if behavior == "truncate":
|
||||
return min(lengths)
|
||||
else: # loop or repeat
|
||||
return max(lengths)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
@@ -77,7 +16,8 @@ class OpenposeEditorNode:
|
||||
"resolution_x": ("INT", {
|
||||
"default": -1,
|
||||
"min": -1,
|
||||
"max": 12800
|
||||
"max": 12800,
|
||||
"tooltip": "Resolution X. -1 means use the original resolution."
|
||||
}),
|
||||
"pose_marker_size": ("INT", {
|
||||
"default": 4,
|
||||
@@ -118,7 +58,14 @@ class OpenposeEditorNode:
|
||||
"max": 10.0,
|
||||
"step": 0.05
|
||||
}),
|
||||
"list_mismatch_behavior": (["truncate", "loop", "repeat"], {"default": "loop", "tooltip": "Truncate: Truncate the list to the shortest length. Loop: Loop the list to the longest length. Repeat: Repeat the list to the longest length."}),
|
||||
"scalelist_behavior": (["poses", "images"], {"default": "poses", "tooltip": "When the scale input is a list, this determines how the scale list takes effect, the differences appear when there are multiple persons(poses) in one image."}),
|
||||
"match_scalelist_method": (["no extend", "loop extend", "clamp extend"], {"default": "loop extend", "tooltip": "Match the scale list to the input poses or images when the scale list length is shorter. No extend: Beyound the scale list will be 1.0. Loop: Loop the scale list to match the poses or images length. Clamp: Use the last scale value to extend the scale list."}),
|
||||
"only_scale_pose_index": ("INT", {
|
||||
"default": 99,
|
||||
"min": -100,
|
||||
"max": 100,
|
||||
"tooltip": "For multiple poses in one image, the scale will be only applied at desired index. If set to a number larger than the number of poses in the image, the scale will be applied to all poses. Negative number will apply to the pose from the end."
|
||||
}),
|
||||
"POSE_JSON": ("STRING", {"multiline": True}),
|
||||
"POSE_KEYPOINT": ("POSE_KEYPOINT",{"default": None}),
|
||||
},
|
||||
@@ -130,116 +77,61 @@ class OpenposeEditorNode:
|
||||
FUNCTION = "load_pose"
|
||||
CATEGORY = "ultimate-openpose"
|
||||
|
||||
def load_pose(self, show_body, show_face, show_hands, resolution_x, pose_marker_size, face_marker_size, hand_marker_size, hands_scale, body_scale, head_scale, overall_scale, list_mismatch_behavior, POSE_JSON: str, POSE_KEYPOINT=None) -> tuple[OpenposeJSON]:
|
||||
def load_pose(self, show_body, show_face, show_hands, resolution_x, pose_marker_size, face_marker_size, hand_marker_size, hands_scale, body_scale, head_scale, overall_scale, scalelist_behavior, match_scalelist_method, only_scale_pose_index, POSE_JSON: str, POSE_KEYPOINT=None) -> tuple[OpenposeJSON]:
|
||||
'''
|
||||
priority output is: POSE_JSON > POSE_KEYPOINT
|
||||
priority edit is: POSE_KEYPOINT > POSE_JSON
|
||||
'''
|
||||
|
||||
# Determine the input data and count
|
||||
if POSE_JSON:
|
||||
POSE_JSON = POSE_JSON.replace("'",'"').replace('None','[]')
|
||||
POSE_PASS = POSE_JSON
|
||||
if POSE_KEYPOINT is not None:
|
||||
POSE_PASS = json.dumps(POSE_KEYPOINT,indent=4).replace("'",'"').replace('None','[]')
|
||||
|
||||
# Parse to determine image count
|
||||
if POSE_JSON.startswith('{'):
|
||||
pose_data = [json.loads(POSE_JSON)]
|
||||
else:
|
||||
pose_data = json.loads(POSE_JSON)
|
||||
pose_count = len(pose_data)
|
||||
|
||||
hands_scalelist, body_scalelist, head_scalelist, overall_scalelist = extend_scalelist(
|
||||
scalelist_behavior, POSE_PASS, hands_scale, body_scale, head_scale, overall_scale,
|
||||
match_scalelist_method, only_scale_pose_index)
|
||||
pose_imgs, POSE_PASS = draw_pose_json(POSE_PASS, resolution_x, show_body, show_face, show_hands, pose_marker_size, face_marker_size, hand_marker_size, hands_scalelist, body_scalelist, head_scalelist, overall_scalelist)
|
||||
|
||||
# parse the JSON
|
||||
hands_scalelist, body_scalelist, head_scalelist, overall_scalelist = extend_scalelist(
|
||||
scalelist_behavior, POSE_JSON, hands_scale, body_scale, head_scale, overall_scale,
|
||||
match_scalelist_method, only_scale_pose_index)
|
||||
pose_imgs, POSE_JSON_SCALED = draw_pose_json(POSE_JSON, resolution_x, show_body, show_face, show_hands, pose_marker_size, face_marker_size, hand_marker_size, hands_scalelist, body_scalelist, head_scalelist, overall_scalelist)
|
||||
if pose_imgs:
|
||||
pose_imgs_np = np.array(pose_imgs).astype(np.float32) / 255
|
||||
return {
|
||||
"ui": {"POSE_JSON": [json.dumps(POSE_PASS, indent=4)]},
|
||||
"result": (torch.from_numpy(pose_imgs_np), POSE_JSON_SCALED, json.dumps(POSE_JSON_SCALED,indent=4))
|
||||
}
|
||||
elif POSE_KEYPOINT is not None:
|
||||
if isinstance(POSE_KEYPOINT, list):
|
||||
pose_data = POSE_KEYPOINT
|
||||
pose_count = len(pose_data)
|
||||
else:
|
||||
pose_data = [POSE_KEYPOINT]
|
||||
pose_count = 1
|
||||
POSE_JSON = json.dumps(pose_data, indent=4).replace("'",'"').replace('None','[]')
|
||||
POSE_PASS = POSE_JSON
|
||||
else:
|
||||
# Default case - create blank image
|
||||
W=512
|
||||
H=768
|
||||
pose_draw = dict(bodies={'candidate':[], 'subset':[]}, faces=[], hands=[])
|
||||
pose_out = dict(pose_keypoints_2d=[], face_keypoints_2d=[], hand_left_keypoints_2d=[], hand_right_keypoints_2d=[])
|
||||
people=[dict(people=[pose_out], canvas_height=H, canvas_width=W)]
|
||||
POSE_JSON = json.dumps(POSE_KEYPOINT,indent=4).replace("'",'"').replace('None','[]')
|
||||
hands_scalelist, body_scalelist, head_scalelist, overall_scalelist = extend_scalelist(
|
||||
scalelist_behavior, POSE_JSON, hands_scale, body_scale, head_scale, overall_scale,
|
||||
match_scalelist_method, only_scale_pose_index)
|
||||
normalized_pose_json = pose_normalized(POSE_JSON)
|
||||
pose_imgs, POSE_SCALED = draw_pose_json(normalized_pose_json, resolution_x, show_body, show_face, show_hands, pose_marker_size, face_marker_size, hand_marker_size, hands_scalelist, body_scalelist, head_scalelist, overall_scalelist)
|
||||
if pose_imgs:
|
||||
pose_imgs_np = np.array(pose_imgs).astype(np.float32) / 255
|
||||
return {
|
||||
"ui": {"POSE_JSON": [json.dumps(POSE_SCALED, indent=4)]},
|
||||
"result": (torch.from_numpy(pose_imgs_np), POSE_SCALED, json.dumps(POSE_SCALED, indent=4))
|
||||
}
|
||||
|
||||
W_scaled = resolution_x
|
||||
if resolution_x < 64:
|
||||
W_scaled = W
|
||||
H_scaled = int(H*(W_scaled*1.0/W))
|
||||
pose_img = [draw_pose(pose_draw, H_scaled, W_scaled, pose_marker_size, face_marker_size, hand_marker_size)]
|
||||
pose_img_np = np.array(pose_img).astype(np.float32) / 255
|
||||
# otherwise output blank images
|
||||
W=512
|
||||
H=768
|
||||
pose_draw = dict(bodies={'candidate':[], 'subset':[]}, faces=[], hands=[])
|
||||
pose_out = dict(pose_keypoints_2d=[], face_keypoints_2d=[], hand_left_keypoints_2d=[], hand_right_keypoints_2d=[])
|
||||
people=[dict(people=[pose_out], canvas_height=H, canvas_width=W)]
|
||||
|
||||
return {
|
||||
W_scaled = resolution_x
|
||||
if resolution_x < 64:
|
||||
W_scaled = W
|
||||
H_scaled = int(H*(W_scaled*1.0/W))
|
||||
pose_img = [draw_pose(pose_draw, H_scaled, W_scaled, pose_marker_size, face_marker_size, hand_marker_size)]
|
||||
pose_img_np = np.array(pose_img).astype(np.float32) / 255
|
||||
|
||||
return {
|
||||
"ui": {"POSE_JSON": people},
|
||||
"result": (torch.from_numpy(pose_img_np), people, json.dumps(people))
|
||||
}
|
||||
|
||||
# Normalize scale parameters
|
||||
scale_params = [hands_scale, body_scale, head_scale, overall_scale]
|
||||
output_length = self.determine_output_length(scale_params, pose_count, list_mismatch_behavior)
|
||||
|
||||
hands_scale_list = self.normalize_scale_parameter(hands_scale, output_length, list_mismatch_behavior)
|
||||
body_scale_list = self.normalize_scale_parameter(body_scale, output_length, list_mismatch_behavior)
|
||||
head_scale_list = self.normalize_scale_parameter(head_scale, output_length, list_mismatch_behavior)
|
||||
overall_scale_list = self.normalize_scale_parameter(overall_scale, output_length, list_mismatch_behavior)
|
||||
|
||||
# Process each image with its corresponding scale values
|
||||
all_pose_imgs = []
|
||||
output_pose_data = []
|
||||
|
||||
for i in range(output_length):
|
||||
# Get the pose data for this index
|
||||
pose_idx = i if i < pose_count else pose_count - 1
|
||||
if list_mismatch_behavior == "loop" and pose_count > 0:
|
||||
pose_idx = i % pose_count
|
||||
|
||||
current_pose_json = json.dumps([pose_data[pose_idx]])
|
||||
|
||||
# Get scale values for this index
|
||||
current_hands_scale = hands_scale_list[i]
|
||||
current_body_scale = body_scale_list[i]
|
||||
current_head_scale = head_scale_list[i]
|
||||
current_overall_scale = overall_scale_list[i]
|
||||
|
||||
# Process this image
|
||||
pose_imgs = draw_pose_json(
|
||||
current_pose_json,
|
||||
resolution_x,
|
||||
show_body,
|
||||
show_face,
|
||||
show_hands,
|
||||
pose_marker_size,
|
||||
face_marker_size,
|
||||
hand_marker_size,
|
||||
current_hands_scale,
|
||||
current_body_scale,
|
||||
current_head_scale,
|
||||
current_overall_scale
|
||||
)
|
||||
|
||||
if pose_imgs:
|
||||
all_pose_imgs.extend(pose_imgs)
|
||||
# Store the processed pose data
|
||||
processed_pose = json.loads(current_pose_json)[0]
|
||||
output_pose_data.append(processed_pose)
|
||||
|
||||
if all_pose_imgs:
|
||||
pose_imgs_np = np.array(all_pose_imgs).astype(np.float32) / 255
|
||||
return {
|
||||
"ui": {"POSE_JSON": [json.dumps(output_pose_data, indent=4)]},
|
||||
"result": (torch.from_numpy(pose_imgs_np), output_pose_data, json.dumps(output_pose_data))
|
||||
}
|
||||
|
||||
# Fallback to original behavior if no images generated
|
||||
pose_imgs = draw_pose_json(POSE_JSON, resolution_x, show_body, show_face, show_hands, pose_marker_size, face_marker_size, hand_marker_size, hands_scale_list[0] if hands_scale_list else 1.0, body_scale_list[0] if body_scale_list else 1.0, head_scale_list[0] if head_scale_list else 1.0, overall_scale_list[0] if overall_scale_list else 1.0)
|
||||
if pose_imgs:
|
||||
pose_imgs_np = np.array(pose_imgs).astype(np.float32) / 255
|
||||
return {
|
||||
"ui": {"POSE_JSON": [POSE_PASS]},
|
||||
"result": (torch.from_numpy(pose_imgs_np), json.loads(POSE_JSON), POSE_JSON)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,97 +4,238 @@ import numpy as np
|
||||
import matplotlib
|
||||
import cv2
|
||||
from comfy.utils import ProgressBar
|
||||
from typing import List, Dict
|
||||
|
||||
eps = 0.01
|
||||
|
||||
def scale(point, scale_factor, pivot):
|
||||
return [(point[0] - pivot[0]) * scale_factor + pivot[0], (point[1] - pivot[1]) * scale_factor + pivot[1]]
|
||||
def extend_scalelist(scalelist_behavior, pose_json, hands_scale, body_scale, head_scale, overall_scale, match_scalelist_method, only_scale_pose_index) -> List[list]:
|
||||
if pose_json.startswith('{'):
|
||||
pose_json = '[{}]'.format(pose_json)
|
||||
poses = json.loads(pose_json)
|
||||
# initialize scale lists
|
||||
hands_scalelist, body_scalelist, head_scalelist, overall_scalelist = [], [], [], []
|
||||
num_imgs = 0
|
||||
num_poses = 0
|
||||
scale_values = [hands_scale, body_scale, head_scale, overall_scale]
|
||||
scale_lists = [hands_scalelist, body_scalelist, head_scalelist, overall_scalelist]
|
||||
for img in poses:
|
||||
default_scale = 0.0
|
||||
default_num_person = 1
|
||||
if 'people' in img:
|
||||
default_scale = 1.0
|
||||
default_num_person = len(img['people'])
|
||||
subscales = [default_scale]*default_num_person
|
||||
if scalelist_behavior == 'poses':
|
||||
for i, scales in enumerate(scale_values):
|
||||
if isinstance(scales, (list, tuple)):
|
||||
if len(scales) >= num_poses + default_num_person:
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = scales[num_poses + only_scale_pose_index]
|
||||
else:
|
||||
subscales = scales[num_poses:num_poses + default_num_person]
|
||||
else:
|
||||
if match_scalelist_method == 'no extend':
|
||||
subscales = [default_scale]*default_num_person
|
||||
elif match_scalelist_method == 'loop extend':
|
||||
extend_scaleslist = scales*math.ceil((num_poses+default_num_person) / len(scales))
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = extend_scaleslist[num_poses + only_scale_pose_index]
|
||||
else:
|
||||
subscales = extend_scaleslist[num_poses:num_poses + default_num_person]
|
||||
elif match_scalelist_method == 'clamp extend':
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = scales[-1]
|
||||
else:
|
||||
subscales = [scales[-1]] * default_num_person
|
||||
else:
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = scales
|
||||
else:
|
||||
subscales = [scales] * default_num_person
|
||||
|
||||
def draw_pose_json(pose_json, resolution_x, show_body, show_face, show_hands, pose_marker_size, face_marker_size, hand_marker_size, hands_scale, body_scale, head_scale, overall_scale):
|
||||
scale_lists[i].append(subscales.copy())
|
||||
else:
|
||||
for i, scales in enumerate(scale_values):
|
||||
if isinstance(scales, (list, tuple)):
|
||||
if len(scales) >= num_imgs + 1:
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = scales[num_imgs]
|
||||
else:
|
||||
subscales = scales[num_poses]*default_num_person
|
||||
else:
|
||||
if match_scalelist_method == 'no extend':
|
||||
subscales = [default_scale]*default_num_person
|
||||
elif match_scalelist_method == 'loop extend':
|
||||
extend_scaleslist = scales*math.ceil((num_imgs+1) / len(scales))
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = extend_scaleslist[num_imgs]
|
||||
else:
|
||||
subscales = extend_scaleslist[num_imgs]*default_num_person
|
||||
elif match_scalelist_method == 'clamp extend':
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = scales[-1]
|
||||
else:
|
||||
subscales = [scales[-1]] * default_num_person
|
||||
else:
|
||||
if only_scale_pose_index<default_num_person and only_scale_pose_index >= -default_num_person:
|
||||
subscales[only_scale_pose_index] = scales
|
||||
else:
|
||||
subscales = [scales] * default_num_person
|
||||
|
||||
scale_lists[i].append(subscales.copy())
|
||||
|
||||
num_poses += default_num_person
|
||||
num_imgs += 1
|
||||
else:
|
||||
# if no people in image
|
||||
for i in range(len(scale_values)):
|
||||
scale_lists[i].append([default_scale])
|
||||
|
||||
return scale_lists
|
||||
|
||||
def pose_normalized(pose_json):
|
||||
if pose_json.startswith('{'):
|
||||
pose_json = '[{}]'.format(pose_json)
|
||||
images = json.loads(pose_json)
|
||||
for image in images:
|
||||
if 'people' not in image:
|
||||
continue
|
||||
figures = image['people']
|
||||
H = image['canvas_height']
|
||||
W = image['canvas_width']
|
||||
normalized = 0.0
|
||||
for figure in figures:
|
||||
if 'pose_keypoints_2d' in figure:
|
||||
body = figure['pose_keypoints_2d']
|
||||
if body:
|
||||
normalized = max(body)
|
||||
if normalized > 2.0:
|
||||
break
|
||||
if 'face_keypoints_2d' in figure:
|
||||
face = figure['face_keypoints_2d']
|
||||
if face:
|
||||
normalized = max(face)
|
||||
if normalized > 2.0:
|
||||
break
|
||||
if 'hand_left_keypoints_2d' in figure:
|
||||
lhand = figure['hand_left_keypoints_2d']
|
||||
if lhand:
|
||||
normalized = max(lhand)
|
||||
if normalized > 2.0:
|
||||
break
|
||||
if 'hand_right_keypoints_2d' in figure:
|
||||
rhand = figure['hand_right_keypoints_2d']
|
||||
if rhand:
|
||||
normalized = max(rhand)
|
||||
if normalized > 2.0:
|
||||
break
|
||||
if normalized > 2.0:
|
||||
for figure in figures:
|
||||
if 'pose_keypoints_2d' in figure:
|
||||
body = figure['pose_keypoints_2d']
|
||||
for i in range(0, len(body), 3):
|
||||
body[i] = body[i] / float(W)
|
||||
body[i+1] = body[i+1] / float(H)
|
||||
if 'face_keypoints_2d' in figure:
|
||||
face = figure['face_keypoints_2d']
|
||||
for i in range(0, len(face), 3):
|
||||
face[i] = face[i] / float(W)
|
||||
face[i+1] = face[i+1] / float(H)
|
||||
if 'hand_left_keypoints_2d' in figure:
|
||||
lhand = figure['hand_left_keypoints_2d']
|
||||
for i in range(0, len(lhand), 3):
|
||||
lhand[i] = lhand[i] / float(W)
|
||||
lhand[i+1] = lhand[i+1] / float(H)
|
||||
if 'hand_right_keypoints_2d' in figure:
|
||||
rhand = figure['hand_right_keypoints_2d']
|
||||
for i in range(0, len(rhand), 3):
|
||||
rhand[i] = rhand[i] / float(W)
|
||||
rhand[i+1] = rhand[i+1] / float(H)
|
||||
return json.dumps(images)
|
||||
|
||||
def scale(point, scale_factor, pivot):
|
||||
return [(point[i] - pivot[i])*scale_factor + pivot[i] for i in range(len(point))]
|
||||
|
||||
def draw_pose_json(pose_json, resolution_x, show_body, show_face, show_hands, pose_marker_size, face_marker_size, hand_marker_size, hands_scalelist, body_scalelist, head_scalelist, overall_scalelist):
|
||||
pose_imgs = []
|
||||
|
||||
pose_scaled = []
|
||||
|
||||
if pose_json:
|
||||
if pose_json.startswith('{'):
|
||||
pose_json = '[{}]'.format(pose_json)
|
||||
images = json.loads(pose_json)
|
||||
pbar = ProgressBar(len(images))
|
||||
for image in images:
|
||||
for img_idx, image in enumerate(images):
|
||||
if 'people' not in image:
|
||||
pbar.update(len(images))
|
||||
return pose_imgs
|
||||
figures = image['people']
|
||||
H = image['canvas_height']
|
||||
W = image['canvas_width']
|
||||
|
||||
|
||||
bodies = []
|
||||
candidate = []
|
||||
subset = [[]]
|
||||
faces = []
|
||||
hands = []
|
||||
pivot = [W * 0.5, H * 0.5]
|
||||
|
||||
for figure_idx, figure in enumerate(figures):
|
||||
|
||||
openpose_json = []
|
||||
for pose_idx, figure in enumerate(figures):
|
||||
body_scale = body_scalelist[img_idx][pose_idx]
|
||||
hands_scale = hands_scalelist[img_idx][pose_idx]
|
||||
head_scale = head_scalelist[img_idx][pose_idx]
|
||||
overall_scale = overall_scalelist[img_idx][pose_idx]
|
||||
body = []
|
||||
face = []
|
||||
lhand = []
|
||||
rhand = []
|
||||
if 'pose_keypoints_2d' in figure:
|
||||
body = figure['pose_keypoints_2d']
|
||||
body_scaled = body.copy()
|
||||
if 'face_keypoints_2d' in figure:
|
||||
face = figure['face_keypoints_2d']
|
||||
face_scaled = face.copy()
|
||||
if 'hand_left_keypoints_2d' in figure:
|
||||
lhand = figure['hand_left_keypoints_2d']
|
||||
lhand_scaled = lhand.copy()
|
||||
if 'hand_right_keypoints_2d' in figure:
|
||||
rhand = figure['hand_right_keypoints_2d']
|
||||
|
||||
rhand_scaled = rhand.copy()
|
||||
|
||||
face_offset = [0, 0]
|
||||
lhand_offset = [0, 0]
|
||||
rhand_offset = [0, 0]
|
||||
|
||||
lhand_pivot = [W * 0.25, H * 0.5]
|
||||
rhand_pivot = [W * 0.75, H * 0.5]
|
||||
face_pivot = [W * 0.5, H * 0.5]
|
||||
overall_pivot = [0.5, 0.5]
|
||||
lhand_pivot = [0.25, 0.5]
|
||||
rhand_pivot = [0.75, 0.5]
|
||||
face_pivot = [0.5, 0.5]
|
||||
|
||||
if body:
|
||||
candidate_start_idx = len(candidate)
|
||||
|
||||
index = 0
|
||||
|
||||
for i in range(0,len(body),3):
|
||||
p = body[i:i+2]
|
||||
confidence = body[i+2]
|
||||
|
||||
if body_scale != 1.0:
|
||||
point = [(p[0] - 0.5) * body_scale + 0.5, (p[1] - 0.5) * body_scale + 0.5]
|
||||
else:
|
||||
point = p[:]
|
||||
|
||||
candidate.append(point)
|
||||
index += 1
|
||||
|
||||
p_scaled = scale(body[i:i+2], body_scale, overall_pivot)
|
||||
p_scaled = scale(p_scaled, overall_scale, overall_pivot)
|
||||
body_scaled[i:i+2] = p_scaled
|
||||
candidate.append(p_scaled)
|
||||
|
||||
figure_head_idx = candidate_start_idx
|
||||
if figure_head_idx < len(candidate):
|
||||
face_offset = [candidate[figure_head_idx][0] - body[0], candidate[figure_head_idx][1] - body[1]]
|
||||
face_offset = [a*0.8 for a in face_offset]
|
||||
else:
|
||||
face_offset = [0, 0]
|
||||
factor = 0.8
|
||||
face_offset = [(candidate[figure_head_idx][0] - body[0])*factor, (candidate[figure_head_idx][1] - body[1])*factor]
|
||||
face_pivot = candidate[figure_head_idx]
|
||||
|
||||
wrist_left_idx = candidate_start_idx + 7
|
||||
wrist_right_idx = candidate_start_idx + 4
|
||||
|
||||
|
||||
if wrist_left_idx < len(candidate) and len(body) > 22:
|
||||
lhand_offset = [candidate[wrist_left_idx][0] - body[21], candidate[wrist_left_idx][1] - body[22]]
|
||||
lhand_pivot = candidate[wrist_left_idx]
|
||||
else:
|
||||
lhand_offset = [0, 0]
|
||||
|
||||
|
||||
if wrist_right_idx < len(candidate) and len(body) > 13:
|
||||
rhand_offset = [candidate[wrist_right_idx][0] - body[12], candidate[wrist_right_idx][1] - body[13]]
|
||||
rhand_pivot = candidate[wrist_right_idx]
|
||||
else:
|
||||
rhand_offset = [0, 0]
|
||||
|
||||
if figure_head_idx < len(candidate):
|
||||
face_pivot = candidate[figure_head_idx]
|
||||
|
||||
if not subset[0]:
|
||||
subset[0].extend([candidate_start_idx+(i//3) if body[i+2]>0 else -1 for i in range(0,len(body),3)])
|
||||
@@ -106,81 +247,56 @@ def draw_pose_json(pose_json, resolution_x, show_body, show_face, show_hands, po
|
||||
f = []
|
||||
for i in range(0,len(face),3):
|
||||
p = face[i:i+2]
|
||||
confidence = face[i+2]
|
||||
p_offset = [p[0] + face_offset[0], p[1] + face_offset[1]]
|
||||
p_scaled = scale(p_offset, head_scale, face_pivot)
|
||||
p_scaled = scale(p_scaled, overall_scale, overall_pivot)
|
||||
face_scaled[i:i+2] = p_scaled
|
||||
f.append(p_scaled)
|
||||
faces.append(f)
|
||||
|
||||
|
||||
if lhand:
|
||||
lh = []
|
||||
for i in range(0, len(lhand), 3):
|
||||
p = lhand[i:i+2]
|
||||
p_offset = [p[0] + lhand_offset[0], p[1] + lhand_offset[1]]
|
||||
p_scaled = scale(p_offset, hands_scale, lhand_pivot)
|
||||
p_scaled = scale(p_scaled, overall_scale, overall_pivot)
|
||||
lhand_scaled[i:i+2] = p_scaled
|
||||
lh.append(p_scaled)
|
||||
hands.append(lh)
|
||||
|
||||
|
||||
if rhand:
|
||||
rh = []
|
||||
for i in range(0, len(rhand), 3):
|
||||
p = rhand[i:i+2]
|
||||
p_offset = [p[0] + rhand_offset[0], p[1] + rhand_offset[1]]
|
||||
p_scaled = scale(p_offset, hands_scale, rhand_pivot)
|
||||
p_scaled = scale(p_scaled, overall_scale, overall_pivot)
|
||||
rhand_scaled[i:i+2] = p_scaled
|
||||
rh.append(p_scaled)
|
||||
hands.append(rh)
|
||||
openpose_json.append(dict(pose_keypoints_2d=body_scaled, face_keypoints_2d=face_scaled, hand_left_keypoints_2d=lhand_scaled, hand_right_keypoints_2d=rhand_scaled))
|
||||
|
||||
normalized_pivot = [0.5, 0.5]
|
||||
|
||||
if hands:
|
||||
hands = [[scale(lm, overall_scale, normalized_pivot) for lm in hand] for hand in hands]
|
||||
if faces:
|
||||
faces = [[scale(lm, overall_scale, normalized_pivot) for lm in face] for face in faces]
|
||||
if candidate:
|
||||
candidate = [scale(lm, overall_scale, normalized_pivot) for lm in candidate]
|
||||
|
||||
if candidate:
|
||||
candidate = np.array(candidate).astype(float)
|
||||
subset = np.array(subset)
|
||||
max_x = np.max(candidate[...,0]) if len(candidate) > 0 else 0
|
||||
max_y = np.max(candidate[...,1]) if len(candidate) > 0 else 0
|
||||
normalized = max(max_x, max_y)
|
||||
if normalized > 2.0:
|
||||
candidate[...,0] = np.clip(candidate[...,0] / float(W), 0, 1)
|
||||
candidate[...,1] = np.clip(candidate[...,1] / float(H), 0, 1)
|
||||
|
||||
if faces:
|
||||
faces = np.array(faces).astype(float)
|
||||
max_x = np.max(faces[...,0]) if len(faces) > 0 else 0
|
||||
max_y = np.max(faces[...,1]) if len(faces) > 0 else 0
|
||||
normalized = max(max_x, max_y)
|
||||
if normalized > 2.0:
|
||||
faces[...,0] = np.clip(faces[...,0] / float(W), 0, 1)
|
||||
faces[...,1] = np.clip(faces[...,1] / float(H), 0, 1)
|
||||
|
||||
if hands:
|
||||
hands = np.array(hands).astype(float)
|
||||
max_x = np.max(hands[...,0]) if len(hands) > 0 else 0
|
||||
max_y = np.max(hands[...,1]) if len(hands) > 0 else 0
|
||||
normalized = max(max_x, max_y)
|
||||
if normalized > 2.0:
|
||||
hands[...,0] = np.clip(hands[...,0] / float(W), 0, 1)
|
||||
hands[...,1] = np.clip(hands[...,1] / float(H), 0, 1)
|
||||
|
||||
bodies = dict(candidate=candidate, subset=subset)
|
||||
pose = dict(bodies=bodies, faces=faces, hands=hands)
|
||||
pose = dict(bodies=bodies if show_body else {'candidate':[], 'subset':[]}, faces=faces if show_face else [], hands=hands if show_hands else [])
|
||||
|
||||
|
||||
W_scaled = resolution_x
|
||||
if resolution_x < 64:
|
||||
W_scaled = W
|
||||
H_scaled = int(H*(W_scaled*1.0/W))
|
||||
|
||||
openpose_json = {
|
||||
'people': openpose_json,
|
||||
'canvas_height': H_scaled,
|
||||
'canvas_width': W_scaled,
|
||||
}
|
||||
|
||||
pose_img = draw_pose(pose, H_scaled, W_scaled, pose_marker_size, face_marker_size, hand_marker_size)
|
||||
pose_imgs.append(pose_img)
|
||||
pose_scaled.append(openpose_json)
|
||||
pbar.update(1)
|
||||
|
||||
return pose_imgs
|
||||
return pose_imgs, pose_scaled
|
||||
|
||||
def draw_pose(pose, H, W, pose_marker_size, face_marker_size, hand_marker_size):
|
||||
bodies = pose['bodies']
|
||||
|
||||
Reference in New Issue
Block a user