diff --git a/ldm/generate.py b/ldm/generate.py index b0d64cd737..3c43e40db3 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -34,6 +34,7 @@ from ldm.invoke.image_util import InitImageResizer from ldm.invoke.devices import choose_torch_device, choose_precision from ldm.invoke.conditioning import get_uc_and_c from ldm.invoke.model_cache import ModelCache +from ldm.invoke.seamless import configure_model_padding def fix_func(orig): if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): @@ -350,31 +351,8 @@ class Generate: # to the width and height of the image training set width = width or self.width height = height or self.height - - def _conv_forward_asymmetric(self, input, weight, bias): - """ - Patch for Conv2d._conv_forward that supports asymmetric padding - """ - working = nn.functional.pad(input, self.asymmetric_padding['x'], mode=self.asymmetric_padding_mode['x']) - working = nn.functional.pad(working, self.asymmetric_padding['y'], mode=self.asymmetric_padding_mode['y']) - return nn.functional.conv2d(working, weight, bias, self.stride, nn.modules.utils._pair(0), self.dilation, self.groups) - for m in model.modules(): - if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): - if seamless: - m.asymmetric_padding_mode = {} - m.asymmetric_padding = {} - m.asymmetric_padding_mode['x'] = 'circular' if ('x' in seamless_axes) else 'constant' - m.asymmetric_padding['x'] = (m._reversed_padding_repeated_twice[0], m._reversed_padding_repeated_twice[1], 0, 0) - m.asymmetric_padding_mode['y'] = 'circular' if ('y' in seamless_axes) else 'constant' - m.asymmetric_padding['y'] = (0, 0, m._reversed_padding_repeated_twice[2], m._reversed_padding_repeated_twice[3]) - m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d) - else: - m._conv_forward = nn.Conv2d._conv_forward.__get__(m, nn.Conv2d) - if hasattr(m, 'asymmetric_padding_mode'): - del m.asymmetric_padding_mode - if hasattr(m, 'asymmetric_padding'): - del m.asymmetric_padding + configure_model_padding(model, seamless, seamless_axes) assert cfg_scale > 1.0, 'CFG_Scale (-C) must be >1.0' assert threshold >= 0.0, '--threshold must be >=0.0' diff --git a/ldm/invoke/seamless.py b/ldm/invoke/seamless.py new file mode 100644 index 0000000000..2dbe241921 --- /dev/null +++ b/ldm/invoke/seamless.py @@ -0,0 +1,30 @@ +import torch.nn as nn + +def _conv_forward_asymmetric(self, input, weight, bias): + """ + Patch for Conv2d._conv_forward that supports asymmetric padding + """ + working = nn.functional.pad(input, self.asymmetric_padding['x'], mode=self.asymmetric_padding_mode['x']) + working = nn.functional.pad(working, self.asymmetric_padding['y'], mode=self.asymmetric_padding_mode['y']) + return nn.functional.conv2d(working, weight, bias, self.stride, nn.modules.utils._pair(0), self.dilation, self.groups) + +def configure_model_padding(model, seamless, seamless_axes): + """ + Modifies the 2D convolution layers to use a circular padding mode based on the `seamless` and `seamless_axes` options. + """ + for m in model.modules(): + if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): + if seamless: + m.asymmetric_padding_mode = {} + m.asymmetric_padding = {} + m.asymmetric_padding_mode['x'] = 'circular' if ('x' in seamless_axes) else 'constant' + m.asymmetric_padding['x'] = (m._reversed_padding_repeated_twice[0], m._reversed_padding_repeated_twice[1], 0, 0) + m.asymmetric_padding_mode['y'] = 'circular' if ('y' in seamless_axes) else 'constant' + m.asymmetric_padding['y'] = (0, 0, m._reversed_padding_repeated_twice[2], m._reversed_padding_repeated_twice[3]) + m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d) + else: + m._conv_forward = nn.Conv2d._conv_forward.__get__(m, nn.Conv2d) + if hasattr(m, 'asymmetric_padding_mode'): + del m.asymmetric_padding_mode + if hasattr(m, 'asymmetric_padding'): + del m.asymmetric_padding \ No newline at end of file