新视角生成是从一个指定的图像或者prompt作为输入,生成该物体/场景在其他视角下的情况,要求生成的结果和原始的图像/Prompt是具有语义和纹理一致性的,同时如果生成多个视角还要保证多个视角之间也是一致的。下面Zero 1-to-3的效果就能很好表明这个问题:

新视角生成示意图(原图来自Zero 1-to-3)

【1】Zero-1-to-3: Zero-shot One Image to 3D Object

项目主页】【论文】【代码

Zero123
  • 算法流程:该算法借助当前强大的2D 下的Diffusion模型生成3D物体,具体步骤为: 该模型根据指定的输入图和相机变换参数,来生成该物体新位置下的新视角图,然后使用多张新视角图再配合Score Jacobian chain来优化得到最终的3D模型。

  • 需要克服的问题

    • “pose未知”: 目前2D的Diffusion模型是在大规模不同视角不同位置的图像集训练的,这些数据并没有位置的对应关系
    • “生成偏见:”因为数据集分布的不均匀,生成图像的结果具有一定的倾向性,比如生成的椅子倾向于生成"正面"。
  • 算法细节:

    • 模型输入输出: 其中表示输入图和输出的新视角图,示旋转平移矩阵,该矩阵矩阵表示输入图到新视角图的变换过程。

      • 在训练时,因为输入图和“输出“的新视角图都是通过带纹理的3D模型渲染出来的,所以这两个视角的相机外参都是已知的,该变换矩阵也比较容易计算出来;
      • 在测试过程中,可以自定义一个变换过程(比如上下旋转30度,左右旋转90度的变换矩阵)
    • 视角生成控制方法:

      从单张图进行3D重建需要low-level的信息(如depth、shading、texture等)也需要high-level的信息(如类别、功能、结构等),对此分别提出了两个控制生成方法:

      • "posed CLIP-保证语义一致性":输入图像通过CLIP提取embedding然后与旋转矩阵拼接,再通过Cross-Attention融入denoise U-Net完成控制。
      • "通道拼接-保证生成identity和细节":输入视角图与noisy 图通道拼接-->保证细节一致性。
    • 训练损失: 其中就是融合了输入图像和旋转变换矩阵的embedding,用其作为生成的控制条件

    • 3D生成/重建:生成的多张视角图作为输入,使用Score Jacobian chain完成3D生成。

参考:

  • https://zhuanlan.zhihu.com/p/650177794

  • https://blog.csdn.net/qq_45752541/article/details/132100570

【2】MVDiffusion: Enabling Holistic Multi-view Image Generation with Correspondence-Aware Diffusion

项目主页】【论文】【代码

MVDiffusion
  • 算法流程:专注于全景图的生成和基于Depth的场景mesh上色,一次性生成一个场景下连续的8个视角,通过新引进的关联性注意力保证多个视角之间的一致性。

  • 算法细节:

    • 网络结构:

      在原SD-Unet的Encoder的每个block后添加一个注意力模块(CAA Block),该注意力模块是保证一次性生成8个视角一致性的关键性模块,该模块同样使用了类似ConrolNet一样的Zero Convolution,保证原本SD的生成能力不会被破坏,冻住原本Stable Diffusion权重只更新CAA模块的参数,

    • Correspondence-aware Attention(关联性注意力)

      这是本论文的核心内容:总体思路是目前拥有8个视角(比如视角图是)下已知的相机外参和内参矩阵,来计算这8个视角之间的关联性矩阵,这个关联性矩阵的尺寸是,矩阵下标为 表示第视角空间位置对应于第个视角的哪个像素点(用xy二位空间坐标表示),感觉有点像光流场的意思。具体地示意图如下:

      MVDiffusion_CAA

      用论文中的说法就是:一个有N个视角,为了生成某个视角下的特征图,我们需要从其他视角找到在空间上存在对应的关系的位置,比如这里的原视角中的位置S,我们通过关联性矩阵招待在其他视角下的位置,然后通过其近邻的像素值来计算attention信息,具体计算方式如下: 其中表示的近邻元素,S和是两组位置对应的像素,表示一般transformer中的位置编码函数。

    • 训练损失: 其中N表示多视角的个数,表示第i个视角在第t步的含噪特征。

  • 代码实践结果:

    该方法是8个视角的结果一起出来的,在计算好关联矩阵后,前向推理过程中会自动获取其他视角与当前视角的对应关系,最后同时生成8个视角(这8个视角每个视角横向跨度90度,相邻视角之间会有45度的重合),然后仿射变换并融合后得到最终的全景图结果:MVDiffusion_run

    此外因为关联矩阵是每两个视角之间都有一个的矩阵,所以上面生成的全景图的左端和右端也是能够”无缝拼接“的,保证整个生成结果的闭环,

  • 应用场景:

    • 通过文字描述从0到1生成各个视角下的图像,然后融合后得到完整全景图
    • 通过文字描述,并指定起始输入图,通过outpainting的方式不生成新视角,然后融合后得到完整全景图
    • 通过depth控制生成全景图

