Mosaic Expand Image (Mikey)¶
Documentation¶
- Class name:
MosaicExpandImage
- Category:
Mikey/Image
- Output node:
False
The MosaicExpandImage node is designed to expand an image by adding mosaic borders around it. These borders are created by cropping sections from the original image, generating a mosaic from these sections, and then pasting these mosaics around the original image to achieve the desired expanded size.
Input types¶
Required¶
image
- The original image to be expanded. It serves as the central piece around which the mosaic borders are created and added.
- Comfy dtype:
IMAGE
- Python dtype:
PIL.Image
left
- The number of mosaic strips to add to the left side of the image, determining the width of the left expansion.
- Comfy dtype:
INT
- Python dtype:
int
top
- The number of mosaic strips to add to the top of the image, determining the height of the top expansion.
- Comfy dtype:
INT
- Python dtype:
int
right
- The number of mosaic strips to add to the right side of the image, determining the width of the right expansion.
- Comfy dtype:
INT
- Python dtype:
int
bottom
- The number of mosaic strips to add to the bottom of the image, determining the height of the bottom expansion.
- Comfy dtype:
INT
- Python dtype:
int
Output types¶
result_img
- Comfy dtype:
IMAGE
- The expanded image with mosaic borders added around the original image.
- Python dtype:
torch.Tensor
- Comfy dtype:
Usage tips¶
- Infra type:
CPU
- Common nodes: unknown
Source code¶
class MosaicExpandImage:
@classmethod
def INPUT_TYPES(cls):
return {'required': {'image': ('IMAGE', {'default': None}),
'left': ('INT', {'default': 0, 'min': 0, 'max': 5}),
'top': ('INT', {'default': 0, 'min': 0, 'max': 5}),
'right': ('INT', {'default': 0, 'min': 0, 'max': 5}),
'bottom': ('INT', {'default': 0, 'min': 0, 'max': 5})}}
RETURN_TYPES = ('IMAGE','MASK',)
RETURN_NAMES = ('result_img',)
FUNCTION = 'mosaic_expand'
CATEGORY = 'Mikey/Image'
def mosaic_expand(self, image, left, top, right, bottom):
img = tensor2pil(image)
width, height = img.size
width_5th, height_5th = width // 5, height // 5
def create_mosaic(side_img, num_fifths, vertical=False):
block_size = 50
num_blocks_wide = side_img.width // block_size
num_blocks_high = (num_fifths * height_5th) // block_size if vertical else side_img.height // block_size
mosaic_width = block_size * num_blocks_wide
mosaic_height = block_size * num_blocks_high
mosaic_img = Image.new('RGB', (mosaic_width, mosaic_height))
for i in range(num_blocks_wide):
for j in range(num_blocks_high):
# Calculate the average color of each block
section = side_img.crop((i * block_size, j * block_size, (i + 1) * block_size, (j + 1) * block_size))
avg_color = np.array(section).mean(axis=(0, 1)).astype(np.uint8)
# Fill the corresponding block in the mosaic
for x in range(block_size):
for y in range(block_size):
mosaic_x = i * block_size + x
mosaic_y = j * block_size + y
mosaic_img.putpixel((mosaic_x, mosaic_y), tuple(avg_color))
return mosaic_img
self.new_width = width + width_5th * (left + right)
self.new_height = height + height_5th * (top + bottom)
new_img = Image.new('RGB', (self.new_width, self.new_height))
# Create and paste mosaic strips
if right > 0:
right_side = img.crop((width - width_5th * right, 0, width, height))
right_mosaic = create_mosaic(right_side, right)
right_mosaic = right_mosaic.transpose(Image.FLIP_LEFT_RIGHT)
# resize mosaic to match new height
right_mosaic = right_mosaic.resize((width_5th * right + 8, self.new_height))
new_img.paste(right_mosaic, (width + width_5th * left, 0))
if left > 0:
left_side = img.crop((0, 0, width_5th * left, height))
left_mosaic = create_mosaic(left_side, left)
left_mosaic = left_mosaic.transpose(Image.FLIP_LEFT_RIGHT)
# resize mosaic to match new height
left_mosaic = left_mosaic.resize((width_5th * left + 8, self.new_height))
new_img.paste(left_mosaic, (0, 0))
if top > 0:
top_side = img.crop((0, 0, width, height_5th * top))
top_mosaic = create_mosaic(top_side, top, vertical=True)
top_mosaic = top_mosaic.transpose(Image.FLIP_TOP_BOTTOM)
top_mosaic = top_mosaic.resize((width + 32, height_5th * top + 8))
new_img.paste(top_mosaic, (width_5th * left, 0))
if bottom > 0:
bottom_side = img.crop((0, height - height_5th * bottom, width, height))
bottom_mosaic = create_mosaic(bottom_side, bottom, vertical=True)
bottom_mosaic = bottom_mosaic.transpose(Image.FLIP_TOP_BOTTOM)
bottom_mosaic = bottom_mosaic.resize((width + 32, height_5th * bottom + 8))
new_img.paste(bottom_mosaic, (width_5th * left, height + height_5th * top))
# Paste original image
new_img.paste(img, (width_5th * left, height_5th * top))
new_img = pil2tensor(new_img)
# create black and white mask image where white is the original image
mask = Image.new('RGB', (self.new_width, self.new_height), (0, 0, 0))
white = Image.new('RGB', (width, height), (255, 255, 255))
# for each side that has been expanded, shrink that side of the white box by 8 pixels
if right > 0:
white = white.crop((0, 0, width - 64, height))
if left > 0:
white = white.crop((64, 0, width, height))
if top > 0:
white = white.crop((0, 64, width, height))
if bottom > 0:
white = white.crop((0, 0, width, height - 64))
paste_x = width_5th * left + 64
if left > 0:
paste_x += 64
paste_y = height_5th * top
if top > 0:
paste_y += 64
mask.paste(white, (paste_x, paste_y))
mask = np.array(mask.getchannel('R')).astype(np.float32) / 255.0
mask = 1. - torch.from_numpy(mask)
mask = mask.unsqueeze(0)
return (new_img, mask)