From 2487040ae329abd690bea84ed23640911c047f4b Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Tue, 8 Nov 2022 17:27:42 +0000 Subject: [PATCH] enhance outcropping with ability to direct contents of new regions - When outcropping an image you can now add a `--new_prompt` option, to specify a new prompt to be used instead of the original one used to generate the image. - Similarly you can provide a new seed using `--seed` (or `-S`). A seed of zero will pick one randomly. - This PR also fixes the crash that happened when trying to outcrop an image that does not contain InvokeAI metadata. --- docs/features/OUTPAINTING.md | 15 +++++++++++++++ ldm/generate.py | 24 ++++++++++++++++-------- ldm/invoke/args.py | 5 +++++ ldm/invoke/restoration/outcrop.py | 4 ++-- scripts/invoke.py | 14 ++++++++++---- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/docs/features/OUTPAINTING.md b/docs/features/OUTPAINTING.md index 122c732605..380467d571 100644 --- a/docs/features/OUTPAINTING.md +++ b/docs/features/OUTPAINTING.md @@ -92,6 +92,21 @@ The new image is larger than the original (576x704) because 64 pixels were added to the top and right sides. You will need enough VRAM to process an image of this size. +#### Outcropping non-InvokeAI images + +You can outcrop an arbitrary image that was not generated by InvokeAI, +but your results will vary. The `inpainting-1.5` model is highly +recommended, but if not feasible, then you may be able to improve the +output by conditioning the outcropping with a text prompt that +describes the scene using the `--new_prompt` argument: + +```bash +invoke> !fix images/vacation.png --outcrop top 128 --new_prompt "family vacation" +``` + +You may also provide a different seed for outcropping to use by passing +`-S`. A seed of "0" will generate a new random seed. + A number of caveats: 1. Although you can specify any pixel values, they will be rounded up to the diff --git a/ldm/generate.py b/ldm/generate.py index d172e97a66..121ccc4e16 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -561,18 +561,22 @@ class Generate: ): # retrieve the seed from the image; seed = None - image_metadata = None prompt = None args = metadata_from_png(image_path) - seed = args.seed - prompt = args.prompt or '' - if seed == 0: - seed = random.randrange(0, np.iinfo(np.uint32).max) - opt.seed = seed - print(f'>> generated new seed {seed} and prompt "{prompt}" for {image_path}') + if opt.seed is not None: + seed = opt.seed + elif args.seed >= 0: + seed = args.seed else: - print(f'>> retrieved seed {seed} and prompt "{prompt}" from {image_path}') + seed = random.randrange(0, np.iinfo(np.uint32).max) + + if opt.prompt is not None: + prompt = opt.prompt + else: + prompt = args.prompt + + print(f'>> using seed {seed} and prompt "{prompt}" for {image_path}') # try to reuse the same filename prefix as the original file. # we take everything up to the first period @@ -619,6 +623,10 @@ class Generate: extend_instructions[direction]=int(pixels) except ValueError: print(f'** invalid extension instruction. Use ..., as in "top 64 left 128 right 64 bottom 64"') + + opt.seed = seed + opt.prompt = prompt + if len(extend_instructions)>0: restorer = Outcrop(image,self,) return restorer.process ( diff --git a/ldm/invoke/args.py b/ldm/invoke/args.py index 3c3b2059d5..0392418d5d 100644 --- a/ldm/invoke/args.py +++ b/ldm/invoke/args.py @@ -866,6 +866,11 @@ class Args(object): default=32, help='When outpainting, the tile size to use for filling outpaint areas', ) + postprocessing_group.add_argument( + '--new_prompt', + type=str, + help='Change the text prompt applied during postprocessing (default, use original generation prompt)', + ) postprocessing_group.add_argument( '-ft', '--facetool', diff --git a/ldm/invoke/restoration/outcrop.py b/ldm/invoke/restoration/outcrop.py index 1a0aaf2c8f..ac0bf41b9e 100644 --- a/ldm/invoke/restoration/outcrop.py +++ b/ldm/invoke/restoration/outcrop.py @@ -32,8 +32,8 @@ class Outcrop(object): image_callback(img,preferred_seed,use_prefix=prefix,**kwargs) result= self.generate.prompt2image( - orig_opt.prompt, - seed = orig_opt.seed if orig_opt.seed>0 else opt.seed, + opt.prompt, + seed = opt.seed if opt.seed else orig_opt.seed, sampler = self.generate.sampler, steps = opt.steps, cfg_scale = opt.cfg_scale, diff --git a/scripts/invoke.py b/scripts/invoke.py index 325a3ca6c4..ad8c5ca062 100755 --- a/scripts/invoke.py +++ b/scripts/invoke.py @@ -277,7 +277,7 @@ def main_loop(gen, opt): filename = f'{prefix}.{use_prefix}.{seed}.png' tm = opt.text_mask[0] th = opt.text_mask[1] if len(opt.text_mask)>1 else 0.5 - formatted_dream_prompt = f'!mask {opt.prompt} -tm {tm} {th}' + formatted_dream_prompt = f'!mask {opt.input_file_path} -tm {tm} {th}' path = file_writer.save_image_and_prompt_to_png( image = image, dream_prompt = formatted_dream_prompt, @@ -317,7 +317,7 @@ def main_loop(gen, opt): tool = re.match('postprocess:(\w+)',opt.last_operation).groups()[0] add_postprocessing_to_metadata( opt, - opt.prompt, + opt.input_file_path, filename, tool, formatted_dream_prompt, @@ -614,10 +614,16 @@ def do_textmask(gen, opt, callback): ) def do_postprocess (gen, opt, callback): - file_path = opt.prompt # treat the prompt as the file pathname + file_path = opt.prompt # treat the prompt as the file pathname + if opt.new_prompt is not None: + opt.prompt = opt.new_prompt + else: + opt.prompt = None + if os.path.dirname(file_path) == '': #basename given file_path = os.path.join(opt.outdir,file_path) + opt.input_file_path = file_path tool=None if opt.facetool_strength > 0: tool = opt.facetool @@ -707,7 +713,7 @@ def prepare_image_metadata( elif len(prior_variations) > 0: formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed) elif operation == 'postprocess': - formatted_dream_prompt = '!fix '+opt.dream_prompt_str(seed=seed) + formatted_dream_prompt = '!fix '+opt.dream_prompt_str(seed=seed,prompt=opt.input_file_path) else: formatted_dream_prompt = opt.dream_prompt_str(seed=seed) return filename,formatted_dream_prompt