Files
bmad4ever-comfyui_quilting/make_seamless2.py
T
Bruno Madeira 0287e2d4a2 Refactoring + fixes + added node (ImageMakeSeamlessSB)
* refactored patch_search.py
* fixed H Seam position on v2
* fixed seamless node bar total steps not accounting for batch size
* added uicd logic to make_seamless2.py
* removed mains from make_seamless.py & make_seamless2.py
* other minor cleanups

- triggered error in find_patch_vx due to random gen having a value equal or lesser than zero as argument.
Was not able to reproduce the error again so far.
Conjecture: it may be the case that the min error was negative due to lack of precision when using patch v3, resulting in an empty list of candidates if tolerance is not zero; if this is the case the error should happen only when using version 3.
2024-07-31 16:10:14 +01:00

90 lines
4.5 KiB
Python

# An alternative approach to making the texture seamless
from .patch_search import compute_errors, get_match_template_method
from .jena2020.generate import getMinCutPatchHorizontal
from .make_seamless import patch_horizontal_seam
from .types import UiCoordData
import numpy as np
import cv2 as cv
def seamless_horizontal(image, block_size, overlap, version, lookup_texture, rng,
uicd: UiCoordData | None = None):
image = np.roll(image, +block_size // 2, axis=1)
# left & right overlap errors
template_method = get_match_template_method(version)
lo_errs = cv.matchTemplate(image=lookup_texture[:, :-block_size],
templ=image[:, :overlap], method=template_method)
if uicd is not None and uicd.add_to_job_data_slot_and_check_interrupt(1):
return None
ro_errs = cv.matchTemplate(image=np.roll(lookup_texture, -block_size + overlap, axis=1)[:, :-block_size],
templ=image[:, block_size - overlap:block_size], method=template_method)
if uicd is not None and uicd.add_to_job_data_slot_and_check_interrupt(1):
return None
err_mat = compute_errors([lo_errs, ro_errs], version)
min_val = np.min(err_mat) # ignore tolerance in this solution
y, x = np.nonzero(err_mat <= min_val) # ignore tolerance here, choose only from the best values
# still select randomly, it may be the case that there are more than one equally good matches
# likely super rare, but doesn't costly to keep the option if eventually applicable
c = rng.integers(len(y))
y, x = y[c], x[c]
# "fake" block will only contain the overlap, in order to re-use existing function.
fake_left_block = np.empty((image.shape[0], image.shape[0], image.shape[2]), dtype=image.dtype)
fake_right_block = np.empty((image.shape[0], image.shape[0], image.shape[2]), dtype=image.dtype)
fake_left_block[:, -overlap:] = image[:, :overlap]
fake_right_block[:, :overlap] = image[:, block_size - overlap:block_size]
fake_block_sized_patch = np.empty((image.shape[0], image.shape[0], image.shape[2]), dtype=image.dtype)
fake_block_sized_patch[:, :overlap] = lookup_texture[y:y + image.shape[0], x:x + overlap]
fake_block_sized_patch[:, -overlap:] = lookup_texture[y:y + image.shape[0], x + block_size - overlap:x + block_size]
left_side_patch = getMinCutPatchHorizontal(fake_left_block, fake_block_sized_patch, image.shape[0], overlap)
right_side_patch = np.fliplr(
getMinCutPatchHorizontal(
np.fliplr(fake_right_block),
np.fliplr(fake_block_sized_patch),
image.shape[0], overlap
)
)
if uicd is not None and uicd.add_to_job_data_slot_and_check_interrupt(1):
return None
# paste vertical stripe patch
image[:, :block_size] = lookup_texture[y:y + image.shape[0], x:x + block_size]
image[:, :overlap] = left_side_patch[:, :overlap]
image[:, block_size - overlap:block_size] = right_side_patch[:, -overlap:]
return image
def seamless_vertical(image, block_size, overlap, version, lookup_texture, rng,
uicd: UiCoordData | None = None):
rotated_solution = seamless_horizontal(np.rot90(image), block_size, overlap,
version, np.rot90(lookup_texture), rng, uicd)
return np.rot90(rotated_solution, -1).copy()
# seamless both needs to patch small seam on last stripe, similar to previous implementation
# just need to check the offsets and adjust in either function,
# so that the seam is positioned the same way in both solutions; then just reuse the squares patches code
def seamless_both(image, block_size, overlap, version, lookup_texture, rng,
uicd: UiCoordData | None = None):
assert image.shape[0] >= block_size
assert image.shape[1] >= block_size + overlap * 2
texture = seamless_vertical(image, block_size, overlap, version, lookup_texture, rng, uicd)
if texture is None:
return None
texture = np.roll(texture, -block_size // 2, axis=0) # center future seam at stripes interception
texture = seamless_horizontal(texture, block_size, overlap, version, lookup_texture, rng, uicd)
if texture is None:
return None
# center seam & patch it
texture = np.roll(texture, texture.shape[0] // 2, axis=0)
texture = np.roll(texture, texture.shape[1] // 2 - block_size // 2, axis=1)
texture = patch_horizontal_seam(texture, lookup_texture, block_size, overlap, 0, rng)
return texture