mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 01:04:32 +02:00
facehires support batch size&count, add override strength
This commit is contained in:
+3
-2
@@ -5,13 +5,13 @@
|
||||
- items that require `diffusers==0.27.0.dev`:
|
||||
- EDM samplers for Playground 2.5
|
||||
- Stable Cascade
|
||||
- LEdits++ pipeline
|
||||
- LEdits++ pipeline: <https://github.com/huggingface/diffusers/pull/6074>
|
||||
- fix reference models:
|
||||
- Warp Wuerstchen: pipeline does not have all components
|
||||
- Kandinsky 2.1: pipeline does not have all components
|
||||
- Kandinsky 2.2: pipeline does not have all components
|
||||
|
||||
## Update for 2024-03-13
|
||||
## Update for 2024-03-14
|
||||
|
||||
- [Playground v2.5](https://huggingface.co/playgroundai/playground-v2.5-1024px-aesthetic)
|
||||
- new model version from Playground: based on SDXL, but with some cool new concepts
|
||||
@@ -90,6 +90,7 @@
|
||||
- set as default face restorer in settings -> postprocessing
|
||||
- disabled by default, to enable simply check *face restore* in your generate advanced settings
|
||||
- strength, steps and sampler are set using by hires section in refine menu
|
||||
- strength can be overriden in settings -> postprocessing
|
||||
- will use secondary prompt and secondary negative prompt if present in refine
|
||||
- **Watermarking**
|
||||
- SD.Next disables all known watermarks in models, but does allow user to set custom watermark
|
||||
|
||||
+2
-2
@@ -216,7 +216,7 @@ def draw_prompt_matrix(im, width, height, all_prompts, margin=0):
|
||||
|
||||
|
||||
def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type='image'):
|
||||
shared.log.debug(f'Image resize: input={im} mode={resize_mode} target={width}x{height} upscaler={upscaler_name} function={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
shared.log.debug(f'Image resize: input={im} mode={resize_mode} target={width}x{height} upscaler={upscaler_name} fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
"""
|
||||
Resizes an image with the specified resize_mode, width, and height.
|
||||
Args:
|
||||
@@ -597,7 +597,7 @@ save_thread.start()
|
||||
|
||||
|
||||
def save_image(image, path, basename='', seed=None, prompt=None, extension=shared.opts.samples_format, info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix='', save_to_dirs=None): # pylint: disable=unused-argument
|
||||
debug(f'Save from function={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
debug(f'Save: fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
if image is None:
|
||||
shared.log.warning('Image is none')
|
||||
return None, None
|
||||
|
||||
+1
-1
@@ -372,7 +372,7 @@ def outpaint(input_image: Image.Image, outpaint_type: str = 'Edge'):
|
||||
|
||||
|
||||
def run_mask(input_image: Image.Image, input_mask: Image.Image = None, return_type: str = None, mask_blur: int = None, mask_padding: int = None, segment_enable=True, invert=None):
|
||||
debug(f'Run mask: function={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
debug(f'Run mask: fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
|
||||
if input_image is None:
|
||||
return input_mask
|
||||
|
||||
+13
-13
@@ -268,18 +268,19 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
extra_network_data = None
|
||||
debug(f'Processing inner: args={vars(p)}')
|
||||
for n in range(p.n_iter):
|
||||
debug(f'Processing inner: iteration={n+1}/{p.n_iter}')
|
||||
p.iteration = n
|
||||
if shared.state.skipped:
|
||||
shared.log.debug(f'Process skipped: {n}/{p.n_iter}')
|
||||
shared.log.debug(f'Process skipped: {n+1}/{p.n_iter}')
|
||||
shared.state.skipped = False
|
||||
continue
|
||||
if shared.state.interrupted:
|
||||
shared.log.debug(f'Process interrupted: {n}/{p.n_iter}')
|
||||
shared.log.debug(f'Process interrupted: {n+1}/{p.n_iter}')
|
||||
break
|
||||
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]
|
||||
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 and isinstance(p.scripts, scripts.ScriptRunner):
|
||||
p.scripts.before_process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
||||
if len(p.prompts) == 0:
|
||||
@@ -313,8 +314,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts.ScriptRunner):
|
||||
p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n)
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts.ScriptRunner):
|
||||
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.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
|
||||
@@ -326,6 +327,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
shared.sd_model.restore_pipeline()
|
||||
|
||||
for i, x_sample in enumerate(x_samples_ddim):
|
||||
if hasattr(p, 'recursion'):
|
||||
continue
|
||||
debug(f'Processing result: index={i+1}/{len(x_samples_ddim)} iteration={n+1}/{p.n_iter}')
|
||||
p.batch_index = i
|
||||
if type(x_sample) == Image.Image:
|
||||
image = x_sample
|
||||
@@ -335,11 +339,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
image = Image.fromarray(x_sample)
|
||||
if p.restore_faces:
|
||||
if not p.do_not_save_samples and shared.opts.save_images_before_face_restoration:
|
||||
orig = p.restore_faces
|
||||
p.restore_faces = False
|
||||
info = infotext(i)
|
||||
p.restore_faces = orig
|
||||
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-restore")
|
||||
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=infotext(i), p=p, suffix="-before-face-restore")
|
||||
p.ops.append('face')
|
||||
x_sample = face_restoration.restore_faces(x_sample, p)
|
||||
image = Image.fromarray(x_sample)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
import inspect
|
||||
import hashlib
|
||||
from typing import Any, Dict, List
|
||||
from dataclasses import dataclass, field
|
||||
@@ -10,6 +12,9 @@ from modules import shared, devices, images, scripts, masking, sd_samplers, sd_m
|
||||
from modules.sd_hijack_hypertile import hypertile_set
|
||||
|
||||
|
||||
debug = shared.log.trace if os.environ.get('SD_PROCESS_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
|
||||
|
||||
@dataclass(repr=False)
|
||||
class StableDiffusionProcessing:
|
||||
"""
|
||||
@@ -507,18 +512,17 @@ class StableDiffusionProcessingControl(StableDiffusionProcessingImg2Img):
|
||||
|
||||
|
||||
def switch_class(p: StableDiffusionProcessing, new_class: type, dct: dict = None):
|
||||
import inspect
|
||||
signature = inspect.signature(type(new_class).__init__, follow_wrapped=True)
|
||||
possible = list(signature.parameters)
|
||||
kwargs = {}
|
||||
for k, v in p.__dict__.items():
|
||||
for k, v in p.__dict__.copy().items():
|
||||
if k in possible:
|
||||
kwargs[k] = v
|
||||
if dct is not None:
|
||||
for k, v in dct.items():
|
||||
if k in possible:
|
||||
kwargs[k] = v
|
||||
shared.log.debug(f"Switching class: {p.__class__} -> {new_class}")
|
||||
debug(f"Switching class: {p.__class__.__name__} -> {new_class.__name__} fn={sys._getframe(1).f_code.co_name}") # pylint: disable=protected-access
|
||||
p.__class__ = new_class
|
||||
p.__init__(**kwargs)
|
||||
for k, v in p.__dict__.items():
|
||||
|
||||
@@ -754,9 +754,9 @@ def move_model(model, device=None, force=False):
|
||||
if device == devices.device: # force vae back to gpu if not in txt2img mode
|
||||
model.vae.to(device)
|
||||
if hasattr(model.vae, '_hf_hook'):
|
||||
debug_move(f'Model move: to={device} class={model.vae.__class__} function={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
debug_move(f'Model move: to={device} class={model.vae.__class__} fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
model.vae._hf_hook.execution_device = device # pylint: disable=protected-access
|
||||
debug_move(f'Model move: device={device} class={model.__class__} accelerate={getattr(model, "has_accelerate", False)} function={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
debug_move(f'Model move: device={device} class={model.__class__} accelerate={getattr(model, "has_accelerate", False)} fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access
|
||||
if hasattr(model, "components"): # accelerate patch
|
||||
for name, m in model.components.items():
|
||||
if not hasattr(m, "_hf_hook"): # not accelerate hook
|
||||
|
||||
@@ -669,6 +669,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), {
|
||||
|
||||
"postprocessing_sep_face_restoration": OptionInfo("<h2>Face Restoration</h2>", "", gr.HTML),
|
||||
"face_restoration_model": OptionInfo("Face HiRes", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
|
||||
"facehires_strength": OptionInfo(0.0, "Face HiRes strength", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
|
||||
"code_former_weight": OptionInfo(0.2, "CodeFormer weight parameter", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
|
||||
"face_restoration_unload": OptionInfo(False, "Move model to CPU when complete"),
|
||||
|
||||
|
||||
+10
-10
@@ -92,7 +92,7 @@ class FaceRestorerYolo(FaceRestoration):
|
||||
from modules import devices, processing_class
|
||||
if not hasattr(p, 'facehires'):
|
||||
p.facehires = 0
|
||||
if np_image is None or p.facehires >= p.batch_size:
|
||||
if np_image is None or p.facehires >= p.batch_size * p.n_iter:
|
||||
return np_image
|
||||
self.load()
|
||||
if self.model is None:
|
||||
@@ -119,7 +119,7 @@ class FaceRestorerYolo(FaceRestoration):
|
||||
'sampler_name': orig_p.get('hr_sampler_name', 'default'),
|
||||
'steps': orig_p.get('hr_second_pass_steps', 0),
|
||||
'negative_prompt': orig_p.get('refiner_negative', ''),
|
||||
'denoising_strength': orig_p.get('denoising_strength', 0.3),
|
||||
'denoising_strength': shared.opts.facehires_strength if shared.opts.facehires_strength > 0 else orig_p.get('denoising_strength', 0.3),
|
||||
'styles': [],
|
||||
'prompt': orig_p.get('refiner_prompt', ''),
|
||||
# TODO facehires expose as tunable
|
||||
@@ -137,6 +137,7 @@ class FaceRestorerYolo(FaceRestoration):
|
||||
if len(p.negative_prompt) == 0:
|
||||
p.negative_prompt = orig_p.get('all_negative_prompts', [''])[0]
|
||||
|
||||
shared.log.debug(f'Face HiRes: faces={[f.__dict__ for f in faces]} strength={p.denoising_strength} blur={p.mask_blur} padding={p.inpaint_full_res_padding} steps={p.steps}')
|
||||
for face in faces:
|
||||
if face.mask is None:
|
||||
continue
|
||||
@@ -145,21 +146,20 @@ class FaceRestorerYolo(FaceRestoration):
|
||||
continue
|
||||
p.init_images = [image]
|
||||
p.image_mask = [face.mask]
|
||||
shared.log.debug(f'Face HiRes: face={p.facehires} {face.__dict__} strength={p.denoising_strength} blur={p.mask_blur} padding={p.inpaint_full_res_padding} steps={p.steps}')
|
||||
p.recursion = True
|
||||
pp = processing.process_images_inner(p)
|
||||
del p.recursion
|
||||
p.overlay_images = None # skip applying overlay twice
|
||||
if pp is not None and pp.images is not None and len(pp.images) > 0:
|
||||
image = pp.images[0]
|
||||
|
||||
if np_image is None or getattr(p, 'facehires', 0) >= p.batch_size:
|
||||
p.facehires = 0
|
||||
image = pp.images[0] # update image to be reused for next face
|
||||
|
||||
# restore pipeline
|
||||
p = processing_class.switch_class(p, orig_cls, orig_p)
|
||||
p.init_images = getattr(orig_p, 'init_images', None)
|
||||
p.image_mask = getattr(orig_p, 'image_mask', None)
|
||||
shared.opts.data['mask_apply_overlay'] = orig_apply_overlay
|
||||
if pp is not None and pp.images is not None and len(pp.images) > 0:
|
||||
image = pp.images[0]
|
||||
np_image = np.array(image)
|
||||
np_image = np.array(image)
|
||||
# shared.log.debug(f'Face HiRes complete: faces={len(faces)} time={t1-t0:.3f}')
|
||||
return np_image
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user