diff --git a/CHANGELOG.md b/CHANGELOG.md index 9144e2d9a..54cf478e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,12 @@ ## TODO -- resize type: strech, fill/color, edge, etc. - reference styles - quick apply style -## Update for 2024-03-16 +## Update for 2024-03-17 -### Highlights 2024-03-16 +### Highlights 2024-03-17 New models: - [Stable Cascade](https://github.com/Stability-AI/StableCascade) *Full* and *Lite* @@ -16,17 +15,24 @@ New models: - [KOALA 700M](https://github.com/youngwanLEE/sdxl-koala) - [Stable Video Diffusion XT 1.1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) - [VGen](https://huggingface.co/ali-vilab/i2vgen-xl) + New pipelines and features: - Trajectory Consistency Distillation [TCD](https://mhh0318.github.io/tcd) for processing in even less steps - Img2img using [LEdit++](https://leditsplusplus-project.static.hf.space/index.html), context aware method with image analysis and positive/negative prompt handling - Visual Query & Answer using [moondream2](https://github.com/vikhyat/moondream) as an addition to standard interrogate methods -- Face-HiRes: simple detailer for face refinements +- Face-HiRes: simple built-in detailer for face refinements +- Even simpler outpaint: when resizing image, simply pick outpaint method and if image has different aspect ratio, blank areas will be outpainted! - UI aspect-ratio controls and other UI improvements - User controllable invisibile and visible watermarking - Native composable LoRA -**Styles**: Not just for prompts! Styles can apply *generate parameters* as templates and can be used to *apply wildcards* to prompts -**Reference models**: *Networks -> Models -> Reference*: All reference models now come with recommended settings that can be auto-applied if desired -Additional Improvements such as: Smooth tiling, Refine/HiRes workflow improvements, Control workflow improvements, Additional API endpoints + +What else? + +- **Reference models**: *Networks -> Models -> Reference*: All reference models now come with recommended settings that can be auto-applied if desired +- **Styles**: Not just for prompts! Styles can apply *generate parameters* as templates and can be used to *apply wildcards* to prompts +improvements, Additional API endpoints +- Given the high interest in [ZLUDA](https://github.com/vosen/ZLUDA) engine introduced in last release we've updated much more flexible/automatic install procedure (see [wiki](https://github.com/vladmandic/automatic/wiki/ZLUDA) for details) +- Plus Additional Improvements such as: Smooth tiling, Refine/HiRes workflow improvements, Control workflow Further details: - For basic instructions, see [README](https://github.com/vladmandic/automatic/blob/master/README.md) @@ -34,7 +40,7 @@ Further details: - For documentation, see [WiKi](https://github.com/vladmandic/automatic/wiki) - [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) server -### Full Changelog 2024-03-16 +### Full Changelog 2024-03-17 - [Stable Cascade](https://github.com/Stability-AI/StableCascade) *Full* and *Lite* - large multi-stage high-quality model from warp-ai/wuerstchen team and released by stabilityai @@ -84,6 +90,7 @@ Further details: - *note*: this is a very experimental feature and may not work as expected - **Control** - added *refiner/hires* workflows + - added resize methods to before/after/mask: fixed, crop, fill - **Samplers** - [TCD](https://mhh0318.github.io/tcd/): Trajectory Consistency Distillation new sampler that produces consistent results in a very low number of steps (comparable to LCM but without reliance on LoRA) diff --git a/modules/images.py b/modules/images.py index 2f52acef6..41a3fa122 100644 --- a/modules/images.py +++ b/modules/images.py @@ -123,13 +123,9 @@ class GridAnnotation: def get_font(fontsize): try: - return ImageFont.truetype( - shared.opts.font or "javascript/notosans-nerdfont-regular.ttf", fontsize - ) + return ImageFont.truetype(shared.opts.font or "javascript/notosans-nerdfont-regular.ttf", fontsize) except Exception: - return ImageFont.truetype( - "javascript/notosans-nerdfont-regular.ttf", fontsize - ) + return ImageFont.truetype("javascript/notosans-nerdfont-regular.ttf", fontsize) def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, title=None): @@ -216,27 +212,15 @@ 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} fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access - """ - Resizes an image with the specified resize_mode, width, and height. - Args: - resize_mode: The mode to use when resizing the image. - 0: No resize - 1: Resize the image to the specified width and height. - 2: Resize the image to fill the specified width and height, maintaining the aspect ratio, and then center the image within the dimensions, cropping the excess. - 3: Resize the image to fit within the specified width and height, maintaining the aspect ratio, and then center the image within the dimensions, filling empty with data from image. - im: The image to resize. - width: The width to resize the image to. - height: The height to resize the image to. - upscaler_name: The name of the upscaler to use. If not provided, defaults to opts.upscaler_for_img2img. - """ + if im.width == width and im.height == height: + shared.log.debug(f'Image resize: input={im} target={width}x{height} mode={shared.resize_modes[resize_mode]} upscaler="{upscaler_name}" fn={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access upscaler_name = upscaler_name or shared.opts.upscaler_for_img2img def latent(im, w, h, upscaler): from modules.processing_vae import vae_encode, vae_decode import torch latents = vae_encode(im, shared.sd_model, full_quality=False) # TODO enable full VAE mode - latents = torch.nn.functional.interpolate(latents, size=(h // 8, w // 8), mode=upscaler["mode"], antialias=upscaler["antialias"]) + latents = torch.nn.functional.interpolate(latents, size=(int(h // 8), int(w // 8)), mode=upscaler["mode"], antialias=upscaler["antialias"]) im = vae_decode(latents, shared.sd_model, output_type='pil', full_quality=False)[0] return im @@ -260,11 +244,7 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type im = im.resize((w, h), resample=Image.Resampling.LANCZOS) return im - if resize_mode == 0 or (im.width == width and im.height == height): - res = im.copy() - elif resize_mode == 1: - res = resize(im, width, height) - elif resize_mode == 2: + def crop(im): ratio = width / height src_ratio = im.width / im.height src_w = width if ratio > src_ratio else im.width * height // im.height @@ -272,7 +252,11 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type resized = resize(im, src_w, src_h) res = Image.new(im.mode, (width, height)) res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2)) - else: + return res + + def fill(im, color=None): + color = color or shared.opts.image_background + """ ratio = round(width / height, 1) src_ratio = round(im.width / im.height, 1) src_w = width if ratio < src_ratio else im.width * height // im.height @@ -290,6 +274,27 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type if height > 0 and fill_width > 0: res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0)) res.paste(resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)), box=(fill_width + src_w, 0)) + return res + """ + ratio = min(width / im.width, height / im.height) + im = resize(im, im.width * ratio, im.height * ratio) + res = Image.new(im.mode, (width, height), color=color) + res.paste(im, box=((width - im.width)//2, (height - im.height)//2)) + return res + + if resize_mode == 0 or (im.width == width and im.height == height): # none + res = im.copy() + elif resize_mode == 1: # fixed + res = resize(im, width, height) + elif resize_mode == 2: # crop + res = crop(im) + elif resize_mode == 3: # fill + res = fill(im) + elif resize_mode == 4: # edge + from modules import masking + res = fill(im, color=0) + res, _mask = masking.outpaint(res) + res.save('/tmp/edge.png') if output_type == 'np': return np.array(res) return res diff --git a/modules/processing_class.py b/modules/processing_class.py index 790ec9867..8440debf4 100644 --- a/modules/processing_class.py +++ b/modules/processing_class.py @@ -405,9 +405,8 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image = images.flatten(img, shared.opts.img2img_background_color) if self.width is None or self.height is None: self.width, self.height = image.width, image.height - if crop_region is None and self.resize_mode != 4 and self.resize_mode > 0: - if image.width != self.width or image.height != self.height: - image = images.resize_image(self.resize_mode, image, self.width, self.height, self.resize_name) + if crop_region is None and self.resize_mode > 0: + image = images.resize_image(self.resize_mode, image, self.width, self.height, self.resize_name) self.width = image.width self.height = image.height if self.image_mask is not None and shared.opts.mask_apply_overlay: @@ -445,8 +444,6 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image = 2. * image - 1. image = image.to(device=shared.device, dtype=devices.dtype_vae) self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image)) - if self.resize_mode == 4: - self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // 8, self.width // 8), mode="bilinear") if self.image_mask is not None: init_mask = latent_mask latmask = init_mask.convert('RGB').resize((self.init_latent.shape[3], self.init_latent.shape[2])) diff --git a/modules/shared.py b/modules/shared.py index 901685c94..8572982cc 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -74,7 +74,7 @@ restricted_opts = { "outdir_save", "outdir_init_images" } -resize_modes = ["None", "Fixed", "Crop", "Fill", "Latent"] +resize_modes = ["None", "Fixed", "Crop", "Fill", "Outpaint"] compatibility_opts = ['clip_skip', 'uni_pc_lower_order_final', 'uni_pc_order'] console = Console(log_time=True, log_time_format='%H:%M:%S-%f') dir_timestamps = {} @@ -515,6 +515,7 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "save_selected_only": OptionInfo(True, "Save only saves selected image"), "include_mask": OptionInfo(False, "Include mask in outputs"), "samples_save_zip": OptionInfo(True, "Create ZIP archive"), + "image_background": OptionInfo("#000000", "Resize background color", gr.ColorPicker, {}), "image_sep_metadata": OptionInfo("