|
import os |
|
import sys |
|
import gradio as gr |
|
import time |
|
|
|
os.makedirs("outputs", exist_ok=True) |
|
sys.path.insert(0, '.') |
|
|
|
import argparse |
|
import os.path as osp |
|
import mmcv |
|
import numpy as np |
|
import torch |
|
from mmcv.runner import load_checkpoint |
|
from mmcv.parallel import MMDataParallel |
|
from scipy.ndimage import gaussian_filter |
|
from IPython.display import Image |
|
|
|
from mogen.models.utils.imagebind_wrapper import ( |
|
extract_text_feature, |
|
extract_audio_feature, |
|
imagebind_huge |
|
) |
|
from mogen.models import build_architecture |
|
|
|
from mogen.utils.plot_utils import ( |
|
plot_3d_motion, |
|
add_audio, |
|
get_audio_length |
|
) |
|
from mogen.datasets.paramUtil import ( |
|
t2m_body_hand_kinematic_chain, |
|
t2m_kinematic_chain |
|
) |
|
from mogen.datasets.utils import recover_from_ric |
|
from mogen.datasets.pipelines import RetargetSkeleton |
|
|
|
import requests |
|
from huggingface_hub import hf_hub_download |
|
from huggingface_hub import login |
|
|
|
def load_large_files(relative_path): |
|
hf_token = os.getenv("HF_TOKEN") |
|
login(token=hf_token) |
|
|
|
url = "https://huggingface.co/mingyuan/data_hf/blob/main/" + relative_path |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
file_path = hf_hub_download(repo_id="mingyuan/data_hf", filename=relative_path, repo_type="dataset") |
|
return file_path |
|
|
|
def motion_temporal_filter(motion, sigma=1): |
|
motion = motion.reshape(motion.shape[0], -1) |
|
for i in range(motion.shape[1]): |
|
motion[:, i] = gaussian_filter(motion[:, i], sigma=sigma, mode="nearest") |
|
return motion.reshape(motion.shape[0], -1, 3) |
|
|
|
def plot_tomato(data, kinematic_chain, result_path, npy_path, fps, sigma=None): |
|
joints = recover_from_ric(torch.from_numpy(data).float(), 52).numpy() |
|
joints = motion_temporal_filter(joints, sigma=2.5) |
|
joints = rtg_skl({"keypoints3d": joints, "meta_data": {"has_lhnd": True}})["keypoints3d"] |
|
plot_3d_motion( |
|
out_path=result_path, |
|
joints=joints, |
|
kinematic_chain=kinematic_chain, |
|
title=None, |
|
fps=fps) |
|
if npy_path is not None: |
|
np.save(npy_path, joints) |
|
|
|
def create_lmm(): |
|
config_path = "configs/lmm/lmm_small_demo.py" |
|
ckpt_path = load_file_list["lmm"] |
|
cfg = mmcv.Config.fromfile(config_path) |
|
model = build_architecture(cfg.model) |
|
load_checkpoint(model, ckpt_path, map_location='cpu') |
|
if device == 'cpu': |
|
model = model.cpu() |
|
else: |
|
model = MMDataParallel(model, device_ids=[0]) |
|
model.eval() |
|
return model |
|
|
|
|
|
device = 'cuda' |
|
type = "hf" |
|
|
|
|
|
if type == "local": |
|
load_file_list = { |
|
"mean": "../data_hf/data/motionverse/statistics/mean.npy", |
|
"std": "../data_hf/data/motionverse/statistics/std.npy", |
|
"skeleton": "../data_hf/data/motionverse/statistics/skeleton.npy", |
|
"lmm": "../data_hf/data/motionverse/pretrained/lmm_small_demo.pth", |
|
"imagebind": "../data_hf/data/motionverse/pretrained/imagebind_huge.pth" |
|
} |
|
os.environ["NO_PROXY"] = os.environ["no_proxy"] = "localhost, 127.0.0.1:7860" |
|
else: |
|
src_file_list = { |
|
"mean": "data/motionverse/statistics/mean.npy", |
|
"std": "data/motionverse/statistics/std.npy", |
|
"skeleton": "data/motionverse/statistics/skeleton.npy", |
|
"lmm": "data/motionverse/pretrained/lmm_small_demo.pth", |
|
"imagebind": "data/motionverse/pretrained/imagebind_huge.pth" |
|
} |
|
load_file_list = {} |
|
for key in src_file_list.keys(): |
|
load_file_list[key] = load_large_files(src_file_list[key]) |
|
|
|
load_file_list["audio_placeholder"] = "./examples/placeholder.m4a" |
|
load_file_list["audio_surprise"] = "./examples/surprise.m4a" |
|
load_file_list["audio_angry"] = "./examples/angry.m4a" |
|
|
|
model_lmm = create_lmm() |
|
model_imagebind = imagebind_huge(pretrained=True, ckpt_path=load_file_list["imagebind"]) |
|
model_imagebind.eval() |
|
model_imagebind.to(device) |
|
rtg_skl = RetargetSkeleton(tgt_skel_file=load_file_list["skeleton"]) |
|
|
|
mean_path = load_file_list["mean"] |
|
std_path = load_file_list["std"] |
|
mean = np.load(mean_path) |
|
std = np.load(std_path) |
|
|
|
def show_generation_result(model, text, audio_path, motion_length, result_path): |
|
fps = 20 |
|
if audio_path is not None: |
|
motion_length = min(200, int(get_audio_length(audio_path) * fps) + 1) |
|
motion = torch.zeros(1, motion_length, 669).to(device) |
|
motion_mask = torch.ones(1, motion_length).to(device) |
|
motion_mask[0, :motion_length] = 1 |
|
motion_mask = motion_mask.unsqueeze(-1).repeat(1, 1, 10) |
|
motion_mask[:, :, 9] = 0 |
|
dataset_name = "humanml3d_t2m" |
|
kinematic_chain = t2m_body_hand_kinematic_chain |
|
rotation_type = "h3d_rot" |
|
motion_metas = [{ |
|
'meta_data': dict(framerate=fps, dataset_name=dataset_name, rotation_type=rotation_type) |
|
}] |
|
motion_length = torch.Tensor([motion_length]).long().to(device) |
|
if text is None and audio_path is not None: |
|
text = "A person is standing and speaking." |
|
|
|
model = model.to(device) |
|
input = { |
|
'motion': motion, |
|
'motion_mask': motion_mask, |
|
'motion_length': motion_length, |
|
'motion_metas': motion_metas, |
|
'num_intervals': 1 |
|
} |
|
if text is not None: |
|
text_word_feat, text_seq_feat = \ |
|
extract_text_feature([text], model_imagebind, device) |
|
assert text_word_feat.shape[0] == 1 |
|
assert text_word_feat.shape[1] == 77 |
|
assert text_word_feat.shape[2] == 1024 |
|
assert text_seq_feat.shape[0] == 1 |
|
assert text_seq_feat.shape[1] == 1024 |
|
input['text_word_feat'] = text_word_feat |
|
input['text_seq_feat'] = text_seq_feat |
|
input['text_cond'] = torch.Tensor([1.0] * 1).to(device) |
|
else: |
|
input['text_word_feat'] = torch.zeros(1, 77, 1024).to(device) |
|
input['text_seq_feat'] = torch.zeros(1, 1024) |
|
input['text_cond'] = torch.Tensor([0] * 1).to(device) |
|
if audio_path is not None: |
|
speech_word_feat, speech_seq_feat = \ |
|
extract_audio_feature([audio_path], model_imagebind, device) |
|
assert speech_word_feat.shape[0] == 1 |
|
assert speech_word_feat.shape[1] == 229 |
|
assert speech_word_feat.shape[2] == 768 |
|
assert speech_seq_feat.shape[0] == 1 |
|
assert speech_seq_feat.shape[1] == 1024 |
|
input['speech_word_feat'] = speech_word_feat |
|
input['speech_seq_feat'] = speech_seq_feat |
|
input['speech_cond'] = torch.Tensor([1.0] * 1).to(device) |
|
else: |
|
input['speech_word_feat'] = torch.zeros(1, 229, 768).to(device) |
|
input['speech_seq_feat'] = torch.zeros(1, 1024) |
|
input['speech_cond'] = torch.Tensor([0] * 1).to(device) |
|
|
|
all_pred_motion = [] |
|
with torch.no_grad(): |
|
input['inference_kwargs'] = {} |
|
output = model(**input)[0]['pred_motion'][:motion_length] |
|
pred_motion = output.cpu().detach().numpy() |
|
pred_motion = pred_motion * std + mean |
|
|
|
plot_tomato(pred_motion, kinematic_chain, result_path, None, fps, 2) |
|
|
|
if audio_path is not None: |
|
add_audio(result_path, [audio_path]) |
|
|
|
def generate(prompt, audio_check, audio_path, length): |
|
if not os.path.exists("outputs"): |
|
os.mkdir("outputs") |
|
result_path = "outputs/" + str(int(time.time())) + ".mp4" |
|
print(audio_path) |
|
if not audio_check: |
|
audio_path = None |
|
if audio_path is not None and not os.path.exists(audio_path): |
|
audio_path = None |
|
if audio_path is not None and audio_path.endswith("placeholder.wav"): |
|
audio_path = None |
|
if len(prompt) == 0: |
|
prompt = None |
|
show_generation_result(model_lmm, prompt, audio_path, length, result_path) |
|
return result_path |
|
|
|
input_audio = gr.Audio( |
|
type='filepath', |
|
format='wav', |
|
label="Audio (1-10s, overwrite motion length):", |
|
show_label=True, |
|
sources=["upload", "microphone"], |
|
min_length=1, |
|
max_length=10, |
|
waveform_options=gr.WaveformOptions( |
|
waveform_color="#01C6FF", |
|
waveform_progress_color="#0066B4", |
|
skip_length=2, |
|
show_controls=False, |
|
), |
|
) |
|
|
|
input_text = gr.Textbox( |
|
label="Text prompt:" |
|
) |
|
|
|
audio_check = gr.Checkbox( |
|
label="Enable audio? " |
|
) |
|
|
|
demo = gr.Interface( |
|
fn=generate, |
|
inputs=[input_text, audio_check, input_audio, gr.Slider(20, 200, value=60, label="Motion length (fps 20):")], |
|
outputs=gr.Video(label="Video:"), |
|
examples=[ |
|
["A person walks in a circle.", False, load_file_list["audio_placeholder"], 120], |
|
["A person jumps forward.", False, load_file_list["audio_placeholder"], 100], |
|
["A person is stretching arms.", False, load_file_list["audio_placeholder"], 80], |
|
["", True, load_file_list["audio_surprise"], 200], |
|
["", True, load_file_list["audio_angry"], 200], |
|
], |
|
title="LMM: Large Motion Model for Unified Multi-Modal Motion Generation", |
|
description="\nThis is an interactive demo for LMM. For more information, feel free to visit our project page(https://github.com/mingyuan-zhang/LMM).") |
|
|
|
demo.queue() |
|
demo.launch() |