diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0769c70a0..70ff8e15b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,10 @@
Simply set *advanced -> attention guidance* and *advanced -> adaptive scaling*
Additional options are available in *settings -> inference settings -> pag*
*Note*: PAG has replaced SAG as attention guidance method in SD.Next
+ - [LayerDiffuse](https://github.com/rootonchair/diffuser_layerdiffuse)
+ Create transparent images with foreground-only being generated
+ Simply select from scripts -> apply to current model
+ All necessary files will be auto-downloaded on first use
- **IP Adapter Masking**:
Powerful method of using masking with ip-adapters
When combined with multiple ip-adapters, it allows for different inputs guidance for each segment of the input image
@@ -139,7 +143,7 @@
- Add option *timesteps* to manually set timesteps instead of relying on steps+spacing
Additionally, presets from nVidias align-you-steps reasearch are provided
Result is that perfectly aligned steps can drastically reduce number of steps needed!
- For example, AIY preset alows DPM++2M to run in ~10 steps with quality equallying ~30 steps!
+ For example, **AYS** preset alows DPM++2M to run in ~10 steps with quality equallying ~30 steps!
- **IPEX**, thanks @Disty0
- Update to *IPEX 2.1.20* on Linux
requires removing the venv folder to update properly
diff --git a/TODO.md b/TODO.md
index 0909abd9c..3307cfd70 100644
--- a/TODO.md
+++ b/TODO.md
@@ -22,11 +22,12 @@ Thanks to @BinaryQuantumSoul for his hard work on this project!
List, preview, search through all your images and videos!
- **HiDiffusion** allows generating very-high resolution images out-of-the-box using standard models
- **Perturbed-Attention Guidance** (PAG) enhances sample quality in addition to standard CFG scale
+- **LayerDiffuse** simply create transparent (foreground-only) images
- **IP adapter masking** allows to use multiple input images for each segment of the input image
- IP adapter **InstantStyle** implementation
-- **Token Downsampling** (ToDO) provides significant speedups with minimal-to-none quality loss
+- **Token Downsampling** (ToDo) provides significant speedups with minimal-to-none quality loss
- **Samplers optimizations** that allow normal samplers to complete work in 1/3 of the steps!
- Yup, even popular DPM++2M can now run in 10 steps with quality equaling 30 steps
+ Yup, even popular DPM++2M can now run in 10 steps with quality equaling 30 steps using **AYS** presets
- Native **wildcards** support
- Improved built-in **Face HiRes**
- Better **outpainting**
diff --git a/modules/layerdiffuse/__init__.py b/modules/layerdiffuse/__init__.py
new file mode 100644
index 000000000..929ec7418
--- /dev/null
+++ b/modules/layerdiffuse/__init__.py
@@ -0,0 +1,58 @@
+# using https://github.com/rootonchair/diffuser_layerdiffuse
+
+from huggingface_hub import hf_hub_download
+from safetensors.torch import load_file
+from modules.layerdiffuse.layerdiffuse_model import TransparentVAEDecoder
+from modules.layerdiffuse.layerdiffuse_loader import load_lora_to_unet, merge_delta_weights_into_unet
+from modules import shared, errors, devices
+
+
+def apply_layerdiffuse_sd15(pipeline):
+ vae_model_path = hf_hub_download('LayerDiffusion/layerdiffusion-v1', 'layer_sd15_vae_transparent_decoder.safetensors', cache_dir=shared.opts.hfcache_dir)
+ transparent_vae = pipeline.vae
+ transparent_vae.__class__ = TransparentVAEDecoder
+ transparent_vae.set_transparent_decoder(load_file(vae_model_path))
+ pipeline.vae = transparent_vae
+
+ lora_model_path = hf_hub_download('LayerDiffusion/layerdiffusion-v1','layer_sd15_transparent_attn.safetensors', cache_dir=shared.opts.hfcache_dir)
+ load_lora_to_unet(pipeline.unet, lora_model_path, frames=1, device=devices.device, dtype=devices.dtype)
+
+
+def apply_layerdiffuse_sdxl_attn(pipeline):
+ vae_model_path = hf_hub_download('LayerDiffusion/layerdiffusion-v1', 'vae_transparent_decoder.safetensors', cache_dir=shared.opts.hfcache_dir)
+ transparent_vae = pipeline.vae
+ transparent_vae.__class__ = TransparentVAEDecoder
+ transparent_vae.set_transparent_decoder(load_file(vae_model_path))
+ pipeline.vae = transparent_vae
+
+ pipeline.load_lora_weights('rootonchair/diffuser_layerdiffuse', weight_name='diffuser_layer_xl_transparent_attn.safetensors')
+
+
+def apply_layerdiffuse_sdxl_conv(pipeline):
+ model_path = hf_hub_download('LayerDiffusion/layerdiffusion-v1', 'vae_transparent_decoder.safetensors', cache_dir=shared.opts.hfcache_dir)
+ transparent_vae = pipeline.vae
+ transparent_vae.__class__ = TransparentVAEDecoder
+ transparent_vae.set_transparent_decoder(load_file(model_path))
+ pipeline.vae = transparent_vae
+
+ lora_model_path = hf_hub_download('rootonchair/diffuser_layerdiffuse', 'diffuser_layer_xl_transparent_conv.safetensors', cache_dir=shared.opts.hfcache_dir)
+ lora_state_dict = load_file(lora_model_path)
+ merge_delta_weights_into_unet(pipeline, lora_state_dict)
+
+
+def apply_layerdiffuse():
+ try:
+ if shared.sd_model_type == 'sd':
+ shared.log.info(f'LayerDiffuse: class={shared.sd_model.__class__.__name__}')
+ apply_layerdiffuse_sd15(shared.sd_model)
+ elif shared.sd_model_type == 'sdxl':
+ # shared.log.info(f'LayerDiffuse: class={shared.sd_model.__class__.__name__} type=attn')
+ # apply_layerdiffuse_sdxl_attn(shared.sd_model)
+ shared.log.info(f'LayerDiffuse: class={shared.sd_model.__class__.__name__} type=conv')
+ apply_layerdiffuse_sdxl_conv(shared.sd_model)
+ else:
+ shared.log.warning(f'LayerDiffuse: class={shared.sd_model.__class__.__name__} not supported')
+ shared.sd_model.layerdiffusion = True
+ except Exception as e:
+ shared.log.error(f'LayerDiffuse: {e}')
+ errors.display(e, 'LayerDiffuse')
diff --git a/modules/layerdiffuse/layerdiffuse_loader.py b/modules/layerdiffuse/layerdiffuse_loader.py
new file mode 100644
index 000000000..577b006fe
--- /dev/null
+++ b/modules/layerdiffuse/layerdiffuse_loader.py
@@ -0,0 +1,75 @@
+from safetensors.torch import load_file
+from modules.layerdiffuse.layerdiffuse_model import LoraLoader, AttentionSharingProcessor
+
+
+def merge_delta_weights_into_unet(pipe, delta_weights):
+ unet_weights = pipe.unet.state_dict()
+
+ for k in delta_weights.keys():
+ assert k in unet_weights.keys(), k
+
+ for key in delta_weights.keys():
+ dtype = unet_weights[key].dtype
+ unet_weights[key] = unet_weights[key].to(dtype=delta_weights[key].dtype) + delta_weights[key].to(device=unet_weights[key].device)
+ unet_weights[key] = unet_weights[key].to(dtype)
+ pipe.unet.load_state_dict(unet_weights, strict=True)
+ return pipe
+
+
+def get_attr(obj, attr):
+ attrs = attr.split(".")
+ for name in attrs:
+ obj = getattr(obj, name)
+ return obj
+
+
+def load_lora_to_unet(unet, model_path, frames, device, dtype):
+ module_mapping_sd15 = {0: 'input_blocks.1.1.transformer_blocks.0.attn1', 1: 'input_blocks.1.1.transformer_blocks.0.attn2', 2: 'input_blocks.2.1.transformer_blocks.0.attn1', 3: 'input_blocks.2.1.transformer_blocks.0.attn2', 4: 'input_blocks.4.1.transformer_blocks.0.attn1', 5: 'input_blocks.4.1.transformer_blocks.0.attn2', 6: 'input_blocks.5.1.transformer_blocks.0.attn1', 7: 'input_blocks.5.1.transformer_blocks.0.attn2', 8: 'input_blocks.7.1.transformer_blocks.0.attn1', 9: 'input_blocks.7.1.transformer_blocks.0.attn2', 10: 'input_blocks.8.1.transformer_blocks.0.attn1', 11: 'input_blocks.8.1.transformer_blocks.0.attn2', 12: 'output_blocks.3.1.transformer_blocks.0.attn1', 13: 'output_blocks.3.1.transformer_blocks.0.attn2', 14: 'output_blocks.4.1.transformer_blocks.0.attn1', 15: 'output_blocks.4.1.transformer_blocks.0.attn2', 16: 'output_blocks.5.1.transformer_blocks.0.attn1', 17: 'output_blocks.5.1.transformer_blocks.0.attn2', 18: 'output_blocks.6.1.transformer_blocks.0.attn1', 19: 'output_blocks.6.1.transformer_blocks.0.attn2', 20: 'output_blocks.7.1.transformer_blocks.0.attn1', 21: 'output_blocks.7.1.transformer_blocks.0.attn2', 22: 'output_blocks.8.1.transformer_blocks.0.attn1', 23: 'output_blocks.8.1.transformer_blocks.0.attn2', 24: 'output_blocks.9.1.transformer_blocks.0.attn1', 25: 'output_blocks.9.1.transformer_blocks.0.attn2', 26: 'output_blocks.10.1.transformer_blocks.0.attn1', 27: 'output_blocks.10.1.transformer_blocks.0.attn2', 28: 'output_blocks.11.1.transformer_blocks.0.attn1', 29: 'output_blocks.11.1.transformer_blocks.0.attn2', 30: 'middle_block.1.transformer_blocks.0.attn1', 31: 'middle_block.1.transformer_blocks.0.attn2'}
+
+ sd15_to_diffusers = {
+ 'input_blocks.1.1.transformer_blocks.0.attn1': 'down_blocks.0.attentions.0.transformer_blocks.0.attn1',
+ 'input_blocks.1.1.transformer_blocks.0.attn2': 'down_blocks.0.attentions.0.transformer_blocks.0.attn2',
+ 'input_blocks.2.1.transformer_blocks.0.attn1': 'down_blocks.0.attentions.1.transformer_blocks.0.attn1',
+ 'input_blocks.2.1.transformer_blocks.0.attn2': 'down_blocks.0.attentions.1.transformer_blocks.0.attn2',
+ 'input_blocks.4.1.transformer_blocks.0.attn1': 'down_blocks.1.attentions.0.transformer_blocks.0.attn1',
+ 'input_blocks.4.1.transformer_blocks.0.attn2': 'down_blocks.1.attentions.0.transformer_blocks.0.attn2',
+ 'input_blocks.5.1.transformer_blocks.0.attn1': 'down_blocks.1.attentions.1.transformer_blocks.0.attn1',
+ 'input_blocks.5.1.transformer_blocks.0.attn2': 'down_blocks.1.attentions.1.transformer_blocks.0.attn2',
+ 'input_blocks.7.1.transformer_blocks.0.attn1': 'down_blocks.2.attentions.0.transformer_blocks.0.attn1',
+ 'input_blocks.7.1.transformer_blocks.0.attn2': 'down_blocks.2.attentions.0.transformer_blocks.0.attn2',
+ 'input_blocks.8.1.transformer_blocks.0.attn1': 'down_blocks.2.attentions.1.transformer_blocks.0.attn1',
+ 'input_blocks.8.1.transformer_blocks.0.attn2': 'down_blocks.2.attentions.1.transformer_blocks.0.attn2',
+ 'output_blocks.3.1.transformer_blocks.0.attn1': "up_blocks.1.attentions.0.transformer_blocks.0.attn1",
+ 'output_blocks.3.1.transformer_blocks.0.attn2': "up_blocks.1.attentions.0.transformer_blocks.0.attn2",
+ 'output_blocks.4.1.transformer_blocks.0.attn1': "up_blocks.1.attentions.1.transformer_blocks.0.attn1",
+ 'output_blocks.4.1.transformer_blocks.0.attn2': "up_blocks.1.attentions.1.transformer_blocks.0.attn2",
+ 'output_blocks.5.1.transformer_blocks.0.attn1': "up_blocks.1.attentions.2.transformer_blocks.0.attn1",
+ 'output_blocks.5.1.transformer_blocks.0.attn2': "up_blocks.1.attentions.2.transformer_blocks.0.attn2",
+ 'output_blocks.6.1.transformer_blocks.0.attn1': "up_blocks.2.attentions.0.transformer_blocks.0.attn1",
+ 'output_blocks.6.1.transformer_blocks.0.attn2': "up_blocks.2.attentions.0.transformer_blocks.0.attn2",
+ 'output_blocks.7.1.transformer_blocks.0.attn1': "up_blocks.2.attentions.1.transformer_blocks.0.attn1",
+ 'output_blocks.7.1.transformer_blocks.0.attn2': "up_blocks.2.attentions.1.transformer_blocks.0.attn2",
+ 'output_blocks.8.1.transformer_blocks.0.attn1': "up_blocks.2.attentions.2.transformer_blocks.0.attn1",
+ 'output_blocks.8.1.transformer_blocks.0.attn2': "up_blocks.2.attentions.2.transformer_blocks.0.attn2",
+ 'output_blocks.9.1.transformer_blocks.0.attn1': "up_blocks.3.attentions.0.transformer_blocks.0.attn1",
+ 'output_blocks.9.1.transformer_blocks.0.attn2': "up_blocks.3.attentions.0.transformer_blocks.0.attn2",
+ 'output_blocks.10.1.transformer_blocks.0.attn1': "up_blocks.3.attentions.1.transformer_blocks.0.attn1",
+ 'output_blocks.10.1.transformer_blocks.0.attn2': "up_blocks.3.attentions.1.transformer_blocks.0.attn2",
+ 'output_blocks.11.1.transformer_blocks.0.attn1': "up_blocks.3.attentions.2.transformer_blocks.0.attn1",
+ 'output_blocks.11.1.transformer_blocks.0.attn2': "up_blocks.3.attentions.2.transformer_blocks.0.attn2",
+ 'middle_block.1.transformer_blocks.0.attn1': "mid_block.attentions.0.transformer_blocks.0.attn1",
+ 'middle_block.1.transformer_blocks.0.attn2': "mid_block.attentions.0.transformer_blocks.0.attn2",
+ }
+
+ layer_list = []
+ for i in range(32):
+ real_key = module_mapping_sd15[i]
+ diffuser_key = sd15_to_diffusers[real_key]
+ attn_module = get_attr(unet, diffuser_key)
+ u = AttentionSharingProcessor(attn_module, frames=frames, use_control=False).to(device=device, dtype=dtype)
+ layer_list.append(u)
+ attn_module.set_processor(u)
+
+ loader = LoraLoader(layer_list)
+ lora_state_dict = load_file(model_path)
+ loader.load_state_dict(lora_state_dict)
diff --git a/modules/layerdiffuse/layerdiffuse_model.py b/modules/layerdiffuse/layerdiffuse_model.py
new file mode 100644
index 000000000..a0eb71cd2
--- /dev/null
+++ b/modules/layerdiffuse/layerdiffuse_model.py
@@ -0,0 +1,518 @@
+import torch.nn as nn
+import torch
+import cv2
+import numpy as np
+
+import einops
+from tqdm import tqdm
+from typing import Optional, Tuple, Union
+from diffusers import AutoencoderKL
+from diffusers.configuration_utils import ConfigMixin, register_to_config
+from diffusers.models.modeling_utils import ModelMixin
+from diffusers.models.unet_2d_blocks import UNetMidBlock2D, get_down_block, get_up_block
+from diffusers.models.autoencoders.vae import DecoderOutput
+from diffusers.models.attention_processor import Attention, AttnProcessor
+
+
+def zero_module(module):
+ """
+ Zero out the parameters of a module and return it.
+ """
+ for p in module.parameters():
+ p.detach().zero_()
+ return module
+
+
+class LatentTransparencyOffsetEncoder(torch.nn.Module):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.blocks = torch.nn.Sequential(
+ torch.nn.Conv2d(4, 32, kernel_size=3, padding=1, stride=1),
+ nn.SiLU(),
+ torch.nn.Conv2d(32, 32, kernel_size=3, padding=1, stride=1),
+ nn.SiLU(),
+ torch.nn.Conv2d(32, 64, kernel_size=3, padding=1, stride=2),
+ nn.SiLU(),
+ torch.nn.Conv2d(64, 64, kernel_size=3, padding=1, stride=1),
+ nn.SiLU(),
+ torch.nn.Conv2d(64, 128, kernel_size=3, padding=1, stride=2),
+ nn.SiLU(),
+ torch.nn.Conv2d(128, 128, kernel_size=3, padding=1, stride=1),
+ nn.SiLU(),
+ torch.nn.Conv2d(128, 256, kernel_size=3, padding=1, stride=2),
+ nn.SiLU(),
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=1),
+ nn.SiLU(),
+ zero_module(torch.nn.Conv2d(256, 4, kernel_size=3, padding=1, stride=1)),
+ )
+
+ def __call__(self, x):
+ return self.blocks(x)
+
+
+# 1024 * 1024 * 3 -> 16 * 16 * 512 -> 1024 * 1024 * 3
+class UNet1024(ModelMixin, ConfigMixin):
+ @register_to_config
+ def __init__(
+ self,
+ in_channels: int = 3,
+ out_channels: int = 3,
+ down_block_types: Tuple[str] = ("DownBlock2D", "DownBlock2D", "DownBlock2D", "DownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D"),
+ up_block_types: Tuple[str] = ("AttnUpBlock2D", "AttnUpBlock2D", "AttnUpBlock2D", "UpBlock2D", "UpBlock2D", "UpBlock2D", "UpBlock2D"),
+ block_out_channels: Tuple[int] = (32, 32, 64, 128, 256, 512, 512),
+ layers_per_block: int = 2,
+ mid_block_scale_factor: float = 1,
+ downsample_padding: int = 1,
+ downsample_type: str = "conv",
+ upsample_type: str = "conv",
+ dropout: float = 0.0,
+ act_fn: str = "silu",
+ attention_head_dim: Optional[int] = 8,
+ norm_num_groups: int = 4,
+ norm_eps: float = 1e-5,
+ ):
+ super().__init__()
+
+ # input
+ self.conv_in = nn.Conv2d(in_channels, block_out_channels[0], kernel_size=3, padding=(1, 1))
+ self.latent_conv_in = zero_module(nn.Conv2d(4, block_out_channels[2], kernel_size=1))
+
+ self.down_blocks = nn.ModuleList([])
+ self.mid_block = None
+ self.up_blocks = nn.ModuleList([])
+
+ # down
+ output_channel = block_out_channels[0]
+ for i, down_block_type in enumerate(down_block_types):
+ input_channel = output_channel
+ output_channel = block_out_channels[i]
+ is_final_block = i == len(block_out_channels) - 1
+
+ down_block = get_down_block(
+ down_block_type,
+ num_layers=layers_per_block,
+ in_channels=input_channel,
+ out_channels=output_channel,
+ temb_channels=None,
+ add_downsample=not is_final_block,
+ resnet_eps=norm_eps,
+ resnet_act_fn=act_fn,
+ resnet_groups=norm_num_groups,
+ attention_head_dim=attention_head_dim if attention_head_dim is not None else output_channel,
+ downsample_padding=downsample_padding,
+ resnet_time_scale_shift="default",
+ downsample_type=downsample_type,
+ dropout=dropout,
+ )
+ self.down_blocks.append(down_block)
+
+ # mid
+ self.mid_block = UNetMidBlock2D(
+ in_channels=block_out_channels[-1],
+ temb_channels=None,
+ dropout=dropout,
+ resnet_eps=norm_eps,
+ resnet_act_fn=act_fn,
+ output_scale_factor=mid_block_scale_factor,
+ resnet_time_scale_shift="default",
+ attention_head_dim=attention_head_dim if attention_head_dim is not None else block_out_channels[-1],
+ resnet_groups=norm_num_groups,
+ attn_groups=None,
+ add_attention=True,
+ )
+
+ # up
+ reversed_block_out_channels = list(reversed(block_out_channels))
+ output_channel = reversed_block_out_channels[0]
+ for i, up_block_type in enumerate(up_block_types):
+ prev_output_channel = output_channel
+ output_channel = reversed_block_out_channels[i]
+ input_channel = reversed_block_out_channels[min(i + 1, len(block_out_channels) - 1)]
+
+ is_final_block = i == len(block_out_channels) - 1
+
+ up_block = get_up_block(
+ up_block_type,
+ num_layers=layers_per_block + 1,
+ in_channels=input_channel,
+ out_channels=output_channel,
+ prev_output_channel=prev_output_channel,
+ temb_channels=None,
+ add_upsample=not is_final_block,
+ resnet_eps=norm_eps,
+ resnet_act_fn=act_fn,
+ resnet_groups=norm_num_groups,
+ attention_head_dim=attention_head_dim if attention_head_dim is not None else output_channel,
+ resnet_time_scale_shift="default",
+ upsample_type=upsample_type,
+ dropout=dropout,
+ )
+ self.up_blocks.append(up_block)
+ prev_output_channel = output_channel
+
+ # out
+ self.conv_norm_out = nn.GroupNorm(num_channels=block_out_channels[0], num_groups=norm_num_groups, eps=norm_eps)
+ self.conv_act = nn.SiLU()
+ self.conv_out = nn.Conv2d(block_out_channels[0], out_channels, kernel_size=3, padding=1)
+
+ def forward(self, x, latent):
+ sample_latent = self.latent_conv_in(latent)
+ sample = self.conv_in(x)
+ emb = None
+
+ down_block_res_samples = (sample,)
+ for i, downsample_block in enumerate(self.down_blocks):
+ if i == 3:
+ sample = sample + sample_latent
+
+ sample, res_samples = downsample_block(hidden_states=sample, temb=emb)
+ down_block_res_samples += res_samples
+
+ sample = self.mid_block(sample, emb)
+
+ for upsample_block in self.up_blocks:
+ res_samples = down_block_res_samples[-len(upsample_block.resnets) :]
+ down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
+ sample = upsample_block(sample, res_samples, emb)
+
+ sample = self.conv_norm_out(sample)
+ sample = self.conv_act(sample)
+ sample = self.conv_out(sample)
+ return sample
+
+
+def checkerboard(shape):
+ return np.indices(shape).sum(axis=0) % 2
+
+
+class TransparentVAEDecoder(AutoencoderKL):
+ @register_to_config
+ def __init__(
+ self,
+ in_channels: int = 3,
+ out_channels: int = 3,
+ down_block_types: Tuple[str] = ("DownEncoderBlock2D",),
+ up_block_types: Tuple[str] = ("UpDecoderBlock2D",),
+ block_out_channels: Tuple[int] = (64,),
+ layers_per_block: int = 1,
+ act_fn: str = "silu",
+ latent_channels: int = 4,
+ norm_num_groups: int = 32,
+ sample_size: int = 32,
+ scaling_factor: float = 0.18215,
+ latents_mean: Optional[Tuple[float]] = None,
+ latents_std: Optional[Tuple[float]] = None,
+ force_upcast: float = True,
+ ):
+ super().__init__(in_channels, out_channels, down_block_types, up_block_types, block_out_channels, layers_per_block, act_fn, latent_channels, norm_num_groups, sample_size, scaling_factor, latents_mean, latents_std, force_upcast)
+
+ def set_transparent_decoder(self, sd, mod_number=1):
+ model = UNet1024(in_channels=3, out_channels=4)
+ model.load_state_dict(sd, strict=True)
+ model.to(device=self.device, dtype=self.dtype)
+ model.eval()
+
+ self.transparent_decoder = model
+ self.mod_number = mod_number
+
+ def estimate_single_pass(self, pixel, latent):
+ y = self.transparent_decoder(pixel, latent)
+ return y
+
+ def estimate_augmented(self, pixel, latent):
+ args = [
+ [False, 0], [False, 1], [False, 2], [False, 3], [True, 0], [True, 1], [True, 2], [True, 3],
+ ]
+
+ result = []
+
+ for flip, rok in tqdm(args):
+ feed_pixel = pixel.clone()
+ feed_latent = latent.clone()
+
+ if flip:
+ feed_pixel = torch.flip(feed_pixel, dims=(3,))
+ feed_latent = torch.flip(feed_latent, dims=(3,))
+
+ feed_pixel = torch.rot90(feed_pixel, k=rok, dims=(2, 3))
+ feed_latent = torch.rot90(feed_latent, k=rok, dims=(2, 3))
+
+ eps = self.estimate_single_pass(feed_pixel, feed_latent).clip(0, 1)
+ eps = torch.rot90(eps, k=-rok, dims=(2, 3))
+
+ if flip:
+ eps = torch.flip(eps, dims=(3,))
+
+ result += [eps]
+
+ result = torch.stack(result, dim=0)
+ median = torch.median(result, dim=0).values
+ return median
+
+ def decode(self, z: torch.Tensor, return_dict: bool = True, generator=None) -> Union[DecoderOutput, torch.Tensor]:
+ pixel = super().decode(z, return_dict=False, generator=generator)[0]
+ pixel = pixel / 2 + 0.5
+
+
+ result_pixel = []
+ for i in range(int(z.shape[0])):
+ if self.mod_number != 1 and i % self.mod_number != 0:
+ img = torch.cat((pixel[i:i+1], torch.ones_like(pixel[i:i+1,:1,:,:])), dim=1)
+ result_pixel.append(img)
+ continue
+
+ y = self.estimate_augmented(pixel[i:i+1], z[i:i+1])
+
+ y = y.clip(0, 1).movedim(1, -1)
+ alpha = y[..., :1]
+ fg = y[..., 1:]
+
+ B, H, W, C = fg.shape
+ cb = checkerboard(shape=(H // 64, W // 64))
+ cb = cv2.resize(cb, (W, H), interpolation=cv2.INTER_NEAREST)
+ cb = (0.5 + (cb - 0.5) * 0.1)[None, ..., None]
+ cb = torch.from_numpy(cb).to(fg)
+
+ png = torch.cat([fg, alpha], dim=3)
+ png = png.permute(0, 3, 1, 2)
+ result_pixel.append(png)
+
+ result_pixel = torch.cat(result_pixel, dim=0)
+ result_pixel = (result_pixel - 0.5) * 2
+
+ if not return_dict:
+ return (result_pixel, )
+ return DecoderOutput(sample=result_pixel)
+
+
+class TransparentVAEEncoder:
+ def __init__(self, sd, device="cpu", torch_dtype=torch.float32):
+ self.load_device = device
+ self.dtype = torch_dtype
+
+ model = LatentTransparencyOffsetEncoder()
+ model.load_state_dict(sd, strict=True)
+ model.to(device=self.offload_device, dtype=self.dtype)
+ model.eval()
+
+
+class HookerLayers(torch.nn.Module):
+ def __init__(self, layer_list):
+ super().__init__()
+ self.layers = torch.nn.ModuleList(layer_list)
+
+
+class AdditionalAttentionCondsEncoder(torch.nn.Module):
+ def __init__(self):
+ super().__init__()
+
+ self.blocks_0 = torch.nn.Sequential(
+ torch.nn.Conv2d(3, 32, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(32, 32, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(32, 64, kernel_size=3, padding=1, stride=2),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(64, 64, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(64, 128, kernel_size=3, padding=1, stride=2),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(128, 128, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(128, 256, kernel_size=3, padding=1, stride=2),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ ) # 64*64*256
+
+ self.blocks_1 = torch.nn.Sequential(
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=2),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ ) # 32*32*256
+
+ self.blocks_2 = torch.nn.Sequential(
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=2),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ ) # 16*16*256
+
+ self.blocks_3 = torch.nn.Sequential(
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=2),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ ) # 8*8*256
+
+ self.blks = [self.blocks_0, self.blocks_1, self.blocks_2, self.blocks_3]
+
+ def __call__(self, h):
+ results = {}
+ for b in self.blks:
+ h = b(h)
+ results[int(h.shape[2]) * int(h.shape[3])] = h
+ return results
+
+
+class LoraLoader(torch.nn.Module):
+ def __init__(self, layer_list, use_control=False):
+ super().__init__()
+ self.hookers = HookerLayers(layer_list)
+
+ if use_control:
+ self.kwargs_encoder = AdditionalAttentionCondsEncoder()
+ else:
+ self.kwargs_encoder = None
+
+
+class LoRALinearLayer(torch.nn.Module):
+ def __init__(self, in_features: int, out_features: int, rank: int = 256):
+ super().__init__()
+ self.down = torch.nn.Linear(in_features, rank, bias=False)
+ self.up = torch.nn.Linear(rank, out_features, bias=False)
+
+ def forward(self, h, org):
+ org_weight = org.weight.to(h)
+ org_bias = org.bias.to(h) if org.bias is not None else None
+ down_weight = self.down.weight
+ up_weight = self.up.weight
+ final_weight = org_weight + torch.mm(up_weight, down_weight)
+ return torch.nn.functional.linear(h, final_weight, org_bias)
+
+
+class AttentionSharingProcessor(nn.Module):
+ def __init__(self, module, frames=2, use_control=True, rank=256):
+ super().__init__()
+
+ self.heads = module.heads
+ self.frames = frames
+ self.original_module = [module]
+ q_in_channels, q_out_channels = module.to_q.in_features, module.to_q.out_features
+ k_in_channels, k_out_channels = module.to_k.in_features, module.to_k.out_features
+ v_in_channels, v_out_channels = module.to_v.in_features, module.to_v.out_features
+ o_in_channels, o_out_channels = module.to_out[0].in_features, module.to_out[0].out_features
+
+ hidden_size = k_out_channels
+
+ self.to_q_lora = [LoRALinearLayer(q_in_channels, q_out_channels, rank) for _ in range(self.frames)]
+ self.to_k_lora = [LoRALinearLayer(k_in_channels, k_out_channels, rank) for _ in range(self.frames)]
+ self.to_v_lora = [LoRALinearLayer(v_in_channels, v_out_channels, rank) for _ in range(self.frames)]
+ self.to_out_lora = [LoRALinearLayer(o_in_channels, o_out_channels, rank) for _ in range(self.frames)]
+
+ self.to_q_lora = torch.nn.ModuleList(self.to_q_lora)
+ self.to_k_lora = torch.nn.ModuleList(self.to_k_lora)
+ self.to_v_lora = torch.nn.ModuleList(self.to_v_lora)
+ self.to_out_lora = torch.nn.ModuleList(self.to_out_lora)
+
+ self.temporal_i = torch.nn.Linear(in_features=hidden_size, out_features=hidden_size)
+ self.temporal_n = torch.nn.LayerNorm(hidden_size, elementwise_affine=True, eps=1e-6)
+ self.temporal_q = torch.nn.Linear(in_features=hidden_size, out_features=hidden_size)
+ self.temporal_k = torch.nn.Linear(in_features=hidden_size, out_features=hidden_size)
+ self.temporal_v = torch.nn.Linear(in_features=hidden_size, out_features=hidden_size)
+ self.temporal_o = torch.nn.Linear(in_features=hidden_size, out_features=hidden_size)
+
+ self.control_convs = None
+
+ if use_control:
+ self.control_convs = [torch.nn.Sequential(
+ torch.nn.Conv2d(256, 256, kernel_size=3, padding=1, stride=1),
+ torch.nn.SiLU(),
+ torch.nn.Conv2d(256, hidden_size, kernel_size=1),
+ ) for _ in range(self.frames)]
+ self.control_convs = torch.nn.ModuleList(self.control_convs)
+
+ self.control_signals = None
+ self.processor = AttnProcessor()
+
+ def __call__(
+ self,
+ attn: Attention,
+ hidden_states: torch.FloatTensor,
+ encoder_hidden_states: Optional[torch.FloatTensor] = None,
+ attention_mask: Optional[torch.FloatTensor] = None,
+ ) -> torch.Tensor:
+ batch_size, sequence_length, _ = (
+ hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape
+ )
+ attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
+
+ modified_hidden_states = einops.rearrange(hidden_states, '(b f) d c -> f b d c', f=self.frames)
+
+ if self.control_convs is not None:
+ context_dim = int(modified_hidden_states.shape[2])
+ control_outs = []
+ for f in range(self.frames):
+ control_signal = self.control_signals[context_dim].to(modified_hidden_states)
+ control = self.control_convs[f](control_signal)
+ control = einops.rearrange(control, 'b c h w -> b (h w) c')
+ control_outs.append(control)
+ control_outs = torch.stack(control_outs, dim=0)
+ modified_hidden_states = modified_hidden_states + control_outs.to(modified_hidden_states)
+
+ if encoder_hidden_states is None:
+ framed_context = modified_hidden_states
+ else:
+ framed_context = einops.rearrange(encoder_hidden_states, '(b f) d c -> f b d c', f=self.frames)
+
+
+ attn_outs = []
+ for f in range(self.frames):
+ fcf = framed_context[f]
+
+ if encoder_hidden_states is not None:
+ framed_cond_mark = einops.rearrange(torch.ones(batch_size*self.frames), '(b f) -> f b', f=self.frames).to(modified_hidden_states)
+ cond_overwrite = []
+ if len(cond_overwrite) > f:
+ cond_overwrite = cond_overwrite[f]
+ else:
+ cond_overwrite = None
+ if cond_overwrite is not None:
+ cond_mark = framed_cond_mark[f][:, None, None]
+ fcf = cond_overwrite.to(fcf) * (1.0 - cond_mark) + fcf * cond_mark
+
+ query = self.to_q_lora[f](modified_hidden_states[f], attn.to_q)
+ key = self.to_k_lora[f](fcf, attn.to_k)
+ value = self.to_v_lora[f](fcf, attn.to_v)
+
+ query = attn.head_to_batch_dim(query)
+ key = attn.head_to_batch_dim(key)
+ value = attn.head_to_batch_dim(value)
+
+ attention_probs = attn.get_attention_scores(query, key, attention_mask)
+ output = torch.bmm(attention_probs, value)
+ output = attn.batch_to_head_dim(output)
+ output = self.to_out_lora[f](output, attn.to_out[0])
+ output = attn.to_out[1](output)
+ attn_outs.append(output)
+
+ attn_outs = torch.stack(attn_outs, dim=0)
+ modified_hidden_states = modified_hidden_states + attn_outs.to(modified_hidden_states)
+ modified_hidden_states = einops.rearrange(modified_hidden_states, 'f b d c -> (b f) d c', f=self.frames)
+
+ x = modified_hidden_states
+ x = self.temporal_n(x)
+ x = self.temporal_i(x)
+ d = x.shape[1]
+
+ x = einops.rearrange(x, "(b f) d c -> (b d) f c", f=self.frames)
+
+ query = self.temporal_q(x)
+ key = self.temporal_k(x)
+ value = self.temporal_v(x)
+
+ query = attn.head_to_batch_dim(query)
+ key = attn.head_to_batch_dim(key)
+ value = attn.head_to_batch_dim(value)
+
+ attention_probs = attn.get_attention_scores(query, key, attention_mask)
+ x = torch.bmm(attention_probs, value)
+ x = attn.batch_to_head_dim(x)
+
+ x = self.temporal_o(x)
+ x = einops.rearrange(x, "(b d) f c -> (b f) d c", d=d)
+
+ modified_hidden_states = modified_hidden_states + x
+
+ return modified_hidden_states - hidden_states
diff --git a/scripts/layerdiffuse.py b/scripts/layerdiffuse.py
new file mode 100644
index 000000000..54bb78c93
--- /dev/null
+++ b/scripts/layerdiffuse.py
@@ -0,0 +1,36 @@
+import gradio as gr
+from modules import scripts, shared
+
+
+class Script(scripts.Script):
+
+ def title(self):
+ return 'LayerDiffuse'
+
+ def show(self, is_img2img):
+ return True if shared.backend == shared.Backend.DIFFUSERS else False
+
+ def apply(self):
+ from modules import layerdiffuse
+ if not shared.sd_loaded:
+ shared.log.error('LayerDiffuse: model not loaded')
+ return
+ if shared.sd_model_type != 'sd' and shared.sd_model_type != 'sdxl':
+ shared.log.error(f'LayerDiffuse: incorrect base model: class={shared.sd_model.__class__.__name__} type={shared.sd_model_type}')
+ return
+ if hasattr(shared.sd_model, 'layerdiffusion'):
+ shared.log.warning('LayerDiffuse: already applied')
+ return
+ layerdiffuse.apply_layerdiffuse()
+
+ def ui(self, _is_img2img):
+ with gr.Row():
+ gr.HTML("""
+   LayerDiffuse
+