ERNIE-Image + InfiniteTalk,从文生图到无限时长视频——国产DCU部署:从图像生成到音画同步数字人视频的全链路实现(含完整代码)

一、引言

数字人技术正从"高成本、低智能"的特效模型,加速演变为"低门槛、高可用"的虚拟个体。在传媒、教育、客服、政务等领域,数字人主播、虚拟教师、智能客服等应用场景持续落地。然而,传统数字人视频生成面临两大瓶颈:一是依赖复杂的3D建模与骨骼绑定流程,制作周期长、成本高;二是长视频生成时难以保持身份一致性与动作连贯性。

本文基于国产DCU算力平台(8×K100_AI,单卡64GB显存),整合ERNIE-Image文生图模型与InfiniteTalk音频驱动视频生成框架,构建了一套完整的数字人视频生成系统。用户仅需输入一段文本描述和一段音频,系统即可自动生成人物图像并驱动其"说话"------不仅实现精准的唇形同步,还能让头部动作、身体姿态和面部表情与音频完美对齐。

二、程序方案设计

本系统的整体架构采用三阶段流水线设计。第一阶段,用户输入图像描述文本,ERNIE-Image模型生成高质量的人物肖像图作为数字人的"外貌基础";第二阶段,系统对用户上传的音频进行预处理,包括响度归一化与Wav2Vec2特征提取,将音频信号转化为与视频帧率对齐的嵌入向量;第三阶段,InfiniteTalk框架以生成的人物图像和音频嵌入为输入,通过音频信号驱动人物面部表情、唇形运动及头部姿态,最终输出音画同步的数字人视频。整个流程中,用户仅需提供图像提示词、视频驱动提示词和一段音频文件,系统即可全自动完成从"静态图像"到"动态说话视频"的端到端生成。

在硬件选型上,本系统部署于一台搭载8张K100_AI显卡的国产DCU服务器,每张显卡配备64GB超大显存。这一硬件配置是本系统能够顺利落地的关键所在。InfiniteTalk框架涉及Wan2.1-I2V-14B(140亿参数)、InfiniteTalk权重(约350亿参数)、T5文本编码器以及Wav2Vec2音频编码器等多个大模型组件,模型总参数量超过500亿。在64GB大显存的支撑下,单卡即可容纳核心模型权重,配合FSDP(全分片数据并行)策略将模型参数均匀切分至多卡,使得系统在8卡环境下能够高效完成分布式推理,大幅降低了跨卡通信带来的性能开销。此外,大显存也允许系统将Wav2Vec2音频编码器等辅助模块常驻显存,避免了频繁的模型加载与卸载操作。

软件环境方面,系统基于统一的Docker镜像构建,镜像标识为harbor.sourcefind.cn:5443/dcu/admin/base/custom:vllm0.15.1-ubuntu22.04-dtk26.04-0130-py3.10-20260220,内部集成了DTK 26.04加速库、Python 3.10运行环境以及PyTorch深度学习框架。DCU平台对PyTorch生态提供了高度兼容的"类CUDA"接口,算子覆盖率达99%以上,使得原有基于NVIDIA GPU开发的推理代码可以几乎无修改地迁移至国产算力平台。容器启动时配置了256GB共享内存并映射了/dev/kfd、/dev/dri等DCU设备节点,确保多卡通信与算力调用的稳定性。

在核心模型选型上,ERNIE-Image是百度文心大模型团队推出的开源文生图模型,基于单流扩散Transformer(DiT)架构设计,参数量为80亿。该模型内置轻量级提示词增强模块,能够将用户简短输入自动扩展为丰富描述,在精准文字渲染和知识密集型生成任务中表现尤为突出,支持中、英、日、韩等多语言生成,并在动漫、二次元、胶片、超现实主义等多种风格中均表现优异。InfiniteTalk则是一个基于稀疏帧设计的视频配音框架,以Wan2.1视频扩散模型为底座,通过维持81帧滚动上下文窗口并生成战略性"运动锚点",实现视频片段间的无缝过渡与身份一致性。与仅关注唇部同步的传统方法不同,InfiniteTalk能同时驱动面部表情、头部动作和身体姿态与音频情感自然对齐,并可直接作为"图像+音频转视频"模型使用。此外,系统还集成了Wan2.1-I2V-14B(140亿参数)作为图像转视频基础模型,以及chinese-wav2vec2-base作为中文语音特征提取模型。四个模型分工明确:ERNIE-Image负责数字人的"外观生成",Wan2.1-I2V-14B和InfiniteTalk负责"动作驱动与视频合成",Wav2Vec2承担"音频特征提取"的桥梁角色,共同构成了一个完整且高效的国产化数字人视频生成解决方案。

三、程序工作流程图

流程图关键路径说明:

