ESC
输入关键词搜索文章
目录

数字人工程解读(九):HDTF 源码:流引导的单样本高分辨率说话人脸生成

第一章
项目定位与背景

HDTF(High-resolution Talking Face Dataset)是 CVPR 2021 论文 "Flow-Guided One-Shot Talking Face Generation With a High-Resolution Audio-Visual Dataset" 的官方代码与数据集仓库。#hdtf-repo

核心任务:给定一张人脸参考图片 + 一段驱动信号(3DMM 投影点序列),生成对应的说话视频。所谓"One-Shot"意味着只需一张参考图,不需要多视角或注册视频。

仓库包含两大模块: CPU 端的 F_app(近似稠密光流)构建代码, GPU 端的 animation-to-video 推理代码;同时发布了包含 ~368 个高分辨率视频的 HDTF 数据集及其元数据。#hdtf-repo

仓库基本信息

属性
Stars / Forks432 ⭐ / 80 🍴(截至 2026-06)
LicenseGNU GPLv3(代码)/ CC BY 4.0(数据集)
主要语言Python(PyTorch 1.0 + numpy/scipy)
创建时间2021-03-28
论文CVPR 2021, Zhang Zhimeng et al.
预训练权重Google Drive(需下载)
第二章
HDTF 数据集

HDTF 数据集包含从 YouTube 采集的 ~368 个高分辨率说话人头视频,分为三个子集:RD(57 个,Radio 类)、WDA(182 个)、WRA(129 个)。视频分辨率为 720P 或 1080P,最终统一 resize 到 512×512。#hdtf-repo

元数据文件结构

文件格式用途
xx_video_url.txt视频名 | YouTube URL视频来源
xx_resolution.txt视频名.mp4 | 分辨率参考下载分辨率
xx_annotion_time.txt视频名.mp4 | 00:30-01:00 | 01:30-02:30 | ...说话头时间段标注
xx_crop_wh.txt视频名_片段.mp4 | min_w | w | min_h | h面部裁剪窗口
xx_crop_ratio.txt视频名_片段.mp4 | 缩放比例裁剪窗口缩放系数

数据处理流程

README 描述了两种裁剪方式:#hdtf-repo

  1. 方式一(作者方法):按 xx_resolution.txt 下载视频 → 用 xx_crop_wh.txt 固定窗口裁剪面部。
  2. 方式二(@universome 改进):下载最高分辨率 → 检测面部 landmark → 统计所有帧的面部区域 → 用 xx_crop_ratio.txt 放大窗口 → 裁剪。

社区提供了自动化处理脚本(@universome/HDTF),方便复现数据集。#universome-hdtf

第三章
系统架构与主调用链

整个系统是一个两阶段 Pipeline:先在 CPU 端从 3DMM 投影点构建近似稠密光流 F_app,再在 GPU 端用 VideoGenerator 逐帧生成视频。

