diff --git a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 index 0f55e98e2..f54a8fc50 160000 --- a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 +++ b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 @@ -1 +1 @@ -Subproject commit 0f55e98e27235984a31fbd38287ba6584c4884c5 +Subproject commit f54a8fc506600340f7955a7251fce0a8fb90185e diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 11d33e181..817155ea7 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 11d33e181523c509c235d1278e94ce61d2d8d366 +Subproject commit 817155ea7a43a78982202a2456088acd6ffd95d5 diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser index 806902a2c..708bd5860 160000 --- a/extensions-builtin/stable-diffusion-webui-images-browser +++ b/extensions-builtin/stable-diffusion-webui-images-browser @@ -1 +1 @@ -Subproject commit 806902a2c6d049308b1f8efd9eaf1fc1c64ac018 +Subproject commit 708bd5860e2432a0021d3aa66fc8fdbff33b2d1a diff --git a/modules/api/api.py b/modules/api/api.py index b3eb04f05..1405c7a63 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -13,9 +13,6 @@ import piexif import piexif.helper import uvicorn import gradio as gr -# from gradio.processing_utils import decode_base64_to_file # gradio 3.23 -# from gradio_client.utils import decode_base64_to_file # gradio 3.28 - from modules import errors, shared, sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing from modules.api.models import * # pylint: disable=unused-wildcard-import, wildcard-import from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images @@ -33,7 +30,7 @@ def upscaler_to_index(name: str): try: return [x.name.lower() for x in shared.sd_upscalers].index(name.lower()) except Exception as e: - raise HTTPException(status_code=400, detail=f"Invalid upscaler, needs to be one of these: {' , '.join([x.name for x in sd_upscalers])}") from e + raise HTTPException(status_code=400, detail=f"Invalid upscaler, needs to be one of these: {' , '.join([x.name for x in shared.sd_upscalers])}") from e def script_name_to_index(name, scripts_list): try: @@ -64,24 +61,24 @@ def decode_base64_to_image(encoding): def encode_pil_to_base64(image): with io.BytesIO() as output_bytes: - if opts.samples_format.lower() == 'png': + if shared.opts.samples_format.lower() == 'png': use_metadata = False encoded_metadata = PngImagePlugin.PngInfo() for k, v in image.info.items(): if isinstance(k, str) and isinstance(v, str): encoded_metadata.add_text(k, v) use_metadata = True - image.save(output_bytes, format="PNG", pnginfo=(encoded_metadata if use_metadata else None), quality=opts.jpeg_quality) + image.save(output_bytes, format="PNG", pnginfo=(encoded_metadata if use_metadata else None), quality=shared.opts.jpeg_quality) - elif opts.samples_format.lower() in ("jpg", "jpeg", "webp"): + elif shared.opts.samples_format.lower() in ("jpg", "jpeg", "webp"): parameters = image.info.get('parameters', None) exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(parameters or "", encoding="unicode") } }) - if opts.samples_format.lower() in ("jpg", "jpeg"): - image.save(output_bytes, format="JPEG", exif = exif_bytes, quality=opts.jpeg_quality) + if shared.opts.samples_format.lower() in ("jpg", "jpeg"): + image.save(output_bytes, format="JPEG", exif = exif_bytes, quality=shared.opts.jpeg_quality) else: - image.save(output_bytes, format="WEBP", exif = exif_bytes, quality=opts.jpeg_quality) + image.save(output_bytes, format="WEBP", exif = exif_bytes, quality=shared.opts.jpeg_quality) else: raise HTTPException(status_code=500, detail="Invalid image format") bytes_data = output_bytes.getvalue() @@ -230,8 +227,8 @@ class Api: with self.queue_lock: p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) p.scripts = script_runner - p.outpath_grids = opts.outdir_grids or opts.outdir_txt2img_grids - p.outpath_samples = opts.outdir_samples or opts.outdir_txt2img_samples + p.outpath_grids = shared.opts.outdir_grids or shared.opts.outdir_txt2img_grids + p.outpath_samples = shared.opts.outdir_samples or shared.opts.outdir_txt2img_samples shared.state.begin() script_args = self.init_script_args(p, txt2imgreq, self.default_script_arg_txt2img, selectable_scripts, selectable_script_idx, script_runner) if selectable_scripts is not None: @@ -278,8 +275,8 @@ class Api: p = StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args) p.init_images = [decode_base64_to_image(x) for x in init_images] p.scripts = script_runner - p.outpath_grids = opts.outdir_img2img_grids - p.outpath_samples = opts.outdir_img2img_samples + p.outpath_grids = shared.opts.outdir_img2img_grids + p.outpath_samples = shared.opts.outdir_img2img_samples shared.state.begin() script_args = self.init_script_args(p, img2imgreq, self.default_script_arg_img2img, selectable_scripts, selectable_script_idx, script_runner) if selectable_scripts is not None: @@ -297,12 +294,9 @@ class Api: def extras_single_image_api(self, req: ExtrasSingleImageRequest): reqDict = setUpscalers(req) - reqDict['image'] = decode_base64_to_image(reqDict['image']) - with self.queue_lock: result = postprocessing.run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", save_output=False, **reqDict) - return ExtrasSingleImageResponse(image=encode_pil_to_base64(result[0][0]), html_info=result[1]) def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): diff --git a/modules/api/models.py b/modules/api/models.py index 21d2c2663..498d8f07c 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, Field, create_model # pylint: disable=no-name-in from typing_extensions import Literal from inflection import underscore from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img -from modules.shared import sd_upscalers, opts, parser +import modules.shared as shared API_NOT_ALLOWED = [ "self", @@ -142,8 +142,8 @@ class ExtrasBaseRequest(BaseModel): upscaling_resize_w: int = Field(default=512, title="Target Width", ge=1, description="Target width for the upscaler to hit. Only used when resize_mode=1.") upscaling_resize_h: int = Field(default=512, title="Target Height", ge=1, description="Target height for the upscaler to hit. Only used when resize_mode=1.") upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the chosen size?") - upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}") - upscaler_2: str = Field(default="None", title="Secondary upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}") + upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in shared.sd_upscalers])}") + upscaler_2: str = Field(default="None", title="Secondary upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in shared.sd_upscalers])}") extras_upscaler_2_visibility: float = Field(default=0, title="Secondary upscaler visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of secondary upscaler, values should be between 0 and 1.") upscale_first: bool = Field(default=False, title="Upscale first", description="Should the upscaler run before restoring faces?") @@ -200,9 +200,9 @@ class PreprocessResponse(BaseModel): info: str = Field(title="Preprocess info", description="Response string from preprocessing task.") fields = {} -for key, metadata in opts.data_labels.items(): - value = opts.data.get(key) - optType = opts.typemap.get(type(metadata.default), type(value)) +for key, metadata in shared.opts.data_labels.items(): + value = shared.opts.data.get(key) + optType = shared.opts.typemap.get(type(metadata.default), type(value)) if metadata is not None: fields.update({key: (Optional[optType], Field( @@ -213,7 +213,7 @@ for key, metadata in opts.data_labels.items(): OptionsModel = create_model("Options", **fields) flags = {} -_options = vars(parser)['_option_string_actions'] +_options = vars(shared.parser)['_option_string_actions'] for key in _options: if _options[key].dest != 'help': flag = _options[key] diff --git a/modules/call_queue.py b/modules/call_queue.py index f8c4a9ce7..2ea136a19 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -22,26 +22,21 @@ def wrap_queued_call(func): def wrap_gradio_gpu_call(func, extra_outputs=None): def f(*args, **kwargs): - # if the first argument is a string that says "task(...)", it is treated as a job id if len(args) > 0 and type(args[0]) == str and args[0][0:5] == "task(" and args[0][-1] == ")": id_task = args[0] progress.add_task_to_queue(id_task) else: id_task = None - with queue_lock: shared.state.begin() progress.start_task(id_task) - try: res = func(*args, **kwargs) progress.record_results(id_task, res) finally: progress.finish_task(id_task) - shared.state.end() - return res return wrap_gradio_call(f, extra_outputs=extra_outputs, add_stats=True) @@ -56,7 +51,13 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False): if shared.cmd_opts.profile: pr = cProfile.Profile() pr.enable() - res = list(func(*args, **kwargs)) + res = func(*args, **kwargs) + if res is None: + msg = "No result returned from function" + shared.log.warning(msg) + res = [None, '', '', f"
{html.escape(msg)}
"] + else: + res = list(res) if shared.cmd_opts.profile: pr.disable() s = io.StringIO() diff --git a/modules/devices.py b/modules/devices.py index 40dc6548d..7f66f0d54 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -72,6 +72,18 @@ def torch_gc(): torch.cuda.ipc_collect() +def test_fp16(): + try: + x = torch.tensor([[1.5,.0,.0,.0]]).to(device).half() + layerNorm = torch.nn.LayerNorm(4, eps=0.00001, elementwise_affine=True, dtype=torch.float16, device=device) + _y = layerNorm(x) + except: + shared.log.warning('Torch FP16 test failed: Forcing FP32 operations') + shared.opts.cuda_dtype = 'FP32' + shared.opts.no_half = True + shared.opts.no_half_vae = True + + def set_cuda_params(): if torch.cuda.is_available(): try: @@ -89,6 +101,7 @@ def set_cuda_params(): pass global dtype, dtype_vae, dtype_unet, unet_needs_upcast # pylint: disable=global-statement # set dtype + test_fp16() if shared.opts.cuda_dtype == 'FP16': dtype = torch.float16 dtype_vae = torch.float16 @@ -105,6 +118,7 @@ def set_cuda_params(): dtype_vae = torch.float32 unet_needs_upcast = shared.opts.upcast_sampling + args = cmd_args.parser.parse_args() if args.use_ipex: cpu = torch.device("xpu") #Use XPU instead of CPU. %20 Perf improvement on weak CPUs. diff --git a/modules/dml/hijack/kdiffusion.py b/modules/dml/hijack/kdiffusion.py index 2eced885f..78bc9f2b5 100644 --- a/modules/dml/hijack/kdiffusion.py +++ b/modules/dml/hijack/kdiffusion.py @@ -1,8 +1,8 @@ import torch from tqdm.auto import tqdm - -from modules.shared import device from k_diffusion import sampling +from modules.shared import device + def dpm_solver_adaptive(self, x, t_start, t_end, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None): noise_sampler = sampling.default_noise_sampler(x) if noise_sampler is None else noise_sampler @@ -86,4 +86,4 @@ def sample_dpm_adaptive(model, x, sigma_min, sigma_max, extra_args=None, callbac sampling.DPMSolver.dpm_solver_adaptive = dpm_solver_adaptive sampling.sample_dpm_fast = sample_dpm_fast -sampling.sample_dpm_adaptive = sample_dpm_adaptive \ No newline at end of file +sampling.sample_dpm_adaptive = sample_dpm_adaptive diff --git a/modules/images.py b/modules/images.py index f23232225..c73c1fd13 100644 --- a/modules/images.py +++ b/modules/images.py @@ -473,7 +473,7 @@ def get_next_sequence_number(path, basename): return result + 1 -def save_image(image, path, basename, seed=None, prompt=None, extension='png', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None): +def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None): """Save an image. Args: @@ -510,16 +510,12 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i if path is None: # set default path to avoid errors when functions are triggered manually or via api and param is not set path = opts.outdir_save - if save_to_dirs is None: save_to_dirs = (grid and opts.grid_save_to_dirs) or (not grid and opts.save_to_dirs and not no_prompt) - if save_to_dirs: dirname = namegen.apply(opts.directories_filename_pattern or "[prompt_words]").lstrip(' ').rstrip('\\ /') path = os.path.join(path, dirname) - os.makedirs(path, exist_ok=True) - if forced_filename is None: if short_filename or seed is None: file_decoration = "" @@ -527,14 +523,10 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i file_decoration = opts.samples_filename_pattern or "[seed]" else: file_decoration = opts.samples_filename_pattern or "[seed]-[prompt_spaces]" - add_number = opts.save_images_add_number or file_decoration == '' - if file_decoration != "" and add_number: file_decoration = "-" + file_decoration - file_decoration = namegen.apply(file_decoration) + suffix - if add_number: basecount = get_next_sequence_number(path, basename) fullfn = None @@ -547,68 +539,71 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i fullfn = os.path.join(path, f"{file_decoration}.{extension}") else: fullfn = os.path.join(path, f"{forced_filename}.{extension}") - pnginfo = existing_info or {} if info is not None: pnginfo[pnginfo_section_name] = info - params = script_callbacks.ImageSaveParams(image, p, fullfn, pnginfo) script_callbacks.before_image_saved_callback(params) image = params.image fullfn = params.filename - exifinfo_data = params.pnginfo.get('UserComment', '') if len(exifinfo_data) > 0: exifinfo_data = exifinfo_data + ', ' + params.pnginfo.get(pnginfo_section_name, '') else: exifinfo_data = params.pnginfo.get(pnginfo_section_name, '') - def _atomically_save_image(image_to_save, filename_without_extension, extension): + def atomically_save_image(image_to_save, filename_without_extension, extension): # save image with .tmp extension to avoid race condition when another process detects new image in the directory temp_file_path = filename_without_extension + ".tmp" image_format = Image.registered_extensions()[extension] - if extension.lower() == '.png': + if image_format == 'PNG': pnginfo_data = PngImagePlugin.PngInfo() if opts.enable_pnginfo: for k, v in params.pnginfo.items(): pnginfo_data.add_text(k, str(v)) image_to_save.save(temp_file_path, format=image_format, quality=opts.jpeg_quality, pnginfo=pnginfo_data) - elif extension.lower() in (".jpg", ".jpeg", ".webp"): + elif image_format == 'JPEG': if image_to_save.mode == 'RGBA': + shared.log.warning('Saving RGBA image as JPEG: Alpha channel will be lost') image_to_save = image_to_save.convert("RGB") elif image_to_save.mode == 'I;16': - image_to_save = image_to_save.point(lambda p: p * 0.0038910505836576).convert("RGB" if extension.lower() == ".webp" else "L") + image_to_save = image_to_save.point(lambda p: p * 0.0038910505836576).convert("L") + image_to_save.save(temp_file_path, format=image_format, quality=opts.jpeg_quality) + if opts.enable_pnginfo: + exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo_data or "", encoding="unicode") } }) + piexif.insert(exif_bytes, temp_file_path) + elif image_format == 'WEBP': + if image_to_save.mode == 'I;16': + image_to_save = image_to_save.point(lambda p: p * 0.0038910505836576).convert("RGB") image_to_save.save(temp_file_path, format=image_format, quality=opts.jpeg_quality, lossless=opts.webp_lossless) if opts.enable_pnginfo: exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo_data or "", encoding="unicode") } }) piexif.insert(exif_bytes, temp_file_path) else: + shared.log.warning(f'Unrecognized image format: {extension} attempting save as {image_format}') image_to_save.save(temp_file_path, format=image_format, quality=opts.jpeg_quality) + os.replace(temp_file_path, filename_without_extension + extension) # atomically rename the file with correct extension - # atomically rename the file with correct extension - os.replace(temp_file_path, filename_without_extension + extension) - - fullfn_without_extension, extension = os.path.splitext(params.filename) + filename, extension = os.path.splitext(params.filename) if hasattr(os, 'statvfs'): max_name_len = os.statvfs(path).f_namemax - fullfn_without_extension = fullfn_without_extension[:max_name_len - max(4, len(extension))] - params.filename = fullfn_without_extension + extension + filename = filename[:max_name_len - max(4, len(extension))] + params.filename = filename + extension fullfn = params.filename - _atomically_save_image(image, fullfn_without_extension, extension) + atomically_save_image(image, filename, extension) image.already_saved_as = fullfn - if opts.save_txt and len(exifinfo_data) > 0: - txt_fullfn = f"{fullfn_without_extension}.txt" - with open(txt_fullfn, "w", encoding="utf8") as file: + filename_txt = f"{filename}.txt" + with open(filename_txt, "w", encoding="utf8") as file: file.write(exifinfo_data + "\n") else: txt_fullfn = None script_callbacks.image_saved_callback(params) - return fullfn, txt_fullfn + def safe_decode_string(s: bytes): remove_prefix = lambda text, prefix: text[len(prefix):] if text.startswith(prefix) else text # pylint: disable=unnecessary-lambda-assignment for encoding in ['utf-8', 'utf-16', 'ascii', 'latin_1', 'cp1252', 'cp437']: # try different encodings @@ -629,6 +624,8 @@ def safe_decode_string(s: bytes): def read_info_from_image(image): items = image.info or {} geninfo = items.pop('parameters', None) + if geninfo is not None and len(geninfo) > 0: + items['UserComment'] = geninfo if "exif" in items: exif = piexif.load(items["exif"]) @@ -662,6 +659,7 @@ Negative prompt: {json_info["uc"]} Steps: {json_info["steps"]}, Sampler: {sampler}, CFG scale: {json_info["scale"]}, Seed: {json_info["seed"]}, Size: {image.width}x{image.height}, Clip skip: 2, ENSD: 31337""" except Exception as e: errors.display(e, 'novelai image parser') + return geninfo, items diff --git a/modules/lora b/modules/lora index ad5f318d0..e6ad3cbc6 160000 --- a/modules/lora +++ b/modules/lora @@ -1 +1 @@ -Subproject commit ad5f318d066c52e5b27306b399bc87e41f2eef2b +Subproject commit e6ad3cbc66130fdc3bf9ecd1e0272969b1d613f7 diff --git a/modules/postprocessing.py b/modules/postprocessing.py index 4e0ee9489..d975f50f8 100644 --- a/modules/postprocessing.py +++ b/modules/postprocessing.py @@ -10,28 +10,27 @@ from modules.shared import opts def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemporaryFile], input_dir, output_dir, show_extras_results, *args, save_output: bool = True): devices.torch_gc() - shared.state.begin() shared.state.job = 'extras' - image_data = [] image_names = [] + image_ext = [] outputs = [] - if extras_mode == 1: for img in image_folder: if isinstance(img, Image.Image): image = img fn = '' + ext = None else: image = Image.open(os.path.abspath(img.name)) - fn = os.path.splitext(img.orig_name)[0] + fn, ext = os.path.splitext(img.orig_name) image_data.append(image) image_names.append(fn) + image_ext.append(ext) elif extras_mode == 2: assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled' assert input_dir, 'input directory not selected' - image_list = shared.listfiles(input_dir) for filename in image_list: try: @@ -40,47 +39,38 @@ def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemp continue image_data.append(image) image_names.append(filename) + image_ext.append(None) else: image_data.append(image) image_names.append(None) - + image_ext.append(None) if extras_mode == 2 and output_dir != '': outpath = output_dir else: outpath = opts.outdir_samples or opts.outdir_extras_samples - infotext = '' - - for image, name in zip(image_data, image_names): + for image, name, ext in zip(image_data, image_names, image_ext): if image is None: continue shared.state.textinfo = name - pp = scripts_postprocessing.PostprocessedImage(image.convert("RGB")) - scripts.scripts_postproc.run(pp, args) - if opts.use_original_name_batch and name is not None: basename = os.path.splitext(os.path.basename(name))[0] else: basename = '' - infotext = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in pp.info.items() if v is not None]) - if opts.enable_pnginfo: _geninfo, items = images.read_info_from_image(image) for k, v in items.items(): pp.image.info[k] = v pp.image.info["postprocessing"] = infotext - if save_output: - images.save_image(pp.image, path=outpath, basename=basename, seed=None, prompt=None, extension=opts.samples_format, info=infotext, short_filename=True, no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=pp.image.info, forced_filename=None) - + images.save_image(pp.image, path=outpath, basename=basename, seed=None, prompt=None, extension=ext or opts.samples_format, info=infotext, short_filename=True, no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=pp.image.info, forced_filename=None) if extras_mode != 2 or show_extras_results: outputs.append(pp.image) devices.torch_gc() - return outputs, ui_common.plaintext_to_html(infotext), '' diff --git a/modules/processing.py b/modules/processing.py index efba7f01e..0d1b52ae9 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -17,7 +17,7 @@ from blendmodes.blend import blendLayers, BlendType import modules.sd_hijack from modules import devices, prompt_parser, masking, sd_samplers, lowvram, generation_parameters_copypaste, script_callbacks, extra_networks, sd_vae_approx, scripts # pylint: disable=unused-import from modules.sd_hijack import model_hijack -from modules.shared import opts, cmd_opts, state, log # pylint: disable=unused-import +from modules.shared import opts, cmd_opts, state, log import modules.shared as shared import modules.paths as paths import modules.face_restoration @@ -611,8 +611,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: with torch.no_grad(), p.sd_model.ema_scope(): with devices.autocast(): p.init(p.all_prompts, p.all_seeds, p.all_subseeds) - - # for OSX, loading the model during sampling changes the generated picture, so it is loaded here if shared.opts.live_previews_enable and opts.show_progress_type == "Approx NN": sd_vae_approx.model() diff --git a/modules/sd_models.py b/modules/sd_models.py index e5f4fb6a2..78e1e222d 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -101,10 +101,13 @@ def list_models(): checkpoints_list.clear() checkpoint_aliases.clear() model_list = modelloader.load_models(model_path=model_path, model_url=None, command_path=shared.opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name=None, ext_blacklist=[".vae.ckpt", ".vae.safetensors"]) - if shared.cmd_opts.ckpt is not None and os.path.exists(shared.cmd_opts.ckpt): - checkpoint_info = CheckpointInfo(shared.cmd_opts.ckpt) - checkpoint_info.register() - shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title + if shared.cmd_opts.ckpt is not None: + if not os.path.exists(shared.cmd_opts.ckpt): + shared.log.warning(f"Requested checkpoint not found: {shared.cmd_opts.ckpt}") + else: + checkpoint_info = CheckpointInfo(shared.cmd_opts.ckpt) + checkpoint_info.register() + shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title elif shared.cmd_opts.ckpt != shared.default_sd_model_file and shared.cmd_opts.ckpt is not None: shared.log.warning(f"Checkpoint not found: {shared.cmd_opts.ckpt}") for filename in sorted(model_list, key=str.lower): @@ -157,7 +160,8 @@ def select_checkpoint(): exit(1) checkpoint_info = next(iter(checkpoints_list.values())) if model_checkpoint is not None: - shared.log.warning(f"Checkpoint {model_checkpoint} not found; loading fallback {checkpoint_info.title}") + shared.log.warning(f"Default checkpoint not found: {model_checkpoint}") + shared.log.warning(f"Loading fallback checkpoint: {checkpoint_info.title}") return checkpoint_info @@ -346,6 +350,8 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None) shared.debug(f'Load model: {checkpoint_info} {already_loaded_state_dict}') from modules import lowvram, sd_hijack checkpoint_info = checkpoint_info or select_checkpoint() + if checkpoint_info is None: + return if timer is None: timer = Timer() current_checkpoint_info = None @@ -389,7 +395,7 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None) if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: lowvram.setup_for_low_vram(sd_model, shared.cmd_opts.medvram) else: - sd_model.to(shared.device) + sd_model.to(devices.device) timer.record("move") shared.debug(f'Model weights moved: {memory_stats()}') sd_hijack.model_hijack.hijack(sd_model) diff --git a/webui.py b/webui.py index 311927b13..1decd4ef7 100644 --- a/webui.py +++ b/webui.py @@ -157,7 +157,8 @@ def load_model(): if shared.sd_model is None: log.error("No stable diffusion model loaded") exit(1) - shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title + else: + shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights())) shared.state.end() startup_timer.record("checkpoint")