Files
smthemex-ComfyUI_Sonic/sonic.py
T
2025-02-07 17:32:06 +08:00

230 lines
7.5 KiB
Python

import os
import torch
import torch.utils.checkpoint
from tqdm import tqdm
import gc
from .src.utils.util import seed_everything
from .src.dataset.test_preprocess import process_bbox
from .src.models.base.unet_spatio_temporal_condition import add_ip_adapters
from .src.pipelines.pipeline_sonic import SonicPipeline
from .src.utils.RIFE.RIFE_HDv3 import RIFEModel
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
def sonic_predata(wav_enc,audio_feature,audio_len,step,audio2bucket,image_encoder,audio_pe,ref_img,clip_img,device):
image_embeds=image_encoder.encode_image(clip_img)["image_embeds"] #torch.Size([1, 1024])
image_embeds=image_embeds.clone().detach().to(device, dtype=torch.float16) #dtype需要改成可选
audio_prompts = []
last_audio_prompts = []
window = 3000
#print(audio_feature.shape) #torch.Size([80, 3000])
for i in range(0, audio_feature.shape[-1], window):
audio_prompt = wav_enc.encoder(audio_feature[:,:,i:i+window], output_hidden_states=True).hidden_states
last_audio_prompt = wav_enc.encoder(audio_feature[:,:,i:i+window]).last_hidden_state
last_audio_prompt = last_audio_prompt.unsqueeze(-2)
audio_prompt = torch.stack(audio_prompt, dim=2)
audio_prompts.append(audio_prompt)
last_audio_prompts.append(last_audio_prompt)
audio_prompts = torch.cat(audio_prompts, dim=1)
audio_prompts = audio_prompts[:,:audio_len*2]
audio_prompts = torch.cat([torch.zeros_like(audio_prompts[:,:4]), audio_prompts, torch.zeros_like(audio_prompts[:,:6])], 1)
last_audio_prompts = torch.cat(last_audio_prompts, dim=1)
last_audio_prompts = last_audio_prompts[:,:audio_len*2]
last_audio_prompts = torch.cat([torch.zeros_like(last_audio_prompts[:,:24]), last_audio_prompts, torch.zeros_like(last_audio_prompts[:,:26])], 1)
ref_tensor_list = []
audio_tensor_list = []
uncond_audio_tensor_list = []
motion_buckets = []
for i in tqdm(range(audio_len//step)):
audio_clip = audio_prompts[:,i*2*step:i*2*step+10].unsqueeze(0)
audio_clip_for_bucket = last_audio_prompts[:,i*2*step:i*2*step+50].unsqueeze(0)
motion_bucket = audio2bucket(audio_clip_for_bucket, image_embeds)
motion_bucket = motion_bucket * 16 + 16
motion_buckets.append(motion_bucket[0])
cond_audio_clip = audio_pe(audio_clip).squeeze(0)
uncond_audio_clip = audio_pe(torch.zeros_like(audio_clip)).squeeze(0)
ref_tensor_list.append(ref_img[0])
audio_tensor_list.append(cond_audio_clip[0])
uncond_audio_tensor_list.append(uncond_audio_clip[0])
return ref_tensor_list,audio_tensor_list,uncond_audio_tensor_list,motion_buckets,image_embeds
def preprocess_face(face_image,face_det, expand_ratio=1.0):
h, w = face_image.shape[:2]
_, _, bboxes = face_det(face_image, maxface=True)
face_num = len(bboxes)
bbox = []
if face_num > 0:
x1, y1, ww, hh = bboxes[0]
x2, y2 = x1 + ww, y1 + hh
bbox = x1, y1, x2, y2
bbox_s = process_bbox(bbox, expand_radio=expand_ratio, height=h, width=w)
return {
'face_num': face_num,
'crop_bbox': bbox_s,
}
def crop_face_image(face_image,crop_bbox):
crop_image = face_image[crop_bbox[1]:crop_bbox[3], crop_bbox[0]:crop_bbox[2]]
return crop_image
def test(
pipe,
config,
audio_tensor_list,
uncond_audio_tensor_list,
motion_buckets,
width,
height,
batch,
image_embeds,
fps,
):
ref_img = batch['ref_img']
face_mask= batch['face_mask']
video = pipe(
ref_img,
image_embeds,
face_mask,
audio_tensor_list,
uncond_audio_tensor_list,
motion_buckets,
height=height,
width=width,
num_frames=len(audio_tensor_list),
decode_chunk_size=config.decode_chunk_size,
motion_bucket_scale=config.motion_bucket_scale,
fps=fps,
noise_aug_strength=config.noise_aug_strength,
min_guidance_scale1=config.min_appearance_guidance_scale, # 1.0,
max_guidance_scale1=config.max_appearance_guidance_scale,
min_guidance_scale2=config.audio_guidance_scale, # 1.0,
max_guidance_scale2=config.audio_guidance_scale,
overlap=config.overlap,
shift_offset=config.shift_offset,
frames_per_batch=config.n_sample_frames,
num_inference_steps=config.num_inference_steps,
i2i_noise_strength=config.i2i_noise_strength
).frames
# Concat it with pose tensor
# pose_tensor = torch.stack(pose_tensor_list,1).unsqueeze(0)
video = (video*0.5 + 0.5).clamp(0, 1)
video = torch.cat([video.to(pipe.device)], dim=0).cpu()
#print("video shape:", video.shape)# torch.Size([1, 3, 250, 512, 512])
return video
class Sonic():
def __init__(self,
device,
weight_dtype,
vae,
val_noise_scheduler,
unet,
flownet_ckpt,
sonic_unet,
use_interframe,
ip_audio_scale,
):
self.use_interframe = use_interframe
#config.use_interframe = enable_interpolate_frame
add_ip_adapters(unet, [32], [ip_audio_scale])
sonic_dict = torch.load(sonic_unet, map_location="cpu")
unet.load_state_dict(sonic_dict,strict=True,)
del sonic_dict
gc.collect()
torch.cuda.empty_cache()
if self.use_interframe:
rife = RIFEModel(device=device)
rife.load_model(flownet_ckpt)
self.rife = rife
vae.to(weight_dtype)
unet.to(weight_dtype)
pipe = SonicPipeline(
unet=unet,
vae=vae,
scheduler=val_noise_scheduler,
)
self.pipe = pipe.to(dtype=weight_dtype)
self.device = device
print('init done')
@torch.no_grad()
def process(self,
audio_tensor_list,
uncond_audio_tensor_list,
motion_buckets,
test_data,
config,
image_embeds,
fps,
inference_steps=25,
dynamic_scale=1.0,
seed=None):
# specific parameters
if seed:
config.seed = seed
config.num_inference_steps = inference_steps
config.motion_bucket_scale = dynamic_scale
seed_everything(config.seed)
height, width = test_data['ref_img'].shape[-2:]
#self.pipe.enable_model_cpu_offload #太慢,没意义
self.pipe.to(self.device)
video = test(
self.pipe,
config,
audio_tensor_list,
uncond_audio_tensor_list,
motion_buckets,
width=width,
height=height,
batch=test_data,
image_embeds=image_embeds,
fps=fps,
)
if self.use_interframe:
rife = self.rife
out = video.to(self.device)
results = []
video_len = out.shape[2]
for idx in tqdm(range(video_len-1), ncols=0):
I1 = out[:, :, idx]
I2 = out[:, :, idx+1]
middle = rife.inference(I1, I2).clamp(0, 1).detach()
results.append(out[:, :, idx])
results.append(middle)
results.append(out[:, :, video_len-1])
video = torch.stack(results, 2).cpu()
return video