GitHub - lllyasviel/stable-diffusion-webui-forge

内容

稳定扩散 WebUI 锻造

稳定扩散 WebUI 锻造

Stable Diffusion WebUI Forge 是建立在 Stable Diffusion WebUI 之上的平台(基于 Gradio),旨在简化开发、优化资源管理并加快推理速度。

名称“Forge”灵感来自“Minecraft Forge”。该项目旨在成为SD WebUI的Forge。

与原始WebUI相比(用于1024px的SDXL推理),您可以期待以下加速:

  1. 如果您使用像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 是很重要的,否则您可能在使用之前版本,其中可能存在未修复的潜在错误。

image

对比截图

对比截图

我使用了几台设备进行测试,这是来自8GB VRAM(3070ti 笔记本电脑)与SDXL的典型结果。

这是原始的 WebUI:

image

image

image

image

(平均约7.4GB/8GB,峰值约7.9GB/8GB)

这是 WebUI Forge:

image

image

image

image

(平均和峰值均为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,
    ))
    返回

看起来是这样的:

image

类似的组件,如HyperTile、KohyaHighResFix、SAG,都可以在100行代码内实现(另请参见代码)。

image

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。

请注意,此管理是完全自动的。这使得编写扩展非常简单。

image

image

同样,Zero123:

image

编写一个简单的 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

image

添加一个预处理器

添加预处理器

以下是完整的代码,用于添加一个具有完美内存管理的普通预处理器。

您可以使用任意独立的扩展来添加预处理器。

所有其他扩展将读取您的预处理器,使用 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

image

image

image

掩码控制网络

image

image

image

PhotoMaker

(请注意,photomaker 是一个特殊控件,需要您添加触发词“photomaker”。您的提示应该是“一个 photomaker 的照片”)

image

万寿菊深度

image

新采样器(不在原始版本中)

新采样器(不在原始版本中)

DDPM
DDPM 卡拉斯
DPM++ 2M Turbo
DPM++ 2M SDE Turbo
LCM 卡拉斯
欧拉 A Turbo

关于扩展

关于扩展

ControlNet 和 TiledVAE 已集成,您应卸载这两个扩展:

sd-webui-controlnet multidiffusion-upscaler-for-automatic1111

请注意AnimateDiff正在由continue-revolutionsd-webui-animatediff forge/master branchsd-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。

总结
Stable Diffusion WebUI Forge是建立在Stable Diffusion WebUI之上的平台,旨在简化开发、优化资源管理和加快推理速度。Forge带来了Unet Patcher,可以在约100行代码中实现诸如Self-Attention Guidance、Kohya High Res Fix、FreeU、StyleAlign、Hypertile等方法。此外,Forge还添加了一些采样器,包括DDPM、DDPM Karras、DPM++ 2M Turbo、DPM++ 2M SDE Turbo、LCM Karras、Euler A Turbo等。Forge承诺只做好自己的工作,不会对用户界面进行不必要的更改。安装Forge可以通过Git和Python,或者使用一键安装包。Forge的性能提升取决于GPU的不同,包括推理速度、GPU内存峰值、最大扩散分辨率和批处理大小等方面。此外,Forge还支持ControlNet和其他新功能,如SVD、Z123、masked Ip-adapter、masked controlnet、photomaker等。