diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7110eb3f2..1d6bea72b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,12 +16,13 @@ Service release addressing all zero-day issues reported so far...
- fix new style filename template
- fix image name template using model name
- fix model path using relative path
-- fix torch-rocm version detection (thanks @xangelix)
+- fix `torch-rocm` and `tensorflow-rocm` version detection (thanks @xangelix)
- fix chainner upscalers color clipping
- force second requirements check on startup
- remove lyco, multiple_tqdm
- enhance extension compatibility for exensions directly importing codeformers
- enhance extension compatibility for exensions directly accessing processing params
+- css fixes
- clearly mark external themes in ui
- new option: *settings -> images -> keep incomplete*
can be used to skip vae decode on aborted/skipped/interrupted image generations
diff --git a/README.md b/README.md
index 13e53421a..98f7cc56c 100644
--- a/README.md
+++ b/README.md
@@ -175,7 +175,7 @@ General goals:
### **Sponsors**
diff --git a/TODO.md b/TODO.md
index 8f83079bb..0c895d17b 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,79 +1,3 @@
# TODO
-## Issues
-
-Stuff to be fixed, in no particular order...
-
-N/A
-
-## Features
-
-Stuff to be added, in no particular order...
-
-- Diffusers:
- - Add ControlNet
- - Add unCLIP model
- - Add Training support
-- Technical debt:
- - Port **A1111** stuff
- - Port `p.all_hr_prompts`
- - Import core repos to reduce dependencies
-- Non-technical:
- - Update Wiki
- - Rename repo: **automatic** -> **sdnext**
-- Backends:
- - PyTorch / XLA
- - Diffusers / ONNX
- - ScaleCrafter
-- New Minor
- - Prompt padding for positive/negative
-- New Major
- - Profile manager (for `config.json` and `ui-config.json`)
- - Multi-user support
- - Image phash and hdash using `imagehash`
- - Model merge using `git-rebasin`
- - Enable refiner-style workflow for `ldm` backend
- - Add `sgm` backend
- - Cache models in VRAM
- - Train:
- - Use `interrogator`
- - Use `rembg`
- - Templates for SD-XL training
- - Lora train UI
-- Redesign
- - New UI
- - New inpainting canvas controls (move from backend to purely frontend)
- - New image browser (move from backend to purely frontend)
- - Change workflows from static/legacy to steps-based
-- Video processing
-
-
-## Investigate
-
-Stuff to be investigated...
-
-## Merge PRs
-
-Pick & merge PRs from main repo...
-
-- up-to-date with: df004be
-- current todo list:
-
-## Integration
-
-Tech that can be integrated as part of the core workflow...
-
-- [Git-ReBasin]([https://github.com/ogkalu2/Merge-Stable-Diffusion-models-without-distortion](https://github.com/vladmandic/automatic/issues/1176))
-- [Null-text inversion](https://github.com/ouhenio/null-text-inversion-colab)
-- [Custom diffusion](https://github.com/guaneec/custom-diffusion-webui), [Custom diffusion](https://www.cs.cmu.edu/~custom-diffusion/)
-- [Dream artist](https://github.com/7eu7d7/DreamArtist-sd-webui-extension)
-- [QuickEmbedding](https://github.com/ethansmith2000/QuickEmbedding)
-- [DragGAN](https://github.com/XingangPan/DragGAN)
-- [LamaCleaner](https://github.com/Sanster/lama-cleaner)
-- [SAG](https://huggingface.co/docs/diffusers/v0.19.3/en/api/pipelines/self_attention_guidance), [SAG](https://github.com/ashen-sensored/sd_webui_SAG)
-- [Localization](https://app.transifex.com/signup/open-source/)
-- `TensorRT`
-
-## Random
-
-- Bunch of stuff:
+ToDo list has moved to [GitHub projects](https://github.com/users/vladmandic/projects)
diff --git a/html/locale_en.json b/html/locale_en.json
index de35d33ec..5a046245f 100644
--- a/html/locale_en.json
+++ b/html/locale_en.json
@@ -237,7 +237,7 @@
{"id":"","label":"latent nothing","localized":"","hint":"fill it with latent space zeroes"},
{"id":"","label":"Whole picture","localized":"","hint":""},
{"id":"","label":"Only masked","localized":"","hint":""},
- {"id":"","label":"Only masked padding, pixels","localized":"","hint":""},
+ {"id":"","label":"Masked padding","localized":"","hint":""},
{"id":"","label":"Scale","localized":"","hint":""},
{"id":"","label":"Unused","localized":"","hint":""},
{"id":"","label":"Image CFG scale","localized":"","hint":""}
diff --git a/installer.py b/installer.py
index e93ae9b01..205f16cee 100644
--- a/installer.py
+++ b/installer.py
@@ -575,7 +575,7 @@ def install_packages():
install('onnxruntime==1.15.1', 'onnxruntime', ignore=True)
install('pi-heif', 'pi_heif', ignore=True)
tensorflow_package = os.environ.get('TENSORFLOW_PACKAGE', 'tensorflow==2.13.0')
- install(tensorflow_package, 'tensorflow', ignore=True)
+ install(tensorflow_package, 'tensorflow-rocm' if 'rocm' in tensorflow_package else 'tensorflow', ignore=True)
# install('nvidia-ml-py', 'pynvml', ignore=True)
bitsandbytes_package = os.environ.get('BITSANDBYTES_PACKAGE', None)
if bitsandbytes_package is not None:
diff --git a/javascript/amethyst-nightfall.css b/javascript/amethyst-nightfall.css
index a8119ed02..6cf91ab63 100644
--- a/javascript/amethyst-nightfall.css
+++ b/javascript/amethyst-nightfall.css
@@ -73,7 +73,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
.block.token-counter span { background-color: #222 !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; }
.tab-nav { zoom: 120%; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; }
.label-wrap { margin: 16px 0px 8px 0px; }
-.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
.gradio-button.tool { border: none; background: none; box-shadow: none; }
#tab_extensions table td, #tab_extensions table th { border: none; padding: 0.5em; }
#tab_extensions table { width: 96vw }
diff --git a/javascript/base.css b/javascript/base.css
index 86ef0f2a7..d6b2c126e 100644
--- a/javascript/base.css
+++ b/javascript/base.css
@@ -71,7 +71,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
#extensions .date{ opacity: 0.85; font-size: 90%; }
/* extra networks */
-.extra-networks > div { margin: 0; gap: 0.2em; border-bottom: none !important; }
+.extra-networks > div { margin: 0; border-bottom: none !important; }
.extra-networks .second-line { display: flex; width: -moz-available; width: -webkit-fill-available; gap: 0.3em; box-shadow: var(--input-shadow); }
.extra-networks .search { flex: 1; }
.extra-networks .description { flex: 3; }
diff --git a/javascript/black-orange.css b/javascript/black-orange.css
index 91eda0540..30e069777 100644
--- a/javascript/black-orange.css
+++ b/javascript/black-orange.css
@@ -78,7 +78,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
.block.token-counter span { background-color: #222 !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; }
.tab-nav { zoom: 120%; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; }
.label-wrap { margin: 16px 0px 8px 0px; }
-.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
.gradio-button.tool { border: none; background: none; box-shadow: none; }
#tab_extensions table td, #tab_extensions table th { border: none; padding: 0.5em; }
#tab_extensions table { width: 96vw }
diff --git a/javascript/black-teal.css b/javascript/black-teal.css
index 87b9d6e4e..bef9280ac 100644
--- a/javascript/black-teal.css
+++ b/javascript/black-teal.css
@@ -83,7 +83,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
.block.token-counter span { background-color: var(--input-background-fill) !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; }
.tab-nav { zoom: 120%; margin-top: 10px; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; }
.label-wrap { margin: 8px 0px 4px 0px; }
-.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
.gradio-button.tool { border: none; background: none; box-shadow: none; filter: hue-rotate(340deg) saturate(0.5); }
#tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: none; padding: 0.5em; }
#tab_extensions table, #tab_config table { width: 96vw }
diff --git a/javascript/invoked.css b/javascript/invoked.css
index eb8960264..6d4150d02 100644
--- a/javascript/invoked.css
+++ b/javascript/invoked.css
@@ -77,7 +77,6 @@ div.tab-nav button.selected {background-color: var(--button-primary-background-f
.label-wrap { background-color: #363c4a; padding: 16px 8px 8px 8px; border-radius: var(--radius-lg); padding-left: 8px !important; }
.small-accordion .label-wrap { padding: 8px 0px 8px 0px; }
.small-accordion .label-wrap .icon { margin-right: 1em; }
-.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
.gradio-button.tool { border: none; box-shadow: none; border-radius: var(--radius-lg);}
button.selected {background: var(--button-primary-background-fill);}
.center.boundedheight.flex {background-color: var(--input-background-fill);}
diff --git a/javascript/light-teal.css b/javascript/light-teal.css
index 0b908c059..7778caded 100644
--- a/javascript/light-teal.css
+++ b/javascript/light-teal.css
@@ -84,7 +84,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
.block.token-counter span { background-color: var(--input-background-fill) !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; }
.tab-nav { zoom: 120%; margin-top: 10px; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; }
.label-wrap { margin: 16px 0px 8px 0px; }
-.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
.gradio-button.tool { border: none; background: none; box-shadow: none; filter: hue-rotate(340deg) saturate(0.5); }
#tab_extensions table td, #tab_extensions table th { border: none; padding: 0.5em; }
#tab_extensions table { width: 96vw }
diff --git a/javascript/midnight-barbie.css b/javascript/midnight-barbie.css
index 67f616f1e..238dac9ff 100644
--- a/javascript/midnight-barbie.css
+++ b/javascript/midnight-barbie.css
@@ -78,7 +78,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
.block.token-counter span { background-color: #222 !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; }
.tab-nav { zoom: 120%; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; }
.label-wrap { margin: 16px 0px 8px 0px; }
-.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
.gradio-button.tool { border: none; background: none; box-shadow: none; }
#tab_extensions table td, #tab_extensions table th { border: none; padding: 0.5em; }
#tab_extensions table { width: 96vw }
diff --git a/javascript/sdnext.css b/javascript/sdnext.css
index fb6a198f3..63859f303 100644
--- a/javascript/sdnext.css
+++ b/javascript/sdnext.css
@@ -38,10 +38,10 @@ tr { border-bottom: none !important; padding: 0.1em 0.5em !important; }
.gradio-html .min { min-height: 0; }
.gradio-html div.wrap { height: 100%; }
.gradio-number { min-width: unset !important; max-width: 5em !important; }
-.gradio-slider { max-width: 50%; margin-right: var(--spacing-sm) !important; }
-.gradio-slider input[type="number"] { width: 6em; margin-left: 0.5em; }
.gradio-textbox { overflow: visible !important; }
-.gradio-radio { padding: 0 !important; }
+.gradio-radio { padding: 0 !important; width: max-content !important; }
+.gradio-slider { margin-right: var(--spacing-sm) !important; width: max-content !important }
+.gradio-slider input[type="number"] { width: 6em; font-size: 0.8rem; height: 20px; margin-left: 0.5em; }
/* custom gradio elements */
.accordion-compact { padding: 8px 0px 4px 0px !important; }
@@ -175,7 +175,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
#extensions .date{ opacity: 0.85; font-size: 90%; }
/* extra networks */
-.extra-networks > div { margin: 0; gap: 0.2em; border-bottom: none !important; }
+.extra-networks > div { margin: 0; border-bottom: none !important; gap: 0.3em 0; }
.extra-networks .second-line { display: flex; width: -moz-available; width: -webkit-fill-available; gap: 0.3em; box-shadow: var(--input-shadow); }
.extra-networks .search { flex: 1; }
.extra-networks .description { flex: 3; }
diff --git a/modules/images.py b/modules/images.py
index 4b0d198a2..937a7c6a7 100644
--- a/modules/images.py
+++ b/modules/images.py
@@ -19,6 +19,7 @@ from PIL import Image, ImageFont, ImageDraw, PngImagePlugin, ExifTags
from modules import sd_samplers, shared, script_callbacks, errors, paths
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS)
+debug = errors.log.info if os.environ.get('SD_PATH_DEBUG', None) is not None else lambda *args, **kwargs: None
try:
@@ -298,7 +299,7 @@ class FilenameGenerator:
'model_name': lambda self: shared.sd_model.sd_checkpoint_info.model_name,
'model_hash': lambda self: shared.sd_model.sd_checkpoint_info.shorthash,
- 'prompt': lambda self: self.prompt,
+ 'prompt': lambda self: self.prompt_full(),
'prompt_no_styles': lambda self: self.prompt_no_style(),
'prompt_words': lambda self: self.prompt_words(),
'prompt_hash': lambda self: hashlib.sha256(self.prompt.encode()).hexdigest()[0:8],
@@ -350,6 +351,9 @@ class FilenameGenerator:
shorthash = hashlib.sha256(img_str).hexdigest()[0:8]
return shorthash
+ def prompt_full(self):
+ return self.prompt_sanitize(self.prompt)
+
def prompt_words(self):
if self.prompt is None:
return ''
@@ -358,7 +362,7 @@ class FilenameGenerator:
no_brackets = re_brackets.sub('', no_network)
words = [x for x in re_nonletters.split(no_brackets or "") if len(x) > 0]
prompt = " ".join(words[0:shared.opts.directories_max_prompt_words])
- return prompt
+ return self.prompt_sanitize(prompt)
def prompt_no_style(self):
if self.p is None or self.prompt is None:
@@ -369,7 +373,7 @@ class FilenameGenerator:
for part in style.split("{prompt}"):
prompt_no_style = prompt_no_style.replace(part, "").replace(", ,", ",")
prompt_no_style = prompt_no_style.replace(style, "")
- return prompt_no_style
+ return self.prompt_sanitize(prompt_no_style)
def datetime(self, *args):
time_datetime = datetime.datetime.now()
@@ -385,20 +389,33 @@ class FilenameGenerator:
formatted_time = time_zone_time.strftime(self.default_time_format)
return formatted_time
+ def prompt_sanitize(self, prompt):
+ invalid_chars = '#<>:\'"\\|?*\n\t\r'
+ sanitized = prompt.translate({ ord(x): '_' for x in invalid_chars }).strip()
+ debug(f'Prompt sanitize: input="{prompt}" output={sanitized}')
+ return sanitized
+
def sanitize(self, filename):
- invalid_chars = '#<>:;"/\\|?*\n\t\r'
- invalid_prefix = ''
- invalid_suffix = '.'
- parts = Path(filename).parts
- for part in parts:
- part = part.translate({ord(x): '_' for x in invalid_chars})
- part = part.lstrip(invalid_prefix)
- part = part.rstrip(invalid_suffix)
- fn = Path(*parts)
- max_length = os.statvfs(__file__).f_namemax if hasattr(os, 'statvfs') else 128
- fn, ext = os.path.splitext(fn)
- fn = fn[:max_length-max(4, len(ext))] + ext
- # shared.log.debug(f'Filename sanitize: input={filename} parts={parts} output={fn}')
+ invalid_chars = '\'"|?*\n\t\r' #
+ invalid_folder = ':'
+ invalid_files = ['CON', 'PRN', 'AUX', 'NUL', 'NULL', 'COM0', 'COM1', 'LPT0', 'LPT1']
+ invalid_prefix = ', '
+ invalid_suffix = '.,_ '
+ fn, ext = os.path.splitext(filename)
+ parts = Path(fn).parts
+ newparts = []
+ for i, part in enumerate(parts):
+ part = part.translate({ ord(x): '_' for x in invalid_chars })
+ if i > 0 or (len(part) >= 2 and part[1] != invalid_folder): # skip drive, otherwise remove
+ part = part.translate({ ord(x): '_' for x in invalid_folder })
+ part = part.lstrip(invalid_prefix).rstrip(invalid_suffix)
+ if part in invalid_files: # reserved names
+ [part := part.replace(word, '_') for word in invalid_files] # pylint: disable=expression-not-assigned
+ newparts.append(part)
+ fn = Path(*newparts)
+ max_length = os.statvfs(__file__).f_namemax - 32 if hasattr(os, 'statvfs') else 230
+ fn = str(fn)[:max_length-max(4, len(ext))].rstrip(invalid_suffix) + ext
+ debug(f'Filename sanitize: input="{filename}" parts={parts} output="{fn}" ext={ext} max={max_length} len={len(fn)}')
return fn
def apply(self, x):
@@ -474,7 +491,10 @@ def atomically_save_image():
pnginfo_data = PngImagePlugin.PngInfo()
for k, v in params.pnginfo.items():
pnginfo_data.add_text(k, str(v))
- image.save(fn, format=image_format, compress_level=6, pnginfo=pnginfo_data if shared.opts.image_metadata else None)
+ try:
+ image.save(fn, format=image_format, compress_level=6, pnginfo=pnginfo_data if shared.opts.image_metadata else None)
+ except Exception as e:
+ shared.log.warning(f'Image save failed: {fn} {e}')
elif image_format == 'JPEG':
if image.mode == 'RGBA':
shared.log.warning('Saving RGBA image as JPEG: Alpha channel will be lost')
@@ -482,7 +502,10 @@ def atomically_save_image():
elif image.mode == 'I;16':
image = image.point(lambda p: p * 0.0038910505836576).convert("L")
exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo, encoding="unicode") } })
- image.save(fn, format=image_format, optimize=True, quality=shared.opts.jpeg_quality, exif=exif_bytes)
+ try:
+ image.save(fn, format=image_format, optimize=True, quality=shared.opts.jpeg_quality, exif=exif_bytes)
+ except Exception as e:
+ shared.log.warning(f'Image save failed: {fn} {e}')
elif image_format == 'WEBP':
if image.mode == 'I;16':
image = image.point(lambda p: p * 0.0038910505836576).convert("RGB")
@@ -563,18 +586,19 @@ def save_image(image, path, basename = '', seed=None, prompt=None, extension=sha
if info is not None:
pnginfo[pnginfo_section_name] = info
params = script_callbacks.ImageSaveParams(image, p, filename, pnginfo)
+ params.filename = namegen.sanitize(filename)
script_callbacks.before_image_saved_callback(params)
exifinfo = params.pnginfo.get('UserComment', '')
exifinfo = (exifinfo + ', ' if len(exifinfo) > 0 else '') + params.pnginfo.get(pnginfo_section_name, '')
- filename = namegen.sanitize(params.filename)
- dirname = os.path.dirname(filename)
+ dirname = os.path.dirname(params.filename)
os.makedirs(dirname, exist_ok=True)
- filename, extension = os.path.splitext(filename)
+ filename, extension = os.path.splitext(params.filename)
filename_txt = f"{filename}.txt" if shared.opts.save_txt and len(exifinfo) > 0 else None
save_queue.put((params.image, filename, extension, params, exifinfo, filename_txt)) # actual save is executed in a thread that polls data from queue
save_queue.join()
-
- params.image.already_saved_as = params.filename
+ if not hasattr(params.image, 'already_saved_as'):
+ debug(f'Image marked: "{params.filename}"')
+ params.image.already_saved_as = params.filename
script_callbacks.image_saved_callback(params)
return params.filename, filename_txt
diff --git a/modules/processing.py b/modules/processing.py
index fffb93455..db1eeda12 100644
--- a/modules/processing.py
+++ b/modules/processing.py
@@ -845,6 +845,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
else:
raise ValueError(f"Unknown backend {shared.backend}")
+ if not shared.opts.keep_incomplete and shared.state.interrupted:
+ x_samples_ddim = []
+
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram and shared.backend == shared.Backend.ORIGINAL:
modules.lowvram.send_everything_to_cpu()
devices.torch_gc()
@@ -945,7 +948,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
index_of_first_image=index_of_first_image,
infotexts=infotexts,
)
- if p.scripts is not None and not shared.state.interrupted:
+ if p.scripts is not None and not (shared.state.interrupted or shared.state.skipped):
p.scripts.postprocess(p, res)
return res
diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py
index 1bcb959e9..84f425f5a 100644
--- a/modules/processing_diffusers.py
+++ b/modules/processing_diffusers.py
@@ -316,17 +316,19 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
else:
pass #Do nothing if compile is disabled
+ def update_sampler(sd_model, second_pass=False):
+ sampler_selection = p.latent_sampler if second_pass else p.sampler_name
+ is_karras_compatible = sd_model.__class__.__init__.__annotations__.get("scheduler", None) == diffusers.schedulers.scheduling_utils.KarrasDiffusionSchedulers
+ if hasattr(sd_model, 'scheduler') and sampler_selection != 'Default' and is_karras_compatible:
+ sampler = sd_samplers.all_samplers_map.get(sampler_selection, None)
+ if sampler is None:
+ sampler = sd_samplers.all_samplers_map.get("UniPC")
+ sd_samplers.create_sampler(sampler.name, sd_model)
+ # TODO extra_generation_params add sampler options
+ # p.extra_generation_params['Sampler options'] = ''
+
recompile_model()
-
- is_karras_compatible = shared.sd_model.__class__.__init__.__annotations__.get("scheduler", None) == diffusers.schedulers.scheduling_utils.KarrasDiffusionSchedulers
- if hasattr(shared.sd_model, 'scheduler') and p.sampler_name != 'Default' and is_karras_compatible:
- sampler = sd_samplers.all_samplers_map.get(p.sampler_name, None)
- if sampler is None:
- sampler = sd_samplers.all_samplers_map.get("UniPC")
- sd_samplers.create_sampler(sampler.name, shared.sd_model)
- # TODO extra_generation_params add sampler options
- # p.extra_generation_params['Sampler options'] = ''
-
+ update_sampler(shared.sd_model)
p.extra_generation_params['Pipeline'] = shared.sd_model.__class__.__name__
if len(getattr(p, 'init_images', [])) > 0:
@@ -403,11 +405,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
p.ops.append('hires')
shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.IMAGE_2_IMAGE)
recompile_model(hires=True)
- if ((not hasattr(shared.sd_model.scheduler, 'name')) or (p.latent_sampler == 'DPM SDE') or (shared.sd_model.scheduler.name != p.latent_sampler)) and (p.latent_sampler != 'Default') and is_karras_compatible:
- sampler = sd_samplers.all_samplers_map.get(p.latent_sampler, None)
- if sampler is None:
- sampler = sd_samplers.all_samplers_map.get("UniPC")
- sd_samplers.create_sampler(sampler.name, shared.sd_model)
+ update_sampler(shared.sd_model, second_pass=True)
hires_args = set_pipeline_args(
model=shared.sd_model,
prompts=[p.refiner_prompt] if len(p.refiner_prompt) > 0 else prompts,
@@ -439,11 +437,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
shared.sd_model.to(devices.cpu)
devices.torch_gc()
- if ((not hasattr(shared.sd_refiner.scheduler, 'name')) or (p.latent_sampler == 'DPM SDE') or (shared.sd_refiner.scheduler.name != p.latent_sampler)) and (p.latent_sampler != 'Default'):
- sampler = sd_samplers.all_samplers_map.get(p.latent_sampler, None)
- if sampler is None:
- sampler = sd_samplers.all_samplers_map.get("UniPC")
- sd_samplers.create_sampler(sampler.name, shared.sd_refiner)
+ update_sampler(shared.sd_refiner, second_pass=True)
if shared.state.interrupted or shared.state.skipped:
return results
diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py
index d538a0da9..db9ad937f 100644
--- a/modules/sd_samplers.py
+++ b/modules/sd_samplers.py
@@ -35,8 +35,8 @@ def find_sampler_config(name):
def visible_sampler_names():
- samplers = [x for x in all_samplers if x.name in shared.opts.show_samplers] if len(shared.opts.show_samplers) > 0 else all_samplers
- return samplers
+ visible_samplers = [x for x in all_samplers if x.name in shared.opts.show_samplers] if len(shared.opts.show_samplers) > 0 else all_samplers
+ return visible_samplers
def create_sampler(name, model):
diff --git a/modules/sd_vae_approx.py b/modules/sd_vae_approx.py
index 3e4b93ddd..bd09529e4 100644
--- a/modules/sd_vae_approx.py
+++ b/modules/sd_vae_approx.py
@@ -71,7 +71,7 @@ def cheap_approximation(sample): # Approximate simple
]).reshape(3, 4, 1, 1)
simple_bias = None
try:
- x_sample = nn.functional.conv2d(sample, simple_weights.to(sample.device, sample.dtype), simple_bias.to(sample.device, sample.dtype)) # pylint: disable=not-callable
+ x_sample = nn.functional.conv2d(sample, simple_weights.to(sample.device, sample.dtype), simple_bias.to(sample.device, sample.dtype) if simple_bias is not None else None) # pylint: disable=not-callable
return x_sample
except Exception as e:
shared.log.error(f'Decode simple: {e}')
diff --git a/modules/ui.py b/modules/ui.py
index 3477a2802..21f816c58 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -725,7 +725,7 @@ def create_ui(startup_timer = None):
with gr.Column():
inpaint_full_res = gr.Radio(label="Inpaint area", choices=["Whole picture", "Only masked"], type="index", value="Whole picture", elem_id="img2img_inpaint_full_res")
with gr.Column():
- inpaint_full_res_padding = gr.Slider(label='Only masked padding, pixels', minimum=0, maximum=256, step=4, value=32, elem_id="img2img_inpaint_full_res_padding")
+ inpaint_full_res_padding = gr.Slider(label='Masked padding', minimum=0, maximum=256, step=4, value=32, elem_id="img2img_inpaint_full_res_padding")
def select_img2img_tab(tab):
return gr.update(visible=tab in [2, 3, 4]), gr.update(visible=tab == 3)
diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py
index a8e93e361..3f5e69b85 100644
--- a/modules/ui_extensions.py
+++ b/modules/ui_extensions.py
@@ -421,7 +421,6 @@ def create_ui():
update_extension_button = gr.Button(elem_id="update_extension_button", visible=False)
with gr.Column(scale=4):
search_text = gr.Text(label="Search")
- info = gr.HTML('Note: After any operation such as install/uninstall or enable/disable, please restart the server')
with gr.Column(scale=1):
sort_column = gr.Dropdown(value="default", label="Sort by", choices=list(sort_ordering.keys()), multiselect=False)
with gr.Column(scale=1):
@@ -429,6 +428,9 @@ def create_ui():
check = gr.Button(value="Update all installed", variant="primary")
apply = gr.Button(value="Apply changes", variant="primary")
list_extensions()
+ gr.HTML('Extension list
⯀ Refesh extension list to download latest list with status
⯀ Check status of an extension by looking at status icon before installing it
⯀ After any operation such as install/uninstall or enable/disable, please restart the server
')
+ gr.HTML('')
+ info = gr.HTML('')
extensions_table = gr.HTML(create_html(search_text.value, sort_column.value))
check.click(
fn=wrap_gradio_gpu_call(check_updates, extra_outputs=[gr.update()]),
diff --git a/modules/ui_prompt_styles.py b/modules/ui_prompt_styles.py
index 9f8a43c7c..c394cd852 100644
--- a/modules/ui_prompt_styles.py
+++ b/modules/ui_prompt_styles.py
@@ -20,9 +20,9 @@ def select_style(name):
def save_style(name, prompt, negative_prompt):
if not name:
return gr.update(visible=False)
- style = styles.PromptStyle(name, prompt, negative_prompt)
+ style = styles.Style(name, prompt, negative_prompt)
shared.prompt_styles.styles[style.name] = style
- shared.prompt_styles.save_styles(shared.styles_filename)
+ shared.prompt_styles.save_styles('')
return gr.update(visible=True)
@@ -30,7 +30,7 @@ def delete_style(name):
if name == "":
return
shared.prompt_styles.styles.pop(name, None)
- shared.prompt_styles.save_styles(shared.styles_filename)
+ shared.prompt_styles.save_styles('')
return '', '', ''
diff --git a/modules/ui_tempdir.py b/modules/ui_tempdir.py
index 5b027fa98..72fe53b7d 100644
--- a/modules/ui_tempdir.py
+++ b/modules/ui_tempdir.py
@@ -4,10 +4,11 @@ from collections import namedtuple
from pathlib import Path
import gradio as gr
from PIL import PngImagePlugin
-from modules import shared
+from modules import shared, errors
Savedfile = namedtuple("Savedfile", ["name"])
+debug = errors.log.info if os.environ.get('SD_PATH_DEBUG', None) is not None else lambda *args, **kwargs: None
def register_tmp_file(gradio, filename):
@@ -45,10 +46,13 @@ def pil_to_temp_file(self, img, dir: str, format="png") -> str: # pylint: disabl
img.save(filename, pnginfo=gr.processing_utils.get_pil_metadata(img))
"""
already_saved_as = getattr(img, 'already_saved_as', None)
- if already_saved_as and os.path.isfile(already_saved_as):
+ exists = os.path.isfile(already_saved_as) if already_saved_as is not None else False
+ debug(f'Image lookup: {already_saved_as} exists={exists}')
+ if already_saved_as and exists:
register_tmp_file(shared.demo, already_saved_as)
file_obj = Savedfile(already_saved_as)
name = file_obj.name
+ debug(f'Image registered: {name}')
return name
if shared.opts.temp_dir != "":
dir = shared.opts.temp_dir
@@ -58,15 +62,14 @@ def pil_to_temp_file(self, img, dir: str, format="png") -> str: # pylint: disabl
if isinstance(key, str) and isinstance(value, str):
metadata.add_text(key, value)
use_metadata = True
- with tempfile.NamedTemporaryFile(delete=False, suffix=".png", dir=dir) as file_obj:
- img.save(file_obj, pnginfo=(metadata if use_metadata else None))
- name = file_obj.name
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png", dir=dir) as tmp:
+ img.save(tmp, pnginfo=(metadata if use_metadata else None))
+ name = tmp.name
shared.log.debug(f'Saving temp: image="{name}"')
return name
# override save to file function so that it also writes PNG info
-# gr.processing_utils.save_pil_to_file = save_pil_to_file # gradio <=3.31.0
gr.components.IOComponent.pil_to_temp_file = pil_to_temp_file # gradio >=3.32.0
def on_tmpdir_changed():
diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py
index 81ef716d1..22eb5699c 100644
--- a/scripts/xyz_grid.py
+++ b/scripts/xyz_grid.py
@@ -444,9 +444,9 @@ class Script(scripts.Script):
else:
return gr.update(), gr.update()
- fill_x_button.click(fn=fill, inputs=[x_type, csv_mode], outputs=[x_values_dropdown])
- fill_y_button.click(fn=fill, inputs=[y_type, csv_mode], outputs=[y_values_dropdown])
- fill_z_button.click(fn=fill, inputs=[z_type, csv_mode], outputs=[z_values_dropdown])
+ fill_x_button.click(fn=fill, inputs=[x_type, csv_mode], outputs=[x_values, x_values_dropdown])
+ fill_y_button.click(fn=fill, inputs=[y_type, csv_mode], outputs=[y_values, y_values_dropdown])
+ fill_z_button.click(fn=fill, inputs=[z_type, csv_mode], outputs=[z_values, z_values_dropdown])
def select_axis(axis_type, axis_values, axis_values_dropdown, csv_mode):
choices = self.current_axis_options[axis_type].choices
@@ -700,3 +700,5 @@ class Script(scripts.Script):
del processed.all_seeds[1]
del processed.infotexts[1]
return processed
+
+print('HERE', [x.name for x in sd_samplers.samplers])
diff --git a/wiki b/wiki
index 58e8d11f9..5e13a66ab 160000
--- a/wiki
+++ b/wiki
@@ -1 +1 @@
-Subproject commit 58e8d11f94503d86f0b9b3f73046c5996a2fc000
+Subproject commit 5e13a66ab9c494f8ac4893d522bfec5c30605cd8