Spaces:
Running
on
Zero
Running
on
Zero
from modules.AutoEncoders import VariationalAE | |
from modules.sample import sampling | |
from modules.UltimateSDUpscale import USDU_upscaler, image_util | |
import torch | |
from PIL import ImageFilter, ImageDraw, Image | |
from enum import Enum | |
import math | |
# taken from https://github.com/ssitu/ComfyUI_UltimateSDUpscale | |
state = USDU_upscaler.state | |
class UnsupportedModel(Exception): | |
"""#### Exception raised for unsupported models.""" | |
pass | |
class StableDiffusionProcessing: | |
"""#### Class representing the processing of Stable Diffusion images.""" | |
def __init__( | |
self, | |
init_img: Image.Image, | |
model: torch.nn.Module, | |
positive: str, | |
negative: str, | |
vae: VariationalAE.VAE, | |
seed: int, | |
steps: int, | |
cfg: float, | |
sampler_name: str, | |
scheduler: str, | |
denoise: float, | |
upscale_by: float, | |
uniform_tile_mode: bool, | |
): | |
""" | |
#### Initialize the StableDiffusionProcessing class. | |
#### Args: | |
- `init_img` (Image.Image): The initial image. | |
- `model` (torch.nn.Module): The model. | |
- `positive` (str): The positive prompt. | |
- `negative` (str): The negative prompt. | |
- `vae` (VariationalAE.VAE): The variational autoencoder. | |
- `seed` (int): The seed. | |
- `steps` (int): The number of steps. | |
- `cfg` (float): The CFG scale. | |
- `sampler_name` (str): The sampler name. | |
- `scheduler` (str): The scheduler. | |
- `denoise` (float): The denoise strength. | |
- `upscale_by` (float): The upscale factor. | |
- `uniform_tile_mode` (bool): Whether to use uniform tile mode. | |
""" | |
# Variables used by the USDU script | |
self.init_images = [init_img] | |
self.image_mask = None | |
self.mask_blur = 0 | |
self.inpaint_full_res_padding = 0 | |
self.width = init_img.width | |
self.height = init_img.height | |
self.model = model | |
self.positive = positive | |
self.negative = negative | |
self.vae = vae | |
self.seed = seed | |
self.steps = steps | |
self.cfg = cfg | |
self.sampler_name = sampler_name | |
self.scheduler = scheduler | |
self.denoise = denoise | |
# Variables used only by this script | |
self.init_size = init_img.width, init_img.height | |
self.upscale_by = upscale_by | |
self.uniform_tile_mode = uniform_tile_mode | |
# Other required A1111 variables for the USDU script that is currently unused in this script | |
self.extra_generation_params = {} | |
class Processed: | |
"""#### Class representing the processed images.""" | |
def __init__( | |
self, p: StableDiffusionProcessing, images: list, seed: int, info: str | |
): | |
""" | |
#### Initialize the Processed class. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `images` (list): The list of images. | |
- `seed` (int): The seed. | |
- `info` (str): The information string. | |
""" | |
self.images = images | |
self.seed = seed | |
self.info = info | |
def infotext(self, p: StableDiffusionProcessing, index: int) -> str: | |
""" | |
#### Get the information text. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `index` (int): The index. | |
#### Returns: | |
- `str`: The information text. | |
""" | |
return None | |
def fix_seed(p: StableDiffusionProcessing) -> None: | |
""" | |
#### Fix the seed for reproducibility. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
""" | |
pass | |
def process_images(p: StableDiffusionProcessing, pipeline: bool = False) -> Processed: | |
""" | |
#### Process the images. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
#### Returns: | |
- `Processed`: The processed images. | |
""" | |
# Where the main image generation happens in A1111 | |
# Setup | |
image_mask = p.image_mask.convert("L") | |
init_image = p.init_images[0] | |
# Locate the white region of the mask outlining the tile and add padding | |
crop_region = image_util.get_crop_region(image_mask, p.inpaint_full_res_padding) | |
x1, y1, x2, y2 = crop_region | |
crop_width = x2 - x1 | |
crop_height = y2 - y1 | |
crop_ratio = crop_width / crop_height | |
p_ratio = p.width / p.height | |
if crop_ratio > p_ratio: | |
target_width = crop_width | |
target_height = round(crop_width / p_ratio) | |
else: | |
target_width = round(crop_height * p_ratio) | |
target_height = crop_height | |
crop_region, _ = image_util.expand_crop( | |
crop_region, | |
image_mask.width, | |
image_mask.height, | |
target_width, | |
target_height, | |
) | |
tile_size = p.width, p.height | |
# Blur the mask | |
if p.mask_blur > 0: | |
image_mask = image_mask.filter(ImageFilter.GaussianBlur(p.mask_blur)) | |
# Crop the images to get the tiles that will be used for generation | |
tiles = [img.crop(crop_region) for img in USDU_upscaler.batch] | |
# Assume the same size for all images in the batch | |
initial_tile_size = tiles[0].size | |
# Resize if necessary | |
for i, tile in enumerate(tiles): | |
if tile.size != tile_size: | |
tiles[i] = tile.resize(tile_size, Image.Resampling.LANCZOS) | |
# Crop conditioning | |
positive_cropped = image_util.crop_cond( | |
p.positive, crop_region, p.init_size, init_image.size, tile_size | |
) | |
negative_cropped = image_util.crop_cond( | |
p.negative, crop_region, p.init_size, init_image.size, tile_size | |
) | |
# Encode the image | |
vae_encoder = VariationalAE.VAEEncode() | |
batched_tiles = torch.cat([image_util.pil_to_tensor(tile) for tile in tiles], dim=0) | |
(latent,) = vae_encoder.encode(p.vae, batched_tiles) | |
# Generate samples | |
(samples,) = sampling.common_ksampler( | |
p.model, | |
p.seed, | |
p.steps, | |
p.cfg, | |
p.sampler_name, | |
p.scheduler, | |
positive_cropped, | |
negative_cropped, | |
latent, | |
denoise=p.denoise, | |
pipeline=pipeline | |
) | |
# Decode the sample | |
vae_decoder = VariationalAE.VAEDecode() | |
(decoded,) = vae_decoder.decode(p.vae, samples) | |
# Convert the sample to a PIL image | |
tiles_sampled = [image_util.tensor_to_pil(decoded, i) for i in range(len(decoded))] | |
for i, tile_sampled in enumerate(tiles_sampled): | |
init_image = USDU_upscaler.batch[i] | |
# Resize back to the original size | |
if tile_sampled.size != initial_tile_size: | |
tile_sampled = tile_sampled.resize( | |
initial_tile_size, Image.Resampling.LANCZOS | |
) | |
# Put the tile into position | |
image_tile_only = Image.new("RGBA", init_image.size) | |
image_tile_only.paste(tile_sampled, crop_region[:2]) | |
# Add the mask as an alpha channel | |
# Must make a copy due to the possibility of an edge becoming black | |
temp = image_tile_only.copy() | |
image_mask = image_mask.resize(temp.size) | |
temp.putalpha(image_mask) | |
temp.putalpha(image_mask) | |
image_tile_only.paste(temp, image_tile_only) | |
# Add back the tile to the initial image according to the mask in the alpha channel | |
result = init_image.convert("RGBA") | |
result.alpha_composite(image_tile_only) | |
# Convert back to RGB | |
result = result.convert("RGB") | |
USDU_upscaler.batch[i] = result | |
processed = Processed(p, [USDU_upscaler.batch[0]], p.seed, None) | |
return processed | |
class USDUMode(Enum): | |
"""#### Enum representing the modes for Ultimate SD Upscale.""" | |
LINEAR = 0 | |
CHESS = 1 | |
NONE = 2 | |
class USDUSFMode(Enum): | |
"""#### Enum representing the seam fix modes for Ultimate SD Upscale.""" | |
NONE = 0 | |
BAND_PASS = 1 | |
HALF_TILE = 2 | |
HALF_TILE_PLUS_INTERSECTIONS = 3 | |
class USDUpscaler: | |
"""#### Class representing the Ultimate SD Upscaler.""" | |
def __init__( | |
self, | |
p: StableDiffusionProcessing, | |
image: Image.Image, | |
upscaler_index: int, | |
save_redraw: bool, | |
save_seams_fix: bool, | |
tile_width: int, | |
tile_height: int, | |
) -> None: | |
""" | |
#### Initialize the USDUpscaler class. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `image` (Image.Image): The image. | |
- `upscaler_index` (int): The upscaler index. | |
- `save_redraw` (bool): Whether to save the redraw. | |
- `save_seams_fix` (bool): Whether to save the seams fix. | |
- `tile_width` (int): The tile width. | |
- `tile_height` (int): The tile height. | |
""" | |
self.p: StableDiffusionProcessing = p | |
self.image: Image = image | |
self.scale_factor = math.ceil( | |
max(p.width, p.height) / max(image.width, image.height) | |
) | |
self.upscaler = USDU_upscaler.sd_upscalers[upscaler_index] | |
self.redraw = USDURedraw() | |
self.redraw.save = save_redraw | |
self.redraw.tile_width = tile_width if tile_width > 0 else tile_height | |
self.redraw.tile_height = tile_height if tile_height > 0 else tile_width | |
self.seams_fix = USDUSeamsFix() | |
self.seams_fix.save = save_seams_fix | |
self.seams_fix.tile_width = tile_width if tile_width > 0 else tile_height | |
self.seams_fix.tile_height = tile_height if tile_height > 0 else tile_width | |
self.initial_info = None | |
self.rows = math.ceil(self.p.height / self.redraw.tile_height) | |
self.cols = math.ceil(self.p.width / self.redraw.tile_width) | |
def get_factor(self, num: int) -> int: | |
""" | |
#### Get the factor for a given number. | |
#### Args: | |
- `num` (int): The number. | |
#### Returns: | |
- `int`: The factor. | |
""" | |
if num == 1: | |
return 2 | |
if num % 4 == 0: | |
return 4 | |
if num % 3 == 0: | |
return 3 | |
if num % 2 == 0: | |
return 2 | |
return 0 | |
def get_factors(self) -> None: | |
""" | |
#### Get the list of scale factors. | |
""" | |
scales = [] | |
current_scale = 1 | |
current_scale_factor = self.get_factor(self.scale_factor) | |
while current_scale < self.scale_factor: | |
current_scale_factor = self.get_factor(self.scale_factor // current_scale) | |
scales.append(current_scale_factor) | |
current_scale = current_scale * current_scale_factor | |
self.scales = enumerate(scales) | |
def upscale(self) -> None: | |
""" | |
#### Upscale the image. | |
""" | |
# Log info | |
print(f"Canva size: {self.p.width}x{self.p.height}") | |
print(f"Image size: {self.image.width}x{self.image.height}") | |
print(f"Scale factor: {self.scale_factor}") | |
# Get list with scale factors | |
self.get_factors() | |
# Upscaling image over all factors | |
for index, value in self.scales: | |
print(f"Upscaling iteration {index + 1} with scale factor {value}") | |
self.image = self.upscaler.scaler.upscale( | |
self.image, value, self.upscaler.data_path | |
) | |
# Resize image to set values | |
self.image = self.image.resize( | |
(self.p.width, self.p.height), resample=Image.LANCZOS | |
) | |
def setup_redraw(self, redraw_mode: int, padding: int, mask_blur: int) -> None: | |
""" | |
#### Set up the redraw. | |
#### Args: | |
- `redraw_mode` (int): The redraw mode. | |
- `padding` (int): The padding. | |
- `mask_blur` (int): The mask blur. | |
""" | |
self.redraw.mode = USDUMode(redraw_mode) | |
self.redraw.enabled = self.redraw.mode != USDUMode.NONE | |
self.redraw.padding = padding | |
self.p.mask_blur = mask_blur | |
def setup_seams_fix( | |
self, padding: int, denoise: float, mask_blur: int, width: int, mode: int | |
) -> None: | |
""" | |
#### Set up the seams fix. | |
#### Args: | |
- `padding` (int): The padding. | |
- `denoise` (float): The denoise strength. | |
- `mask_blur` (int): The mask blur. | |
- `width` (int): The width. | |
- `mode` (int): The mode. | |
""" | |
self.seams_fix.padding = padding | |
self.seams_fix.denoise = denoise | |
self.seams_fix.mask_blur = mask_blur | |
self.seams_fix.width = width | |
self.seams_fix.mode = USDUSFMode(mode) | |
self.seams_fix.enabled = self.seams_fix.mode != USDUSFMode.NONE | |
def calc_jobs_count(self) -> None: | |
""" | |
#### Calculate the number of jobs. | |
""" | |
redraw_job_count = (self.rows * self.cols) if self.redraw.enabled else 0 | |
seams_job_count = self.rows * (self.cols - 1) + (self.rows - 1) * self.cols | |
global state | |
state.job_count = redraw_job_count + seams_job_count | |
def print_info(self) -> None: | |
""" | |
#### Print the information. | |
""" | |
print(f"Tile size: {self.redraw.tile_width}x{self.redraw.tile_height}") | |
print(f"Tiles amount: {self.rows * self.cols}") | |
print(f"Grid: {self.rows}x{self.cols}") | |
print(f"Redraw enabled: {self.redraw.enabled}") | |
print(f"Seams fix mode: {self.seams_fix.mode.name}") | |
def add_extra_info(self) -> None: | |
""" | |
#### Add extra information. | |
""" | |
self.p.extra_generation_params["Ultimate SD upscale upscaler"] = ( | |
self.upscaler.name | |
) | |
self.p.extra_generation_params["Ultimate SD upscale tile_width"] = ( | |
self.redraw.tile_width | |
) | |
self.p.extra_generation_params["Ultimate SD upscale tile_height"] = ( | |
self.redraw.tile_height | |
) | |
self.p.extra_generation_params["Ultimate SD upscale mask_blur"] = ( | |
self.p.mask_blur | |
) | |
self.p.extra_generation_params["Ultimate SD upscale padding"] = ( | |
self.redraw.padding | |
) | |
def process(self, pipeline) -> None: | |
""" | |
#### Process the image. | |
""" | |
USDU_upscaler.state.begin() | |
self.calc_jobs_count() | |
self.result_images = [] | |
if self.redraw.enabled: | |
self.image = self.redraw.start(self.p, self.image, self.rows, self.cols, pipeline) | |
self.initial_info = self.redraw.initial_info | |
self.result_images.append(self.image) | |
if self.seams_fix.enabled: | |
self.image = self.seams_fix.start(self.p, self.image, self.rows, self.cols, pipeline) | |
self.initial_info = self.seams_fix.initial_info | |
self.result_images.append(self.image) | |
USDU_upscaler.state.end() | |
class USDURedraw: | |
"""#### Class representing the redraw functionality for Ultimate SD Upscale.""" | |
def init_draw(self, p: StableDiffusionProcessing, width: int, height: int) -> tuple: | |
""" | |
#### Initialize the draw. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `width` (int): The width. | |
- `height` (int): The height. | |
#### Returns: | |
- `tuple`: The mask and draw objects. | |
""" | |
p.inpaint_full_res = True | |
p.inpaint_full_res_padding = self.padding | |
p.width = math.ceil((self.tile_width + self.padding) / 64) * 64 | |
p.height = math.ceil((self.tile_height + self.padding) / 64) * 64 | |
mask = Image.new("L", (width, height), "black") | |
draw = ImageDraw.Draw(mask) | |
return mask, draw | |
def calc_rectangle(self, xi: int, yi: int) -> tuple: | |
""" | |
#### Calculate the rectangle coordinates. | |
#### Args: | |
- `xi` (int): The x index. | |
- `yi` (int): The y index. | |
#### Returns: | |
- `tuple`: The rectangle coordinates. | |
""" | |
x1 = xi * self.tile_width | |
y1 = yi * self.tile_height | |
x2 = xi * self.tile_width + self.tile_width | |
y2 = yi * self.tile_height + self.tile_height | |
return x1, y1, x2, y2 | |
def linear_process( | |
self, p: StableDiffusionProcessing, image: Image.Image, rows: int, cols: int, pipeline: bool = False | |
) -> Image.Image: | |
""" | |
#### Perform linear processing. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `image` (Image.Image): The image. | |
- `rows` (int): The number of rows. | |
- `cols` (int): The number of columns. | |
#### Returns: | |
- `Image.Image`: The processed image. | |
""" | |
global state | |
mask, draw = self.init_draw(p, image.width, image.height) | |
for yi in range(rows): | |
for xi in range(cols): | |
if state.interrupted: | |
break | |
draw.rectangle(self.calc_rectangle(xi, yi), fill="white") | |
p.init_images = [image] | |
p.image_mask = mask | |
processed = process_images(p, pipeline) | |
draw.rectangle(self.calc_rectangle(xi, yi), fill="black") | |
if len(processed.images) > 0: | |
image = processed.images[0] | |
p.width = image.width | |
p.height = image.height | |
self.initial_info = processed.infotext(p, 0) | |
return image | |
def start(self, p: StableDiffusionProcessing, image: Image.Image, rows: int, cols: int, pipeline: bool = False) -> Image.Image: | |
"""#### Start the redraw. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `image` (Image.Image): The image. | |
- `rows` (int): The number of rows. | |
- `cols` (int): The number of columns. | |
#### Returns: | |
- `Image.Image`: The processed image. | |
""" | |
self.initial_info = None | |
return self.linear_process(p, image, rows, cols, pipeline=pipeline) | |
class USDUSeamsFix: | |
"""#### Class representing the seams fix functionality for Ultimate SD Upscale.""" | |
def init_draw(self, p: StableDiffusionProcessing) -> None: | |
"""#### Initialize the draw. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
""" | |
self.initial_info = None | |
p.width = math.ceil((self.tile_width + self.padding) / 64) * 64 | |
p.height = math.ceil((self.tile_height + self.padding) / 64) * 64 | |
def half_tile_process( | |
self, p: StableDiffusionProcessing, image: Image.Image, rows: int, cols: int, pipeline: bool = False | |
) -> Image.Image: | |
"""#### Perform half-tile processing. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `image` (Image.Image): The image. | |
- `rows` (int): The number of rows. | |
- `cols` (int): The number of columns. | |
#### Returns: | |
- `Image.Image`: The processed image. | |
""" | |
global state | |
self.init_draw(p) | |
processed = None | |
gradient = Image.linear_gradient("L") | |
row_gradient = Image.new("L", (self.tile_width, self.tile_height), "black") | |
row_gradient.paste( | |
gradient.resize( | |
(self.tile_width, self.tile_height // 2), resample=Image.BICUBIC | |
), | |
(0, 0), | |
) | |
row_gradient.paste( | |
gradient.rotate(180).resize( | |
(self.tile_width, self.tile_height // 2), resample=Image.BICUBIC | |
), | |
(0, self.tile_height // 2), | |
) | |
col_gradient = Image.new("L", (self.tile_width, self.tile_height), "black") | |
col_gradient.paste( | |
gradient.rotate(90).resize( | |
(self.tile_width // 2, self.tile_height), resample=Image.BICUBIC | |
), | |
(0, 0), | |
) | |
col_gradient.paste( | |
gradient.rotate(270).resize( | |
(self.tile_width // 2, self.tile_height), resample=Image.BICUBIC | |
), | |
(self.tile_width // 2, 0), | |
) | |
p.denoising_strength = self.denoise | |
p.mask_blur = self.mask_blur | |
for yi in range(rows - 1): | |
for xi in range(cols): | |
p.width = self.tile_width | |
p.height = self.tile_height | |
p.inpaint_full_res = True | |
p.inpaint_full_res_padding = self.padding | |
mask = Image.new("L", (image.width, image.height), "black") | |
mask.paste( | |
row_gradient, | |
( | |
xi * self.tile_width, | |
yi * self.tile_height + self.tile_height // 2, | |
), | |
) | |
p.init_images = [image] | |
p.image_mask = mask | |
processed = process_images(p, pipeline) | |
if len(processed.images) > 0: | |
image = processed.images[0] | |
for yi in range(rows): | |
for xi in range(cols - 1): | |
p.width = self.tile_width | |
p.height = self.tile_height | |
p.inpaint_full_res = True | |
p.inpaint_full_res_padding = self.padding | |
mask = Image.new("L", (image.width, image.height), "black") | |
mask.paste( | |
col_gradient, | |
( | |
xi * self.tile_width + self.tile_width // 2, | |
yi * self.tile_height, | |
), | |
) | |
p.init_images = [image] | |
p.image_mask = mask | |
processed = process_images(p, pipeline) | |
if len(processed.images) > 0: | |
image = processed.images[0] | |
p.width = image.width | |
p.height = image.height | |
if processed is not None: | |
self.initial_info = processed.infotext(p, 0) | |
return image | |
def start( | |
self, p: StableDiffusionProcessing, image: Image.Image, rows: int, cols: int, pipeline: bool = False | |
) -> Image.Image: | |
"""#### Start the seams fix process. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `image` (Image.Image): The image. | |
- `rows` (int): The number of rows. | |
- `cols` (int): The number of columns. | |
#### Returns: | |
- `Image.Image`: The processed image. | |
""" | |
return self.half_tile_process(p, image, rows, cols, pipeline=pipeline) | |
class Script(USDU_upscaler.Script): | |
"""#### Class representing the script for Ultimate SD Upscale.""" | |
def run( | |
self, | |
p: StableDiffusionProcessing, | |
_: None, | |
tile_width: int, | |
tile_height: int, | |
mask_blur: int, | |
padding: int, | |
seams_fix_width: int, | |
seams_fix_denoise: float, | |
seams_fix_padding: int, | |
upscaler_index: int, | |
save_upscaled_image: bool, | |
redraw_mode: int, | |
save_seams_fix_image: bool, | |
seams_fix_mask_blur: int, | |
seams_fix_type: int, | |
target_size_type: int, | |
custom_width: int, | |
custom_height: int, | |
custom_scale: float, | |
pipeline: bool = False, | |
) -> Processed: | |
"""#### Run the script. | |
#### Args: | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `_` (None): Unused parameter. | |
- `tile_width` (int): The tile width. | |
- `tile_height` (int): The tile height. | |
- `mask_blur` (int): The mask blur. | |
- `padding` (int): The padding. | |
- `seams_fix_width` (int): The seams fix width. | |
- `seams_fix_denoise` (float): The seams fix denoise strength. | |
- `seams_fix_padding` (int): The seams fix padding. | |
- `upscaler_index` (int): The upscaler index. | |
- `save_upscaled_image` (bool): Whether to save the upscaled image. | |
- `redraw_mode` (int): The redraw mode. | |
- `save_seams_fix_image` (bool): Whether to save the seams fix image. | |
- `seams_fix_mask_blur` (int): The seams fix mask blur. | |
- `seams_fix_type` (int): The seams fix type. | |
- `target_size_type` (int): The target size type. | |
- `custom_width` (int): The custom width. | |
- `custom_height` (int): The custom height. | |
- `custom_scale` (float): The custom scale. | |
#### Returns: | |
- `Processed`: The processed images. | |
""" | |
# Init | |
fix_seed(p) | |
USDU_upscaler.torch_gc() | |
p.do_not_save_grid = True | |
p.do_not_save_samples = True | |
p.inpaint_full_res = False | |
p.inpainting_fill = 1 | |
p.n_iter = 1 | |
p.batch_size = 1 | |
seed = p.seed | |
# Init image | |
init_img = p.init_images[0] | |
init_img = image_util.flatten( | |
init_img, USDU_upscaler.opts.img2img_background_color | |
) | |
p.width = math.ceil((init_img.width * custom_scale) / 64) * 64 | |
p.height = math.ceil((init_img.height * custom_scale) / 64) * 64 | |
# Upscaling | |
upscaler = USDUpscaler( | |
p, | |
init_img, | |
upscaler_index, | |
save_upscaled_image, | |
save_seams_fix_image, | |
tile_width, | |
tile_height, | |
) | |
upscaler.upscale() | |
# Drawing | |
upscaler.setup_redraw(redraw_mode, padding, mask_blur) | |
upscaler.setup_seams_fix( | |
seams_fix_padding, | |
seams_fix_denoise, | |
seams_fix_mask_blur, | |
seams_fix_width, | |
seams_fix_type, | |
) | |
upscaler.print_info() | |
upscaler.add_extra_info() | |
upscaler.process(pipeline=pipeline) | |
result_images = upscaler.result_images | |
return Processed( | |
p, | |
result_images, | |
seed, | |
upscaler.initial_info if upscaler.initial_info is not None else "", | |
) | |
# Upscaler | |
old_init = USDUpscaler.__init__ | |
def new_init( | |
self: USDUpscaler, | |
p: StableDiffusionProcessing, | |
image: Image.Image, | |
upscaler_index: int, | |
save_redraw: bool, | |
save_seams_fix: bool, | |
tile_width: int, | |
tile_height: int, | |
) -> None: | |
"""#### Initialize the USDUpscaler class with new settings. | |
#### Args: | |
- `self` (USDUpscaler): The USDUpscaler instance. | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `image` (Image.Image): The image. | |
- `upscaler_index` (int): The upscaler index. | |
- `save_redraw` (bool): Whether to save the redraw. | |
- `save_seams_fix` (bool): Whether to save the seams fix. | |
- `tile_width` (int): The tile width. | |
- `tile_height` (int): The tile height. | |
""" | |
p.width = math.ceil((image.width * p.upscale_by) / 8) * 8 | |
p.height = math.ceil((image.height * p.upscale_by) / 8) * 8 | |
old_init( | |
self, | |
p, | |
image, | |
upscaler_index, | |
save_redraw, | |
save_seams_fix, | |
tile_width, | |
tile_height, | |
) | |
USDUpscaler.__init__ = new_init | |
# Redraw | |
old_setup_redraw = USDURedraw.init_draw | |
def new_setup_redraw( | |
self: USDURedraw, p: StableDiffusionProcessing, width: int, height: int | |
) -> tuple: | |
"""#### Set up the redraw with new settings. | |
#### Args: | |
- `self` (USDURedraw): The USDURedraw instance. | |
- `p` (StableDiffusionProcessing): The processing object. | |
- `width` (int): The width. | |
- `height` (int): The height. | |
#### Returns: | |
- `tuple`: The mask and draw objects. | |
""" | |
mask, draw = old_setup_redraw(self, p, width, height) | |
p.width = math.ceil((self.tile_width + self.padding) / 8) * 8 | |
p.height = math.ceil((self.tile_height + self.padding) / 8) * 8 | |
return mask, draw | |
USDURedraw.init_draw = new_setup_redraw | |
# Seams fix | |
old_setup_seams_fix = USDUSeamsFix.init_draw | |
def new_setup_seams_fix(self: USDUSeamsFix, p: StableDiffusionProcessing) -> None: | |
"""#### Set up the seams fix with new settings. | |
#### Args: | |
- `self` (USDUSeamsFix): The USDUSeamsFix instance. | |
- `p` (StableDiffusionProcessing): The processing object. | |
""" | |
old_setup_seams_fix(self, p) | |
p.width = math.ceil((self.tile_width + self.padding) / 8) * 8 | |
p.height = math.ceil((self.tile_height + self.padding) / 8) * 8 | |
USDUSeamsFix.init_draw = new_setup_seams_fix | |
# Make the script upscale on a batch of images instead of one image | |
old_upscale = USDUpscaler.upscale | |
def new_upscale(self: USDUpscaler) -> None: | |
"""#### Upscale a batch of images. | |
#### Args: | |
- `self` (USDUpscaler): The USDUpscaler instance. | |
""" | |
old_upscale(self) | |
USDU_upscaler.batch = [self.image] + [ | |
img.resize((self.p.width, self.p.height), resample=Image.LANCZOS) | |
for img in USDU_upscaler.batch[1:] | |
] | |
USDUpscaler.upscale = new_upscale | |
MAX_RESOLUTION = 8192 | |
# The modes available for Ultimate SD Upscale | |
MODES = { | |
"Linear": USDUMode.LINEAR, | |
"Chess": USDUMode.CHESS, | |
"None": USDUMode.NONE, | |
} | |
# The seam fix modes | |
SEAM_FIX_MODES = { | |
"None": USDUSFMode.NONE, | |
"Band Pass": USDUSFMode.BAND_PASS, | |
"Half Tile": USDUSFMode.HALF_TILE, | |
"Half Tile + Intersections": USDUSFMode.HALF_TILE_PLUS_INTERSECTIONS, | |
} | |
class UltimateSDUpscale: | |
"""#### Class representing the Ultimate SD Upscale functionality.""" | |
def upscale( | |
self, | |
image: torch.Tensor, | |
model: torch.nn.Module, | |
positive: str, | |
negative: str, | |
vae: VariationalAE.VAE, | |
upscale_by: float, | |
seed: int, | |
steps: int, | |
cfg: float, | |
sampler_name: str, | |
scheduler: str, | |
denoise: float, | |
upscale_model: any, | |
mode_type: str, | |
tile_width: int, | |
tile_height: int, | |
mask_blur: int, | |
tile_padding: int, | |
seam_fix_mode: str, | |
seam_fix_denoise: float, | |
seam_fix_mask_blur: int, | |
seam_fix_width: int, | |
seam_fix_padding: int, | |
force_uniform_tiles: bool, | |
pipeline: bool = False, | |
) -> tuple: | |
"""#### Upscale the image. | |
#### Args: | |
- `image` (torch.Tensor): The image tensor. | |
- `model` (torch.nn.Module): The model. | |
- `positive` (str): The positive prompt. | |
- `negative` (str): The negative prompt. | |
- `vae` (VariationalAE.VAE): The variational autoencoder. | |
- `upscale_by` (float): The upscale factor. | |
- `seed` (int): The seed. | |
- `steps` (int): The number of steps. | |
- `cfg` (float): The CFG scale. | |
- `sampler_name` (str): The sampler name. | |
- `scheduler` (str): The scheduler. | |
- `denoise` (float): The denoise strength. | |
- `upscale_model` (any): The upscale model. | |
- `mode_type` (str): The mode type. | |
- `tile_width` (int): The tile width. | |
- `tile_height` (int): The tile height. | |
- `mask_blur` (int): The mask blur. | |
- `tile_padding` (int): The tile padding. | |
- `seam_fix_mode` (str): The seam fix mode. | |
- `seam_fix_denoise` (float): The seam fix denoise strength. | |
- `seam_fix_mask_blur` (int): The seam fix mask blur. | |
- `seam_fix_width` (int): The seam fix width. | |
- `seam_fix_padding` (int): The seam fix padding. | |
- `force_uniform_tiles` (bool): Whether to force uniform tiles. | |
#### Returns: | |
- `tuple`: The resulting tensor. | |
""" | |
# Set up A1111 patches | |
# Upscaler | |
# An object that the script works with | |
USDU_upscaler.sd_upscalers[0] = USDU_upscaler.UpscalerData() | |
# Where the actual upscaler is stored, will be used when the script upscales using the Upscaler in UpscalerData | |
USDU_upscaler.actual_upscaler = upscale_model | |
# Set the batch of images | |
USDU_upscaler.batch = [image_util.tensor_to_pil(image, i) for i in range(len(image))] | |
# Processing | |
sdprocessing = StableDiffusionProcessing( | |
image_util.tensor_to_pil(image), | |
model, | |
positive, | |
negative, | |
vae, | |
seed, | |
steps, | |
cfg, | |
sampler_name, | |
scheduler, | |
denoise, | |
upscale_by, | |
force_uniform_tiles, | |
) | |
# Running the script | |
script = Script() | |
script.run( | |
p=sdprocessing, | |
_=None, | |
tile_width=tile_width, | |
tile_height=tile_height, | |
mask_blur=mask_blur, | |
padding=tile_padding, | |
seams_fix_width=seam_fix_width, | |
seams_fix_denoise=seam_fix_denoise, | |
seams_fix_padding=seam_fix_padding, | |
upscaler_index=0, | |
save_upscaled_image=False, | |
redraw_mode=MODES[mode_type], | |
save_seams_fix_image=False, | |
seams_fix_mask_blur=seam_fix_mask_blur, | |
seams_fix_type=SEAM_FIX_MODES[seam_fix_mode], | |
target_size_type=2, | |
custom_width=None, | |
custom_height=None, | |
custom_scale=upscale_by, | |
pipeline=pipeline, | |
) | |
# Return the resulting images | |
images = [image_util.pil_to_tensor(img) for img in USDU_upscaler.batch] | |
tensor = torch.cat(images, dim=0) | |
return (tensor,) |