|---------|-------------------------------|-------------------------------|
| 阶段 | 核心操作 | 关键模型/工具 |
| ① 文生图 | 图像提示词 → 8步扩散推理 | ERNIE-Image-Turbo (8B) |
| ② 音频预处理 | 响度归一化 → Wav2Vec2特征提取 → 嵌入向量生成 | Wav2Vec2 |
| ③ 分布式推理 | JSON配置 → 4卡并行 → 逐段生成视频 | InfiniteTalk + Wan2.1-I2V-14B |
| ④ 后处理 | 视频拼接 + 音轨合并 | FFmpeg |

四、 环境准备

4 .1 硬件与系统环境

|-----|----------------------|
| 组件 | 规格 |
| CPU | 2×海光7490 2.7GHz 64C |
| 内存 | 16×32G DDR5 |
| GPU | 8×海光DCU 64GB K100_AI |

4.2 软件环境部署

系统的软件部署基于统一的Docker容器方案,ERNIE-Image与InfiniteTalk两个模型共用同一个官方推荐镜像:harbor.sourcefind.cn:5443/dcu/admin/base/custom:vllm0.15.1-ubuntu22.04-dtk26.04-0130-py3.10-20260220。该镜像集成了DTK 26.04加速库、Python 3.10运行环境以及适配DCU的PyTorch深度学习框架,为两个模型提供了统一的运行底座。启动容器时需配置充足的共享内存(建议256GB)并采用主机网络模式,同时映射DCU设备节点/dev/kfd、/dev/dri和/dev/mkfd,以确保多卡通信与算力调用的稳定性。容器启动命令如下:

bash 复制代码
docker run -it --shm-size 256g --network=host \
  --device=/dev/kfd --device=/dev/dri --device=/dev/mkfd \
  -v /path/models:/home/models \
  -v /path/output:/home/models/output \
harbor.sourcefind.cn:5443/dcu/admin/base/custom:vllm0.15.1-ubuntu22.04-dtk26.04-0130-py3.10-20260220 bash

需安装系统级音视频处理工具FFmpeg,并根据DCU平台提供的特殊深度学习库(可从光合开发者社区下载)安装xformers等加速库。其余Python依赖参照https://developer.sourcefind.cn/codes/modelzoo/infinitetalk和https://developer.sourcefind.cn/codes/modelzoo/ernie-image_pytorch,可通过pip install -r requirements.txt完成安装。

所有模型权重均通过ModelScope命令行工具从魔搭社区下载至本地指定目录。ERNIE-Image权重约32GB;InfiniteTalk涉及三个模型文件:Wan2.1-I2V-14B-480P(14B参数)、chinese-wav2vec2-base(0.9B参数)以及InfiniteTalk主权重(35B参数)。下载命令如下:

bash 复制代码
cd /home/models/InfiniteTalk
modelscope download Wan-AI/Wan2.1-I2V-14B-480P --local_dir ./weights/Wan2.1-I2V-14B-480P

modelscope download TencentGameMate/chinese-wav2vec2-base --local_dir ./weights/chinese-wav2vec2-base

modelscope download TencentGameMate/chinese-wav2vec2-base model.safetensors --revision refs/pr/1 --local_dir ./weights/chinese-wav2vec2-basemodelscope download MeiGen-AI/InfiniteTalk --local_dir ./weights/InfiniteTalk

cd /home/models
modelscope download --model PaddlePaddle/ERNIE-Image-Turbo --local_dir ./ERNIE-Image-Turbo

模型下载完成后,将模型路径配置到推理脚本的相应参数中即可启动生成任务。

4.3程序代码设计

整个系统的程序代码由两部分构成:InfiniteTalk官方推理框架作为视频生成的核心引擎,自研的三个模块(ernie_model.py、pipeline.py、pipeline_web.py)完成文生图、流程编排和Web交互功能。以下从模块功能、交互逻辑和代码设计三个维度进行阐述。

1. InfiniteTalk官方推理框架

InfiniteTalk是一个稀疏帧视频配音框架,能够根据输入的视频或图像以及音频轨道,合成唇形同步、头部动作、身体姿态与音频完美对齐的新视频。该框架基于Wan2.1视频扩散模型构建,通过维持81帧滚动上下文窗口并生成战略性"运动锚点",实现视频片段间的无缝过渡与身份一致性。官方代码仓库提供了完整的推理脚本generate_infinitetalk.py,支持单卡和多卡分布式推理。其核心功能是读取一个JSON配置文件(包含prompt、条件图像路径和音频路径),调用Wan2.1-I2V-14B-480P(14B参数)和InfiniteTalk,生成音画同步的数字人视频。

2. 自研模块一:ernie_model.py(文生图模块)

