import torch import comfy.utils import cv2 import numpy as np import folder_paths import os import math connect_color = [ [ 0, 0, 255], [255, 0, 0], [255, 170, 0], [255, 255, 0], [255, 85, 0], [170, 255, 0], [ 85, 255, 0], [ 0, 255, 0], [ 0, 255, 85], [ 0, 255, 170], [ 0, 255, 255], [ 0, 170, 255], [ 0, 85, 255], [ 85, 0, 255], [170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85] ] for i, (R, G, B) in enumerate(connect_color): connect_color[i] = [B,G,R] # 骨架连接的关节对 skeleton = [ [0, 1], [1, 2], [2, 3], [3, 4], [1, 5], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10], [1, 11], [11, 12], [12, 13], [14, 0], [14, 16], [15, 0], [15, 17] ] bonesPoints = [ [1,0],# neck [0,14 ],#right eye [0,15 ],#left eye [14,16 ],# right ear [15,17 ],# left ear [1,2 ],#right shoulder [1,5 ],#left shoulder [2,3 ],#right elbow [5,6 ],#left elbow [3,4 ],#right wrist [6,7 ],#left wrist [1,8 ],#right hip [1,11 ],#left hip [8,9 ],#right knee [11,12 ],# left knee [9,10 ],#right ankle [12,13 ] # left ankle ] class HumanPart: def __init__(self, index): self.index = index self.abs_x = 0.0 #绝对坐标 self.abs_y = 0.0 # 绝对坐标 self.length = 0.0 #长度 self.score=1.0 #置信度 self.abs_angle=0.0 #绝对角度 self.ref_angle=0.0 #相对角度 self.children = [] #子物体 self.ref_scale=1.0 self.abs_scale=1.0 self.parent = None #父物体 def SetParent(self, parent): self.parent = parent parent.children.append(self) #根据相对角度和长度计算绝对坐标 def Update(self): # 如果有父节点,则更新绝对位置和角度 if self.parent is not None: if self.score > 0.0: # 更新绝对缩放 self.abs_scale = self.parent.abs_scale * self.ref_scale # 更新绝对角度 self.abs_angle = self.parent.abs_angle + self.ref_angle # 更新绝对x坐标 self.abs_x = self.parent.abs_x + self.length * math.cos(self.abs_angle)* self.abs_scale # 更新绝对y坐标 self.abs_y = self.parent.abs_y + self.length * math.sin(self.abs_angle)* self.abs_scale else: # 如果没有父节点,则绝对角度和缩放等于参考角度和缩放 self.abs_angle = self.ref_angle self.abs_scale = self.ref_scale #self.score=1.0 # 遍历子节点,递归调用Update方法 for child in self.children: child.Update() #根据绝对位置,计算角度、相对位置和相对角度 def CalculateRef(self): # 如果有父节点,则更新相对位置和角度 if self.parent is not None: if self.score > 0.0: self.length = math.sqrt((self.abs_x - self.parent.abs_x)**2 + (self.abs_y - self.parent.abs_y)**2) self.abs_angle =math.atan2(self.abs_y - self.parent.abs_y, self.abs_x - self.parent.abs_x) self.ref_angle = self.abs_angle - self.parent.abs_angle self.ref_scale = self.abs_scale / self.parent.abs_scale #如果父节点score为0,而本节点score为1,那么这个节点就只有abs_x,abs_y是可靠的,其他都是不可靠的 #除非使用IK算法,否则无法计算可靠的角度和长度。超出了这个插件编写的本意。留着以后优化吧。 #openpose的线条,在两个端点都可靠的情况下才会绘制。因此本插件即使不计算这个骨骼,也不影响绘制。 #本插件还有猜测不可靠节点的位置的功能,不放在这个函数里处理,是因为这个函数是递归调用的,放在这里的话,猜测的位置会不断被覆盖。 #猜测功能用在pose interpolation里,因为设计本意里,pose interpolation的输入来自两张不同的图片,里面的可靠节点数量不一致,所以需要猜测不可靠节点的位置。 else: self.length = 0.0 self.ref_angle = 0.0 self.abs_angle= 0.0 # 遍历子节点,递归调用Update方法 for child in self.children: child.CalculateRef() class HumanDoll: def __init__(self, canvas_width, canvas_height): self.canvas_width = canvas_width self.canvas_height = canvas_height self.parts = [None] * (len(bonesPoints) + 1) self.keyPoints=[] self.RootPart = HumanPart(1) # 根部 固定长度为0,对应keypoints[1]的位置 self.abs_x = canvas_width / 2 self.abs_y = canvas_height / 3 self.RootPart.abs_x = canvas_width / 2 self.RootPart.abs_y = canvas_height / 3 self.RootPart.ref_angle = 0.0 self.RootPart.score = 1.0 self.parts[1] = self.RootPart for (p,c) in bonesPoints: # p为父节点,c为子节点 child = HumanPart(c) child.SetParent(self.parts[p]) self.parts[c] = child def UpdateByKeypoints(self, keypoints): self.keyPoints = keypoints for i, keypoint in enumerate(keypoints): if keypoint is not None: self.parts[i].abs_x = keypoint[0] self.parts[i].abs_y = keypoint[1] self.parts[i].score = keypoint[2] else: self.parts[i].score = 0.0 self.RootPart.CalculateRef() def UpdateByParts(self): self.RootPart.Update() for i in range(len(self.keyPoints)): self.keyPoints[i] = (self.parts[i].abs_x, self.parts[i].abs_y, self.parts[i].score) def Draw(self, width, height,draw_all=False): # 在这里实现绘制函数 return keyPoints_2_image(self.keyPoints, width, height,draw_all) def guessMissingParts(self): # 在这里实现猜测缺失部分的功能。用于pose interpolation doll_T_Pose = HumanDoll(self.canvas_width,self.canvas_height) keyPoints = gen_t_pose_keypoints(self.canvas_width,self.canvas_height) doll_T_Pose.UpdateByKeypoints(keyPoints) # 定义各部位缺失时的参考顺序数组 # 格式为:(目标部位索引, [参考部位列表按优先级排序]) reference_order = [ (0, [2,5,8,11]), # 补充头部(部位0)时参考的顺序 (2, [5,8,11,0]), # 补充右肩(部位2)时参考的顺序 (5, [2,8,11,0]), # 补充左肩(部位5)时参考的顺序 (8, [11,2,5,0]), # 补充右髋(部位8)时参考的顺序 (11, [8,2,5,0]), # 补充左髋(部位11)时参考的顺序 (3, [6,2,8,11,0]), # 补充右上臂(部位3)时参考的顺序 (6, [3,2,11,8,0]), # 补充左上臂(部位6)时参考的顺序 (4, [3,7,2,5,8,11,0]), # 补充右前臂(部位4)时参考的顺序 (7, [4,6,5,3,2,8,11,0]), # 补充左前臂(部位7)时参考的顺序 (9, [12,8,11,2,5,0]), # 补充右大腿(部位9)时参考的顺序 (12, [9,11,8,5,2,0]), # 补充左大腿(部位12)时参考的顺序 (10, [13,9,12,8,11,2,5,0]), # 补充右小腿(部位10)时参考的顺序 (13, [10,12,9,11,8,2,5,0]), # 补充左小腿(部位13)时参考的顺序 #以上都是以父节点长度参考缺失节点长度,而角度则从t-pose中复制 #但是脸上的部位,需要另外的算法。实际运行中出现过耳朵可靠而眼睛不可靠的情况,因此需要另外的算法 # (14, [15,0]), # 补充右眼睛(部位14)时参考的顺序 # (15, [14,0]), # 补充左眼睛(部位15)时参考的顺序 # (16, [17,14,15,0]), # 补充右耳朵(部位16)时参考的顺序 # (17, [16,15,14,0]), # 补充左耳朵(部位17)时参考的顺序 # 其他部位的参考规则可以继续添加 # ... ] # 遍历所有定义好的参考规则 for target_part, reference_parts in reference_order: # 跳过1号部位(根据需求描述1号部位不会缺失) if target_part == 1: continue # 如果目标部位缺失(分数为0) if self.parts[target_part].score == 0.0: # 标记为已尝试修复 self.parts[target_part].score = 1.0 # 复制T姿态下的参考角度 self.parts[target_part].ref_angle = doll_T_Pose.parts[target_part].ref_angle # 遍历参考部位列表,按优先级尝试补充数据 for ref_part in reference_parts: if self.parts[ref_part].score > 0.0: # 使用参考部位数据补充目标部位长度 # 比例因子:目标部位在T姿态下的长度 / 参考部位在T姿态下的长度 ratio = doll_T_Pose.parts[target_part].length / doll_T_Pose.parts[ref_part].length #if target_part == 16 or target_part == 17: self.parts[target_part].length = self.parts[ref_part].length * ratio #print(f"[{target_part}]-[{ref_part}]-ratio:{ratio}- self.parts[ref_part].length:{self.parts[ref_part].length} - result_length:{self.parts[target_part].length}") break # 找到第一个有效参考后退出循环 else: # 如果所有参考部位都无效,重置分数 self.parts[target_part].score = 0.0 #现在处理右眼。如果右眼缺失,则根据左耳朵和鼻子的位置计算 if self.parts[14].score == 0.0: self.parts[14].score=1.0 if self.parts[16].score > 0.0 and self.parts[0].score > 0.0: self.parts[14].abs_x = (self.parts[16].abs_x + self.parts[0].abs_x)/2 self.parts[14].abs_y = (self.parts[16].abs_y + self.parts[0].abs_y)/2 self.parts[14].CalculateRef() elif self.parts[0].score > 0.0: ratio = doll_T_Pose.parts[14].length / doll_T_Pose.parts[0].length self.parts[14].length = self.parts[0].length * ratio else: self.parts[14].score = 0.0 #现在处理左眼。如果左眼缺失,则根据右耳朵和鼻子的位置计算 if self.parts[15].score == 0.0: self.parts[15].score=1.0 if self.parts[17].score > 0.0 and self.parts[0].score > 0.0: self.parts[15].abs_x = (self.parts[17].abs_x + self.parts[0].abs_x)/2 self.parts[15].abs_y = (self.parts[17].abs_y + self.parts[0].abs_y)/2 self.parts[15].CalculateRef() elif self.parts[0].score > 0.0: ratio = doll_T_Pose.parts[15].length / doll_T_Pose.parts[0].length self.parts[15].length = self.parts[0].length * ratio else: self.parts[15].score = 0.0 reference_order_part2 = [ (16, [17,14,15,0]), # 补充右耳朵(部位16)时参考的顺序 (17, [16,15,14,0]), # 补充左耳朵(部位17)时参考的顺序 ] for target_part, reference_parts in reference_order_part2: # 跳过1号部位(根据需求描述1号部位不会缺失) if target_part == 1: continue # 如果目标部位缺失(分数为0) if self.parts[target_part].score == 0.0: # 标记为已尝试修复 self.parts[target_part].score = 1.0 # 复制T姿态下的参考角度 self.parts[target_part].ref_angle = doll_T_Pose.parts[target_part].ref_angle # 遍历参考部位列表,按优先级尝试补充数据 for ref_part in reference_parts: if self.parts[ref_part].score > 0.0: # 使用参考部位数据补充目标部位长度 # 比例因子:目标部位在T姿态下的长度 / 参考部位在T姿态下的长度 ratio = doll_T_Pose.parts[target_part].length / doll_T_Pose.parts[ref_part].length #if target_part == 16 or target_part == 17: self.parts[target_part].length = self.parts[ref_part].length * ratio #print(f"[{target_part}]-[{ref_part}]-ratio:{ratio}- self.parts[ref_part].length:{self.parts[ref_part].length} - result_length:{self.parts[target_part].length}") break # 找到第一个有效参考后退出循环 else: # 如果所有参考部位都无效,重置分数 self.parts[target_part].score = 0.0 #现在处理右眼。如果右眼缺失,则根据左耳朵和鼻子的位置计算 print("修复完成") def gen_t_pose_keypoints(canvas_width, canvas_height): # 初始化关键点数组 keyPoints = [None] * (len(bonesPoints) + 1) # 定义起始点(1号关键点,颈部)的位置 start_x = canvas_width / 2 start_y = canvas_height / 4 keyPoints[1] = (start_x, start_y, 1.0) # 定义骨骼长度 neck_length = canvas_height / 10 shoulder_length = canvas_width / 8 arm_length = canvas_width / 6 hip_width = shoulder_length hip_height = canvas_height / 4 leg_length = canvas_height / 5 head_radius = canvas_height / 15 # 头部半径 eye_distance = canvas_width / 10 # 眼睛间距 ear_distance = canvas_width / 10 # 耳朵间距 # 计算0号关键点(头部中心)的位置 head_center_x = start_x head_center_y = start_y - neck_length keyPoints[0] = (head_center_x, head_center_y, 1.0) # 计算右肩(2号关键点)的位置 right_shoulder_x = start_x - shoulder_length right_shoulder_y = start_y keyPoints[2] = (right_shoulder_x, right_shoulder_y, 1.0) # 计算左肩(5号关键点)的位置 left_shoulder_x = start_x + shoulder_length left_shoulder_y = start_y keyPoints[5] = (left_shoulder_x, left_shoulder_y, 1.0) # 计算右肘(3号关键点)的位置 right_elbow_x = right_shoulder_x - arm_length right_elbow_y = right_shoulder_y keyPoints[3] = (right_elbow_x, right_elbow_y, 1.0) # 计算左肘(6号关键点)的位置 left_elbow_x = left_shoulder_x + arm_length left_elbow_y = left_shoulder_y keyPoints[6] = (left_elbow_x, left_elbow_y, 1.0) # 计算右手腕(4号关键点)的位置 right_wrist_x = right_elbow_x - arm_length right_wrist_y = right_elbow_y keyPoints[4] = (right_wrist_x, right_wrist_y, 1.0) # 计算左手腕(7号关键点)的位置 left_wrist_x = left_elbow_x+ arm_length left_wrist_y = left_elbow_y keyPoints[7] = (left_wrist_x, left_wrist_y, 1.0) # 计算右髋(8号关键点)的位置 right_hip_x = start_x - hip_width right_hip_y = start_y + hip_height keyPoints[8] = (right_hip_x, right_hip_y, 1.0) # 计算左髋(11号关键点)的位置 left_hip_x = start_x + hip_width left_hip_y = start_y + hip_height keyPoints[11] = (left_hip_x, left_hip_y, 1.0) # 计算右膝(9号关键点)的位置 right_knee_x = right_hip_x right_knee_y = right_hip_y + leg_length keyPoints[9] = (right_knee_x, right_knee_y, 1.0) # 计算左膝(12号关键点)的位置 left_knee_x = left_hip_x left_knee_y = left_hip_y + leg_length keyPoints[12] = (left_knee_x, left_knee_y, 1.0) # 计算右脚踝(10号关键点)的位置 right_ankle_x = right_knee_x right_ankle_y = right_knee_y + leg_length keyPoints[10] = (right_ankle_x, right_ankle_y, 1.0) # 计算左脚踝(13号关键点)的位置 left_ankle_x = left_knee_x left_ankle_y = left_knee_y + leg_length keyPoints[13] = (left_ankle_x, left_ankle_y, 1.0) # 计算右眼(14号关键点)的位置 right_eye_x = head_center_x - eye_distance / 2 right_eye_y = head_center_y- eye_distance / 2 keyPoints[14] = (right_eye_x, right_eye_y, 1.0) # 计算左眼(15号关键点)的位置 left_eye_x = head_center_x + eye_distance / 2 left_eye_y = head_center_y- eye_distance / 2 keyPoints[15] = (left_eye_x, left_eye_y, 1.0) # 计算右耳(16号关键点)的位置 right_ear_x = head_center_x - ear_distance right_ear_y = head_center_y keyPoints[16] = (right_ear_x, right_ear_y, 1.0) # 计算左耳(17号关键点)的位置 left_ear_x = head_center_x + ear_distance left_ear_y = head_center_y keyPoints[17] = (left_ear_x, left_ear_y, 1.0) return keyPoints def poseKeypoints2D_2_keypoints(pose_keypoints_2d, canvas_width, canvas_height,landmarkType): keyPoints = [pose_keypoints_2d[i:i + 3] for i in range(0, len(pose_keypoints_2d), 3)] if landmarkType == "DWPose": canvas_height=1 canvas_width=1 for i in range(len(keyPoints)): x, y, z = keyPoints[i] keyPoints[i] = (x * canvas_width, y * canvas_height, z) return keyPoints def keypoints_2_bones(keyPoints): bones=[] for i, (a, b) in enumerate(bonesPoints): a_x, a_y, a_z = keyPoints[a] b_x, b_y, b_z = keyPoints[b] # 现在 (a_x,a_y) 是骨骼起点的坐标,(b_x,b_y) 是骨骼终点的坐标 # 然后计算骨骼的长度和角度,连同坐标一起生成一个骨骼对象,放入bones数组 # 如果a_z=0或者b_z=0,则忽略这个骨骼,放入一个空对象 if a_z == 0 or b_z == 0: bone = { 'start': (a_x, a_y), 'end': (b_x, b_y), 'length': -1, 'angle': 0 } bones.append(bone) continue # 计算骨骼的长度 bone_length = math.sqrt((b_x - a_x) ** 2 + (b_y - a_y) ** 2) # 计算骨骼的角度 bone_angle = math.atan2(b_y - a_y, b_x - a_x) # 生成骨骼对象 bone = { 'start': (a_x, a_y), 'end': (b_x, b_y), 'length': bone_length, 'angle': bone_angle } bones.append(bone) return bones def bones_2_keypoints(bones): keyPoints = [None] * 18 # 从1号关键点开始 start_bone = bones[0] start_x, start_y = start_bone['start'] neck_length = start_bone['length'] neck_angle = start_bone['angle'] keyPoints[1] = (start_x ,start_y,1.0) # 计算新的0号关键点的位置 new_x = start_x + neck_length * math.cos(neck_angle) new_y = start_y + neck_length * math.sin(neck_angle) keyPoints[0] = (new_x ,new_y,1.0) # 依次计算后续关键点的位置 for i, (a, b) in enumerate(bonesPoints): if i == 0: continue # 跳过第一个骨骼,因为它已经处理过了 # 获取前一个关键点的位置 prev_x,prev_y,score = keyPoints[a] # 获取当前骨骼的信息 bone = bones[i] bone_length = bone['length'] bone_angle = bone['angle'] if bone_length == -1: keyPoints[b]=(0,0,0) continue # 计算新的关键点位置 new_x = prev_x + bone_length * math.cos(bone_angle) new_y = prev_y + bone_length * math.sin(bone_angle) # 将新的关键点位置存入数组 keyPoints[b]=(new_x ,new_y,1.0) return keyPoints def keyPoints_2_image(keyPoints, canvas_width, canvas_height ,draw_all=False): image = np.zeros((canvas_height, canvas_width, 3), dtype=np.uint8) # 绘制骨架 for i, (a, b) in enumerate(skeleton): a_x, a_y, a_z = keyPoints[a] b_x, b_y, b_z = keyPoints[b] if draw_all: if a_z != 0 and b_z != 0: cv2.line(image, (int(a_x), int(a_y)), (int(b_x), int(b_y)), connect_color[i] + [0], 4) else: cv2.line(image, (int(a_x), int(a_y)), (int(b_x), int(b_y)), (255,255,255), 4) else: # 检查是否在画面内(假设a_z和b_z为0表示不在画面内) a_in_canvas = a_z != 0 and 0 <= a_x < canvas_width and 0 <= a_y < canvas_height b_in_canvas = b_z != 0 and 0 <= b_x < canvas_width and 0 <= b_y < canvas_height if a_in_canvas and b_in_canvas: cv2.line(image, (int(a_x), int(a_y)), (int(b_x), int(b_y)), connect_color[i] + [0], 4) # 绘制关键点 for i, (x, y, z) in enumerate(keyPoints): if z!=0: try: cv2.circle(image, (int(x), int(y)), int(canvas_width/100), connect_color[i], -1) except: pass # cv2.putText(image, str(i), (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) return image def draw_skeleton(pose_keypoints_2d, canvas_width, canvas_height,landmarkType): # 加载背景图片或创建一个空白画布 image = None #cv2.imread('background.jpg') # 使用实际的背景图片路径 if image is None: image = np.zeros((canvas_height, canvas_width, 3), dtype=np.uint8) if landmarkType == "DWPose": canvas_height=1 canvas_width=1 tri_tuples = [pose_keypoints_2d[i:i + 3] for i in range(0, len(pose_keypoints_2d), 3)] # 绘制骨架 for i, (a, b) in enumerate(skeleton): a_x, a_y, a_z = tri_tuples[a] a_x, a_y = ( a_x * canvas_width, a_y * canvas_height ) b_x, b_y, b_z = tri_tuples[b] b_x, b_y = ( b_x * canvas_width, b_y * canvas_height ) if a_z != 0 and b_z != 0: cv2.line(image, (int(a_x), int(a_y)), (int(b_x), int(b_y)), connect_color[i] + [0], 4) # 绘制关键点 for i, (x, y, z) in enumerate(tri_tuples): if z!=0: cv2.circle(image, (int(x * canvas_width), int(y * canvas_height)), 6, connect_color[i], -1) #cv2.putText(image, str(i), (int(x * canvas_width), int(y * canvas_height)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) return image def transform_keypoints(keypoints_1, keypoints_2, frames): tri_tuples_1 = [keypoints_1[i:i + 3] for i in range(0, len(keypoints_1), 3)] tri_tuples_2 = [keypoints_2[i:i + 3] for i in range(0, len(keypoints_2), 3)] keypoints_array = [keypoints_1] for j in range(1, frames): kp = [] for i in range(len(tri_tuples_1)): x1, y1, z1 = tri_tuples_1[i] x2, y2, z2 = tri_tuples_2[i] if z1 == 0 and z2 == 0: new_x, new_y, new_z = (0.0, 0.0, 0.0) elif z1 == 0: new_x, new_y, new_z = (x2, y2, z2) elif z2 == 0: new_x, new_y, new_z = (x1, y1, z1) else: new_x, new_y, new_z = ( x1 + (x2-x1) * j/frames, y1 + (y2-y1) * j/frames , 1.0) kp.append( new_x) kp.append( new_y) kp.append( new_z) keypoints_array.append(kp) #keypoints_array.append(keypoints_2) return keypoints_array class Pose_Inter: def __init__(self): pass @classmethod def INPUT_TYPES(s): return { "required": { "pose_from": ("POSE_KEYPOINT", ), "pose_to": ("POSE_KEYPOINT", ), "interpolate_frames": ("INT", {"default": 10, "min": 2, "max": 100, "step": 1}), "landmarkType": (["OpenPose","DWPose"], ), }, } RETURN_TYPES = ("IMAGE",) FUNCTION = "run" CATEGORY = "Pose Interpolation" def run(self,pose_from,pose_to,interpolate_frames,landmarkType): openpose_dict_2 = pose_from[0] openpose_dict = pose_to[0] keypoints_array = transform_keypoints( openpose_dict_2["people"][0]["pose_keypoints_2d"], openpose_dict["people"][0]["pose_keypoints_2d"], interpolate_frames ) output=[] #print("image shape") #print(image.shape) for i, keypoints in enumerate(keypoints_array): # 显示图像 image = draw_skeleton( keypoints, openpose_dict_2["canvas_width"], openpose_dict_2["canvas_height"], landmarkType ) image = torch.from_numpy(image.astype(np.float32) / 255.0)#.unsqueeze(0) output.append(image) tensor_stacked = torch.stack(output) #print("shape of tensor_stacked") #print(tensor_stacked.shape) return (tensor_stacked,) class PoseModify: def __init__(self): pass @classmethod def INPUT_TYPES(s): return { "required": { "pose_keypoint": ("POSE_KEYPOINT", ), "landmarkType": (["OpenPose","DWPose"], ), "globalScale": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0}), "head": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "neck": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "neck": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "torsoWidth": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "torsoHeight": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "upperArmLength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "lowerArmLength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "upperLegLength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), "lowerLegLength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0,"step":0.1}), }, } RETURN_TYPES = ("IMAGE",) FUNCTION = "run" CATEGORY = "Pose Interpolation" def run(self,pose_keypoint,landmarkType,globalScale,head,neck,torsoWidth,torsoHeight,upperArmLength,lowerArmLength,upperLegLength,lowerLegLength): # if head==1.0 & neck==1.0 & torsoWidth==1.0 & torsoHeight==1.0 & upperArmLength==1.0 & lowerArmLength==1.0 & upperLegLength==1.0 & lowerLegLength==1.0: # lengthFactors = [1.0] * 18 # else: lengthFactors = [neck,1.0, torsoWidth, upperArmLength, lowerArmLength, torsoWidth, upperArmLength, lowerArmLength, torsoHeight, upperLegLength, lowerLegLength, torsoHeight, upperLegLength, lowerLegLength, head, head, head, head] openpose_dict_2 = pose_keypoint[0] width = openpose_dict_2["canvas_width"] height = openpose_dict_2["canvas_height"] output=[] openpose_dict_2 = pose_keypoint[0] width = openpose_dict_2["canvas_width"] height = openpose_dict_2["canvas_height"] keyPoints = gen_t_pose_keypoints(width,height) doll = HumanDoll(width,height) doll.UpdateByKeypoints(keyPoints) for openpose_dict_2 in pose_keypoint: human1Keypoints = poseKeypoints2D_2_keypoints( openpose_dict_2["people"][0]["pose_keypoints_2d"], width, height, landmarkType ) doll.UpdateByKeypoints(human1Keypoints) doll.RootPart.ref_scale = globalScale for i in range(len(doll.parts)): if doll.parts[i].score > 0.0: doll.parts[i].length *= lengthFactors[i] doll.UpdateByParts() image=doll.Draw(width,height) image = torch.from_numpy(image.astype(np.float32) / 255.0)#.unsqueeze(0) output.append(image) tensor_stacked = torch.stack(output) #print("shape of tensor_stacked") #print(tensor_stacked.shape) return (tensor_stacked,) class GenTPose: def __init__(self): pass @classmethod def INPUT_TYPES(s): return { "required": { "width": ("INT", {"default": 480, "min": 2, "max": 1024, "step": 1}), "height": ("INT", {"default": 640, "min": 2, "max": 1024, "step": 1}), }, } RETURN_TYPES = ("IMAGE",) FUNCTION = "run" CATEGORY = "Pose Interpolation" def run(self,width,height): keyPoints = gen_t_pose_keypoints(width,height) doll = HumanDoll(width, height) doll.UpdateByKeypoints(keyPoints) doll.UpdateByParts() image=doll.Draw(width, height) output=[] image = torch.from_numpy(image.astype(np.float32) / 255.0)#.unsqueeze(0) output.append(image) tensor_stacked = torch.stack(output) #print("shape of tensor_stacked") #print(tensor_stacked.shape) return (tensor_stacked,) class Pose_Inter_V2: def __init__(self): pass @classmethod def INPUT_TYPES(s): return { "required": { "pose_from": ("POSE_KEYPOINT", ), "pose_to": ("POSE_KEYPOINT", ), "interpolate_frames": ("INT", {"default": 10, "min": 2, "max": 100, "step": 1}), "landmarkType": (["OpenPose","DWPose"], ), "draw_all": ("BOOLEAN", {"default": False}), }, } RETURN_TYPES = ("IMAGE",) FUNCTION = "run" CATEGORY = "Pose Interpolation" def run(self,pose_from,pose_to,interpolate_frames,landmarkType,draw_all): output=[] openpose_dict = pose_from[0] openpose_dict_2 = pose_to[0] width = openpose_dict_2["canvas_width"] height = openpose_dict_2["canvas_height"] dollFrom = HumanDoll(width,height) fromKeys = poseKeypoints2D_2_keypoints(openpose_dict["people"][0]["pose_keypoints_2d"],width,height,landmarkType) dollFrom.UpdateByKeypoints(fromKeys) dollTo = HumanDoll(width,height) toKeys = poseKeypoints2D_2_keypoints(openpose_dict_2["people"][0]["pose_keypoints_2d"],width,height,landmarkType) dollTo.UpdateByKeypoints(toKeys) dollFrom.guessMissingParts() dollTo.guessMissingParts() image=dollFrom.Draw(width,height,draw_all) image = torch.from_numpy(image.astype(np.float32) / 255.0) output.append(image) position_x_arr = np.full(interpolate_frames+2, -1.0, dtype=np.float32) position_y_arr = np.full(interpolate_frames+2,-1.0, dtype=np.float32) position_x_arr[0] = dollFrom.RootPart.abs_x position_y_arr[0] = dollFrom.RootPart.abs_y position_x_arr[interpolate_frames+1] = dollTo.RootPart.abs_x position_y_arr[interpolate_frames+1] = dollTo.RootPart.abs_y for i in range(1, interpolate_frames+1): position_x_arr[i] = position_x_arr[0] + (position_x_arr[interpolate_frames+1] - position_x_arr[0]) * (i - 1) / interpolate_frames position_y_arr[i] = position_y_arr[0] + (position_y_arr[interpolate_frames+1] - position_y_arr[0]) * (i - 1) / interpolate_frames #18个骨骼,每个骨骼2个数组,每个数组有interpolate_frames+2个元素表示骨骼每一帧的长度和角度 curveDatas = np.full((18, 2, interpolate_frames+2), -1.0, dtype=np.float32) for i in range(len(dollFrom.parts)): if dollFrom.parts[i].score > 0.0: curveDatas[i][0][0] = dollFrom.parts[i].length curveDatas[i][1][0] = dollFrom.parts[i].ref_angle dollFrom.parts[i].score=1.0 for i in range(len(dollTo.parts)): if dollTo.parts[i].score > 0.0: curveDatas[i][0][interpolate_frames+1] = dollTo.parts[i].length curveDatas[i][1][interpolate_frames+1] = dollTo.parts[i].ref_angle dollTo.parts[i].score=1.0 for i in range(18): j=0 #骨骼长度,线性插值 for k in range(1, interpolate_frames+1): curveDatas[i][j][k] = curveDatas[i][j][0] + (curveDatas[i][j][interpolate_frames+1] - curveDatas[i][j][0]) * (k) / (interpolate_frames+1) for i in range(18): j=1 a = curveDatas[i][j][0] b = curveDatas[i][j][interpolate_frames+1] # 将 b 调整到与 a 变化量最小的等效弧度值 b_adjusted = b - 2 * np.pi * np.round((b - a) / (2 * np.pi)) curveDatas[i][j][interpolate_frames+1] = b_adjusted for k in range(1, interpolate_frames+1): curveDatas[i][j][k] = a + (b_adjusted - a) * (k) / (interpolate_frames+1) curveDatas[i][j] = np.mod(curveDatas[i][j], 2 * np.pi) for i in range(1,interpolate_frames+1): dollFrom.RootPart.abs_x = position_x_arr[i] dollFrom.RootPart.abs_y = position_y_arr[i] for k in range(18): dollFrom.parts[k].length = curveDatas[k][0][i] dollFrom.parts[k].ref_angle = curveDatas[k][1][i] dollFrom.UpdateByParts() image=dollFrom.Draw(width,height,draw_all) image = torch.from_numpy(image.astype(np.float32) / 255.0)#.unsqueeze(0) output.append(image) image=dollTo.Draw(width,height,draw_all) image = torch.from_numpy(image.astype(np.float32) / 255.0)#.unsqueeze(0) output.append(image) tensor_stacked = torch.stack(output) return (tensor_stacked,) # A dictionary that contains all nodes you want to export with their names # NOTE: names should be globally unique NODE_CLASS_MAPPINGS = { "Pose_Inter": Pose_Inter, "Pose_Inter_V2": Pose_Inter_V2, "PoseModify": PoseModify, "GenTPose":GenTPose } # A dictionary that contains the friendly/humanly readable titles for the nodes NODE_DISPLAY_NAME_MAPPINGS = { "Pose_Inter": "Pose Interpolation", "Pose_Inter_V2": "Pose Interpolation_V2", "PoseModify": "Pose Modify by factor", "GenTPose": "GenTPose" }