diff --git a/docs/features/OUTPAINTING.md b/docs/features/OUTPAINTING.md index 185e8cd2c8..823077edcb 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 55cd5c5435..4cabeb6810 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -561,17 +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 - print(f'>> retrieved seed {seed} and prompt "{prompt}" from {image_path}') + if opt.seed is not None: + seed = opt.seed + elif args.seed >= 0: + seed = args.seed + else: + seed = random.randrange(0, np.iinfo(np.uint32).max) - if not seed: - print('* Could not recover seed for image. Replacing with 42. This will not affect image quality') - seed = 42 + 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 @@ -618,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 fac9fb6caa..a8a5dff4b5 100644 --- a/ldm/invoke/args.py +++ b/ldm/invoke/args.py @@ -864,6 +864,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 b5d42250c5..8b7b977759 100644 --- a/ldm/invoke/restoration/outcrop.py +++ b/ldm/invoke/restoration/outcrop.py @@ -31,8 +31,8 @@ class Outcrop(object): image_callback(img,orig_opt.seed,use_prefix=prefix,**kwargs) result= self.generate.prompt2image( - orig_opt.prompt, - seed = orig_opt.seed, # uncomment to make it deterministic + 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 e60bc892ba..031e56d3fa 100755 --- a/scripts/invoke.py +++ b/scripts/invoke.py @@ -282,7 +282,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, @@ -322,7 +322,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, @@ -620,10 +620,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 @@ -710,7 +716,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