base and refiner mix and match

This commit is contained in:
Vladimir Mandic
2023-09-04 15:55:38 -04:00
parent 616e8f793c
commit 5b622cdfda
16 changed files with 177 additions and 203 deletions
+18 -4
View File
@@ -20,15 +20,29 @@
- **metadata** and **info** buttons only show if there is actual content
- diffusers:
- ability to interrupt (stop/skip) model generate
- add `diffusers_force_zeros` setting
create zero-tensor for prompt if prompt is empty (positive or negative)
- add `diffusers_aesthetics_score` setting
- mix&match **base** and **refiner** models (*experimental*):
most of those are "because why not" and can result in corrupt images, but some are actually useful
also note that if you're not using actual refiner model, you need to bump refiner steps
as normal models are not designed to work with low step count
and if you're having issues, try setting prompt parser to "fixed attention" as majority of problems
are due to token mismatches when using prompt attention
- any sd15 + any sd15
- any sd15 + sdxl-refiner
- any sdxl-base + sdxl-refiner
- any sdxl-base + any sd15
- any sdxl-base + any sdxl-base
- add `diffusers_aesthetics_score` setting (for sdxl)
automatically guide unet towards higher pleasing images
highly recommended for simple prompts
- add `diffusers_force_zeros` setting
create zero-tensor for prompt if prompt is empty (positive or negative)
- general:
- `rembg` remove backgrounds support for **is-net** model
- **settings** now show markers for all items set to non-default values
- pre-create all system folders on startup
- **metadata** refactored how/what/when metadata is added to images
should result in much cleaner and more complete metadata
- pre-create all system folders on startup
- handle model load errors gracefully
## Update for 2023-08-30
+1 -1
View File
@@ -216,7 +216,7 @@
{"id":"","label":"Only masked padding, pixels","localized":"","hint":""},
{"id":"","label":"Scale","localized":"","hint":""},
{"id":"","label":"Unused","localized":"","hint":""},
{"id":"","label":"Image CFG Scale","localized":"","hint":""}
{"id":"","label":"Image CFG scale","localized":"","hint":""}
],
"models tabs": [
{"id":"","label":"Convert","localized":"","hint":""},
+1 -1
View File
@@ -216,7 +216,7 @@
{"id":"","label":"Only masked padding, pixels","localized":"","hint":""},
{"id":"","label":"Scale","localized":"","hint":""},
{"id":"","label":"Unused","localized":"","hint":""},
{"id":"","label":"Image CFG Scale","localized":"","hint":""}
{"id":"","label":"Image CFG scale","localized":"","hint":""}
],
"models tabs": [
{"id":"","label":"Convert","localized":"변환","hint":""},
+3 -4
View File
@@ -3,9 +3,8 @@ function onCalcResolutionHires(enable_hr, width, height, hr_scale, hr_resize_x,
const hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale');
const hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x');
const hrResizeY = gradioApp().getElementById('txt2img_hr_resize_y');
gradioApp().getElementById('txt2img_hires_fix_row3').style.display = opts.use_old_hires_fix_width_height ? 'none' : '';
setInactive(hrUpscaleBy, opts.use_old_hires_fix_width_height || hr_resize_x > 0 || hr_resize_y > 0);
setInactive(hrResizeX, opts.use_old_hires_fix_width_height || hr_resize_x === 0);
setInactive(hrResizeY, opts.use_old_hires_fix_width_height || hr_resize_y === 0);
setInactive(hrUpscaleBy, hr_resize_x > 0 || hr_resize_y > 0);
setInactive(hrResizeX, hr_resize_x === 0);
setInactive(hrResizeY, hr_resize_y === 0);
return [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler];
}
-1
View File
@@ -45,7 +45,6 @@ function markIfModified(setting_name, value) {
const changed_value = previous_value !== current_value;
if (changed_value) elem.title = `click to revert to previous value: ${previous_value}`;
const is_stored = opts_metadata[setting_name].is_stored;
if (is_stored) console.log('A', opts_metadata[setting_name]);
if (is_stored) elem.title = 'custom value';
elem.disabled = !changed_value && !is_stored;
elem.classList.toggle('changed', changed_value);
@@ -202,39 +202,6 @@ def find_hypernetwork_key(hypernet_name, hypernet_hash=None):
return None
def restore_old_hires_fix_params(res):
"""for infotexts that specify old First pass size parameter, convert it into
width, height, and hr scale"""
firstpass_width = res.get('First pass size-1', None)
firstpass_height = res.get('First pass size-2', None)
if shared.opts.use_old_hires_fix_width_height:
hires_width = int(res.get("Hires resize-1", 0))
hires_height = int(res.get("Hires resize-2", 0))
if hires_width and hires_height:
res['Size-1'] = hires_width
res['Size-2'] = hires_height
return
if firstpass_width is None or firstpass_height is None:
return
firstpass_width, firstpass_height = int(firstpass_width), int(firstpass_height)
width = int(res.get("Size-1", 512))
height = int(res.get("Size-2", 512))
if firstpass_width == 0 or firstpass_height == 0:
from modules import processing
firstpass_width, firstpass_height = processing.old_hires_fix_first_pass_dimensions(width, height)
res['Size-1'] = firstpass_width
res['Size-2'] = firstpass_height
res['Hires resize-1'] = width
res['Hires resize-2'] = height
def parse_generation_parameters(x: str):
"""parses generation parameters string, the one you see in text field under the picture in UI:
```
@@ -288,7 +255,6 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
if "Hires resize-1" not in res:
res["Hires resize-1"] = 0
res["Hires resize-2"] = 0
restore_old_hires_fix_params(res)
return res
+3 -1
View File
@@ -385,6 +385,8 @@ class FilenameGenerator:
def apply(self, x):
res = ''
if self.p is None:
return res
for m in re_pattern.finditer(x):
text, pattern = m.groups()
if pattern is None:
@@ -629,7 +631,7 @@ def read_info_from_image(image):
for key, val in subkey.items():
if isinstance(val, bytes): # decode bytestring
val = safe_decode_string(val)
if isinstance(val, tuple) and isinstance(val[0], int) and isinstance(val[1], int): # convert camera ratios
if isinstance(val, tuple) and isinstance(val[0], int) and isinstance(val[1], int) and val[1] > 0: # convert camera ratios
val = round(val[0] / val[1], 2)
if val is not None and key in ExifTags.TAGS: # add known tags
if ExifTags.TAGS[key] == 'UserComment': # add geninfo from UserComment
+107 -99
View File
@@ -455,7 +455,8 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
vae = (None if not shared.opts.add_model_name_to_info or sd_vae.loaded_vae_file is None else os.path.splitext(os.path.basename(sd_vae.loaded_vae_file))[0]) if p.full_quality else 'TAESD'
comment = ', '.join(comments) if comments is not None and type(comments) is list else None
generation_params = {
args = {
# basic
"Steps": p.steps,
"Seed": all_seeds[index],
"Sampler": p.sampler_name,
@@ -465,42 +466,66 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
"Parser": shared.opts.prompt_attention,
"Model": None if (not shared.opts.add_model_name_to_info) or (not shared.sd_model.sd_checkpoint_info.model_name) else shared.sd_model.sd_checkpoint_info.model_name.replace(',', '').replace(':', ''),
"Model hash": getattr(p, 'sd_model_hash', None if (not shared.opts.add_model_hash_to_info) or (not shared.sd_model.sd_model_hash) else shared.sd_model.sd_model_hash),
"Refiner": None if (not shared.opts.add_model_name_to_info) or (not shared.sd_refiner) or (not shared.sd_refiner.sd_checkpoint_info.model_name) else shared.sd_refiner.sd_checkpoint_info.model_name.replace(',', '').replace(':', ''),
"VAE": vae,
# subseed
"Variation seed": None if p.subseed_strength == 0 else all_subseeds[index],
"Variation strength": None if p.subseed_strength == 0 else p.subseed_strength,
# seed resize
"Seed resize from": None if p.seed_resize_from_w == 0 or p.seed_resize_from_h == 0 else f"{p.seed_resize_from_w}x{p.seed_resize_from_h}",
"Init image hash": getattr(p, 'init_img_hash', None),
"Conditional mask weight": getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None,
# clip skip
"Clip skip": p.clip_skip if p.clip_skip > 1 else None,
# ensd
"Prompt2": p.refiner_prompt if len(p.refiner_prompt) > 0 else None,
"Negative2": p.refiner_negative if len(p.refiner_negative) > 0 else None,
# other
"ENSD": shared.opts.eta_noise_seed_delta if shared.opts.eta_noise_seed_delta != 0 and sd_samplers_common.is_sampler_using_eta_noise_seed_delta(p) else None,
# restore_faces, tiling
"Face restoration": shared.opts.face_restoration_model if p.restore_faces else None,
"Tiling": p.tiling if p.tiling else None,
# enable_hr
"Prompt2": p.refiner_prompt if p.enable_hr and len(p.refiner_prompt) > 0 else None,
"Negative2": p.refiner_negative if p.enable_hr and len(p.refiner_negative) > 0 else None,
"Latent sampler": p.latent_sampler if p.enable_hr and p.latent_sampler != p.sampler_name else None,
"Denoising strength": p.denoising_strength if p.enable_hr else None,
"Image CFG Scale": p.image_cfg_scale,
# sdnext
"Backend": 'Diffusers' if shared.backend == shared.Backend.DIFFUSERS else 'Original',
"Version": git_commit,
"Comment": comment,
"Operations": ', '.join(list(set(p.ops))) if len(p.ops) > 0 else None,
"Operations": ', '.join(list(set(p.ops))).replace('"', '') if len(p.ops) > 0 else None,
}
if 'txt2img' in p.ops:
pass
if 'hires' in p.ops:
args["Hires steps"] = p.hr_second_pass_steps
args["Hires upscaler"] = p.hr_upscaler
args["Hires upscale"] = p.hr_scale
args["Hires resize"] = f"{p.hr_resize_x}x{p.hr_resize_y}"
args["Hires size"] = f"{p.hr_upscale_to_x}x{p.hr_upscale_to_y}"
args["Denoising strength"] = p.denoising_strength
args["Latent sampler"] = p.latent_sampler
args["Image CFG scale"] = p.image_cfg_scale
args["CFG rescale"] = p.diffusers_guidance_rescale if shared.backend == shared.Backend.DIFFUSERS else None
if 'refine' in p.ops:
args["Refiner"] = None if (not shared.opts.add_model_name_to_info) or (not shared.sd_refiner) or (not shared.sd_refiner.sd_checkpoint_info.model_name) else shared.sd_refiner.sd_checkpoint_info.model_name.replace(',', '').replace(':', '')
args['Image CFG scale'] = p.image_cfg_scale
args['Refiner steps'] = p.refiner_steps
args['Refiner start'] = p.refiner_start
args["Hires steps"] = p.hr_second_pass_steps
args["Latent sampler"] = p.latent_sampler
args["CFG rescale"] = p.diffusers_guidance_rescale if shared.backend == shared.Backend.DIFFUSERS else None
if 'img2img' in p.ops or 'inpaint' in p.ops:
args["Init image size"] = f"{getattr(p, 'init_img_width', 0)}x{getattr(p, 'init_img_height', 0)}"
args["Init image hash"] = getattr(p, 'init_img_hash', None)
args["Conditional mask weight"] = getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None
args['Resize mode'] = p.resize_mode
args["Mask blur"] = p.mask_blur if p.mask is not None and p.mask_blur > 0 else None
args["Noise multiplier"] = p.initial_noise_multiplier if p.initial_noise_multiplier != 1.0 else None
args["Denoising strength"] = p.denoising_strength
if 'face' in p.ops:
args["Face restoration"] = shared.opts.face_restoration_model
if 'color' in p.ops:
args["Color correction"] = True
# tome
token_merging_ratio = p.get_token_merging_ratio()
token_merging_ratio_hr = p.get_token_merging_ratio(for_hr=True) if p.enable_hr else None
generation_params['Token merging ratio'] = token_merging_ratio if token_merging_ratio != 0 else None
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])
args['Token merging ratio'] = token_merging_ratio if token_merging_ratio != 0 else None
args['Token merging ratio hr'] = token_merging_ratio_hr if token_merging_ratio_hr != 0 else None
args.update(p.extra_generation_params)
params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in args.items() if v is not None])
negative_prompt_text = f"\nNegative prompt: {all_negative_prompts[index]}" if all_negative_prompts[index] else ""
infotext = f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
infotext = f"{all_prompts[index]}{negative_prompt_text}\n{params_text}".strip()
return infotext
@@ -886,19 +911,10 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
self.height = self.height or 512
def init_hr(self):
if shared.opts.use_old_hires_fix_width_height and self.applied_old_hires_behavior_to != (self.width, self.height):
self.hr_resize_x = self.width
self.hr_resize_y = self.height
self.hr_upscale_to_x = self.width
self.hr_upscale_to_y = self.height
self.width, self.height = old_hires_fix_first_pass_dimensions(self.width, self.height)
self.applied_old_hires_behavior_to = (self.width, self.height)
if self.hr_resize_x == 0 and self.hr_resize_y == 0:
self.extra_generation_params["Hires upscale"] = self.hr_scale
self.hr_upscale_to_x = int(self.width * self.hr_scale)
self.hr_upscale_to_y = int(self.height * self.hr_scale)
else:
self.extra_generation_params["Hires resize"] = f"{self.hr_resize_x}x{self.hr_resize_y}"
if self.hr_resize_y == 0:
self.hr_upscale_to_x = self.hr_resize_x
self.hr_upscale_to_y = self.hr_resize_x * self.height // self.width
@@ -908,10 +924,6 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
else:
target_w = self.hr_resize_x
target_h = self.hr_resize_y
"""
self.hr_upscale_to_x = self.hr_resize_x
self.hr_upscale_to_y = self.hr_resize_y
"""
src_ratio = self.width / self.height
dst_ratio = self.hr_resize_x / self.hr_resize_y
if src_ratio < dst_ratio:
@@ -923,21 +935,16 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
self.truncate_x = (self.hr_upscale_to_x - target_w) // 8
self.truncate_y = (self.hr_upscale_to_y - target_h) // 8
# special case: the user has chosen to do nothing
if self.hr_upscale_to_x == self.width and self.hr_upscale_to_y == self.height:
self.extra_generation_params.pop("Hires upscale", None)
self.extra_generation_params.pop("Hires resize", None)
if (self.hr_upscale_to_x == self.width and self.hr_upscale_to_y == self.height) or self.hr_upscaler is None or self.hr_upscaler == 'None':
self.is_hr_pass = False
return
self.is_hr_pass = True
if not shared.state.processing_has_refined_job_count:
if shared.state.job_count == -1:
shared.state.job_count = self.n_iter
shared.state.job_count = shared.state.job_count * 2
shared.state.processing_has_refined_job_count = True
if self.hr_second_pass_steps:
self.extra_generation_params["Hires steps"] = self.hr_second_pass_steps
if self.hr_upscaler is not None:
self.extra_generation_params["Hires upscaler"] = self.hr_upscaler
shared.log.debug(f'Init hires: upscaler={self.hr_upscaler} sampler={self.latent_sampler} resize={self.hr_resize_x}x{self.hr_resize_y} upscale={self.hr_upscale_to_x}x{self.hr_upscale_to_y}')
self.extra_generation_params["Secondary sampler"] = self.latent_sampler
shared.log.debug(f'Init hires: upscaler={self.hr_upscaler} sampler={self.latent_sampler} resize={self.hr_resize_x}x{self.hr_resize_y} upscale={self.hr_upscale_to_x}x{self.hr_upscale_to_y}')
def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
@@ -961,7 +968,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
self.ops.append('txt2img')
self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model)
latent_scale_mode = shared.latent_upscale_modes.get(self.hr_upscaler, None) if self.hr_upscaler is not None else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "nearest")
latent_scale_mode = shared.latent_upscale_modes.get(self.hr_upscaler, None) if self.hr_upscaler is not None else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "None")
if self.enable_hr and latent_scale_mode is None:
if len([x for x in shared.sd_upscalers if x.name == self.hr_upscaler]) == 0:
shared.log.warning("Could not find upscaler to use with hrfix")
@@ -970,57 +977,59 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
samples = self.sampler.sample(self, x, conditioning, unconditional_conditioning, image_conditioning=self.txt2img_image_conditioning(x))
if not self.enable_hr or shared.state.interrupted or shared.state.skipped:
return samples
self.is_hr_pass = True
self.init_hr()
self.ops.append('hires')
target_width = self.hr_upscale_to_x
target_height = self.hr_upscale_to_y
if latent_scale_mode is not None:
for i in range(samples.shape[0]):
save_intermediate(samples, i)
samples = torch.nn.functional.interpolate(samples, size=(target_height // 8, target_width // 8), mode=latent_scale_mode["mode"], antialias=latent_scale_mode["antialias"])
if getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0:
image_conditioning = self.img2img_image_conditioning(decode_first_stage(self.sd_model, samples.to(dtype=devices.dtype_vae)), samples)
self.init_hr()
if self.is_hr_pass:
self.ops.append('hires')
target_width = self.hr_upscale_to_x
target_height = self.hr_upscale_to_y
if latent_scale_mode is not None:
for i in range(samples.shape[0]):
save_intermediate(samples, i)
samples = torch.nn.functional.interpolate(samples, size=(target_height // 8, target_width // 8), mode=latent_scale_mode["mode"], antialias=latent_scale_mode["antialias"])
if getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0:
image_conditioning = self.img2img_image_conditioning(decode_first_stage(self.sd_model, samples.to(dtype=devices.dtype_vae)), samples)
else:
image_conditioning = self.txt2img_image_conditioning(samples.to(dtype=devices.dtype_vae))
else:
image_conditioning = self.txt2img_image_conditioning(samples.to(dtype=devices.dtype_vae))
else:
decoded_samples = decode_first_stage(self.sd_model, samples.to(dtype=devices.dtype_vae))
lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
batch_images = []
for i, x_sample in enumerate(lowres_samples):
x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2)
x_sample = validate_sample(x_sample)
image = Image.fromarray(x_sample)
save_intermediate(image, i)
image = images.resize_image(1, image, target_width, target_height, upscaler_name=self.hr_upscaler)
image = np.array(image).astype(np.float32) / 255.0
image = np.moveaxis(image, 2, 0)
batch_images.append(image)
decoded_samples = torch.from_numpy(np.array(batch_images))
decoded_samples = decoded_samples.to(device=shared.device, dtype=devices.dtype_vae)
decoded_samples = 2. * decoded_samples - 1.
if shared.opts.sd_vae_sliced_encode and len(decoded_samples) > 1:
samples = torch.stack([
self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(torch.unsqueeze(decoded_sample, 0)))[0]
for decoded_sample
in decoded_samples
])
else:
samples = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(decoded_samples))
image_conditioning = self.img2img_image_conditioning(decoded_samples, samples)
shared.state.nextjob()
if self.latent_sampler == "PLMS":
self.latent_sampler = 'UniPC'
self.sampler = sd_samplers.create_sampler(self.latent_sampler or self.sampler_name, self.sd_model)
samples = samples[:, :, self.truncate_y//2:samples.shape[2]-(self.truncate_y+1)//2, self.truncate_x//2:samples.shape[3]-(self.truncate_x+1)//2]
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, p=self)
x = None
devices.torch_gc() # GC now before running the next img2img to prevent running out of memory
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio(for_hr=True))
samples = self.sampler.sample_img2img(self, samples, noise, conditioning, unconditional_conditioning, steps=self.hr_second_pass_steps or self.steps, image_conditioning=image_conditioning)
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio())
self.is_hr_pass = False
decoded_samples = decode_first_stage(self.sd_model, samples.to(dtype=devices.dtype_vae))
lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
batch_images = []
for i, x_sample in enumerate(lowres_samples):
x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2)
x_sample = validate_sample(x_sample)
image = Image.fromarray(x_sample)
save_intermediate(image, i)
image = images.resize_image(1, image, target_width, target_height, upscaler_name=self.hr_upscaler)
image = np.array(image).astype(np.float32) / 255.0
image = np.moveaxis(image, 2, 0)
batch_images.append(image)
decoded_samples = torch.from_numpy(np.array(batch_images))
decoded_samples = decoded_samples.to(device=shared.device, dtype=devices.dtype_vae)
decoded_samples = 2. * decoded_samples - 1.
if shared.opts.sd_vae_sliced_encode and len(decoded_samples) > 1:
samples = torch.stack([
self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(torch.unsqueeze(decoded_sample, 0)))[0]
for decoded_sample
in decoded_samples
])
else:
samples = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(decoded_samples))
image_conditioning = self.img2img_image_conditioning(decoded_samples, samples)
shared.state.nextjob()
if self.latent_sampler == "PLMS":
self.latent_sampler = 'UniPC'
self.sampler = sd_samplers.create_sampler(self.latent_sampler or self.sampler_name, self.sd_model)
samples = samples[:, :, self.truncate_y//2:samples.shape[2]-(self.truncate_y+1)//2, self.truncate_x//2:samples.shape[3]-(self.truncate_x+1)//2]
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, p=self)
x = None
devices.torch_gc() # GC now before running the next img2img to prevent running out of memory
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio(for_hr=True))
samples = self.sampler.sample_img2img(self, samples, noise, conditioning, unconditional_conditioning, steps=self.hr_second_pass_steps or self.steps, image_conditioning=image_conditioning)
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio())
self.is_hr_pass = False
return samples
@@ -1099,9 +1108,10 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
imgs = []
unprocessed = []
for img in self.init_images:
# Save init image
self.init_img_hash = hashlib.sha256(img.tobytes()).hexdigest()[0:8] # pylint: disable=attribute-defined-outside-init
self.init_img_width = img.width # pylint: disable=attribute-defined-outside-init
self.init_img_height = img.height # pylint: disable=attribute-defined-outside-init
if shared.opts.save_init_img:
self.init_img_hash = hashlib.sha256(img.tobytes()).hexdigest()[0:8] # pylint: disable=attribute-defined-outside-init
images.save_image(img, path=shared.opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False)
image = images.flatten(img, shared.opts.img2img_background_color)
if crop_region is None and self.resize_mode != 4:
@@ -1174,9 +1184,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
self.sd_model.dtype = self.sd_model.unet.dtype
x = create_random_tensors([4, self.height // 8, self.width // 8], seeds=seeds, subseeds=subseeds, subseed_strength=self.subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self)
if self.initial_noise_multiplier != 1.0:
self.extra_generation_params["Noise multiplier"] = self.initial_noise_multiplier
x *= self.initial_noise_multiplier
x *= self.initial_noise_multiplier
samples = self.sampler.sample_img2img(self, self.init_latent, x, conditioning, unconditional_conditioning, image_conditioning=self.image_conditioning)
if self.mask is not None:
samples = samples * self.nmask + self.init_latent * self.mask
+15 -25
View File
@@ -44,8 +44,8 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
from modules.processing import create_infotext
info=create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, [], iteration=p.iteration, position_in_batch=i)
decoded = vae_decode(latents=latents, model=shared.sd_model, output_type='pil', full_quality=p.full_quality)
for i in range(len(decoded)):
images.save_image(decoded[i], path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix=suffix)
for j in range(len(decoded)):
images.save_image(decoded[j], path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix=suffix)
def diffusers_callback(_step: int, _timestep: int, latents: torch.FloatTensor):
shared.state.sampling_step += 1
@@ -123,12 +123,15 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
negative_prompts_2.append(negative_prompts_2[-1])
return prompts, negative_prompts, prompts_2, negative_prompts_2
def set_pipeline_args(model, prompts: list, negative_prompts: list, prompts_2: typing.Optional[list]=None, negative_prompts_2: typing.Optional[list]=None, is_refiner: bool=False, desc:str='', **kwargs):
def set_pipeline_args(model, prompts: list, negative_prompts: list, prompts_2: typing.Optional[list]=None, negative_prompts_2: typing.Optional[list]=None, desc:str='', **kwargs):
try:
is_refiner = model.text_encoder.__class__.__name__ != 'CLIPTextModel'
except Exception:
is_refiner = False
if hasattr(model, "set_progress_bar_config"):
model.set_progress_bar_config(bar_format='Progress {rate_fmt}{postfix} {bar} {percentage:3.0f}% {n_fmt}/{total_fmt} {elapsed} {remaining} '+desc, ncols=80, colour='#327fba')
args = {}
pipeline = model
signature = inspect.signature(type(pipeline).__call__)
signature = inspect.signature(type(model).__call__)
possible = signature.parameters.keys()
generator_device = devices.cpu if shared.opts.diffusers_generator_device == "cpu" else shared.device
generator = [torch.Generator(generator_device).manual_seed(s) for s in seeds]
@@ -141,24 +144,20 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
prompt_embed, pooled, negative_embed, negative_pooled = prompt_parser_diffusers.compel_encode_prompts(model, prompts, negative_prompts, prompts_2, negative_prompts_2, is_refiner, kwargs.pop("clip_skip", None))
if 'prompt' in possible:
if hasattr(model, 'text_encoder') and 'prompt_embeds' in possible and prompt_embed is not None:
if type(pooled) == list:
pooled = pooled[0]
if type(negative_pooled) == list:
negative_pooled = negative_pooled[0]
args['prompt_embeds'] = prompt_embed
if not is_refiner and shared.sd_model_type == "sdxl":
if 'XL' in model.__class__.__name__:
args['pooled_prompt_embeds'] = pooled
# args['prompt_2'] = None # Cannot pass prompts when passing embeds
if is_refiner and shared.sd_refiner_type == "sdxl":
args['pooled_prompt_embeds'] = pooled
# args['prompt_2'] = None # Cannot pass prompts when passing embeds
else:
args['prompt'] = prompts
if 'negative_prompt' in possible:
if hasattr(model, 'text_encoder') and 'negative_prompt_embeds' in possible and negative_embed is not None:
args['negative_prompt_embeds'] = negative_embed
if not is_refiner and shared.sd_model_type == "sdxl":
if 'XL' in model.__class__.__name__:
args['negative_pooled_prompt_embeds'] = negative_pooled
# args['negative_prompt_2'] = None
if is_refiner and shared.sd_refiner_type == "sdxl":
args['negative_pooled_prompt_embeds'] = negative_pooled
# args['negative_prompt_2'] = None
else:
args['negative_prompt'] = negative_prompts
if 'guidance_scale' in possible:
@@ -198,7 +197,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
if 'negative_pooled_prompt_embeds' in clean:
clean['negative_pooled_prompt_embeds'] = clean['negative_pooled_prompt_embeds'].shape if torch.is_tensor(clean['negative_pooled_prompt_embeds']) else type(clean['negative_pooled_prompt_embeds'])
clean['generator'] = generator_device
shared.log.debug(f'Diffuser pipeline: {pipeline.__class__.__name__} task={sd_models.get_diffusers_task(model)} set={clean}')
shared.log.debug(f'Diffuser pipeline: {model.__class__.__name__} task={sd_models.get_diffusers_task(model)} set={clean}')
return args
def recompile_model(hires=False):
@@ -294,7 +293,6 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
denoising_start=0 if use_refiner_start else p.refiner_start if use_denoise_start else None,
denoising_end=p.refiner_start if use_refiner_start else 1 if use_denoise_start else None,
output_type='latent' if hasattr(shared.sd_model, 'vae') else 'np',
is_refiner=False,
clip_skip=p.clip_skip,
desc='Base',
**task_specific_kwargs
@@ -334,7 +332,6 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
guidance_scale=p.image_cfg_scale if p.image_cfg_scale is not None else p.cfg_scale,
guidance_rescale=p.diffusers_guidance_rescale,
output_type='latent' if hasattr(shared.sd_model, 'vae') else 'np',
is_refiner=False,
clip_skip=p.clip_skip,
image=p.init_images,
strength=p.denoising_strength,
@@ -381,7 +378,6 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
denoising_end=1 if p.refiner_start > 0 and p.refiner_start < 1 else None,
image=output.images[i],
output_type='latent' if hasattr(shared.sd_refiner, 'vae') else 'np',
is_refiner=True,
clip_skip=p.clip_skip,
desc='Refiner',
)
@@ -390,12 +386,6 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
except AssertionError as e:
shared.log.info(e)
p.extra_generation_params['Image CFG scale'] = p.image_cfg_scale if p.image_cfg_scale is not None else None
p.extra_generation_params['Refiner steps'] = p.refiner_steps
p.extra_generation_params['Refiner start'] = p.refiner_start
p.extra_generation_params["Hires steps"] = p.hr_second_pass_steps
p.extra_generation_params["Secondary sampler"] = p.latent_sampler
if not shared.state.interrupted and not shared.state.skipped:
refiner_images = vae_decode(latents=refiner_output.images, model=shared.sd_refiner, full_quality=True)
for refiner_image in refiner_images:
+10 -10
View File
@@ -61,9 +61,9 @@ def compel_encode_prompts(
prompt_embeds = torch.cat(prompt_embeds, dim=0)
if negative_embeds is not None:
negative_embeds = torch.cat(negative_embeds, dim=0)
if positive_pooleds is not None and shared.sd_model_type == "sdxl":
if positive_pooleds is not None and 'XL' in pipeline.__class__.__name__:
positive_pooleds = torch.cat(positive_pooleds, dim=0)
if negative_pooleds is not None and shared.sd_model_type == "sdxl":
if negative_pooleds is not None and 'XL' in pipeline.__class__.__name__:
negative_pooleds = torch.cat(negative_pooleds, dim=0)
return prompt_embeds, positive_pooleds, negative_embeds, negative_pooleds
@@ -81,11 +81,11 @@ def compel_encode_prompt(
shared.log.warning(f"Prompt parser: Compel not supported: {type(pipeline).__name__}")
return (None, None, None, None)
if not is_refiner and shared.sd_model_type == "sdxl":
if not is_refiner and 'XL' in pipeline.__class__.__name__:
embedding_type = ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED
if clip_skip is not None and clip_skip > 1:
shared.log.warning(f"Prompt parser SDXL unsupported: clip_skip={clip_skip}")
elif is_refiner and shared.sd_refiner_type == "sdxl":
elif is_refiner and 'XL' in pipeline.__class__.__name__:
embedding_type = ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED
if clip_skip is not None and clip_skip > 1:
shared.log.warning(f"Prompt parser SDXL unsupported: clip_skip={clip_skip}")
@@ -109,7 +109,7 @@ def compel_encode_prompt(
device=shared.device
)
if not is_refiner and shared.sd_model_type == "sdxl":
if 'XL' in pipeline.__class__.__name__ and not is_refiner:
compel_te2 = Compel(tokenizer=pipeline.tokenizer_2, text_encoder=pipeline.text_encoder_2, returned_embeddings_type=embedding_type, requires_pooled=True, device=shared.device)
positive_te1 = compel_te1(prompt)
positive_te2, positive_pooled = compel_te2(prompt_2)
@@ -123,7 +123,7 @@ def compel_encode_prompt(
[prompt_embed, negative_embed] = compel_te2.pad_conditioning_tensors_to_same_length([positive, negative])
return prompt_embed, positive_pooled, negative_embed, negative_pooled
if is_refiner and shared.sd_refiner_type == "sdxl":
elif 'XL' in pipeline.__class__.__name__ and is_refiner:
compel_te2 = Compel(tokenizer=pipeline.tokenizer_2, text_encoder=pipeline.text_encoder_2, returned_embeddings_type=embedding_type, requires_pooled=True, device=shared.device)
positive, positive_pooled = compel_te2(prompt)
negative, negative_pooled = compel_te2(negative_prompt)
@@ -133,7 +133,7 @@ def compel_encode_prompt(
[prompt_embed, negative_embed] = compel_te2.pad_conditioning_tensors_to_same_length([positive, negative])
return prompt_embed, positive_pooled, negative_embed, negative_pooled
# neither base+sdxl nor refiner+sdxl
positive, negative = compel_te1(prompt), compel_te1(negative_prompt)
[prompt_embed, negative_embed] = compel_te1.pad_conditioning_tensors_to_same_length([positive, negative])
return prompt_embed, None, negative_embed, None
else:
positive, negative = compel_te1(prompt), compel_te1(negative_prompt)
[prompt_embed, negative_embed] = compel_te1.pad_conditioning_tensors_to_same_length([positive, negative])
return prompt_embed, None, negative_embed, None
+5 -9
View File
@@ -518,7 +518,7 @@ class ModelData:
elif shared.backend == shared.Backend.DIFFUSERS:
load_diffuser(op='model')
else:
shared.log.error(f"Unknown Stable Diffusion backend: {shared.backend}")
shared.log.error(f"Unknown Execution backend: {shared.backend}")
self.initial = False
except Exception as e:
shared.log.error("Failed to load stable diffusion model")
@@ -538,7 +538,7 @@ class ModelData:
elif shared.backend == shared.Backend.DIFFUSERS:
load_diffuser(op='refiner')
else:
shared.log.error(f"Unknown Stable Diffusion backend: {shared.backend}")
shared.log.error(f"Unknown Execution backend: {shared.backend}")
self.initial = False
except Exception as e:
shared.log.error("Failed to load stable diffusion model")
@@ -547,7 +547,6 @@ class ModelData:
return self.sd_refiner
def set_sd_refiner(self, v):
shared.log.debug(f"Class refiner: {v}")
self.sd_refiner = v
model_data = ModelData()
@@ -580,16 +579,13 @@ def detect_pipeline(f: str, op: str = 'model'):
shared.log.warning(f'Model detected as SD-XL refiner model, but attempting to load using backend=original: {f} size={size} GB')
if op == 'model':
shared.log.warning(f'Model detected as SD-XL refiner model, but attempting to load a base model: {f} size={size} GB')
else:
guess = 'Stable Diffusion XL'
guess = 'Stable Diffusion XL'
elif size < 7:
if shared.backend == shared.Backend.ORIGINAL:
shared.log.warning(f'Model detected as SD-XL base model, but attempting to load using backend=original: {f} size={size} GB')
if op == 'refiner':
shared.log.warning(f'Model size matches SD-XL base model, but attempting to load a refiner model: {f} size={size} GB')
else:
guess = 'Stable Diffusion XL'
guess = 'Stable Diffusion XL'
else:
guess = 'Unknown'
shared.log.error(f'Model autodetect failed, set diffuser pipeline manually: {f}')
return None, None
shared.log.debug(f'Model autodetect {op}: {f} pipeline={guess} size={size} GB')
+5 -5
View File
@@ -348,8 +348,8 @@ elif devices.backend == "rocm":
else: # cuda
cross_attention_optimization_default ="Scaled-Dot-Product"
options_templates.update(options_section(('sd', "Stable Diffusion"), {
"sd_backend": OptionInfo("diffusers" if cmd_opts.use_openvino else "original", "Stable Diffusion backend", gr.Radio, lambda: {"choices": ["original", "diffusers"] }),
options_templates.update(options_section(('sd', "Execution & Models"), {
"sd_backend": OptionInfo("diffusers" if cmd_opts.use_openvino else "original", "Execution backend", gr.Radio, lambda: {"choices": ["original", "diffusers"] }),
"sd_checkpoint_autoload": OptionInfo(True, "Model autoload on server start"),
"sd_model_checkpoint": OptionInfo(default_checkpoint, "Base model", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints),
"sd_model_refiner": OptionInfo('None', "Refiner model", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints),
@@ -520,7 +520,7 @@ options_templates.update(options_section(('ui', "User Interface"), {
"keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
"keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing <extra networks:0.9>", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
"keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), # pylint: disable=anomalous-backslash-in-string
"quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
"quicksettings_list": OptionInfo(["sd_model_checkpoint"] if backend == Backend.ORIGINAL else ["sd_model_checkpoint", "sd_model_refiner"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
"ui_scripts_reorder": OptionInfo("", "UI scripts order"),
}))
@@ -574,8 +574,8 @@ options_templates.update(options_section(('sampler-params', "Sampler Settings"),
options_templates.update(options_section(('postprocessing', "Postprocessing"), {
'postprocessing_enable_in_main_ui': OptionInfo([], "Enable addtional postprocessing operations", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
"use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution"),
"dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers"),
# "use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution"),
# "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers"),
"postprocessing_sep_img2img": OptionInfo("<h2>Img2Img & Inpainting</h2>", "", gr.HTML),
"img2img_color_correction": OptionInfo(False, "Apply color correction to match original colors"),
+5 -5
View File
@@ -378,10 +378,10 @@ def create_ui(startup_timer = None):
with FormGroup(visible=show_advanced.value, elem_id="txt2img_advanced") as advanced_group:
with FormRow():
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.1, label='CFG Scale', value=6.0, elem_id="txt2img_cfg_scale")
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id="txt2img_cfg_scale")
clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=14, step=1, elem_id='txt2img_clip_skip', interactive=True)
with FormRow(elem_id="guidence_scale_row", variant="compact"):
image_cfg_scale = gr.Slider(minimum=1.1, maximum=30.0, step=0.1, label='Secondary CFG Scale', value=6.0, elem_id="txt2img_image_cfg_scale")
image_cfg_scale = gr.Slider(minimum=1.1, maximum=30.0, step=0.1, label='Secondary CFG scale', value=6.0, elem_id="txt2img_image_cfg_scale")
diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale")
with FormRow(elem_classes="checkboxes-row", variant="compact"):
full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="txt2img_full_quality")
@@ -680,11 +680,11 @@ def create_ui(startup_timer = None):
with FormGroup(visible=show_advanced.value, elem_id=f"{tab}_advanced_group") as advanced_group:
with FormRow():
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=6.0, elem_id="img2img_cfg_scale")
image_cfg_scale = gr.Slider(minimum=0, maximum=30.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale")
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG scale', value=6.0, elem_id="img2img_cfg_scale")
image_cfg_scale = gr.Slider(minimum=0, maximum=30.0, step=0.05, label='Image CFG scale', value=1.5, elem_id="img2img_image_cfg_scale")
with FormRow():
clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=4, step=1, elem_id='img2img_clip_skip', interactive=True)
diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance Rescale', value=0.7, elem_id="txt2img_image_cfg_rescale")
diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale")
with FormRow(elem_classes="img2img_checkboxes_row", variant="compact"):
full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="img2img_full_quality")
restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="img2img_restore_faces")
+1 -1
View File
@@ -61,5 +61,5 @@ transformers==4.31.0
tomesd==0.1.3
urllib3==1.26.15
Pillow==9.5.0
timm==0.6.13
timm==0.9.7
pydantic==1.10.11
+2 -2
View File
@@ -218,7 +218,7 @@ axis_options = [
AxisOption("Clip skip", int, apply_clip_skip),
AxisOption("Denoising", float, apply_field("denoising_strength")),
AxisOptionTxt2Img("Hires steps", int, apply_field("hr_second_pass_steps")),
AxisOptionImg2Img("Image CFG Scale", float, apply_field("image_cfg_scale")),
AxisOptionImg2Img("Image CFG scale", float, apply_field("image_cfg_scale")),
AxisOption("Prompt order", str_permutations, apply_order, fmt=format_value_join_list),
AxisOption("Sampler Sigma Churn", float, apply_field("s_churn")),
AxisOption("Sampler Sigma min", float, apply_field("s_tmin")),
@@ -235,7 +235,7 @@ axis_options = [
AxisOption("SecondPass Sampler", str, apply_latent_sampler, fmt=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]),
AxisOption("SecondPass Denoising Strength", float, apply_field("denoising_strength")),
AxisOption("SecondPass Steps", int, apply_field("hr_second_pass_steps")),
AxisOption("SecondPass CFG Scale", float, apply_field("image_cfg_scale")),
AxisOption("SecondPass CFG scale", float, apply_field("image_cfg_scale")),
AxisOption("SecondPass Guidance Rescale", float, apply_field("diffusers_guidance_rescale")),
AxisOption("SecondPass Refiner Start", float, apply_field("refiner_start")),
]