AnchorCrafter算法简析

项目地址:https://github.com/cangcz/AnchorCrafter
论文地址:https://arxiv.org/pdf/2411.17383.pdf
官方介绍:https://cangcz.github.io/Anchor-Crafter/
部署测试:https://www.bilibili.com/video/BV1gup3zxESj

优云智算⚡️,一键部署:AnchorCrafter

🔗https://www.compshare.cn/images/w2ArgcG3iSN5?referral_code=CpxneZgXby6EOmUwLGr7hQ

【开源】AnchorCrafter 带货数字人视频生成,通过人-物交互视频生成技术,动画化展示您的产品

在这里插入图片描述

🎯 一句话总结

AnchorCrafter 是一个可以根据一张人物图和多视角产品图,生成人物与产品自然互动视频的AI系统。

🔍 核心问题

传统的人物视频生成方法(如 MimicMotion、StableAnimator)虽然能生成高质量人物动作视频,但无法处理“人物与物体的交互”,比如手拿杯子、打开盒子等动作,常见的问题包括:

把物体当成衣服的一部分(粘在身上)
手穿模或抓不住物体
物体不动或变形
✅ AnchorCrafter 的解决方案
系统分为两个核心模块:

1️⃣ HOI-Appearance Perception(人-物外观感知)
目的:让人和物体的外观清晰分离,避免“粘在一起”或“互相污染”

关键技术:
多视角物体特征融合:用三张产品图(正面、左侧、右侧)提取3D一致的物体特征
人-物双分支注意力机制(Human-Object Dual Adapter):
人和物体分别用不同的注意力分支处理
避免人物特征影响物体,反之亦然
2️⃣ HOI-Motion Injection(人-物动作注入)
目的:让人物的动作和物体的运动协调一致,比如手抓物体时,物体要跟着手走

关键技术:
输入控制信号:
人体骨架(OpenPose)
手部3D网格(HaMeR)
物体深度图(ViTA)
遮挡处理:当手被物体挡住时,系统仍能推理出手的正确位置
物体轨迹控制:用深度图引导物体在三维空间中的移动路径
3️⃣ HOI-Region Reweighting Loss(交互区域加权损失)
目的:让模型更关注“手和物体交互”的区域,提升细节真实感

在训练时,手和物体的区域损失权重更高
避免模型只关注人脸或身体,忽略手和物体的细节

🧠 训练与推理流程

在这里插入图片描述

🧪 实验效果

物体保留度提升 7.5%
物体定位精度提升 2 倍
手部动作更自然
用户主观评分最高(外观、动作、真实感)

🧩 举个例子

你可以这样用它:

输入一张主播图 + 三张 iPhone 图 + 一段“打开盒子”的动作视频
👉 输出:主播自然地拿起 iPhone,打开盒子,展示手机

⚠️ 局限性

对透明物体(如玻璃杯)效果差
对非刚性物体(如毛巾、衣服)支持不好
依赖预处理模型(如姿态估计),如果输入错了,输出也会错

✅ 总结一句话

AnchorCrafter 是第一个真正把“人-物交互”做进视频生成的系统,能让虚拟主播自然地“拿产品、展示、操作”,非常适合电商、广告、虚拟直播等场景。

训练简析

AnchorCrafter 训练脚本的主文件,用于训练一个扩散模型(diffusion model)来生成人物与物体交互的视频。它整合了多个模块(U-Net、姿态网络、物体跟踪、注意力机制等),并通过大量参数配置支持分布式训练、混合精度、EMA、断点续训等功能。


🧠 一、核心目标

训练一个模型,使其能够:

  • 输入:一张人物图 + 多视角产品图 + 动作控制信号(姿态、手部、深度)
  • 输出:人物与产品自然互动的视频(如拿杯子、打开盒子)

🧱 二、核心模块结构

模块名 作用
Net 主模型,整合所有子网络
unet 扩散模型的主干,用于去噪生成图像
pose_net 处理人体姿态特征
track_net 处理物体跟踪特征(如深度图)
obj_proj_net 投影物体类别特征
obj_attn_net 对物体特征做注意力处理
IPAttnProcessor 自定义注意力处理器,支持图像提示(image prompt)

🧪 三、训练流程概览

1. 数据加载

train_dataset = AnchorDataset(...)
  • 加载HOI(人-物交互)视频帧
  • 包括:人物图、产品图、姿态图、手部图、物体掩码图等

