From 9557dbf9d41eec8e17d4b7085b0f7ad99232aa6d Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sun, 10 Mar 2024 19:11:29 -0400 Subject: [PATCH] facehires improvements and fixes --- modules/modelloader.py | 7 +++-- modules/processing_class.py | 2 +- scripts/face-details.py | 52 ++++++++++++++++++++++--------------- wiki | 2 +- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/modules/modelloader.py b/modules/modelloader.py index 99fb4a33b..6fb452941 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -377,6 +377,8 @@ def download_url_to_file(url: str, dst: str): def load_file_from_url(url: str, *, model_dir: str, progress: bool = True, file_name = None): # pylint: disable=unused-argument """Download a file from url into model_dir, using the file present if possible. Returns the path to the downloaded file.""" + if model_dir is None: + shared.log.error('Download folder is none') os.makedirs(model_dir, exist_ok=True) if not file_name: parts = urlparse(url) @@ -398,14 +400,15 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None @param ext_filter: An optional list of filename extensions to filter by @return: A list of paths containing the desired model(s) """ - places = list(set([model_path, command_path])) # noqa:C405 + places = [x for x in list(set([model_path, command_path])) if x is not None] # noqa:C405 output = [] try: output:list = [*files_cache.list_files(*places, ext_filter=ext_filter, ext_blacklist=ext_blacklist)] if model_url is not None and len(output) == 0: if download_name is not None: dl = load_file_from_url(model_url, model_dir=places[0], progress=True, file_name=download_name) - output.append(dl) + if dl is not None: + output.append(dl) else: output.append(model_url) except Exception as e: diff --git a/modules/processing_class.py b/modules/processing_class.py index fa34d0a31..0961372a0 100644 --- a/modules/processing_class.py +++ b/modules/processing_class.py @@ -242,7 +242,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): if all_subseeds is not None: self.all_subseeds = all_subseeds - def init_hr(self, scale = None, upscaler = None): + def init_hr(self, scale = None, upscaler = None, force = False): # pylint: disable=unused-argument scale = scale or self.hr_scale upscaler = upscaler or self.hr_upscaler if self.hr_resize_x == 0 and self.hr_resize_y == 0: diff --git a/scripts/face-details.py b/scripts/face-details.py index dba35dcba..64250f1c2 100644 --- a/scripts/face-details.py +++ b/scripts/face-details.py @@ -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 getattr(p, 'facehires', 0) >= p.batch_size: + if np_image is None or p.facehires >= p.batch_size: return np_image self.load() if self.model is None: @@ -109,9 +109,33 @@ class FaceRestorerYolo(FaceRestoration): orig_cls = p.__class__ pp = None - p.facehires += 1 # set flag to avoid recursion shared.opts.data['mask_apply_overlay'] = True - p = processing_class.switch_class(p, processing.StableDiffusionProcessingImg2Img) + args = { + 'batch_size': 1, + 'n_iter': 1, + 'inpaint_full_res': True, + 'inpainting_mask_invert': 0, + 'inpainting_fill': 1, # no fill + '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), + 'styles': [], + 'prompt': orig_p.get('refiner_prompt', ''), + # TODO facehires expose as tunable + 'mask_blur': 10, + 'inpaint_full_res_padding': 15, + 'restore_faces': True, + } + p = processing_class.switch_class(p, processing.StableDiffusionProcessingImg2Img, args) + p.facehires += 1 # set flag to avoid recursion + + if p.steps < 1: + p.steps = orig_p.get('steps', 0) + if len(p.prompt) == 0: + p.prompt = orig_p.get('all_prompts', [''])[0] + if len(p.negative_prompt) == 0: + p.negative_prompt = orig_p.get('all_negative_prompts', [''])[0] for face in faces: if face.mask is None: @@ -121,29 +145,15 @@ class FaceRestorerYolo(FaceRestoration): continue p.init_images = [image] p.image_mask = [face.mask] - p.inpaint_full_res = True - p.inpainting_mask_invert = 0 - p.inpainting_fill = 1 # no fill - p.sampler_name = orig_p.get('hr_sampler_name', 'default') - p.steps = orig_p.get('hr_second_pass_steps', p.steps) - p.denoising_strength = orig_p.get('denoising_strength', 0.3) - p.styles = [] - p.prompt = orig_p.get('refiner_prompt', '') - if len(p.prompt) == 0: - p.prompt = orig_p.get('all_prompts', [''])[0] - p.negative_prompt = orig_p.get('refiner_negative', '') - if len(p.negative_prompt) == 0: - p.negative_prompt = orig_p.get('all_negative_prompts', [''])[0] - # TODO facehires expose as tunable - p.mask_blur = 10 - p.inpaint_full_res_padding = 15 - p.restore_faces = True - shared.log.debug(f'Face HiRes: {face.__dict__} strength={p.denoising_strength} blur={p.mask_blur} padding={p.inpaint_full_res_padding}') + 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}') pp = processing.process_images_inner(p) 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 + # restore pipeline p = processing_class.switch_class(p, orig_cls, orig_p) shared.opts.data['mask_apply_overlay'] = orig_apply_overlay diff --git a/wiki b/wiki index b707b4e2b..048c32c28 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit b707b4e2b588d5d4809a9e97491acfcc66acc40d +Subproject commit 048c32c284795a5c4d4494c3b898eb68ea62b3d7