1
0
Fork 0

Update tile.py

Update spiral tiling
This commit is contained in:
HoopyFreud 2023-07-06 22:46:36 -04:00 committed by GitHub
parent cecee8653b
commit 05e8addb06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 122 additions and 87 deletions

View File

@ -1,11 +1,14 @@
from logging import getLogger from logging import getLogger
from math import ceil from math import ceil
from typing import List, Optional, Protocol, Tuple from typing import List, Optional, Protocol, Tuple
from enum import Enum
import itertools
import numpy as np import numpy as np
from PIL import Image from PIL import Image
from ..params import Size, TileOrder from ..params import Size, TileOrder
from ..image.noise_source import noise_source_histogram
# from skimage.exposure import match_histograms # from skimage.exposure import match_histograms
@ -125,24 +128,43 @@ def blend_tiles(
scaled_left = left * scale scaled_left = left * scale
# equalized size may be wrong/too much # equalized size may be wrong/too much
scaled_bottom = min(scaled_top + equalized.shape[0], scaled_size[0]) scaled_bottom = scaled_top + equalized.shape[0]
scaled_right = min(scaled_left + equalized.shape[1], scaled_size[1]) scaled_right = scaled_left + equalized.shape[1]
logger.trace(
writable_top = max(scaled_top,0)
writable_left = max(scaled_left,0)
writable_bottom = min(scaled_bottom,scaled_size[0])
writable_right = min(scaled_right,scaled_size[1])
margin_top = writable_top - scaled_top
margin_left = writable_left - scaled_left
margin_bottom = writable_bottom - scaled_bottom
margin_right = writable_right - scaled_right
logger.debug(
"tile broadcast shapes: %s, %s, %s, %s", "tile broadcast shapes: %s, %s, %s, %s",
scaled_top, writable_top,
scaled_bottom, writable_left,
writable_bottom,
writable_right,
)
logger.debug(
"writing shapes: %s, %s, %s, %s",
margin_top,
equalized.shape[0] + margin_bottom,
scaled_left, scaled_left,
scaled_right, equalized.shape[0] + margin_right,
) )
# accumulation # accumulation
value[scaled_top:scaled_bottom, scaled_left:scaled_right, :] += equalized[ value[writable_top:writable_bottom, writable_left:writable_right, :] += equalized[
0 : scaled_bottom - scaled_top, 0 : scaled_right - scaled_left, : margin_top : equalized.shape[0] + margin_bottom, margin_left : equalized.shape[1] + margin_right, :
] ]
count[scaled_top:scaled_bottom, scaled_left:scaled_right, :] += np.repeat( count[writable_top:writable_bottom, writable_left:writable_right, :] += np.repeat(
mask[ mask[
0 : scaled_bottom - scaled_top, margin_top : equalized.shape[0] + margin_bottom,
0 : scaled_right - scaled_left, margin_left : equalized.shape[1] + margin_right,
np.newaxis, np.newaxis,
], ],
3, 3,
@ -207,17 +229,11 @@ def process_tile_spiral(
overlap: float = 0.5, overlap: float = 0.5,
**kwargs, **kwargs,
) -> Image.Image: ) -> Image.Image:
if scale != 1:
raise ValueError("unsupported scale")
width, height = kwargs.get("size", source.size if source else None) width, height = kwargs.get("size", source.size if source else None)
# spiral uses the previous run and needs a scratch texture for 3x memory # spiral uses the previous run and needs a scratch texture for 3x memory
image = Image.new("RGB", (width * scale, height * scale))
image.paste(source, (0, 0, width, height))
tiles: List[Tuple[int, int, Image.Image]] = [] tiles: List[Tuple[int, int, Image.Image]] = []
tiles.append((0, 0, source))
# tile tuples is source, multiply by scale for dest # tile tuples is source, multiply by scale for dest
counter = 0 counter = 0
@ -228,13 +244,35 @@ def process_tile_spiral(
"processing tile %s of %s, %sx%s", counter, len(tile_coords), left, top "processing tile %s of %s, %sx%s", counter, len(tile_coords), left, top
) )
tile_image = image.crop((left, top, left + tile, top + tile)) if image else None right = left + tile
tile_image = complete_tile(tile_image, tile) bottom = top + tile
for filter in filters: left_margin = right_margin = top_margin = bottom_margin = 0
tile_image = filter(tile_image, (left, top, tile)) needs_margin = False
if left < 0:
needs_margin = True
left_margin = 0 - left
if right > width:
needs_margin = True
right_margin = width - right
if top < 0:
needs_margin = True
top_margin = 0 - top
if bottom > height:
needs_margin = True
bottom_margin = height - bottom
if needs_margin:
base_image = source.crop((left+left_margin, top+top_margin, right-right_margin, bottom-bottom_margin)) if source else None
tile_image = noise_source_histogram(base_image,(tile,tile),(0,0))
tile_image.paste(base_image,(left_margin,top_margin))
else:
tile_image = source.crop((left, top, right, bottom)) if source else None
for image_filter in filters:
tile_image = image_filter(tile_image, (left, top, tile))
image.paste(tile_image, (left * scale, top * scale))
tiles.append((left, top, tile_image)) tiles.append((left, top, tile_image))
return blend_tiles(tiles, scale, width, height, tile, overlap) return blend_tiles(tiles, scale, width, height, tile, overlap)
@ -270,75 +308,72 @@ def generate_tile_spiral(
) -> List[Tuple[int, int]]: ) -> List[Tuple[int, int]]:
spacing = 1.0 - overlap spacing = 1.0 - overlap
# round dims up to nearest tiles tile_increment = round(tile * spacing/2)*2 #dividing and then multiplying by 2 ensures this will be an even number, which is necessary for the initial tile placement calculation
tile_width = ceil(width / tile)
tile_height = ceil(height / tile)
# start walking from the north-west corner, heading east #calculate the number of tiles needed
dir_height = 0 width_tile_target = 1
dir_width = 1 height_tile_target = 1
if width > tile:
width_tile_target = 1 + ceil((width - tile) / tile_increment)
if height > tile:
height_tile_target = 1 + ceil((height - tile) / tile_increment)
walk_height = tile_height #calculate the start position of the tiling
walk_width = tile_width span_x = tile + (width_tile_target - 1)*tile_increment
span_y = tile + (height_tile_target - 1)*tile_increment
accum_height = 0 tile_left = (width - span_x)/2 #guaranteed to be an integer because width and span will both be even
accum_width = 0 tile_top = (height - span_y)/2 #guaranteed to be an integer because width and span will both be even
tile_top = 0
tile_left = 0 logger.debug(
"image size %s x %s, tiling to %s x %s, starting at %s, %s",
width,
height,
width_tile_target,
height_tile_target,
tile_left,
tile_top
)
tile_coords = [] tile_coords = []
while walk_width > 0 and walk_height > 0:
# exhaust the current direction, then turn # start walking from the north-west corner, heading east
while accum_width < walk_width and accum_height < walk_height: class WalkState(Enum):
EAST = (1,0)
SOUTH = (0,1)
WEST = (-1,0)
NORTH = (0,-1)
#initialize the tile_left placement
tile_left -= tile_increment
height_tile_target -= 1
for state in itertools.cycle(WalkState):
#This expression is stupid, but all it does is calculate the number of tiles we need in the appropriate direction
accum_tile_target = max(map(lambda coord,val: abs(coord*val),state.value,(width_tile_target,height_tile_target)))
#check if done
if accum_tile_target == 0:
break
#reset tile count
accum_tiles = 0
while accum_tiles < accum_tile_target:
# move to the next
tile_left += tile_increment*state.value[0]
tile_top += tile_increment*state.value[1]
# add a tile # add a tile
logger.trace( logger.debug(
"adding tile at %s:%s, %s:%s, %s:%s, %s", "adding tile at %s:%s",
tile_left, tile_left,
tile_top, tile_top
accum_width,
accum_height,
walk_width,
walk_height,
spacing,
) )
tile_coords.append((int(tile_left), int(tile_top))) tile_coords.append((int(tile_left), int(tile_top)))
# move to the next accum_tiles += 1
tile_top += dir_height * spacing * tile
tile_left += dir_width * spacing * tile
accum_height += abs(dir_height * spacing) width_tile_target -= abs(state.value[0])
accum_width += abs(dir_width * spacing) height_tile_target -= abs(state.value[1])
# reset for the next direction
accum_height = 0
accum_width = 0
# why tho
tile_top -= dir_height
tile_left -= dir_width
# turn right
if dir_width == 1 and dir_height == 0:
dir_width = 0
dir_height = 1
elif dir_width == 0 and dir_height == 1:
dir_width = -1
dir_height = 0
elif dir_width == -1 and dir_height == 0:
dir_width = 0
dir_height = -1
elif dir_width == 0 and dir_height == -1:
dir_width = 1
dir_height = 0
# step to the next tile as part of the turn
tile_top += dir_height
tile_left += dir_width
# shrink the last direction
walk_height -= abs(dir_height)
walk_width -= abs(dir_width)
return tile_coords return tile_coords