* 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.
90 lines
4.5 KiB
Python
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
|