yey
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +0,0 @@
|
|||||||
import importlib
|
|
||||||
importlib.invalidate_caches()
|
|
||||||
import transition
|
|
||||||
print('has transform_images:', hasattr(transition, 'transform_images'))
|
|
||||||
print('transform_images object:', getattr(transition, 'transform_images', None))
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
def fix_image_resolution(image_path, size=(0,0)):
|
def resize_image_to_resolution(image_path, size=(0, 0)):
|
||||||
if image_path == None or size == (0,0):
|
if image_path is None or size == (0, 0):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
with Image.open(image_path) as image:
|
with Image.open(image_path) as image:
|
||||||
|
|||||||
+50
-50
@@ -12,9 +12,9 @@ class ImageInterface:
|
|||||||
def __init__(self, base_dir=None):
|
def __init__(self, base_dir=None):
|
||||||
self.base_dir = Path(base_dir or Path(__file__).resolve().parent.parent)
|
self.base_dir = Path(base_dir or Path(__file__).resolve().parent.parent)
|
||||||
self.image_sets = {
|
self.image_sets = {
|
||||||
"source": self._load_images(str(self.base_dir / "images" / "source" / "*.png")),
|
"source": self.load_images(str(self.base_dir / "images" / "source" / "*.png")),
|
||||||
"goal": self._load_images(str(self.base_dir / "images" / "goal" / "*.png")),
|
"goal": self.load_images(str(self.base_dir / "images" / "goal" / "*.png")),
|
||||||
"shenanigans": self._load_images(str(self.base_dir / "images" / "shenanigans" / "*.png")),
|
"shenanigans": self.load_images(str(self.base_dir / "images" / "shenanigans" / "*.png")),
|
||||||
}
|
}
|
||||||
self.current_category = None
|
self.current_category = None
|
||||||
self.current_index = 0
|
self.current_index = 0
|
||||||
@@ -31,19 +31,19 @@ class ImageInterface:
|
|||||||
"button": "#2b2b2b",
|
"button": "#2b2b2b",
|
||||||
"button_hover": "#3a3a3a",
|
"button_hover": "#3a3a3a",
|
||||||
}
|
}
|
||||||
def _load_images(self, pattern):
|
def load_images(self, pattern):
|
||||||
return sorted(glob(pattern))
|
return sorted(glob(pattern))
|
||||||
def _ensure_figure(self):
|
def ensure_figure(self):
|
||||||
if self.figure is None:
|
if self.figure is None:
|
||||||
self.figure = plt.figure(figsize=(10, 6))
|
self.figure = plt.figure(figsize=(10, 6))
|
||||||
self.key_connection_id = self.figure.canvas.mpl_connect("key_press_event", self._on_key_press)
|
self.key_connection_id = self.figure.canvas.mpl_connect("key_press_event", self.on_key_press)
|
||||||
def _clear_layout(self):
|
def clear_layout(self):
|
||||||
self._ensure_figure()
|
self.ensure_figure()
|
||||||
self.figure.clf()
|
self.figure.clf()
|
||||||
self.image_axis = None
|
self.image_axis = None
|
||||||
self.list_axis = None
|
self.list_axis = None
|
||||||
self.buttons = []
|
self.buttons = []
|
||||||
def _make_button(self, rect, label, callback):
|
def make_button(self, rect, label, callback):
|
||||||
button_axis = self.figure.add_axes(rect)
|
button_axis = self.figure.add_axes(rect)
|
||||||
button_axis.set_facecolor(self.colors["button"])
|
button_axis.set_facecolor(self.colors["button"])
|
||||||
button_axis.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)
|
button_axis.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)
|
||||||
@@ -52,12 +52,12 @@ class ImageInterface:
|
|||||||
button.on_clicked(callback)
|
button.on_clicked(callback)
|
||||||
self.buttons.append(button)
|
self.buttons.append(button)
|
||||||
return button
|
return button
|
||||||
def _open_image(self, image_paths, index):
|
def open_image(self, image_paths, index):
|
||||||
if not image_paths:
|
if not image_paths:
|
||||||
return None
|
return None
|
||||||
safe_index = max(0, min(index, len(image_paths) - 1))
|
safe_index = max(0, min(index, len(image_paths) - 1))
|
||||||
return Image.open(image_paths[safe_index])
|
return Image.open(image_paths[safe_index])
|
||||||
def _draw_image_panel(self, axis, image, title):
|
def draw_image_panel(self, axis, image, title):
|
||||||
axis.clear()
|
axis.clear()
|
||||||
axis.set_title(title)
|
axis.set_title(title)
|
||||||
axis.set_facecolor(self.colors["panel"])
|
axis.set_facecolor(self.colors["panel"])
|
||||||
@@ -67,7 +67,7 @@ class ImageInterface:
|
|||||||
axis.imshow(image)
|
axis.imshow(image)
|
||||||
else:
|
else:
|
||||||
axis.text(0.5, 0.5, "No images found", ha="center", va="center", color=self.colors["text"])
|
axis.text(0.5, 0.5, "No images found", ha="center", va="center", color=self.colors["text"])
|
||||||
def _draw_list_panel(self, axis, image_paths, current_index):
|
def draw_list_panel(self, axis, image_paths, current_index):
|
||||||
axis.clear()
|
axis.clear()
|
||||||
axis.set_facecolor(self.colors["panel"])
|
axis.set_facecolor(self.colors["panel"])
|
||||||
axis.axis("off")
|
axis.axis("off")
|
||||||
@@ -83,19 +83,19 @@ class ImageInterface:
|
|||||||
lines.append(f"{prefix}{name}")
|
lines.append(f"{prefix}{name}")
|
||||||
axis.text(0.02, 0.98, "\n".join(lines), ha="left", va="top", family="monospace", color=self.colors["text"])
|
axis.text(0.02, 0.98, "\n".join(lines), ha="left", va="top", family="monospace", color=self.colors["text"])
|
||||||
axis.title.set_color(self.colors["text"])
|
axis.title.set_color(self.colors["text"])
|
||||||
def _show_menu(self):
|
def show_menu(self):
|
||||||
self.current_category = None
|
self.current_category = None
|
||||||
self._clear_layout()
|
self.clear_layout()
|
||||||
self.figure.patch.set_facecolor(self.colors["figure"])
|
self.figure.patch.set_facecolor(self.colors["figure"])
|
||||||
self._menu_row(0.66, "Source Images", "source", lambda event: self._open_category("source"))
|
self.menu_row(0.66, "Source Images", "source", lambda event: self.open_category("source"))
|
||||||
self._menu_row(0.50, "Goal Images", "goal", lambda event: self._open_category("goal"))
|
self.menu_row(0.50, "Goal Images", "goal", lambda event: self.open_category("goal"))
|
||||||
self._make_button([0.08, 0.34, 0.32, 0.12], "Shenanigans Image", lambda event: self._open_shenanigans())
|
self.make_button([0.08, 0.34, 0.32, 0.12], "Shenanigans Image", lambda event: self.open_shenanigans())
|
||||||
self.figure.canvas.draw_idle()
|
self.figure.canvas.draw_idle()
|
||||||
def _open_category(self, category):
|
def open_category(self, category):
|
||||||
self.current_category = category
|
self.current_category = category
|
||||||
self.current_index = 0
|
self.current_index = 0
|
||||||
self._show_browser()
|
self.show_browser()
|
||||||
def _open_shenanigans(self):
|
def open_shenanigans(self):
|
||||||
src_paths = self.image_sets.get("source", [])
|
src_paths = self.image_sets.get("source", [])
|
||||||
goal_paths = self.image_sets.get("goal", [])
|
goal_paths = self.image_sets.get("goal", [])
|
||||||
if not src_paths or not goal_paths:
|
if not src_paths or not goal_paths:
|
||||||
@@ -109,77 +109,77 @@ class ImageInterface:
|
|||||||
importlib.reload(transition)
|
importlib.reload(transition)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if not hasattr(transition, "transform_images"):
|
if not hasattr(transition, "show_image_transition"):
|
||||||
return
|
return
|
||||||
transition.transform_images(src_path, goal_path)
|
transition.show_image_transition(src_path, goal_path)
|
||||||
def _selected_image_name(self, category):
|
def selected_image_name(self, category):
|
||||||
selected_index = self.selected_indices.get(category)
|
selected_index = self.selected_indices.get(category)
|
||||||
image_paths = self.image_sets.get(category, [])
|
image_paths = self.image_sets.get(category, [])
|
||||||
if selected_index is None or not image_paths:
|
if selected_index is None or not image_paths:
|
||||||
return None
|
return None
|
||||||
safe_index = max(0, min(selected_index, len(image_paths) - 1))
|
safe_index = max(0, min(selected_index, len(image_paths) - 1))
|
||||||
return Path(image_paths[safe_index]).name
|
return Path(image_paths[safe_index]).name
|
||||||
def _menu_label(self, base_label, category=None):
|
def menu_label(self, base_label, category=None):
|
||||||
if category is None:
|
if category is None:
|
||||||
return base_label
|
return base_label
|
||||||
selected_name = self._selected_image_name(category)
|
selected_name = self.selected_image_name(category)
|
||||||
if selected_name is None:
|
if selected_name is None:
|
||||||
return base_label
|
return base_label
|
||||||
return f"{base_label} - {selected_name}"
|
return f"{base_label} - {selected_name}"
|
||||||
def _menu_row(self, y, label, category, callback):
|
def menu_row(self, y, label, category, callback):
|
||||||
self._make_button([0.08, y, 0.32, 0.12], label, callback)
|
self.make_button([0.08, y, 0.32, 0.12], label, callback)
|
||||||
selected_name = self._selected_image_name(category)
|
selected_name = self.selected_image_name(category)
|
||||||
if selected_name is not None:
|
if selected_name is not None:
|
||||||
self.figure.text(0.44, y + 0.055, f"- {selected_name}", color=self.colors["text"], va="center", ha="left", fontsize=11)
|
self.figure.text(0.44, y + 0.055, f"- {selected_name}", color=self.colors["text"], va="center", ha="left", fontsize=11)
|
||||||
def _select_current_image(self):
|
def select_current_image(self):
|
||||||
if self.current_category in ("source", "goal"):
|
if self.current_category in ("source", "goal"):
|
||||||
self.selected_indices[self.current_category] = self.current_index
|
self.selected_indices[self.current_category] = self.current_index
|
||||||
self._show_menu()
|
self.show_menu()
|
||||||
def _close_interface(self):
|
def close_interface(self):
|
||||||
if self.figure is not None:
|
if self.figure is not None:
|
||||||
plt.close(self.figure)
|
plt.close(self.figure)
|
||||||
def _show_browser(self):
|
def show_browser(self):
|
||||||
self._clear_layout()
|
self.clear_layout()
|
||||||
self.figure.patch.set_facecolor(self.colors["figure"])
|
self.figure.patch.set_facecolor(self.colors["figure"])
|
||||||
self.figure.suptitle(self.current_category.replace("_", " ").title(), fontsize=16, color=self.colors["text"])
|
self.figure.suptitle(self.current_category.replace("_", " ").title(), fontsize=16, color=self.colors["text"])
|
||||||
self.image_axis, self.list_axis = self.figure.subplots(1, 2, gridspec_kw={"width_ratios": [3, 1]})
|
self.image_axis, self.list_axis = self.figure.subplots(1, 2, gridspec_kw={"width_ratios": [3, 1]})
|
||||||
self._refresh_browser_view()
|
self.refresh_browser_view()
|
||||||
self._make_button([0.02, 0.02, 0.12, 0.06], "Back", lambda event: self._show_menu())
|
self.make_button([0.02, 0.02, 0.12, 0.06], "Back", lambda event: self.show_menu())
|
||||||
if self.current_category in ("source", "goal"):
|
if self.current_category in ("source", "goal"):
|
||||||
self._make_button([0.76, 0.10, 0.18, 0.06], "Select", lambda event: self._select_current_image())
|
self.make_button([0.76, 0.10, 0.18, 0.06], "Select", lambda event: self.select_current_image())
|
||||||
self._make_button([0.76, 0.02, 0.08, 0.06], "<", lambda event: self.previous_image())
|
self.make_button([0.76, 0.02, 0.08, 0.06], "<", lambda event: self.previous_image())
|
||||||
self._make_button([0.86, 0.02, 0.08, 0.06], ">", lambda event: self.next_image())
|
self.make_button([0.86, 0.02, 0.08, 0.06], ">", lambda event: self.next_image())
|
||||||
self.figure.subplots_adjust(left=0.05, right=0.95, bottom=0.12, top=0.90, wspace=0.22)
|
self.figure.subplots_adjust(left=0.05, right=0.95, bottom=0.12, top=0.90, wspace=0.22)
|
||||||
self.figure.canvas.draw_idle()
|
self.figure.canvas.draw_idle()
|
||||||
def _refresh_browser_view(self):
|
def refresh_browser_view(self):
|
||||||
images = self.image_sets.get(self.current_category, [])
|
images = self.image_sets.get(self.current_category, [])
|
||||||
current_image = self._open_image(images, self.current_index)
|
current_image = self.open_image(images, self.current_index)
|
||||||
category_title = self.current_category.replace("_", " ").title() if self.current_category else "Images"
|
category_title = self.current_category.replace("_", " ").title() if self.current_category else "Images"
|
||||||
self._draw_image_panel(self.image_axis, current_image, f"{category_title} {self.current_index + 1}/{max(len(images), 1)}")
|
self.draw_image_panel(self.image_axis, current_image, f"{category_title} {self.current_index + 1}/{max(len(images), 1)}")
|
||||||
self._draw_list_panel(self.list_axis, images, self.current_index)
|
self.draw_list_panel(self.list_axis, images, self.current_index)
|
||||||
self.figure.canvas.draw_idle()
|
self.figure.canvas.draw_idle()
|
||||||
def _step_image(self, delta):
|
def step_image(self, delta):
|
||||||
images = self.image_sets.get(self.current_category, [])
|
images = self.image_sets.get(self.current_category, [])
|
||||||
if images:
|
if images:
|
||||||
self.current_index = (self.current_index + delta) % len(images)
|
self.current_index = (self.current_index + delta) % len(images)
|
||||||
self._refresh_browser_view()
|
self.refresh_browser_view()
|
||||||
def next_image(self):
|
def next_image(self):
|
||||||
self._step_image(1)
|
self.step_image(1)
|
||||||
def previous_image(self):
|
def previous_image(self):
|
||||||
self._step_image(-1)
|
self.step_image(-1)
|
||||||
def _on_key_press(self, event):
|
def on_key_press(self, event):
|
||||||
if self.current_category is None:
|
if self.current_category is None:
|
||||||
if event.key in ("escape", "q"):
|
if event.key in ("escape", "q"):
|
||||||
self._close_interface()
|
self.close_interface()
|
||||||
return
|
return
|
||||||
if event.key in ("right", "d"):
|
if event.key in ("right", "d"):
|
||||||
self.next_image()
|
self.next_image()
|
||||||
elif event.key in ("left", "a"):
|
elif event.key in ("left", "a"):
|
||||||
self.previous_image()
|
self.previous_image()
|
||||||
elif event.key == "escape":
|
elif event.key == "escape":
|
||||||
self._show_menu()
|
self.show_menu()
|
||||||
def render(self):
|
def render(self):
|
||||||
self._show_menu()
|
self.show_menu()
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def launch_interface():
|
def launch_interface():
|
||||||
|
|||||||
+55
-62
@@ -4,75 +4,68 @@ from PIL import Image, ImageDraw
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.tri import Triangulation
|
from matplotlib.tri import Triangulation
|
||||||
|
|
||||||
from image_preparing import fix_image_resolution
|
from image_preparing import resize_image_to_resolution
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
transformation_resolution = (1600, 1200)
|
transformation_resolution = (1600, 1200)
|
||||||
|
|
||||||
def auto_correspondences_grid(pil, nx=8, ny=6):
|
def create_grid_correspondences(image, nx=8, ny=6):
|
||||||
w, h = pil.size
|
w, h = image.size
|
||||||
xs = np.linspace(0, w - 1, nx)
|
xs = np.linspace(0, w - 1, nx)
|
||||||
ys = np.linspace(0, h - 1, ny)
|
ys = np.linspace(0, h - 1, ny)
|
||||||
pts = []
|
pts = []
|
||||||
for y in ys:
|
for y in ys:
|
||||||
for x in xs:
|
for x in xs:
|
||||||
pts.append([float(x), float(y)])
|
pts.append([float(x), float(y)])
|
||||||
corners = [[0.0, 0.0], [w - 1.0, 0.0], [w - 1.0, h - 1.0], [0.0, h - 1.0]]
|
return np.array(pts, dtype=float).copy()
|
||||||
for c in corners:
|
def calculate_delaunay_triangles(points):
|
||||||
if c not in pts:
|
tri = Triangulation(points[:, 0], points[:, 1])
|
||||||
pts.insert(0, c)
|
|
||||||
return np.array(pts, dtype=float), np.array(pts, dtype=float).copy()
|
|
||||||
def compute_delaunay_on_points(pts):
|
|
||||||
pts = np.asarray(pts)
|
|
||||||
tri = Triangulation(pts[:, 0], pts[:, 1])
|
|
||||||
return tri.triangles
|
return tri.triangles
|
||||||
def _affine_coeffs(from_tri, to_tri):
|
def calculate_affine_coefficients(from_triangle, to_triangle):
|
||||||
A = []
|
A = []
|
||||||
B = []
|
B = []
|
||||||
for (x_dst, y_dst), (x_src, y_src) in zip(from_tri, to_tri):
|
for (x_dst, y_dst), (x_src, y_src) in zip(from_triangle, to_triangle):
|
||||||
A.append([x_dst, y_dst, 1, 0, 0, 0])
|
A.append([x_dst, y_dst, 1, 0, 0, 0])
|
||||||
A.append([0, 0, 0, x_dst, y_dst, 1])
|
A.append([0, 0, 0, x_dst, y_dst, 1])
|
||||||
B.append(x_src)
|
B.append(x_src)
|
||||||
B.append(y_src)
|
B.append(y_src)
|
||||||
sol, *_ = np.linalg.lstsq(np.array(A), np.array(B), rcond=None)
|
sol, *_ = np.linalg.lstsq(np.array(A), np.array(B), rcond=None)
|
||||||
return tuple(sol.tolist())
|
return tuple(sol.tolist())
|
||||||
def _warp_triangle(src_pil, src_tri, dst_tri, out_size, offset):
|
def warp_triangle(source_image, source_triangle, destination_triangle, output_size, offset):
|
||||||
dx, dy = offset
|
dx, dy = offset
|
||||||
dst_local = np.array(dst_tri) - np.array([dx, dy])
|
destination_local = np.array(destination_triangle) - np.array([dx, dy])
|
||||||
coeffs = _affine_coeffs(dst_local, src_tri)
|
coefficients = calculate_affine_coefficients(destination_local, source_triangle)
|
||||||
return src_pil.transform(out_size, Image.AFFINE, coeffs, resample=Image.BILINEAR)
|
return source_image.transform(output_size, Image.AFFINE, coefficients, resample=Image.BILINEAR)
|
||||||
def reconstruct_goal_from_source(src_pil, pts1, pts2):
|
def reconstruct_goal_from_source(source_image, source_points, destination_points):
|
||||||
w, h = src_pil.size
|
w, h = source_image.size
|
||||||
pts1 = np.asarray(pts1)
|
triangles = calculate_delaunay_triangles(destination_points)
|
||||||
pts2 = np.asarray(pts2)
|
|
||||||
triangles = compute_delaunay_on_points(pts2)
|
|
||||||
out = Image.new("RGB", (w, h))
|
out = Image.new("RGB", (w, h))
|
||||||
for tri in triangles:
|
for tri in triangles:
|
||||||
tri = np.asarray(tri, dtype=int)
|
tri = np.asarray(tri, dtype=int)
|
||||||
src_tri = pts1[tri]
|
source_triangle = source_points[tri]
|
||||||
dst_tri = pts2[tri]
|
destination_triangle = destination_points[tri]
|
||||||
min_x = max(int(np.floor(dst_tri[:, 0].min())), 0)
|
min_x = max(int(np.floor(destination_triangle[:, 0].min())), 0)
|
||||||
max_x = min(int(np.ceil(dst_tri[:, 0].max())), w)
|
max_x = min(int(np.ceil(destination_triangle[:, 0].max())), w)
|
||||||
min_y = max(int(np.floor(dst_tri[:, 1].min())), 0)
|
min_y = max(int(np.floor(destination_triangle[:, 1].min())), 0)
|
||||||
max_y = min(int(np.ceil(dst_tri[:, 1].max())), h)
|
max_y = min(int(np.ceil(destination_triangle[:, 1].max())), h)
|
||||||
if max_x <= min_x or max_y <= min_y:
|
if max_x <= min_x or max_y <= min_y:
|
||||||
continue
|
continue
|
||||||
out_w = max_x - min_x
|
out_w = max_x - min_x
|
||||||
out_h = max_y - min_y
|
out_h = max_y - min_y
|
||||||
warped = _warp_triangle(src_pil, src_tri, dst_tri, (out_w, out_h), (min_x, min_y))
|
warped = warp_triangle(source_image, source_triangle, destination_triangle, (out_w, out_h), (min_x, min_y))
|
||||||
mask = Image.new("L", (out_w, out_h), 0)
|
mask = Image.new("L", (out_w, out_h), 0)
|
||||||
draw = ImageDraw.Draw(mask)
|
draw = ImageDraw.Draw(mask)
|
||||||
tri_local = [(x - min_x, y - min_y) for x, y in dst_tri.tolist()]
|
tri_local = [(x - min_x, y - min_y) for x, y in destination_triangle.tolist()]
|
||||||
draw.polygon(tri_local, fill=255)
|
draw.polygon(tri_local, fill=255)
|
||||||
out.paste(warped, (min_x, min_y), mask)
|
out.paste(warped, (min_x, min_y), mask)
|
||||||
return out
|
return out
|
||||||
def transform_images(src_path, goal_path):
|
def show_image_transition(source_path, goal_path):
|
||||||
src = fix_image_resolution(src_path, size=transformation_resolution)
|
src = resize_image_to_resolution(source_path, size=transformation_resolution)
|
||||||
goal = fix_image_resolution(goal_path, size=transformation_resolution)
|
goal = resize_image_to_resolution(goal_path, size=transformation_resolution)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
image = tile_reposition_transition(src, goal, grid=transformation_resolution)
|
image = reposition_image_tiles(src, goal, grid=transformation_resolution)
|
||||||
end = time.time()
|
end = time.time()
|
||||||
print(f"Transition took {end - start:.2f} seconds")
|
print(f"Transition took {end - start:.2f} seconds")
|
||||||
fig, ax = plt.subplots(figsize=(8, 6), facecolor="black")
|
fig, ax = plt.subplots(figsize=(8, 6), facecolor="black")
|
||||||
@@ -81,32 +74,32 @@ def transform_images(src_path, goal_path):
|
|||||||
ax.imshow(image, cmap=None)
|
ax.imshow(image, cmap=None)
|
||||||
ax.axis("off")
|
ax.axis("off")
|
||||||
|
|
||||||
def _close_on_key(event):
|
def close_transition_on_key(event):
|
||||||
if event.key in ("q", "escape"):
|
if event.key in ("q", "escape"):
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
fig.canvas.mpl_connect("key_press_event", _close_on_key)
|
fig.canvas.mpl_connect("key_press_event", close_transition_on_key)
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
def _grid_edges(size, divisions):
|
def calculate_grid_edges(size, divisions):
|
||||||
return np.rint(np.linspace(0, size, divisions + 1)).astype(np.int32)
|
return np.rint(np.linspace(0, size, divisions + 1)).astype(np.int32)
|
||||||
def _build_tile_boxes_and_centers(w, h, nx, ny):
|
def build_tile_boxes_and_centers(width, height, columns, rows):
|
||||||
x_edges = _grid_edges(w, nx)
|
x_edges = calculate_grid_edges(width, columns)
|
||||||
y_edges = _grid_edges(h, ny)
|
y_edges = calculate_grid_edges(height, rows)
|
||||||
|
|
||||||
x0 = np.repeat(x_edges[:-1], ny)
|
x0 = np.repeat(x_edges[:-1], rows)
|
||||||
x1 = np.repeat(x_edges[1:], ny)
|
x1 = np.repeat(x_edges[1:], rows)
|
||||||
y0 = np.tile(y_edges[:-1], nx)
|
y0 = np.tile(y_edges[:-1], columns)
|
||||||
y1 = np.tile(y_edges[1:], nx)
|
y1 = np.tile(y_edges[1:], columns)
|
||||||
|
|
||||||
boxes = np.stack([x0, y0, x1, y1], axis=1)
|
boxes = np.stack([x0, y0, x1, y1], axis=1)
|
||||||
centers = np.stack([(x0 + x1) * 0.5, (y0 + y1) * 0.5], axis=1).astype(np.float32)
|
centers = np.stack([(x0 + x1) * 0.5, (y0 + y1) * 0.5], axis=1).astype(np.float32)
|
||||||
areas = np.maximum((x1 - x0) * (y1 - y0), 1).astype(np.float32)
|
areas = np.maximum((x1 - x0) * (y1 - y0), 1).astype(np.float32)
|
||||||
return boxes, centers, areas
|
return boxes, centers, areas
|
||||||
def _integral_image_rgb(arr):
|
def calculate_integral_image_rgb(array):
|
||||||
integral = arr.cumsum(axis=0).cumsum(axis=1)
|
integral = array.cumsum(axis=0).cumsum(axis=1)
|
||||||
return np.pad(integral, ((1, 0), (1, 0), (0, 0)), mode="constant", constant_values=0)
|
return np.pad(integral, ((1, 0), (1, 0), (0, 0)), mode="constant", constant_values=0)
|
||||||
def _tile_means_from_integral(integral, boxes, areas):
|
def calculate_tile_means(integral, boxes, areas):
|
||||||
x0 = boxes[:, 0]
|
x0 = boxes[:, 0]
|
||||||
y0 = boxes[:, 1]
|
y0 = boxes[:, 1]
|
||||||
x1 = boxes[:, 2]
|
x1 = boxes[:, 2]
|
||||||
@@ -119,16 +112,16 @@ def _tile_means_from_integral(integral, boxes, areas):
|
|||||||
+ integral[y0, x0]
|
+ integral[y0, x0]
|
||||||
)
|
)
|
||||||
return sums / areas[:, None]
|
return sums / areas[:, None]
|
||||||
def _match_tiles_by_feature_rank(src_avgs, src_centers, goal_avgs, goal_centers, w, h):
|
def match_tiles_by_feature_rank(source_averages, source_centers, goal_averages, goal_centers, width, height):
|
||||||
color_scale = 255.0 * np.sqrt(3.0)
|
color_scale = 255.0 * np.sqrt(3.0)
|
||||||
spatial_scale = max(np.hypot(w, h), 1e-6)
|
spatial_scale = max(np.hypot(width, height), 1e-6)
|
||||||
|
|
||||||
src_feat = np.hstack([
|
src_feat = np.hstack([
|
||||||
src_avgs / color_scale,
|
source_averages / color_scale,
|
||||||
(src_centers / spatial_scale) * 0.35,
|
(source_centers / spatial_scale) * 0.35,
|
||||||
])
|
])
|
||||||
goal_feat = np.hstack([
|
goal_feat = np.hstack([
|
||||||
goal_avgs / color_scale,
|
goal_averages / color_scale,
|
||||||
(goal_centers / spatial_scale) * 0.35,
|
(goal_centers / spatial_scale) * 0.35,
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -137,24 +130,24 @@ def _match_tiles_by_feature_rank(src_avgs, src_centers, goal_avgs, goal_centers,
|
|||||||
src_order = np.argsort(src_feat @ projection)
|
src_order = np.argsort(src_feat @ projection)
|
||||||
goal_order = np.argsort(goal_feat @ projection)
|
goal_order = np.argsort(goal_feat @ projection)
|
||||||
return src_order, goal_order
|
return src_order, goal_order
|
||||||
def tile_reposition_transition(src_pil, goal_pil, grid=transformation_resolution):
|
def reposition_image_tiles(source_image, goal_image, grid=transformation_resolution):
|
||||||
src = src_pil.convert("RGB")
|
src = source_image.convert("RGB")
|
||||||
goal = goal_pil.convert("RGB")
|
goal = goal_image.convert("RGB")
|
||||||
w, h = src.size
|
w, h = src.size
|
||||||
nx, ny = grid
|
nx, ny = grid
|
||||||
|
|
||||||
src_boxes, src_centers, src_areas = _build_tile_boxes_and_centers(w, h, nx, ny)
|
src_boxes, src_centers, src_areas = build_tile_boxes_and_centers(w, h, nx, ny)
|
||||||
goal_boxes, goal_centers, goal_areas = _build_tile_boxes_and_centers(w, h, nx, ny)
|
goal_boxes, goal_centers, goal_areas = build_tile_boxes_and_centers(w, h, nx, ny)
|
||||||
|
|
||||||
src_arr = np.asarray(src, dtype=np.float32)
|
src_arr = np.asarray(src, dtype=np.float32)
|
||||||
goal_arr = np.asarray(goal, dtype=np.float32)
|
goal_arr = np.asarray(goal, dtype=np.float32)
|
||||||
src_integral = _integral_image_rgb(src_arr)
|
src_integral = calculate_integral_image_rgb(src_arr)
|
||||||
goal_integral = _integral_image_rgb(goal_arr)
|
goal_integral = calculate_integral_image_rgb(goal_arr)
|
||||||
|
|
||||||
src_avgs = _tile_means_from_integral(src_integral, src_boxes, src_areas)
|
src_avgs = calculate_tile_means(src_integral, src_boxes, src_areas)
|
||||||
goal_avgs = _tile_means_from_integral(goal_integral, goal_boxes, goal_areas)
|
goal_avgs = calculate_tile_means(goal_integral, goal_boxes, goal_areas)
|
||||||
|
|
||||||
src_order, goal_order = _match_tiles_by_feature_rank(
|
src_order, goal_order = match_tiles_by_feature_rank(
|
||||||
src_avgs,
|
src_avgs,
|
||||||
src_centers,
|
src_centers,
|
||||||
goal_avgs,
|
goal_avgs,
|
||||||
|
|||||||
Reference in New Issue
Block a user