| from scipy.interpolate import interp1d, PchipInterpolator |
|
|
| import numpy as np |
| from PIL import Image |
| import cv2 |
| import torch |
|
|
|
|
| def sift_match( |
| img1, img2, |
| thr=0.5, |
| topk=5, method="max_dist", |
| output_path="sift_matches.png", |
| ): |
| |
| assert method in ["max_dist", "random", "max_score", "max_score_even"] |
|
|
| |
| |
|
|
| |
| img1_rgb = np.array(img1).copy() |
| img2_rgb = np.array(img2).copy() |
| img1 = cv2.cvtColor(np.array(img1), cv2.COLOR_RGB2BGR) |
| img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) |
| img2 = cv2.cvtColor(np.array(img2), cv2.COLOR_RGB2BGR) |
| img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) |
|
|
| |
| |
| sift = cv2.SIFT_create() |
| |
| kp1, des1 = sift.detectAndCompute(img1, None) |
| kp2, des2 = sift.detectAndCompute(img2, None) |
| |
| bf = cv2.BFMatcher() |
| |
| |
| matches = bf.knnMatch(des1, des2, k=2) |
|
|
| |
| good = [] |
| point_list = [] |
| distance_list = [] |
|
|
| if method in ['max_score', 'max_score_even']: |
| matches = sorted(matches, key=lambda x: x[0].distance / x[1].distance) |
|
|
| anchor_points_list = [] |
| for m, n in matches[:topk]: |
| print(m.distance / n.distance) |
|
|
| |
| if method == 'max_score_even': |
| to_close = False |
| for anchor_point in anchor_points_list: |
| pt1 = kp1[m.queryIdx].pt |
| dist = np.linalg.norm(np.array(pt1) - np.array(anchor_point)) |
| if dist < 50: |
| to_close = True |
| break |
| if to_close: |
| continue |
|
|
| good.append([m]) |
|
|
| pt1 = kp1[m.queryIdx].pt |
| pt2 = kp2[m.trainIdx].pt |
| dist = np.linalg.norm(np.array(pt1) - np.array(pt2)) |
| distance_list.append(dist) |
|
|
| anchor_points_list.append(pt1) |
|
|
| pt1 = torch.tensor(pt1) |
| pt2 = torch.tensor(pt2) |
| pt = torch.stack([pt1, pt2]) |
| point_list.append(pt) |
|
|
| if method in ['max_dist', 'random']: |
| for m, n in matches: |
| if m.distance < thr * n.distance: |
| good.append([m]) |
|
|
| pt1 = kp1[m.queryIdx].pt |
| pt2 = kp2[m.trainIdx].pt |
| dist = np.linalg.norm(np.array(pt1) - np.array(pt2)) |
| distance_list.append(dist) |
|
|
| pt1 = torch.tensor(pt1) |
| pt2 = torch.tensor(pt2) |
| pt = torch.stack([pt1, pt2]) |
| point_list.append(pt) |
|
|
| distance_list = np.array(distance_list) |
| |
| idx = np.argsort(distance_list) |
| if method == "max_dist": |
| idx = idx[-topk:] |
| elif method == "random": |
| topk = min(topk, len(idx)) |
| idx = np.random.choice(idx, topk, replace=False) |
| elif method == "max_score": |
| import pdb; pdb.set_trace() |
| raise NotImplementedError |
| |
| else: |
| raise ValueError(f"Unknown method {method}") |
|
|
| point_list = [point_list[i] for i in idx] |
| good = [good[i] for i in idx] |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| margin = 10 |
| img3 = np.zeros((img1_rgb.shape[0] + img2_rgb.shape[0] + margin, max(img1_rgb.shape[1], img2_rgb.shape[1]), 3), dtype=np.uint8) |
| |
| img3[:, :] = 255 |
| img3[:img1_rgb.shape[0], :img1_rgb.shape[1]] = img1_rgb |
| img3[img1_rgb.shape[0] + margin:, :img2_rgb.shape[1]] = img2_rgb |
| |
| color_list = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255), (255, 0, 255)] |
| for color_idx, m in enumerate(good): |
| pt1 = kp1[m[0].queryIdx].pt |
| pt2 = kp2[m[0].trainIdx].pt |
| pt1 = (int(pt1[0]), int(pt1[1])) |
| pt2 = (int(pt2[0]), int(pt2[1]) + img1_rgb.shape[0] + margin) |
| |
| |
| |
| color = color_list[color_idx % len(color_list)] |
| cv2.line(img3, pt1, pt2, color, 1, lineType=cv2.LINE_AA) |
| |
| cv2.circle(img3, pt1, 3, color, lineType=cv2.LINE_AA) |
| cv2.circle(img3, pt2, 3, color, lineType=cv2.LINE_AA) |
|
|
| Image.fromarray(img3).save(output_path) |
| print(f"Save the sift matches to {output_path}") |
|
|
| |
| if len(point_list) == 0: |
| return None |
|
|
| point_list = torch.stack(point_list) |
| point_list = point_list.permute(1, 0, 2) |
|
|
| return point_list |
|
|
|
|
| def interpolate_trajectory(points_torch, num_frames, t=None): |
| |
|
|
| num_points = points_torch.shape[1] |
| points_torch = points_torch.permute(1, 0, 2) |
|
|
| points_list = [] |
| for i in range(num_points): |
| |
| points = points_torch[i].cpu().numpy() |
|
|
| x = [point[0] for point in points] |
| y = [point[1] for point in points] |
|
|
| if t is None: |
| t = np.linspace(0, 1, len(points)) |
|
|
| |
| |
| fx = PchipInterpolator(t, x) |
| fy = PchipInterpolator(t, y) |
|
|
| new_t = np.linspace(0, 1, num_frames) |
|
|
| new_x = fx(new_t) |
| new_y = fy(new_t) |
| new_points = list(zip(new_x, new_y)) |
|
|
| points_list.append(new_points) |
|
|
| points = torch.tensor(points_list) |
| points = points.permute(1, 0, 2) |
|
|
| return points |
|
|
|
|
| |
| def point_tracking( |
| F0, |
| F1, |
| handle_points, |
| handle_points_init, |
| track_dist=5, |
| ): |
| |
| |
| |
| |
|
|
| |
| handle_points = torch.stack([handle_points[:, 1], handle_points[:, 0]], dim=-1) |
| handle_points_init = torch.stack([handle_points_init[:, 1], handle_points_init[:, 0]], dim=-1) |
|
|
| with torch.no_grad(): |
| _, _, max_r, max_c = F0.shape |
|
|
| for i in range(len(handle_points)): |
| pi0, pi = handle_points_init[i], handle_points[i] |
| f0 = F0[:, :, int(pi0[0]), int(pi0[1])] |
|
|
| r1, r2 = max(0, int(pi[0]) - track_dist), min(max_r, int(pi[0]) + track_dist + 1) |
| c1, c2 = max(0, int(pi[1]) - track_dist), min(max_c, int(pi[1]) + track_dist + 1) |
| F1_neighbor = F1[:, :, r1:r2, c1:c2] |
| all_dist = (f0.unsqueeze(dim=-1).unsqueeze(dim=-1) - F1_neighbor).abs().sum(dim=1) |
| all_dist = all_dist.squeeze(dim=0) |
| row, col = divmod(all_dist.argmin().item(), all_dist.shape[-1]) |
| |
| |
| handle_points[i][0] = r1 + row |
| handle_points[i][1] = c1 + col |
|
|
| handle_points = torch.stack([handle_points[:, 1], handle_points[:, 0]], dim=-1) |
|
|
| return handle_points |
|
|