|
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 |
|
|