2. 图像编码

encoder_hidden_states, obj_embeddings = _encode_image(...)
  • 人物图 → CLIP → 嵌入向量
  • 产品图 → DINOv2 → 嵌入向量(多视角拼接)

3. 加噪与 timestep 采样

sigmas = rand_log_normal(...)
noisy_latents = latents + noise * sigmas
  • 使用 log-normal 分布 采样噪声强度(模仿Stable Video Diffusion)
  • 构造扩散训练输入

4. 模型前向传播

model_pred = net(...)
  • 输入:噪声潜变量 + 条件图 + 姿态 + 手部 + 物体嵌入
  • 输出:预测的噪声(用于去噪)

5. 损失计算(重点)

loss = torch.mean(... * loss_weight)
  • MSE损失:预测噪声 vs 真实噪声
  • 加权损失:对“手+物体”区域加权(loss_rate_hoi控制权重)
  • 目的:让模型更关注交互区域,提升细节质量

6. 反向传播与优化

accelerator.backward(loss)
optimizer.step()
  • 使用 AdamW 优化器
  • 支持混合精度(fp16/bf16)
  • 支持梯度累积、梯度裁剪、EMA等

⚙️ 四、关键参数说明(args)

参数名 含义
--base_folder HOI视频数据集路径
--noobj_folder 无物体视频数据集路径
--pretrained_model_name_or_path 预训练模型路径(如Stable Video Diffusion)
--dino_path DINOv2模型路径(用于物体特征提取)
--num_frames 每段视频采样帧数(默认16)
--resolution 图像分辨率(默认576)
--loss_rate_hoi 交互区域损失权重(默认1.0)
--finetune 是否进入微调模式(只训练物体细节)

🧩 五、特色设计详解

✅ 1. 多视角物体特征融合

obj_all_embeddings = []
for obj in obj_pixels:
    obj_embeddings = obj_image_encoder(obj).last_hidden_state
    obj_all_embeddings.append(obj_embeddings)
obj_all_embeddings = torch.cat(obj_all_embeddings, dim=1)
  • 每个物体3张图(左、正、右)→ DINOv2 → 拼接 → 19个token
  • 提供3D一致的物体表示

✅ 2. 人-物双分支注意力(IPAttnProcessor)

attn_procs[name] = IPAttnProcessor(
    hidden_size=hidden_size,
    cross_attention_dim=cross_attention_dim,
    scale=1.0,
    num_tokens=19
)
  • 在U-Net的cross-attention层中插入
  • 支持将物体特征作为“图像提示”注入生成过程

✅ 3. 交互区域加权损失(HOI Region Reweighting)

loss_weight_frame[no_zero == 1] = resize_weight
  • 根据物体掩码+手部掩码计算交互区域
  • 对这些区域赋予更高损失权重 → 提升细节还原

✅ 4. 支持微调模式(finetune)

if not args.finetune:
    no_zero[hand_pixels_sum[0][idx] != -3] = 1
  • 微调时不强制加入手部区域
  • 更专注于物体纹理而非交互动作

🧪 六、调试与监控

功能 文件/路径
训练日志 logs/
模型保存 outputs/checkpoint-*/
参数冻结记录 params_freeze.txt
参数训练记录 params_train.txt

✅ 七、总结一句话

这段代码是 AnchorCrafter 的完整训练系统,它通过:

  • 多视角物体特征
  • 人-物注意力分离
  • 交互区域加权损失
  • 扩散模型去噪训练

实现了高质量的人-物交互视频生成,支持微调、分布式、混合精度等工业级训练需求。


如果你想,我可以给你画一个训练流程图,或者单独讲某一部分(如损失设计、注意力机制、数据加载)。

训练通俗话简析

好的!下面我把这段代码拆成 7 个生活化的“小剧场”,用买菜、做菜、拍照、画画这些日常例子来解释。你完全不需要 AI 背景,就能明白每一大段到底在干什么。


🧃 0. 先把整件事想成“做一段炒菜 Vlog”

真·AI 术语 生活化翻译
训练模型 教一个新手剪辑师(模型)怎样把你拍的生肉素材剪成漂亮 Vlog
数据集 你之前拍过的几百条做菜视频
损失函数 观众弹幕:“手遮住了锅!”“菜颜色不对!”——这些差评就是损失
优化器 剪辑师根据差评改剪法
预训练模型 剪辑师已经会剪旅游 Vlog,你只要微调他让他学会剪做菜即可

