Merge remote-tracking branch 'origin/main' into feat/db/migrations

This commit is contained in:
psychedelicious
2023-12-12 17:22:52 +11:00
33 changed files with 1933 additions and 195 deletions

View File

@ -1,7 +1,12 @@
import numpy as np
import pytest
from invokeai.backend.tiles.tiles import calc_tiles_with_overlap, merge_tiles_with_linear_blending
from invokeai.backend.tiles.tiles import (
calc_tiles_even_split,
calc_tiles_min_overlap,
calc_tiles_with_overlap,
merge_tiles_with_linear_blending,
)
from invokeai.backend.tiles.utils import TBLR, Tile
####################################
@ -14,7 +19,10 @@ def test_calc_tiles_with_overlap_single_tile():
tiles = calc_tiles_with_overlap(image_height=512, image_width=1024, tile_height=512, tile_width=1024, overlap=64)
expected_tiles = [
Tile(coords=TBLR(top=0, bottom=512, left=0, right=1024), overlap=TBLR(top=0, bottom=0, left=0, right=0))
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=1024),
overlap=TBLR(top=0, bottom=0, left=0, right=0),
)
]
assert tiles == expected_tiles
@ -27,13 +35,31 @@ def test_calc_tiles_with_overlap_evenly_divisible():
expected_tiles = [
# Row 0
Tile(coords=TBLR(top=0, bottom=320, left=0, right=576), overlap=TBLR(top=0, bottom=64, left=0, right=64)),
Tile(coords=TBLR(top=0, bottom=320, left=512, right=1088), overlap=TBLR(top=0, bottom=64, left=64, right=64)),
Tile(coords=TBLR(top=0, bottom=320, left=1024, right=1600), overlap=TBLR(top=0, bottom=64, left=64, right=0)),
Tile(
coords=TBLR(top=0, bottom=320, left=0, right=576),
overlap=TBLR(top=0, bottom=64, left=0, right=64),
),
Tile(
coords=TBLR(top=0, bottom=320, left=512, right=1088),
overlap=TBLR(top=0, bottom=64, left=64, right=64),
),
Tile(
coords=TBLR(top=0, bottom=320, left=1024, right=1600),
overlap=TBLR(top=0, bottom=64, left=64, right=0),
),
# Row 1
Tile(coords=TBLR(top=256, bottom=576, left=0, right=576), overlap=TBLR(top=64, bottom=0, left=0, right=64)),
Tile(coords=TBLR(top=256, bottom=576, left=512, right=1088), overlap=TBLR(top=64, bottom=0, left=64, right=64)),
Tile(coords=TBLR(top=256, bottom=576, left=1024, right=1600), overlap=TBLR(top=64, bottom=0, left=64, right=0)),
Tile(
coords=TBLR(top=256, bottom=576, left=0, right=576),
overlap=TBLR(top=64, bottom=0, left=0, right=64),
),
Tile(
coords=TBLR(top=256, bottom=576, left=512, right=1088),
overlap=TBLR(top=64, bottom=0, left=64, right=64),
),
Tile(
coords=TBLR(top=256, bottom=576, left=1024, right=1600),
overlap=TBLR(top=64, bottom=0, left=64, right=0),
),
]
assert tiles == expected_tiles
@ -46,16 +72,30 @@ def test_calc_tiles_with_overlap_not_evenly_divisible():
expected_tiles = [
# Row 0
Tile(coords=TBLR(top=0, bottom=256, left=0, right=512), overlap=TBLR(top=0, bottom=112, left=0, right=64)),
Tile(coords=TBLR(top=0, bottom=256, left=448, right=960), overlap=TBLR(top=0, bottom=112, left=64, right=272)),
Tile(coords=TBLR(top=0, bottom=256, left=688, right=1200), overlap=TBLR(top=0, bottom=112, left=272, right=0)),
# Row 1
Tile(coords=TBLR(top=144, bottom=400, left=0, right=512), overlap=TBLR(top=112, bottom=0, left=0, right=64)),
Tile(
coords=TBLR(top=144, bottom=400, left=448, right=960), overlap=TBLR(top=112, bottom=0, left=64, right=272)
coords=TBLR(top=0, bottom=256, left=0, right=512),
overlap=TBLR(top=0, bottom=112, left=0, right=64),
),
Tile(
coords=TBLR(top=144, bottom=400, left=688, right=1200), overlap=TBLR(top=112, bottom=0, left=272, right=0)
coords=TBLR(top=0, bottom=256, left=448, right=960),
overlap=TBLR(top=0, bottom=112, left=64, right=272),
),
Tile(
coords=TBLR(top=0, bottom=256, left=688, right=1200),
overlap=TBLR(top=0, bottom=112, left=272, right=0),
),
# Row 1
Tile(
coords=TBLR(top=144, bottom=400, left=0, right=512),
overlap=TBLR(top=112, bottom=0, left=0, right=64),
),
Tile(
coords=TBLR(top=144, bottom=400, left=448, right=960),
overlap=TBLR(top=112, bottom=0, left=64, right=272),
),
Tile(
coords=TBLR(top=144, bottom=400, left=688, right=1200),
overlap=TBLR(top=112, bottom=0, left=272, right=0),
),
]
@ -75,7 +115,12 @@ def test_calc_tiles_with_overlap_not_evenly_divisible():
],
)
def test_calc_tiles_with_overlap_input_validation(
image_height: int, image_width: int, tile_height: int, tile_width: int, overlap: int, raises: bool
image_height: int,
image_width: int,
tile_height: int,
tile_width: int,
overlap: int,
raises: bool,
):
"""Test that calc_tiles_with_overlap() raises an exception if the inputs are invalid."""
if raises:
@ -85,6 +130,306 @@ def test_calc_tiles_with_overlap_input_validation(
calc_tiles_with_overlap(image_height, image_width, tile_height, tile_width, overlap)
####################################
# Test calc_tiles_min_overlap(...)
####################################
def test_calc_tiles_min_overlap_single_tile():
"""Test calc_tiles_min_overlap() behavior when a single tile covers the image."""
tiles = calc_tiles_min_overlap(
image_height=512,
image_width=1024,
tile_height=512,
tile_width=1024,
min_overlap=64,
)
expected_tiles = [
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=1024),
overlap=TBLR(top=0, bottom=0, left=0, right=0),
)
]
assert tiles == expected_tiles
def test_calc_tiles_min_overlap_evenly_divisible():
"""Test calc_tiles_min_overlap() behavior when the image is evenly covered by multiple tiles."""
# Parameters mimic roughly the same output as the original tile generations of the same test name
tiles = calc_tiles_min_overlap(
image_height=576,
image_width=1600,
tile_height=320,
tile_width=576,
min_overlap=64,
)
expected_tiles = [
# Row 0
Tile(
coords=TBLR(top=0, bottom=320, left=0, right=576),
overlap=TBLR(top=0, bottom=64, left=0, right=64),
),
Tile(
coords=TBLR(top=0, bottom=320, left=512, right=1088),
overlap=TBLR(top=0, bottom=64, left=64, right=64),
),
Tile(
coords=TBLR(top=0, bottom=320, left=1024, right=1600),
overlap=TBLR(top=0, bottom=64, left=64, right=0),
),
# Row 1
Tile(
coords=TBLR(top=256, bottom=576, left=0, right=576),
overlap=TBLR(top=64, bottom=0, left=0, right=64),
),
Tile(
coords=TBLR(top=256, bottom=576, left=512, right=1088),
overlap=TBLR(top=64, bottom=0, left=64, right=64),
),
Tile(
coords=TBLR(top=256, bottom=576, left=1024, right=1600),
overlap=TBLR(top=64, bottom=0, left=64, right=0),
),
]
assert tiles == expected_tiles
def test_calc_tiles_min_overlap_not_evenly_divisible():
"""Test calc_tiles_min_overlap() behavior when the image requires 'uneven' overlaps to achieve proper coverage."""
# Parameters mimic roughly the same output as the original tile generations of the same test name
tiles = calc_tiles_min_overlap(
image_height=400,
image_width=1200,
tile_height=256,
tile_width=512,
min_overlap=64,
)
expected_tiles = [
# Row 0
Tile(
coords=TBLR(top=0, bottom=256, left=0, right=512),
overlap=TBLR(top=0, bottom=112, left=0, right=168),
),
Tile(
coords=TBLR(top=0, bottom=256, left=344, right=856),
overlap=TBLR(top=0, bottom=112, left=168, right=168),
),
Tile(
coords=TBLR(top=0, bottom=256, left=688, right=1200),
overlap=TBLR(top=0, bottom=112, left=168, right=0),
),
# Row 1
Tile(
coords=TBLR(top=144, bottom=400, left=0, right=512),
overlap=TBLR(top=112, bottom=0, left=0, right=168),
),
Tile(
coords=TBLR(top=144, bottom=400, left=344, right=856),
overlap=TBLR(top=112, bottom=0, left=168, right=168),
),
Tile(
coords=TBLR(top=144, bottom=400, left=688, right=1200),
overlap=TBLR(top=112, bottom=0, left=168, right=0),
),
]
assert tiles == expected_tiles
@pytest.mark.parametrize(
[
"image_height",
"image_width",
"tile_height",
"tile_width",
"min_overlap",
"raises",
],
[
(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, 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.
],
)
def test_calc_tiles_min_overlap_input_validation(
image_height: int,
image_width: int,
tile_height: int,
tile_width: int,
min_overlap: int,
raises: bool,
):
"""Test that calc_tiles_min_overlap() raises an exception if the inputs are invalid."""
if raises:
with pytest.raises(AssertionError):
calc_tiles_min_overlap(image_height, image_width, tile_height, tile_width, min_overlap)
else:
calc_tiles_min_overlap(image_height, image_width, tile_height, tile_width, min_overlap)
####################################
# Test calc_tiles_even_split(...)
####################################
def test_calc_tiles_even_split_single_tile():
"""Test calc_tiles_even_split() behavior when a single tile covers the image."""
tiles = calc_tiles_even_split(
image_height=512, image_width=1024, num_tiles_x=1, num_tiles_y=1, overlap_fraction=0.25
)
expected_tiles = [
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=1024),
overlap=TBLR(top=0, bottom=0, left=0, right=0),
)
]
assert tiles == expected_tiles
def test_calc_tiles_even_split_evenly_divisible():
"""Test calc_tiles_even_split() behavior when the image is evenly covered by multiple tiles."""
# Parameters mimic roughly the same output as the original tile generations of the same test name
tiles = calc_tiles_even_split(
image_height=576, image_width=1600, num_tiles_x=3, num_tiles_y=2, overlap_fraction=0.25
)
expected_tiles = [
# Row 0
Tile(
coords=TBLR(top=0, bottom=320, left=0, right=624),
overlap=TBLR(top=0, bottom=72, left=0, right=136),
),
Tile(
coords=TBLR(top=0, bottom=320, left=488, right=1112),
overlap=TBLR(top=0, bottom=72, left=136, right=136),
),
Tile(
coords=TBLR(top=0, bottom=320, left=976, right=1600),
overlap=TBLR(top=0, bottom=72, left=136, right=0),
),
# Row 1
Tile(
coords=TBLR(top=248, bottom=576, left=0, right=624),
overlap=TBLR(top=72, bottom=0, left=0, right=136),
),
Tile(
coords=TBLR(top=248, bottom=576, left=488, right=1112),
overlap=TBLR(top=72, bottom=0, left=136, right=136),
),
Tile(
coords=TBLR(top=248, bottom=576, left=976, right=1600),
overlap=TBLR(top=72, bottom=0, left=136, right=0),
),
]
assert tiles == expected_tiles
def test_calc_tiles_even_split_not_evenly_divisible():
"""Test calc_tiles_even_split() behavior when the image requires 'uneven' overlaps to achieve proper coverage."""
# Parameters mimic roughly the same output as the original tile generations of the same test name
tiles = calc_tiles_even_split(
image_height=400, image_width=1200, num_tiles_x=3, num_tiles_y=2, overlap_fraction=0.25
)
expected_tiles = [
# Row 0
Tile(
coords=TBLR(top=0, bottom=224, left=0, right=464),
overlap=TBLR(top=0, bottom=56, left=0, right=104),
),
Tile(
coords=TBLR(top=0, bottom=224, left=360, right=824),
overlap=TBLR(top=0, bottom=56, left=104, right=104),
),
Tile(
coords=TBLR(top=0, bottom=224, left=720, right=1200),
overlap=TBLR(top=0, bottom=56, left=104, right=0),
),
# Row 1
Tile(
coords=TBLR(top=168, bottom=400, left=0, right=464),
overlap=TBLR(top=56, bottom=0, left=0, right=104),
),
Tile(
coords=TBLR(top=168, bottom=400, left=360, right=824),
overlap=TBLR(top=56, bottom=0, left=104, right=104),
),
Tile(
coords=TBLR(top=168, bottom=400, left=720, right=1200),
overlap=TBLR(top=56, bottom=0, left=104, right=0),
),
]
assert tiles == expected_tiles
def test_calc_tiles_even_split_difficult_size():
"""Test calc_tiles_even_split() behavior when the image is a difficult size to spilt evenly and keep div8."""
# Parameters are a difficult size for other tile gen routines to calculate
tiles = calc_tiles_even_split(
image_height=1000, image_width=1000, num_tiles_x=2, num_tiles_y=2, overlap_fraction=0.25
)
expected_tiles = [
# Row 0
Tile(
coords=TBLR(top=0, bottom=560, left=0, right=560),
overlap=TBLR(top=0, bottom=128, left=0, right=128),
),
Tile(
coords=TBLR(top=0, bottom=560, left=432, right=1000),
overlap=TBLR(top=0, bottom=128, left=128, right=0),
),
# Row 1
Tile(
coords=TBLR(top=432, bottom=1000, left=0, right=560),
overlap=TBLR(top=128, bottom=0, left=0, right=128),
),
Tile(
coords=TBLR(top=432, bottom=1000, left=432, right=1000),
overlap=TBLR(top=128, bottom=0, left=128, right=0),
),
]
assert tiles == expected_tiles
@pytest.mark.parametrize(
["image_height", "image_width", "num_tiles_x", "num_tiles_y", "overlap_fraction", "raises"],
[
(128, 128, 1, 1, 0.25, False), # OK
(128, 128, 1, 1, 0, False), # OK
(128, 128, 2, 1, 0, False), # OK
(127, 127, 1, 1, 0, True), # image size must be dividable by 8
],
)
def test_calc_tiles_even_split_input_validation(
image_height: int,
image_width: int,
num_tiles_x: int,
num_tiles_y: int,
overlap_fraction: float,
raises: bool,
):
"""Test that calc_tiles_even_split() raises an exception if the inputs are invalid."""
if raises:
with pytest.raises(ValueError):
calc_tiles_even_split(image_height, image_width, num_tiles_x, num_tiles_y, overlap_fraction)
else:
calc_tiles_even_split(image_height, image_width, num_tiles_x, num_tiles_y, overlap_fraction)
#############################################
# Test merge_tiles_with_linear_blending(...)
#############################################
@ -95,8 +440,14 @@ def test_merge_tiles_with_linear_blending_horizontal(blend_amount: int):
"""Test merge_tiles_with_linear_blending(...) behavior when merging horizontally."""
# Initialize 2 tiles side-by-side.
tiles = [
Tile(coords=TBLR(top=0, bottom=512, left=0, right=512), overlap=TBLR(top=0, bottom=0, left=0, right=64)),
Tile(coords=TBLR(top=0, bottom=512, left=448, right=960), overlap=TBLR(top=0, bottom=0, left=64, right=0)),
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=512),
overlap=TBLR(top=0, bottom=0, left=0, right=64),
),
Tile(
coords=TBLR(top=0, bottom=512, left=448, right=960),
overlap=TBLR(top=0, bottom=0, left=64, right=0),
),
]
dst_image = np.zeros((512, 960, 3), dtype=np.uint8)
@ -116,7 +467,10 @@ def test_merge_tiles_with_linear_blending_horizontal(blend_amount: int):
expected_output[:, 480 + (blend_amount // 2) :, :] = 128
merge_tiles_with_linear_blending(
dst_image=dst_image, tiles=tiles, tile_images=tile_images, blend_amount=blend_amount
dst_image=dst_image,
tiles=tiles,
tile_images=tile_images,
blend_amount=blend_amount,
)
np.testing.assert_array_equal(dst_image, expected_output, strict=True)
@ -127,8 +481,14 @@ def test_merge_tiles_with_linear_blending_vertical(blend_amount: int):
"""Test merge_tiles_with_linear_blending(...) behavior when merging vertically."""
# Initialize 2 tiles stacked vertically.
tiles = [
Tile(coords=TBLR(top=0, bottom=512, left=0, right=512), overlap=TBLR(top=0, bottom=64, left=0, right=0)),
Tile(coords=TBLR(top=448, bottom=960, left=0, right=512), overlap=TBLR(top=64, bottom=0, left=0, right=0)),
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=512),
overlap=TBLR(top=0, bottom=64, left=0, right=0),
),
Tile(
coords=TBLR(top=448, bottom=960, left=0, right=512),
overlap=TBLR(top=64, bottom=0, left=0, right=0),
),
]
dst_image = np.zeros((960, 512, 3), dtype=np.uint8)
@ -148,7 +508,10 @@ def test_merge_tiles_with_linear_blending_vertical(blend_amount: int):
expected_output[480 + (blend_amount // 2) :, :, :] = 128
merge_tiles_with_linear_blending(
dst_image=dst_image, tiles=tiles, tile_images=tile_images, blend_amount=blend_amount
dst_image=dst_image,
tiles=tiles,
tile_images=tile_images,
blend_amount=blend_amount,
)
np.testing.assert_array_equal(dst_image, expected_output, strict=True)
@ -160,8 +523,14 @@ def test_merge_tiles_with_linear_blending_blend_amount_exceeds_vertical_overlap(
"""
# Initialize 2 tiles stacked vertically.
tiles = [
Tile(coords=TBLR(top=0, bottom=512, left=0, right=512), overlap=TBLR(top=0, bottom=64, left=0, right=0)),
Tile(coords=TBLR(top=448, bottom=960, left=0, right=512), overlap=TBLR(top=64, bottom=0, left=0, right=0)),
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=512),
overlap=TBLR(top=0, bottom=64, left=0, right=0),
),
Tile(
coords=TBLR(top=448, bottom=960, left=0, right=512),
overlap=TBLR(top=64, bottom=0, left=0, right=0),
),
]
dst_image = np.zeros((960, 512, 3), dtype=np.uint8)
@ -180,8 +549,14 @@ def test_merge_tiles_with_linear_blending_blend_amount_exceeds_horizontal_overla
"""
# Initialize 2 tiles side-by-side.
tiles = [
Tile(coords=TBLR(top=0, bottom=512, left=0, right=512), overlap=TBLR(top=0, bottom=0, left=0, right=64)),
Tile(coords=TBLR(top=0, bottom=512, left=448, right=960), overlap=TBLR(top=0, bottom=0, left=64, right=0)),
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=512),
overlap=TBLR(top=0, bottom=0, left=0, right=64),
),
Tile(
coords=TBLR(top=0, bottom=512, left=448, right=960),
overlap=TBLR(top=0, bottom=0, left=64, right=0),
),
]
dst_image = np.zeros((512, 960, 3), dtype=np.uint8)
@ -198,7 +573,12 @@ def test_merge_tiles_with_linear_blending_tiles_overflow_dst_image():
"""Test that merge_tiles_with_linear_blending(...) raises an exception if any of the tiles overflows the
dst_image.
"""
tiles = [Tile(coords=TBLR(top=0, bottom=512, left=0, right=512), overlap=TBLR(top=0, bottom=0, left=0, right=0))]
tiles = [
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=512),
overlap=TBLR(top=0, bottom=0, left=0, right=0),
)
]
dst_image = np.zeros((256, 512, 3), dtype=np.uint8)
@ -213,7 +593,12 @@ def test_merge_tiles_with_linear_blending_mismatched_list_lengths():
"""Test that merge_tiles_with_linear_blending(...) raises an exception if the lengths of 'tiles' and 'tile_images'
do not match.
"""
tiles = [Tile(coords=TBLR(top=0, bottom=512, left=0, right=512), overlap=TBLR(top=0, bottom=0, left=0, right=0))]
tiles = [
Tile(
coords=TBLR(top=0, bottom=512, left=0, right=512),
overlap=TBLR(top=0, bottom=0, left=0, right=0),
)
]
dst_image = np.zeros((256, 512, 3), dtype=np.uint8)