diff --git a/README.md b/README.md index 68902491401..dda55f40110 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,13 @@ You may also pass a -v option to generate count variants on the original passing the first generated image back into img2img the requested number of times. It generates interesting variants. +## Seamless Tiling + +The seamless tiling mode causes generated images to seamlessly tile with itself. To use it, add the --seamless option when starting the script which will result in all generated images to tile, or for each dream> prompt as shown here: +``` +dream> "pond garden with lotus by claude monet" --seamless -s100 -n4 +``` + ## GFPGAN and Real-ESRGAN Support The script also provides the ability to do face restoration and @@ -400,7 +407,11 @@ repository and associated paper for details and limitations. # Latest Changes -- v1.13 (3 September 2022) +- v1.14 (In progress) + + - Add "seamless mode" for circular tiling of image. Generates beautiful effects. ([prixt](https://github.com/prixt)) + +- v1.13 (3 September 2022 - Support image variations (see [VARIATIONS](VARIATIONS.md) ([Kevin Gibbons](https://github.com/bakkot) and many contributors and reviewers) - Supports a Google Colab notebook for a standalone server running on Google hardware [Arturo Mendivil](https://github.com/artmen1516) diff --git a/ldm/dream/pngwriter.py b/ldm/dream/pngwriter.py index 2461486b22c..b97cc1470ca 100644 --- a/ldm/dream/pngwriter.py +++ b/ldm/dream/pngwriter.py @@ -59,6 +59,8 @@ def normalize_prompt(self): switches.append(f'-H{opt.height or t2i.height}') switches.append(f'-C{opt.cfg_scale or t2i.cfg_scale}') switches.append(f'-A{opt.sampler_name or t2i.sampler_name}') + if opt.seamless or t2i.seamless: + switches.append(f'--seamless') if opt.init_img: switches.append(f'-I{opt.init_img}') if opt.fit: diff --git a/ldm/dream/server.py b/ldm/dream/server.py index f592457e4c8..37020eba29d 100644 --- a/ldm/dream/server.py +++ b/ldm/dream/server.py @@ -70,7 +70,8 @@ def do_POST(self): steps = int(post_data['steps']) width = int(post_data['width']) height = int(post_data['height']) - fit = 'fit' in post_data + fit = 'fit' in post_data + seamless = 'seamless' in post_data cfgscale = float(post_data['cfgscale']) sampler_name = post_data['sampler'] gfpgan_strength = float(post_data['gfpgan_strength']) if gfpgan_model_exists else 0 @@ -164,6 +165,7 @@ def image_progress(sample, step): gfpgan_strength = gfpgan_strength, upscale = upscale, sampler_name = sampler_name, + seamless = seamless, step_callback=image_progress, image_callback=image_done) else: @@ -185,6 +187,7 @@ def image_progress(sample, step): width = width, height = height, fit = fit, + seamless = seamless, gfpgan_strength=gfpgan_strength, upscale = upscale, step_callback=image_progress, diff --git a/ldm/simplet2i.py b/ldm/simplet2i.py index c32f57c3886..9bce156e18c 100644 --- a/ldm/simplet2i.py +++ b/ldm/simplet2i.py @@ -14,6 +14,7 @@ from tqdm import tqdm, trange from itertools import islice from einops import rearrange, repeat +from torch import nn from torchvision.utils import make_grid from pytorch_lightning import seed_everything from torch import autocast @@ -109,6 +110,7 @@ class T2I: downsampling_factor precision strength + seamless embedding_path The vast majority of these arguments default to reasonable values. @@ -132,6 +134,7 @@ def __init__( precision='autocast', full_precision=False, strength=0.75, # default in scripts/img2img.py + seamless=False, embedding_path=None, device_type = 'cuda', # just to keep track of this parameter when regenerating prompt @@ -153,6 +156,7 @@ def __init__( self.precision = precision self.full_precision = True if choose_torch_device() == 'mps' else full_precision self.strength = strength + self.seamless = seamless self.embedding_path = embedding_path self.device_type = device_type self.model = None # empty for now @@ -217,6 +221,7 @@ def prompt2image( step_callback = None, width = None, height = None, + seamless = False, # these are specific to img2img init_img = None, fit = False, @@ -240,6 +245,7 @@ def prompt2image( width // width of image, in multiples of 64 (512) height // height of image, in multiples of 64 (512) cfg_scale // how strongly the prompt influences the image (7.5) (must be >1) + seamless // whether the generated image should tile init_img // path to an initial image - its dimensions override width and height strength // strength for noising/unnoising init_img. 0.0 preserves image exactly, 1.0 replaces it completely gfpgan_strength // strength for GFPGAN. 0.0 preserves image exactly, 1.0 replaces it completely @@ -268,6 +274,7 @@ def process_image(image,seed): steps = steps or self.steps width = width or self.width height = height or self.height + seamless = seamless or self.seamless cfg_scale = cfg_scale or self.cfg_scale ddim_eta = ddim_eta or self.ddim_eta iterations = iterations or self.iterations @@ -278,6 +285,10 @@ def process_image(image,seed): model = ( self.load_model() ) # will instantiate the model or return it from cache + for m in model.modules(): + if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): + m.padding_mode = 'circular' if seamless else m._orig_padding_mode + assert cfg_scale > 1.0, 'CFG_Scale (-C) must be >1.0' assert ( 0.0 <= strength <= 1.0 @@ -603,6 +614,10 @@ def load_model(self): self._set_sampler() + for m in self.model.modules(): + if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): + m._orig_padding_mode = m.padding_mode + return self.model # returns a tensor filled with random numbers from a normal distribution diff --git a/scripts/dream.py b/scripts/dream.py index b1b9282ec0c..a044962f3b5 100755 --- a/scripts/dream.py +++ b/scripts/dream.py @@ -62,6 +62,7 @@ def main(): grid = opt.grid, # this is solely for recreating the prompt latent_diffusion_weights=opt.laion400m, + seamless=opt.seamless, embedding_path=opt.embedding_path, device_type=opt.device ) @@ -87,6 +88,9 @@ def main(): print(f'{e}. Aborting.') sys.exit(-1) + if opt.seamless: + print(">> changed to seamless tiling mode") + # preload the model tic = time.time() t2i.load_model() @@ -418,6 +422,11 @@ def create_argv_parser(): default='outputs/img-samples', help='Directory to save generated images and a log of prompts and seeds. Default: outputs/img-samples', ) + parser.add_argument( + '--seamless', + action='store_true', + help='Change the model to seamless tiling (circular) mode', + ) parser.add_argument( '--embedding_path', type=str, @@ -540,6 +549,11 @@ def create_cmd_parser(): default=None, help='Directory to save generated images and a log of prompts and seeds', ) + parser.add_argument( + '--seamless', + action='store_true', + help='Change the model to seamless tiling (circular) mode', + ) parser.add_argument( '-i', '--individual', diff --git a/static/dream_web/index.html b/static/dream_web/index.html index bf57afae3f1..49a0fcccd49 100644 --- a/static/dream_web/index.html +++ b/static/dream_web/index.html @@ -37,6 +37,8 @@ + +
- +
@@ -74,7 +76,7 @@ - +