Paper: Qi C, Cun X, Zhang Y, et al. Fatezero: Fusing attentions for zero-shot text-based video editing[C]//Proceedings of the IEEE/CVF International Conference on Computer Vision. 2023: 15932-15942.
Introduction: https://fate-zero-edit.github.io/
Code: https://github.com/ChenyangQiQi/FateZero
FateZero 是一种 zero-shot 视频编辑方法,通过单独编辑每一帧图像实现视频编辑。为了保证时间维度的一致性,在 DDIM Editing 的过程中注入原始图像的 self-attention maps,从而维护了视频中物体的结构和运动信息的连续性。
FateZero 所做的就是视频编辑任务,主要针对视频风格编辑 (video stylizing editing) 和视频局部编辑 (video local editing)。不仅如此,FateZero 方法还可以和 Tune-a-video 结合,用于视频中物体形状编辑 (shape-aware video editing)。
目录
一. 研究思路
在 AIGC 任务中,diffusion 模型可以根据文本 prompt 生成高质量的图像和视频。对于图像编辑任务,也可以通过编辑 DDIM 反演到隐空间中的隐变量实现对图像的编辑。但将这种方法用于视频编辑会带来许多挑战:
- text-to-image 模型缺乏时间维度的感知信息,无法建模运动和三维形状;
- 现有的视频编辑方法需要额外的学习和优化,且无法胜任形状编辑任务;
- 视频编辑中使用 DDIM 反演的过程中产生的噪声可能会导致累计误差,从而破坏原始视频的运动和结构;
因此,本文提出了 FateZero,可以在 zero-shot 的情况下实现视频的编辑。基于 text-to-image diffusion 模型的视频编辑方法需要保证编辑后的视频在时间上的连续性,为此 FateZero 提出了两点创新:
- 存储 DDIM inversion 过程中每一步的 self-attention maps 和 cross-attention maps,然后在后续 DDIM 的去噪过程中进行融合,这样能够在维持原始视频中物体运动和结构的同时实现视频的编辑;
- 将 self-attention 块替换为时空 attention 块 (spatial-temporal attention block),以保证视频更加连续;
self-attention maps 能够存储图像的结构信息,优化后的 spatial-temporal attention 能够存储图像在时间维度的运动信息,cross-attention maps 能够存储语义信息并生成 mask。
二. FateZero 视频编辑
FateZero 视频编辑本质上是对每一帧图像进行编辑,编辑过程中需要尽可能保持时间维度上的连续性。FateZero 编辑视频可以分为两个过程:先 DDIM Inversion 将原始图像映射到隐空间中的噪声,然后在噪声隐变量的去噪过程中进行编辑。
DDIM Inversion:将原始图像 x x x 映射到隐空间并加噪得到原始图像在隐空间中的噪声表示(Hugging Face 提供了完整的 DDIM Inversion 教程和示例代码 1,可以直观地感受 DDIM Inversion 过程);
DDIM Reconstruction:使用隐空间中的原始图像噪声进行逆扩散以生成图像;
DDIM Editing:使用隐空间中的原始图像噪声在逆扩散的过程中进行编辑以生成编辑图像;
FateZero 的输入是视频编辑前后的文本描述
p
src
p_{\text{src}}
psrc 和
p
edit
p_{\text{edit}}
pedit,以及视频
x
=
{
x
1
,
x
2
,
…
x
n
}
x=\left\{x^1, x^2, \ldots x^n\right\}
x={x1,x2,…xn} 的隐变量
z
=
{
z
1
,
z
2
,
…
z
n
}
z=\left\{z^1, z^2, \ldots z^n\right\}
z={z1,z2,…zn},其中
n
n
n 是视频的帧数。FateZero 的 pipeline 如下:
- 使用 DDIM inversion pipeline 将视频 x x x 映射到隐空间得到 z z z,然后正向扩散得到噪声隐变量 z T z_T zT。扩散过程中存储每一个时间步长 t t t 时的 self-attention maps s t s r c s^{src}_t stsrc 和 cross-attention maps c t s r c c^{src}_t ctsrc,用于后续的 attention map 注入;
- 在编辑阶段,根据 p edit p_{\text{edit}} pedit 对 z T z_T zT 去噪得到 z ^ 0 \hat{z}_0 z^0。在去噪的过程中,使用 Attention 混合块将 ( s t s r c , c t s r c ) (s^{src}_t, c^{src}_t) (stsrc,ctsrc) 和 ( s t e d i t , c t e d i t ) (s^{edit}_t, c^{edit}_t) (stedit,ctedit) 混合;
1. Inversion Attention Fusion
直接在逆扩散过程中进行编辑会导致相邻帧之间的不一致,这可以归因于以下两个因素:
(1)DDIM 的可逆性质仅在小步骤极限下成立,而 DDIM 需要 50 步去噪,会逐渐累计误差;
(2)大规模无分类引导 scfg>>1 可以提高去噪的编辑能力,但较大的自由度也会导致相邻帧不一致;
虽然这些问题在单帧编辑中微不足道,但在处理视频时会被放大,因为即使是帧之间的微小差异也会随着时间索引而增加。
为了解决上述问题,FateZero 在 DDIM Inversion 的过程中,在
z
0
z_0
z0 正向扩散到
z
T
z_T
zT 的同时保存该过程中的 attention maps:
z
T
,
[
c
t
s
r
c
]
t
=
1
T
,
[
s
t
s
r
c
]
t
=
1
T
=
DDIM
−
INV
(
z
0
,
p
s
r
c
)
z_T,\left[c_t^{\mathrm{src}}\right]_{t=1}^T,\left[s_t^{\mathrm{src}}\right]_{t=1}^T=\operatorname{DDIM}-\operatorname{INV}\left(z_0, p_{s r c}\right)
zT,[ctsrc]t=1T,[stsrc]t=1T=DDIM−INV(z0,psrc)
这些 attention maps 对应 p src p_{\text{src}} psrc 的扩散过程,保留了原始视频中元素的几何和运动信息。
编辑时,将
p
src
p_{\text{src}}
psrc 和
p
edit
p_{\text{edit}}
pedit 对比得到需要编辑的区域,然后将原始图像中不需要编辑区域的 cross-attention maps 注入 DDIM Inversion 的过程:
ϵ
^
t
=
ATT
−
F
U
S
I
O
N
(
ε
θ
,
z
t
,
t
,
p
edit
,
c
t
s
r
c
,
s
t
s
r
c
)
\hat{\epsilon}_t=\operatorname{ATT}-\mathrm{FUSION}\left(\varepsilon_\theta, z_t, t, p_{\text {edit }}, c_t^{\mathrm{src}}, s_t^{\mathrm{src}}\right)
ϵ^t=ATT−FUSION(εθ,zt,t,pedit ,ctsrc,stsrc)
为了维持原始视频中的结构和动作,需要在去噪过程中替换掉一些 self-attention maps,详见下一节。
2. Attention Map Blending
在 DDIM Editing 的过程中,如果直接将
s
e
d
i
t
s^{edit}
sedit 替换成
s
s
r
c
s^{src}
ssrc,生成结果会出现伪影;如果直接保留
s
e
d
i
t
s^{edit}
sedit,会改变原始视频中物体的结构及姿势。所以需要对
s
e
d
i
t
s^{edit}
sedit 和
s
s
r
c
s^{src}
ssrc 进行融合:
考虑到 cross-attention maps 存储了图像的语义信息,可以先使用一个阈值
τ
\tau
τ 查询
c
s
r
c
c^{src}
csrc 得到需要编辑区域的 mask,然后根据 mask 混合原始图像和编辑后图像的 self-attention maps:
M
t
=
HEAVISIDESTEP
(
c
t
s
r
c
,
τ
)
s
t
fused
=
M
t
⊙
s
t
edit
+
(
1
−
M
t
)
⊙
s
t
s
r
c
M_t=\operatorname{HEAVISIDESTEP}\left(c_t^{s r c}, \tau\right) \\ s_t^{\text {fused }}=M_t \odot s_t^{\text {edit }}+\left(1-M_t\right) \odot s_t^{\mathrm{src}} \\
Mt=HEAVISIDESTEP(ctsrc,τ)stfused =Mt⊙stedit +(1−Mt)⊙stsrc
3. Spatial-Temporal Self-Attention
Inversion Attention Fusion 和 Attention Map Blending 并不能完全保证编辑视频每一帧之间的连续性,因此 FateZero 引入了 self-attention 并将其调整为时空 self-attention (spatial-temporal self-attention):即在查询 K 和 V 时将
z
i
z^i
zi 替换为
[
z
i
,
z
w
]
[z^i,z^w]
[zi,zw]:
Q
=
W
Q
z
i
,
K
=
W
K
[
z
i
;
z
w
]
,
V
=
W
V
[
z
i
;
z
w
]
Q=W^Q \mathbf{z}^i, K=W^K\left[\mathbf{z}^i ; \mathbf{z}^{\mathrm{w}}\right], V=W^V\left[\mathbf{z}^i ; \mathbf{z}^{\mathrm{w}}\right]
Q=WQzi,K=WK[zi;zw],V=WV[zi;zw]
其中 z w z^w zw 是确定的, z w = z R o u n d [ n / 2 ] z^w=z^{Round[n/2]} zw=zRound[n/2],表示视频的时间维度中点的隐变量。
FateZero 伪代码如下:
FateZero 里的 cross-attention map 和 prompt-to-prompt 里功能相近,不过为了时间序列上的一致性,引入了 self-attention map:在 ATT-FUSION 的内部,使用当前 z t z_t zt 作 Q 和 K 计算 s t e d i t s_t^{edit} stedit,然后和 s t s r c s_t^{src} stsrc 融合后再使用 z t z_t zt 作 V 计算得到新的 z t z_t zt,后面接着计算 cross-attention 得到最终的 z t z_t zt。
三. Shape-Aware 视频编辑
FateZero 方法还可以和预训练的视频 diffusion —— Tune-a-video 结合,用于视频中的物体形状编辑。
四. 实验
Shape-Aware 视频编辑效果如下:
FateZero 视频编辑效果如下:
五. 总结
FateZero 是一种融合注意力机制的视频编辑方法,本质上是 Prompt-to-Prompt 方法在时间维度的扩展,单独编辑每一帧图像实现了视频的编辑。FateZero 的编辑发生在去噪过程中,先使用 cross-attention maps 过滤得到 mask,然后将 mask 以外区域的 self-attention maps 替换成原始图像的 self-attention maps,从而维护了图像中物体的结构和运动信息。
FateZero 的缺点也非常明显:通过阈值筛选 cross-attention maps 得到 mask 进行编辑显然只适用于编辑前后形状相似的物体,如果形状差异较大,那么编辑结果很难令人满意。此外,FateZero 在编辑过程中出现了语义泄露的问题,编辑视频的背景都没有保持原样。
六. 复现
- 平台:AutoDL
- 显卡:RTX 4090 24G
- 镜像:PyTorch 2.0.0、Python 3.8(ubuntu20.04)、Cuda 11.8
- 源码:https://github.com/ChenyangQiQi/FateZero
1. 形状和运动编辑 (Shape and large motion editing)
实验记录:
- 克隆仓库后,按照 README 创建虚拟环境
fatezero38
并安装依赖,然后验证依赖包的安装情况; - 为了节省时间和磁盘空间,没有下载
download_all.sh
中的所有数据和节点,只在ckpt
文件夹下载了预训练的jeep_tuned_200
节点。下载前需要安装lfs
,如果安装失败可以参考 Cannot install git-lfs, ‘‘lfs’ appears to be a git command, but we were not able to execute it’ #2898; - 在使用
accelerate launch test_fatezero.py --config config/teaser/jeep_posche.yaml
指令进行 Shape-Aware 视频编辑时遇到报错:FileNotFoundError: Package has no location <module 'imageio_ffmpeg.binaries' (namespace)>
:
可能是因为该系统中曾经安装过ffmpeg
并且添加了系统环境变量,因此此处需要手动添加imageio_ffmpeg
的环境变量 2。先使用ls /root/miniconda3/envs/fatezero38/lib/python3.8/site-packages/imageio_ffmpeg*
命令查找与之相关的文件
然后设置系统环境变量export IMAGEIO_FFMPEG_EXE=/root/miniconda3/envs/fatezero38/lib/python3.8/site-packages/imageio_ffmpeg/binaries/ffmpeg-linux64-v4.2.2
即可; - 然后就可以进行 Shape-Aware 视频编辑:
编辑结果被保存在FateZero/result
文件夹下,其中 step_0_0 表示 DDIM 重建结果,step_0_1 表示 DDIM 编辑结果 3:├─ config:不同训练任务的配置文件 | ├─ custom:自定义数据集的配置文件 | | └ lamborghini_posche.yaml | ├─ teaser:预训练所用数据集的配置文件 | └ ... ├─ data:训练数据 ├─ result:编辑结果 | ├─ custom | | ├─ lamborghini_posche_240606-184030 | | | ├─ attention_blend_mask:融合后的 self-attention maps | | | ├─ cross_attention:每个 token 的 cross-attention maps | | | ├─ sample:编辑后视频 | | | | ├─ step_0:每一帧原始图像、重建图像、编辑图像的对比 | | | | ├─ step_0atten:每一帧原始图像、编辑图像的 attention maps 对比 | | | | ├─ step_0_0_0:每一帧重建图像 | | | | ├─ step_0_0_0atten:每一帧重建图像的 attention maps | | | | ├─ step_0_1_0:每一帧编辑图像 | | | | ├─ step_0_1_0atten:每一帧编辑图像的 attention maps | | | | ├─ step_0.gif | | | | ├─ step_0atten.gif | | | | ├─ step_0_0_0.gif | | | | ├─ step_0_0_0atten.gif | | | | ├─ step_0_1_0.gif | | | | └ step_0_1_0atten.gif | | | ├─ train_samples:编辑前视频的逐帧导出图像 | | | ├─ config.yml:配置参数 | | | ├─ log.log:日志信息 | | | ├─ train_samples.gif:编辑前视频的 gif 格式 | | | └ train_samples.mp4:编辑前视频 | ├─ teaser | └ ...
实验结果:
编辑前后视频如下:
2. 风格和属性变换 (Style and Attribute Editing)
实验过程:
- 使用此前配置好的环境即可,需要下载
stable-diffusion-v1-4
预训练权重,因为节点太大只能放到数据盘。需要修改config/teaser/jeep_watercolor.yaml
中的配置信息:pretrained_model_path: "../autodl-tmp/stable-diffusion-v1-4"
; - 设置
IMAGEIO_FFMPEG_EXE
环境变量后即可进行 FateZero 视频编辑:
实验结果:
编辑前后视频如下:
3. 自定义数据集
-
如果使用的自定义数据集是视频格式,需要先提取出每一帧:
import cv2 import os def extract_frames(video_path, output_folder): if not os.path.exists(output_folder): os.makedirs(output_folder) cap = cv2.VideoCapture(video_path) if not cap.isOpened(): print(f"无法打开视频文件:{video_path}") return frame_count = 0 while True: ret, frame = cap.read() if ret: frame_filename = os.path.join(output_folder, f"{frame_count:05d}.png") cv2.imwrite(frame_filename, frame) frame_count += 1 else: break cap.release() print(f"总共导出 {frame_count} 帧图片到 {output_folder}") video_path = r"data\drift.mp4" output_folder = 'output' extract_frames(video_path, output_folder)
-
仿照
config/teaser/jeep_posche.yaml
创建配置文件,因为显存有限,只能选取非常少量的帧进行编辑:
实验结果 1:
编辑前后视频如下:
实验结果 2:
编辑前后视频如下:
实验结果 3:
编辑前后视频如下: