原文:stable-diffusion 生成同一人物不同动作的尝试(基础动作融合) - 知乎
目录
开发游戏中,美术资源是一大块成本。而游戏中包含很多角色,每个角色需要表现出不同的动作,因此一直在研究的一个课题“是如何用AI去生成同一个角色的多种动作”,此前也尝试过不少方法,研究之路继续进行,以下是一些以前的研究连接。
罗培羽:AI做游戏,扩散模型生成角色行走图76 赞同 · 18 评论文章正在上传…重新上传取消
罗培羽:stable-diffusion 生成同一人物不同动作的尝试(多姿势图)98 赞同 · 9 评论文章正在上传…重新上传取消
本次是尝试修改stable-diffusion的生成过程,使得模型可以记录一部分角色信息,从而为后续生成同个角色的其他动作的图像提供可能性。
效果展示
如下图所示,左图是用sd模型+controlnet生成的一张角色图(图中的“特征图”),而如果不采用任何新方法,使用同样参数+另外的controlnet姿态,会生成下图(图中”动作A未融合“)的样式,角色样貌与“特征图”会有一些差异,例如人物衣服的颜色有较大的不同。使用新方法生成的图片如左上图所示(图中”动作A特征融合“),可见角色形态与”特征图“更为接近。
下图是另一个例子,使用新方法后,人物的衣服和裙子样式、头上的发饰都更接近原始的”特征图“,更像是同一个角色的不同动作。
原理说明
要做到”同一角色“,就需要把角色的信息传递给生成模型,目前会有比较多的想法,待一步步尝试。而本文所使用的方法,因实现较为简单,因此先做个实验。如下图所示,展示了diffusion+controlnet的生成过程,在每一个去噪步骤中,都会输入噪声、prompt和openpose姿势图。
有一个简单办法可以保留部分”特征图“的信息,如下图所示,在前几个生成步骤中,使用同特征图的参数,而在后几个步骤中,再改变姿态图。从中可以预想,前几步的生成过程中”特征图“与”新图像“遵循着同样的步骤和参数,相当于”特征图“的一部分生成过程信息传递到”新图像“中。
代码实现
为了实现这个过程,简单修改了stable-diffusion-webui中controlnet插件的代码(插件代码地址:https://github.com/Mikubill/sd-webui-controlnet),在下方新增identity_reserve_step、is_save_identity、is_load_identity三个选项。在生成特征图时,勾选is_save_identity,那么程序会记录生成过程中一些信息;在生成新图像时,勾选is_load_identity,以读取之前保存的信息;identity_reserve_step是一个控制项,指定在前多少个去噪步骤中使用保存的信息。
出于实验性质的考虑,我们此处并不考虑代码质量,仅仅关注实现功能。若后续得到很好的实验结果,再做代码改进。本次主要改动controlnet插件script/hook.py中的代码,因为它包含对unet改动的相关内容。
先是定义两个全局变量,一个用于计数,一个用于保存信息。注意,此处过多考虑代码质量,仅做实验展示。
#identity
identity_count=0
identity_dict = {}
然后修改cfg_based_adder方法,原始unet和controlnet结果相加部分会在这里,两处”##### identity start #####“和”##### identity end #####“中间的内容为插入的代码,具体作用是保存生成信息以及恢复。代码中写死了17、20等数值,它们代表每一次去噪中unet与controlnet结果相加的次数,以及20次去噪步骤。
def hook(self, model):
……
def cfg_based_adder(base, x, require_autocast, is_adapter=False):
##### identity start #####
global identity_count
global identity_dict
param = self.control_params[0]
per_step_length = 17 #没有反向词17,有反向词34(不完全准确)
identity_index = identity_count % (20*per_step_length) #20个step
if identity_index == 0:
print("param.identity_reserve_step",param.identity_reserve_step)
print("param.is_save_identity",param.is_save_identity)
print("param.is_load_identity",param.is_load_identity)
identity_count = identity_count+1
##### identity end #####
(……原始内容……)
##### identity start #####
if param.is_save_identity == True:
print("save identity_index " + str(identity_index),identity_count)
identity_dict[identity_index] = base + x
if param.is_load_identity == True and identity_index < per_step_length*param.identity_reserve_step:
print("read identity_index " + str(identity_index),identity_count)
return identity_dict[identity_index]
##### identity end #####
return base + x
为了在界面上增加三个参数,修改controlnet.py,增加三个UI控件。
class Script(scripts.Script):
……
def uigroup(self, tabname, is_img2img):
……
with gr.Row():
identity_reserve_step = gr.Slider(label="identity_reserve_step", value=default_unit.identity_reserve_step, minimum=0.0, maximum=20, interactive=True)
is_save_identity = gr.Checkbox(label='is_save_identity', value=default_unit.is_save_identity)
is_load_identity = gr.Checkbox(label='is_load_identity', value=default_unit.is_load_identity)
而相关参数传递涉及较多处代码,这里仿照其他参数的写法,修改了相应的文件,修改内容如下。
更多效果
下图展示更多的生成效果,图中展示两个动作,可见用新方法得到的图像与第一张图更加接近,长袖、长裙、卷发这些元素得到体现。
下图是另一个实例,可见的裙子样式和颜色与原图更加接近。
不足之处
该方法也有一些不足之处,如下所示,当动作变化较大时,一个正面一个侧面,虽说也更像同一个角色,但拟合效果就打了折扣。
后续也将继续实验,用另外的方法将特征图的信息传递到生成图像。
PS:如果你想从事游戏开发,就不得不提到笔者编写的《Unity3D网络游戏实战》这本书,它是一本非常实用的书籍,看完就可以自己做一款网络游戏出来,注意了注意了,是当今游戏公司都在做的网络游戏哦,而不是较为简单的单机游戏。