Compare commits

...

66 Commits

Author SHA1 Message Date
w-e-w cdb88d798d disable img preprocess for extra submit
base64 img
2024-05-01 01:07:22 +09:00
w-e-w d66fa38804 run_postprocessing: allow base64 image as input 2024-05-01 01:05:33 +09:00
w-e-w d36b732f55 util decode_base64_to_image 2024-05-01 01:04:20 +09:00
w-e-w 9d39380705 fix extra batch mode P Transparency
red, green, blue = transparency TypeError: cannot unpack non-iterable int object
2024-04-30 19:17:53 +09:00
AUTOMATIC1111 ddb28b33a3 Merge branch 'master' into dev 2024-04-22 18:01:16 +03:00
AUTOMATIC1111 1c0a0c4c26 Merge branch 'dev' 2024-04-22 18:00:36 +03:00
AUTOMATIC1111 7dfe959f4b update changelog 2024-04-22 18:00:23 +03:00
AUTOMATIC1111 8f64dad282 Merge pull request #15594 from AUTOMATIC1111/fix-get_crop_region_v2
fix get_crop_region_v2
2024-04-22 17:57:39 +03:00
w-e-w 821adc3041 fix get_crop_region_v2
Co-Authored-By: Dowon <ks2515@naver.com>
2024-04-22 23:10:19 +09:00
AUTOMATIC1111 e2b177c508 Merge branch 'dev' 2024-04-22 12:26:24 +03:00
AUTOMATIC1111 e837124f4b changelog 2024-04-22 12:26:05 +03:00
AUTOMATIC1111 3fdc3cfbbf Merge pull request #15591 from AUTOMATIC1111/restore-1.8.0-style-naming-of-scripts
Restore 1.8.0 style naming of scripts
2024-04-22 12:24:06 +03:00
w-e-w e9809de651 restore 1.8.0-style naming of scripts 2024-04-22 18:23:58 +09:00
AUTOMATIC1111 61f6479ea9 restore 1.8.0-style naming of scripts 2024-04-22 12:19:30 +03:00
AUTOMATIC1111 e84703b253 update changelog 2024-04-22 11:59:54 +03:00
AUTOMATIC1111 e4aa0c362e Merge pull request #15587 from AUTOMATIC1111/fix-mistake-in-#15583
fix mistake in #15583
2024-04-22 11:50:34 +03:00
AUTOMATIC1111 a183ea4ba7 undo adding scripts to sys.modules 2024-04-22 11:49:55 +03:00
w-e-w 6c7c176dc9 fix mistake in #15583 2024-04-22 00:10:49 +09:00
AUTOMATIC1111 e6a8d0b4e6 Merge pull request #15583 from AUTOMATIC1111/get_crop_region_v2
get_crop_region_v2
2024-04-21 18:06:40 +03:00
w-e-w db263df5d5 get_crop_region_v2 2024-04-21 19:34:11 +09:00
AUTOMATIC1111 d1998d747d Merge pull request #15531 from thatfuckingbird/fix-mistyped-function-name
fix typo in function call (eror -> error)
2024-04-21 07:43:19 +03:00
AUTOMATIC1111 c0eaeb15af Merge pull request #15532 from huchenlei/fix_module
Fix cls.__module__ value in extension script
2024-04-21 07:42:57 +03:00
AUTOMATIC1111 9bcfb92a00 rename logging from textual inversion to not confuse it with global logging module 2024-04-21 07:41:28 +03:00
AUTOMATIC1111 d74fc56fa5 Merge pull request #15547 from AUTOMATIC1111/numpy-DeprecationWarning-product---prod
numpy DeprecationWarning product -> prod
2024-04-21 07:23:38 +03:00
AUTOMATIC1111 a44ed231c2 Merge pull request #15555 from light-and-ray/fix_x1_upscalers
fix x1 upscalers
2024-04-21 07:22:58 +03:00
AUTOMATIC1111 daae17851a Merge pull request #15560 from AUTOMATIC1111/api-downscale
Remove API upscaling factor limits
2024-04-21 07:22:30 +03:00
AUTOMATIC1111 ce19a7baef Merge pull request #15544 from cabelo/master
Compatibility with Debian 11, Fedora 34+ and openSUSE 15.4+
2024-04-21 07:22:04 +03:00
AUTOMATIC1111 8d6e72dbfa Merge pull request #15561 from speculativemoonstone/fix-launch-git-directories
Allow webui.sh to be runnable from arbitrary directories containing a .git file
2024-04-21 07:21:21 +03:00
AUTOMATIC1111 6f4f6bff6b add more info to the error message for #15567 2024-04-21 07:18:58 +03:00
AUTOMATIC1111 367b823466 Merge pull request #15567 from AUTOMATIC1111/no-image-data-blocks-debug
Hide 'No Image data blocks found.' message
2024-04-21 07:09:27 +03:00
AUTOMATIC1111 c8ac42aad1 Merge pull request #15533 from travisg/callback-fix
fix: remove_callbacks_for_function should also remove from the ordered map
2024-04-21 07:07:58 +03:00
AUTOMATIC1111 449bc7bcf3 Merge pull request #15534 from storyicon/fix-masking
Fix images do not match / Coordinate 'right' is less than 'left'
2024-04-21 07:06:45 +03:00
AUTOMATIC1111 3810413c00 Merge pull request #15581 from AUTOMATIC1111/FilenameGenerator-sampler-scheduler
FilenameGenerator Sampler Scheduler
2024-04-21 07:00:28 +03:00
AUTOMATIC1111 f8f5d6cea2 Merge pull request #15577 from AUTOMATIC1111/api-get-schedulers
Add schedulers API endpoint
2024-04-21 06:59:56 +03:00
AUTOMATIC1111 cde35bebe9 Merge pull request #15582 from kaanyalova/avif-support
Add avif support
2024-04-21 06:59:38 +03:00
kaanyalova 49fee7c8db Add avif support 2024-04-20 23:26:04 +03:00
w-e-w b5b1487f6a FilenameGenerator Sampler Scheduler 2024-04-21 04:52:03 +09:00
missionfloyd 5cb567c138 Add schedulers API endpoint 2024-04-19 20:32:09 -06:00
missionfloyd d212fb59fe Hide 'No Image data blocks found.' message 2024-04-18 20:56:51 -06:00
storyicon 71314e47b1 feat:compatible with inconsistent/empty mask
Signed-off-by: storyicon <storyicon@foxmail.com>
2024-04-18 11:59:25 +00:00
Speculative Moonstone ba2a737cce Allow webui.sh to be runnable from directories containing a .git file 2024-04-18 04:25:32 +00:00
missionfloyd 909c3dfe83 Remove API upscaling factor limits 2024-04-17 21:20:03 -06:00
Andray 9d4fdc45d3 fix x1 upscalers 2024-04-18 01:53:23 +04:00
w-e-w 63fd38a04f numpy DeprecationWarning product -> prod 2024-04-17 15:54:45 +09:00
Alessandro de Oliveira Faria (A.K.A. CABELO) 50190ca669 Compatibility with Debian 11, Fedora 34+ and openSUSE 15.4+ 2024-04-17 00:01:56 -03:00
storyicon 0980fdfe8c fix: images do not match
Signed-off-by: storyicon <storyicon@foxmail.com>
2024-04-16 07:35:33 +00:00
Travis Geiselbrecht bba306d414 fix: remove callbacks properly in remove_callbacks_for_function()
Like remove_current_script_callback just before, also remove from the
ordered_callbacks_map to keep the callback map and ordered callback map
in sync.
2024-04-15 21:10:11 -07:00
huchenlei a95326bec4 nit 2024-04-15 22:34:01 -04:00
huchenlei 0f82948e4f Fix cls.__module__ 2024-04-15 22:14:19 -04:00
thatfuckingbird 8e1c3561be fix typo in function call (eror -> error) 2024-04-15 21:17:24 +02:00
AUTOMATIC1111 ff6f4680c4 Merge branch 'master' into dev 2024-04-13 06:38:58 +03:00
AUTOMATIC1111 3fadb4fc85 Merge pull request #15492 from w-e-w/update-restricted_opts
update restricted_opts
2024-04-11 19:33:55 +03:00
w-e-w 592e40ebe9 update restricted_opts 2024-04-11 23:10:25 +09:00
storyicon 4068429ac7 fix: Coordinate 'right' is less than 'left'
Signed-off-by: storyicon <storyicon@foxmail.com>
2024-04-10 10:53:25 +00:00
AUTOMATIC1111 ac8ffb34e3 Merge pull request #15470 from AUTOMATIC1111/read-infotext-Script-not-found
error handling paste_field callables
2024-04-09 16:00:56 +03:00
w-e-w ef83f6831f catch exception for all paste_fields callable 2024-04-09 21:32:58 +09:00
w-e-w 600f339c4c Warning when Script is not found 2024-04-09 21:32:41 +09:00
AUTOMATIC1111 a976f4dff9 Merge pull request #15460 from AUTOMATIC1111/create_infotext-index-and-callable
create_infotext allow index and callable, re-work Hires prompt infotext
2024-04-09 12:05:02 +03:00
AUTOMATIC1111 c48b6bf6bd Merge pull request #15465 from jordenyt/fix-extras-api-upscale-enabled
Fix extra-single-image API not doing upscale failed
2024-04-09 11:00:30 +03:00
Jorden Tse 2580235c72 Fix extra-single-image API not doing upscale failed 2024-04-09 11:13:47 +08:00
AUTOMATIC1111 d9708c92b4 fix limited file write (thanks, Sylwia) 2024-04-08 16:15:25 +03:00
w-e-w e3aabe6959 add documentation for create_infotext 2024-04-08 20:14:02 +09:00
w-e-w 1e1176b6eb non-serializable as None 2024-04-08 18:18:33 +09:00
w-e-w 219e64489c re-work extra_generation_params for Hires prompt 2024-04-08 18:18:13 +09:00
w-e-w 47ed9b2d39 allow list or callables in generation_params 2024-04-08 01:39:31 +09:00
w-e-w 6efdfe3234 if use use_main_prompt index = 0 2024-04-07 22:58:12 +09:00
20 changed files with 186 additions and 43 deletions
+36 -1
View File
@@ -1,3 +1,39 @@
## 1.9.3
### Bug Fixes:
* fix get_crop_region_v2 ([#15594](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15594))
## 1.9.2
### Extensions and API:
* restore 1.8.0-style naming of scripts
## 1.9.1
### Minor:
* Add avif support ([#15582](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15582))
* Add filename patterns: `[sampler_scheduler]` and `[scheduler]` ([#15581](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15581))
### Extensions and API:
* undo adding scripts to sys.modules
* Add schedulers API endpoint ([#15577](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15577))
* Remove API upscaling factor limits ([#15560](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15560))
### Bug Fixes:
* Fix images do not match / Coordinate 'right' is less than 'left' ([#15534](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15534))
* fix: remove_callbacks_for_function should also remove from the ordered map ([#15533](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15533))
* fix x1 upscalers ([#15555](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15555))
* Fix cls.__module__ value in extension script ([#15532](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15532))
* fix typo in function call (eror -> error) ([#15531](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15531))
### Other:
* Hide 'No Image data blocks found.' message ([#15567](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15567))
* Allow webui.sh to be runnable from arbitrary directories containing a .git file ([#15561](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15561))
* Compatibility with Debian 11, Fedora 34+ and openSUSE 15.4+ ([#15544](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15544))
* numpy DeprecationWarning product -> prod ([#15547](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15547))
* get_crop_region_v2 ([#15583](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15583), [#15587](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15587))
## 1.9.0 ## 1.9.0
### Features: ### Features:
@@ -85,7 +121,6 @@
* Fix extra-single-image API not doing upscale failed ([#15465](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15465)) * Fix extra-single-image API not doing upscale failed ([#15465](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15465))
* error handling paste_field callables ([#15470](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15470)) * error handling paste_field callables ([#15470](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/15470))
### Hardware: ### Hardware:
* Add training support and change lspci for Ascend NPU ([#14981](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14981)) * Add training support and change lspci for Ascend NPU ([#14981](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14981))
* Update to ROCm5.7 and PyTorch ([#14820](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14820)) * Update to ROCm5.7 and PyTorch ([#14820](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/14820))
+1 -1
View File
@@ -568,7 +568,7 @@ function extraNetworksShowMetadata(text) {
return; return;
} }
} catch (error) { } catch (error) {
console.eror(error); console.error(error);
} }
var elem = document.createElement('pre'); var elem = document.createElement('pre');
+13 -1
View File
@@ -17,7 +17,7 @@ from fastapi.encoders import jsonable_encoder
from secrets import compare_digest from secrets import compare_digest
import modules.shared as shared import modules.shared as shared
from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items, script_callbacks, infotext_utils, sd_models from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items, script_callbacks, infotext_utils, sd_models, sd_schedulers
from modules.api import models from modules.api import models
from modules.shared import opts from modules.shared import opts
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images
@@ -221,6 +221,7 @@ class Api:
self.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"]) self.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"])
self.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=models.FlagsModel) self.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=models.FlagsModel)
self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=list[models.SamplerItem]) self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=list[models.SamplerItem])
self.add_api_route("/sdapi/v1/schedulers", self.get_schedulers, methods=["GET"], response_model=list[models.SchedulerItem])
self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=list[models.UpscalerItem]) self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=list[models.UpscalerItem])
self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=list[models.LatentUpscalerModeItem]) self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=list[models.LatentUpscalerModeItem])
self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=list[models.SDModelItem]) self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=list[models.SDModelItem])
@@ -683,6 +684,17 @@ class Api:
def get_samplers(self): def get_samplers(self):
return [{"name": sampler[0], "aliases":sampler[2], "options":sampler[3]} for sampler in sd_samplers.all_samplers] return [{"name": sampler[0], "aliases":sampler[2], "options":sampler[3]} for sampler in sd_samplers.all_samplers]
def get_schedulers(self):
return [
{
"name": scheduler.name,
"label": scheduler.label,
"aliases": scheduler.aliases,
"default_rho": scheduler.default_rho,
"need_inner_model": scheduler.need_inner_model,
}
for scheduler in sd_schedulers.schedulers]
def get_upscalers(self): def get_upscalers(self):
return [ return [
{ {
+8 -1
View File
@@ -147,7 +147,7 @@ class ExtrasBaseRequest(BaseModel):
gfpgan_visibility: float = Field(default=0, title="GFPGAN Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of GFPGAN, values should be between 0 and 1.") gfpgan_visibility: float = Field(default=0, title="GFPGAN Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of GFPGAN, values should be between 0 and 1.")
codeformer_visibility: float = Field(default=0, title="CodeFormer Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of CodeFormer, values should be between 0 and 1.") codeformer_visibility: float = Field(default=0, title="CodeFormer Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of CodeFormer, values should be between 0 and 1.")
codeformer_weight: float = Field(default=0, title="CodeFormer Weight", ge=0, le=1, allow_inf_nan=False, description="Sets the weight of CodeFormer, values should be between 0 and 1.") codeformer_weight: float = Field(default=0, title="CodeFormer Weight", ge=0, le=1, allow_inf_nan=False, description="Sets the weight of CodeFormer, values should be between 0 and 1.")
upscaling_resize: float = Field(default=2, title="Upscaling Factor", ge=1, le=8, description="By how much to upscale the image, only used when resize_mode=0.") upscaling_resize: float = Field(default=2, title="Upscaling Factor", gt=0, description="By how much to upscale the image, only used when resize_mode=0.")
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_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_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?") upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the chosen size?")
@@ -235,6 +235,13 @@ class SamplerItem(BaseModel):
aliases: list[str] = Field(title="Aliases") aliases: list[str] = Field(title="Aliases")
options: dict[str, str] = Field(title="Options") options: dict[str, str] = Field(title="Options")
class SchedulerItem(BaseModel):
name: str = Field(title="Name")
label: str = Field(title="Label")
aliases: Optional[list[str]] = Field(title="Aliases")
default_rho: Optional[float] = Field(title="Default Rho")
need_inner_model: Optional[bool] = Field(title="Needs Inner Model")
class UpscalerItem(BaseModel): class UpscalerItem(BaseModel):
name: str = Field(title="Name") name: str = Field(title="Name")
model_name: Optional[str] = Field(title="Model Name") model_name: Optional[str] = Field(title="Model Name")
+2 -2
View File
@@ -11,7 +11,7 @@ import tqdm
from einops import rearrange, repeat from einops import rearrange, repeat
from ldm.util import default from ldm.util import default
from modules import devices, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors from modules import devices, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors
from modules.textual_inversion import textual_inversion, logging from modules.textual_inversion import textual_inversion, saving_settings
from modules.textual_inversion.learn_schedule import LearnRateScheduler from modules.textual_inversion.learn_schedule import LearnRateScheduler
from torch import einsum from torch import einsum
from torch.nn.init import normal_, xavier_normal_, xavier_uniform_, kaiming_normal_, kaiming_uniform_, zeros_ from torch.nn.init import normal_, xavier_normal_, xavier_uniform_, kaiming_normal_, kaiming_uniform_, zeros_
@@ -533,7 +533,7 @@ def train_hypernetwork(id_task, hypernetwork_name: str, learn_rate: float, batch
model_name=checkpoint.model_name, model_hash=checkpoint.shorthash, num_of_dataset_images=len(ds), model_name=checkpoint.model_name, model_hash=checkpoint.shorthash, num_of_dataset_images=len(ds),
**{field: getattr(hypernetwork, field) for field in ['layer_structure', 'activation_func', 'weight_init', 'add_layer_norm', 'use_dropout', ]} **{field: getattr(hypernetwork, field) for field in ['layer_structure', 'activation_func', 'weight_init', 'add_layer_norm', 'use_dropout', ]}
) )
logging.save_settings_to_file(log_directory, {**saved_params, **locals()}) saving_settings.save_settings_to_file(log_directory, {**saved_params, **locals()})
latent_sampling_method = ds.latent_sampling_method latent_sampling_method = ds.latent_sampling_method
+41 -2
View File
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import datetime import datetime
import functools
import pytz import pytz
import io import io
import math import math
@@ -13,6 +13,8 @@ import numpy as np
import piexif import piexif
import piexif.helper import piexif.helper
from PIL import Image, ImageFont, ImageDraw, ImageColor, PngImagePlugin, ImageOps from PIL import Image, ImageFont, ImageDraw, ImageColor, PngImagePlugin, ImageOps
# pillow_avif needs to be imported somewhere in code for it to work
import pillow_avif # noqa: F401
import string import string
import json import json
import hashlib import hashlib
@@ -347,6 +349,32 @@ def sanitize_filename_part(text, replace_spaces=True):
return text return text
@functools.cache
def get_scheduler_str(sampler_name, scheduler_name):
"""Returns {Scheduler} if the scheduler is applicable to the sampler"""
if scheduler_name == 'Automatic':
config = sd_samplers.find_sampler_config(sampler_name)
scheduler_name = config.options.get('scheduler', 'Automatic')
return scheduler_name.capitalize()
@functools.cache
def get_sampler_scheduler_str(sampler_name, scheduler_name):
"""Returns the '{Sampler} {Scheduler}' if the scheduler is applicable to the sampler"""
return f'{sampler_name} {get_scheduler_str(sampler_name, scheduler_name)}'
def get_sampler_scheduler(p, sampler):
"""Returns '{Sampler} {Scheduler}' / '{Scheduler}' / 'NOTHING_AND_SKIP_PREVIOUS_TEXT'"""
if hasattr(p, 'scheduler') and hasattr(p, 'sampler_name'):
if sampler:
sampler_scheduler = get_sampler_scheduler_str(p.sampler_name, p.scheduler)
else:
sampler_scheduler = get_scheduler_str(p.sampler_name, p.scheduler)
return sanitize_filename_part(sampler_scheduler, replace_spaces=False)
return NOTHING_AND_SKIP_PREVIOUS_TEXT
class FilenameGenerator: class FilenameGenerator:
replacements = { replacements = {
'seed': lambda self: self.seed if self.seed is not None else '', 'seed': lambda self: self.seed if self.seed is not None else '',
@@ -358,6 +386,8 @@ class FilenameGenerator:
'height': lambda self: self.image.height, 'height': lambda self: self.image.height,
'styles': lambda self: self.p and sanitize_filename_part(", ".join([style for style in self.p.styles if not style == "None"]) or "None", replace_spaces=False), 'styles': lambda self: self.p and sanitize_filename_part(", ".join([style for style in self.p.styles if not style == "None"]) or "None", replace_spaces=False),
'sampler': lambda self: self.p and sanitize_filename_part(self.p.sampler_name, replace_spaces=False), 'sampler': lambda self: self.p and sanitize_filename_part(self.p.sampler_name, replace_spaces=False),
'sampler_scheduler': lambda self: self.p and get_sampler_scheduler(self.p, True),
'scheduler': lambda self: self.p and get_sampler_scheduler(self.p, False),
'model_hash': lambda self: getattr(self.p, "sd_model_hash", shared.sd_model.sd_model_hash), 'model_hash': lambda self: getattr(self.p, "sd_model_hash", shared.sd_model.sd_model_hash),
'model_name': lambda self: sanitize_filename_part(shared.sd_model.sd_checkpoint_info.name_for_extra, replace_spaces=False), 'model_name': lambda self: sanitize_filename_part(shared.sd_model.sd_checkpoint_info.name_for_extra, replace_spaces=False),
'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'), 'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'),
@@ -569,6 +599,16 @@ def save_image_with_geninfo(image, geninfo, filename, extension=None, existing_p
}) })
piexif.insert(exif_bytes, filename) piexif.insert(exif_bytes, filename)
elif extension.lower() == '.avif':
if opts.enable_pnginfo and geninfo is not None:
exif_bytes = piexif.dump({
"Exif": {
piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(geninfo or "", encoding="unicode")
},
})
image.save(filename,format=image_format, exif=exif_bytes)
elif extension.lower() == ".gif": elif extension.lower() == ".gif":
image.save(filename, format=image_format, comment=geninfo) image.save(filename, format=image_format, comment=geninfo)
else: else:
@@ -747,7 +787,6 @@ def read_info_from_image(image: Image.Image) -> tuple[str | None, dict]:
exif_comment = exif_comment.decode('utf8', errors="ignore") exif_comment = exif_comment.decode('utf8', errors="ignore")
if exif_comment: if exif_comment:
items['exif comment'] = exif_comment
geninfo = exif_comment geninfo = exif_comment
elif "comment" in items: # for gif elif "comment" in items: # for gif
geninfo = items["comment"].decode('utf8', errors="ignore") geninfo = items["comment"].decode('utf8', errors="ignore")
+32 -10
View File
@@ -1,17 +1,39 @@
from PIL import Image, ImageFilter, ImageOps from PIL import Image, ImageFilter, ImageOps
def get_crop_region(mask, pad=0): def get_crop_region_v2(mask, pad=0):
"""finds a rectangular region that contains all masked ares in an image. Returns (x1, y1, x2, y2) coordinates of the rectangle. """
For example, if a user has painted the top-right part of a 512x512 image, the result may be (256, 0, 512, 256)""" Finds a rectangular region that contains all masked ares in a mask.
mask_img = mask if isinstance(mask, Image.Image) else Image.fromarray(mask) Returns None if mask is completely black mask (all 0)
box = mask_img.getbbox()
if box: Parameters:
mask: PIL.Image.Image L mode or numpy 1d array
pad: int number of pixels that the region will be extended on all sides
Returns: (x1, y1, x2, y2) | None
Introduced post 1.9.0
"""
mask = mask if isinstance(mask, Image.Image) else Image.fromarray(mask)
if box := mask.getbbox():
x1, y1, x2, y2 = box x1, y1, x2, y2 = box
else: # when no box is found return (max(x1 - pad, 0), max(y1 - pad, 0), min(x2 + pad, mask.size[0]), min(y2 + pad, mask.size[1])) if pad else box
x1, y1 = mask_img.size
x2 = y2 = 0
return max(x1 - pad, 0), max(y1 - pad, 0), min(x2 + pad, mask_img.size[0]), min(y2 + pad, mask_img.size[1]) def get_crop_region(mask, pad=0):
"""
Same function as get_crop_region_v2 but handles completely black mask (all 0) differently
when mask all black still return coordinates but the coordinates may be invalid ie x2>x1 or y2>y1
Notes: it is possible for the coordinates to be "valid" again if pad size is sufficiently large
(mask_size.x-pad, mask_size.y-pad, pad, pad)
Extension developer should use get_crop_region_v2 instead unless for compatibility considerations.
"""
mask = mask if isinstance(mask, Image.Image) else Image.fromarray(mask)
if box := get_crop_region_v2(mask, pad):
return box
x1, y1 = mask.size
x2 = y2 = 0
return max(x1 - pad, 0), max(y1 - pad, 0), min(x2 + pad, mask.size[0]), min(y2 + pad, mask.size[1])
def expand_crop_region(crop_region, processing_width, processing_height, image_width, image_height): def expand_crop_region(crop_region, processing_width, processing_height, image_width, image_height):
+6 -2
View File
@@ -2,7 +2,7 @@ import os
from PIL import Image from PIL import Image
from modules import shared, images, devices, scripts, scripts_postprocessing, ui_common, infotext_utils from modules import shared, images, devices, scripts, scripts_postprocessing, ui_common, infotext_utils, util
from modules.shared import opts from modules.shared import opts
@@ -31,6 +31,8 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir,
for filename in image_list: for filename in image_list:
yield filename, filename yield filename, filename
else: else:
if isinstance(image, str):
image = util.decode_base64_to_image(image)
assert image, 'image not selected' assert image, 'image not selected'
yield image, None yield image, None
@@ -62,11 +64,13 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir,
else: else:
image_data = image_placeholder image_data = image_placeholder
image_data = image_data if image_data.mode in ("RGBA", "RGB") else image_data.convert("RGB")
parameters, existing_pnginfo = images.read_info_from_image(image_data) parameters, existing_pnginfo = images.read_info_from_image(image_data)
if parameters: if parameters:
existing_pnginfo["parameters"] = parameters existing_pnginfo["parameters"] = parameters
initial_pp = scripts_postprocessing.PostprocessedImage(image_data if image_data.mode in ("RGBA", "RGB") else image_data.convert("RGB")) initial_pp = scripts_postprocessing.PostprocessedImage(image_data)
scripts.scripts_postproc.run(initial_pp, args) scripts.scripts_postproc.run(initial_pp, args)
+19 -10
View File
@@ -1611,16 +1611,23 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
if self.inpaint_full_res: if self.inpaint_full_res:
self.mask_for_overlay = image_mask self.mask_for_overlay = image_mask
mask = image_mask.convert('L') mask = image_mask.convert('L')
crop_region = masking.get_crop_region(mask, self.inpaint_full_res_padding) crop_region = masking.get_crop_region_v2(mask, self.inpaint_full_res_padding)
crop_region = masking.expand_crop_region(crop_region, self.width, self.height, mask.width, mask.height) if crop_region:
x1, y1, x2, y2 = crop_region crop_region = masking.expand_crop_region(crop_region, self.width, self.height, mask.width, mask.height)
x1, y1, x2, y2 = crop_region
mask = mask.crop(crop_region) mask = mask.crop(crop_region)
image_mask = images.resize_image(2, mask, self.width, self.height) image_mask = images.resize_image(2, mask, self.width, self.height)
self.paste_to = (x1, y1, x2-x1, y2-y1) self.paste_to = (x1, y1, x2-x1, y2-y1)
self.extra_generation_params["Inpaint area"] = "Only masked"
self.extra_generation_params["Inpaint area"] = "Only masked" self.extra_generation_params["Masked area padding"] = self.inpaint_full_res_padding
self.extra_generation_params["Masked area padding"] = self.inpaint_full_res_padding else:
crop_region = None
image_mask = None
self.mask_for_overlay = None
self.inpaint_full_res = False
massage = 'Unable to perform "Inpaint Only mask" because mask is blank, switch to img2img mode.'
model_hijack.comments.append(massage)
logging.info(massage)
else: else:
image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height) image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height)
np_mask = np.array(image_mask) np_mask = np.array(image_mask)
@@ -1648,6 +1655,8 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
image = images.resize_image(self.resize_mode, image, self.width, self.height) image = images.resize_image(self.resize_mode, image, self.width, self.height)
if image_mask is not None: if image_mask is not None:
if self.mask_for_overlay.size != (image.width, image.height):
self.mask_for_overlay = images.resize_image(self.resize_mode, self.mask_for_overlay, image.width, image.height)
image_masked = Image.new('RGBa', (image.width, image.height)) image_masked = Image.new('RGBa', (image.width, image.height))
image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L'))) image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L')))
+3
View File
@@ -448,6 +448,9 @@ def remove_callbacks_for_function(callback_func):
for callback_list in callback_map.values(): for callback_list in callback_map.values():
for callback_to_remove in [cb for cb in callback_list if cb.callback == callback_func]: for callback_to_remove in [cb for cb in callback_list if cb.callback == callback_func]:
callback_list.remove(callback_to_remove) callback_list.remove(callback_to_remove)
for ordered_callback_list in ordered_callbacks_map.values():
for callback_to_remove in [cb for cb in ordered_callback_list if cb.callback == callback_func]:
ordered_callback_list.remove(callback_to_remove)
def on_app_started(callback, *, name=None): def on_app_started(callback, *, name=None):
-5
View File
@@ -2,7 +2,6 @@ import os
import importlib.util import importlib.util
from modules import errors from modules import errors
import sys
loaded_scripts = {} loaded_scripts = {}
@@ -14,10 +13,6 @@ def load_module(path):
module_spec.loader.exec_module(module) module_spec.loader.exec_module(module)
loaded_scripts[path] = module loaded_scripts[path] = module
module_name, _ = os.path.splitext(os.path.basename(path))
sys.modules["scripts." + module_name] = module
return module return module
+6 -2
View File
@@ -1,12 +1,16 @@
import base64 import base64
import json import json
import os.path
import warnings import warnings
import logging
import numpy as np import numpy as np
import zlib import zlib
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
import torch import torch
logger = logging.getLogger(__name__)
class EmbeddingEncoder(json.JSONEncoder): class EmbeddingEncoder(json.JSONEncoder):
def default(self, obj): def default(self, obj):
@@ -43,7 +47,7 @@ def lcg(m=2**32, a=1664525, c=1013904223, seed=0):
def xor_block(block): def xor_block(block):
g = lcg() g = lcg()
randblock = np.array([next(g) for _ in range(np.product(block.shape))]).astype(np.uint8).reshape(block.shape) randblock = np.array([next(g) for _ in range(np.prod(block.shape))]).astype(np.uint8).reshape(block.shape)
return np.bitwise_xor(block.astype(np.uint8), randblock & 0x0F) return np.bitwise_xor(block.astype(np.uint8), randblock & 0x0F)
@@ -114,7 +118,7 @@ def extract_image_data_embed(image):
outarr = crop_black(np.array(image.convert('RGB').getdata()).reshape(image.size[1], image.size[0], d).astype(np.uint8)) & 0x0F outarr = crop_black(np.array(image.convert('RGB').getdata()).reshape(image.size[1], image.size[0], d).astype(np.uint8)) & 0x0F
black_cols = np.where(np.sum(outarr, axis=(0, 2)) == 0) black_cols = np.where(np.sum(outarr, axis=(0, 2)) == 0)
if black_cols[0].shape[0] < 2: if black_cols[0].shape[0] < 2:
print('No Image data blocks found.') logger.debug(f'{os.path.basename(getattr(image, "filename", "unknown image file"))}: no embedded information found.')
return None return None
data_block_lower = outarr[:, :black_cols[0].min(), :].astype(np.uint8) data_block_lower = outarr[:, :black_cols[0].min(), :].astype(np.uint8)
@@ -17,7 +17,7 @@ import modules.textual_inversion.dataset
from modules.textual_inversion.learn_schedule import LearnRateScheduler from modules.textual_inversion.learn_schedule import LearnRateScheduler
from modules.textual_inversion.image_embedding import embedding_to_b64, embedding_from_b64, insert_image_data_embed, extract_image_data_embed, caption_image_overlay from modules.textual_inversion.image_embedding import embedding_to_b64, embedding_from_b64, insert_image_data_embed, extract_image_data_embed, caption_image_overlay
from modules.textual_inversion.logging import save_settings_to_file from modules.textual_inversion.saving_settings import save_settings_to_file
TextualInversionTemplate = namedtuple("TextualInversionTemplate", ["name", "path"]) TextualInversionTemplate = namedtuple("TextualInversionTemplate", ["name", "path"])
+1
View File
@@ -54,6 +54,7 @@ def create_ui():
output_panel.html_log, output_panel.html_log,
], ],
show_progress=False, show_progress=False,
preprocess=False,
) )
parameters_copypaste.add_paste_fields("extras", extras_image, None) parameters_copypaste.add_paste_fields("extras", extras_image, None)
+1 -1
View File
@@ -57,7 +57,7 @@ class Upscaler:
dest_h = int((img.height * scale) // 8 * 8) dest_h = int((img.height * scale) // 8 * 8)
for _ in range(3): for _ in range(3):
if img.width >= dest_w and img.height >= dest_h: if img.width >= dest_w and img.height >= dest_h and scale != 1:
break break
if shared.state.interrupted: if shared.state.interrupted:
+10
View File
@@ -211,3 +211,13 @@ Requested path was: {path}
subprocess.Popen(["wsl-open", path]) subprocess.Popen(["wsl-open", path])
else: else:
subprocess.Popen(["xdg-open", path]) subprocess.Popen(["xdg-open", path])
def decode_base64_to_image(base64_str: str):
from modules import images
from io import BytesIO
import base64
if base64_str.startswith("data:image/"):
base64_str = base64_str.partition(';')[2].partition(',')[2]
image = images.read(BytesIO(base64.b64decode(base64_str)))
return image
+1
View File
@@ -30,3 +30,4 @@ torch
torchdiffeq torchdiffeq
torchsde torchsde
transformers==4.30.2 transformers==4.30.2
pillow-avif-plugin==1.4.3
+1
View File
@@ -29,3 +29,4 @@ torchdiffeq==0.2.3
torchsde==0.2.6 torchsde==0.2.6
transformers==4.30.2 transformers==4.30.2
httpx==0.24.1 httpx==0.24.1
pillow-avif-plugin==1.4.3
+4 -4
View File
@@ -113,13 +113,13 @@ then
exit 1 exit 1
fi fi
if [[ -d .git ]] if [[ -d "$SCRIPT_DIR/.git" ]]
then then
printf "\n%s\n" "${delimiter}" printf "\n%s\n" "${delimiter}"
printf "Repo already cloned, using it as install directory" printf "Repo already cloned, using it as install directory"
printf "\n%s\n" "${delimiter}" printf "\n%s\n" "${delimiter}"
install_dir="${PWD}/../" install_dir="${SCRIPT_DIR}/../"
clone_dir="${PWD##*/}" clone_dir="${SCRIPT_DIR##*/}"
fi fi
# Check prerequisites # Check prerequisites
@@ -243,7 +243,7 @@ prepare_tcmalloc() {
for lib in "${TCMALLOC_LIBS[@]}" for lib in "${TCMALLOC_LIBS[@]}"
do do
# Determine which type of tcmalloc library the library supports # Determine which type of tcmalloc library the library supports
TCMALLOC="$(PATH=/usr/sbin:$PATH ldconfig -p | grep -P $lib | head -n 1)" TCMALLOC="$(PATH=/sbin:/usr/sbin:$PATH ldconfig -p | grep -P $lib | head -n 1)"
TC_INFO=(${TCMALLOC//=>/}) TC_INFO=(${TCMALLOC//=>/})
if [[ ! -z "${TC_INFO}" ]]; then if [[ ! -z "${TC_INFO}" ]]; then
echo "Check TCMalloc: ${TC_INFO}" echo "Check TCMalloc: ${TC_INFO}"