409 lines
19 KiB
Python
409 lines
19 KiB
Python
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
|