a lot of progress
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
import importlib
|
||||
importlib.invalidate_caches()
|
||||
import transition
|
||||
print('has transform_images:', hasattr(transition, 'transform_images'))
|
||||
print('transform_images object:', getattr(transition, 'transform_images', None))
|
||||
+17
-9
@@ -6,6 +6,7 @@ import matplotlib.pylab as plt
|
||||
from matplotlib.widgets import Button
|
||||
|
||||
from image_preparing import fix_image_resolution
|
||||
import transition
|
||||
|
||||
|
||||
class ImageInterface:
|
||||
@@ -96,15 +97,22 @@ class ImageInterface:
|
||||
self.current_index = 0
|
||||
self._show_browser()
|
||||
def _open_shenanigans(self):
|
||||
image_paths = self.image_sets.get("source", [])
|
||||
selected_index = self.selected_indices.get("source")
|
||||
if selected_index is None:
|
||||
selected_index = self.current_index if self.current_category == "source" else 0
|
||||
selected_path = None
|
||||
if image_paths:
|
||||
safe_index = max(0, min(selected_index, len(image_paths) - 1))
|
||||
selected_path = image_paths[safe_index]
|
||||
fix_image_resolution(selected_path)
|
||||
src_paths = self.image_sets.get("source", [])
|
||||
goal_paths = self.image_sets.get("goal", [])
|
||||
if not src_paths or not goal_paths:
|
||||
return
|
||||
sel_src = self.selected_indices.get("source")
|
||||
sel_goal = self.selected_indices.get("goal")
|
||||
src_path = src_paths[sel_src] if sel_src is not None else src_paths[0]
|
||||
goal_path = goal_paths[sel_goal] if sel_goal is not None else goal_paths[0]
|
||||
try:
|
||||
import importlib
|
||||
importlib.reload(transition)
|
||||
except Exception:
|
||||
pass
|
||||
if not hasattr(transition, "transform_images"):
|
||||
return
|
||||
transition.transform_images(src_path, goal_path)
|
||||
def _selected_image_name(self, category):
|
||||
selected_index = self.selected_indices.get(category)
|
||||
image_paths = self.image_sets.get(category, [])
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
from PIL import Image, ImageDraw
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.tri import Triangulation
|
||||
|
||||
from image_preparing import fix_image_resolution
|
||||
|
||||
|
||||
def prepare_image(path):
|
||||
pil = Image.open(path).convert("RGB").resize((800, 600))
|
||||
return pil
|
||||
def auto_correspondences_grid(pil, nx=8, ny=6):
|
||||
w, h = pil.size
|
||||
xs = np.linspace(0, w - 1, nx)
|
||||
ys = np.linspace(0, h - 1, ny)
|
||||
pts = []
|
||||
for y in ys:
|
||||
for x in xs:
|
||||
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]]
|
||||
for c in corners:
|
||||
if c not in pts:
|
||||
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
|
||||
def _affine_coeffs(from_tri, to_tri):
|
||||
A = []
|
||||
B = []
|
||||
for (x_dst, y_dst), (x_src, y_src) in zip(from_tri, to_tri):
|
||||
A.append([x_dst, y_dst, 1, 0, 0, 0])
|
||||
A.append([0, 0, 0, x_dst, y_dst, 1])
|
||||
B.append(x_src)
|
||||
B.append(y_src)
|
||||
sol, *_ = np.linalg.lstsq(np.array(A), np.array(B), rcond=None)
|
||||
return tuple(sol.tolist())
|
||||
def _warp_triangle(src_pil, src_tri, dst_tri, out_size, offset):
|
||||
dx, dy = offset
|
||||
dst_local = np.array(dst_tri) - np.array([dx, dy])
|
||||
coeffs = _affine_coeffs(dst_local, src_tri)
|
||||
return src_pil.transform(out_size, Image.AFFINE, coeffs, resample=Image.BILINEAR)
|
||||
def reconstruct_goal_from_source(src_pil, pts1, pts2):
|
||||
w, h = src_pil.size
|
||||
pts1 = np.asarray(pts1)
|
||||
pts2 = np.asarray(pts2)
|
||||
triangles = compute_delaunay_on_points(pts2)
|
||||
out = Image.new("RGB", (w, h))
|
||||
for tri in triangles:
|
||||
tri = np.asarray(tri, dtype=int)
|
||||
src_tri = pts1[tri]
|
||||
dst_tri = pts2[tri]
|
||||
min_x = max(int(np.floor(dst_tri[:, 0].min())), 0)
|
||||
max_x = min(int(np.ceil(dst_tri[:, 0].max())), w)
|
||||
min_y = max(int(np.floor(dst_tri[:, 1].min())), 0)
|
||||
max_y = min(int(np.ceil(dst_tri[:, 1].max())), h)
|
||||
if max_x <= min_x or max_y <= min_y:
|
||||
continue
|
||||
out_w = max_x - min_x
|
||||
out_h = max_y - min_y
|
||||
warped = _warp_triangle(src_pil, src_tri, dst_tri, (out_w, out_h), (min_x, min_y))
|
||||
mask = Image.new("L", (out_w, out_h), 0)
|
||||
draw = ImageDraw.Draw(mask)
|
||||
tri_local = [(x - min_x, y - min_y) for x, y in dst_tri.tolist()]
|
||||
draw.polygon(tri_local, fill=255)
|
||||
out.paste(warped, (min_x, min_y), mask)
|
||||
return out
|
||||
def transform_images(src_path, goal_path):
|
||||
fix_image_resolution(src_path)
|
||||
fix_image_resolution(goal_path)
|
||||
src = prepare_image(src_path)
|
||||
goal = prepare_image(goal_path)
|
||||
frames = tile_reposition_transition(src, goal, grid=(40, 30), n_frames=24)
|
||||
fig, ax = plt.subplots(figsize=(8, 6))
|
||||
im = ax.imshow(frames[0])
|
||||
ax.axis("off")
|
||||
for frame in frames:
|
||||
im.set_data(frame)
|
||||
fig.canvas.draw_idle()
|
||||
plt.pause(0.05)
|
||||
plt.show()
|
||||
def tile_reposition_transition(src_pil, goal_pil, grid=(40, 30), n_frames=24):
|
||||
|
||||
src = src_pil.convert("RGBA")
|
||||
goal = goal_pil.convert("RGBA")
|
||||
w, h = src.size
|
||||
nx, ny = grid
|
||||
|
||||
def make_tiles(pil):
|
||||
tiles = []
|
||||
for iy in range(ny):
|
||||
y0 = int(round(iy * h / ny))
|
||||
y1 = int(round((iy + 1) * h / ny))
|
||||
for ix in range(nx):
|
||||
x0 = int(round(ix * w / nx))
|
||||
x1 = int(round((ix + 1) * w / nx))
|
||||
box = (x0, y0, x1, y1)
|
||||
crop = pil.crop(box).convert("RGB")
|
||||
arr = np.array(crop, dtype=float)
|
||||
avg = arr.reshape(-1, 3).mean(axis=0)
|
||||
center = ((x0 + x1) / 2.0, (y0 + y1) / 2.0)
|
||||
tiles.append({"box": box, "img": crop, "avg": avg, "center": center})
|
||||
return tiles
|
||||
|
||||
src_tiles = make_tiles(src)
|
||||
goal_tiles = make_tiles(goal)
|
||||
|
||||
max_spatial = np.hypot(w, h)
|
||||
src_available = set(range(len(src_tiles)))
|
||||
mapping = {}
|
||||
for g_idx, g in enumerate(goal_tiles):
|
||||
best = None
|
||||
best_cost = None
|
||||
for s_idx in list(src_available):
|
||||
s = src_tiles[s_idx]
|
||||
color_dist = np.linalg.norm(g["avg"] - s["avg"]) / (255.0 * np.sqrt(3))
|
||||
spatial_dist = np.linalg.norm(np.array(g["center"]) - np.array(s["center"])) / max_spatial
|
||||
cost = color_dist + 0.35 * spatial_dist
|
||||
if best_cost is None or cost < best_cost:
|
||||
best_cost = cost
|
||||
best = s_idx
|
||||
if best is None:
|
||||
best = src_available.pop()
|
||||
else:
|
||||
src_available.remove(best)
|
||||
mapping[best] = g_idx
|
||||
|
||||
targets = {}
|
||||
for s_idx, g_idx in mapping.items():
|
||||
s = src_tiles[s_idx]
|
||||
g = goal_tiles[g_idx]
|
||||
targets[s_idx] = {"start": (s["box"][0], s["box"][1]), "end": (g["box"][0], g["box"][1]), "img": s["img"], "size": (s["box"][2]-s["box"][0], s["box"][3]-s["box"][1])}
|
||||
|
||||
frames = []
|
||||
for k in range(n_frames + 1):
|
||||
t = k / float(n_frames)
|
||||
frame = Image.new("RGBA", (w, h), (0, 0, 0, 255))
|
||||
for s_idx, info in targets.items():
|
||||
sx, sy = info["start"]
|
||||
ex, ey = info["end"]
|
||||
cx = int(round(sx + (ex - sx) * t))
|
||||
cy = int(round(sy + (ey - sy) * t))
|
||||
frame.paste(info["img"], (cx, cy))
|
||||
frames.append(frame.convert("RGB"))
|
||||
|
||||
final = Image.new("RGB", (w, h))
|
||||
for s_idx, g_idx in mapping.items():
|
||||
src_img = src_tiles[s_idx]["img"].convert("RGB")
|
||||
x0, y0, x1, y1 = goal_tiles[g_idx]["box"]
|
||||
final.paste(src_img, (x0, y0))
|
||||
frames[-1] = final
|
||||
return frames
|
||||
|
||||
|
||||
Reference in New Issue
Block a user