该模块封装了ERNIE-Image文生图模型的加载与推理逻辑。ERNIE-Image是百度文心大模型团队推出的文生图模型,基于DiT架构设计,参数量为80亿,仅需24GB显存即可运行。代码设计上,首先通过环境变量HIP_VISIBLE_DEVICES="4"将ERNIE-Image固定在GPU 4上运行,避免与后续InfiniteTalk的多卡推理产生资源冲突。模型加载时采用torch.bfloat16精度以降低显存占用,并执行attention算子的预热操作,消除首次推理的延迟。generate_image函数对外提供简洁的调用接口,支持自定义图像尺寸、推理步数和引导系数,默认8步推理即可生成高质量图像。

3. 自研模块二:pipeline.py(流程编排模块)

该模块是整个系统的"胶水代码",负责将ERNIE-Image文生图和InfiniteTalk视频生成串联为一个端到端的流水线。run_pipeline函数接收三个参数------图像提示词、视频驱动提示词和音频文件路径,依次执行五个步骤:调用ernie_model.generate_image生成人物肖像图并保存为临时PNG文件;将用户上传的音频拷贝到临时工作目录;构造符合InfiniteTalk要求的JSON配置文件(包含prompt、cond_video图像路径和cond_audio音频路径);通过subprocess调用torchrun启动4卡分布式推理,执行generate_infinitetalk.py脚本;最后从InfiniteTalk的输出目录中提取生成的视频文件,移动到最终的输出目录。代码中通过cwd=INFINITETALK_DIR指定工作目录,并通过env传递HIP_VISIBLE_DEVICES="0,1,2,3"环境变量,确保InfiniteTalk使用前4张显卡进行分布式推理。

4. 自研模块三:pipeline_web.py(Gradio Web交互模块)

该模块基于Gradio框架构建Web图形界面,将整个数字人视频生成能力封装为可视化的交互服务。界面设计包含三个输入组件------视频驱动提示词(必填,描述人物的动作、表情和运镜)、文生图提示词(可选,留空则自动复用视频驱动提示词)和音频文件上传;一个输出组件用于展示生成的视频结果。generate_video函数作为Gradio的回调接口,对输入进行非空校验后调用pipeline.run_pipeline执行完整的生成流程。界面还提供了详细的提示信息,包括音频格式建议(16kHz单声道WAV)和预计生成时间。服务启动后监听0.0.0.0:7861,支持局域网内访问。

5. 模块间的交互与数据流

三个自研模块与InfiniteTalk官方框架之间的数据流清晰明确:用户通过Gradio界面提交提示词和音频 → pipeline_web.py将参数传递给pipeline.py → pipeline.py调用ernie_model.py生成图像,构造JSON配置文件,再通过subprocess调用generate_infinitetalk.py → InfiniteTalk读取JSON配置,加载预训练权重,生成视频文件 → pipeline.py将视频文件移动到输出目录并返回路径 → pipeline_web.py将视频展示在Gradio界面上。整个流程实现了从"文本+音频"到"数字人视频"的全自动化生成,用户无需关注任何底层模型调用细节。

6.自研程序代码如下:

ernie_model.py代码内容如下:

python 复制代码
# ernie_model.py
import os
# 强制只使用 GPU 4
os.environ["HIP_VISIBLE_DEVICES"] = "4"

import torch
from modelscope import ErnieImagePipeline

# 全局配置(提升速度)
torch.set_float32_matmul_precision('high')
torch.backends.cudnn.benchmark = True
torch.backends.cuda.enable_mem_efficient_sdp(True)

# 加载模型(只执行一次)
print("Loading ERNIE-Image-Turbo on GPU 4 ...")
pipe = ErnieImagePipeline.from_pretrained(
    "/home/models/ERNIE-Image-Turbo",
    torch_dtype=torch.bfloat16,
).to("cuda")
print("ERNIE model loaded.")

# 可选:预热 attention 算子
with torch.no_grad():
    dummy_q = torch.randn(1, 8, 40, 64, device="cuda", dtype=torch.bfloat16)
    dummy_k = torch.randn(1, 8, 40, 64, device="cuda", dtype=torch.bfloat16)
    dummy_v = torch.randn(1, 8, 40, 64, device="cuda", dtype=torch.bfloat16)
    torch.nn.functional.scaled_dot_product_attention(dummy_q, dummy_k, dummy_v)


def generate_image(prompt, height=1264, width=848, steps=8, guidance_scale=1.0, use_pe=True):
    """
    生成图片并返回 PIL.Image 对象
    """
    with torch.no_grad():
        image = pipe(
            prompt=prompt,
            height=height,
            width=width,
            num_inference_steps=steps,
            guidance_scale=guidance_scale,
            use_pe=use_pe
        ).images[0]
    return image

pipeline.py代码内容如下:

