diff --git a/javascript/sdnext.css b/javascript/sdnext.css index f50e9ce94..0738d7988 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -100,8 +100,8 @@ button.custom-button{ border-radius: var(--button-large-radius); padding: var(-- #control_generate_box { min-width: unset; width: 100%; } #txt2img_actions_column, #img2img_actions_column, #control_actions { flex-flow: wrap; justify-content: space-between; } #txt2img_enqueue_wrapper, #img2img_enqueue_wrapper, #control_enqueue_wrapper { min-width: unset !important; width: 32%; } -.interrogate-clip { position: absolute; right: 3em; top: -2.5em; max-width: fit-content; background: none !important; } -.interrogate-blip { position: absolute; right: 1em; top: -2.5em; max-width: fit-content; background: none !important; } +.interrogate-clip { position: absolute; right: 6em; top: 8px; max-width: fit-content; background: none !important; z-index: 50; } +.interrogate-blip { position: absolute; right: 4em; top: 8px; max-width: fit-content; background: none !important; z-index: 50; } .interrogate-col { min-width: 0 !important; max-width: fit-content; margin-right: var(--spacing-xxl); } .interrogate-col>button { flex: 1; width: 7em; max-height: 84px; } #sampler_selection_img2img { margin-top: 1em; } diff --git a/modules/control/run.py b/modules/control/run.py index f0e54c5b2..3ecd9ac6c 100644 --- a/modules/control/run.py +++ b/modules/control/run.py @@ -132,6 +132,10 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_ if p.enable_hr and (p.hr_resize_x == 0 or p.hr_resize_y == 0): p.hr_upscale_to_x, p.hr_upscale_to_y = 8 * int(p.width * p.hr_scale / 8), 8 * int(p.height * p.hr_scale / 8) + if shared.sd_model is None: + shared.log.warning('Model not loaded') + return [], '', '', 'Error: model not loaded' + t0 = time.time() num_units = 0 for u in units: diff --git a/modules/deepbooru.py b/modules/deepbooru.py index 099a32fe8..23ec10caa 100644 --- a/modules/deepbooru.py +++ b/modules/deepbooru.py @@ -47,56 +47,40 @@ class DeepDanbooru: return res def tag_multi(self, pil_image, force_disable_ranks=False): - threshold = shared.opts.interrogate_deepbooru_score_threshold - use_spaces = shared.opts.deepbooru_use_spaces - use_escape = shared.opts.deepbooru_escape - alpha_sort = shared.opts.deepbooru_sort_alpha - include_ranks = shared.opts.interrogate_return_ranks and not force_disable_ranks - if isinstance(pil_image, list): - pil_image = pil_image[0] + pil_image = pil_image[0] if len(pil_image) > 0 else None if isinstance(pil_image, dict) and 'name' in pil_image: pil_image = Image.open(pil_image['name']) - + if pil_image is None: + return '' pic = images.resize_image(2, pil_image.convert("RGB"), 512, 512) a = np.expand_dims(np.array(pic, dtype=np.float32), 0) / 255 - with devices.inference_context(), devices.autocast(): x = torch.from_numpy(a).to(devices.device) y = self.model(x)[0].detach().float().cpu().numpy() - probability_dict = {} - for tag, probability in zip(self.model.tags, y): - if probability < threshold: + if probability < shared.opts.interrogate_deepbooru_score_threshold: continue - if tag.startswith("rating:"): continue - probability_dict[tag] = probability - - if alpha_sort: + if shared.opts.deepbooru_sort_alpha: tags = sorted(probability_dict) else: tags = [tag for tag, _ in sorted(probability_dict.items(), key=lambda x: -x[1])] - res = [] - filtertags = {x.strip().replace(' ', '_') for x in shared.opts.deepbooru_filter_tags.split(",")} - for tag in [x for x in tags if x not in filtertags]: probability = probability_dict[tag] tag_outformat = tag - if use_spaces: + if shared.opts.deepbooru_use_spaces: tag_outformat = tag_outformat.replace('_', ' ') - if use_escape: + if shared.opts.deepbooru_escape: tag_outformat = re.sub(re_special, r'\\\1', tag_outformat) - if include_ranks: + if shared.opts.interrogate_return_ranks and not force_disable_ranks: tag_outformat = f"({tag_outformat}:{probability:.3f})" - res.append(tag_outformat) - return ", ".join(res) diff --git a/modules/face/instantid.py b/modules/face/instantid.py index 9c03452bb..c170e0b26 100644 --- a/modules/face/instantid.py +++ b/modules/face/instantid.py @@ -21,7 +21,7 @@ def instant_id(p: processing.StableDiffusionProcessing, app, source_images, stre shared.log.warning('InstantID: no input images') return None - c = shared.sd_model.__class__.__name__ if shared.sd_model is not None else '' + c = shared.sd_model.__class__.__name__ if shared.sd_loaded else '' if c != 'StableDiffusionXLPipeline': shared.log.warning(f'InstantID invalid base model: current={c} required=StableDiffusionXLPipeline') return None diff --git a/modules/face/photomaker.py b/modules/face/photomaker.py index bcad460b8..9e86316b0 100644 --- a/modules/face/photomaker.py +++ b/modules/face/photomaker.py @@ -11,7 +11,7 @@ def photo_maker(p: processing.StableDiffusionProcessing, input_images, trigger, shared.log.warning('PhotoMaker: no input images') return None - c = shared.sd_model.__class__.__name__ if shared.sd_model is not None else '' + c = shared.sd_model.__class__.__name__ if shared.sd_loaded else '' if c != 'StableDiffusionXLPipeline': shared.log.warning(f'PhotoMaker invalid base model: current={c} required=StableDiffusionXLPipeline') return None diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 4973bb072..4cd5cda69 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -187,7 +187,7 @@ def send_image_and_dimensions(x): return img, w, h -def parse_generation_parameters(infotext): +def parse_generation_parameters(infotext, no_prompt=False): if not isinstance(infotext, str): return {} debug(f'Parse infotext: {infotext}') @@ -236,8 +236,9 @@ def parse_generation_parameters(infotext): params["Full quality"] = False else: params[k] = v - params["Prompt"] = prompt.replace('Prompt:', '').strip() - params["Negative prompt"] = negative.replace('Negative prompt:', '').strip() + if not no_prompt: + params["Prompt"] = prompt.replace('Prompt:', '').strip() + params["Negative prompt"] = negative.replace('Negative prompt:', '').strip() debug(f"Parse: {params}") return params @@ -314,7 +315,7 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp prompt = '' else: shared.log.debug(f'Paste prompt: type="current" prompt="{prompt}"') - params = parse_generation_parameters(prompt) + params = parse_generation_parameters(prompt, no_prompt=False) script_callbacks.infotext_pasted_callback(prompt, params) res = [] applied = {} @@ -324,7 +325,7 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp else: v = params.get(key, None) if v is None: - res.append(gr.update()) + res.append(gr.update()) # triggers update for each gradio component even if there are no updates elif isinstance(v, type_of_gr_update): res.append(v) applied[key] = v @@ -344,6 +345,10 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp if override_settings_component is not None: def paste_settings(params): + params.pop('Prompt', None) + params.pop('Negative prompt', None) + if not params: + gr.Dropdown.update(value=[], choices=[], visible=False) vals = {} for param_name, setting_name in infotext_to_setting_name_mapping: v = params.get(param_name, None) @@ -360,8 +365,10 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp continue vals[param_name] = v vals_pairs = [f"{k}: {v}" for k, v in vals.items()] - shared.log.debug(f'Settings overrides: {vals_pairs}') + if len(vals_pairs) > 0: + shared.log.debug(f'Settings overrides: {vals_pairs}') return gr.Dropdown.update(value=vals_pairs, choices=vals_pairs, visible=len(vals_pairs) > 0) + local_paste_fields = local_paste_fields + [(override_settings_component, paste_settings)] button.click( diff --git a/modules/interrogate.py b/modules/interrogate.py index c795cb7dc..505dd5f7e 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -170,9 +170,11 @@ class InterrogateModels: devices.torch_gc() self.load() if isinstance(pil_image, list): - pil_image = pil_image[0] + pil_image = pil_image[0] if len(pil_image) > 0 else None if isinstance(pil_image, dict) and 'name' in pil_image: pil_image = Image.open(pil_image['name']) + if pil_image is None: + return '' pil_image = pil_image.convert("RGB") caption = self.generate_caption(pil_image) self.send_blip_to_ram() diff --git a/modules/modeldata.py b/modules/modeldata.py index bfb011115..600763635 100644 --- a/modules/modeldata.py +++ b/modules/modeldata.py @@ -47,6 +47,11 @@ class ModelData: # provides shared.sd_model field as a property class Shared(sys.modules[__name__].__class__): + @property + def sd_loaded(self): + import modules.sd_models # pylint: disable=W0621 + return modules.sd_models.model_data.sd_model is not None + @property def sd_model(self): import modules.sd_models # pylint: disable=W0621 diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index c26f0753e..392842449 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -103,7 +103,7 @@ class CFGDenoiser(torch.nn.Module): time.sleep(0.1) # at self.image_cfg_scale == 1.0 produced results for edit model are the same as with normal sampling, # so is_edit_model is set to False to support AND composition. - is_edit_model = (shared.sd_model is not None) and hasattr(shared.sd_model, 'cond_stage_key') and (shared.sd_model.cond_stage_key == "edit") and (self.image_cfg_scale is not None) and (self.image_cfg_scale != 1.0) + is_edit_model = shared.sd_loaded and hasattr(shared.sd_model, 'cond_stage_key') and (shared.sd_model.cond_stage_key == "edit") and (self.image_cfg_scale is not None) and (self.image_cfg_scale != 1.0) conds_list, tensor = prompt_parser.reconstruct_multicond_batch(cond, self.step) uncond = prompt_parser.reconstruct_cond_batch(uncond, self.step) assert not is_edit_model or all(len(conds) == 1 for conds in conds_list), "AND is not supported for InstructPix2Pix checkpoint (unless using Image CFG scale = 1.0)" diff --git a/modules/shared.py b/modules/shared.py index c18db3f93..8546c5ca4 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -1078,6 +1078,7 @@ sd_model: diffusers.DiffusionPipeline = None # dummy and overwritten by class sd_refiner: diffusers.DiffusionPipeline = None # dummy and overwritten by class sd_model_type: str = '' # dummy and overwritten by class sd_refiner_type: str = '' # dummy and overwritten by class +sd_loaded: bool = False # dummy and overwritten by class compiled_model_state = None listfiles = listdir diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 8cc81de4e..3c4c79e6d 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -140,7 +140,7 @@ class EmbeddingDatabase: def get_expected_shape(self): if shared.backend == shared.Backend.DIFFUSERS: return 0 - if shared.sd_model is None: + if shared.sd_loaded: shared.log.error('Model not loaded') return 0 vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1) @@ -151,7 +151,7 @@ class EmbeddingDatabase: embeddings_to_load = [] loaded_embeddings = {} skipped_embeddings = [] - if shared.sd_model is None: + if not shared.sd_loaded: return 0 tokenizer = getattr(shared.sd_model, 'tokenizer', None) tokenizer_2 = getattr(shared.sd_model, 'tokenizer_2', None) @@ -336,7 +336,7 @@ class EmbeddingDatabase: continue def load_textual_inversion_embeddings(self, force_reload=False): - if shared.sd_model is None: + if not shared.sd_loaded: return t0 = time.time() if not force_reload: diff --git a/modules/ui_common.py b/modules/ui_common.py index 6f3566944..0db9ecdc2 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -286,10 +286,14 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None): debug(f'Paste field: tab={tabname} fields={paste_field_names}') for paste_tabname, paste_button in buttons.items(): debug(f'Create output panel: source={tabname} target={paste_tabname} button={paste_button}') - bindings = generation_parameters_copypaste.ParamBinding(paste_button=paste_button, tabname=paste_tabname, source_tabname=tabname, source_image_component=result_gallery, paste_field_names=paste_field_names, source_text_component=generation_info) - - # txt2img_bindings = generation_parameters_copypaste.ParamBinding(paste_button=txt2img_paste, tabname="txt2img", source_text_component=txt2img_prompt, source_image_component=None) - + bindings = generation_parameters_copypaste.ParamBinding( + paste_button=paste_button, + tabname=paste_tabname, + source_tabname=tabname, + source_image_component=result_gallery, + paste_field_names=paste_field_names, + source_text_component=prompt or generation_info + ) generation_parameters_copypaste.register_paste_params_button(bindings) return result_gallery, generation_info, html_info, html_info_formatted, html_log @@ -373,7 +377,7 @@ def update_token_counter(text, steps): from modules import sd_hijack token_count, max_length = max([sd_hijack.model_hijack.get_prompt_lengths(prompt) for prompt in prompts], key=lambda args: args[0]) elif shared.backend == shared.Backend.DIFFUSERS: - if shared.sd_model is not None and hasattr(shared.sd_model, 'tokenizer') and shared.sd_model.tokenizer is not None: + if shared.sd_loaded and hasattr(shared.sd_model, 'tokenizer') and shared.sd_model.tokenizer is not None: has_bos_token = shared.sd_model.tokenizer.bos_token_id is not None has_eos_token = shared.sd_model.tokenizer.eos_token_id is not None ids = [shared.sd_model.tokenizer(prompt) for prompt in prompts] diff --git a/modules/ui_img2img.py b/modules/ui_img2img.py index 1c6f75953..1c43847df 100644 --- a/modules/ui_img2img.py +++ b/modules/ui_img2img.py @@ -155,7 +155,7 @@ def create_ui(): with gr.Group(elem_id="img2img_script_container"): img2img_script_inputs = modules.scripts.scripts_img2img.setup_ui(parent='img2img', accordion=True) - img2img_gallery, img2img_generation_info, img2img_html_info, _img2img_html_info_formatted, img2img_html_log = ui_common.create_output_panel("img2img", prompt=None) + img2img_gallery, img2img_generation_info, img2img_html_info, _img2img_html_info_formatted, img2img_html_log = ui_common.create_output_panel("img2img", prompt=img2img_prompt) ui_common.connect_reuse_seed(seed, reuse_seed, img2img_generation_info, is_subseed=False) ui_common.connect_reuse_seed(subseed, reuse_subseed, img2img_generation_info, is_subseed=True) diff --git a/modules/ui_txt2img.py b/modules/ui_txt2img.py index 18b4db8ae..662ab9c24 100644 --- a/modules/ui_txt2img.py +++ b/modules/ui_txt2img.py @@ -50,7 +50,7 @@ def create_ui(): with gr.Group(elem_id="txt2img_script_container"): txt2img_script_inputs = modules.scripts.scripts_txt2img.setup_ui(parent='txt2img', accordion=True) - txt2img_gallery, txt2img_generation_info, txt2img_html_info, _txt2img_html_info_formatted, txt2img_html_log = ui_common.create_output_panel("txt2img", preview=True, prompt=None) + txt2img_gallery, txt2img_generation_info, txt2img_html_info, _txt2img_html_info_formatted, txt2img_html_log = ui_common.create_output_panel("txt2img", preview=True, prompt=txt2img_prompt) ui_common.connect_reuse_seed(seed, reuse_seed, txt2img_generation_info, is_subseed=False) ui_common.connect_reuse_seed(subseed, reuse_subseed, txt2img_generation_info, is_subseed=True) diff --git a/scripts/animatediff.py b/scripts/animatediff.py index 326af6c47..c41fe9c12 100644 --- a/scripts/animatediff.py +++ b/scripts/animatediff.py @@ -48,14 +48,14 @@ orig_pipe = None # original sd_model pipeline def set_adapter(adapter_name: str = 'None'): - if shared.sd_model is None: + if not shared.sd_loaded: return if shared.backend != shared.Backend.DIFFUSERS: shared.log.warning('AnimateDiff: not in diffusers mode') return global motion_adapter, loaded_adapter, orig_pipe # pylint: disable=global-statement # adapter_name = name if name is not None and isinstance(name, str) else loaded_adapter - if adapter_name is None or adapter_name == 'None' or shared.sd_model is None: + if adapter_name is None or adapter_name == 'None' or not shared.sd_loaded: motion_adapter = None loaded_adapter = None if orig_pipe is not None: diff --git a/scripts/blipdiffusion.py b/scripts/blipdiffusion.py index bff6dd315..f2005a0b3 100644 --- a/scripts/blipdiffusion.py +++ b/scripts/blipdiffusion.py @@ -24,7 +24,7 @@ class Script(scripts.Script): return [source_subject, target_subject, prompt_strength] def run(self, p: processing.StableDiffusionProcessing, source_subject, target_subject, prompt_strength): # pylint: disable=arguments-differ, unused-argument - c = shared.sd_model.__class__.__name__ if shared.sd_model is not None else '' + c = shared.sd_model.__class__.__name__ if shared.sd_loaded else '' if c != 'BlipDiffusionPipeline': shared.log.error(f'{title}: model selected={c} required=BLIPDiffusion') return None diff --git a/scripts/demofusion.py b/scripts/demofusion.py index dd4f0b9d2..eecae05a3 100644 --- a/scripts/demofusion.py +++ b/scripts/demofusion.py @@ -1244,7 +1244,7 @@ class Script(scripts.Script): return [cosine_scale_1, cosine_scale_2, cosine_scale_3, sigma, view_batch_size, stride, multi_decoder] def run(self, p: processing.StableDiffusionProcessing, cosine_scale_1, cosine_scale_2, cosine_scale_3, sigma, view_batch_size, stride, multi_decoder): # pylint: disable=arguments-differ - c = shared.sd_model.__class__.__name__ if shared.sd_model is not None else '' + c = shared.sd_model.__class__.__name__ if shared.sd_loaded else '' if c != 'StableDiffusionXLPipeline': shared.log.warning(f'DemoFusion: pipeline={c} required=StableDiffusionXLPipeline') return None diff --git a/scripts/example.py b/scripts/example.py index 98b198355..a3f52ea7b 100644 --- a/scripts/example.py +++ b/scripts/example.py @@ -88,7 +88,7 @@ class Script(scripts.Script): # Run pipeline def run(self, p: processing.StableDiffusionProcessing, *args): # pylint: disable=arguments-differ # prepare pipeline - c = shared.sd_model.__class__.__name__ if shared.sd_model is not None else '' + c = shared.sd_model.__class__.__name__ if shared.sd_loaded else '' if c != pipeline_base: shared.log.warning(f'{title}: pipeline={c} required={pipeline_base}') return None diff --git a/scripts/stablevideodiffusion.py b/scripts/stablevideodiffusion.py index 56a76189d..41b588eb4 100644 --- a/scripts/stablevideodiffusion.py +++ b/scripts/stablevideodiffusion.py @@ -71,7 +71,7 @@ class Script(scripts.Script): shared.log.error(f'SVD: no checkpoint for {model_name}') modelloader.load_reference(model_path, variant='fp16') c = shared.sd_model.__class__.__name__ - model_loaded = shared.sd_model.sd_checkpoint_info.model_name if shared.sd_model is not None else None + model_loaded = shared.sd_model.sd_checkpoint_info.model_name if shared.sd_loaded else None if model_name != model_loaded or c != 'StableVideoDiffusionPipeline': shared.opts.sd_model_checkpoint = model_path sd_models.reload_model_weights()