flowchart TD
    subgraph "阶段 1: F_app 构建 (CPU)"
        F1["np.load 投影点"] --> F2["稀疏位移 = ref - drive"]
        F2 --> F3["scipy griddata 最近邻插值"]
        F3 --> F4["躯干区域 = 平均运动"]
        F4 --> F5["+ 坐标网格 = Fapp.npy"]
    end
    subgraph "阶段 2: Animation2Video (GPU)"
        V0["VideoGenerator"] --> V1["Encoder
参考图特征提取"] V0 --> V2["ForegroundMatting
Hourglass U-Net"] V1 --> V3["warp + matting blend
特征级扭曲 + 混合"] V2 --> V3 V3 --> V4["Bottleneck
ResBlock 精炼"] V4 --> V5["Decoder → RGB"] V5 --> V6["VideoWriter + ffmpeg"] end F5 -.->|输入| V0

仓库目录结构

目录职责关键文件
code_constructing_Fapp/CPU 端构建近似稠密光流inference.py
code_animation2video/GPU 端动画转视频推理inference.py, models.py, opts.py
HDTF_dataset/数据集元数据(368 视频)RD_/WDA_/WRA_*.txt

主调用链时序图

sequenceDiagram
    participant User as 用户
    participant Fapp as F_app 构建
    participant Infer as inference.py
    participant VG as VideoGenerator
    participant Enc as Encoder
    participant FM as ForegroundMatting
    participant BN as Bottleneck
    participant Dec as Decoder
    participant VW as VideoWriter

    User->>Fapp: 提供 ref/drive 投影点
    Fapp-->>User: Fapp.npy (T×512×512×2)
    User->>Infer: image + Fapp + audio
    Infer->>VG: 加载 checkpoint
    loop 每帧
        Infer->>VG: forward(ref_tensor, Fapp_i)
        VG->>Enc: encoder(ref) → feature_map
        VG->>FM: foreground_matting(ref, Fapp)
        FM-->>VG: warped_image, fg_mask, matting_mask, matting_img
        VG->>VG: warp(feature, revised_flow) * matting + (1-matting) * matting_img
        VG->>BN: bottleneck(warped_feature)
        VG->>Dec: decoder(bottleneck_out)
        Dec-->>VG: synthetic_image
        VG-->>Infer: res_out
        Infer->>VW: write(synthetic_frame)
    end
    Infer->>VW: ffmpeg merge audio
    VW-->>User: video_add_audio.mp4
  
第四章
关键设计点深读

设计点 1:F_app 近似稠密光流

直接训练光流网络计算 dense flow 计算量大且需要额外标注。HDTF 的方案是用 3DMM 投影的稀疏网格点(约 50 个点)通过 scipy 最近邻插值生成 512×512 的稠密光流。#hdtf-repo

# code_constructing_Fapp/inference.py :: construct_Fapp
reference_projected_mesh_points = (reference_projected_mesh_points / image_size * 2) - 1
drive_projected_mesh_points = (drive_projected_mesh_points / image_size * 2) - 1
drive_projected_mesh_points_yx = drive_projected_mesh_points[:, [1, 0]]

# 稀疏光流 = 参考点 - 驱动点
sparse_dense_flow = reference_projected_mesh_points - drive_projected_mesh_points
mean_dense_flow = np.mean(sparse_dense_flow, axis=0)

# 面部区域:最近邻插值稀疏流
dense_foreground_flow_x = griddata(
    drive_projected_mesh_points_yx,
    sparse_dense_flow[:, 0],
    (grid_y, grid_x), method='nearest')
# 躯干区域:全局平均运动
dense_foreground_flow_x[face_max_h:, :] = mean_dense_flow[0]

Fapp = np.stack([dense_foreground_flow_x, dense_foreground_flow_y], 2)
Fapp = grid_mesh + Fapp  # 加坐标网格变成绝对采样坐标
设计洞察:面部区域用最近邻插值得到局部变形场,躯干(下巴以下)用全局平均运动——这种"分区策略"在计算效率和物理合理性之间取得了平衡。最终 Fapp 不是位移场,而是加了坐标网格后的绝对采样坐标,可直接用于 F.grid_sample

设计点 2:VideoGenerator 主模型架构

VideoGenerator 的核心思想是在特征空间扭曲,而非像素空间。先用 Encoder 提取参考图特征,再用 ForegroundMatting 修正光流并预测 matting mask,最后扭曲特征图 + matting 混合 + Bottleneck 精炼 + Decoder 重建。

# code_animation2video/models.py :: VideoGenerator.forward
def forward(self, reference_image, dense_flow):
    feature_map = self.encoder(reference_image)
    res_out = self.foreground_matting(reference_image, dense_flow)

    # 扭曲特征图 + matting 混合
    warped_feature_map = warp_image(feature_map, res_out['dense_flow_foreground'])
    warped_feature_map = (warped_feature_map * res_out['matting_mask']
                          + (1 - res_out['matting_mask']) * res_out['matting_image'])

    warped_feature_map = self.bottleneck(warped_feature_map)
    synthetic_image = self.decoder(warped_feature_map)
    res_out['synthetic_image'] = synthetic_image
    return res_out

matting_maskmatting_image 是 Hourglass 预测的修复信号——当光流扭曲不够准确时,matting 通路提供"修补",使得最终特征图是扭曲结果和修复结果的加权混合。

设计点 3:ForegroundMatting 前景修正

直接用 F_app 扭曲整张图像会导致背景也跟着移动,产生严重伪影。ForegroundMatting 模块通过 Hourglass U-Net 预测前景 mask,只扭曲前景区域,背景保持坐标网格(即不扭曲)。

# code_animation2video/models.py :: ForegroundMatting.forward
def forward(self, reference_image, dense_flow):
    if self.scale_factor != 1:
        reference_image = self.down_sample_image(reference_image)
        dense_flow = self.down_sample_flow(dense_flow.permute(0,3,1,2)).permute(0,2,3,1)

    warped_image = warp_image(reference_image, dense_flow)
    # Hourglass 输入:[原图, 光流, 扭曲图] 三通道拼接
    hourglass_input = torch.cat(
        [reference_image, dense_flow.permute(0,3,1,2), warped_image], dim=1)
    hourglass_out = self.hourglass(hourglass_input)

    foreground_mask = self.sigmoid(self.foreground_mask(hourglass_out))
    matting_mask = self.sigmoid(self.matting_mask(hourglass_out))
    matting_image = self.matting(hourglass_out)

    # 修正光流:前景用光流,背景用坐标网格(不扭曲)
    grid_flow = make_coordinate_grid((h, w), dense_flow.type())
    dense_flow_foreground = (dense_flow * foreground_mask
                             + (1 - foreground_mask) * grid_flow.unsqueeze(0))
    return res_out

Hourglass 输入拼接了三个信号:原图、光流、扭曲后的图,让网络同时"看到"输入、运动意图和扭曲结果,从而预测出合理的前景 mask。输出三件套(foreground_mask、matting_mask、matting_image)分别解决"哪里该动"、"扭曲结果和修复结果如何混合"、"修复成什么样"三个子问题。

设计点 4:AntiAliasInterpolation2d

ForegroundMatting 在 128×128 分辨率工作(相比输入 512×512),需要下采样。直接双线性下采样会产生锯齿伪影,因此先做高斯模糊滤除高频,再下采样:

# code_animation2video/models.py :: AntiAliasInterpolation2d
class AntiAliasInterpolation2d(nn.Module):
    def __init__(self, channels, scale):
        sigma = (1 / scale - 1) / 2
        kernel_size = 2 * round(sigma * 4) + 1
        # 构建高斯核,注册为 buffer
        kernel = gaussian_kernel(kernel_size, sigma)
        self.register_buffer('weight', kernel)

    def forward(self, input):
        if self.scale == 1.0:
            return input
        out = F.pad(input, (self.ka, self.kb, self.ka, self.kb))
        out = F.conv2d(out, weight=self.weight, groups=self.groups)
        out = F.interpolate(out, scale_factor=(self.scale, self.scale))
        return out

这是信号处理的标准做法(Nyquist-Shannon 采样定理),但在 GAN 图像生成中常被忽略。这里的实现用 depthwise convolution + 高斯核,开销极小。

设计点 5:Hourglass U-Net

Hourglass 是标准的编码器-解码器结构,带 skip connection:

# code_animation2video/models.py :: HourglassEncoder.forward
def forward(self, x):
    outs = [x]
    for down_block in self.down_blocks:
        outs.append(down_block(outs[-1]))
    return outs  # 返回所有层级特征用于 skip connection

# HourglassDecoder.forward
def forward(self, x):
    out = x.pop()
    for up_block in self.up_blocks:
        out = up_block(out)
        skip = x.pop()
        out = torch.cat([out, skip], dim=1)
    return out

默认 5 层 Hourglass(houglass_num_blocks=5),block_expansion=64,max_features=512。下采样路径:输入通道 → 64 → 128 → 256 → 512,上采样路径反向并拼接 skip connection。

设计点 6:推理循环与多路调试输出

推理脚本同时输出 6 路视频,便于分析模型各模块的效果:

# code_animation2video/inference.py :: main loop
for i in range(frame_length):
    Fapp_i = Fapp[i,:,:]
    with torch.no_grad():
        Fapp_i = torch.from_numpy(Fapp_i).float().cuda().unsqueeze(0)
        res_out = video_generator(reference_tensor, Fapp_i)
        # 写入 6 路视频:
        # synthetic_video, Fapp 可视化, matting_mask,
        # revised_dense, foreground_mask, warped_image
        synthetic_video_i = res_out['synthetic_image'] * 255
        videowriter_synthetic_video.write(synthetic_video_i)
        # ... 其他 5 路

# 可选:ffmpeg 合并音频
if os.path.exists(opt.audio_path):
    cmd = 'ffmpeg -i {} -i {} -c:v copy -c:a aac ...'
    subprocess.call(cmd, shell=True)

这种"全中间结果可视化"策略对调试生成模型非常有价值——可以直接看到光流在哪扭曲了、mask 在哪预测错了、matting 在哪修复了。

第五章
如何使用

前置准备

Google Drive 下载以下文件:#hdtf-repo

  • checkpoint_animation2video.pth — 预训练模型,放入 ./checkpoints/
  • taile_Fapp.npy / mengnalisa_Fapp.npy — 预计算 F_app,放入 ./test_data/
  • 投影点 .npy 文件(如需自行构建 F_app)

步骤 1:构建 F_app

cd code_constructing_Fapp
python inference.py \
  --reference_projected_mesh_points_path=./test_data/taile_source_points.npy \
  --drive_projected_mesh_points_path=./test_data/taile_drive_points.npy \
  --image_size=512 \
  --res_dir=./result
参数作用输入/输出含义如何替换
--reference_projected_mesh_points_path参考人脸 3DMM 投影点输入:.npy (N×2)替换为自己的参考帧投影点
--drive_projected_mesh_points_path驱动帧投影点序列输入:.npy (T×N×2)替换为驱动序列投影点
--image_size图像尺寸默认 512需与模型训练尺寸一致
--res_dir输出目录输出:taile_Fapp.npy指定结果保存路径

步骤 2:动画转视频

cd code_animation2video
python inference.py \
  --model_path=./checkpoints/checkpoint_animation2video.pth \
  --image_path=./test_data/taile.jpg \
  --dense_flow_path=./test_data/taile_Fapp.npy \
  --audio_path=./test_data/chuanpu.wav \
  --res_path=./result
参数作用输入/输出含义如何替换
--model_path预训练模型.pth 文件下载后放入 checkpoints/
--image_path参考人脸图片512×512 RGB 图片替换为任意正面人脸
--dense_flow_pathF_app 稠密光流.npy (T×512×512×2)步骤 1 的输出或预计算文件
--audio_path输入音频.wav 文件替换为目标音频
--res_path输出目录视频输出路径指定结果保存路径

网络配置参数

参数默认值说明
--encoder_num_down_blocks2Encoder 下采样层数
--encoder_block_expansion64Encoder 基础通道数
--encoder_max_features512Encoder 最大通道数
--houglass_num_blocks5Hourglass U-Net 层数
--houglass_block_expansion64Hourglass 基础通道数
--houglass_max_features512Hourglass 最大通道数
--num_bottleneck_blocks2Bottleneck ResBlock 数量

训练与评估

仓库未提供训练代码和评估脚本。README 中 code of audio-to-animationcode of reproducing other works 标注为 "coming soon",至今未开源。#hdtf-repo 推理仅依赖预训练权重,无法自行训练。

第六章
工程评价与可迁移经验

优点

  • F_app 方法轻量有效:纯 CPU numpy/scipy 计算,无需训练光流网络,推理速度快
  • 特征级扭曲优于像素级:在 Encoder 特征空间扭曲保留更多细节
  • 前景/背景分离策略:预测前景 mask,只扭曲前景区域,避免背景伪影
  • 多路调试输出:6 路中间结果视频,便于分析模型各模块效果
  • 代码结构清晰:模块化好,每个 nn.Module 职责单一

已知局限

复现风险

  • 依赖版本极老:torch 1.0(2018 年发布),与现代 Python 3.8+ 不兼容,需降级到 Python 3.6 或使用 Docker
  • audio-to-animation 模块未开源:README 标注 "coming soon" 多年未更新,无法从音频端到端推理
  • 无训练代码:只有推理代码,无法在自有数据上训练
  • 3DMM 模型不可分享:源码注释明确说明 "It is not allowed to share the 3DMM model",需自行准备
  • GPU 需求:推理需要 CUDA GPU,模型加载到 .cuda()

可迁移经验

  1. 稀疏→稠密光流插值:用少量关键点 + 最近邻插值生成稠密光流,比训练光流网络轻量得多,适用于有 3DMM 或 landmark 的场景
  2. 特征级扭曲 + matting 修复:在 encoder 特征空间扭曲比像素级扭曲保留更多细节;matting mask 混合扭曲结果和修复结果,平滑过渡
  3. 前景/背景分离:预测前景 mask 只扭曲前景区域,避免背景伪影——这对任何基于光流的图像生成方法都适用
  4. 抗锯齿下采样:高斯模糊 + 下采样避免锯齿,开销极小(depthwise conv + 高斯核)
  5. 全中间结果可视化:输出所有中间产物(光流、mask、扭曲图)便于调试,是生成模型开发的实用策略
总结
读完这个仓库能带走什么

复习速查

  • 项目定位:CVPR 2021 流引导单样本说话人脸生成,含高分辨率数据集
  • 核心路径code_constructing_Fapp/inference.pycode_animation2video/inference.pyVideoGenerator
  • 关键设计:F_app 稀疏→稠密插值、特征级扭曲 + matting 混合、前景 mask 修正、抗锯齿下采样
  • 迁移价值:轻量光流替代方案、特征级扭曲策略、前景/背景分离模式