Spaces:
Runtime error
Runtime error
| """ | |
| Hugging Face Space demo for UniBioTransfer. | |
| Gradio interface for face/hair/motion/head transfer. | |
| ZeroGPU Compatible: | |
| - Model initialized on CPU (no GPU memory during startup) | |
| - Inference wrapped with @spaces.GPU decorator | |
| - Thread-safe global variable access with Lock | |
| """ | |
| import threading | |
| import torch | |
| from PIL import Image | |
| import numpy as np | |
| if 1: | |
| # ========================================== | |
| # patch: | |
| # 解决 ZeroGPU 下 torch.load 默认寻找旧 CUDA 设备的报错 | |
| # ========================================== | |
| _original_load = torch.load | |
| def _safe_load(*args, **kwargs): | |
| kwargs['map_location'] = 'cpu' | |
| kwargs['weights_only'] = False | |
| return _original_load(*args, **kwargs) | |
| torch.load = _safe_load | |
| # ========================================== | |
| # 兼容层:处理本地测试 vs HF ZeroGPU 环境 | |
| # ========================================== | |
| try: | |
| import spaces | |
| print("Detected spaces library (Hugging Face environment).") | |
| except ImportError: | |
| print("Local environment detected. Mocking spaces.GPU...") | |
| class spaces: | |
| def GPU(func): | |
| return func # 本地测试时,装饰器变为空壳,直接执行原函数 | |
| from infer_hf import UniBioTransferPipeline | |
| # 锁和全局单例 Pipeline | |
| inference_lock = threading.Lock() | |
| global_pipeline :UniBioTransferPipeline = UniBioTransferPipeline.from_pretrained( | |
| repo_id="scy639/UniBioTransfer", | |
| task=0, | |
| device="cpu", | |
| ) | |
| def get_pipeline(task): | |
| """ | |
| 单例模式:全局只初始化一次模型(放在 CPU),后续只切换任务。 | |
| 强制写死 CPU,保证 ZeroGPU 全局初始化时不碰显卡。 | |
| """ | |
| global global_pipeline | |
| if global_pipeline is None: | |
| print("Initializing pipeline once on CPU...") | |
| # 强制写死 CPU,保证 ZeroGPU 全局初始化时不碰显卡 | |
| global_pipeline = UniBioTransferPipeline.from_pretrained( | |
| repo_id="scy639/UniBioTransfer", | |
| task=task, | |
| device="cpu", | |
| ) | |
| else: | |
| # 如果模型已经在内存中,只需切换 task ID 即可 | |
| print(f"Switching existing pipeline to task: {task}") | |
| global_pipeline.set_task(task) | |
| return global_pipeline | |
| # 核心:将所有会用到 GPU 的前向推理逻辑包裹在这里 | |
| def run_gpu_inference(pipeline:UniBioTransferPipeline, tgt_pil, ref_pil, ddim_steps, scale, seed, num_images): | |
| """ | |
| 这里是 ZeroGPU 分配算力的地方。进入此函数时可以安全地 to("cuda")。 | |
| 如果是在本地服务器,这个装饰器没用,但内部的 .to("cuda") 同样生效。 | |
| """ | |
| return pipeline( | |
| tgt_pil, | |
| ref_pil, | |
| ddim_steps=ddim_steps, | |
| scale=scale, | |
| seed=seed, | |
| num_images=num_images, | |
| ) | |
| def inference(task, tgt_img, ref_img, ddim_steps, seed, num_images): | |
| """ | |
| Run inference for the demo. | |
| """ | |
| if tgt_img is None or ref_img is None: | |
| return None, "Please upload both target and reference images." | |
| try: | |
| # 1. 拿模型 (此时模型在 CPU) | |
| pipeline = get_pipeline(task) | |
| tgt_pil = Image.fromarray(tgt_img).convert("RGB") | |
| ref_pil = Image.fromarray(ref_img).convert("RGB") | |
| # 2. 加锁,防止并发污染 global_.task,进入 GPU 推理 | |
| with inference_lock: | |
| results = run_gpu_inference( | |
| pipeline, | |
| tgt_pil, | |
| ref_pil, | |
| int(ddim_steps), | |
| float(3), | |
| int(seed), | |
| int(num_images) | |
| ) | |
| return results, f"Success! Task: {task} transfer completed." | |
| except Exception as e: | |
| import traceback | |
| error_msg = f"Error: {str(e)}\n{traceback.format_exc()}" | |
| print(f"{error_msg}") | |
| return None, error_msg | |
| def create_demo(): | |
| """Create Gradio demo interface.""" | |
| import gradio as gr | |
| with gr.Blocks(title="UniBioTransfer") as demo: | |
| gr.Markdown( | |
| """ | |
| # UniBioTransfer | |
| Perform face transfer, hair transfer, motion transfer (face reenactment), and head transfer. | |
| - **Face Transfer**: Transfer face identity from reference to target | |
| - **Hair Transfer**: Transfer hairstyle from reference to target | |
| - **Motion Transfer**: Transfer motion(expression+head pose) from reference to target | |
| - **Head Transfer**: Transfer entire head from reference to target | |
| [Code](https://github.com/scy639/UniBioTransfer) | |
| [Project Page](https://scy639.github.io/UniBioTransfer.github.io/) | |
| [Paper](https://arxiv.org/abs/2603.19637) | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| task_dropdown = gr.Dropdown( | |
| choices=["face", "hair", "motion", "head"], | |
| value="face", | |
| label="Task", | |
| info="Select the transfer type", | |
| ) | |
| with gr.Row(): | |
| tgt_image = gr.Image( | |
| label="Target Image", | |
| type="numpy", | |
| height=300, | |
| ) | |
| ref_image = gr.Image( | |
| label="Reference Image", | |
| type="numpy", | |
| height=300, | |
| ) | |
| with gr.Row(): | |
| ddim_steps = gr.Slider( | |
| minimum=4, | |
| maximum=50, | |
| value=50, | |
| step=1, | |
| label="DDIM Steps", | |
| info="More steps = better quality but slower", | |
| ) | |
| # scale = gr.Slider( | |
| # minimum=1.0, | |
| # maximum=10.0, | |
| # value=3.0, | |
| # step=0.5, | |
| # label="CFG Scale", | |
| # info="Guidance scale for conditioning", | |
| # ) | |
| seed = gr.Number( | |
| value=42, | |
| label="Random Seed", | |
| info="For reproducibility", | |
| ) | |
| num_images = gr.Slider( | |
| minimum=1, | |
| maximum=32, | |
| value=4, | |
| step=1, | |
| label="Number of output images", | |
| info="Multi-output with different initial noise", | |
| ) | |
| run_btn = gr.Button("Run Inference", variant="primary") | |
| with gr.Column(): | |
| output_gallery = gr.Gallery( | |
| label="Results", | |
| height=800, | |
| columns=2, | |
| ) | |
| status_text = gr.Textbox( | |
| label="Status", | |
| lines=3, | |
| ) | |
| gr.Markdown( | |
| """ | |
| ### Usage | |
| 1. Upload a **target image** (the person whose face/hair/motion/head will be modified) | |
| 2. Upload a **reference image** (the source of the attribute to transfer) | |
| 3. Select the **task** type | |
| 4. Click "Run Inference" | |
| ### Requirements | |
| - Works best when the heads in the two input images have similar sizes. | |
| """ | |
| ) | |
| run_btn.click( | |
| fn=inference, | |
| inputs=[task_dropdown, tgt_image, ref_image, ddim_steps, seed, num_images], | |
| outputs=[output_gallery, status_text], | |
| ) | |
| task_dropdown.change( | |
| fn=lambda t: f"Task switched to: {t} transfer", | |
| inputs=[task_dropdown], | |
| outputs=[status_text], | |
| ) | |
| gr.Examples( | |
| examples=[ | |
| ["face", "examples/face/tgt.png", "examples/face/ref.png", 20, 42, 4], | |
| ["hair", "examples/hair/tgt.png", "examples/hair/ref.png", 20, 42, 4], | |
| ["motion", "examples/motion/tgt.png", "examples/motion/ref.png", 20, 42, 4], | |
| ["head", "examples/head/tgt.png", "examples/head/ref.png", 20, 42, 4], | |
| ], | |
| inputs=[task_dropdown, tgt_image, ref_image, ddim_steps, seed, num_images], | |
| label="Examples", | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_demo() | |
| demo.launch() | |