python 复制代码
# pipeline.py
import os
import json
import shutil
import subprocess
import tempfile
import uuid
from pathlib import Path
from PIL import Image
from typing import Optional

from ernie_model import generate_image

# ---------- 路径配置(按实际修改) ----------
INFINITETALK_DIR = "/home/models/InfiniteTalk"
CKPT_DIR = f"{INFINITETALK_DIR}/weights/Wan2.1-I2V-14B-480P"
WAV2VEC_DIR = f"{INFINITETALK_DIR}/weights/chinese-wav2vec2-base"
INFINITETALK_WEIGHT = f"{INFINITETALK_DIR}/weights/InfiniteTalk/single/infinitetalk.safetensors"
OUTPUT_DIR = "/home/models/output"

os.makedirs(OUTPUT_DIR, exist_ok=True)


def run_pipeline(
    image_prompt: str,
    video_prompt: str,
    audio_file_path: str,
    height: int = 1264,
    width: int = 848,
    steps: int = 8,
    guidance_scale: float = 1.0,
    use_pe: bool = True,
    existing_image_path: Optional[str] = None,
    skip_video: bool = False
) -> tuple[Optional[str], Optional[str]]:
    """
    参数:
      image_prompt      : 文生图提示词(仅当 existing_image_path 为 None 时使用)
      video_prompt      : 视频驱动提示词
      audio_file_path   : 音频文件路径
      height, width, steps, guidance_scale, use_pe : 文生图参数(仅生成图片时使用)
      existing_image_path : 若提供,则直接使用该图片,跳过生成步骤
      skip_video        : 若为 True,则不执行视频生成,只返回图片路径
    返回:
      (图片路径, 视频路径) ,视频路径在 skip_video=True 时为 None
    """
    # ---------- 1. 处理图片 ----------
    if existing_image_path is not None:
        # 使用已有图片
        if not os.path.exists(existing_image_path):
            raise FileNotFoundError(f"指定的图片不存在: {existing_image_path}")
        image_path = existing_image_path
        print(f"[Pipeline] Using existing image: {image_path}")
    else:
        # 生成新图片
        print("[Pipeline] Generating image with ERNIE...")
        image = generate_image(
            prompt=image_prompt,
            height=height,
            width=width,
            steps=steps,
            guidance_scale=guidance_scale,
            use_pe=use_pe
        )
        # 保存到输出目录
        image_filename = f"generated_image_{uuid.uuid4().hex[:8]}.png"
        image_path = os.path.join(OUTPUT_DIR, image_filename)
        image.save(image_path)
        print(f"[Pipeline] Image saved to {image_path}")

    # 如果只生成图片,则直接返回
    if skip_video:
        return image_path, None

    # ---------- 2. 准备视频生成 ----------
    work_dir = tempfile.mkdtemp(prefix="digi_")
    # 复制图片到工作目录
    image_copy = os.path.join(work_dir, "input.png")
    shutil.copy(image_path, image_copy)

    audio_copy = os.path.join(work_dir, "audio.wav")
    shutil.copy(audio_file_path, audio_copy)
    print(f"[Pipeline] Audio copied to {audio_copy}")

    # ---------- 3. 构造 JSON ----------
    json_data = {
        "prompt": video_prompt,
        "cond_video": image_copy,
        "cond_audio": {
            "person1": audio_copy
        }
    }
    json_path = os.path.join(work_dir, "config.json")
    with open(json_path, 'w', encoding='utf-8') as f:
        json.dump(json_data, f, indent=2, ensure_ascii=False)

    # ---------- 4. 调用 InfiniteTalk ----------
    output_video_name = f"final_video_{uuid.uuid4().hex[:8]}"
    final_video_path = os.path.join(OUTPUT_DIR, f"{output_video_name}.mp4")

    env = os.environ.copy()
    env["HIP_VISIBLE_DEVICES"] = "0,1,2,3"

    gpu_num = 4
    cmd = [
        "torchrun",
        f"--nproc_per_node={gpu_num}",
        "--standalone",
        "generate_infinitetalk.py",
        f"--ckpt_dir={CKPT_DIR}",
        f"--wav2vec_dir={WAV2VEC_DIR}",
        f"--infinitetalk_dir={INFINITETALK_WEIGHT}",
        "--dit_fsdp",
        "--t5_fsdp",
        f"--ulysses_size={gpu_num}",
        f"--input_json={json_path}",
        "--size=infinitetalk-480",
        "--sample_steps=40",
        "--mode=streaming",
        "--motion_frame=9",
        f"--save_file={output_video_name}",
    ]

    print("[Pipeline] Running InfiniteTalk...")
    print(f"[Pipeline] Command: {' '.join(cmd)}")
    result = subprocess.run(
        cmd,
        cwd=INFINITETALK_DIR,
        env=env,
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        print("[Pipeline] InfiniteTalk error:")
        print(result.stderr)
        raise RuntimeError("InfiniteTalk generation failed")

    # ---------- 5. 提取生成的视频 ----------
    generated = os.path.join(INFINITETALK_DIR, f"{output_video_name}.mp4")
    if not os.path.exists(generated):
        import glob
        candidates = glob.glob(os.path.join(INFINITETALK_DIR, f"{output_video_name}*.mp4"))
        if candidates:
            generated = candidates[0]
        else:
            raise FileNotFoundError("Generated video file not found")

    shutil.move(generated, final_video_path)
    print(f"[Pipeline] Video saved to {final_video_path}")

    # 清理临时目录(可选)
    # shutil.rmtree(work_dir)

    return image_path, final_video_path

pipeline_web.py代码内容如下:

python 复制代码
# pipeline_web.py
import gradio as gr
from pipeline import run_pipeline, OUTPUT_DIR

# 状态(存储生成的图片路径)
image_state = gr.State(value=None)

def generate_image_only(
    image_prompt, video_prompt, audio_file,
    height, width, steps, guidance_scale, use_pe
):
    """仅生成图片并返回路径,同时更新状态"""
    if not video_prompt.strip():
        raise gr.Error("视频驱动提示词不能为空")
    # 若文生图提示为空则复用视频提示
    if not image_prompt.strip():
        image_prompt = video_prompt

    if audio_file is None:
        raise gr.Error("请上传音频文件(视频生成需要音频)")

    try:
        img_path, _ = run_pipeline(
            image_prompt=image_prompt,
            video_prompt=video_prompt,
            audio_file_path=audio_file,
            height=height,
            width=width,
            steps=steps,
            guidance_scale=guidance_scale,
            use_pe=use_pe,
            skip_video=True   # 只生成图片
        )
        return img_path, img_path  # 更新图片显示和状态
    except Exception as e:
        raise gr.Error(f"生成图片失败: {str(e)}")

def generate_video_from_image(
    video_prompt, audio_file,
    image_state_value
):
    """使用状态中的图片路径生成视频"""
    if not video_prompt.strip():
        raise gr.Error("视频驱动提示词不能为空")
    if audio_file is None:
        raise gr.Error("请上传音频文件")
    if image_state_value is None:
        raise gr.Error("请先生成图片")

    try:
        # 使用现有图片,忽略文生图参数(传入空字符串等占位)
        _, video_path = run_pipeline(
            image_prompt="",          # 不会被使用
            video_prompt=video_prompt,
            audio_file_path=audio_file,
            existing_image_path=image_state_value,
            skip_video=False
        )
        return video_path
    except Exception as e:
        raise gr.Error(f"生成视频失败: {str(e)}")

# 构建界面
with gr.Blocks(title="数字人生成系统", theme=gr.themes.Soft()) as demo:
    gr.Markdown(
        """
        # 🎤 文生图 + 音频驱动数字人视频(分步生成)
        1. 填写提示词、上传音频并调整参数,点击 **生成图片** 预览效果。
        2. 若满意,点击 **生成视频** 继续;若不满意,调整参数后重新生成图片。
        """
    )

    # 状态(隐藏)
    image_state = gr.State(value=None)

    with gr.Row():
        # 左列:输入
        with gr.Column(scale=1):
            with gr.Group():
                video_prompt = gr.Textbox(
                    label="🎬 视频驱动提示词(必填)",
                    lines=4,
                    placeholder="描述视频中人物的动作、表情、运镜等",
                    value="The woman is passionately singing into a professional microphone in a recording studio. She wears large black headphones and a dark cardigan over a gray top. Her long, wavy brown hair frames her face as she looks slightly upwards, her mouth open mid-song. The studio is equipped with various audio equipment, including a mixing console and a keyboard, with soundproofing panels on the walls. The lighting is warm and focused on her, creating a professional and intimate atmosphere. A close-up shot captures her expressive performance."
                )
                image_prompt = gr.Textbox(
                    label="🖼️ 文生图提示词(可选,留空自动复用)",
                    lines=4,
                    placeholder="如需生成不同的背景/人物,请在此输入。留空则使用视频驱动提示词。",
                    value=""
                )
                audio = gr.Audio(
                    label="🎵 上传音频 (WAV, 16kHz, 单声道)",
                    type="filepath"
                )

            with gr.Accordion("⚙️ 文生图高级参数", open=False):
                with gr.Row():
                    height = gr.Number(label="高度 (Height)", value=1264, precision=0)
                    width = gr.Number(label="宽度 (Width)", value=848, precision=0)
                with gr.Row():
                    steps = gr.Slider(label="推理步数 (Steps)", minimum=1, maximum=20, value=8, step=1)
                    guidance_scale = gr.Slider(label="引导系数 (Guidance Scale)", minimum=0.5, maximum=2.0, value=1.0, step=0.1)
                use_pe = gr.Checkbox(label="启用提示词增强 (use_pe)", value=True)

            # 操作按钮
            with gr.Row():
                gen_img_btn = gr.Button("🖼️ 生成图片", variant="secondary", size="lg")
                gen_video_btn = gr.Button("🎬 生成视频", variant="primary", size="lg")

        # 右列:输出
        with gr.Column(scale=1):
            with gr.Group():
                image_output = gr.Image(label="🖼️ 生成的图片", interactive=False, height=300)
            with gr.Group():
                video_output = gr.Video(label="🎬 生成的视频", interactive=False, height=400)

    # 事件绑定
    # 生成图片:调用 generate_image_only,输出图片并更新状态
    gen_img_btn.click(
        fn=generate_image_only,
        inputs=[image_prompt, video_prompt, audio, height, width, steps, guidance_scale, use_pe],
        outputs=[image_output, image_state]
    )

    # 生成视频:从状态获取图片路径,生成视频
    gen_video_btn.click(
        fn=generate_video_from_image,
        inputs=[video_prompt, audio, image_state],
        outputs=[video_output]
    )

    gr.Markdown(
        """
        ---
        **注意**:
        - 音频建议为 16kHz 采样率、单声道 WAV 格式。
        - 生成视频约需 3~5 分钟,请耐心等待。
        - 调整步数可平衡质量与速度,引导系数控制与提示词的贴合度。
        """
    )

if __name__ == "__main__":
    demo.launch(
        server_name="0.0.0.0",
        server_port=7861,
        share=False,
        allowed_paths=[OUTPUT_DIR]   # 允许访问输出目录
    )

五、程序使用方法及效果

程序运行截图

系统启动后,用户通过浏览器访问 http://服务器IP:7861 即可进入Gradio Web界面。界面左侧为输入区域,用户需填写视频驱动提示词(描述人物的动作、表情和运镜),可选填写文生图提示词(若不填写则自动复用前者),并上传一段16kHz采样率的单声道WAV音频文件;右侧为输出区域,用于展示生成的视频结果。点击"生成视频"按钮后,系统首先调用ERNIE-Image模型生成一张人物肖像图,推理仅需8步。随后系统自动构造JSON配置文件,并通过torchrun启动4卡分布式推理,调用InfiniteTalk生成音画同步的数字人视频。生成的视频不仅实现了精准的唇形同步,还能看到人物的头部动作、身体姿态和面部表情与音频情感自然对齐,画面流畅自然,身份一致性保持良好。系统运行过程中,终端会输出详细的日志信息,包括ERNIE模型加载状态、图像生成进度、InfiniteTalk分布式推理配置以及视频合成完成提示,方便用户实时掌握生成进度。

程序文生图的效果


程序最后生成视频的效果


InfiniteTalk生成视频效果

六、程序踩坑故障排查

在系统部署和运行过程中,可能遇到以下几类典型问题,以下逐一分析现象、原因与解决方案。

1. Docker容器DCU设备映射失败

****现象:****容器启动时报错"Device or resource busy"或无法找到/dev/kfd、/dev/dri设备节点,导致DCU显卡无法被容器识别。

****原因:****国产DCU显卡需要同时映射/dev/kfd(内核级设备接口)、/dev/dri(直接渲染管理)和/dev/mkfd(多设备扩展接口)三个设备节点,缺一不可。特别需要注意的是,如果宿主机刚刚完成重启,显卡驱动可能尚未完全加载,此时立即启动容器会导致设备节点不可用或识别异常。驱动加载通常需要数秒至数十秒的时间,在此期间/dev/kfd等设备节点虽然存在,但驱动内部状态尚未就绪,容器启动时无法正确绑定DCU设备。

****解决方案:****服务器重启后,首先执行ls -la /dev/kfd /dev/dri /dev/mkfd确认设备节点存在且权限正确,同时建议等待约30秒确保驱动完全加载后再启动容器。容器启动时必须添加完整的设备映射参数:--device=/dev/kfd --device=/dev/dri --device=/dev/mkfd。此外还需添加--shm-size 256g和--network=host参数确保共享内存充足与网络通信正常。如果设备节点存在但容器仍无法识别显卡,可尝试在宿主机执行dmesg | grep -i amdgpu检查驱动加载日志,确认驱动初始化完成后再启动容器。

2. Transformers库版本选择失误导致运行失败

****现象:****按照ERNIE-Image或InfiniteTalk官方文档安装指定版本的Transformers库后,模型加载时报错"AttributeError"或"ImportError",提示某个类或方法不存在。

****原因:****开源项目的文档可能存在滞后性,官方文档推荐的Transformers版本与实际代码依赖的版本不一致。开发者查阅文档时容易根据旧版本文档安装错误版本,导致依赖缺失或不兼容。经实测,ERNIE-Image和InfiniteTalk框架均可在Transformers 5.14.1版本下正常运行。

****解决方案:****进入容器后直接执行pip install transformers==5.14.1安装统一版本,无需创建多个虚拟环境进行隔离。执行以下命令验证安装:python -c "import transformers; print(transformers.version)",输出应为5.14.1。若此前已安装其他版本,先执行pip uninstall transformers -y卸载后再安装正确版本。项目根目录下的requirements.txt文件也应更新为transformers==5.14.1,确保后续环境重建时无需重复手动指定版本。

3. subprocess调用InfiniteTalk时找不到generate_infinitetalk.py

****现象:****点击生成视频后,终端报错"python: can't open file '/home/models/InfiniteTalk/generate_infinitetalk.py': Errno 2 No such file or directory"。

****原因:****subprocess.run执行时,默认工作目录为当前Python进程所在目录,而非InfiniteTalk项目根目录。pipeline.py中的cwd=INFINITETALK_DIR参数未正确指向包含generate_infinitetalk.py的项目根目录,或者该目录缺少__init__.py文件导致Python无法将其识别为有效的包。

****解决方案:****首先确认INFINITETALK_DIR变量指向正确的项目解压根目录(通常为/home/models/InfiniteTalk或/home/models/infinitetalk-master),且该目录下确实存在generate_infinitetalk.py和__init__.py文件。若缺少__init__.py,手动创建空文件:touch /home/models/InfiniteTalk/init.py。其次,在subprocess.run中显式设置cwd=INFINITETALK_DIR,并确保PYTHONPATH环境变量包含项目根目录:env"PYTHONPATH" = INFINITETALK_DIR + ":" + env.get("PYTHONPATH", "")。

4. GPU资源分配冲突导致显存不足

****现象:****运行过程中报错"HIP out of memory"或"RuntimeError: CUDA error: out of memory",或者ERNIE-Image与InfiniteTalk同时抢占同一块GPU导致进程卡死。

****原因:****ERNIE-Image文生图模块(约24GB显存)与InfiniteTalk推理模块(总权重约500亿参数,多卡分布式部署)均在DCU服务器上运行,如果两者显卡编号重叠,会导致显存不足或资源竞争。ERNIE-Image加载后常驻显存,而InfiniteTalk通过torchrun启动多卡分布式进程,两者必须使用不同的显卡。

****解决方案:****在ernie_model.py中通过os.environ"HIP_VISIBLE_DEVICES" = "4"将ERNIE-Image固定在GPU 4上运行(该卡不参与InfiniteTalk的分布式推理)。在pipeline.py中通过env"HIP_VISIBLE_DEVICES" = "0,1,2,3"将InfiniteTalk限制在前4张显卡上运行。此外,在generate_infinitetalk.py启动参数中设置--num_persistent_param_in_dit(如设置为10)来限制DiT模型中常驻显存的参数量,进一步降低单卡显存峰值占用。

5. JSON配置文件格式不正确导致InfiniteTalk解析失败

****现象:****InfiniteTalk报错"KeyError: 'cond_video'"或"JSONDecodeError: Expecting value: line X column Y",生成流程中断。

****原因:****pipeline.py中构造的json_data字典结构与InfiniteTalk官方要求的格式不一致。官方examples/single_example_image.json中的cond_audio是一个包含person1和person2键的对象,且cond_video必须为有效的图像文件路径。如果字典缺少必要字段、文件路径中包含中文或特殊字符,或JSON格式错误(如尾随逗号),均会导致解析失败。

****解决方案:****严格按照官方示例构造JSON,确保包含prompt、cond_video、cond_audio和audio_type等必要字段。文件路径使用绝对路径且不含空格或特殊字符,可通过os.path.abspath()转换。在写入JSON前使用json.dumps(json_data, ensure_ascii=False, indent=2)验证格式,并通过try-except捕获json.JSONDecodeError异常。另外,cond_video字段在InfiniteTalk中既支持视频文件也支持图像文件,当输入为图像时,系统会将其作为首帧处理,因此pipeline.py中传入PNG图像路径是合法的。

6. 生成的视频文件名不匹配导致文件提取失败

****现象:****InfiniteTalk日志显示生成完成,但pipeline.py报错"FileNotFoundError: Generated video file not found",无法定位输出视频。

****原因:****generate_infinitetalk.py中--save_file参数指定的文件名可能被追加了时间戳或任务ID后缀,导致最终生成的视频文件名与预期不完全一致。例如,指定--save_file=final_video时,实际输出文件可能为final_video_20260726_123456.mp4。此外,如果--save_file参数未指定完整路径,文件会保存在InfiniteTalk项目的根目录(即INFINITETALK_DIR),而非OUTPUT_DIR。

****解决方案:****在pipeline.py中提取视频文件时,不依赖精确的文件名匹配,而是使用glob.glob进行模糊匹配:先精确拼接预期路径检查是否存在,若不存在则使用glob.glob(os.path.join(INFINITETALK_DIR, f"{output_video_name}*.mp4"))匹配所有以output_video_name开头的mp4文件,取匹配结果中的第一个。同时,在subprocess.run执行成功后,添加适当的延时(如time.sleep(2))等待文件系统写入完成,避免因文件尚未完全落盘导致匹配失败。最后,在shutil.move移动文件后,删除原始目录中的残留文件以避免下次运行时的命名冲突。

七、总结和展望

本文基于国产DCU算力平台,整合ERNIE-Image文生图模型与InfiniteTalk音频驱动视频生成框架,构建了一套完整的数字人视频生成系统。系统以"文本描述+音频"为输入,自动完成"文生图→图像驱动→视频合成"的端到端流程,实现了从静态图像到动态说话视频的全自动化生成。在硬件方面,系统充分利用了K100_AI单卡64GB大显存的硬件优势,通过单机多卡分布式推理承载了总计超过500亿参数的大模型组合,验证了国产算力在大规模生成式AI任务中的可行性与实用性。在软件架构方面,ERNIE-Image负责数字人的"外观生成",InfiniteTalk(基于Wan2.1底座)负责"动作驱动与视频合成",Wav2Vec2承担"音频特征提取"的桥梁角色,四个模型分工明确、协同工作,构成了一个完整且高效的国产化数字人视频生成解决方案。在交互体验方面,系统通过Gradio框架提供了简洁直观的Web界面,用户无需任何编程知识即可完成从提示词输入到视频生成的全流程操作,大幅降低了数字人视频的制作门槛。

展望未来,系统的优化与扩展可以从以下几个方向展开。在推理性能方面,可以引入InfiniteTalk已支持的TeaCache缓存机制和APG自适应投影引导,减少扩散模型推理过程中的冗余计算,提升用户体验的流畅度。在功能扩展方面,当前系统仅支持单人说话场景,后续可基于generate_infinitetalk.py中已实现的process_tts_multi函数(支持(s1)和(s2)标记的多角色TTS),完善双人对话功能,实现双数字人交互式对话视频生成,拓展数字人在访谈节目、双人播客等场景中的应用。在实时交互方面,可以结合流式语音识别与实时推理技术,探索数字人直播场景下的低延迟视频生成方案,推动数字人从"离线生产"走向"实时交互"。在模型轻量化方面,可尝试INT8或FP8量化进一步降低模型显存占用,使得在单卡64GB显存下能够同时承载更多模型组件,甚至将整套系统部署在更少卡数的服务器上,降低硬件成本,推动数字人技术的普惠化。此外,还可以探索数字人姿态与手势的精细化控制能力,结合3D关键点检测或姿态估计技术,让用户能够指定数字人的肢体动作,进一步提升生成视频的表现力与可控性。

综上所述,本系统为国产算力平台上的数字人视频生产提供了一个低成本、高效率、全链路国产化的技术路径,随着国产算力生态的持续完善和生成式AI技术的不断发展,数字人技术必将在传媒、教育、客服、政务、文旅等领域发挥更大的价值,推动虚拟内容生产进入全新的智能化时代。

相关推荐
中微极客1 小时前
AI视频生成器技术选型与工程实践指南(2026)
人工智能·音视频
神奇霸王龙11 小时前
国产音乐视频 Prompt 三段式屠夫榜:5 个国产视频模型实测对比
人工智能·ai·prompt·音视频·agent·ai编程·agi
神奇霸王龙11 小时前
AI视频Prompt结构化实战指南:可灵/万相/豆包
人工智能·ai·prompt·aigc·音视频·ai编程
SEONIB_Explorer14 小时前
跨境电商新手零门槛教程:AI 一键生成 TikTok/Ins 投放短视频,5 步 60 秒出片
人工智能·音视频·跨境电商·视频制作·veonib
运营小白17 小时前
电商产品链接自动生成视频广告:从“信息搬运“到“链接进、视频出“的工程化思路
大数据·人工智能·aigc·音视频·veonib·电商视频广告·产品链接解析
AI创界者20 小时前
【AI视频生成】ComfyUI + LTX-Video 2.3 整合包:超低显存实现高帧率AI视频渲染与工作流详解
人工智能·aigc·音视频
开开心心就好1 天前
Word双击预览图片插件弥补Word功能缺失
人工智能·python·智能手机·ocr·电脑·word·音视频
tedcloud1232 天前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
潜创微科技2 天前
8K HDMI 2.1 多源切换芯片方案,一颗芯片搞定长线不黑屏
音视频