带着这个比喻,我们进入正式板块。


🧺 板块 1. 数据加载(AnchorDataset)

原始代码位置

train_dataset = AnchorDataset(base_folder, noobj_folder, ...)

生活剧

  1. 你把硬盘里的“做菜视频”分成两堆
    • 有锅/铲出现的 → base_folder(HOI 视频)
    • 只有人没锅的 → noobj_folder(纯人视频)
  2. 每段视频剪成 16 张关键帧(num_frames=16),相当于从“洗菜→切菜→下锅”里抽 16 张代表性照片。
  3. 同时额外导出 5 种“小抄”:
    • 人物证件照(image_pixels
    • 锅的证件照 3 张(左、正、右,obj_pixels
    • 人物骨架简笔画(pose_pixels
    • 手部特写(hand_pixels
    • 锅的位置遮罩图(box_video_pixels

一句话
Dataset 就是“视频+小抄”打包机,让模型后面看图说话。


📷 板块 2. 图像编码器(_encode_image

原始代码位置

encoder_hidden_states, obj_embeddings = _encode_image(...)

生活剧

  1. 人物证件照 → 交给“人像描述员”(CLIP 模型)→ 得到 1 句 768 字的文字简历。
  2. 锅的 3 张证件照 → 交给“物体描述员”(DINOv2 模型)→ 得到 19 句 1024 字的简历。
  3. 这些简历后续会被 U-Net 当成“提示词”使用,就像你告诉剪辑师“这段要剪成温馨风格”一样。

🌪 板块 3. 加噪与 timestep(rand_log_normal 等)

原始代码位置

sigmas = rand_log_normal(...)
noisy_latents = latents + noise * sigmas

生活剧

  1. 把原始 16 帧清晰图 → 压成“马赛克”潜变量(VAE 编码)。
  2. 随机决定今天要“糊”到什么程度:
    • 有时轻雾(σ 小)
    • 有时重雾霾(σ 大)
  3. 模型任务:从雾霾图猜回原图。
    为什么这么做?
    就像给剪辑师出练习题:先故意把视频打码,让他练习“去码”,练多了以后遇到真实模糊他也能还原。

🧠 板块 4. 主模型 Net(U-Net + 小菜网络)

原始代码位置

model_pred = net(inp_noisy_latents, encoder_hidden_states, ...)

生活剧
U-Net 是“主剪辑师”,但他需要 4 位助理不断递小抄:

助理 递的小抄 作用
pose_net 人物骨架简笔画 提醒“手在哪个位置”
track_net 锅的深度/轨迹图 提醒“锅要往哪儿移动”
obj_proj_net 锅的 3 句简历 提醒“锅长啥样”
obj_attn_net 锅的 19 句详细简历 提醒“锅的细节纹理”

主剪辑师把这些小抄与雾霾图一起放进时间轴,输出“去码后的清晰图”。


🎯 板块 5. 损失函数(MSE + 手锅加权)

原始代码位置

loss = torch.mean(... * loss_weight)

生活剧

  1. 观众弹幕差评分为两种:
    • 普通区域差一点点颜色 → 扣 1 分
    • 手或锅区域严重穿帮 → 扣 10 分(因为 loss_weight 在这里加了倍)
  2. 剪辑师根据“扣分地图”重点练手锅区域,下次手不会穿模,锅也不会变形。

🔧 板块 6. 优化器 & 学习率调度

原始代码位置

optimizer = torch.optim.AdamW(...)
lr_scheduler = get_scheduler(...)

生活剧

  • 剪辑师每次剪完一条视频,观众给他打分(损失)。
  • 他根据扣分多少决定下次剪法微调幅度(学习率)。
  • 初期步子大(warm-up),后期步子小(cosine/constant),防止“矫枉过正”。

💾 板块 7. 断点保存 & 恢复

原始代码位置

accelerator.save_state(save_path)

生活剧

  • 每剪 500 条视频自动存档(checkpoint)。
  • 电脑突然死机 → 重新打开脚本,选择“从最新存档点继续”,之前练的不会白费。

🧪 板块 8. 微调模式(finetune 开关)

原始代码位置

if not args.finetune: ...

生活剧

  • 预训练阶段:剪辑师既要学“手怎么握锅”,也要学“锅长啥样”。
  • 微调阶段:你已经有一个很会握锅的剪辑师,只想让他记住“新锅的花纹”,于是把手部差评关掉,只专注锅的外观。

🎬 总结成一句人话

整段代码就是——
“手把手教一个会剪片的新手,先随便给视频打码,再让他靠‘骨架小抄+锅的简历’反复练习去码,观众专门挑手锅区域给差评,他越剪越精,最后能把你给的任何证件照变成一段流畅的做菜 Vlog。”


进一步简化讲解

🔍 1. VAE(变分自编码器)——“高清图 ↔ 小方块”的魔法复印机

📌 你只需要知道:
  • VAE 就像一个魔法复印机,能把高清图压成小方块(潜变量),也能把小方块还原回高清图。
  • 为什么要压小?因为小方块处理得快,训练时省时间、省显存。
🧃 生活例子:

想象你有一本厚厚的菜谱:

  • 压缩:你把每页菜谱拍成一张缩略图(小方块),方便快速浏览。
  • 还原:当你看到缩略图,魔法复印机又能还原成清晰原图,让你看清每道菜细节。
🎬 在代码里:
latents = tensor_to_vae_latent(video_pixels, vae)
  • 把原始视频帧(清晰图)→ 压成小方块(潜变量)
  • 后续所有“加噪”、“去噪”操作都在小方块上进行,速度飞快!

🔍 2. DINOv2(物体描述员)——“只用三张照片,生成物体简历”

📌 你只需要知道:
  • DINOv2 是一个专门描述物体的 AI 模型。
  • 你给 DINOv2 三张产品图(左、正、右),它会输出19 句“简历”,每句 1024 个字,描述物体的形状、纹理、颜色。
🧃 生活例子:

你去面试,HR 让你交三张照片(正面、左侧、右侧),然后 HR 给你写一份 19 句的简历

  • 第 1 句:“此人圆脸,戴眼镜。”
  • 第 2 句:“头发黑色,微卷。”
  • 第 19 句:“整体看起来科技感强。”
🎬 在代码里:
obj_embeddings = obj_image_encoder(obj).last_hidden_state
  • 三张产品图 → DINOv2 → 19 句简历(19×1024 维向量)
  • 这些简历后续会喂给 U-Net,提醒它“锅长啥样”。

🔍 3. 手锅遮罩(hand_pixels & box_video_pixels)——“告诉模型哪里是手、哪里是锅”

📌 你只需要知道:
  • 遮罩就是一张黑白图,白色区域表示“这里很重要”,黑色区域表示“这里可以忽略”。
  • 代码里有两类遮罩:
    • hand_pixels:白色区域是“手”
    • box_video_pixels:白色区域是“锅”
🧃 生活例子:

你拍照后,用美图软件手动涂白两个区域:

  • 涂白:告诉修图师“手要精修”
  • 涂白:告诉修图师“锅也要精修”
    其余背景保持黑色:修图师可以随便压画质,观众看不出来。
🎬 在代码里:
no_zero[hand_pixels_sum[0][idx] != -3] = 1
  • 如果某像素不是“空白”(-3 表示空白),就认为它是手,涂白。
  • 最终生成一张白色=手或锅的遮罩图,用于后续损失加权

🔍 4. 损失加权(loss_weight)——“观众弹幕扣分地图”

📌 你只需要知道:
  • 原本的损失函数是“整张图平均扣分”。
  • 现在用遮罩图,把手 & 锅区域的扣分乘以倍数,让模型更关注这些区域。
🧃 生活例子:

老师改作文:

  • 原来:整篇作文错 10 个字,扣 10 分。
  • 现在:老师用红笔圈出标题 & 结尾(关键区域),这里错 1 个字扣 5 分,其余段落错 1 个字扣 1 分。
  • 结果:你下次写作文,会特别小心标题和结尾,因为扣分重。
🎬 在代码里:
loss_weight_frame[no_zero == 1] = resize_weight
  • 白色区域(手/锅)乘以高权重(例如 5 倍)
  • 黑色区域(背景)乘以 1 倍
  • 最终模型会重点优化手锅区域,减少穿模、变形。

🔍 5. 潜变量(latents)——“在缩略图上画画,再放大成高清图”

📌 你只需要知道:
  • 所有“加噪”、“去噪”操作,都在**小方块(潜变量)**上进行,而不是直接改高清图。
  • 好处:速度快、显存省、训练稳定。
🧃 生活例子:

你要画一幅巨幅海报

  • 直接画大海报?—— 累得慌,容易画错。
  • 先画一张 A4 草图(小方块),画错也不怕,随便改。
  • 画完后,用投影仪放大成海报(VAE 解码),又快又清晰。
🎬 在代码里:
inp_noisy_latents = noisy_latents / ((sigmas ** 2 + 1) ** 0.5)
model_pred = net(inp_noisy_latents, ...)
denoised_latents = model_pred * c_out + c_skip * noisy_latents
  • 所有“去噪”操作都在小方块上进行。
  • 最后用小方块→VAE→还原成高清视频帧。

🎯 总结成一张图(文字版)

步骤 生活比喻 代码中的体现
1. 高清图 → 小方块 把大海报拍成 A4 缩略图 tensor_to_vae_latent
2. 产品图 → 19 句简历 三张证件照 → HR 写简历 obj_image_encoder(obj)
3. 手/锅涂白 美图软件局部精修 hand_pixels, box_video_pixels
4. 扣分加重 老师圈重点,错一字扣 5 分 loss_weight[white_area] *= 5
5. 小方块 → 高清图 A4 草图 → 投影仪放大 vae.decode(latents)

🔍6. U-Net

把 U-Net 想成一位**“修图师”,专门干“去雾霾”这一件事。
它手里有
两把刷子、一张草稿纸、一本小抄册**,工作流程特别固定,但效果奇好——现在几乎所有 AI 画图/视频都离不开它。

🧃 1. 先上一张“身份证照片”
    输入(雾霾图)     输出(清晰图)
        │                  │
        ▼                  ▲
   [ 收 缩 路 ]  →  →  →  [ 扩 张 路 ]
    越来越小             越来越大
    特征变抽象           特征变具体
  • 收缩路 = 把图“挤”成几颗小豆豆,抓住“大轮廓”
  • 扩张路 = 把小豆豆“吹”回全尺寸,恢复“细节”
  • 中间还有“跳连接”= 把早期草稿直接复印回来,防止画歪。

🧃 2. 生活版小故事——“帮奶奶修复老照片”
  1. 你先把老照片复印一份(跳连接)放旁边。
  2. 把原图缩印 4 次,越缩越小——
    • 第一次还能看见“人脸”。
    • 最后一次只剩“黑白大色块”。
  3. 在最小色块上,你拿粗笔把“划痕”涂掉——这一步叫“去噪/去雾霾”。
  4. 再把图放大回来,每放大一次,都拿早期草稿对一下位置
    • “鼻子原来在这儿,别画歪。”
    • “皱纹保留,别当划痕全抹了。”
  5. 最后得到一张干净、不歪、细节全的新照片。

→ 这套“缩-涂-放-对草稿”的流程,就是 U-Net 的核心。


🧃 3. 技术黑话对照表(一看就懂)
黑话 生活翻译
编码器(Encoder) 收缩路:把图越压越小,抓大轮廓
解码器(Decoder) 扩张路:把小图吹回来,补细节
跳连接(Skip Connection) 早期草稿复印机:防止“画歪”
特征图(Feature Map) 草稿纸:每一层都画一张“内部草图”
卷积层(Conv) 画笔:在草稿纸上涂涂抹抹
扩散模型里的 U-Net 专门负责“去雾霾”的修图师

🧃 4. 在 AnchorCrafter 里它具体干什么?
model_pred = self.unet(
    inp_noisy_latents,   # 雾霾小方块(不是高清图)
    timesteps,           # 今天雾霾有多重
    encoder_hidden_states, # 小抄:人物简历+锅的简历
    pose_latents,        # 小抄:骨架简笔画
    obj_track_latents,   # 小抄:锅的深度轨迹
    added_time_ids,      # 小抄:帧率、运动强度
).sample

一句话:
U-Net 拿着“雾霾小方块”和一堆“小抄”,一层层缩、一层层放,最后给你一张“去雾霾后的清晰小方块”。


🧃 5. 为什么叫“U-Net”?

把模型图竖起来看,就是一个大写字母 U——先下去(收缩)再上来(扩张),所以干脆叫 U-Net。


🧃 6. 一句话总结

U-Net 就是**“先缩后放、草稿对位”的修图师,
在 AnchorCrafter 里专门负责
把雾霾小方块还原成清晰小方块**,
让最终视频手不歪、锅不变形、动作流畅

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