cosmichackerx
Safeguard against Gradio ffmpeg postprocessing conflicts by returning a unique copy of the output video and removing format='mp4' constraint
7a10a46
Raw
History Blame Contribute Delete
7.45 kB
# Patch for basicsr torchvision compatibility
import sys
import torchvision.transforms.functional as TF
sys.modules['torchvision.transforms.functional_tensor'] = TF
import os
import subprocess
import spaces
import gradio as gr
# Patch load_file_from_url to avoid PermissionError in HF spaces (read-only site-packages)
import os
os.makedirs("gfpgan/weights", exist_ok=True)
try:
import facexlib.utils
orig_facex_load = facexlib.utils.load_file_from_url
def patched_facex_load(*args, **kwargs):
kwargs["model_dir"] = "gfpgan/weights"
kwargs["save_dir"] = "gfpgan/weights"
if len(args) > 1:
args = list(args)
args[1] = "gfpgan/weights"
args = tuple(args)
try:
return orig_facex_load(*args, **kwargs)
except TypeError as e:
if "save_dir" in str(e) and "save_dir" in kwargs:
kwargs.pop("save_dir")
return orig_facex_load(*args, **kwargs)
raise
facexlib.utils.load_file_from_url = patched_facex_load
except Exception:
pass
try:
import basicsr.utils.download_util
orig_basicsr_load = basicsr.utils.download_util.load_file_from_url
def patched_basicsr_load(*args, **kwargs):
kwargs["model_dir"] = "gfpgan/weights"
kwargs["save_dir"] = "gfpgan/weights"
if len(args) > 1:
args = list(args)
args[1] = "gfpgan/weights"
args = tuple(args)
try:
return orig_basicsr_load(*args, **kwargs)
except TypeError as e:
if "save_dir" in str(e) and "save_dir" in kwargs:
kwargs.pop("save_dir")
return orig_basicsr_load(*args, **kwargs)
raise
basicsr.utils.download_util.load_file_from_url = patched_basicsr_load
except Exception:
pass
def download_models():
if not os.path.exists("checkpoints/mapping_00109-model.pth.tar"):
print("Downloading SadTalker checkpoints...")
os.makedirs("checkpoints", exist_ok=True)
models = [
("https://github.com/OpenTalker/SadTalker/releases/download/v0.0.2-rc/mapping_00109-model.pth.tar", "checkpoints/mapping_00109-model.pth.tar"),
("https://github.com/OpenTalker/SadTalker/releases/download/v0.0.2-rc/mapping_00229-model.pth.tar", "checkpoints/mapping_00229-model.pth.tar"),
("https://github.com/OpenTalker/SadTalker/releases/download/v0.0.2-rc/SadTalker_V0.0.2_256.safetensors", "checkpoints/SadTalker_V0.0.2_256.safetensors"),
("https://github.com/OpenTalker/SadTalker/releases/download/v0.0.2-rc/SadTalker_V0.0.2_512.safetensors", "checkpoints/SadTalker_V0.0.2_512.safetensors")
]
gfpgan_models = [
("https://github.com/xinntao/facexlib/releases/download/v0.1.0/alignment_WFLW_4HG.pth", "gfpgan/weights/alignment_WFLW_4HG.pth"),
("https://github.com/xinntao/facexlib/releases/download/v0.1.0/detection_Resnet50_Final.pth", "gfpgan/weights/detection_Resnet50_Final.pth"),
("https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth", "gfpgan/weights/GFPGANv1.4.pth"),
("https://github.com/xinntao/facexlib/releases/download/v0.2.2/parsing_parsenet.pth", "gfpgan/weights/parsing_parsenet.pth"),
("https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", "gfpgan/weights/RealESRGAN_x2plus.pth")
]
for url, path in models:
if not os.path.exists(path):
subprocess.run(["wget", "-nc", url, "-O", path])
os.makedirs("gfpgan/weights", exist_ok=True)
for url, path in gfpgan_models:
if not os.path.exists(path):
subprocess.run(["wget", "-nc", url, "-O", path])
print("Downloads complete.")
download_models()
from src.gradio_demo import SadTalker
# Initialize with lazy_load=True so weights only move to GPU during the wrapped function call
sad_talker = SadTalker(checkpoint_path='checkpoints', config_path='src/config', lazy_load=True)
import traceback
@spaces.GPU(duration=120)
def generate_video(source_image, driven_audio, preprocess_type, is_still_mode, enhancer, batch_size, size_of_image, pose_style):
try:
if not source_image or not driven_audio:
return None, "Please provide both a source image and an audio file."
vid_path = sad_talker.test(source_image, driven_audio, preprocess_type, is_still_mode, enhancer, batch_size, size_of_image, pose_style)
import shutil, tempfile
out_dir = tempfile.mkdtemp()
safe_vid_path = os.path.join(out_dir, "output_avatar.mp4")
shutil.copy(vid_path, safe_vid_path)
return safe_vid_path, "Success!"
except Exception as e:
import traceback
err = traceback.format_exc()
print(f"ERROR: {err}")
return None, f"Error: {str(e)}\n\nTraceback:\n{err}"
# Sleek Web UI
css = """
#header { text-align: center; margin-bottom: 20px; }
#header h1 { font-family: 'Inter', sans-serif; font-weight: 800; color: #E0E7FF; font-size: 2.5rem; }
#header p { font-family: 'Inter', sans-serif; color: #9CA3AF; font-size: 1.1rem; }
.generate-btn { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); color: white !important; font-weight: bold; border: none !important; transition: transform 0.2s; }
.generate-btn:hover { transform: translateY(-2px); }
"""
with gr.Blocks(css=css, theme=gr.themes.Monochrome()) as demo:
with gr.Column(elem_id="header"):
gr.Markdown("# 🗣️ Real-Live Talking Avatar")
gr.Markdown("Convert any portrait photo into a highly realistic speaking avatar using audio. Powered by **SadTalker** & **ZeroGPU**.")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 1. Inputs")
source_image = gr.Image(label="Source Image (Face)", type="filepath")
driven_audio = gr.Audio(label="Driving Audio (Speech)", type="filepath")
with gr.Accordion("Advanced Settings", open=False):
preprocess_type = gr.Radio(['crop', 'resize', 'full', 'extcrop', 'extfull'], value='crop', label='Preprocess')
is_still_mode = gr.Checkbox(label="Still Mode (Fewer head movements, best with 'full' preprocess)")
enhancer = gr.Checkbox(label="GFPGAN Face Enhancement (Sharper face)", value=True)
size_of_image = gr.Radio([256, 512], value=256, label='Face Model Resolution')
pose_style = gr.Slider(minimum=0, maximum=46, step=1, label="Pose Style Index", value=0)
batch_size = gr.Slider(label="Batch Size", step=1, maximum=10, value=2)
submit_btn = gr.Button("🎬 GENERATE AVATAR (ZEROGPU)", elem_classes="generate-btn")
with gr.Column(scale=1):
gr.Markdown("### 2. Output")
gen_video = gr.Video(label="Generated Avatar")
error_log = gr.Textbox(label="Error Logs (if any)", lines=5)
submit_btn.click(
fn=generate_video,
inputs=[
source_image, driven_audio, preprocess_type,
is_still_mode, enhancer, batch_size,
size_of_image, pose_style
],
outputs=[gen_video, error_log]
)
if __name__ == "__main__":
demo.launch(show_error=True)