Files
kijai-ComfyUI-SCAIL-Pose/NLFPoseExtract/nlf_render.py
T

409 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import numpy as np
import torch
import os, platform, copy
if platform.system() == 'Linux':
if 'PYOPENGL_PLATFORM' not in os.environ:
os.environ['PYOPENGL_PLATFORM'] = 'egl'
elif platform.system() == 'Windows':
os.environ.pop('PYOPENGL_PLATFORM', None)
from ..render_3d.taichi_cylinder import render_whole
from ..pose_draw.draw_pose_utils import draw_pose_to_canvas_np
def p3d_single_p2d(points, intrinsic_matrix):
X, Y, Z = points[0], points[1], points[2]
u = (intrinsic_matrix[0, 0] * X / Z) + intrinsic_matrix[0, 2]
v = (intrinsic_matrix[1, 1] * Y / Z) + intrinsic_matrix[1, 2]
u_np = u.cpu().numpy()
v_np = v.cpu().numpy()
return np.array([u_np, v_np])
def process_data_to_COCO_format(joints):
"""Args:
joints: numpy array of shape (24, 2) or (24, 3)
Returns:
new_joints: numpy array of shape (17, 2) or (17, 3)
"""
if joints.ndim != 2:
raise ValueError(f"Expected shape (24,2) or (24,3), got {joints.shape}")
dim = joints.shape[1] # 2D or 3D
mapping = {
15: 0, # head
12: 1, # neck
17: 2, # left shoulder
16: 5, # right shoulder
19: 3, # left elbow
18: 6, # right elbow
21: 4, # left hand
20: 7, # right hand
2: 8, # left pelvis
1: 11, # right pelvis
5: 9, # left knee
4: 12, # right knee
8: 10, # left feet
7: 13, # right feet
}
new_joints = np.zeros((18, dim), dtype=joints.dtype)
for src, dst in mapping.items():
new_joints[dst] = joints[src]
return new_joints
def intrinsic_matrix_from_field_of_view(imshape, fov_degrees:float =55): # nlf default fov_degrees 55
imshape = np.array(imshape)
fov_radians = fov_degrees * np.array(np.pi / 180)
larger_side = np.max(imshape)
focal_length = larger_side / (np.tan(fov_radians / 2) * 2)
# intrinsic_matrix 3*3
return np.array([
[focal_length, 0, imshape[1] / 2],
[0, focal_length, imshape[0] / 2],
[0, 0, 1],
])
def shift_dwpose_according_to_nlf(smpl_poses, aligned_poses, ori_intrinstics, modified_intrinstics, height, width):
########## warning: 会改变body; shift 之后 body是不准的 ##########
for i in range(len(smpl_poses)):
persons_joints_list = smpl_poses[i]
poses_list = aligned_poses[i]
# 对里面每一个人,取关节并进行变形;并且修改2d;如果3d不存在,把2d的手/脸也去掉
for person_idx, person_joints in enumerate(persons_joints_list):
face = poses_list["faces"][person_idx]
right_hand = poses_list["hands"][2 * person_idx]
left_hand = poses_list["hands"][2 * person_idx + 1]
candidate = poses_list["bodies"]["candidate"][person_idx]
# 注意,这里不是coco format
person_joint_15_2d_shift = p3d_single_p2d(person_joints[15], modified_intrinstics) - p3d_single_p2d(person_joints[15], ori_intrinstics) if person_joints[15, 2] > 0.01 else np.array([0.0, 0.0]) # face
person_joint_20_2d_shift = p3d_single_p2d(person_joints[20], modified_intrinstics) - p3d_single_p2d(person_joints[20], ori_intrinstics) if person_joints[20, 2] > 0.01 else np.array([0.0, 0.0]) # right hand
person_joint_21_2d_shift = p3d_single_p2d(person_joints[21], modified_intrinstics) - p3d_single_p2d(person_joints[21], ori_intrinstics) if person_joints[21, 2] > 0.01 else np.array([0.0, 0.0]) # left hand
face[:, 0] += person_joint_15_2d_shift[0] / width
face[:, 1] += person_joint_15_2d_shift[1] / height
right_hand[:, 0] += person_joint_20_2d_shift[0] / width
right_hand[:, 1] += person_joint_20_2d_shift[1] / height
left_hand[:, 0] += person_joint_21_2d_shift[0] / width
left_hand[:, 1] += person_joint_21_2d_shift[1] / height
candidate[:, 0] += person_joint_15_2d_shift[0] / width
candidate[:, 1] += person_joint_15_2d_shift[1] / height
def get_single_pose_cylinder_specs(args):
"""Helper function for rendering a single pose, used for parallel processing."""
idx, pose, focal, princpt, height, width, colors, limb_seq, draw_seq = args
cylinder_specs = []
for joints3d in pose: # 多人
joints3d = joints3d.cpu().numpy()
joints3d = process_data_to_COCO_format(joints3d)
for line_idx in draw_seq:
line = limb_seq[line_idx]
start, end = line[0], line[1]
if np.sum(joints3d[start]) == 0 or np.sum(joints3d[end]) == 0:
continue
else:
cylinder_specs.append((joints3d[start], joints3d[end], colors[line_idx]))
return cylinder_specs
def collect_smpl_poses(data):
uncollected_smpl_poses = [item['nlfpose'] for item in data]
smpl_poses = [[] for _ in range(len(uncollected_smpl_poses))]
for frame_idx in range(len(uncollected_smpl_poses)):
for person_idx in range(len(uncollected_smpl_poses[frame_idx])): # 每个人(每个bbox)只给出一个pose
if len(uncollected_smpl_poses[frame_idx][person_idx]) > 0: # 有返回的骨骼
smpl_poses[frame_idx].append(uncollected_smpl_poses[frame_idx][person_idx][0])
else:
smpl_poses[frame_idx].append(torch.zeros((24, 3), dtype=torch.float32)) # 没有检测到人,就放一个全0的
return smpl_poses
def collect_smpl_poses_samurai(data):
uncollected_smpl_poses = [item['nlfpose'] for item in data]
smpl_poses_first = [[] for _ in range(len(uncollected_smpl_poses))]
smpl_poses_second = [[] for _ in range(len(uncollected_smpl_poses))]
for frame_idx in range(len(uncollected_smpl_poses)):
for person_idx in range(len(uncollected_smpl_poses[frame_idx])): # 每个人(每个bbox)只给出一个pose
if len(uncollected_smpl_poses[frame_idx][person_idx]) > 0: # 有返回的骨骼
if person_idx == 0:
smpl_poses_first[frame_idx].append(uncollected_smpl_poses[frame_idx][person_idx][0])
elif person_idx == 1:
smpl_poses_second[frame_idx].append(uncollected_smpl_poses[frame_idx][person_idx][0])
else:
if person_idx == 0:
smpl_poses_first[frame_idx].append(torch.zeros((24, 3), dtype=torch.float32)) # 没有检测到人,就放一个全0的
elif person_idx == 1:
smpl_poses_second[frame_idx].append(torch.zeros((24, 3), dtype=torch.float32))
return smpl_poses_first, smpl_poses_second
def render_nlf_as_images(smpl_poses, dw_poses, height, width, video_length, intrinsic_matrix=None, draw_2d=True, draw_face=True, draw_hands=True):
""" return a list of images """
base_colors_255_dict = {
# Warm Colors for Right Side (R.) - Red, Orange, Yellow
"Red": [255, 0, 0],
"Orange": [255, 85, 0],
"Golden Orange": [255, 170, 0],
"Yellow": [255, 240, 0],
"Yellow-Green": [180, 255, 0],
# Cool Colors for Left Side (L.) - Green, Blue, Purple
"Bright Green": [0, 255, 0],
"Light Green-Blue": [0, 255, 85],
"Aqua": [0, 255, 170],
"Cyan": [0, 255, 255],
"Sky Blue": [0, 170, 255],
"Medium Blue": [0, 85, 255],
"Pure Blue": [0, 0, 255],
"Purple-Blue": [85, 0, 255],
"Medium Purple": [170, 0, 255],
# Neutral/Central Colors (e.g., for Neck, Nose, Eyes, Ears)
"Grey": [150, 150, 150],
"Pink-Magenta": [255, 0, 170],
"Dark Pink": [255, 0, 85],
"Violet": [100, 0, 255],
"Dark Violet": [50, 0, 255],
}
ordered_colors_255 = [
base_colors_255_dict["Red"], # Neck -> R. Shoulder (Red)
base_colors_255_dict["Cyan"], # Neck -> L. Shoulder (Cyan)
base_colors_255_dict["Orange"], # R. Shoulder -> R. Elbow (Orange)
base_colors_255_dict["Golden Orange"], # R. Elbow -> R. Wrist (Golden Orange)
base_colors_255_dict["Sky Blue"], # L. Shoulder -> L. Elbow (Sky Blue)
base_colors_255_dict["Medium Blue"], # L. Elbow -> L. Wrist (Medium Blue)
base_colors_255_dict["Yellow-Green"], # Neck -> R. Hip ( Yellow-Green)
base_colors_255_dict["Bright Green"], # R. Hip -> R. Knee (Bright Green - transitioning warm to cool spectrum)
base_colors_255_dict["Light Green-Blue"], # R. Knee -> R. Ankle (Light Green-Blue - transitioning)
base_colors_255_dict["Pure Blue"], # Neck -> L. Hip (Pure Blue)
base_colors_255_dict["Purple-Blue"], # L. Hip -> L. Knee (Purple-Blue)
base_colors_255_dict["Medium Purple"], # L. Knee -> L. Ankle (Medium Purple)
base_colors_255_dict["Grey"], # Neck -> Nose (Grey)
base_colors_255_dict["Pink-Magenta"], # Nose -> R. Eye (Pink/Magenta)
base_colors_255_dict["Dark Violet"], # R. Eye -> R. Ear (Dark Pink)
base_colors_255_dict["Pink-Magenta"], # Nose -> L. Eye (Violet)
base_colors_255_dict["Dark Violet"], # L. Eye -> L. Ear (Dark Violet)
]
limb_seq = [
[1, 2], # 0 Neck -> R. Shoulder
[1, 5], # 1 Neck -> L. Shoulder
[2, 3], # 2 R. Shoulder -> R. Elbow
[3, 4], # 3 R. Elbow -> R. Wrist
[5, 6], # 4 L. Shoulder -> L. Elbow
[6, 7], # 5 L. Elbow -> L. Wrist
[1, 8], # 6 Neck -> R. Hip
[8, 9], # 7 R. Hip -> R. Knee
[9, 10], # 8 R. Knee -> R. Ankle
[1, 11], # 9 Neck -> L. Hip
[11, 12], # 10 L. Hip -> L. Knee
[12, 13], # 11 L. Knee -> L. Ankle
[1, 0], # 12 Neck -> Nose
[0, 14], # 13 Nose -> R. Eye
[14, 16], # 14 R. Eye -> R. Ear
[0, 15], # 15 Nose -> L. Eye
[15, 17], # 16 L. Eye -> L. Ear
]
draw_seq = [0, 2, 3, # Neck -> R. Shoulder -> R. Elbow -> R. Wrist
1, 4, 5, # Neck -> L. Shoulder -> L. Elbow -> L. Wrist
6, 7, 8, # Neck -> R. Hip -> R. Knee -> R. Ankle
9, 10, 11, # Neck -> L. Hip -> L. Knee -> L. Ankle
12, # Neck -> Nose
13, 14, # Nose -> R. Eye -> R. Ear
15, 16, # Nose -> L. Eye -> L. Ear
] # Expanding outward from the proximal end
colors = [[c / 300 + 0.15 for c in color_rgb] + [0.8] for color_rgb in ordered_colors_255]
if dw_poses is not None:
aligned_poses = copy.deepcopy(dw_poses)
if intrinsic_matrix is None:
intrinsic_matrix = intrinsic_matrix_from_field_of_view((height, width))
focal_x = intrinsic_matrix[0,0]
focal_y = intrinsic_matrix[1,1]
princpt = (intrinsic_matrix[0,2], intrinsic_matrix[1,2]) # (cx, cy)
# obtain cylinder_specs for each frame
cylinder_specs_list = []
for i in range(video_length):
cylinder_specs = get_single_pose_cylinder_specs((i, smpl_poses[i], None, None, None, None, colors, limb_seq, draw_seq))
cylinder_specs_list.append(cylinder_specs)
frames_np_rgba = render_whole(cylinder_specs_list, H=height, W=width, fx=focal_x, fy=focal_y, cx=princpt[0], cy=princpt[1])
if dw_poses is not None and draw_2d:
canvas_2d = draw_pose_to_canvas_np(aligned_poses, pool=None, H=height, W=width, reshape_scale=0, show_feet_flag=False, show_body_flag=False, show_cheek_flag=True, dw_hand=True, show_face_flag=draw_face, show_hand_flag=draw_hands)
for i in range(len(frames_np_rgba)):
frame_img = frames_np_rgba[i]
canvas_img = canvas_2d[i]
mask = canvas_img != 0
frame_img[:, :, :3][mask] = canvas_img[mask]
frames_np_rgba[i] = frame_img
return frames_np_rgba
def render_multi_nlf_as_images(smpl_poses, dw_poses, height, width, video_length, intrinsic_matrix=None, draw_2d=True, draw_face=True, draw_hands=True):
second_person_base_colors_255_dict = {
# Warm Colors for Right Side (R.) - Red, Orange, Yellow
"Red": [255, 20, 20],
"Orange": [255, 60, 0],
"Golden Orange": [255, 110, 0],
"Yellow": [255, 200, 0],
"Yellow-Green": [160, 255, 40],
# Cool Colors for Left Side (L.) - Green, Blue, Purple
"Bright Green": [0, 255, 50],
"Light Green-Blue": [0, 255, 100],
"Aqua": [0, 255, 200],
"Cyan": [0, 230, 255],
"Sky Blue": [0, 130, 255],
"Medium Blue": [0, 70, 255],
"Pure Blue": [0, 0, 255],
"Purple-Blue": [80, 0, 255],
"Medium Purple": [160, 0, 255],
# Neutral/Central Colors (e.g., for Neck, Nose, Eyes, Ears)
"Grey": [130, 130, 130],
"Pink-Magenta": [255, 0, 150],
"Dark Pink": [255, 0, 100],
"Violet": [120, 0, 255],
"Dark Violet": [60, 0, 255],
}
first_person_base_colors_255_dict = {
# Warm Colors for Right Side (R.) - Red, Orange, Yellow
"Red": [255, 150, 150],
"Orange": [255, 180, 140],
"Golden Orange": [255, 215, 150],
"Yellow": [255, 240, 170],
"Yellow-Green": [200, 255, 100],
# Cool Colors for Left Side (L.) - Green, Blue, Purple
"Bright Green": [100, 255, 100],
"Light Green-Blue": [140, 255, 180],
"Aqua": [150, 240, 200],
"Cyan": [180, 230, 240],
"Sky Blue": [160, 200, 255],
"Medium Blue": [100, 120, 255],
"Pure Blue": [120, 140, 255],
"Purple-Blue": [180, 90, 255],
"Medium Purple": [190, 120, 255],
# Neutral/Central Colors (e.g., for Neck, Nose, Eyes, Ears)
"Grey": [210, 210, 210],
"Pink-Magenta": [255, 120, 200],
"Dark Pink": [255, 150, 180],
"Violet": [200, 90, 255],
"Dark Violet": [130, 80, 255],
}
base_colors_255_dict_list = [first_person_base_colors_255_dict, second_person_base_colors_255_dict]
ordered_colors_255_list = [[
base_colors_255_dict["Red"], # Neck -> R. Shoulder (Red)
base_colors_255_dict["Cyan"], # Neck -> L. Shoulder (Cyan)
base_colors_255_dict["Orange"], # R. Shoulder -> R. Elbow (Orange)
base_colors_255_dict["Golden Orange"], # R. Elbow -> R. Wrist (Golden Orange)
base_colors_255_dict["Sky Blue"], # L. Shoulder -> L. Elbow (Sky Blue)
base_colors_255_dict["Medium Blue"], # L. Elbow -> L. Wrist (Medium Blue)
base_colors_255_dict["Yellow-Green"], # Neck -> R. Hip ( Yellow-Green)
base_colors_255_dict["Bright Green"], # R. Hip -> R. Knee (Bright Green - transitioning warm to cool spectrum)
base_colors_255_dict["Light Green-Blue"], # R. Knee -> R. Ankle (Light Green-Blue - transitioning)
base_colors_255_dict["Pure Blue"], # Neck -> L. Hip (Pure Blue)
base_colors_255_dict["Purple-Blue"], # L. Hip -> L. Knee (Purple-Blue)
base_colors_255_dict["Medium Purple"], # L. Knee -> L. Ankle (Medium Purple)
base_colors_255_dict["Grey"], # Neck -> Nose (Grey)
base_colors_255_dict["Pink-Magenta"], # Nose -> R. Eye (Pink/Magenta)
base_colors_255_dict["Dark Violet"], # R. Eye -> R. Ear (Dark Pink)
base_colors_255_dict["Pink-Magenta"], # Nose -> L. Eye (Violet)
base_colors_255_dict["Dark Violet"], # L. Eye -> L. Ear (Dark Violet)
] for base_colors_255_dict in base_colors_255_dict_list]
limb_seq = [
[1, 2], # 0 Neck -> R. Shoulder
[1, 5], # 1 Neck -> L. Shoulder
[2, 3], # 2 R. Shoulder -> R. Elbow
[3, 4], # 3 R. Elbow -> R. Wrist
[5, 6], # 4 L. Shoulder -> L. Elbow
[6, 7], # 5 L. Elbow -> L. Wrist
[1, 8], # 6 Neck -> R. Hip
[8, 9], # 7 R. Hip -> R. Knee
[9, 10], # 8 R. Knee -> R. Ankle
[1, 11], # 9 Neck -> L. Hip
[11, 12], # 10 L. Hip -> L. Knee
[12, 13], # 11 L. Knee -> L. Ankle
[1, 0], # 12 Neck -> Nose
[0, 14], # 13 Nose -> R. Eye
[14, 16], # 14 R. Eye -> R. Ear
[0, 15], # 15 Nose -> L. Eye
[15, 17], # 16 L. Eye -> L. Ear
]
draw_seq = [0, 2, 3, # Neck -> R. Shoulder -> R. Elbow -> R. Wrist
1, 4, 5, # Neck -> L. Shoulder -> L. Elbow -> L. Wrist
6, 7, 8, # Neck -> R. Hip -> R. Knee -> R. Ankle
9, 10, 11, # Neck -> L. Hip -> L. Knee -> L. Ankle
12, # Neck -> Nose
13, 14, # Nose -> R. Eye -> R. Ear
15, 16, # Nose -> L. Eye -> L. Ear
] # Expanding outward from the proximal end
colors_first = [[c / 300 + 0.15 for c in color_rgb] + [0.8] for color_rgb in ordered_colors_255_list[0]]
colors_second = [[c / 300 + 0.15 for c in color_rgb] + [0.8] for color_rgb in ordered_colors_255_list[1]]
smpl_poses_first = []
smpl_poses_second = []
for i in range(video_length):
if len(smpl_poses[i]) >= 1:
smpl_poses_first.append([smpl_poses[i][0]]) # First person
else:
smpl_poses_first.append([torch.zeros((24, 3), dtype=torch.float32)])
if len(smpl_poses[i]) >= 2:
smpl_poses_second.append([smpl_poses[i][1]]) # Second person
else:
smpl_poses_second.append([torch.zeros((24, 3), dtype=torch.float32)])
if intrinsic_matrix is None:
intrinsic_matrix = intrinsic_matrix_from_field_of_view((height, width))
focal_x = intrinsic_matrix[0,0]
focal_y = intrinsic_matrix[1,1]
princpt = (intrinsic_matrix[0,2], intrinsic_matrix[1,2]) # (cx, cy)
# obtain cylinder_specs for each frame
cylinder_specs_list = []
for i in range(video_length):
cylinder_specs_first = get_single_pose_cylinder_specs((i, smpl_poses_first[i], None, None, None, None, colors_first, limb_seq, draw_seq))
cylinder_specs_second = get_single_pose_cylinder_specs((i, smpl_poses_second[i], None, None, None, None, colors_second, limb_seq, draw_seq))
cylinder_specs = cylinder_specs_first + cylinder_specs_second
cylinder_specs_list.append(cylinder_specs)
frames_np_rgba = render_whole(cylinder_specs_list, H=height, W=width, fx=focal_x, fy=focal_y, cx=princpt[0], cy=princpt[1])
if dw_poses is not None and draw_2d:
aligned_poses = copy.deepcopy(dw_poses)
canvas_2d = draw_pose_to_canvas_np(aligned_poses, pool=None, H=height, W=width, reshape_scale=0, show_feet_flag=False, show_body_flag=False, show_cheek_flag=True, dw_hand=True)
for i in range(len(frames_np_rgba)):
frame_img = frames_np_rgba[i]
canvas_img = canvas_2d[i]
mask = canvas_img != 0
frame_img[:, :, :3][mask] = canvas_img[mask]
frames_np_rgba[i] = frame_img
return frames_np_rgba