758 lines
27 KiB
Python
758 lines
27 KiB
Python
# !/usr/bin/env python
|
|
# -*- coding: UTF-8 -*-
|
|
import datetime
|
|
import logging
|
|
import os
|
|
import torch
|
|
from PIL import Image
|
|
import numpy as np
|
|
import cv2
|
|
import pickle
|
|
import gc
|
|
import shutil
|
|
from huggingface_hub import hf_hub_download
|
|
|
|
try:
|
|
from moviepy.editor import VideoFileClip, AudioFileClip
|
|
except:
|
|
try:
|
|
from moviepy import VideoFileClip, AudioFileClip
|
|
except:
|
|
from moviepy import *
|
|
import random
|
|
from .src.utils.mp_utils import LMKExtractor
|
|
from .src.utils.motion_utils import motion_sync
|
|
from .src.utils.util import save_videos_grid, crop_and_pad,crop_and_pad_rectangle,center_crop
|
|
from .echomimic_v2.src.utils.dwpose_util import draw_pose_select_v2
|
|
|
|
from comfy.utils import common_upscale,ProgressBar
|
|
import folder_paths
|
|
from multiprocessing.pool import ThreadPool
|
|
|
|
weight_dtype = torch.float16
|
|
cur_path = os.path.dirname(os.path.abspath(__file__))
|
|
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
tensorrt_lite= os.path.join(folder_paths.get_input_directory(),"tensorrt_lite")
|
|
MAX_SIZE = 768
|
|
|
|
def process_video_v2(infer_image_pil,ref_image_pil, uploaded_audio,face_locator_tensor, W_change, H_change, start_idx, seed,
|
|
context_frames, context_overlap, cfg, steps, sample_rate, fps, pipe,mask_len,origin_h,origin_w,
|
|
save_video, audio_file_prefix,L,whisper_chunks):
|
|
|
|
# audio_clip = AudioFileClip(audio_clip)
|
|
#
|
|
# audio_clip = audio_clip.set_duration(L / fps)
|
|
if seed is not None and seed > -1:
|
|
generator = torch.manual_seed(seed)
|
|
else:
|
|
generator = torch.manual_seed(random.randint(100, 1000000))
|
|
|
|
video = pipe(
|
|
infer_image_pil,
|
|
#uploaded_audio,
|
|
face_locator_tensor,
|
|
width=W_change,
|
|
height=H_change,
|
|
video_length=L,
|
|
num_inference_steps=steps,
|
|
guidance_scale=cfg,
|
|
generator=generator,
|
|
audio_sample_rate=sample_rate,
|
|
context_frames=context_frames,
|
|
fps=fps,
|
|
context_overlap=context_overlap,
|
|
start_idx=start_idx,
|
|
whisper_chunks=whisper_chunks,
|
|
).videos
|
|
|
|
final_length = min(video.shape[2],mask_len, L)
|
|
video_sig = video[:, :, :final_length, :, :]
|
|
output_file = os.path.join(folder_paths.output_directory, f"{audio_file_prefix}_echo.mp4")
|
|
print(f"**** final_length is : {final_length} ****")
|
|
|
|
if origin_h!=origin_w:
|
|
ouput_list = save_videos_grid(video_sig, output_file, n_rows=1, fps=fps, save_video=save_video,size=(origin_w,origin_h),ref_image_pil=ref_image_pil)
|
|
else:
|
|
ouput_list = save_videos_grid(video_sig, output_file, n_rows=1, fps=fps, save_video=save_video)
|
|
|
|
if save_video:
|
|
output_video_path = os.path.join(folder_paths.output_directory, f"{audio_file_prefix}_audio.mp4")
|
|
video_clip = VideoFileClip(output_file)
|
|
audio_clip = AudioFileClip(uploaded_audio)
|
|
final_clip = video_clip.set_audio(audio_clip)
|
|
final_clip.write_videofile(
|
|
output_video_path,
|
|
codec="libx264", audio_codec="aac")
|
|
print(f"**** saving{output_file} at {output_video_path} ****")
|
|
video_clip.reader.close()
|
|
audio_clip.close()
|
|
final_clip.reader.close()
|
|
return ouput_list
|
|
|
|
|
|
def process_video(ref_image_pil, uploaded_audio, width, height, length, seed, face_locator_tensor,context_frames, context_overlap, cfg, steps,
|
|
sample_rate, fps, pipe,save_video,mask_len, audio_file_prefix,whisper_chunks):
|
|
if seed is not None and seed > -1:
|
|
generator = torch.manual_seed(seed)
|
|
else:
|
|
generator = torch.manual_seed(random.randint(100, 1000000))
|
|
|
|
video = pipe(
|
|
ref_image_pil,
|
|
#uploaded_audio,
|
|
c_face_locator_tensor=face_locator_tensor,
|
|
width=width,
|
|
height=height,
|
|
video_length=length,
|
|
num_inference_steps=steps,
|
|
guidance_scale=cfg,
|
|
generator=generator,
|
|
audio_sample_rate=sample_rate,
|
|
context_frames=context_frames,
|
|
fps=fps,
|
|
context_overlap=context_overlap,
|
|
whisper_chunks=whisper_chunks,
|
|
).videos
|
|
|
|
final_length = min(video.shape[2], mask_len, length)
|
|
output_file = os.path.join(folder_paths.output_directory, f"{audio_file_prefix}_echo.mp4")
|
|
print(f"**** final_length is : {final_length} ****")
|
|
|
|
ouput_list = save_videos_grid(video, output_file, n_rows=1, fps=fps, save_video=save_video)
|
|
|
|
if save_video:
|
|
output_video_path = os.path.join(folder_paths.output_directory, f"{audio_file_prefix}_audio.mp4")
|
|
video_clip = VideoFileClip(output_file)
|
|
audio_clip = AudioFileClip(uploaded_audio)
|
|
final_clip = video_clip.set_audio(audio_clip)
|
|
final_clip.write_videofile(
|
|
output_video_path,
|
|
codec="libx264", audio_codec="aac")
|
|
print(f"**** saving{output_file} at {output_video_path} ****")
|
|
video_clip.reader.close()
|
|
audio_clip.close()
|
|
final_clip.reader.close()
|
|
|
|
return ouput_list
|
|
|
|
def motion_sync_main(vis, width, height, video_images, face_img,facecrop_dilation_ratio, audio_file_prefix):
|
|
lmk_extractor = LMKExtractor()
|
|
ref_det = lmk_extractor(face_img)
|
|
|
|
#driver_video = os.path.join(folder_paths.input_directory, driver_video)
|
|
# if audio_form_video:
|
|
# audio_path = os.path.join(folder_paths.input_directory, f"{audio_file_prefix}_audio.wav")
|
|
# video_clip = VideoFileClip(driver_video)
|
|
# audio_clip = video_clip.audio
|
|
# audio_clip.write_audiofile(audio_path)
|
|
# video_clip.close()
|
|
# audio_clip.close()
|
|
|
|
video_len,_,_,_=video_images.size()
|
|
if video_len<25:
|
|
raise "input video has not much frames for driver,change your input video!"
|
|
else:
|
|
tensor_list = list(torch.chunk(video_images, chunks=video_len))
|
|
input_frames_cv2=[tensor2cv(tensor_upscale(i, width, height)) for i in tensor_list]
|
|
|
|
# print(ref_det)
|
|
sequence_driver_det = []
|
|
if input_frames_cv2:
|
|
try:
|
|
print("**** Starting process video ****")
|
|
for frame in input_frames_cv2:
|
|
result = lmk_extractor(frame)
|
|
assert result is not None, "bad video, face not detected"
|
|
sequence_driver_det.append(result)
|
|
except:
|
|
print("face detection failed")
|
|
else:
|
|
raise "input video error,change your input video!"
|
|
|
|
print("**** motion sync lenght " f"{len(sequence_driver_det)} ****")
|
|
|
|
if vis:
|
|
if facecrop_dilation_ratio==0:
|
|
facecrop_dilation_ratio=1
|
|
pose_frames_driver = [vis.draw_landmarks((width, height), i["lmks"],facecrop_dilation_ratio, normed=True) for i in sequence_driver_det]
|
|
poses_add_driver = [(i * 0.5 + j * 0.5).clip(0, 255).astype(np.uint8) for i, j in
|
|
zip(input_frames_cv2, pose_frames_driver)]
|
|
#print(f"**** poses_add_driver is done in len : {len(poses_add_driver)} ****")
|
|
|
|
save_dir = os.path.join(tensorrt_lite, audio_file_prefix)
|
|
|
|
if not os.path.exists(save_dir):
|
|
os.makedirs(save_dir)
|
|
sequence_det_ms = motion_sync(sequence_driver_det, ref_det)
|
|
for i in range(len(sequence_det_ms)):
|
|
with open('{}/{}.pkl'.format(save_dir, i), 'wb') as file:
|
|
pickle.dump(sequence_det_ms[i], file)
|
|
print(f"**** motion_sync {save_dir} is done ****")
|
|
else: #即便有文件夹,还是重新生成,避免出错
|
|
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
save_dir=os.path.join(tensorrt_lite,f"{audio_file_prefix}_{timestamp}")
|
|
os.makedirs(save_dir)
|
|
sequence_det_ms = motion_sync(sequence_driver_det, ref_det)
|
|
for i in range(len(sequence_det_ms)):
|
|
with open('{}/{}.pkl'.format(save_dir, i), 'wb') as file:
|
|
pickle.dump(sequence_det_ms[i], file)
|
|
print(f"**** motion_sync {save_dir} is done ****")
|
|
return save_dir,video_len
|
|
|
|
|
|
def select_face(det_bboxes, probs):
|
|
## max face from faces that the prob is above 0.8
|
|
## box: xyxy
|
|
if det_bboxes is None or probs is None:
|
|
return None
|
|
filtered_bboxes = []
|
|
for bbox_i in range(len(det_bboxes)):
|
|
if probs[bbox_i] > 0.8:
|
|
filtered_bboxes.append(det_bboxes[bbox_i])
|
|
if len(filtered_bboxes) == 0:
|
|
return None
|
|
sorted_bboxes = sorted(filtered_bboxes, key=lambda x: (x[3] - x[1]) * (x[2] - x[0]), reverse=True)
|
|
return sorted_bboxes[0]
|
|
|
|
|
|
def find_directories(base_path):
|
|
directories = []
|
|
for root, dirs, files in os.walk(base_path):
|
|
for name in dirs:
|
|
directories.append(name)
|
|
return directories
|
|
|
|
def download_weights(file_dir,repo_id,subfolder="",pt_name=""):
|
|
if subfolder:
|
|
file_path = os.path.join(file_dir,subfolder, pt_name)
|
|
sub_dir=os.path.join(file_dir,subfolder)
|
|
if not os.path.exists(sub_dir):
|
|
os.makedirs(sub_dir)
|
|
if not os.path.exists(file_path):
|
|
file_path = hf_hub_download(
|
|
repo_id=repo_id,
|
|
subfolder=subfolder,
|
|
filename=pt_name,
|
|
local_dir = file_dir,
|
|
)
|
|
return file_path
|
|
else:
|
|
file_path = os.path.join(file_dir, pt_name)
|
|
if not os.path.exists(file_dir):
|
|
os.makedirs(file_dir)
|
|
if not os.path.exists(file_path):
|
|
file_path = hf_hub_download(
|
|
repo_id=repo_id,
|
|
filename=pt_name,
|
|
local_dir=file_dir,
|
|
)
|
|
return file_path
|
|
|
|
|
|
def pil2narry(img):
|
|
img = torch.from_numpy(np.array(img).astype(np.float32) / 255.0).unsqueeze(0)
|
|
return img
|
|
|
|
|
|
def narry_list(list_in):
|
|
for i in range(len(list_in)):
|
|
value = list_in[i]
|
|
modified_value = pil2narry(value)
|
|
list_in[i] = modified_value
|
|
return list_in
|
|
|
|
|
|
def get_video_img(tensor):
|
|
if tensor == None:
|
|
return None
|
|
outputs = []
|
|
for x in tensor:
|
|
x = tensor_to_pil(x)
|
|
outputs.append(x)
|
|
yield outputs
|
|
|
|
|
|
def gen_img_form_video(tensor):
|
|
pil = []
|
|
for x in tensor:
|
|
pil[x] = tensor_to_pil(x)
|
|
yield pil
|
|
|
|
|
|
def phi_list(list_in):
|
|
for i in range(len(list_in)):
|
|
value = list_in[i]
|
|
list_in[i] = value
|
|
return list_in
|
|
|
|
def tensor_to_pil(tensor):
|
|
image_np = tensor.squeeze().mul(255).clamp(0, 255).byte().numpy()
|
|
image = Image.fromarray(image_np, mode='RGB')
|
|
return image
|
|
|
|
def nomarl_upscale(img_tensor, width, height):
|
|
samples = img_tensor.movedim(-1, 1)
|
|
img = common_upscale(samples, width, height, "nearest-exact", "center")
|
|
samples = img.movedim(1, -1)
|
|
img_pil = tensor_to_pil(samples)
|
|
return img_pil
|
|
|
|
def tensor_upscale(img_tensor, width, height):
|
|
samples = img_tensor.movedim(-1, 1)
|
|
img = common_upscale(samples, width, height, "nearest-exact", "center")
|
|
samples = img.movedim(1, -1)
|
|
return samples
|
|
|
|
def tensor2cv(tensor_image):
|
|
if len(tensor_image.shape)==4:# b hwc to hwc
|
|
tensor_image=tensor_image.squeeze(0)
|
|
if tensor_image.is_cuda:
|
|
tensor_image = tensor_image.cpu()
|
|
tensor_image=tensor_image.numpy()
|
|
#反归一化
|
|
maxValue=tensor_image.max()
|
|
tensor_image=tensor_image*255/maxValue
|
|
img_cv2=np.uint8(tensor_image)#32 to uint8
|
|
img_cv2=cv2.cvtColor(img_cv2,cv2.COLOR_RGB2BGR)
|
|
return img_cv2
|
|
|
|
def cvargb2tensor(img):
|
|
assert type(img) == np.ndarray, 'the img type is {}, but ndarry expected'.format(type(img))
|
|
img = torch.from_numpy(img.transpose((2, 0, 1)))
|
|
return img.float().div(255).unsqueeze(0) # 255也可以改为256
|
|
|
|
def cv2tensor(img):
|
|
assert type(img) == np.ndarray, 'the img type is {}, but ndarry expected'.format(type(img))
|
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
img = torch.from_numpy(img.transpose((2, 0, 1)))
|
|
return img.float().div(255).unsqueeze(0) # 255也可以改为256
|
|
|
|
def images_generator(img_list: list,):
|
|
#get img size
|
|
sizes = {}
|
|
for image_ in img_list:
|
|
if isinstance(image_,Image.Image):
|
|
count = sizes.get(image_.size, 0)
|
|
sizes[image_.size] = count + 1
|
|
elif isinstance(image_,np.ndarray):
|
|
count = sizes.get(image_.shape[:2][::-1], 0)
|
|
sizes[image_.shape[:2][::-1]] = count + 1
|
|
else:
|
|
raise "unsupport image list,must be pil or cv2!!!"
|
|
size = max(sizes.items(), key=lambda x: x[1])[0]
|
|
yield size[0], size[1]
|
|
|
|
# any to tensor
|
|
def load_image(img_in):
|
|
if isinstance(img_in, Image.Image):
|
|
img_in=img_in.convert("RGB")
|
|
i = np.array(img_in, dtype=np.float32)
|
|
i = torch.from_numpy(i).div_(255)
|
|
if i.shape[0] != size[1] or i.shape[1] != size[0]:
|
|
i = torch.from_numpy(i).movedim(-1, 0).unsqueeze(0)
|
|
i = common_upscale(i, size[0], size[1], "lanczos", "center")
|
|
i = i.squeeze(0).movedim(0, -1).numpy()
|
|
return i
|
|
elif isinstance(img_in,np.ndarray):
|
|
i=cv2.cvtColor(img_in,cv2.COLOR_BGR2RGB).astype(np.float32)
|
|
i = torch.from_numpy(i).div_(255)
|
|
#print(i.shape)
|
|
return i
|
|
else:
|
|
raise "unsupport image list,must be pil,cv2 or tensor!!!"
|
|
|
|
total_images = len(img_list)
|
|
processed_images = 0
|
|
pbar = ProgressBar(total_images)
|
|
images = map(load_image, img_list)
|
|
try:
|
|
prev_image = next(images)
|
|
while True:
|
|
next_image = next(images)
|
|
yield prev_image
|
|
processed_images += 1
|
|
pbar.update_absolute(processed_images, total_images)
|
|
prev_image = next_image
|
|
except StopIteration:
|
|
pass
|
|
if prev_image is not None:
|
|
yield prev_image
|
|
|
|
def load_images(img_list: list,):
|
|
gen = images_generator(img_list)
|
|
(width, height) = next(gen)
|
|
images = torch.from_numpy(np.fromiter(gen, np.dtype((np.float32, (height, width, 3)))))
|
|
if len(images) == 0:
|
|
raise FileNotFoundError(f"No images could be loaded .")
|
|
return images
|
|
|
|
def tensor2pil(tensor):
|
|
image_np = tensor.squeeze().mul(255).clamp(0, 255).byte().numpy()
|
|
image = Image.fromarray(image_np, mode='RGB')
|
|
return image
|
|
|
|
|
|
def cf_tensor2cv(tensor,width, height):
|
|
d1, _, _, _ = tensor.size()
|
|
if d1 > 1:
|
|
tensor_list = list(torch.chunk(tensor, chunks=d1))
|
|
tensor = [tensor_list][0]
|
|
cr_tensor=tensor_upscale(tensor,width, height)
|
|
cv_img=tensor2cv(cr_tensor)
|
|
return cv_img
|
|
|
|
def center_resize_pad(img, new_width, new_height):#为简化,new已是正方形
|
|
h, w = img.shape[:2]
|
|
if w == h:
|
|
if w == new_width:
|
|
return img
|
|
else:
|
|
return cv2.resize(img, (new_width, new_height))
|
|
else: #蒙版也有可能不是正方形
|
|
if h > w: # 竖直图左右填充
|
|
s = max(h, w)
|
|
f = np.zeros((s, s, 3), np.uint8)
|
|
ax, ay = (s - img.shape[1]) // 2, (s - img.shape[0]) // 2
|
|
f[ay:img.shape[0] + ay, ax:ax + img.shape[1]] = img
|
|
else:
|
|
f = center_crop(img, h, h)
|
|
return cv2.resize(f, (new_width, new_height))
|
|
|
|
|
|
def img_padding(height,width,ref_image_pil):
|
|
output_max = max(height, width)
|
|
img = tensor2cv(ref_image_pil)
|
|
h, w = img.shape[:2]
|
|
if h==w:
|
|
return cv2.resize(img, (output_max,output_max), interpolation=cv2.INTER_AREA)
|
|
else:
|
|
if h > w: #竖直图左右填充
|
|
s = max(h, w)
|
|
f = np.zeros((s, s, 3), np.uint8)
|
|
ax, ay = (s - w) // 2, (s - h) // 2
|
|
f[ay:h + ay, ax:ax + w] = img
|
|
else:
|
|
f=center_crop(img, h, h)
|
|
|
|
return cv2.resize(f, (output_max,output_max), interpolation=cv2.INTER_AREA)
|
|
|
|
def estimate_ratio(keypoint: list,box_xy,length=None):
|
|
x1, y1, x2, y2=box_xy
|
|
bbox_width, bbox_height = x2 - x1, y2 - y1
|
|
left_eye_y = []
|
|
left_shoulder_y = []
|
|
left_elbow_y = []
|
|
for i, (name, (x, y, conf)) in enumerate(keypoint[0].items()):
|
|
if name == "left_eye":
|
|
if conf > 0.3:
|
|
y_coord =int(y * bbox_height / 256) + y1 #获取坐标点在原图的绝对值
|
|
left_eye_y.append(y_coord)
|
|
if name == "left_shoulder":
|
|
if conf > 0.3:
|
|
y_coord = int(y * bbox_height / 256) + y1
|
|
left_shoulder_y.append(y_coord)
|
|
if name == "left_elbow":
|
|
if conf > 0.3:
|
|
y_coord = int(y * bbox_height / 256) + y1
|
|
left_elbow_y.append(y_coord)
|
|
|
|
if left_eye_y and left_elbow_y:
|
|
length=left_elbow_y[0] - left_eye_y[0]
|
|
elif left_eye_y and left_shoulder_y and not left_elbow_y :
|
|
length = left_shoulder_y[0] - left_eye_y[0]
|
|
else:
|
|
pass
|
|
if left_eye_y:
|
|
left_eye_y=[left_eye_y[0]]
|
|
return length,left_eye_y
|
|
|
|
|
|
def align_img(input_length, first_length, height, width, input_frames_cv2_first, input_left_eye_y, first_left_eye_y):
|
|
ratio = input_length / first_length #603 [201] 679 [220]
|
|
input_frames_cv2_first = cv2.resize(input_frames_cv2_first, (int(height * ratio), int(height * ratio)),
|
|
interpolation=cv2.INTER_AREA) # 缩小
|
|
base_image=np.zeros((height, width,3), np.uint8)
|
|
if input_length / height < first_length / height: # 输入图的人物占比要小,pose图需要缩小0.88对齐,#603 [201] 679 [220]
|
|
reduced_image,pad_size=center_paste(base_image, input_frames_cv2_first) #中心粘贴,pad为黑边尺寸
|
|
eye_y=int(first_left_eye_y*ratio+pad_size[1]) #图片缩放后的绝对值
|
|
|
|
else: # pose图里人物的比例小于输入图,pose要放大
|
|
reduced_image=center_crop(input_frames_cv2_first, height, width) #中心裁切
|
|
h, w = input_frames_cv2_first.shape[:2]
|
|
shift_y=(h-height)//2 #图片放大后裁切掉的边界
|
|
eye_y= int(first_left_eye_y*ratio-shift_y)
|
|
|
|
move_ = -int(eye_y - input_left_eye_y) if eye_y>= int(input_left_eye_y) else int(input_left_eye_y- eye_y) #对齐眼睛
|
|
translation_matrix = np.float32([[1, 0, 0], [0, 1, move_]])
|
|
shifted_image = cv2.warpAffine(reduced_image, translation_matrix, (width, height))
|
|
|
|
return shifted_image
|
|
|
|
def center_paste(img_b,img_f):
|
|
b_h,b_w=img_b.shape[:2]
|
|
f_h, f_w = img_f.shape[:2]
|
|
x = (b_w - f_w) // 2
|
|
y = (b_h - f_h) // 2
|
|
# 确保坐标不会是负数
|
|
x = max(0, x)
|
|
y = max(0, y)
|
|
img_b[y:y + f_h, x:x + f_w] = img_f
|
|
return img_b,(x,y)
|
|
|
|
|
|
def affine_img(input_left_eye_y, first_left_eye_y, img):
|
|
height,width=img.shape[:2]
|
|
move_ = int(input_left_eye_y[0] - first_left_eye_y[0])
|
|
translation_matrix = np.float32([[1, 0, 0], [0, 1, move_]]) # y轴位移
|
|
shifted_image = cv2.warpAffine(img, translation_matrix, (width, height))
|
|
return shifted_image
|
|
|
|
|
|
def convert_fps(src_path, tgt_path, tgt_fps=24, tgt_sr=16000):
|
|
clip = VideoFileClip(src_path)
|
|
new_clip = clip.set_fps(tgt_fps)
|
|
if tgt_fps is not None:
|
|
audio = new_clip.audio
|
|
audio = audio.set_fps(tgt_sr)
|
|
new_clip = new_clip.set_audio(audio)
|
|
if '.mov' in tgt_path:
|
|
tgt_path = tgt_path.replace('.mov', '.mp4')
|
|
new_clip.write_videofile(tgt_path, codec='libx264', audio_codec='aac')
|
|
|
|
|
|
def get_video_pose(visualizer,
|
|
frames,
|
|
sample_stride: int = 1,
|
|
max_frame=None):
|
|
# read input video
|
|
#vr = decord.VideoReader(video_path, ctx=decord.cpu(0))
|
|
#sample_stride *= max(1, int(vr.get_avg_fps() / 24))
|
|
|
|
#frames = vr.get_batch(list(range(0, len(vr), sample_stride))).asnumpy()
|
|
#frames=vr.get_batch(list(range(0, len(vr), sample_stride))).asnumpy()
|
|
# print(frames[0])
|
|
if max_frame is not None:
|
|
frames = frames[:max_frame]
|
|
height, width, _ = frames[0].shape
|
|
print("start get video pose list")
|
|
detected_poses = [visualizer(frm) for frm in frames]
|
|
visualizer.release_memory()
|
|
|
|
return detected_poses, height, width, frames
|
|
|
|
|
|
def resize_and_pad(img, max_size):
|
|
img_new = np.zeros((max_size, max_size, 3)).astype('uint8')
|
|
imh, imw = img.shape[0], img.shape[1]
|
|
half = max_size // 2
|
|
if imh > imw:
|
|
imh_new = max_size
|
|
imw_new = int(round(imw / imh * imh_new))
|
|
half_w = imw_new // 2
|
|
rb, re = 0, max_size
|
|
cb = half - half_w
|
|
ce = cb + imw_new
|
|
else:
|
|
imw_new = max_size
|
|
imh_new = int(round(imh / imw * imw_new))
|
|
half_h = imh_new // 2
|
|
cb, ce = 0, max_size
|
|
rb = half - half_h
|
|
re = rb + imh_new
|
|
|
|
img_resize = cv2.resize(img, (imw_new, imh_new))
|
|
img_new[rb:re, cb:ce, :] = img_resize
|
|
return img_new
|
|
|
|
|
|
def resize_and_pad_param(imh, imw, max_size):
|
|
half = max_size // 2
|
|
if imh > imw:
|
|
imh_new = max_size
|
|
imw_new = int(round(imw / imh * imh_new))
|
|
half_w = imw_new // 2
|
|
rb, re = 0, max_size
|
|
cb = half - half_w
|
|
ce = cb + imw_new
|
|
else:
|
|
imw_new = max_size
|
|
imh_new = int(round(imh / imw * imw_new))
|
|
imh_new = max_size
|
|
|
|
half_h = imh_new // 2
|
|
cb, ce = 0, max_size
|
|
rb = half - half_h
|
|
re = rb + imh_new
|
|
|
|
return imh_new, imw_new, rb, re, cb, ce
|
|
|
|
|
|
def get_pose_params(detected_poses, max_size):
|
|
|
|
print('get_pose_params...')
|
|
# pose rescale
|
|
height = 768
|
|
width=768
|
|
|
|
w_min_all, w_max_all, h_min_all, h_max_all = [], [], [], []
|
|
mid_all = []
|
|
for num, detected_pose in enumerate(detected_poses):
|
|
detected_poses[num]['num'] = num
|
|
candidate_body = detected_pose['bodies']['candidate']
|
|
score_body = detected_pose['bodies']['score']
|
|
candidate_face = detected_pose['faces']
|
|
score_face = detected_pose['faces_score']
|
|
candidate_hand = detected_pose['hands']
|
|
score_hand = detected_pose['hands_score']
|
|
|
|
# face
|
|
if candidate_face.shape[0] > 1:
|
|
index = 0
|
|
candidate_face = candidate_face[index]
|
|
score_face = score_face[index]
|
|
detected_poses[num]['faces'] = candidate_face.reshape(1, candidate_face.shape[0], candidate_face.shape[1])
|
|
detected_poses[num]['faces_score'] = score_face.reshape(1, score_face.shape[0])
|
|
else:
|
|
candidate_face = candidate_face[0]
|
|
score_face = score_face[0]
|
|
|
|
# body
|
|
if score_body.shape[0] > 1:
|
|
tmp_score = []
|
|
for k in range(0, score_body.shape[0]):
|
|
tmp_score.append(score_body[k].mean())
|
|
index = np.argmax(tmp_score)
|
|
candidate_body = candidate_body[index * 18:(index + 1) * 18, :]
|
|
score_body = score_body[index]
|
|
score_hand = score_hand[(index * 2):(index * 2 + 2), :]
|
|
candidate_hand = candidate_hand[(index * 2):(index * 2 + 2), :, :]
|
|
else:
|
|
score_body = score_body[0]
|
|
all_pose = np.concatenate((candidate_body, candidate_face))
|
|
all_score = np.concatenate((score_body, score_face))
|
|
all_pose = all_pose[all_score > 0.8]
|
|
|
|
body_pose = np.concatenate((candidate_body,))
|
|
mid_ = body_pose[1, 0]
|
|
|
|
face_pose = candidate_face
|
|
hand_pose = candidate_hand
|
|
|
|
h_min, h_max = np.min(face_pose[:, 1]), np.max(body_pose[:7, 1])
|
|
|
|
h_ = h_max - h_min
|
|
|
|
mid_w = mid_
|
|
w_min = mid_w - h_ // 2
|
|
w_max = mid_w + h_ // 2
|
|
|
|
w_min_all.append(w_min)
|
|
w_max_all.append(w_max)
|
|
h_min_all.append(h_min)
|
|
h_max_all.append(h_max)
|
|
mid_all.append(mid_w)
|
|
|
|
w_min = np.min(w_min_all)
|
|
w_max = np.max(w_max_all)
|
|
h_min = np.min(h_min_all)
|
|
h_max = np.max(h_max_all)
|
|
mid = np.mean(mid_all)
|
|
|
|
margin_ratio = 0.25
|
|
h_margin = (h_max - h_min) * margin_ratio
|
|
|
|
h_min = max(h_min - h_margin * 0.8, 0)
|
|
h_max = min(h_max + h_margin * 0.1, 1)
|
|
|
|
h_new = h_max - h_min
|
|
|
|
h_min_real = int(h_min * height)
|
|
h_max_real = int(h_max * height)
|
|
mid_real = int(mid * width)
|
|
|
|
height_new = h_max_real - h_min_real + 1
|
|
width_new = height_new
|
|
w_min_real = mid_real - width_new // 2
|
|
if w_min_real < 0:
|
|
w_min_real = 0
|
|
width_new = mid_real * 2
|
|
|
|
w_max_real = w_min_real + width_new
|
|
w_min = w_min_real / width
|
|
w_max = w_max_real / width
|
|
|
|
imh_new, imw_new, rb, re, cb, ce = resize_and_pad_param(height_new, width_new, max_size)
|
|
res = {'draw_pose_params': [imh_new, imw_new, rb, re, cb, ce],
|
|
'pose_params': [w_min, w_max, h_min, h_max],
|
|
'video_params': [h_min_real, h_max_real, w_min_real, w_max_real],
|
|
}
|
|
return res
|
|
|
|
|
|
def save_pose_params_item(input_items):
|
|
detected_pose, pose_params, draw_pose_params, save_dir = input_items
|
|
w_min, w_max, h_min, h_max = pose_params
|
|
num = detected_pose['num']
|
|
candidate_body = detected_pose['bodies']['candidate']
|
|
candidate_face = detected_pose['faces'][0]
|
|
candidate_hand = detected_pose['hands']
|
|
candidate_body[:, 0] = (candidate_body[:, 0] - w_min) / (w_max - w_min)
|
|
candidate_body[:, 1] = (candidate_body[:, 1] - h_min) / (h_max - h_min)
|
|
candidate_face[:, 0] = (candidate_face[:, 0] - w_min) / (w_max - w_min)
|
|
candidate_face[:, 1] = (candidate_face[:, 1] - h_min) / (h_max - h_min)
|
|
candidate_hand[:, :, 0] = (candidate_hand[:, :, 0] - w_min) / (w_max - w_min)
|
|
candidate_hand[:, :, 1] = (candidate_hand[:, :, 1] - h_min) / (h_max - h_min)
|
|
detected_pose['bodies']['candidate'] = candidate_body
|
|
detected_pose['faces'] = candidate_face.reshape(1, candidate_face.shape[0], candidate_face.shape[1])
|
|
detected_pose['hands'] = candidate_hand
|
|
detected_pose['draw_pose_params'] = draw_pose_params
|
|
np.save(save_dir + '/' + str(num) + '.npy', detected_pose)
|
|
|
|
|
|
def save_pose_params(detected_poses, pose_params, draw_pose_params, save_dir):
|
|
#save_dir = ori_video_path.replace('video', 'pose/')
|
|
if not os.path.exists(save_dir):
|
|
os.makedirs(save_dir)
|
|
|
|
input_list = []
|
|
|
|
for i, detected_pose in enumerate(detected_poses):
|
|
input_list.append([detected_pose, pose_params, draw_pose_params, save_dir])
|
|
|
|
pool = ThreadPool(8)
|
|
pool.map(save_pose_params_item, input_list)
|
|
pool.close()
|
|
pool.join()
|
|
return save_dir
|
|
|
|
|
|
def get_img_pose(visualizer,
|
|
img_path: str,
|
|
sample_stride: int = 1,
|
|
max_frame=None):
|
|
# read input img
|
|
frame = cv2.imread(img_path)
|
|
height, width, _ = frame.shape
|
|
short_size = min(height, width)
|
|
resize_ratio = max(MAX_SIZE / short_size, 1.0)
|
|
frame = cv2.resize(frame, (int(resize_ratio * width), int(resize_ratio * height)))
|
|
height, width, _ = frame.shape
|
|
detected_poses = [visualizer(frame)]
|
|
visualizer.release_memory()
|
|
|
|
return detected_poses, height, width, frame
|
|
|
|
|
|
def save_aligned_img(ori_frame, video_params, max_size):
|
|
h_min_real, h_max_real, w_min_real, w_max_real = video_params
|
|
img = ori_frame[h_min_real:h_max_real, w_min_real:w_max_real, :]
|
|
img_aligened = resize_and_pad(img, max_size=max_size)
|
|
print('aligned img shape:', img_aligened.shape)
|
|
save_dir = './assets/refimg_aligned'
|
|
|
|
os.makedirs(save_dir, exist_ok=True)
|
|
save_path = os.path.join(save_dir, 'aligned.png')
|
|
cv2.imwrite(save_path, img_aligened)
|
|
return save_path
|
|
|