稳定扩散 WebUI 锻造
稳定扩散 WebUI 锻造
Stable Diffusion WebUI Forge 是建立在 Stable Diffusion WebUI 之上的平台(基于 Gradio),旨在简化开发、优化资源管理并加快推理速度。
名称“Forge”灵感来自“Minecraft Forge”。该项目旨在成为SD WebUI的Forge。
与原始WebUI相比(用于1024px的SDXL推理),您可以期待以下加速:
- 如果您使用像8GB vram这样的常见GPU,您可以期望在推理速度(it/s)上获得约30 extasciitilde45%的加速,GPU内存峰值(在任务管理器中)将减少约700MB至1.3GB,最大扩散分辨率(不会OOM)将增加约2倍至3倍,最大扩散批处理大小(不会OOM)将增加约4倍至6倍。2. 如果您使用像6GB vram这样性能较弱的GPU,您可以期望在推理速度(it/s)上获得约60 extasciitilde75%的加速,GPU内存峰值(在任务管理器中)将减少约800MB至1.5GB,最大扩散分辨率(不会OOM)将增加约3倍,最大扩散批处理大小(不会OOM)将增加约4倍。3. 如果您使用像24GB vram这样的强大GPU,您可以期望在推理速度(it/s)上获得约3 extasciitilde6%的加速,GPU内存峰值(在任务管理器中)将减少约1GB至1.4GB,最大扩散分辨率(不会OOM)将增加约1.6倍,最大扩散批处理大小(不会OOM)将增加约2倍。4. 如果您使用ControlNet进行SDXL,最大ControlNet计数(不会OOM)将增加约2倍,使用SDXL+ControlNet的速度将加速约30 extasciitilde45%。
Forge带来的另一个非常重要的变化是Unet Patcher。使用Unet Patcher,像自我关注引导、Kohya高分辨率修复、FreeU、StyleAlign、Hypertile等方法都可以在大约100行代码中实现。
感谢 Unet Patcher,现在在 Forge 中支持了许多新功能,包括 SVD、Z123、掩码 Ip 适配器、掩码控制网络、照片制作等。
不再需要对 UNet 进行猴子补丁,也不会与其他扩展产生冲突了!
Forge还添加了一些采样器,包括但不限于DDPM,DDPM Karras,DPM++ 2M Turbo,DPM++ 2M SDE Turbo,LCM Karras,Euler A Turbo等(LCM自1.7.0版本起已经在原始webui中)。
最后,Forge 承诺我们只会做好自己的工作。Forge 永远不会对用户界面进行不必要的主观更改。您仍在使用 100% Automatic1111 WebUI。
安装 Forge
安装 Forge
如果您精通 Git 并且想要将 Forge 安装为 SD-WebUI 的另一个分支,请参阅这里。通过这种方式,您可以重用您在原始 SD-WebUI 中之前安装的所有 SD 检查点和所有扩展,但您应该知道自己在做什么。
如果你知道你在做什么,你可以使用与SD-WebUI相同的方法安装Forge。(安装Git、Python,Git克隆Forge存储库https://github.com/lllyasviel/stable-diffusion-webui-forge.git
,然后运行webui-user.bat)。
或者您可以使用这个一键安装包(包含git和python)。
[
点击这里下载一键安装包 <<< ](https://github.com/lllyasviel/stable-diffusion-webui-forge/releases/download/latest/webui%5Fforge%5Fcu121%5Ftorch21.7z)
下载后,解压缩,使用 update.bat
进行更新,使用 run.bat
运行。
请注意运行 update.bat
是很重要的,否则您可能在使用之前版本,其中可能存在未修复的潜在错误。
对比截图
对比截图
我使用了几台设备进行测试,这是来自8GB VRAM(3070ti 笔记本电脑)与SDXL的典型结果。
这是原始的 WebUI:
(平均约7.4GB/8GB,峰值约7.9GB/8GB)
这是 WebUI Forge:
(平均和峰值均为6.3GB/8GB)
你可以看到Forge不会改变WebUI的结果。安装Forge不会导致种子破坏。
Forge可以完美地保持WebUI不变,即使是最复杂的提示,比如幻想景观,有一座[山:湖:0.25]和[一棵橡树:一棵圣诞树:0.75][在前景中::0.6][在背景中:0.25][简陋的:精湛的:0.5]
。
在 Forge 中,您所有以前的作品仍然有效!
Forge 后端
Forge 后端删除了所有与资源管理相关的 WebUI 代码,并对所有内容进行了重新设计。所有以前的 CMD 标志,如 medvram、lowvram、medvram-sdxl、precision full、no half、no half vae、attention_xxx、upcast unet
,... 都已经 删除。添加这些标志不会导致错误,但现在它们将不起作用。我们强烈建议 Forge 用户删除所有 cmd 标志,让 Forge 决定如何加载模型。
没有任何cmd标志,Forge可以使用4GB vram运行SDXL,使用2GB vram运行SD1.5。
您可能仍需注意的一些标志:
如果你真的想要玩转cmd标志,你还可以通过以下方式额外控制GPU:
(极端 VRAM 情况)
--always-gpu
--always-cpu
(罕见关注案例)
--注意力分割
--四重注意力
--PyTorch注意力
--禁用Xformers
--禁用注意力升级
(浮点类型)
--全FP32
--全FP16
--UNet中BF16
--UNet中FP16
--UNet中FP8-E4M3FN
--UNet中FP8-E5M2
--VAE中FP16
--VAE中FP32
--VAE中BF16
--Clip中FP8-E4M3FN
--Clip中FP8-E5M2
--Clip中FP16
--Clip中FP32
(罕见平台)
--directml
--disable-ipex-hijack
--pytorch-deterministic
再次强调,Forge 不建议用户使用任何 cmd 标志,除非您非常确定您确实需要这些。
UNet 补丁程序
[](#UNet 补丁程序)
请注意,Forge 不使用任何其他软件作为后端。后端的全称是Stable Diffusion WebUI with Forge backend
,或者简称为Forge backend
。API 和 Python 符号仅为降低开发人员的学习成本而设计得与之前的软件相似。
现在开发扩展非常简单。我们终于有了可修补的 UNet。
以下是使用一个包含80行代码的单个文件来支持FreeU:
extensions-builtin/sd_forge_freeu/scripts/forge_freeu.py
import torch import gradio as gr from modules import scripts
def Fourier_filter(x, threshold, scale): x_freq = torch.fft.fftn(x.float(), dim=(-2, -1)) x_freq = torch.fft.fftshift(x_freq, dim=(-2, -1)) B, C, H, W = x_freq.shape mask = torch.ones((B, C, H, W), device=x.device) crow, ccol = H // 2, W //2 mask[..., crow - threshold:crow + threshold, ccol - threshold:ccol + threshold] = scale x_freq = x_freq * mask x_freq = torch.fft.ifftshift(x_freq, dim=(-2, -1)) x_filtered = torch.fft.ifftn(x_freq, dim=(-2, -1)).real return x_filtered.to(x.dtype)
def set_freeu_v2_patch(model, b1, b2, s1, s2): model_channels = model.model.model_config.unet_config["model_channels"] scale_dict = {model_channels * 4: (b1, s1), model_channels * 2: (b2, s2)}
def output_block_patch(h, hsp, *args, **kwargs):
scale = scale_dict.get(h.shape[1], None)
if scale is not None:
hidden_mean = h.mean(1).unsqueeze(1)
B = hidden_mean.shape[0]
hidden_max, _ = torch.max(hidden_mean.view(B, -1), dim=-1, keepdim=True)
hidden_min, _ = torch.min(hidden_mean.view(B, -1), dim=-1, keepdim=True)
hidden_mean = (hidden_mean - hidden_min.unsqueeze(2).unsqueeze(3)) / \
(hidden_max - hidden_min).unsqueeze(2).unsqueeze(3)
h[:, :h.shape[1] // 2] = h[:, :h.shape[1] // 2] * ((scale[0] - 1) * hidden_mean + 1)
hsp = Fourier_filter(hsp, threshold=1, scale=scale[1])
return h, hsp
m = model.clone()
m.set_model_output_block_patch(output_block_patch)
return m
class FreeUForForge(scripts.Script): def title(self): return "FreeU Integrated"
def show(self, is_img2img):
# 使此扩展在 txt2img 和 img2img 选项卡中均可见。
return scripts.AlwaysVisible
def ui(self, *args, **kwargs):
with gr.Accordion(open=False, label=self.title()):
freeu_enabled = gr.Checkbox(label='启用', value=False)
freeu_b1 = gr.Slider(label='B1', minimum=0, maximum=2, step=0.01, value=1.01)
freeu_b2 = gr.Slider(label='B2', minimum=0, maximum=2, step=0.01, value=1.02)
freeu_s1 = gr.Slider(label='S1', minimum=0, maximum=4, step=0.01, value=0.99)
freeu_s2 = gr.Slider(label='S2', minimum=0, maximum=4, step=0.01, value=0.95)
返回 freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2
def process_before_every_sampling(self, p, *script_args, **kwargs):
# 每次采样前都会调用此函数。
# 如果使用高分辨率修复,此函数将被调用两次。
freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2 = script_args
如果未启用freeu:
返回
unet = p.sd_model.forge_objects.unet
unet = set_freeu_v2_patch(unet, freeu_b1, freeu_b2, freeu_s1, freeu_s2)
p.sd_model.forge_objects.unet = unet
# 以下代码将在UI上图像输出下方的文本中添加一些日志。
# extra_generation_params 不会影响结果。
p.extra_generation_params.update(dict(
freeu_enabled=freeu_enabled,
freeu_b1=freeu_b1,
freeu_b2=freeu_b2,
freeu_s1=freeu_s1,
freeu_s2=freeu_s2,
))
返回
看起来是这样的:
类似的组件,如HyperTile、KohyaHighResFix、SAG,都可以在100行代码内实现(另请参见代码)。
ControlNets 最终可以被不同的扩展名调用。
现在实现稳定的视频扩散和 Zero123 也变得非常简单(另请参阅代码)。
稳定的视频传输:
extensions-builtin/sd_forge_svd/scripts/forge_svd.py
import torch import gradio as gr import os import pathlib
从模块中导入脚本回调 从模块路径导入模型路径 从模块 UI 共用导入工具按钮、刷新符号 从模块中导入共享
from modules_forge.forge_util import numpy_to_pytorch, pytorch_to_numpy from ldm_patched.modules.sd import load_checkpoint_guess_config from ldm_patched.contrib.external_video_model import VideoLinearCFGGuidance, SVD_img2vid_Conditioning from ldm_patched.contrib.external import KSampler, VAEDecode
opVideoLinearCFGGuidance = VideoLinearCFGGuidance() opSVD_img2vid_Conditioning = SVD_img2vid_Conditioning() opKSampler = KSampler() opVAEDecode = VAEDecode()
svd_root = os.path.join(models_path, 'svd') os.makedirs(svd_root, exist_ok=True) svd_filenames = []
def update_svd_filenames(): global svd_filenames svd_filenames = [ pathlib.Path(x).name for x in shared.walk_files(svd_root, allowed_extensions=[".pt", ".ckpt", ".safetensors"]) ] return svd_filenames
@torch.inference_mode()
@torch.no_grad()
def predict(filename, width, height, video_frames, motion_bucket_id, fps, augmentation_level,
sampling_seed, sampling_steps, sampling_cfg, sampling_sampler_name, sampling_scheduler,
sampling_denoise, guidance_min_cfg, input_image):
filename = os.path.join(svd_root, filename)
model_raw, _, vae, clip_vision =
load_checkpoint_guess_config(filename, output_vae=True, output_clip=False, output_clipvision=True)
model = opVideoLinearCFGGuidance.patch(model_raw, guidance_min_cfg)[0]
init_image = numpy_to_pytorch(input_image)
positive, negative, latent_image = opSVD_img2vid_Conditioning.encode(
clip_vision, init_image, vae, width, height, video_frames, motion_bucket_id, fps, augmentation_level)
output_latent = opKSampler.sample(model, sampling_seed, sampling_steps, sampling_cfg,
sampling_sampler_name, sampling_scheduler, positive,
negative, latent_image, sampling_denoise)[0]
output_pixels = opVAEDecode.decode(vae, output_latent)[0]
outputs = pytorch_to_numpy(output_pixels)
return outputs
def on_ui_tabs(): with gr.Blocks() as svd_block: with gr.Row(): with gr.Column(): input_image = gr.Image(label='输入图片', source='upload', type='numpy', height=400)
with gr.Row():
filename = gr.Dropdown(label="SVD 检查点文件名",
choices=svd_filenames,
value=svd_filenames[0] if len(svd_filenames) > 0 else None)
refresh_button = ToolButton(value=refresh_symbol, tooltip="刷新")
refresh_button.click(
fn=lambda: gr.update(choices=update_svd_filenames),
inputs=[], outputs=filename)
width = gr.Slider(label='宽度', minimum=16, maximum=8192, step=8, value=1024) height = gr.Slider(label='高度', minimum=16, maximum=8192, step=8, value=576) video_frames = gr.Slider(label='视频帧数', minimum=1, maximum=4096, step=1, value=14) motion_bucket_id = gr.Slider(label='运动桶ID', minimum=1, maximum=1023, step=1, value=127) fps = gr.Slider(label='每秒帧数', minimum=1, maximum=1024, step=1, value=6) augmentation_level = gr.Slider(label='增强级别', minimum=0.0, maximum=10.0, step=0.01, value=0.0) sampling_steps = gr.Slider(label='采样步数', minimum=1, maximum=200, step=1, value=20) sampling_cfg = gr.Slider(label='CFG比例', minimum=0.0, maximum=50.0, step=0.1, value=2.5) sampling_denoise = gr.Slider(label='采样去噪', minimum=0.0, maximum=1.0, step=0.01, value=1.0) guidance_min_cfg = gr.Slider(label='引导最小CFG', minimum=0.0, maximum=100.0, step=0.5, value=1.0) sampling_sampler_name = gr.Radio(label='采样器名称', choices=['欧拉', '欧拉祖先', 'Heun', 'Heunpp2', 'DPM_2', 'DPM_2_祖先', 'LMS', 'DPM快速', 'DPM自适应', 'DPMPP_2s_祖先', 'DPMPP_SDE', 'DPMPP_SDE_GPU', 'DPMPP_2m', 'DPMPP_2m_SDE', 'DPMPP_2m_SDE_GPU', 'DPMPP_3m_SDE', 'DPMPP_3m_SDE_GPU', 'DDPM', 'LCM', 'DDIM', 'UNI_PC', 'UNI_PC_BH2'], value='欧拉') sampling_scheduler = gr.Radio(label='调度器', choices=['正常', 'Karras', '指数', 'SGM均匀', '简单', 'DDIM均匀'], value='Karras') sampling_seed = gr.Number(label='种子', value=12345, precision=0)
generate_button = gr.Button(value="生成")
ctrls = [文件名, 宽度, 高度, 视频帧数, 运动桶ID, 帧率, 增强级别, 采样种子, 采样步数, 采样配置, 采样采样器名称, 采样调度器, 采样去噪, 引导最小配置, 输入图像]
with gr.Column():
output_gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain',
visible=True, height=1024, columns=4)
generate_button.click(predict, inputs=ctrls, outputs=[output_gallery])
return [(svd_block, "SVD", "svd")]
update_svd_filenames() script_callbacks.on_ui_tabs(on_ui_tabs)
请注意,尽管上述代码看起来像是独立的代码,但实际上它们会自动卸载/加载其他模型。例如,下面是我打开 webui,加载 SDXL,生成图像,然后转到 SVD,然后生成图像帧。您可以看到 GPU 内存得到了完美的管理,SDXL 被移至 RAM,然后 SVD 被移至 GPU。
请注意,此管理是完全自动的。这使得编写扩展非常简单。
同样,Zero123:
编写一个简单的 ControlNet:
写一个简单的ControlNet
以下是一个简单的扩展,可以完全独立地通过 ControlNet,不会与任何其他扩展冲突:
extensions-builtin/sd_forge_controlnet_example/scripts/sd_forge_controlnet_example.py
请注意,此扩展被隐藏,因为它仅供开发人员使用。要在用户界面中查看它,请使用 --show-controlnet-example
。
在这个示例中,内存优化是完全自动的。您无需关心内存和推理速度,但如果需要,您可能希望缓存对象。
使用 --show-controlnet-example 查看此扩展。
import cv2 import gradio as gr import torch
从模块中导入脚本 从模块共享cmd选项导入cmd_opts 从modules_forge.shared导入支持的预处理器 从modelloader导入从URL加载文件 从ldm_patched.modules.controlnet导入加载控制网络 从modules_forge.controlnet导入应用高级控制网络 从modules_forge.forge_util导入numpy_to_pytorch 从modules_forge.shared导入controlnet_dir
class ControlNetExampleForge(scripts.Script): model = None
def title(self):
return "ControlNet 开发人员示例"
def show(self, is_img2img):
# 使此扩展在 txt2img 和 img2img 选项卡中均可见。
return scripts.AlwaysVisible
def ui(self, *args, **kwargs):
with gr.Accordion(open=False, label=self.title()):
gr.HTML('这是一个供开发人员使用的控制网络扩展示例。')
gr.HTML('您看到这个扩展是因为您使用了--show-controlnet-example')
input_image = gr.Image(source='upload', type='numpy')
funny_slider = gr.Slider(label='这个滑块什么也不做。它只是向您展示如何传递参数。',
minimum=0.0, maximum=1.0, value=0.5)
返回输入图像,有趣的滑块
def process(self, p, *script_args, **kwargs):
input_image, funny_slider = script_args
这个滑块什么也不做。它只是向您展示如何传递参数。
del funny_slider
如果输入图像为 None:
返回
controlnet_canny_path = load_file_from_url( url='https://huggingface.co/lllyasviel/fav_models/resolve/main/fav/control_v11p_sd15_canny_fp16.safetensors', model_dir=controlnet_dir, file_name='control_v11p_sd15_canny_fp16.safetensors' ) print('The model [control_v11p_sd15_canny_fp16.safetensors] download finished.')
self.model = load_controlnet(controlnet_canny_path)
print('Controlnet loaded.')
返回
def process_before_every_sampling(self, p, *script_args, **kwargs):
# 每次采样前都会调用此函数。
# 如果使用高分辨率修复,此函数将被调用两次。
input_image, funny_slider = script_args
如果 input_image 为 None 或 self.model 为 None:
返回
B, C, H, W = kwargs['noise'].shape # latent_shape
height = H * 8
width = W * 8
batch_size = p.batch_size
# 在特定分辨率下检测控制
control_image = preprocessor(
input_image, resolution=512, slider_1=100, slider_2=200, slider_3=None)
# 这里我们只使用最近邻插值来对齐输入形状。
# 您可能想要裁剪和调整大小,或裁剪和填充,或其他操作。
control_image = cv2.resize(
control_image, (width, height), interpolation=cv2.INTER_NEAREST)
# 输出预处理器结果。现在每次采样时调用。以你自己的方式缓存。
p.extra_result_images.append(control_image)
print('预处理器Canny完成。')
control_image_bchw = numpy_to_pytorch(control_image).movedim(-1, 1)
unet = p.sd_model.forge_objects.unet
# Unet 具有输入、中间、输出块,我们可以为所有块中的每个层分配不同的权重。
# 以下是一个在中间块中进行更强控制的示例。
# 这对于一些高分辨率修复通道很有帮助。(p.is_hr_pass)
positive_advanced_weighting = {
'input': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2],
'middle': [1.0],
'output': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2]
}
negative_advanced_weighting = {
'input': [0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95, 1.05, 1.15, 1.25],
'middle': [1.05],
'output': [0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95, 1.05, 1.15, 1.25]
}
# advanced_frame_weighting 是应用于批处理中每个图像的权重。
# 此列表的长度必须与批处理大小相同
# 例如,如果批处理大小为5,则下面的列表是 [0.2, 0.4, 0.6, 0.8, 1.0]
# 如果您将这5个图像视为视频中的5个帧,则会随着时间的推移对时间产生越来越强的控制。
advanced_frame_weighting = [float(i + 1) / float(batch_size) for i in range(batch_size)]
# advanced_sigma_weighting 允许您根据扩散时间步长(sigma)动态计算控制权重。
# 例如,下面的代码可以使开始步骤比结束步骤更强。
sigma_max = unet.model.model_sampling.sigma_max
sigma_min = unet.model.model_sampling.sigma_min
advanced_sigma_weighting = lambda s: (s - sigma_min) / (sigma_max - sigma_min)
# 甚至可以输入一个张量来屏蔽所有控制注入
# 在 UNet 推理过程中,掩码将自动调整大小。
# 大小应为 B 1 H W,H 和 W 不重要
# 因为它们将被自动调整大小
advanced_mask_weighting = torch.ones(size=(1, 1, 512, 512))
# 但在这个简单的示例中我们不使用它们
positive_advanced_weighting = None
negative_advanced_weighting = None
advanced_frame_weighting = None
advanced_sigma_weighting = None
advanced_mask_weighting = None
unet = apply_controlnet_advanced(unet=unet, controlnet=self.model, image_bchw=control_image_bchw, strength=0.6, start_percent=0.0, end_percent=0.8, positive_advanced_weighting=positive_advanced_weighting, negative_advanced_weighting=negative_advanced_weighting, advanced_frame_weighting=advanced_frame_weighting, advanced_sigma_weighting=advanced_sigma_weighting, advanced_mask_weighting=advanced_mask_weighting)
p.sd_model.forge_objects.unet = unet
以下代码将在UI上的图像输出下方添加一些日志。# extra_generation_params不会影响结果。p.extra_generation_params.update(dict(controlnet_info='您应该在输出图像下方看到这些文本!',))
返回
使用 --show-controlnet-example 查看此扩展。
if not cmd_opts.show_controlnet_example: del ControlNetExampleForge
添加一个预处理器
添加预处理器
以下是完整的代码,用于添加一个具有完美内存管理的普通预处理器。
您可以使用任意独立的扩展来添加预处理器。
所有其他扩展将读取您的预处理器,使用 modules_forge.shared.preprocessors
以下代码位于extensions-builtin\forge_preprocessor_normalbae\scripts\preprocessor_normalbae.py
from modules_forge.supported_preprocessor import Preprocessor, PreprocessorParameter from modules_forge.shared import preprocessor_dir, add_supported_preprocessor from modules_forge.forge_util import resize_image_with_pad from modules.modelloader import load_file_from_url
import types import torch import numpy as np
从 einops 中导入 rearrange 从 annotator.normalbae.models.NNET 中导入 NNET 从 annotator.normalbae 中导入 load_checkpoint 从 torchvision 中导入 transforms
class PreprocessorNormalBae(Preprocessor): def init(self): super().init() self.name = 'normalbae' self.tags = ['NormalMap'] self.model_filename_filters = ['normal'] self.slider_resolution = PreprocessorParameter( label='分辨率', minimum=128, maximum=2048, value=512, step=8, visible=True) self.slider_1 = PreprocessorParameter(visible=False) self.slider_2 = PreprocessorParameter(visible=False) self.slider_3 = PreprocessorParameter(visible=False) self.show_control_mode = True self.do_not_need_model = False self.sorting_priority = 100 # 数值越高,显示在列表顶部
def load_model(self):
if self.model_patcher is not None:
return
model_path = load_file_from_url(
"https://huggingface.co/lllyasviel/Annotators/resolve/main/scannet.pt",
model_dir=preprocessor_dir)
args = types.SimpleNamespace()
args.mode = 'client'
args.architecture = 'BN'
args.pretrained = 'scannet'
args.sampling_ratio = 0.4
args.importance_ratio = 0.7
model = NNET(args)
model = load_checkpoint(model_path, model)
self.norm = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
self.model_patcher = self.setup_model_patcher(model)
def __call__(self, input_image, resolution, slider_1=None, slider_2=None, slider_3=None, **kwargs):
input_image, remove_pad = resize_image_with_pad(input_image, resolution)
self.load_model()
self.move_all_model_patchers_to_gpu()
确保 input_image 的维度为 3
image_normal = input_image
with torch.no_grad():
image_normal = self.send_tensor_to_model_device(torch.from_numpy(image_normal))
image_normal = image_normal / 255.0
image_normal = rearrange(image_normal, 'h w c -> 1 c h w')
image_normal = self.norm(image_normal)
normal = self.model_patcher.model(image_normal)
normal = normal[0][-1][:, :3]
normal = ((normal + 1) * 0.5).clip(0, 1)
normal = rearrange(normal[0], 'c h w -> h w c').cpu().numpy() normal_image = (normal * 255.0).clip(0, 255).astype(np.uint8)
return remove_pad(normal_image)
add_supported_preprocessor(PreprocessorNormalBae())
新功能(原始 WebUI 中不可用的功能)
原始 WebUI 中不可用的新功能
感谢 Unet Patcher,现在在 Forge 中支持了许多新功能,包括 SVD、Z123、掩码 IP 适配器、掩码控制网络、照片制作等。
遮蔽的Ip-Adapter
掩码控制网络
PhotoMaker
(请注意,photomaker 是一个特殊控件,需要您添加触发词“photomaker”。您的提示应该是“一个 photomaker 的照片”)
万寿菊深度
新采样器(不在原始版本中)
新采样器(不在原始版本中)
DDPM
DDPM 卡拉斯
DPM++ 2M Turbo
DPM++ 2M SDE Turbo
LCM 卡拉斯
欧拉 A Turbo
关于扩展
关于扩展
ControlNet 和 TiledVAE 已集成,您应卸载这两个扩展:
sd-webui-controlnet multidiffusion-upscaler-for-automatic1111
请注意AnimateDiff正在由continue-revolution在sd-webui-animatediff forge/master branch和sd-forge-animatediff(它们是同步的)上进行建设。(continue-revolution原话:prompt travel, inf t2v, controlnet v2v已被证明运行良好;motion lora, i2i batch仍在建设中,可能在一周内完成)
其他扩展应该可以正常工作,比如:
canvas-zoom
translations/localizations
Dynamic Prompts
Adetailer
Ultimate SD Upscale
Reactor
然而,如果新的扩展使用 Forge,它们的代码可以更短。
通常,如果对旧扩展进行 Forge 的 unet 补丁重做,80% 的代码可以被移除,特别是当它们需要调用 controlnet 时。
贡献
Forge 每天下午(如果合并由 git 机器人、我的编译器或我的 ChatGPT 机器人自动成功,或者在午夜(如果我的编译器和 ChatGPT 机器人都未能成功合并,我会手动审核))使用机器人从 https://github.com/AUTOMATIC1111/stable-diffusion-webui/tree/dev 获取提交和代码。
所有可以在https://github.com/AUTOMATIC1111/stable-diffusion-webui/tree/dev实现的PR都应该提交到那里。
随时可以在这里提交与 Forge 功能相关的 PR。