【3】MVDream: Multi-view Diffusion for 3D Generation

项目主页】【论文】【代码

MVDream
  • 算法流程:一种利用Stable Diffusion模型生成多视角图的方法,对原有SD模型中的2D Attention进行改动,支持3D维度的attention,保证生成视角的一致性。

  • 多视角生成问题:

    • "多头问题":模型分不清视角正面背面,导致生成目标的位置混淆产生多个脸
    • "内容偏移":生成过程中之间视角之间没有约束导致生成内容不一致产生偏差

    为了解决多视角生成的问题,通常的解决办法包括两种:

    • DreamFusion一样在prompt中添加多视角相关的额描述词
    • Zero 1-to-3一样使用相机参数强行增加模型对于位置的感知
  • 算法细节:

    • 模型输入与输出是怎样?

      • 输入:噪声图()和文本prompt+相机外参矩阵()
      • 输出:多视角图()

      其中是多视角的个数

    • 如何嵌入相机参数?

      本文首先尝试使用类似Make A Video一样使用相对位置参数(两个视角的变换矩阵)还是不够的,这其实会导致重复视角的生成(Zero 1-to-3好像也是用的相对视角位置参数)!所以论文又尝试了直接将相机参数通过一个两层的MLP得到camera embedding融入到模型中,具体融入方法又尝试了两种:

      • 直接通过残差的方式将camera embedding融入到time embedding中
      • 通过拼接的方式将camera embedding融入到text embedding中,然后用于cross attention

      最后作者实验发现第一种方法会更好,因为camera embedding可能会和文本描述产生一定的耦合。

    • 如何提升多视角一致性?

      为了提升多视角的一致性,本方法也同样是从attention的角度出发,探索了三种attention的方式:

      • Video diffusion中常用的时序注意力:只能在不同帧的同一位置像素交换信息,当视角变化比较大时同一个像素在不同视角间相距会很远仍然会有内容偏移的问题,无法保证一致性
      • 增加新的3D自注意力:要从头训练,需要花费更多的数据和时间,无法保留原SD模型强大的生成能力生成效果下降,也无法保证一致性
      • 复用原2D的自注意力:效果不下降,且一致性也保持的最好【TODO: 这部分论文中好像没有做详细的公式和细节介绍,等待看了代码后做进一步补充】
    • 如何制作训练数据?

      • 视角的选择:非随机,固定仰角度数,均匀采样视角得到多视角数据集
      • 生成图像数量:4个
      • 分辨率:
      • 联合训练:与原文生2D的数据集(30%采样概率)联合训练
    • 损失函数: 其中分别表示原文生2D的数据集和多视角数据集,c是 camera embedding,y是控制条件(如prompt),如下图所示的上下两条支路分别代表使用文生2D生成多视角生成

      MVDream_training
  • 算法应用:

    • 配合DreamBooth完成指定目标的多视角生成
    • 配合DreamFusion的SDS 损失完成3D生成

【4】SyncDreamer: Generating Multiview-consistent Images from a Single-view Image

项目主页】【论文】【代码

