From 5561a95232db3887ce02c6103e6fb04f47f86c6f Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Sun, 23 Oct 2022 22:52:32 -0400 Subject: [PATCH 1/5] inpainting fix per PR #1218 - This is a merge of the final version of PR #1218 "Inpainting Improvements" Various merge conflicts made it easier to commit directly. Author: Kyle0654 Co-Author: lstein --- configs/models.yaml | 6 +++--- ldm/invoke/generator/inpaint.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/configs/models.yaml b/configs/models.yaml index 9d6d80084f..46bc2df8b4 100644 --- a/configs/models.yaml +++ b/configs/models.yaml @@ -8,15 +8,15 @@ stable-diffusion-1.4: config: configs/stable-diffusion/v1-inference.yaml weights: models/ldm/stable-diffusion-v1/model.ckpt - vae: models/ldm/stable-diffusion-v1/vae-ft-mse-840000-ema-pruned.ckpt +# vae: models/ldm/stable-diffusion-v1/vae-ft-mse-840000-ema-pruned.ckpt description: Stable Diffusion inference model version 1.4 + default: true width: 512 height: 512 stable-diffusion-1.5: config: configs/stable-diffusion/v1-inference.yaml weights: models/ldm/stable-diffusion-v1/v1-5-pruned-emaonly.ckpt +# vae: models/ldm/stable-diffusion-v1/vae-ft-mse-840000-ema-pruned.ckpt description: Stable Diffusion inference model version 1.5 width: 512 height: 512 - vae: models/ldm/stable-diffusion-v1/vae-ft-mse-840000-ema-pruned.ckpt - default: true diff --git a/ldm/invoke/generator/inpaint.py b/ldm/invoke/generator/inpaint.py index f524f3d236..8fbcf249aa 100644 --- a/ldm/invoke/generator/inpaint.py +++ b/ldm/invoke/generator/inpaint.py @@ -109,7 +109,7 @@ class Inpaint(Img2Img): return make_image - def sample_to_image(self, samples)->Image: + def sample_to_image(self, samples)->Image.Image: gen_result = super().sample_to_image(samples).convert('RGB') if self.pil_image is None or self.pil_mask is None: @@ -142,7 +142,7 @@ class Inpaint(Img2Img): # Blur the mask out (into init image) by specified amount if mask_blur_radius > 0: nm = np.asarray(pil_init_mask, dtype=np.uint8) - nmd = cv.dilate(nm, kernel=np.ones((3,3), dtype=np.uint8), iterations=int(mask_blur_radius / 2)) + nmd = cv.erode(nm, kernel=np.ones((3,3), dtype=np.uint8), iterations=int(mask_blur_radius / 2)) pmd = Image.fromarray(nmd, mode='L') blurred_init_mask = pmd.filter(ImageFilter.BoxBlur(mask_blur_radius)) else: From 9cdd78c6cbf9a23e7dc3818f68acbb123639a963 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Sun, 23 Oct 2022 22:56:58 -0400 Subject: [PATCH 2/5] developer documentation fixes --- ldm/generate.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ldm/generate.py b/ldm/generate.py index ce2331806c..8ffb7110a3 100644 --- a/ldm/generate.py +++ b/ldm/generate.py @@ -110,12 +110,13 @@ still work. The full list of arguments to Generate() are: gr = Generate( # these values are set once and shouldn't be changed - conf = path to configuration file ('configs/models.yaml') - model = symbolic name of the model in the configuration file - precision = float precision to be used + conf:str = path to configuration file ('configs/models.yaml') + model:str = symbolic name of the model in the configuration file + precision:float = float precision to be used + safety_checker:bool = activate safety checker [False] # this value is sticky and maintained between generation calls - sampler_name = ['ddim', 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler', 'k_heun', 'k_lms', 'plms'] // k_lms + sampler_name:str = ['ddim', 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler', 'k_heun', 'k_lms', 'plms'] // k_lms # these are deprecated - use conf and model instead weights = path to model weights ('models/ldm/stable-diffusion-v1/model.ckpt') From 17aee4873470c684c597ec49446d19828406b770 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 21 Oct 2022 17:06:35 -0400 Subject: [PATCH 3/5] fix incorrect handling of single quotes in prompts --- ldm/invoke/args.py | 14 +++++++++++--- scripts/invoke.py | 43 ++++++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/ldm/invoke/args.py b/ldm/invoke/args.py index e2302e4452..561c58f06e 100644 --- a/ldm/invoke/args.py +++ b/ldm/invoke/args.py @@ -172,6 +172,7 @@ class Args(object): command = cmd_string.replace("'", "\\'") try: elements = shlex.split(command) + elements = [x.replace("\\'","'") for x in elements] except ValueError: import sys, traceback print(traceback.format_exc(), file=sys.stderr) @@ -930,7 +931,7 @@ def metadata_loads(metadata) -> list: for image in images: # repack the prompt and variations if 'prompt' in image: - image['prompt'] = ','.join([':'.join([x['prompt'], str(x['weight'])]) for x in image['prompt']]) + image['prompt'] = repack_prompt(image['prompt']) if 'variations' in image: image['variations'] = ','.join([':'.join([str(x['seed']),str(x['weight'])]) for x in image['variations']]) # fix a bit of semantic drift here @@ -938,12 +939,19 @@ def metadata_loads(metadata) -> list: opt = Args() opt._cmd_switches = Namespace(**image) results.append(opt) - except KeyError as e: + except Exception as e: import sys, traceback - print('>> badly-formatted metadata',file=sys.stderr) + print('>> could not read metadata',file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) return results +def repack_prompt(prompt_list:list)->str: + # in the common case of no weighting syntax, just return the prompt as is + if len(prompt_list) > 1: + return ','.join([':'.join([x['prompt'], str(x['weight'])]) for x in prompt_list]) + else: + return prompt_list[0]['prompt'] + # image can either be a file path on disk or a base64-encoded # representation of the file's contents def calculate_init_img_hash(image_string): diff --git a/scripts/invoke.py b/scripts/invoke.py index f4d4f3c4c0..917436cdd1 100644 --- a/scripts/invoke.py +++ b/scripts/invoke.py @@ -799,19 +799,33 @@ def retrieve_dream_command(opt,command,completer): will retrieve and format the dream command used to generate the image, and pop it into the readline buffer (linux, Mac), or print out a comment for cut-and-paste (windows) + Given a wildcard path to a folder with image png files, will retrieve and format the dream command used to generate the images, and save them to a file commands.txt for further processing ''' if len(command) == 0: return + tokens = command.split() if len(tokens) > 1: - outfilepath = tokens[1] - else: - outfilepath = "commands.txt" - + return write_commands(opt,tokens) + + try: + cmd = dream_cmd_from_png(tokens[0]) + except OSError: + print(f'## {path}: file could not be read') + except (KeyError, AttributeError, IndexError): + print(f'## {path}: file has no metadata') + except: + print(f'## {path}: file could not be processed') + if len(cmd)>0: + completer.set_line(cmd) + +def write_commands(opt, tokens:list): file_path = tokens[0] + outfilepath = tokens[1] + dir,basename = os.path.split(file_path) if len(dir) == 0: dir = opt.outdir @@ -826,28 +840,23 @@ def retrieve_dream_command(opt,command,completer): return commands = [] + cmd = None for path in paths: try: cmd = dream_cmd_from_png(path) except OSError: print(f'## {path}: file could not be read') - continue except (KeyError, AttributeError, IndexError): print(f'## {path}: file has no metadata') - continue except: print(f'## {path}: file could not be processed') - continue - - commands.append(f'# {path}') - commands.append(cmd) - - with open(outfilepath, 'w', encoding='utf-8') as f: - f.write('\n'.join(commands)) - print(f'>> File {outfilepath} with commands created') - - if len(commands) == 2: - completer.set_line(commands[1]) + if cmd: + commands.append(f'# {path}') + commands.append(cmd) + if len(commands)>0: + with open(outfilepath, 'w', encoding='utf-8') as f: + f.write('\n'.join(commands)) + print(f'>> File {outfilepath} with commands created') ###################################### From 37d38f196e59843640fc6abb0874b7b8d9f631ac Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Fri, 21 Oct 2022 17:45:15 -0400 Subject: [PATCH 4/5] fix a few more metadata bugs - facetool and upscale arguments now written into metadata - cleaned up handling of !fetch command --- ldm/invoke/args.py | 6 +++--- ldm/invoke/pngwriter.py | 2 +- scripts/invoke.py | 35 +++++++++++++++++------------------ 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/ldm/invoke/args.py b/ldm/invoke/args.py index 561c58f06e..1961b4bb7c 100644 --- a/ldm/invoke/args.py +++ b/ldm/invoke/args.py @@ -113,8 +113,8 @@ PRECISION_CHOICES = [ ] # is there a way to pick this up during git commits? -APP_ID = 'lstein/stable-diffusion' -APP_VERSION = 'v1.15' +APP_ID = 'invoke-ai/InvokeAI' +APP_VERSION = 'v2.02' class ArgFormatter(argparse.RawTextHelpFormatter): # use defined argument order to display usage @@ -847,7 +847,7 @@ def metadata_dumps(opt, # remove any image keys not mentioned in RFC #266 rfc266_img_fields = ['type','postprocessing','sampler','prompt','seed','variations','steps', 'cfg_scale','threshold','perlin','step_number','width','height','extra','strength', - 'init_img','init_mask'] + 'init_img','init_mask','facetool','facetool_strength','upscale'] rfc_dict ={} diff --git a/ldm/invoke/pngwriter.py b/ldm/invoke/pngwriter.py index 8f38e1add0..3a43b9bc92 100644 --- a/ldm/invoke/pngwriter.py +++ b/ldm/invoke/pngwriter.py @@ -38,7 +38,7 @@ class PngWriter: info = PngImagePlugin.PngInfo() info.add_text('Dream', dream_prompt) if metadata: - info.add_text('sd-metadata', json.dumps(metadata)) + info.add_text('sd-metadata', json.dumps(metadata)) image.save(path, 'PNG', pnginfo=info, compress_level=compress_level) return path diff --git a/scripts/invoke.py b/scripts/invoke.py index 917436cdd1..3ddc3821ed 100644 --- a/scripts/invoke.py +++ b/scripts/invoke.py @@ -808,31 +808,29 @@ def retrieve_dream_command(opt,command,completer): return tokens = command.split() - if len(tokens) > 1: - return write_commands(opt,tokens) + dir,basename = os.path.split(tokens[0]) + if len(dir) == 0: + path = os.path.join(opt.outdir,basename) + else: + path = tokens[0] + if len(tokens) > 1: + return write_commands(opt, path, tokens[1]) + + cmd = '' try: - cmd = dream_cmd_from_png(tokens[0]) + cmd = dream_cmd_from_png(path) except OSError: - print(f'## {path}: file could not be read') + print(f'## {tokens[0]}: file could not be read') except (KeyError, AttributeError, IndexError): - print(f'## {path}: file has no metadata') + print(f'## {tokens[0]}: file has no metadata') except: - print(f'## {path}: file could not be processed') + print(f'## {tokens[0]}: file could not be processed') if len(cmd)>0: completer.set_line(cmd) -def write_commands(opt, tokens:list): - file_path = tokens[0] - outfilepath = tokens[1] - +def write_commands(opt, file_path:str, outfilepath:str): dir,basename = os.path.split(file_path) - if len(dir) == 0: - dir = opt.outdir - - outdir,outname = os.path.split(outfilepath) - if len(outdir) == 0: - outfilepath = os.path.join(dir,outname) try: paths = list(Path(dir).glob(basename)) except ValueError: @@ -844,8 +842,6 @@ def write_commands(opt, tokens:list): for path in paths: try: cmd = dream_cmd_from_png(path) - except OSError: - print(f'## {path}: file could not be read') except (KeyError, AttributeError, IndexError): print(f'## {path}: file has no metadata') except: @@ -854,6 +850,9 @@ def write_commands(opt, tokens:list): commands.append(f'# {path}') commands.append(cmd) if len(commands)>0: + dir,basename = os.path.split(outfilepath) + if len(dir)==0: + outfilepath = os.path.join(opt.outdir,basename) with open(outfilepath, 'w', encoding='utf-8') as f: f.write('\n'.join(commands)) print(f'>> File {outfilepath} with commands created') From 3081b6b7dd4c2fb1156e7a99dc461012c4ecda35 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Sun, 23 Oct 2022 23:46:16 -0400 Subject: [PATCH 5/5] fix clipseg install problem; close #1150 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2e85166841..3d17a8bfd3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,4 +35,4 @@ realesrgan git+https://github.com/openai/CLIP.git@main#egg=clip git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k-diffusion git+https://github.com/TencentARC/GFPGAN.git#egg=gfpgan -git+https://github.com/invoke-ai/clipseg.git@models-rename#egg=clipseg +-e git+https://github.com/invoke-ai/clipseg.git@models-rename#egg=clipseg