Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be17320be6 |
@@ -1,42 +1,18 @@
|
||||
|
||||
# SimpleTiles
|
||||
## DynamicTileSplit / DynamicTileMerge
|
||||

|
||||
|
||||
## TileSplit
|
||||
Splits image into tiles. Overlap value decides how much overlap there is between tiles on y axis, x axis is calculated to have the same ratio to image height as y axis.
|
||||
|
||||
Automatically splits image into tiles based on image size and tile size. Tiles can be different ratio than images.
|
||||
|
||||
DynamicTileSplit outputs a `tile_calc` object. The object contains info about size and overlap and should be passed to DynamicTileMerge.
|
||||
|
||||
**Overlap** value decides how much overlap there is between tiles on y axis, x axis is calculated to have the same ratio to image height as y axis. Should be set to same value as used in TileSplit.
|
||||
|
||||
**Blend** value decides how many pixels the blending is done over. Should be less than overlap value. Blending is done linearly from 0 to 1 over the blend distance.
|
||||
|
||||
|
||||
## Legacy
|
||||
DynamicTileSplit and DynamicTileMerge are the new versions of TileSplit and TileMerge. They are more flexible and easier to use.
|
||||
|
||||
Legacy nodes don't work well if image ratio and tile ratio is different.
|
||||
|
||||
Use TileCalc to calculate the final image size, pipe the final size to TileMerge and ImageScale.
|
||||
|
||||
|
||||
### TileSplit (Legacy)
|
||||
Splits image into tiles. Overlap value decides how much overlap there is between tiles on y axis, x axis is calculated to have the same ratio to image height as y axis.
|
||||
|
||||
### TileMerge (Legacy)
|
||||
## TileMerge
|
||||
Merge tiles into image.
|
||||
|
||||
**Overlap** value decides how much overlap there is between tiles on y axis, x axis is calculated to have the same ratio to image height as y axis. Should be set to same value as used in TileSplit.
|
||||
|
||||
**Blend** value decides how many pixels the blending is done over. Should be less than overlap value. Blending is done linearly from 0 to 1 over the blend distance.
|
||||
|
||||
### TileCalc (Legacy)
|
||||
## TileCalc
|
||||
|
||||
Util to calculate final image size based on tile sizes and overlaps.
|
||||
|
||||
|
||||
|
||||
## Example Ipadapter
|
||||