SyncDreamer
  • 算法流程:接受单张图作为输入,可以同时生成该物体的多个视角图,具体是方位角从且俯仰角均为个新视角。为了保证生成视角的一致性,该模型从Zero 1-to-3作为基础backbone的基础上(上图的下半红色透明底的部分其实就完全是zero 1-to-3的结构),提出了一个Multiview Diffusion Model

  • 算法细节:

    • 与Zero 1-to-3的关系:其实总体来说,SyncDreamer就是在Zero 1-to-3的基础上额外添加了一个多视角Diffusion模型,使用一个专门的Spatial Volume来提供全局多视角特征,保证生成指定视角时会参考其他所有视角的信息。【Spatial Volume后面会介绍】可以理解为之前Zero 1-to-3生成新视角的时候只有输入视角图和代表视角差异的旋转变换矩阵,然后生成的结果也只是专门针对目标视角的而完全不会考虑其他可能视角的结果,而SyncDreamer则会考虑其他可能的新视角的结果。

    • Multiview Diffusion Model:在原先Diffusion模型的基础上引入了多个视角的同时生成,这里我们先回顾下原Diffusion模型的加噪、去噪过程和损失函数【具体其实也可以参考我的另一篇博客】: 为了生成多视角的图,需要学习一个联合分布,其中y是输入的视角图。于是上述公式在增加了多视角的约束后就变成了: 其中要特别注意的是:

      • 上面最后一行的是对预测第n个视角添加的噪声,则表示从所有视角预测的第n个视角噪声

      • 这里每个视角预测噪声的网络在具体实现中其实是共享的,然后具体预测第n个视角的噪声会计算当前这个视角和输入图像的视角pose差异(有点类似于Zero 1-to-3的旋转变换矩阵)作为输入条件,于是就变成了: 即通过当前视角噪声图、所有视角噪声图、当前视角与输入视角的pose差异、时间戳来预测当前视角的下一个去噪结果

      • 在去噪预测第个视角的时候,会使用所有其他视角的信息

    • syncchronized noise predictor:上述的其实就是同步噪声预测器(syncchronized noise predictor),也是本文的核心内容,通过关联多视角的特征来保证生成视角的一致性,具体实现则是通过一个3D-aware attention,下面则具体介绍下这个3D-aware attention

      • backbone网络:使用的是Zero 1-to-3的初始权重作为初始化,同时backbone网络的输入像也是和Zero 1-to-3一样由两个部分组成:

        • 输入视角图和待去噪的噪声图的拼接
        • 输入视角和目标视角的pose差异和输入视角的clip embedding特征拼接
      • 多视角关联:剩下一个最重要的问题如何在生成第n个视角的过程利用其他所有视角的信息来保证生成结果的一致性,文章中主要构建了Spatial Volume和Depth-Wise Attention两个模块:

        • Spatial Volume对每个视角生成是共享的,这就对生成的每个视角有一个全局的约束来其生成的多视角图是“同一个物体的”。具体做法是:

          输入16个待去噪的视角噪声图,并融合位置差异,时间编码,16个视角的相机外参和16个视角的相机内参,通过3D CNN卷积得到了最终的Spatial Volume尺寸为,这个spatial_volume融合所有视角的空间信息以提升视角生成的一致性。

        • 新添加的Depth-Wise Attention是沿着通道维度添加,其能够提供一个局部位置的强力约束-其实就是相当于空间位置完全对齐的生成过程。

  • 代码阅读记录:

    • 视角差异就是在新视角图在俯仰角、偏航角、翻滚角这三个角度和原始输入视角的差异:

      # 计算各个新视角和原始输入视角的差异,最终的d_e,d_a,d_z都是[1, 16]大小-->16是视角个数!!
      d_e = d_e.unsqueeze(1).repeat(1, N)  # 俯仰角差异(全是0)-类似pitch(抬头低头上下角度)
      d_a = azimuth_target - azimuth_input  # 偏航角差异(这个是核心差异点)-类似于yaw(左右摇头角度)
      d_a = d_a.unsqueeze(0).repeat(B, 1)
      d_z = torch.zeros_like(d_a)   # 翻滚角(也全是0)-类似roll(旋转歪头角度-垂直于前方视线)
      # 计算位置pose差异embedding---->尺寸为[1, 16, 4]
      embedding = torch.stack([d_e, torch.sin(d_a), torch.cos(d_a), d_z], -1)
    • 输入视角图的clipe embedding()与某个目标视角和输入视角相机视角差异直接拼接后过一个Linear层保证尺寸又回到768长度,然后再送入到SD-Unet中:

      # Linear定义
      self.cc_projection = nn.Linear(772, 768)
      nn.init.eye_(list(self.cc_projection.parameters())[0][:768, :768])
      nn.init.zeros_(list(self.cc_projection.parameters())[1])
      
      # 两个embedding拼接再过一个linear
      self.cc_projection(torch.cat([clip_embed_, v_embed_.unsqueeze(1)], -1))
    • 自己测试集上的测试效果:

      总的来说效果还是很好的,多视角的一致性也不错,非要挑挑毛病的话,就是重构当前输入图像得到的第一个视角图细节丢失比较多,然后就是每个视角之间还是会存在比较小的不一致性(比如第一件衣服上的纽扣)。

      SyncDreamer_results
  • 配合three studio完成3D的重建

总结

首先要知道的是:以上这几种方法都不是直接生成3D模型的方法,无法生成point cloud或者mesh等3D shape,而是对目前强大的Stable Diffusion模型进行改造让其可以生成视角一致性好的2D图像,然后这些多视角的2D图像再通过Score Jacobian chain或者DreamFusion的SDS 损失或者three studio来优化得到最终的3D模型。

上面多视角生成的方法对Stable Diffusion进行升级的点主要有3个:

  • 收集多视角训练数据。
  • 融入相机参数信息让模型学习到原2D Diffison不具有的空间识别能力。
  • 设计专门用于多视角生成的Attention方式并融合。

但是上面几种方法也具有小小的不同点:

算法名称 应用 输入 输出 相机控制方式
Zero 1-to-3 Object 单张输入视角、变换矩阵(R,T) 单张视角图 输入图过CLIP然后拼接旋转变换矩阵(R,T),通过Cross-Attention注入到U-Net
MVDream Object 文本描述,多个pose相机外参 多个视角图 相机外参融入到time embedding然后送到U-Net
SyncDreamer Object 单张输入视角,多个视角的相机外参 多个视角图 在zero 1-to-3的基础上额外添加了一个多视角diffusion模型保证生成固定视角时会参考其他所有视角的信息。
MVDiffusion Sence prompt/单张视角图[可选]/每个pose下depth[可选] 一个batch内同时生成多个视角图 根据每个pose 计算总体关联性矩阵,然后用该矩阵计算attention