2023-02-12 08:52:50 +00:00
|
|
|
# inspired by https://github.com/ProGamerGov/neural-dream/blob/master/neural_dream/dream_tile.py
|
|
|
|
# but with all the bugs fixed and lots of simplifications
|
|
|
|
# MIT License
|
|
|
|
import math
|
|
|
|
|
|
|
|
import torch
|
|
|
|
|
|
|
|
|
|
|
|
def mask_tile(tile, overlap, std_overlap, side="bottom"):
|
2023-02-16 10:39:17 +00:00
|
|
|
b, c, h, w = tile.shape
|
2023-02-12 08:52:50 +00:00
|
|
|
top_overlap, bottom_overlap, right_overlap, left_overlap = overlap
|
|
|
|
(
|
|
|
|
std_top_overlap,
|
|
|
|
std_bottom_overlap,
|
|
|
|
std_right_overlap,
|
|
|
|
std_left_overlap,
|
|
|
|
) = std_overlap
|
|
|
|
|
|
|
|
if "left" in side:
|
|
|
|
lin_mask_left = torch.linspace(0, 1, std_left_overlap, device=tile.device)
|
|
|
|
if left_overlap > std_left_overlap:
|
|
|
|
zeros_mask = torch.zeros(
|
|
|
|
left_overlap - std_left_overlap, device=tile.device
|
|
|
|
)
|
|
|
|
lin_mask_left = (
|
|
|
|
torch.cat([zeros_mask, lin_mask_left], 0)
|
|
|
|
.repeat(h, 1)
|
2023-02-16 10:39:17 +00:00
|
|
|
.repeat(c, 1, 1)
|
2023-02-12 08:52:50 +00:00
|
|
|
.unsqueeze(0)
|
|
|
|
)
|
|
|
|
|
|
|
|
if "right" in side:
|
|
|
|
lin_mask_right = (
|
|
|
|
torch.linspace(1, 0, right_overlap, device=tile.device)
|
|
|
|
.repeat(h, 1)
|
2023-02-16 10:39:17 +00:00
|
|
|
.repeat(c, 1, 1)
|
2023-02-12 08:52:50 +00:00
|
|
|
.unsqueeze(0)
|
|
|
|
)
|
|
|
|
if "top" in side:
|
|
|
|
lin_mask_top = torch.linspace(0, 1, std_top_overlap, device=tile.device)
|
|
|
|
if top_overlap > std_top_overlap:
|
|
|
|
zeros_mask = torch.zeros(top_overlap - std_top_overlap, device=tile.device)
|
|
|
|
lin_mask_top = torch.cat([zeros_mask, lin_mask_top], 0)
|
2023-02-16 10:39:17 +00:00
|
|
|
lin_mask_top = lin_mask_top.repeat(w, 1).rot90(3).repeat(c, 1, 1).unsqueeze(0)
|
2023-02-12 08:52:50 +00:00
|
|
|
|
|
|
|
if "bottom" in side:
|
|
|
|
lin_mask_bottom = (
|
|
|
|
torch.linspace(1, 0, std_bottom_overlap, device=tile.device)
|
|
|
|
.repeat(w, 1)
|
|
|
|
.rot90(3)
|
2023-02-16 10:39:17 +00:00
|
|
|
.repeat(c, 1, 1)
|
2023-02-12 08:52:50 +00:00
|
|
|
.unsqueeze(0)
|
|
|
|
)
|
|
|
|
|
|
|
|
base_mask = torch.ones_like(tile)
|
|
|
|
|
|
|
|
if "right" in side:
|
|
|
|
base_mask[:, :, :, w - right_overlap :] = (
|
|
|
|
base_mask[:, :, :, w - right_overlap :] * lin_mask_right
|
|
|
|
)
|
|
|
|
if "left" in side:
|
|
|
|
base_mask[:, :, :, :left_overlap] = (
|
|
|
|
base_mask[:, :, :, :left_overlap] * lin_mask_left
|
|
|
|
)
|
|
|
|
if "bottom" in side:
|
|
|
|
base_mask[:, :, h - bottom_overlap :, :] = (
|
|
|
|
base_mask[:, :, h - bottom_overlap :, :] * lin_mask_bottom
|
|
|
|
)
|
|
|
|
if "top" in side:
|
|
|
|
base_mask[:, :, :top_overlap, :] = (
|
|
|
|
base_mask[:, :, :top_overlap, :] * lin_mask_top
|
|
|
|
)
|
|
|
|
return tile * base_mask
|
|
|
|
|
|
|
|
|
|
|
|
def get_tile_coords(d, tile_dim, overlap=0):
|
|
|
|
move = int(math.ceil(round(tile_dim * (1 - overlap), 10)))
|
|
|
|
c, tile_start, coords = 1, 0, [0]
|
|
|
|
while tile_start + tile_dim < d:
|
|
|
|
tile_start = move * c
|
|
|
|
if tile_start + tile_dim >= d:
|
|
|
|
coords.append(d - tile_dim)
|
|
|
|
else:
|
|
|
|
coords.append(tile_start)
|
|
|
|
c += 1
|
|
|
|
return coords
|
|
|
|
|
|
|
|
|
|
|
|
def get_tiles(img, tile_coords, tile_size):
|
|
|
|
tile_list = []
|
|
|
|
for y in tile_coords[0]:
|
|
|
|
for x in tile_coords[1]:
|
|
|
|
tile = img[:, :, y : y + tile_size[0], x : x + tile_size[1]]
|
|
|
|
tile_list.append(tile)
|
|
|
|
return tile_list
|
|
|
|
|
|
|
|
|
|
|
|
def final_overlap(tile_coords, tile_size):
|
|
|
|
last_row, last_col = len(tile_coords[0]) - 1, len(tile_coords[1]) - 1
|
|
|
|
|
|
|
|
f_ovlp = [
|
|
|
|
(tile_coords[0][last_row - 1] + tile_size[0]) - (tile_coords[0][last_row]),
|
|
|
|
(tile_coords[1][last_col - 1] + tile_size[1]) - (tile_coords[1][last_col]),
|
|
|
|
]
|
|
|
|
return f_ovlp
|
|
|
|
|
|
|
|
|
|
|
|
def add_tiles(tiles, base_img, tile_coords, tile_size, overlap):
|
|
|
|
f_ovlp = final_overlap(tile_coords, tile_size)
|
|
|
|
h, w = tiles[0].size(2), tiles[0].size(3)
|
|
|
|
if f_ovlp[0] == h:
|
|
|
|
f_ovlp[0] = 0
|
|
|
|
|
|
|
|
if f_ovlp[1] == w:
|
|
|
|
f_ovlp[1] = 0
|
|
|
|
|
|
|
|
t = 0
|
2023-03-17 16:40:05 +00:00
|
|
|
(
|
|
|
|
column,
|
|
|
|
row,
|
|
|
|
) = (
|
2023-02-12 08:52:50 +00:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
|
|
|
|
for y in tile_coords[0]:
|
|
|
|
for x in tile_coords[1]:
|
|
|
|
mask_sides = ""
|
|
|
|
c_overlap = overlap.copy()
|
|
|
|
if row == 0:
|
|
|
|
mask_sides += "bottom"
|
|
|
|
elif 0 < row < len(tile_coords[0]) - 2:
|
|
|
|
mask_sides += "bottom,top"
|
|
|
|
elif row == len(tile_coords[0]) - 2:
|
|
|
|
mask_sides += "bottom,top"
|
|
|
|
elif row == len(tile_coords[0]) - 1:
|
|
|
|
mask_sides += "top"
|
|
|
|
if f_ovlp[0] > 0:
|
|
|
|
c_overlap[0] = f_ovlp[0] # Change top overlap
|
|
|
|
|
|
|
|
if column == 0:
|
|
|
|
mask_sides += ",right"
|
|
|
|
elif 0 < column < len(tile_coords[1]) - 2:
|
|
|
|
mask_sides += ",right,left"
|
|
|
|
elif column == len(tile_coords[1]) - 2:
|
|
|
|
mask_sides += ",right,left"
|
|
|
|
elif column == len(tile_coords[1]) - 1:
|
|
|
|
mask_sides += ",left"
|
|
|
|
if f_ovlp[1] > 0:
|
|
|
|
c_overlap[3] = f_ovlp[1] # Change left overlap
|
|
|
|
|
|
|
|
# print(f"mask_tile: tile.shape={tiles[t].shape}, overlap={c_overlap}, side={mask_sides} col={column}, row={row}")
|
|
|
|
|
|
|
|
tile = mask_tile(tiles[t], c_overlap, std_overlap=overlap, side=mask_sides)
|
|
|
|
# torch_img_to_pillow_img(tile).show()
|
|
|
|
base_img[:, :, y : y + tile_size[0], x : x + tile_size[1]] = (
|
|
|
|
base_img[:, :, y : y + tile_size[0], x : x + tile_size[1]] + tile
|
|
|
|
)
|
|
|
|
# torch_img_to_pillow_img(base_img).show()
|
|
|
|
t += 1
|
|
|
|
column += 1
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
# if row >= 2:
|
|
|
|
# exit()
|
|
|
|
column = 0
|
|
|
|
return base_img
|
|
|
|
|
|
|
|
|
|
|
|
def tile_setup(tile_size, overlap_percent, base_size):
|
|
|
|
if not isinstance(tile_size, (tuple, list)):
|
|
|
|
tile_size = (tile_size, tile_size)
|
|
|
|
if not isinstance(overlap_percent, (tuple, list)):
|
|
|
|
overlap_percent = (overlap_percent, overlap_percent)
|
|
|
|
if min(tile_size) < 1:
|
|
|
|
raise ValueError("tile_size must be at least 1")
|
|
|
|
|
|
|
|
if max(overlap_percent) > 0.5:
|
|
|
|
raise ValueError("overlap_percent must not be greater than 0.5")
|
|
|
|
|
|
|
|
x_coords = get_tile_coords(base_size[1], tile_size[1], overlap_percent[1])
|
|
|
|
y_coords = get_tile_coords(base_size[0], tile_size[0], overlap_percent[0])
|
|
|
|
y_ovlp = int(math.floor(round(tile_size[0] * overlap_percent[0], 10)))
|
|
|
|
x_ovlp = int(math.floor(round(tile_size[1] * overlap_percent[1], 10)))
|
|
|
|
if len(x_coords) == 1:
|
|
|
|
x_ovlp = 0
|
|
|
|
if len(y_coords) == 1:
|
|
|
|
y_ovlp = 0
|
|
|
|
|
|
|
|
return (y_coords, x_coords), tile_size, [y_ovlp, y_ovlp, x_ovlp, x_ovlp]
|
|
|
|
|
|
|
|
|
|
|
|
def tile_image(img, tile_size, overlap_percent):
|
|
|
|
tile_coords, tile_size, _ = tile_setup(
|
|
|
|
tile_size, overlap_percent, (img.size(2), img.size(3))
|
|
|
|
)
|
|
|
|
|
|
|
|
return get_tiles(img, tile_coords, tile_size)
|
|
|
|
|
|
|
|
|
|
|
|
def rebuild_image(tiles, base_img, tile_size, overlap_percent):
|
|
|
|
if len(tiles) == 1:
|
|
|
|
return tiles[0]
|
|
|
|
base_img = torch.zeros_like(base_img)
|
|
|
|
tile_coords, tile_size, overlap = tile_setup(
|
|
|
|
tile_size, overlap_percent, (base_img.size(2), base_img.size(3))
|
|
|
|
)
|
|
|
|
return add_tiles(tiles, base_img, tile_coords, tile_size, overlap)
|