From fefb78795f810f707bc77ded6d5107e5e1428cb6 Mon Sep 17 00:00:00 2001 From: skunkworxdark Date: Mon, 11 Dec 2023 16:55:27 +0000 Subject: [PATCH] - Even_spilt overlap renamed to overlap_fraction - min_overlap removed * restrictions and round_to_8 - min_overlap handles tile size > image size by clipping the num tiles to 1. - Updated assert test on min_overlap. --- invokeai/app/invocations/tiles.py | 20 +++++--------------- invokeai/backend/tiles/tiles.py | 22 +++++++++++----------- invokeai/backend/tiles/utils.py | 5 +++++ tests/backend/tiles/test_tiles.py | 4 ++-- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/invokeai/app/invocations/tiles.py b/invokeai/app/invocations/tiles.py index beec084b7d..e368976b4b 100644 --- a/invokeai/app/invocations/tiles.py +++ b/invokeai/app/invocations/tiles.py @@ -88,7 +88,7 @@ class CalculateImageTilesEvenSplitInvocation(BaseInvocation): ge=1, description="Number of tiles to divide image into on the y axis", ) - overlap: float = InputField( + overlap_fraction: float = InputField( default=0.25, ge=0, lt=1, @@ -101,7 +101,7 @@ class CalculateImageTilesEvenSplitInvocation(BaseInvocation): image_width=self.image_width, num_tiles_x=self.num_tiles_x, num_tiles_y=self.num_tiles_y, - overlap=self.overlap, + overlap_fraction=self.overlap_fraction, ) return CalculateImageTilesOutput(tiles=tiles) @@ -120,18 +120,9 @@ class CalculateImageTilesMinimumOverlapInvocation(BaseInvocation): image_height: int = InputField( ge=1, default=1024, description="The image height, in pixels, to calculate tiles for." ) - tile_width: int = InputField(ge=1, default=576, multiple_of=8, description="The tile width, in pixels.") - tile_height: int = InputField(ge=1, default=576, multiple_of=8, description="The tile height, in pixels.") - min_overlap: int = InputField( - default=128, - ge=0, - multiple_of=8, - description="Minimum overlap between adjacent tiles, in pixels(must be a multiple of 8).", - ) - round_to_8: bool = InputField( - default=False, - description="Round outputs down to the nearest 8 (for pulling from a large noise field)", - ) + tile_width: int = InputField(ge=1, default=576, description="The tile width, in pixels.") + tile_height: int = InputField(ge=1, default=576, description="The tile height, in pixels.") + min_overlap: int = InputField(default=128, ge=0, description="Minimum overlap between adjacent tiles, in pixels.") def invoke(self, context: InvocationContext) -> CalculateImageTilesOutput: tiles = calc_tiles_min_overlap( @@ -140,7 +131,6 @@ class CalculateImageTilesMinimumOverlapInvocation(BaseInvocation): tile_height=self.tile_height, tile_width=self.tile_width, min_overlap=self.min_overlap, - round_to_8=self.round_to_8, ) return CalculateImageTilesOutput(tiles=tiles) diff --git a/invokeai/backend/tiles/tiles.py b/invokeai/backend/tiles/tiles.py index 2f71db03cb..499558ce9d 100644 --- a/invokeai/backend/tiles/tiles.py +++ b/invokeai/backend/tiles/tiles.py @@ -102,7 +102,7 @@ def calc_tiles_with_overlap( def calc_tiles_even_split( - image_height: int, image_width: int, num_tiles_x: int, num_tiles_y: int, overlap: float = 0 + image_height: int, image_width: int, num_tiles_x: int, num_tiles_y: int, overlap_fraction: float = 0 ) -> list[Tile]: """Calculate the tile coordinates for a given image shape with the number of tiles requested. @@ -111,7 +111,7 @@ def calc_tiles_even_split( image_width (int): The image width in px. num_x_tiles (int): The number of tile to split the image into on the X-axis. num_y_tiles (int): The number of tile to split the image into on the Y-axis. - overlap (float, optional): The target overlap amount of the tiles size. Defaults to 0. + overlap_fraction (float, optional): The target overlap as fraction of the tiles size. Defaults to 0. Returns: list[Tile]: A list of tiles that cover the image shape. Ordered from left-to-right, top-to-bottom. @@ -119,11 +119,15 @@ def calc_tiles_even_split( # Ensure tile size is divisible by 8 if image_width % LATENT_SCALE_FACTOR != 0 or image_height % LATENT_SCALE_FACTOR != 0: - raise ValueError(f"image size (({image_width}, {image_height})) must be divisible by 8") + raise ValueError(f"image size (({image_width}, {image_height})) must be divisible by {LATENT_SCALE_FACTOR}") # Calculate the overlap size based on the percentage and adjust it to be divisible by 8 (rounding up) - overlap_x = LATENT_SCALE_FACTOR * math.ceil(int((image_width / num_tiles_x) * overlap) / LATENT_SCALE_FACTOR) - overlap_y = LATENT_SCALE_FACTOR * math.ceil(int((image_height / num_tiles_y) * overlap) / LATENT_SCALE_FACTOR) + overlap_x = LATENT_SCALE_FACTOR * math.ceil( + int((image_width / num_tiles_x) * overlap_fraction) / LATENT_SCALE_FACTOR + ) + overlap_y = LATENT_SCALE_FACTOR * math.ceil( + int((image_height / num_tiles_y) * overlap_fraction) / LATENT_SCALE_FACTOR + ) # Calculate the tile size based on the number of tiles and overlap, and ensure it's divisible by 8 (rounding down) tile_size_x = LATENT_SCALE_FACTOR * math.floor( @@ -184,11 +188,11 @@ def calc_tiles_min_overlap( Returns: list[Tile]: A list of tiles that cover the image shape. Ordered from left-to-right, top-to-bottom. """ - assert image_height >= tile_height - assert image_width >= tile_width + assert min_overlap < tile_height assert min_overlap < tile_width + # The If Else catches the case when the tile size is larger than the images size and just clips the number of tiles to 1 num_tiles_x = math.ceil((image_width - min_overlap) / (tile_width - min_overlap)) if tile_width < image_width else 1 num_tiles_y = ( math.ceil((image_height - min_overlap) / (tile_height - min_overlap)) if tile_height < image_height else 1 @@ -200,14 +204,10 @@ def calc_tiles_min_overlap( # Calculate tile coordinates. (Ignore overlap values for now.) for tile_idx_y in range(num_tiles_y): top = (tile_idx_y * (image_height - tile_height)) // (num_tiles_y - 1) if num_tiles_y > 1 else 0 - if round_to_8: - top = LATENT_SCALE_FACTOR * (top // LATENT_SCALE_FACTOR) bottom = top + tile_height for tile_idx_x in range(num_tiles_x): left = (tile_idx_x * (image_width - tile_width)) // (num_tiles_x - 1) if num_tiles_x > 1 else 0 - if round_to_8: - left = LATENT_SCALE_FACTOR * (left // LATENT_SCALE_FACTOR) right = left + tile_width tile = Tile( diff --git a/invokeai/backend/tiles/utils.py b/invokeai/backend/tiles/utils.py index 8596d25840..dc6d914170 100644 --- a/invokeai/backend/tiles/utils.py +++ b/invokeai/backend/tiles/utils.py @@ -74,6 +74,9 @@ def seam_blend(ia1: np.ndarray, ia2: np.ndarray, blend_amount: int, x_seam: bool return result # Assume RGB and convert to grey + # Could offer other options for the luminance conversion + # BT.709 [0.2126, 0.7152, 0.0722], BT.2020 [0.2627, 0.6780, 0.0593]) + # it might not have a huge impact due to the blur that is applied over the seam iag1 = np.dot(ia1, [0.2989, 0.5870, 0.1140]) # BT.601 perceived brightness iag2 = np.dot(ia2, [0.2989, 0.5870, 0.1140]) @@ -92,6 +95,7 @@ def seam_blend(ia1: np.ndarray, ia2: np.ndarray, blend_amount: int, x_seam: bool min_x = gutter # Calc the energy in the difference + # Could offer different energy calculations e.g. Sobel or Scharr energy = np.abs(np.gradient(ia, axis=0)) + np.abs(np.gradient(ia, axis=1)) # Find the starting position of the seam @@ -107,6 +111,7 @@ def seam_blend(ia1: np.ndarray, ia2: np.ndarray, blend_amount: int, x_seam: bool lowest_energy_line[max_y - 1] = np.argmin(res[max_y - 1, min_x : max_x - 1]) # Calc the path of the seam + # could offer options for larger search than just 1 pixel by adjusting lpos and rpos for ypos in range(max_y - 2, -1, -1): lowest_pos = lowest_energy_line[ypos + 1] lpos = lowest_pos - 1 diff --git a/tests/backend/tiles/test_tiles.py b/tests/backend/tiles/test_tiles.py index 87700f8d09..3ae7e2b91d 100644 --- a/tests/backend/tiles/test_tiles.py +++ b/tests/backend/tiles/test_tiles.py @@ -371,8 +371,8 @@ def test_calc_tiles_min_overlap_difficult_size_div8(): (128, 128, 128, 128, 127, False), # OK (128, 128, 128, 128, 0, False), # OK (128, 128, 64, 64, 0, False), # OK - (128, 128, 129, 128, 0, True), # tile_height exceeds image_height. - (128, 128, 128, 129, 0, True), # tile_width exceeds image_width. + (128, 128, 129, 128, 0, False), # tile_height exceeds image_height defaults to 1 tile. + (128, 128, 128, 129, 0, False), # tile_width exceeds image_width defaults to 1 tile. (128, 128, 64, 128, 64, True), # overlap equals tile_height. (128, 128, 128, 64, 64, True), # overlap equals tile_width. ],