InvokeAI/invokeai/backend/generator/embiggen.py

559 lines
27 KiB
Python
Raw Normal View History

2023-03-03 06:02:00 +00:00
"""
invokeai.backend.generator.embiggen descends from .generator
and generates with .generator.img2img
"""
2023-02-28 05:37:13 +00:00
import numpy as np
import torch
from PIL import Image
from tqdm import trange
from .base import Generator
from .img2img import Img2Img
class Embiggen(Generator):
def __init__(self, model, precision):
super().__init__(model, precision)
2023-03-03 06:02:00 +00:00
self.init_latent = None
2023-02-28 05:37:13 +00:00
# Replace generate because Embiggen doesn't need/use most of what it does normallly
2023-03-03 06:02:00 +00:00
def generate(
self,
prompt,
iterations=1,
seed=None,
image_callback=None,
step_callback=None,
**kwargs,
):
make_image = self.get_make_image(prompt, step_callback=step_callback, **kwargs)
results = []
seed = seed if seed else self.new_seed()
2023-02-28 05:37:13 +00:00
# Noise will be generated by the Img2Img generator when called
2023-03-03 06:02:00 +00:00
for _ in trange(iterations, desc="Generating"):
2023-02-28 05:37:13 +00:00
# make_image will call Img2Img which will do the equivalent of get_noise itself
image = make_image()
results.append([image, seed])
if image_callback is not None:
image_callback(image, seed, prompt_in=prompt)
seed = self.new_seed()
return results
@torch.no_grad()
def get_make_image(
self,
prompt,
sampler,
steps,
cfg_scale,
ddim_eta,
conditioning,
init_img,
strength,
width,
height,
embiggen,
embiggen_tiles,
step_callback=None,
2023-03-03 06:02:00 +00:00
**kwargs,
2023-02-28 05:37:13 +00:00
):
"""
Returns a function returning an image derived from the prompt and multi-stage twice-baked potato layering over the img2img on the initial image
Return value depends on the seed at the time you call it
"""
2023-03-03 06:02:00 +00:00
assert (
not sampler.uses_inpainting_model()
), "--embiggen is not supported by inpainting models"
2023-02-28 05:37:13 +00:00
# Construct embiggen arg array, and sanity check arguments
if embiggen == None: # embiggen can also be called with just embiggen_tiles
embiggen = [1.0] # If not specified, assume no scaling
elif embiggen[0] < 0:
embiggen[0] = 1.0
print(
2023-03-03 06:02:00 +00:00
">> Embiggen scaling factor cannot be negative, fell back to the default of 1.0 !"
)
2023-02-28 05:37:13 +00:00
if len(embiggen) < 2:
embiggen.append(0.75)
elif embiggen[1] > 1.0 or embiggen[1] < 0:
embiggen[1] = 0.75
2023-03-03 06:02:00 +00:00
print(
">> Embiggen upscaling strength for ESRGAN must be between 0 and 1, fell back to the default of 0.75 !"
)
2023-02-28 05:37:13 +00:00
if len(embiggen) < 3:
embiggen.append(0.25)
elif embiggen[2] < 0:
embiggen[2] = 0.25
2023-03-03 06:02:00 +00:00
print(
">> Overlap size for Embiggen must be a positive ratio between 0 and 1 OR a number of pixels, fell back to the default of 0.25 !"
)
2023-02-28 05:37:13 +00:00
# Convert tiles from their user-freindly count-from-one to count-from-zero, because we need to do modulo math
# and then sort them, because... people.
if embiggen_tiles:
2023-03-03 06:02:00 +00:00
embiggen_tiles = list(map(lambda n: n - 1, embiggen_tiles))
2023-02-28 05:37:13 +00:00
embiggen_tiles.sort()
if strength >= 0.5:
2023-03-03 06:02:00 +00:00
print(
f"* WARNING: Embiggen may produce mirror motifs if the strength (-f) is too high (currently {strength}). Try values between 0.35-0.45."
)
2023-02-28 05:37:13 +00:00
# Prep img2img generator, since we wrap over it
2023-03-03 06:02:00 +00:00
gen_img2img = Img2Img(self.model, self.precision)
2023-02-28 05:37:13 +00:00
# Open original init image (not a tensor) to manipulate
initsuperimage = Image.open(init_img)
with Image.open(init_img) as img:
2023-03-03 06:02:00 +00:00
initsuperimage = img.convert("RGB")
2023-02-28 05:37:13 +00:00
# Size of the target super init image in pixels
initsuperwidth, initsuperheight = initsuperimage.size
# Increase by scaling factor if not already resized, using ESRGAN as able
if embiggen[0] != 1.0:
2023-03-03 06:02:00 +00:00
initsuperwidth = round(initsuperwidth * embiggen[0])
initsuperheight = round(initsuperheight * embiggen[0])
2023-02-28 05:37:13 +00:00
if embiggen[1] > 0: # No point in ESRGAN upscaling if strength is set zero
2023-03-03 06:02:00 +00:00
from ..restoration.realesrgan import ESRGAN
2023-02-28 05:37:13 +00:00
esrgan = ESRGAN()
print(
2023-03-03 06:02:00 +00:00
f">> ESRGAN upscaling init image prior to cutting with Embiggen with strength {embiggen[1]}"
)
2023-02-28 05:37:13 +00:00
if embiggen[0] > 2:
initsuperimage = esrgan.process(
initsuperimage,
embiggen[1], # upscale strength
self.seed,
4, # upscale scale
)
else:
initsuperimage = esrgan.process(
initsuperimage,
embiggen[1], # upscale strength
self.seed,
2, # upscale scale
)
# We could keep recursively re-running ESRGAN for a requested embiggen[0] larger than 4x
# but from personal experiance it doesn't greatly improve anything after 4x
# Resize to target scaling factor resolution
initsuperimage = initsuperimage.resize(
2023-03-03 06:02:00 +00:00
(initsuperwidth, initsuperheight), Image.Resampling.LANCZOS
)
2023-02-28 05:37:13 +00:00
# Use width and height as tile widths and height
# Determine buffer size in pixels
if embiggen[2] < 1:
if embiggen[2] < 0:
embiggen[2] = 0
overlap_size_x = round(embiggen[2] * width)
overlap_size_y = round(embiggen[2] * height)
else:
overlap_size_x = round(embiggen[2])
overlap_size_y = round(embiggen[2])
# With overall image width and height known, determine how many tiles we need
def ceildiv(a, b):
return -1 * (-a // b)
# X and Y needs to be determined independantly (we may have savings on one based on the buffer pixel count)
# (initsuperwidth - width) is the area remaining to the right that we need to layers tiles to fill
# (width - overlap_size_x) is how much new we can fill with a single tile
emb_tiles_x = 1
emb_tiles_y = 1
if (initsuperwidth - width) > 0:
2023-03-03 06:02:00 +00:00
emb_tiles_x = ceildiv(initsuperwidth - width, width - overlap_size_x) + 1
2023-02-28 05:37:13 +00:00
if (initsuperheight - height) > 0:
2023-03-03 06:02:00 +00:00
emb_tiles_y = ceildiv(initsuperheight - height, height - overlap_size_y) + 1
2023-02-28 05:37:13 +00:00
# Sanity
2023-03-03 06:02:00 +00:00
assert (
emb_tiles_x > 1 or emb_tiles_y > 1
), f"ERROR: Based on the requested dimensions of {initsuperwidth}x{initsuperheight} and tiles of {width}x{height} you don't need to Embiggen! Check your arguments."
2023-02-28 05:37:13 +00:00
# Prep alpha layers --------------
# https://stackoverflow.com/questions/69321734/how-to-create-different-transparency-like-gradient-with-python-pil
# agradientL is Left-side transparent
2023-03-03 06:02:00 +00:00
agradientL = (
Image.linear_gradient("L").rotate(90).resize((overlap_size_x, height))
)
2023-02-28 05:37:13 +00:00
# agradientT is Top-side transparent
2023-03-03 06:02:00 +00:00
agradientT = Image.linear_gradient("L").resize((width, overlap_size_y))
2023-02-28 05:37:13 +00:00
# radial corner is the left-top corner, made full circle then cut to just the left-top quadrant
2023-03-03 06:02:00 +00:00
agradientC = Image.new("L", (256, 256))
2023-02-28 05:37:13 +00:00
for y in range(256):
for x in range(256):
# Find distance to lower right corner (numpy takes arrays)
distanceToLR = np.sqrt([(255 - x) ** 2 + (255 - y) ** 2])[0]
# Clamp values to max 255
if distanceToLR > 255:
distanceToLR = 255
2023-03-03 06:02:00 +00:00
# Place the pixel as invert of distance
2023-02-28 05:37:13 +00:00
agradientC.putpixel((x, y), round(255 - distanceToLR))
# Create alternative asymmetric diagonal corner to use on "tailing" intersections to prevent hard edges
# Fits for a left-fading gradient on the bottom side and full opacity on the right side.
2023-03-03 06:02:00 +00:00
agradientAsymC = Image.new("L", (256, 256))
2023-02-28 05:37:13 +00:00
for y in range(256):
for x in range(256):
2023-03-03 06:02:00 +00:00
value = round(max(0, x - (255 - y)) * (255 / max(1, y)))
# Clamp values
2023-02-28 05:37:13 +00:00
value = max(0, value)
value = min(255, value)
agradientAsymC.putpixel((x, y), value)
# Create alpha layers default fully white
alphaLayerL = Image.new("L", (width, height), 255)
alphaLayerT = Image.new("L", (width, height), 255)
alphaLayerLTC = Image.new("L", (width, height), 255)
# Paste gradients into alpha layers
alphaLayerL.paste(agradientL, (0, 0))
alphaLayerT.paste(agradientT, (0, 0))
alphaLayerLTC.paste(agradientL, (0, 0))
alphaLayerLTC.paste(agradientT, (0, 0))
alphaLayerLTC.paste(agradientC.resize((overlap_size_x, overlap_size_y)), (0, 0))
# make masks with an asymmetric upper-right corner so when the curved transparent corner of the next tile
# to its right is placed it doesn't reveal a hard trailing semi-transparent edge in the overlapping space
alphaLayerTaC = alphaLayerT.copy()
2023-03-03 06:02:00 +00:00
alphaLayerTaC.paste(
agradientAsymC.rotate(270).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, 0),
)
2023-02-28 05:37:13 +00:00
alphaLayerLTaC = alphaLayerLTC.copy()
2023-03-03 06:02:00 +00:00
alphaLayerLTaC.paste(
agradientAsymC.rotate(270).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, 0),
)
2023-02-28 05:37:13 +00:00
if embiggen_tiles:
# Individual unconnected sides
alphaLayerR = Image.new("L", (width, height), 255)
2023-03-03 06:02:00 +00:00
alphaLayerR.paste(agradientL.rotate(180), (width - overlap_size_x, 0))
2023-02-28 05:37:13 +00:00
alphaLayerB = Image.new("L", (width, height), 255)
2023-03-03 06:02:00 +00:00
alphaLayerB.paste(agradientT.rotate(180), (0, height - overlap_size_y))
2023-02-28 05:37:13 +00:00
alphaLayerTB = Image.new("L", (width, height), 255)
alphaLayerTB.paste(agradientT, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerTB.paste(agradientT.rotate(180), (0, height - overlap_size_y))
2023-02-28 05:37:13 +00:00
alphaLayerLR = Image.new("L", (width, height), 255)
alphaLayerLR.paste(agradientL, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerLR.paste(agradientL.rotate(180), (width - overlap_size_x, 0))
2023-02-28 05:37:13 +00:00
# Sides and corner Layers
alphaLayerRBC = Image.new("L", (width, height), 255)
2023-03-03 06:02:00 +00:00
alphaLayerRBC.paste(agradientL.rotate(180), (width - overlap_size_x, 0))
alphaLayerRBC.paste(agradientT.rotate(180), (0, height - overlap_size_y))
alphaLayerRBC.paste(
agradientC.rotate(180).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, height - overlap_size_y),
)
2023-02-28 05:37:13 +00:00
alphaLayerLBC = Image.new("L", (width, height), 255)
alphaLayerLBC.paste(agradientL, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerLBC.paste(agradientT.rotate(180), (0, height - overlap_size_y))
alphaLayerLBC.paste(
agradientC.rotate(90).resize((overlap_size_x, overlap_size_y)),
(0, height - overlap_size_y),
)
2023-02-28 05:37:13 +00:00
alphaLayerRTC = Image.new("L", (width, height), 255)
2023-03-03 06:02:00 +00:00
alphaLayerRTC.paste(agradientL.rotate(180), (width - overlap_size_x, 0))
2023-02-28 05:37:13 +00:00
alphaLayerRTC.paste(agradientT, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerRTC.paste(
agradientC.rotate(270).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, 0),
)
2023-02-28 05:37:13 +00:00
# All but X layers
alphaLayerABT = Image.new("L", (width, height), 255)
alphaLayerABT.paste(alphaLayerLBC, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerABT.paste(agradientL.rotate(180), (width - overlap_size_x, 0))
alphaLayerABT.paste(
agradientC.rotate(180).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, height - overlap_size_y),
)
2023-02-28 05:37:13 +00:00
alphaLayerABL = Image.new("L", (width, height), 255)
alphaLayerABL.paste(alphaLayerRTC, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerABL.paste(agradientT.rotate(180), (0, height - overlap_size_y))
alphaLayerABL.paste(
agradientC.rotate(180).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, height - overlap_size_y),
)
2023-02-28 05:37:13 +00:00
alphaLayerABR = Image.new("L", (width, height), 255)
alphaLayerABR.paste(alphaLayerLBC, (0, 0))
alphaLayerABR.paste(agradientT, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerABR.paste(
agradientC.resize((overlap_size_x, overlap_size_y)), (0, 0)
)
2023-02-28 05:37:13 +00:00
alphaLayerABB = Image.new("L", (width, height), 255)
alphaLayerABB.paste(alphaLayerRTC, (0, 0))
alphaLayerABB.paste(agradientL, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerABB.paste(
agradientC.resize((overlap_size_x, overlap_size_y)), (0, 0)
)
2023-02-28 05:37:13 +00:00
# All-around layer
alphaLayerAA = Image.new("L", (width, height), 255)
alphaLayerAA.paste(alphaLayerABT, (0, 0))
alphaLayerAA.paste(agradientT, (0, 0))
2023-03-03 06:02:00 +00:00
alphaLayerAA.paste(
agradientC.resize((overlap_size_x, overlap_size_y)), (0, 0)
)
alphaLayerAA.paste(
agradientC.rotate(270).resize((overlap_size_x, overlap_size_y)),
(width - overlap_size_x, 0),
)
2023-02-28 05:37:13 +00:00
# Clean up temporary gradients
del agradientL
del agradientT
del agradientC
def make_image():
# Make main tiles -------------------------------------------------
if embiggen_tiles:
2023-03-03 06:02:00 +00:00
print(f">> Making {len(embiggen_tiles)} Embiggen tiles...")
2023-02-28 05:37:13 +00:00
else:
print(
2023-03-03 06:02:00 +00:00
f">> Making {(emb_tiles_x * emb_tiles_y)} Embiggen tiles ({emb_tiles_x}x{emb_tiles_y})..."
)
2023-02-28 05:37:13 +00:00
emb_tile_store = []
# Although we could use the same seed for every tile for determinism, at higher strengths this may
# produce duplicated structures for each tile and make the tiling effect more obvious
# instead track and iterate a local seed we pass to Img2Img
seed = self.seed
2023-03-03 06:02:00 +00:00
seedintlimit = (
np.iinfo(np.uint32).max - 1
) # only retreive this one from numpy
2023-02-28 05:37:13 +00:00
for tile in range(emb_tiles_x * emb_tiles_y):
# Don't iterate on first tile
if tile != 0:
if seed < seedintlimit:
seed += 1
else:
seed = 0
# Determine if this is a re-run and replace
if embiggen_tiles and not tile in embiggen_tiles:
continue
# Get row and column entries
emb_row_i = tile // emb_tiles_x
emb_column_i = tile % emb_tiles_x
# Determine bounds to cut up the init image
# Determine upper-left point
if emb_column_i + 1 == emb_tiles_x:
left = initsuperwidth - width
else:
left = round(emb_column_i * (width - overlap_size_x))
if emb_row_i + 1 == emb_tiles_y:
top = initsuperheight - height
else:
top = round(emb_row_i * (height - overlap_size_y))
right = left + width
bottom = top + height
# Cropped image of above dimension (does not modify the original)
newinitimage = initsuperimage.crop((left, top, right, bottom))
# DEBUG:
# newinitimagepath = init_img[0:-4] + f'_emb_Ti{tile}.png'
# newinitimage.save(newinitimagepath)
if embiggen_tiles:
print(
2023-03-03 06:02:00 +00:00
f"Making tile #{tile + 1} ({embiggen_tiles.index(tile) + 1} of {len(embiggen_tiles)} requested)"
)
2023-02-28 05:37:13 +00:00
else:
2023-03-03 06:02:00 +00:00
print(f"Starting {tile + 1} of {(emb_tiles_x * emb_tiles_y)} tiles")
2023-02-28 05:37:13 +00:00
# create a torch tensor from an Image
2023-03-03 06:02:00 +00:00
newinitimage = np.array(newinitimage).astype(np.float32) / 255.0
2023-02-28 05:37:13 +00:00
newinitimage = newinitimage[None].transpose(0, 3, 1, 2)
newinitimage = torch.from_numpy(newinitimage)
newinitimage = 2.0 * newinitimage - 1.0
newinitimage = newinitimage.to(self.model.device)
2023-03-03 06:02:00 +00:00
clear_cuda_cache = (
kwargs["clear_cuda_cache"] if "clear_cuda_cache" in kwargs else None
)
2023-02-28 05:37:13 +00:00
tile_results = gen_img2img.generate(
prompt,
2023-03-03 06:02:00 +00:00
iterations=1,
seed=seed,
sampler=sampler,
steps=steps,
cfg_scale=cfg_scale,
conditioning=conditioning,
ddim_eta=ddim_eta,
image_callback=None, # called only after the final image is generated
step_callback=step_callback, # called after each intermediate image is generated
width=width,
height=height,
init_image=newinitimage, # notice that init_image is different from init_img
mask_image=None,
strength=strength,
clear_cuda_cache=clear_cuda_cache,
2023-02-28 05:37:13 +00:00
)
emb_tile_store.append(tile_results[0][0])
# DEBUG (but, also has other uses), worth saving if you want tiles without a transparency overlap to manually composite
# emb_tile_store[-1].save(init_img[0:-4] + f'_emb_To{tile}.png')
del newinitimage
# Sanity check we have them all
2023-03-03 06:02:00 +00:00
if len(emb_tile_store) == (emb_tiles_x * emb_tiles_y) or (
embiggen_tiles != [] and len(emb_tile_store) == len(embiggen_tiles)
):
outputsuperimage = Image.new("RGBA", (initsuperwidth, initsuperheight))
2023-02-28 05:37:13 +00:00
if embiggen_tiles:
outputsuperimage.alpha_composite(
2023-03-03 06:02:00 +00:00
initsuperimage.convert("RGBA"), (0, 0)
)
2023-02-28 05:37:13 +00:00
for tile in range(emb_tiles_x * emb_tiles_y):
if embiggen_tiles:
if tile in embiggen_tiles:
intileimage = emb_tile_store.pop(0)
else:
continue
else:
intileimage = emb_tile_store[tile]
2023-03-03 06:02:00 +00:00
intileimage = intileimage.convert("RGBA")
2023-02-28 05:37:13 +00:00
# Get row and column entries
emb_row_i = tile // emb_tiles_x
emb_column_i = tile % emb_tiles_x
if emb_row_i == 0 and emb_column_i == 0 and not embiggen_tiles:
left = 0
top = 0
else:
# Determine upper-left point
if emb_column_i + 1 == emb_tiles_x:
left = initsuperwidth - width
else:
2023-03-03 06:02:00 +00:00
left = round(emb_column_i * (width - overlap_size_x))
2023-02-28 05:37:13 +00:00
if emb_row_i + 1 == emb_tiles_y:
top = initsuperheight - height
else:
top = round(emb_row_i * (height - overlap_size_y))
# Handle gradients for various conditions
# Handle emb_rerun case
if embiggen_tiles:
# top of image
if emb_row_i == 0:
if emb_column_i == 0:
2023-03-03 06:02:00 +00:00
if (tile + 1) in embiggen_tiles: # Look-ahead right
if (
tile + emb_tiles_x
) not in embiggen_tiles: # Look-ahead down
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerB)
# Otherwise do nothing on this tile
2023-03-03 06:02:00 +00:00
elif (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down only
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerR)
else:
intileimage.putalpha(alphaLayerRBC)
elif emb_column_i == emb_tiles_x - 1:
2023-03-03 06:02:00 +00:00
if (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerL)
else:
intileimage.putalpha(alphaLayerLBC)
else:
2023-03-03 06:02:00 +00:00
if (tile + 1) in embiggen_tiles: # Look-ahead right
if (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerL)
else:
intileimage.putalpha(alphaLayerLBC)
2023-03-03 06:02:00 +00:00
elif (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down only
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerLR)
else:
intileimage.putalpha(alphaLayerABT)
# bottom of image
elif emb_row_i == emb_tiles_y - 1:
if emb_column_i == 0:
2023-03-03 06:02:00 +00:00
if (tile + 1) in embiggen_tiles: # Look-ahead right
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerTaC)
else:
intileimage.putalpha(alphaLayerRTC)
elif emb_column_i == emb_tiles_x - 1:
# No tiles to look ahead to
intileimage.putalpha(alphaLayerLTC)
else:
2023-03-03 06:02:00 +00:00
if (tile + 1) in embiggen_tiles: # Look-ahead right
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerLTaC)
else:
intileimage.putalpha(alphaLayerABB)
# vertical middle of image
else:
if emb_column_i == 0:
2023-03-03 06:02:00 +00:00
if (tile + 1) in embiggen_tiles: # Look-ahead right
if (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerTaC)
else:
intileimage.putalpha(alphaLayerTB)
2023-03-03 06:02:00 +00:00
elif (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down only
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerRTC)
else:
intileimage.putalpha(alphaLayerABL)
elif emb_column_i == emb_tiles_x - 1:
2023-03-03 06:02:00 +00:00
if (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerLTC)
else:
intileimage.putalpha(alphaLayerABR)
else:
2023-03-03 06:02:00 +00:00
if (tile + 1) in embiggen_tiles: # Look-ahead right
if (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerLTaC)
else:
intileimage.putalpha(alphaLayerABR)
2023-03-03 06:02:00 +00:00
elif (
tile + emb_tiles_x
) in embiggen_tiles: # Look-ahead down only
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerABB)
else:
intileimage.putalpha(alphaLayerAA)
# Handle normal tiling case (much simpler - since we tile left to right, top to bottom)
else:
if emb_row_i == 0 and emb_column_i >= 1:
intileimage.putalpha(alphaLayerL)
elif emb_row_i >= 1 and emb_column_i == 0:
2023-03-03 06:02:00 +00:00
if (
emb_column_i + 1 == emb_tiles_x
): # If we don't have anything that can be placed to the right
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerT)
else:
intileimage.putalpha(alphaLayerTaC)
else:
2023-03-03 06:02:00 +00:00
if (
emb_column_i + 1 == emb_tiles_x
): # If we don't have anything that can be placed to the right
2023-02-28 05:37:13 +00:00
intileimage.putalpha(alphaLayerLTC)
else:
intileimage.putalpha(alphaLayerLTaC)
# Layer tile onto final image
outputsuperimage.alpha_composite(intileimage, (left, top))
else:
2023-03-03 06:02:00 +00:00
print(
"Error: could not find all Embiggen output tiles in memory? Something must have gone wrong with img2img generation."
)
2023-02-28 05:37:13 +00:00
# after internal loops and patching up return Embiggen image
return outputsuperimage
2023-03-03 06:02:00 +00:00
2023-02-28 05:37:13 +00:00
# end of function declaration
return make_image