|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB |
-203
@@ -1,203 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
import torch
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
|
||||
|
||||
|
||||
def order_by_center_last(tiles, image_width, image_height, tile_width, tile_height):
|
||||
# for 3x3: custom_order = [0, 2, 6, 8, 1, 3, 5, 7, 4] # First 4 corners, then the sides, then the center
|
||||
# order the tiles so they are add based on absolute distance from the center of the tile to the center of the image
|
||||
# this is done so that the center of the image is the last tile to be added, so that the center of the image is the most refined
|
||||
|
||||
# get the center of the image
|
||||
center_x = image_width // 2
|
||||
center_y = image_height // 2
|
||||
|
||||
# sort the tiles by distance from the center
|
||||
tiles = sorted(
|
||||
tiles,
|
||||
key=lambda tile: abs(tile[0] + tile_width // 2 - center_x)
|
||||
+ abs(tile[1] + tile_height // 2 - center_y),
|
||||
)
|
||||
|
||||
# reverse the order so that the center is last
|
||||
tiles = tiles[::-1]
|
||||
|
||||
return tiles
|
||||
|
||||
|
||||
def generate_tiles(
|
||||
image_width, image_height, tile_width, tile_height, overlap, offset=0
|
||||
):
|
||||
tiles = []
|
||||
|
||||
y = 0
|
||||
while y < image_height:
|
||||
if y == 0:
|
||||
next_y = y + tile_height - overlap + offset
|
||||
else:
|
||||
next_y = y + tile_height - overlap
|
||||
|
||||
if y + tile_height >= image_height:
|
||||
y = max(image_height - tile_height, 0)
|
||||
next_y = image_height
|
||||
|
||||
x = 0
|
||||
while x < image_width:
|
||||
if x == 0:
|
||||
next_x = x + tile_width - overlap + offset
|
||||
else:
|
||||
next_x = x + tile_width - overlap
|
||||
if x + tile_width >= image_width:
|
||||
x = max(image_width - tile_width, 0)
|
||||
next_x = image_width
|
||||
|
||||
tiles.append((x, y))
|
||||
|
||||
if next_x > image_width:
|
||||
break
|
||||
x = next_x
|
||||
|
||||
if next_y > image_height:
|
||||
break
|
||||
y = next_y
|
||||
|
||||
return order_by_center_last(tiles, image_width, image_height, tile_width, tile_height)
|
||||
|
||||
|
||||
class DynamicTileSplit:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"tile_width": ("INT", {"default": 512, "min": 1, "max": 10000}),
|
||||
"tile_height": ("INT", {"default": 512, "min": 1, "max": 10000}),
|
||||
"overlap": ("INT", {"default": 128, "min": 1, "max": 10000}),
|
||||
"offset": ("INT", {"default": 0, "min": 0, "max": 10000}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "TILE_CALC")
|
||||
FUNCTION = "process"
|
||||
CATEGORY = "ipadapter"
|
||||
|
||||
def process(self, image, tile_width, tile_height, overlap, offset):
|
||||
image_height = image.shape[1]
|
||||
image_width = image.shape[2]
|
||||
|
||||
tile_coordinates = generate_tiles(
|
||||
image_width, image_height, tile_width, tile_height, overlap, offset
|
||||
)
|
||||
|
||||
print("Tile coordinates: {}".format(tile_coordinates))
|
||||
|
||||
iteration = 1
|
||||
|
||||
image_tiles = []
|
||||
for tile_coordinate in tile_coordinates:
|
||||
print("Processing tile {} of {}".format(iteration, len(tile_coordinates)))
|
||||
print("Tile coordinate: {}".format(tile_coordinate))
|
||||
iteration += 1
|
||||
|
||||
image_tile = image[
|
||||
:,
|
||||
tile_coordinate[1] : tile_coordinate[1] + tile_height,
|
||||
tile_coordinate[0] : tile_coordinate[0] + tile_width,
|
||||
:,
|
||||
]
|
||||
|
||||
image_tiles.append(image_tile)
|
||||
|
||||
tiles_tensor = torch.stack(image_tiles).squeeze(1)
|
||||
tile_calc = (overlap, image_height, image_width, offset)
|
||||
|
||||
return (tiles_tensor, tile_calc)
|
||||
|
||||
|
||||
class DynamicTileMerge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"blend": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"tile_calc": ("TILE_CALC",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "process"
|
||||
CATEGORY = "utils"
|
||||
|
||||
def process(self, images, blend, tile_calc):
|
||||
overlap, final_height, final_width, offset = tile_calc
|
||||
tile_height = images.shape[1]
|
||||
tile_width = images.shape[2]
|
||||
print("Tile height: {}".format(tile_height))
|
||||
print("Tile width: {}".format(tile_width))
|
||||
print("Final height: {}".format(final_height))
|
||||
print("Final width: {}".format(final_width))
|
||||
print("Overlap: {}".format(overlap))
|
||||
|
||||
tile_coordinates = generate_tiles(
|
||||
final_width, final_height, tile_width, tile_height, overlap, offset
|
||||
)
|
||||
|
||||
print("Tile coordinates: {}".format(tile_coordinates))
|
||||
original_shape = (1, final_height, final_width, 3)
|
||||
count = torch.zeros(original_shape, dtype=images.dtype)
|
||||
output = torch.zeros(original_shape, dtype=images.dtype)
|
||||
|
||||
index = 0
|
||||
iteration = 1
|
||||
for tile_coordinate in tile_coordinates:
|
||||
image_tile = images[index]
|
||||
x = tile_coordinate[0]
|
||||
y = tile_coordinate[1]
|
||||
|
||||
print("Processing tile {} of {}".format(iteration, len(tile_coordinates)))
|
||||
print("Tile coordinate: {}".format(tile_coordinate))
|
||||
iteration += 1
|
||||
|
||||
channels = images.shape[3]
|
||||
weight_matrix = torch.ones((tile_height, tile_width, channels))
|
||||
|
||||
# blend border
|
||||
for i in range(blend):
|
||||
weight = float(i) / blend
|
||||
weight_matrix[i, :, :] *= weight # Top rows
|
||||
weight_matrix[-(i + 1), :, :] *= weight # Bottom rows
|
||||
weight_matrix[:, i, :] *= weight # Left columns
|
||||
weight_matrix[:, -(i + 1), :] *= weight # Right columns
|
||||
|
||||
# We only want to blend with already processed pixels, so we keep
|
||||
# track if it has been processed.
|
||||
old_tile = output[:, y : y + tile_height, x : x + tile_width, :]
|
||||
old_tile_count = count[:, y : y + tile_height, x : x + tile_width, :]
|
||||
|
||||
weight_matrix = (
|
||||
weight_matrix * (old_tile_count != 0).float()
|
||||
+ (old_tile_count == 0).float()
|
||||
)
|
||||
|
||||
image_tile = image_tile * weight_matrix + old_tile * (1 - weight_matrix)
|
||||
|
||||
output[:, y : y + tile_height, x : x + tile_width, :] = image_tile
|
||||
count[:, y : y + tile_height, x : x + tile_width, :] = 1
|
||||
|
||||
index += 1
|
||||
return [output]
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DynamicTileSplit": DynamicTileSplit,
|
||||
"DynamicTileMerge": DynamicTileMerge,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DynamicTileSplit": "DynamicTileSplit",
|
||||
"DynamicTileMerge": "DynamicTileMerge",
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 MiB |
@@ -1,18 +1,179 @@
|
||||
from ComfyUI_SimpleTiles.standard import TileSplit, TileMerge, TileCalc
|
||||
from ComfyUI_SimpleTiles.dynamic import DynamicTileSplit, DynamicTileMerge
|
||||
import sys
|
||||
import os
|
||||
|
||||
import torch
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
|
||||
|
||||
|
||||
IMAGE_SIZE = 1472
|
||||
TILE_SIZE = 4096
|
||||
OVERLAP = 64
|
||||
|
||||
# Splits an image in four tiles and returns them as a list
|
||||
class TileSplit:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":{
|
||||
"image": ("IMAGE", ),
|
||||
"tile_height": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"tile_width": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"overlap": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "split"
|
||||
CATEGORY = "utils"
|
||||
|
||||
def split(self, image, tile_height, tile_width, overlap):
|
||||
|
||||
height, width = image.shape[1], image.shape[2]
|
||||
overlap_x = overlap
|
||||
overlap_y = int(overlap * (tile_height / tile_width))
|
||||
|
||||
tiles = []
|
||||
for y in range(0, height-tile_height+1, tile_height-overlap_y):
|
||||
for x in range(0, width-tile_width+1, tile_width-overlap_x):
|
||||
tile = image[:, y:y+tile_height, x:x+tile_width, :]
|
||||
tiles.append(tile)
|
||||
|
||||
# Convert tiles list to a tensor if needed
|
||||
tiles_tensor = torch.stack(tiles).squeeze(1)
|
||||
|
||||
|
||||
|
||||
return [tiles_tensor]
|
||||
|
||||
class TileMerge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":{
|
||||
"images": ("IMAGE", ),
|
||||
"overlap": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"blend": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"final_height": ("INT", {"default": 2048, "min": 0, "max": 9*4096}),
|
||||
"final_width": ("INT", {"default": 2048, "min": 0, "max": 9*4096}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "blend_tiles"
|
||||
CATEGORY = "utils"
|
||||
def blend_tiles(self, images, overlap, blend, final_height, final_width):
|
||||
tiles = images
|
||||
tile_height, tile_width = images.shape[1], images.shape[2]
|
||||
original_shape = (1, final_height, final_width, 3)
|
||||
overlap_x = overlap
|
||||
overlap_y = int(overlap * (tile_height / tile_width))
|
||||
|
||||
batch, height, width, channels = original_shape
|
||||
output = torch.zeros(original_shape, dtype=tiles.dtype)
|
||||
count = torch.zeros(original_shape, dtype=tiles.dtype)
|
||||
idx = 0
|
||||
|
||||
# for 3x3: custom_order = [0, 2, 6, 8, 1, 3, 5, 7, 4] # First 4 corners, then the sides, then the center
|
||||
|
||||
# Calculate grid dimensions
|
||||
# rows = (height - tile_height) // (tile_height - overlap_y) + 1
|
||||
# cols = (width - tile_width) // (tile_width - overlap_x) + 1
|
||||
|
||||
# Calculate the center of the grid
|
||||
# center_row, center_col = rows // 2, cols // 2
|
||||
|
||||
# print("Rows: {}, Cols: {}".format(rows, cols))
|
||||
# print("Center row: {}, Center col: {}".format(center_row, center_col))
|
||||
|
||||
# # Calculate the order in which to blend the tiles
|
||||
# # Order based on distance from center
|
||||
# distances = []
|
||||
# for i in range(rows):
|
||||
# for j in range(cols):
|
||||
# distance = abs(i - center_row) + abs(j - center_col)
|
||||
# distances.append(distance)
|
||||
|
||||
# Sort the tiles based on distance from center
|
||||
# reverse_custom_order = sorted(range(len(distances)), key=lambda k: distances[k])
|
||||
custom_order = [0, 1, 2, 3, 4, 5, 6, 7, 8]
|
||||
|
||||
print("Custom order: {}".format(custom_order))
|
||||
|
||||
|
||||
|
||||
|
||||
ys = [y for y in range(0, height-tile_height+1, tile_height-overlap_y)]
|
||||
xs = [x for x in range(0, width-tile_width+1, tile_width-overlap_x)]
|
||||
for idx in custom_order:
|
||||
|
||||
y = ys[idx // len(ys)]
|
||||
x = xs[idx % len(xs)]
|
||||
|
||||
tile = tiles[idx]
|
||||
|
||||
weight_matrix = torch.ones((tile_height, tile_width, channels))
|
||||
# if not center tile
|
||||
for i in range(blend):
|
||||
weight = float(i) / blend
|
||||
weight_matrix[i, :, :] *= weight # Top rows
|
||||
weight_matrix[-(i + 1), :, :] *= weight # Bottom rows
|
||||
weight_matrix[:, i, :] *= weight # Left columns
|
||||
weight_matrix[:, -(i + 1), :] *= weight # Right columns
|
||||
|
||||
old_tile = output[:, y:y+tile_height, x:x+tile_width, :]
|
||||
old_tile_count = count[:, y:y+tile_height, x:x+tile_width, :]
|
||||
|
||||
weight_matrix = weight_matrix * (old_tile_count != 0).float() + (old_tile_count == 0).float()
|
||||
|
||||
# Blend the old tile with the new tile
|
||||
tile = tile * weight_matrix + old_tile * (1 - weight_matrix)
|
||||
|
||||
output[:, y:y+tile_height, x:x+tile_width, :] = tile
|
||||
count[:, y:y+tile_height, x:x+tile_width, :] = 1
|
||||
|
||||
# Normalize the output and return
|
||||
#output /= count
|
||||
|
||||
|
||||
|
||||
return [output]
|
||||
|
||||
|
||||
class TileCalc:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":{
|
||||
"tile_height": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"tile_width": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"overlap": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"tile_width_n": ("INT", {"default": 3, "min": 1, "max": 9}),
|
||||
"tile_height_n": ("INT", {"default": 3, "min": 1, "max": 9}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("final_height", "final_width")
|
||||
FUNCTION = "calc"
|
||||
CATEGORY = "utils"
|
||||
def calc(self, tile_height, tile_width, overlap, tile_width_n, tile_height_n):
|
||||
overlap_x = overlap
|
||||
overlap_y = int(overlap * (tile_height / tile_width))
|
||||
|
||||
final_height = tile_height * tile_height_n - overlap_y * (tile_height_n - 1)
|
||||
final_width = tile_width * tile_width_n - overlap_x * (tile_width_n - 1)
|
||||
print("Final height: {}, Final width: {}".format(final_height, final_width))
|
||||
|
||||
return [final_height, final_width]
|
||||
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"TileSplit": TileSplit,
|
||||
"TileMerge": TileMerge,
|
||||
"TileCalc": TileCalc,
|
||||
"DynamicTileSplit": DynamicTileSplit,
|
||||
"DynamicTileMerge": DynamicTileMerge,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"TileSplit": "TileSplit (Legacy)",
|
||||
"TileMerge": "TileMerge (Legacy)",
|
||||
"TileCalc": "TileCalc (Legacy)",
|
||||
"DynamicTileSplit": "TileSplit (Dynamic)",
|
||||
"DynamicTileMerge": "TileMerge (Dynamic)",
|
||||
"TileSplit": "TileSplit",
|
||||
"TileMerge": "TileMerge",
|
||||
"TileCalc": "TileCalc",
|
||||
}
|
||||
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
import torch
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
|
||||
|
||||
|
||||
IMAGE_SIZE = 1472
|
||||
TILE_SIZE = 4096
|
||||
OVERLAP = 64
|
||||
|
||||
|
||||
# Splits an image in four tiles and returns them as a list
|
||||
class TileSplit:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"tile_height": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"tile_width": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"overlap": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "split"
|
||||
CATEGORY = "utils"
|
||||
|
||||
def split(self, image, tile_height, tile_width, overlap):
|
||||
height, width = image.shape[1], image.shape[2]
|
||||
overlap_x = overlap
|
||||
overlap_y = int(overlap * (tile_height / tile_width))
|
||||
|
||||
tiles = []
|
||||
for y in range(0, height - tile_height + 1, tile_height - overlap_y):
|
||||
for x in range(0, width - tile_width + 1, tile_width - overlap_x):
|
||||
tile = image[:, y : y + tile_height, x : x + tile_width, :]
|
||||
tiles.append(tile)
|
||||
|
||||
# Convert tiles list to a tensor if needed
|
||||
tiles_tensor = torch.stack(tiles).squeeze(1)
|
||||
|
||||
return [tiles_tensor]
|
||||
|
||||
|
||||
class TileMerge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"overlap": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"blend": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"final_height": ("INT", {"default": 2048, "min": 0, "max": 9 * 4096}),
|
||||
"final_width": ("INT", {"default": 2048, "min": 0, "max": 9 * 4096}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "blend_tiles"
|
||||
CATEGORY = "utils"
|
||||
|
||||
def blend_tiles(self, images, overlap, blend, final_height, final_width):
|
||||
tiles = images
|
||||
tile_height, tile_width = images.shape[1], images.shape[2]
|
||||
original_shape = (1, final_height, final_width, 3)
|
||||
overlap_x = overlap
|
||||
overlap_y = int(overlap * (tile_height / tile_width))
|
||||
|
||||
batch, height, width, channels = original_shape
|
||||
output = torch.zeros(original_shape, dtype=tiles.dtype)
|
||||
count = torch.zeros(original_shape, dtype=tiles.dtype)
|
||||
idx = 0
|
||||
|
||||
# for 3x3: custom_order = [0, 2, 6, 8, 1, 3, 5, 7, 4] # First 4 corners, then the sides, then the center
|
||||
|
||||
# Calculate grid dimensions
|
||||
rows = (height - tile_height) // (tile_height - overlap_y) + 1
|
||||
cols = (width - tile_width) // (tile_width - overlap_x) + 1
|
||||
|
||||
# Calculate the center of the grid
|
||||
center_row, center_col = rows // 2, cols // 2
|
||||
|
||||
print("Rows: {}, Cols: {}".format(rows, cols))
|
||||
print("Center row: {}, Center col: {}".format(center_row, center_col))
|
||||
|
||||
# Calculate the order in which to blend the tiles
|
||||
# Order based on distance from center
|
||||
distances = []
|
||||
for i in range(rows):
|
||||
for j in range(cols):
|
||||
distance = abs(i - center_row) + abs(j - center_col)
|
||||
distances.append(distance)
|
||||
|
||||
# Sort the tiles based on distance from center
|
||||
reverse_custom_order = sorted(range(len(distances)), key=lambda k: distances[k])
|
||||
custom_order = reverse_custom_order[::-1]
|
||||
|
||||
print("Custom order: {}".format(custom_order))
|
||||
|
||||
ys = [y for y in range(0, height - tile_height + 1, tile_height - overlap_y)]
|
||||
xs = [x for x in range(0, width - tile_width + 1, tile_width - overlap_x)]
|
||||
for idx in custom_order:
|
||||
y = ys[idx // len(ys)]
|
||||
x = xs[idx % len(xs)]
|
||||
|
||||
tile = tiles[idx]
|
||||
|
||||
weight_matrix = torch.ones((tile_height, tile_width, channels))
|
||||
# if not center tile
|
||||
for i in range(blend):
|
||||
weight = float(i) / blend
|
||||
weight_matrix[i, :, :] *= weight # Top rows
|
||||
weight_matrix[-(i + 1), :, :] *= weight # Bottom rows
|
||||
weight_matrix[:, i, :] *= weight # Left columns
|
||||
weight_matrix[:, -(i + 1), :] *= weight # Right columns
|
||||
|
||||
old_tile = output[:, y : y + tile_height, x : x + tile_width, :]
|
||||
old_tile_count = count[:, y : y + tile_height, x : x + tile_width, :]
|
||||
|
||||
weight_matrix = (
|
||||
weight_matrix * (old_tile_count != 0).float()
|
||||
+ (old_tile_count == 0).float()
|
||||
)
|
||||
|
||||
# Blend the old tile with the new tile
|
||||
tile = tile * weight_matrix + old_tile * (1 - weight_matrix)
|
||||
|
||||
output[:, y : y + tile_height, x : x + tile_width, :] = tile
|
||||
count[:, y : y + tile_height, x : x + tile_width, :] = 1
|
||||
|
||||
# Normalize the output and return
|
||||
# output /= count
|
||||
|
||||
return [output]
|
||||
|
||||
|
||||
class TileCalc:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"tile_height": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"tile_width": ("INT", {"default": 64, "min": 64, "max": 4096}),
|
||||
"overlap": ("INT", {"default": 64, "min": 0, "max": 4096}),
|
||||
"tile_width_n": ("INT", {"default": 3, "min": 1, "max": 9}),
|
||||
"tile_height_n": ("INT", {"default": 3, "min": 1, "max": 9}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("final_height", "final_width")
|
||||
FUNCTION = "calc"
|
||||
CATEGORY = "utils"
|
||||
|
||||
def calc(self, tile_height, tile_width, overlap, tile_width_n, tile_height_n):
|
||||
overlap_x = overlap
|
||||
overlap_y = int(overlap * (tile_height / tile_width))
|
||||
|
||||
final_height = tile_height * tile_height_n - overlap_y * (tile_height_n - 1)
|
||||
final_width = tile_width * tile_width_n - overlap_x * (tile_width_n - 1)
|
||||
print("Final height: {}, Final width: {}".format(final_height, final_width))
|
||||
|
||||
return [final_height, final_width]
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"TileSplit": TileSplit,
|
||||
"TileMerge": TileMerge,
|
||||
"TileCalc": TileCalc,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"TileSplit": "TileSplit",
|
||||
"TileMerge": "TileMerge",
|
||||
"TileCalc": "TileCalc",
|
||||
}
|
||||
Reference in New Issue
Block a user