mirror of
https://github.com/vladmandic/automatic
synced 2026-09-20 01:31:13 +02:00
Merge remote-tracking branch 'origin/master' into Compel
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2023-08-05
|
||||
|
||||
Another minor update, but it unlocks some cool new items...
|
||||
|
||||
- diffusers:
|
||||
- vaesd live preview (sd and sd-xl)
|
||||
- fix inpainting (sd and sd-xl)
|
||||
- general:
|
||||
- new torch 2.0 with ipex (intel arc)
|
||||
- additional callbacks for extensions
|
||||
enables latest comfyui extension
|
||||
- update requirements
|
||||
|
||||
## Update for 2023-07-30
|
||||
|
||||
Smaller release, but IMO worth a post...
|
||||
|
||||
@@ -15,7 +15,6 @@ Stuff to be added, in no particular order...
|
||||
- Add Hires
|
||||
- Add Lora/Lyco mixer
|
||||
- Add ControlNet
|
||||
- Fix SD-XL Img2img/Inpaint
|
||||
- Add SD and SD-XL Pix2Pix
|
||||
- Fix DeepFloyd IF model
|
||||
- Redo Prompt parser for diffusers
|
||||
|
||||
Submodule extensions-builtin/sd-dynamic-thresholding updated: 5349f00872...c60fe071e5
Submodule extensions-builtin/sd-extension-system-info updated: 9d3c0ca0f2...19d190e71b
Submodule extensions-builtin/sd-webui-controlnet updated: 5ae9b4a1a0...af34f51449
+1
-1
@@ -328,7 +328,7 @@ def check_torch():
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.1a0 torchvision==0.15.2a0 intel_extension_for_pytorch==2.0.110+xpu -f https://developer.intel.com/ipex-whl-stable-xpu')
|
||||
os.environ.setdefault('TENSORFLOW_PACKAGE', 'tensorflow==2.13.0 intel-extension-for-tensorflow[gpu]')
|
||||
else:
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0a0 torchvision intel_extension_for_pytorch==2.0.110+gitba7f6c1 -f https://developer.intel.com/ipex-whl-stable-xpu')
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.0.0a0 intel_extension_for_pytorch==2.0.110+gitba7f6c1 -f https://developer.intel.com/ipex-whl-stable-xpu')
|
||||
else:
|
||||
machine = platform.machine()
|
||||
if sys.platform == 'darwin':
|
||||
|
||||
+4
-4
@@ -119,9 +119,9 @@ def set_cuda_params():
|
||||
shared.log.debug('Verifying Torch settings')
|
||||
if cuda_ok:
|
||||
try:
|
||||
torch.backends.cuda.matmul.allow_tf32 = shared.opts.cuda_allow_tf32
|
||||
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = shared.opts.cuda_allow_tf16_reduced
|
||||
torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = shared.opts.cuda_allow_tf16_reduced
|
||||
torch.backends.cuda.matmul.allow_tf32 = True
|
||||
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = True
|
||||
torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = True
|
||||
except Exception:
|
||||
pass
|
||||
if torch.backends.cudnn.is_available():
|
||||
@@ -130,7 +130,7 @@ def set_cuda_params():
|
||||
if shared.opts.cudnn_benchmark:
|
||||
shared.log.debug('Torch enable cuDNN benchmark')
|
||||
torch.backends.cudnn.benchmark_limit = 0
|
||||
torch.backends.cudnn.allow_tf32 = shared.opts.cuda_allow_tf32
|
||||
torch.backends.cudnn.allow_tf32 = True
|
||||
except Exception:
|
||||
pass
|
||||
global dtype, dtype_vae, dtype_unet, unet_needs_upcast # pylint: disable=global-statement
|
||||
|
||||
+2
-1
@@ -106,7 +106,8 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
|
||||
elif mode == 2: # inpaint
|
||||
if init_img_with_mask is None:
|
||||
return
|
||||
image, mask = init_img_with_mask["image"], init_img_with_mask["mask"]
|
||||
image = init_img_with_mask["image"]
|
||||
mask = init_img_with_mask["mask"]
|
||||
alpha_mask = ImageOps.invert(image.split()[-1]).convert('L').point(lambda x: 255 if x > 0 else 0, mode='1')
|
||||
mask = ImageChops.lighter(alpha_mask, mask.convert('L')).convert('L')
|
||||
image = image.convert("RGB")
|
||||
|
||||
@@ -4,6 +4,7 @@ import torch
|
||||
import intel_extension_for_pytorch as ipex
|
||||
from modules import shared
|
||||
from modules.sd_hijack_utils import CondFunc
|
||||
from .diffusers import ipex_diffusers
|
||||
|
||||
#ControlNet depth_leres++
|
||||
class DummyDataParallel(torch.nn.Module):
|
||||
@@ -111,13 +112,28 @@ def ipex_init():
|
||||
CondFunc('torch.nn.modules.GroupNorm.forward',
|
||||
lambda orig_func, self, input: orig_func(self, input.to(self.weight.data.dtype)),
|
||||
lambda orig_func, self, input: input.dtype != self.weight.data.dtype)
|
||||
#FP32:
|
||||
CondFunc('torch.nn.modules.Linear.forward',
|
||||
lambda orig_func, self, input: orig_func(self, input.to(self.weight.data.dtype)),
|
||||
lambda orig_func, self, input: input.dtype != self.weight.data.dtype)
|
||||
#Diffusers bfloat16:
|
||||
CondFunc('torch.nn.modules.Conv2d._conv_forward',
|
||||
lambda orig_func, self, input, weight, bias=None: orig_func(self, input.to(weight.data.dtype), weight, bias=bias),
|
||||
lambda orig_func, self, input, weight, bias=None: input.dtype != weight.data.dtype)
|
||||
#Embedding FP32:
|
||||
CondFunc('torch.bmm',
|
||||
lambda orig_func, input, mat2, *args, **kwargs: orig_func(input, mat2.to(input.dtype), *args, **kwargs),
|
||||
lambda orig_func, input, mat2, *args, **kwargs: input.dtype != mat2.dtype)
|
||||
#BF16:
|
||||
CondFunc('torch.nn.functional.layer_norm',
|
||||
lambda orig_func, input, normalized_shape=None, weight=None, *args, **kwargs:
|
||||
orig_func(input.to(weight.data.dtype), normalized_shape, weight, *args, **kwargs),
|
||||
lambda orig_func, input, normalized_shape=None, weight=None, *args, **kwargs:
|
||||
input.dtype != weight.data.dtype and weight is not None)
|
||||
#Embedding BF16
|
||||
CondFunc('torch.cat',
|
||||
lambda orig_func, input, *args, **kwargs: orig_func([input[0].to(input[1].dtype), input[1], input[2].to(input[1].dtype)], *args, **kwargs),
|
||||
lambda orig_func, input, *args, **kwargs: len(input) == 3 and (input[0].dtype != input[1].dtype or input[2].dtype != input[1].dtype))
|
||||
#Diffusers BF16:
|
||||
CondFunc('torch.nn.functional.conv2d',
|
||||
lambda orig_func, input, weight, *args, **kwargs: orig_func(input.to(weight.data.dtype), weight, *args, **kwargs),
|
||||
lambda orig_func, input, weight, *args, **kwargs: input.dtype != weight.data.dtype)
|
||||
|
||||
#Functions that does not work with the XPU:
|
||||
#UniPC:
|
||||
@@ -149,3 +165,5 @@ def ipex_init():
|
||||
weight if weight is not None else torch.ones(input.size()[1], device=shared.device),
|
||||
bias if bias is not None else torch.zeros(input.size()[1], device=shared.device), *args, **kwargs),
|
||||
lambda orig_func, input, *args, **kwargs: input.device != torch.device("cpu"))
|
||||
|
||||
ipex_diffusers()
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import torch
|
||||
import intel_extension_for_pytorch as ipex
|
||||
import diffusers
|
||||
|
||||
#ARC GPUs can't allocate more than 4GB to a single block:
|
||||
class SlicedAttnProcessor:
|
||||
r"""
|
||||
Processor for implementing sliced attention.
|
||||
|
||||
Args:
|
||||
slice_size (`int`, *optional*):
|
||||
The number of steps to compute attention. Uses as many slices as `attention_head_dim // slice_size`, and
|
||||
`attention_head_dim` must be a multiple of the `slice_size`.
|
||||
"""
|
||||
|
||||
def __init__(self, slice_size):
|
||||
self.slice_size = slice_size
|
||||
|
||||
def __call__(self, attn: diffusers.models.attention_processor.Attention, hidden_states, encoder_hidden_states=None, attention_mask=None):
|
||||
residual = hidden_states
|
||||
|
||||
input_ndim = hidden_states.ndim
|
||||
|
||||
if input_ndim == 4:
|
||||
batch_size, channel, height, width = hidden_states.shape
|
||||
hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2)
|
||||
|
||||
batch_size, sequence_length, _ = (
|
||||
hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape
|
||||
)
|
||||
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
|
||||
|
||||
if attn.group_norm is not None:
|
||||
hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2)
|
||||
|
||||
query = attn.to_q(hidden_states)
|
||||
dim = query.shape[-1]
|
||||
query = attn.head_to_batch_dim(query)
|
||||
|
||||
if encoder_hidden_states is None:
|
||||
encoder_hidden_states = hidden_states
|
||||
elif attn.norm_cross:
|
||||
encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states)
|
||||
|
||||
key = attn.to_k(encoder_hidden_states)
|
||||
value = attn.to_v(encoder_hidden_states)
|
||||
key = attn.head_to_batch_dim(key)
|
||||
value = attn.head_to_batch_dim(value)
|
||||
|
||||
batch_size_attention, query_tokens, shape_three = query.shape
|
||||
hidden_states = torch.zeros(
|
||||
(batch_size_attention, query_tokens, dim // attn.heads), device=query.device, dtype=query.dtype
|
||||
)
|
||||
|
||||
block_multiply = 2.4 if query.dtype == torch.float32 else 1.2
|
||||
block_size = (batch_size_attention * query_tokens * shape_three) / 1024 * block_multiply #MB
|
||||
split_2_slice_size = query_tokens
|
||||
if block_size >= 4000:
|
||||
do_split_2 = True
|
||||
#Find something divisible with the query_tokens
|
||||
while ((self.slice_size * split_2_slice_size * shape_three) / 1024 * block_multiply) > 4000:
|
||||
split_2_slice_size = split_2_slice_size // 2
|
||||
else:
|
||||
do_split_2 = False
|
||||
|
||||
for i in range(batch_size_attention // self.slice_size):
|
||||
start_idx = i * self.slice_size
|
||||
end_idx = (i + 1) * self.slice_size
|
||||
|
||||
if do_split_2:
|
||||
for i2 in range(query_tokens // split_2_slice_size):
|
||||
start_idx_2 = i2 * split_2_slice_size
|
||||
end_idx_2 = (i2 + 1) * split_2_slice_size
|
||||
|
||||
query_slice = query[start_idx:end_idx, start_idx_2:end_idx_2]
|
||||
key_slice = key[start_idx:end_idx, start_idx_2:end_idx_2]
|
||||
attn_mask_slice = attention_mask[start_idx:end_idx, start_idx_2:end_idx_2] if attention_mask is not None else None
|
||||
|
||||
attn_slice = attn.get_attention_scores(query_slice, key_slice, attn_mask_slice)
|
||||
attn_slice = torch.bmm(attn_slice, value[start_idx:end_idx, start_idx_2:end_idx_2])
|
||||
|
||||
hidden_states[start_idx:end_idx, start_idx_2:end_idx_2] = attn_slice
|
||||
else:
|
||||
query_slice = query[start_idx:end_idx]
|
||||
key_slice = key[start_idx:end_idx]
|
||||
attn_mask_slice = attention_mask[start_idx:end_idx] if attention_mask is not None else None
|
||||
|
||||
attn_slice = attn.get_attention_scores(query_slice, key_slice, attn_mask_slice)
|
||||
|
||||
attn_slice = torch.bmm(attn_slice, value[start_idx:end_idx])
|
||||
|
||||
hidden_states[start_idx:end_idx] = attn_slice
|
||||
|
||||
hidden_states = attn.batch_to_head_dim(hidden_states)
|
||||
|
||||
# linear proj
|
||||
hidden_states = attn.to_out[0](hidden_states)
|
||||
# dropout
|
||||
hidden_states = attn.to_out[1](hidden_states)
|
||||
|
||||
if input_ndim == 4:
|
||||
hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width)
|
||||
|
||||
if attn.residual_connection:
|
||||
hidden_states = hidden_states + residual
|
||||
|
||||
hidden_states = hidden_states / attn.rescale_output_factor
|
||||
|
||||
return hidden_states
|
||||
|
||||
def ipex_diffusers():
|
||||
diffusers.models.attention_processor.SlicedAttnProcessor = SlicedAttnProcessor
|
||||
+37
-26
@@ -444,8 +444,13 @@ def fix_seed(p):
|
||||
p.subseed = get_fixed_seed(p.subseed)
|
||||
|
||||
|
||||
def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument
|
||||
index = position_in_batch + iteration * p.batch_size
|
||||
def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0, index=None, all_negative_prompts=None): # pylint: disable=unused-argument
|
||||
if index is None:
|
||||
index = position_in_batch + iteration * p.batch_size
|
||||
|
||||
if all_negative_prompts is None:
|
||||
all_negative_prompts = p.all_negative_prompts
|
||||
|
||||
generation_params = {
|
||||
"Steps": p.steps,
|
||||
"Seed": all_seeds[index],
|
||||
@@ -487,7 +492,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
|
||||
generation_params['Token merging ratio hr'] = token_merging_ratio_hr if token_merging_ratio_hr != 0 else None
|
||||
generation_params.update(p.extra_generation_params)
|
||||
generation_params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in generation_params.items() if v is not None])
|
||||
negative_prompt_text = f"\nNegative prompt: {p.all_negative_prompts[index]}" if p.all_negative_prompts[index] else ""
|
||||
negative_prompt_text = f"\nNegative prompt: {all_negative_prompts[index]}" if all_negative_prompts[index] else ""
|
||||
return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
|
||||
|
||||
|
||||
@@ -599,9 +604,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
else:
|
||||
p.all_subseeds = [int(subseed) + x for x in range(len(p.all_prompts))]
|
||||
|
||||
def infotext(iteration=0, position_in_batch=0):
|
||||
return create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, comments, iteration, position_in_batch)
|
||||
|
||||
if os.path.exists(shared.opts.embeddings_dir) and not p.do_not_reload_embeddings:
|
||||
model_hijack.embedding_db.load_textual_inversion_embeddings()
|
||||
if p.scripts is not None:
|
||||
@@ -646,20 +648,20 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if shared.state.interrupted:
|
||||
shared.log.debug(f'Process interrupted: {n}/{p.n_iter}')
|
||||
break
|
||||
prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
seeds = p.all_seeds[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
subseeds = p.all_subseeds[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
p.prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
p.seeds = p.all_seeds[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
p.subseeds = p.all_subseeds[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
if p.scripts is not None:
|
||||
p.scripts.before_process_batch(p, batch_number=n, prompts=prompts, seeds=seeds, subseeds=subseeds)
|
||||
if len(prompts) == 0:
|
||||
p.scripts.before_process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
||||
if len(p.prompts) == 0:
|
||||
break
|
||||
prompts, extra_network_data = extra_networks.parse_prompts(prompts)
|
||||
p.prompts, extra_network_data = extra_networks.parse_prompts(p.prompts)
|
||||
if not p.disable_extra_networks:
|
||||
with devices.autocast():
|
||||
extra_networks.activate(p, extra_network_data)
|
||||
if p.scripts is not None:
|
||||
p.scripts.process_batch(p, batch_number=n, prompts=prompts, seeds=seeds, subseeds=subseeds)
|
||||
p.scripts.process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
||||
if n == 0:
|
||||
with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file:
|
||||
processed = Processed(p, [], p.seed, "")
|
||||
@@ -671,13 +673,13 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
shared.state.job = f"Batch {n+1} out of {p.n_iter}"
|
||||
|
||||
if shared.backend == shared.Backend.ORIGINAL:
|
||||
uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, negative_prompts, p.steps * step_multiplier, cached_uc)
|
||||
c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, prompts, p.steps * step_multiplier, cached_c)
|
||||
uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, p.negative_prompts, p.steps * step_multiplier, cached_uc)
|
||||
c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, p.prompts, p.steps * step_multiplier, cached_c)
|
||||
if len(model_hijack.comments) > 0:
|
||||
for comment in model_hijack.comments:
|
||||
comments[comment] = 1
|
||||
with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast():
|
||||
samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, prompts=prompts)
|
||||
samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts)
|
||||
x_samples_ddim = [decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))]
|
||||
try:
|
||||
for x in x_samples_ddim:
|
||||
@@ -699,7 +701,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
|
||||
elif shared.backend == shared.Backend.DIFFUSERS:
|
||||
from modules.processing_diffusers import process_diffusers
|
||||
x_samples_ddim = process_diffusers(p, seeds, prompts, negative_prompts)
|
||||
x_samples_ddim = process_diffusers(p, p.seeds, p.prompts, p.negative_prompts)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unknown backend {shared.backend}")
|
||||
@@ -709,6 +711,15 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
devices.torch_gc()
|
||||
if p.scripts is not None:
|
||||
p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n)
|
||||
if p.scripts is not None:
|
||||
p.prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
||||
batch_params = scripts.PostprocessBatchListArgs(list(x_samples_ddim))
|
||||
p.scripts.postprocess_batch_list(p, batch_params, batch_number=n)
|
||||
x_samples_ddim = batch_params.images
|
||||
|
||||
def infotext(index=0):
|
||||
return create_infotext(p, p.prompts, p.seeds, p.subseeds, index=index, all_negative_prompts=p.negative_prompts)
|
||||
|
||||
for i, x_sample in enumerate(x_samples_ddim):
|
||||
p.batch_index = i
|
||||
@@ -721,9 +732,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_face_restoration:
|
||||
orig = p.restore_faces
|
||||
p.restore_faces = False
|
||||
info=infotext(n, i)
|
||||
info = infotext(i)
|
||||
p.restore_faces = orig
|
||||
images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restoration")
|
||||
images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restoration")
|
||||
p.ops.append('face')
|
||||
x_sample = modules.face_restoration.restore_faces(x_sample)
|
||||
image = Image.fromarray(x_sample)
|
||||
@@ -735,16 +746,16 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_color_correction:
|
||||
orig = p.color_corrections
|
||||
p.color_corrections = None
|
||||
info=infotext(n, i)
|
||||
info = infotext(i)
|
||||
p.color_corrections = orig
|
||||
image_without_cc = apply_overlay(image, p.paste_to, i, p.overlay_images)
|
||||
images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correction")
|
||||
images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correction")
|
||||
p.ops.append('color')
|
||||
image = apply_color_correction(p.color_corrections[i], image)
|
||||
image = apply_overlay(image, p.paste_to, i, p.overlay_images)
|
||||
if shared.opts.samples_save and not p.do_not_save_samples:
|
||||
images.save_image(image, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=infotext(n, i), p=p)
|
||||
text = infotext(n, i)
|
||||
images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p)
|
||||
text = infotext(i)
|
||||
infotexts.append(text)
|
||||
image.info["parameters"] = text
|
||||
output_images.append(image)
|
||||
@@ -752,9 +763,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
image_mask = p.mask_for_overlay.convert('RGB')
|
||||
image_mask_composite = Image.composite(image.convert('RGBA').convert('RGBa'), Image.new('RGBa', image.size), images.resize_image(3, p.mask_for_overlay, image.width, image.height).convert('L')).convert('RGBA')
|
||||
if shared.opts.save_mask:
|
||||
images.save_image(image_mask, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=infotext(n, i), p=p, suffix="-mask")
|
||||
images.save_image(image_mask, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p, suffix="-mask")
|
||||
if shared.opts.save_mask_composite:
|
||||
images.save_image(image_mask_composite, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=infotext(n, i), p=p, suffix="-mask-composite")
|
||||
images.save_image(image_mask_composite, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=infotext(i), p=p, suffix="-mask-composite")
|
||||
if shared.opts.return_mask:
|
||||
output_images.append(image_mask)
|
||||
if shared.opts.return_mask_composite:
|
||||
|
||||
@@ -134,7 +134,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
|
||||
task_specific_kwargs = {"image": p.init_images, "strength": p.denoising_strength}
|
||||
elif sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.INPAINTING:
|
||||
p.ops.append('inpaint')
|
||||
task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength}
|
||||
task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength, "height": p.height, "width": p.width}
|
||||
|
||||
# TODO diffusers use transformers for prompt parsing
|
||||
# from modules.prompt_parser import parse_prompt_attention
|
||||
@@ -229,6 +229,4 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
|
||||
if p.is_hr_pass:
|
||||
shared.log.warning('Diffusers not implemented: hires fix')
|
||||
|
||||
|
||||
|
||||
return results
|
||||
|
||||
@@ -18,6 +18,11 @@ class PostprocessImageArgs:
|
||||
self.image = image
|
||||
|
||||
|
||||
class PostprocessBatchListArgs:
|
||||
def __init__(self, images):
|
||||
self.images = images
|
||||
|
||||
|
||||
class Script:
|
||||
name = None
|
||||
filename = None
|
||||
@@ -108,6 +113,22 @@ class Script:
|
||||
"""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, *args, **kwargs):
|
||||
"""
|
||||
Same as postprocess_batch(), but receives batch images as a list of 3D tensors instead of a 4D tensor.
|
||||
This is useful when you want to update the entire batch instead of individual images.
|
||||
You can modify the postprocessing object (pp) to update the images in the batch, remove images, add images, etc.
|
||||
If the number of images is different from the batch size when returning,
|
||||
then the script has the responsibility to also update the following attributes in the processing object (p):
|
||||
- p.prompts
|
||||
- p.negative_prompts
|
||||
- p.seeds
|
||||
- p.subseeds
|
||||
**kwargs will have same items as process_batch, and also:
|
||||
- batch_number - index of current batch, from 0 to number of batches-1
|
||||
"""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def postprocess(self, p, processed, *args):
|
||||
"""
|
||||
This function is called after processing ends for AlwaysVisible scripts.
|
||||
@@ -457,6 +478,18 @@ class ScriptRunner:
|
||||
errors.display(e, f'Running script before postprocess batch: {script.filename}')
|
||||
log.debug(f'Script postprocess-batch: {s}')
|
||||
|
||||
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, **kwargs):
|
||||
s = []
|
||||
for script in self.alwayson_scripts:
|
||||
try:
|
||||
t0 = time.time()
|
||||
args = p.per_script_args.get(script.title(), p.script_args[script.args_from:script.args_to])
|
||||
script.postprocess_batch_list(p, pp, *args, **kwargs)
|
||||
s.append(f'{script.title()}:{round(time.time()-t0, 2)}s')
|
||||
except Exception as e:
|
||||
errors.display(e, f'Running script before postprocess batch list: {script.filename}')
|
||||
log.debug(f'Script postprocess-batch-list: {s}')
|
||||
|
||||
def postprocess_image(self, p, pp: PostprocessImageArgs):
|
||||
s = []
|
||||
for script in self.alwayson_scripts:
|
||||
|
||||
@@ -534,10 +534,7 @@ def change_backend():
|
||||
|
||||
|
||||
def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=None, op='model'): # pylint: disable=unused-argument
|
||||
if op != 'model' and checkpoint_info is None and (shared.cmd_opts.ckpt is None or shared.cmd_opts.ckpt.lower() == 'none'):
|
||||
return
|
||||
import torch # pylint: disable=reimported,redefined-outer-name
|
||||
devices.set_cuda_params()
|
||||
if timer is None:
|
||||
timer = Timer()
|
||||
import logging
|
||||
@@ -570,7 +567,6 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
|
||||
if (model_data.sd_refiner is not None) and (checkpoint_info is not None) and (checkpoint_info.hash == model_data.sd_refiner.sd_checkpoint_info.hash): # trying to load the same model
|
||||
return
|
||||
|
||||
shared.log.debug(f'Diffusers load {op} config: {diffusers_load_config}')
|
||||
sd_model = None
|
||||
|
||||
try:
|
||||
@@ -580,7 +576,9 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
|
||||
if model_name is not None:
|
||||
shared.log.info(f'Loading diffuser {op}: {model_name}')
|
||||
model_file = modelloader.download_diffusers_model(hub_id=model_name)
|
||||
devices.set_cuda_params()
|
||||
try:
|
||||
shared.log.debug(f'Diffusers load {op} config: {diffusers_load_config}')
|
||||
sd_model = diffusers.DiffusionPipeline.from_pretrained(model_file, **diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Diffusers failed loading model: {model_file} {e}')
|
||||
@@ -592,8 +590,8 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
|
||||
if checkpoint_info is None:
|
||||
unload_model_weights(op=op)
|
||||
return
|
||||
shared.log.info(f'Loading diffuser {op}: {checkpoint_info.filename}')
|
||||
|
||||
devices.set_cuda_params()
|
||||
vae = None
|
||||
if op == 'model' or op == 'refiner':
|
||||
vae_file, vae_source = sd_vae.resolve_vae(checkpoint_info.filename)
|
||||
@@ -601,8 +599,10 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
|
||||
if vae is not None:
|
||||
diffusers_load_config["vae"] = vae
|
||||
|
||||
shared.log.info(f'Loading diffuser {op}: {checkpoint_info.filename}')
|
||||
if not os.path.isfile(checkpoint_info.path):
|
||||
try:
|
||||
shared.log.debug(f'Diffusers load {op} config: {diffusers_load_config}')
|
||||
sd_model = diffusers.DiffusionPipeline.from_pretrained(checkpoint_info.path, **diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Diffusers {op} failed loading model: {checkpoint_info.path} {e}')
|
||||
|
||||
@@ -23,9 +23,7 @@ def list_samplers(backend_name = shared.backend):
|
||||
samplers = all_samplers
|
||||
samplers_for_img2img = all_samplers
|
||||
samplers_map = {}
|
||||
shared.log.debug(f'Samplers enumerated: {[x.name for x in all_samplers]}')
|
||||
|
||||
list_samplers()
|
||||
shared.log.debug(f'Available samplers: {[x.name for x in all_samplers]}')
|
||||
|
||||
|
||||
def find_sampler_config(name):
|
||||
|
||||
@@ -2,17 +2,16 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import torch
|
||||
from PIL import Image
|
||||
from modules import devices, processing, images, sd_vae_approx, sd_samplers, sd_vae_taesd
|
||||
from modules.shared import opts, state
|
||||
from modules import devices, processing, images, sd_vae_approx, sd_samplers
|
||||
import modules.shared as shared
|
||||
|
||||
import modules.taesd.sd_vae_taesd as sd_vae_taesd
|
||||
|
||||
SamplerData = namedtuple('SamplerData', ['name', 'constructor', 'aliases', 'options'])
|
||||
approximation_indexes = {"Full VAE": 0, "Approximate NN": 1, "Approximate simple": 2, "TAESD": 3}
|
||||
|
||||
|
||||
def setup_img2img_steps(p, steps=None):
|
||||
if opts.img2img_fix_steps or steps is not None:
|
||||
if shared.opts.img2img_fix_steps or steps is not None:
|
||||
requested_steps = (steps or p.steps)
|
||||
steps = int(requested_steps / min(p.denoising_strength, 0.999)) if p.denoising_strength > 0 else 0
|
||||
t_enc = requested_steps - 1
|
||||
@@ -25,7 +24,7 @@ def setup_img2img_steps(p, steps=None):
|
||||
|
||||
def single_sample_to_image(sample, approximation=None):
|
||||
if approximation is None:
|
||||
approximation = approximation_indexes.get(opts.show_progress_type, 0)
|
||||
approximation = approximation_indexes.get(shared.opts.show_progress_type, 0)
|
||||
if approximation == 0:
|
||||
x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] * 0.5 + 0.5
|
||||
elif approximation == 1:
|
||||
@@ -33,8 +32,9 @@ def single_sample_to_image(sample, approximation=None):
|
||||
elif approximation == 2:
|
||||
x_sample = sd_vae_approx.cheap_approximation(sample) * 0.5 + 0.5
|
||||
elif approximation == 3:
|
||||
x_sample = sample * 1.5
|
||||
x_sample = sd_vae_taesd.model()(x_sample.to(devices.device, devices.dtype).unsqueeze(0))[0].detach()
|
||||
# x_sample = sample * 1.5
|
||||
# x_sample = sd_vae_taesd.model()(x_sample.to(devices.device, devices.dtype).unsqueeze(0))[0].detach()
|
||||
x_sample = sd_vae_taesd.decode(sample)
|
||||
else:
|
||||
shared.log.warning(f"Unknown image decode type: {approximation}")
|
||||
return Image.new(mode="RGB", size=(512, 512))
|
||||
@@ -52,10 +52,11 @@ def samples_to_image_grid(samples, approximation=None):
|
||||
|
||||
|
||||
def store_latent(decoded):
|
||||
state.current_latent = decoded
|
||||
if opts.live_previews_enable and opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % opts.show_progress_every_n_steps == 0:
|
||||
shared.state.current_latent = decoded
|
||||
if shared.opts.live_previews_enable and shared.opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % shared.opts.show_progress_every_n_steps == 0:
|
||||
if not shared.parallel_processing_allowed:
|
||||
shared.state.assign_current_image(sample_to_image(decoded))
|
||||
image = sample_to_image(decoded)
|
||||
shared.state.assign_current_image(image)
|
||||
|
||||
|
||||
def is_sampler_using_eta_noise_seed_delta(p):
|
||||
|
||||
+4
-3
@@ -196,9 +196,10 @@ def load_vae_diffusers(_model, vae_file=None, vae_source="from unknown source"):
|
||||
try:
|
||||
import diffusers
|
||||
if os.path.isfile(vae_file):
|
||||
# load_config passed to from_single_file doesn't apply
|
||||
# from_single_file by default downloads VAE1.5 config
|
||||
shared.log.warning("Using SDXL VAE loaded from singular file will result in low contrast images.")
|
||||
if shared.opts.diffusers_pipeline == "Stable Diffusion XL":
|
||||
# load_config passed to from_single_file doesn't apply
|
||||
# from_single_file by default downloads VAE1.5 config
|
||||
shared.log.warning("Using SDXL VAE loaded from singular file will result in low contrast images.")
|
||||
vae = diffusers.AutoencoderKL.from_single_file(vae_file)
|
||||
vae = vae.to(devices.dtype_vae)
|
||||
else:
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
"""
|
||||
Tiny AutoEncoder for Stable Diffusion
|
||||
(DNN for encoding / decoding SD's latent space)
|
||||
|
||||
https://github.com/madebyollin/taesd
|
||||
"""
|
||||
import os
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from modules import devices, paths_internal
|
||||
|
||||
sd_vae_taesd = None
|
||||
|
||||
|
||||
def conv(n_in, n_out, **kwargs):
|
||||
return nn.Conv2d(n_in, n_out, 3, padding=1, **kwargs)
|
||||
|
||||
|
||||
class Clamp(nn.Module):
|
||||
@staticmethod
|
||||
def forward(x):
|
||||
return torch.tanh(x / 3) * 3
|
||||
|
||||
|
||||
class Block(nn.Module):
|
||||
def __init__(self, n_in, n_out):
|
||||
super().__init__()
|
||||
self.conv = nn.Sequential(conv(n_in, n_out), nn.ReLU(), conv(n_out, n_out), nn.ReLU(), conv(n_out, n_out))
|
||||
self.skip = nn.Conv2d(n_in, n_out, 1, bias=False) if n_in != n_out else nn.Identity()
|
||||
self.fuse = nn.ReLU()
|
||||
|
||||
def forward(self, x):
|
||||
return self.fuse(self.conv(x) + self.skip(x))
|
||||
|
||||
|
||||
def decoder():
|
||||
return nn.Sequential(
|
||||
Clamp(), conv(4, 64), nn.ReLU(),
|
||||
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
|
||||
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
|
||||
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
|
||||
Block(64, 64), conv(64, 3),
|
||||
)
|
||||
|
||||
|
||||
class TAESD(nn.Module): # pylint: disable=abstract-method
|
||||
latent_magnitude = 3
|
||||
latent_shift = 0.5
|
||||
|
||||
def __init__(self, decoder_path="taesd_decoder.pth"):
|
||||
"""Initialize pretrained TAESD on the given device from the given checkpoints."""
|
||||
super().__init__()
|
||||
self.decoder = decoder()
|
||||
self.decoder.load_state_dict(
|
||||
torch.load(decoder_path, map_location='cpu' if devices.device.type != 'cuda' else None))
|
||||
|
||||
@staticmethod
|
||||
def unscale_latents(x):
|
||||
"""[0, 1] -> raw latents"""
|
||||
return x.sub(TAESD.latent_shift).mul(2 * TAESD.latent_magnitude)
|
||||
|
||||
|
||||
def download_model(model_path):
|
||||
model_url = 'https://github.com/madebyollin/taesd/raw/main/taesd_decoder.pth'
|
||||
|
||||
if not os.path.exists(model_path):
|
||||
os.makedirs(os.path.dirname(model_path), exist_ok=True)
|
||||
|
||||
print(f'Downloading TAESD decoder to: {model_path}')
|
||||
torch.hub.download_url_to_file(model_url, model_path)
|
||||
|
||||
|
||||
def model():
|
||||
global sd_vae_taesd # pylint: disable=global-statement
|
||||
|
||||
if sd_vae_taesd is None:
|
||||
model_path = os.path.join(paths_internal.models_path, "VAE-taesd", "taesd_decoder.pth")
|
||||
download_model(model_path)
|
||||
|
||||
if os.path.exists(model_path):
|
||||
sd_vae_taesd = TAESD(model_path)
|
||||
sd_vae_taesd.eval()
|
||||
sd_vae_taesd.to(devices.device, devices.dtype)
|
||||
else:
|
||||
raise FileNotFoundError('TAESD model not found')
|
||||
|
||||
return sd_vae_taesd.decoder
|
||||
+25
-11
@@ -171,13 +171,11 @@ class State:
|
||||
return
|
||||
import modules.sd_samplers # pylint: disable=W0621
|
||||
try:
|
||||
if opts.show_progress_grid:
|
||||
self.assign_current_image(modules.sd_samplers.samples_to_image_grid(self.current_latent))
|
||||
else:
|
||||
self.assign_current_image(modules.sd_samplers.sample_to_image(self.current_latent))
|
||||
except Exception:
|
||||
pass
|
||||
self.current_image_sampling_step = self.sampling_step
|
||||
image = modules.sd_samplers.samples_to_image_grid(self.current_latent) if opts.show_progress_grid else modules.sd_samplers.sample_to_image(self.current_latent)
|
||||
self.assign_current_image(image)
|
||||
self.current_image_sampling_step = self.sampling_step
|
||||
except Exception as e:
|
||||
log.error(f'Error setting current image: step={self.sampling_step} {e}')
|
||||
|
||||
def assign_current_image(self, image):
|
||||
self.current_image = image
|
||||
@@ -382,8 +380,8 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
|
||||
"rollback_vae": OptionInfo(False, "Attempt VAE roll back when produced NaN values (experimental)"),
|
||||
"opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "),
|
||||
"cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"),
|
||||
"cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"),
|
||||
"cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"),
|
||||
# "cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"),
|
||||
# "cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"),
|
||||
"cuda_compile": OptionInfo(False, "Enable model compile (experimental)"),
|
||||
"cuda_compile_backend": OptionInfo("none", "Model compile backend (experimental)", gr.Radio, lambda: {"choices": ['none', 'inductor', 'cudagraphs', 'aot_ts_nvfuser', 'hidet', 'ipex']}),
|
||||
"cuda_compile_mode": OptionInfo("default", "Model compile mode (experimental)", gr.Radio, lambda: {"choices": ['default', 'reduce-overhead', 'max-autotune']}),
|
||||
@@ -398,7 +396,6 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
|
||||
options_templates.update(options_section(('diffusers', "Diffusers Settings"), {
|
||||
"diffusers_allow_safetensors": OptionInfo(True, 'Diffusers allow loading from safetensors files'),
|
||||
"diffusers_pipeline": OptionInfo(pipelines[0], 'Diffusers pipeline', gr.Dropdown, lambda: {"choices": pipelines}),
|
||||
"diffusers_refiner_latents": OptionInfo(True, "Use latents when using refiner"),
|
||||
"diffusers_move_base": OptionInfo(False, "Move base model to CPU when using refiner"),
|
||||
"diffusers_move_refiner": OptionInfo(True, "Move refiner model to CPU when not in use"),
|
||||
"diffusers_move_unet": OptionInfo(False, "Move UNet to CPU while VAE decoding"),
|
||||
@@ -409,7 +406,7 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), {
|
||||
"diffusers_vae_upcast": OptionInfo("default", "VAE upcasting", gr.Radio, lambda: {"choices": ['default', 'true', 'false']}),
|
||||
"diffusers_vae_slicing": OptionInfo(True, "Enable VAE slicing"),
|
||||
"diffusers_vae_tiling": OptionInfo(False, "Enable VAE tiling"),
|
||||
"diffusers_attention_slicing": OptionInfo(False, "Enable attention slicing"),
|
||||
"diffusers_attention_slicing": OptionInfo(True if devices.backend == "ipex" else False, "Enable attention slicing"),
|
||||
"diffusers_model_load_variant": OptionInfo("default", "Diffusers model loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}),
|
||||
"diffusers_vae_load_variant": OptionInfo("default", "Diffusers VAE loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}),
|
||||
# "diffusers_force_zeros": OptionInfo(False, "Force zeros for prompts when empty"),
|
||||
@@ -984,7 +981,24 @@ class Shared(sys.modules[__name__].__class__): # this class is here to provide s
|
||||
def backend(self):
|
||||
return Backend.ORIGINAL if opts.data['sd_backend'] == 'original' else Backend.DIFFUSERS
|
||||
|
||||
@property
|
||||
def sd_model_type(self):
|
||||
try:
|
||||
if backend == Backend.ORIGINAL:
|
||||
model_type = 'ldm'
|
||||
elif "StableDiffusionXL" in self.sd_model.__class__.__name__:
|
||||
model_type = 'sdxl'
|
||||
elif "StableDiffusion" in self.sd_model.__class__.__name__:
|
||||
model_type = 'sd'
|
||||
elif "Kandinsky" in self.sd_model.__class__.__name__:
|
||||
model_type = 'kandinsky'
|
||||
else:
|
||||
model_type = self.sd_model.__class__.__name__
|
||||
except Exception:
|
||||
model_type = 'unknown'
|
||||
return model_type
|
||||
|
||||
sd_model = None
|
||||
sd_refiner = None
|
||||
sd_model_type = ''
|
||||
sys.modules[__name__].__class__ = Shared
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Tiny AutoEncoder for Stable Diffusion
|
||||
(DNN for encoding / decoding SD's latent space)
|
||||
|
||||
https://github.com/madebyollin/taesd
|
||||
"""
|
||||
import os
|
||||
from PIL import Image
|
||||
from modules import devices, paths_internal
|
||||
from modules.taesd.taesd import TAESD
|
||||
|
||||
taesd_models = { 'sd-decoder': None, 'sd-encoder': None, 'sdxl-decoder': None, 'sdxl-encoder': None }
|
||||
|
||||
def download_model(model_path):
|
||||
model_name = os.path.basename(model_path)
|
||||
model_url = f'https://github.com/madebyollin/taesd/raw/main/{model_name}'
|
||||
if not os.path.exists(model_path):
|
||||
os.makedirs(os.path.dirname(model_path), exist_ok=True)
|
||||
from modules.shared import log
|
||||
log.info(f'Downloading TAESD decoder: {model_path}')
|
||||
import torch
|
||||
torch.hub.download_url_to_file(model_url, model_path)
|
||||
|
||||
|
||||
def model(model_class = 'sd', model_type = 'decoder'):
|
||||
vae = taesd_models[f'{model_class}-{model_type}']
|
||||
if vae is None:
|
||||
model_path = os.path.join(paths_internal.models_path, "TAESD", f"tae{model_class}_{model_type}.pth")
|
||||
download_model(model_path)
|
||||
if os.path.exists(model_path):
|
||||
taesd_models[f'{model_class}-{model_type}'] = TAESD(decoder_path=model_path, encoder_path=None) if model_type == 'decoder' else TAESD(encoder_path=model_path, decoder_path=None)
|
||||
vae = taesd_models[f'{model_class}-{model_type}']
|
||||
vae.eval()
|
||||
vae.to(devices.device, devices.dtype_vae)
|
||||
else:
|
||||
raise FileNotFoundError('TAESD model not found')
|
||||
if vae is None:
|
||||
return None
|
||||
else:
|
||||
return vae.decoder if model_type == 'decoder' else vae.encoder
|
||||
|
||||
|
||||
def decode(latents):
|
||||
from modules import shared
|
||||
model_class = shared.sd_model_type
|
||||
if model_class == 'ldm':
|
||||
model_class = 'sd'
|
||||
if 'sd' not in model_class:
|
||||
shared.log.warning(f'TAESD unsupported model type: {model_class}')
|
||||
return Image.new('RGB', (8, 8), color = (0, 0, 0))
|
||||
vae = taesd_models[f'{model_class}-decoder']
|
||||
if vae is None:
|
||||
model_path = os.path.join(paths_internal.models_path, "TAESD", f"tae{model_class}_decoder.pth")
|
||||
download_model(model_path)
|
||||
if os.path.exists(model_path):
|
||||
taesd_models[f'{model_class}-decoder'] = TAESD(decoder_path=model_path, encoder_path=None)
|
||||
vae = taesd_models[f'{model_class}-decoder']
|
||||
vae.to(devices.device, devices.dtype_vae)
|
||||
enc = latents.unsqueeze(0).to(devices.device, devices.dtype_vae)
|
||||
image = vae.decoder(enc).clamp(0, 1).detach()
|
||||
return image[0]
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tiny AutoEncoder for Stable Diffusion
|
||||
(DNN for encoding / decoding SD's latent space)
|
||||
"""
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
def conv(n_in, n_out, **kwargs):
|
||||
return nn.Conv2d(n_in, n_out, 3, padding=1, **kwargs)
|
||||
|
||||
class Clamp(nn.Module):
|
||||
def forward(self, x):
|
||||
return torch.tanh(x / 3) * 3
|
||||
|
||||
class Block(nn.Module):
|
||||
def __init__(self, n_in, n_out):
|
||||
super().__init__()
|
||||
self.conv = nn.Sequential(conv(n_in, n_out), nn.ReLU(), conv(n_out, n_out), nn.ReLU(), conv(n_out, n_out))
|
||||
self.skip = nn.Conv2d(n_in, n_out, 1, bias=False) if n_in != n_out else nn.Identity()
|
||||
self.fuse = nn.ReLU()
|
||||
def forward(self, x):
|
||||
return self.fuse(self.conv(x) + self.skip(x))
|
||||
|
||||
def Encoder():
|
||||
return nn.Sequential(
|
||||
conv(3, 64), Block(64, 64),
|
||||
conv(64, 64, stride=2, bias=False), Block(64, 64), Block(64, 64), Block(64, 64),
|
||||
conv(64, 64, stride=2, bias=False), Block(64, 64), Block(64, 64), Block(64, 64),
|
||||
conv(64, 64, stride=2, bias=False), Block(64, 64), Block(64, 64), Block(64, 64),
|
||||
conv(64, 4),
|
||||
)
|
||||
|
||||
def Decoder():
|
||||
return nn.Sequential(
|
||||
Clamp(), conv(4, 64), nn.ReLU(),
|
||||
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
|
||||
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
|
||||
Block(64, 64), Block(64, 64), Block(64, 64), nn.Upsample(scale_factor=2), conv(64, 64, bias=False),
|
||||
Block(64, 64), conv(64, 3),
|
||||
)
|
||||
|
||||
class TAESD(nn.Module):
|
||||
latent_magnitude = 3
|
||||
latent_shift = 0.5
|
||||
|
||||
def __init__(self, encoder_path="taesd_encoder.pth", decoder_path="taesd_decoder.pth"):
|
||||
"""Initialize pretrained TAESD on the given device from the given checkpoints."""
|
||||
super().__init__()
|
||||
self.encoder = Encoder()
|
||||
self.decoder = Decoder()
|
||||
if encoder_path is not None:
|
||||
self.encoder.load_state_dict(torch.load(encoder_path, map_location="cpu"))
|
||||
if decoder_path is not None:
|
||||
self.decoder.load_state_dict(torch.load(decoder_path, map_location="cpu"))
|
||||
|
||||
@staticmethod
|
||||
def scale_latents(x):
|
||||
"""raw latents -> [0, 1]"""
|
||||
return x.div(2 * TAESD.latent_magnitude).add(TAESD.latent_shift).clamp(0, 1)
|
||||
|
||||
@staticmethod
|
||||
def unscale_latents(x):
|
||||
"""[0, 1] -> raw latents"""
|
||||
return x.sub(TAESD.latent_shift).mul(2 * TAESD.latent_magnitude)
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def main():
|
||||
from PIL import Image
|
||||
import sys
|
||||
import torchvision.transforms.functional as TF
|
||||
dev = torch.device("cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu")
|
||||
print("Using device", dev)
|
||||
taesd = TAESD().to(dev)
|
||||
for im_path in sys.argv[1:]:
|
||||
im = TF.to_tensor(Image.open(im_path).convert("RGB")).unsqueeze(0).to(dev)
|
||||
|
||||
# encode image, quantize, and save to file
|
||||
im_enc = taesd.scale_latents(taesd.encoder(im)).mul_(255).round_().byte()
|
||||
enc_path = im_path + ".encoded.png"
|
||||
TF.to_pil_image(im_enc[0]).save(enc_path)
|
||||
print(f"Encoded {im_path} to {enc_path}")
|
||||
|
||||
# load the saved file, dequantize, and decode
|
||||
im_enc = taesd.unscale_latents(TF.to_tensor(Image.open(enc_path)).unsqueeze(0).to(dev))
|
||||
im_dec = taesd.decoder(im_enc).clamp(0, 1)
|
||||
dec_path = im_path + ".decoded.png"
|
||||
print(f"Decoded {enc_path} to {dec_path}")
|
||||
TF.to_pil_image(im_dec[0]).save(dec_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -7,7 +7,7 @@ from modules.memstats import memory_stats
|
||||
|
||||
def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, latent_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, diffusers_guidance_rescale: float, clip_skip: int, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, refiner_start: int, refiner_prompt: str, refiner_negative: str, override_settings_texts, *args): # pylint: disable=unused-argument
|
||||
|
||||
shared.log.debug(f'txt2img: id_task={id_task}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|clip_skip={clip_skip}|seed={seed}|subseed={subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}||height={height}|width={width}|enable_hr={enable_hr}|denoising_strength={denoising_strength}|hr_scale={hr_scale}|hr_upscaler={hr_upscaler}|hr_second_pass_steps={hr_second_pass_steps}|hr_resize_x={hr_resize_x}|hr_resize_y={hr_resize_y}|image_cfg_scale={image_cfg_scale}|diffusers_guidance_rescale={diffusers_guidance_rescale}|refiner_start={refiner_start}||refiner_prompt={refiner_prompt}|refiner_negative={refiner_negative}|override_settings_texts={override_settings_texts}args={args}')
|
||||
shared.log.debug(f'txt2img: id_task={id_task}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|clip_skip={clip_skip}|seed={seed}|subseed={subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}||height={height}|width={width}|enable_hr={enable_hr}|denoising_strength={denoising_strength}|hr_scale={hr_scale}|hr_upscaler={hr_upscaler}|hr_second_pass_steps={hr_second_pass_steps}|hr_resize_x={hr_resize_x}|hr_resize_y={hr_resize_y}|image_cfg_scale={image_cfg_scale}|diffusers_guidance_rescale={diffusers_guidance_rescale}|refiner_start={refiner_start}||refiner_prompt={refiner_prompt}|refiner_negative={refiner_negative}|override_settings_texts={override_settings_texts}|args={args}')
|
||||
|
||||
if shared.sd_model is None:
|
||||
shared.log.warning('Model not loaded')
|
||||
|
||||
@@ -48,6 +48,7 @@ ignore = [
|
||||
"C408", # Rewrite as a literal
|
||||
"E402", # Module level import not at top of file
|
||||
"F401", # Imported but unused
|
||||
"EXE001", # Shebang present
|
||||
"ISC003", # Implicit string concatenation
|
||||
"RUF005", # Consider concatenation
|
||||
"RUF012", # Mutable class attributes
|
||||
|
||||
@@ -52,6 +52,7 @@ opencv-python-headless==4.7.0.72
|
||||
diffusers==0.19.3
|
||||
einops==0.4.1
|
||||
gradio==3.32.0
|
||||
huggingface_hub==0.16.4
|
||||
numexpr==2.8.4
|
||||
numpy==1.23.5
|
||||
numba==0.57.0
|
||||
|
||||
@@ -105,6 +105,9 @@ def initialize():
|
||||
shared.disable_extensions()
|
||||
check_rollback_vae()
|
||||
|
||||
modules.sd_samplers.list_samplers()
|
||||
startup_timer.record("samplers")
|
||||
|
||||
modules.sd_vae.refresh_vae_list()
|
||||
startup_timer.record("vae")
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ if [[ ! -z "${ACCELERATE}" ]] && [ ${ACCELERATE}="True" ] && [ -x "$(command -v
|
||||
then
|
||||
echo "Launching accelerate launch.py..."
|
||||
exec accelerate launch --num_cpu_threads_per_process=6 launch.py "$@"
|
||||
elif [[ -z "${first_launch}" ]] && [[ $(uname -a) != *WSL2* ]] && [ -x "$(command -v ipexrun)" ] && [ -x "$(command -v numactl)" ] && [ -x "$(command -v sycl-ls)" ]
|
||||
elif [[ "$@" == *"--use-ipex"* ]] && [[ -z "${first_launch}" ]] && [[ $(uname -a) != *WSL2* ]] && [ -x "$(command -v ipexrun)" ] && [ -x "$(command -v numactl)" ] && [ -x "$(command -v sycl-ls)" ]
|
||||
then
|
||||
echo "Launching ipexrun launch.py..."
|
||||
exec ipexrun launch.py "$@"
|
||||
|
||||
+1
-1
Submodule wiki updated: f76cc3a9ac...35142f02ae
Reference in New Issue
Block a user