| import torch |
| import numpy as np |
| import cv2 |
| from lightglue import LightGlue |
| from lightglue.utils import rbd |
| from lightglue import SuperPoint, SIFT |
| from lightglue.utils import load_image |
|
|
|
|
| def unrotate_kps_W(kps_rot, k, H, W): |
| |
| if hasattr(kps_rot, 'cpu'): kps_rot = kps_rot.cpu().numpy() |
| if hasattr(k, 'cpu'): k = k.cpu().numpy() |
| |
| |
| if k.ndim > 1: k = k.squeeze() |
| if kps_rot.ndim > 2: kps_rot = kps_rot.squeeze() |
|
|
| x_r = kps_rot[:, 0] |
| y_r = kps_rot[:, 1] |
| |
| x = np.zeros_like(x_r) |
| y = np.zeros_like(y_r) |
| |
| mask0 = (k == 0) |
| x[mask0], y[mask0] = x_r[mask0], y_r[mask0] |
| |
| mask1 = (k == 1) |
| x[mask1], y[mask1] = (W - 1) - y_r[mask1], x_r[mask1] |
| |
| mask2 = (k == 2) |
| x[mask2], y[mask2] = (W - 1) - x_r[mask2], (H - 1) - y_r[mask2] |
| |
| mask3 = (k == 3) |
| x[mask3], y[mask3] = y_r[mask3], (H - 1) - x_r[mask3] |
| |
| return np.stack([x, y], axis=-1) |
|
|
| def extract_keypoints(path_to_image0, features='superpoint', rotations = [0,1,2,3]): |
| |
| device = 'cuda' if torch.cuda.is_available() else 'cpu' |
| |
| |
| timg = load_image(path_to_image0).to(device) |
| _, h, w = timg.shape |
|
|
| if features == 'sift': |
| extractor = SIFT(max_num_keypoints=2048).eval().to(device) |
| feats = extractor.extract(timg) |
| return feats , h, w |
| |
| if features == 'superpoint': |
| extractor = SuperPoint(max_num_keypoints=2048).eval().to(device) |
|
|
| |
| feats = {} |
| for k in (rotations): |
| timg_rotated = torch.rot90(timg, k, dims=(1, 2)) |
| feats[k] = extractor.extract(timg_rotated) |
| |
|
|
| |
| all_keypoints = [] |
| all_scores = [] |
| all_descriptors = [] |
| all_rotations = [] |
| for k, feat in feats.items(): |
| kpts = feat['keypoints'] |
| num_kpts = kpts.shape[1] |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| rot_indices = torch.full((1, num_kpts), k, dtype=torch.long, device=device) |
| all_keypoints.append(feat['keypoints']) |
| all_scores.append(feat['keypoint_scores']) |
| all_descriptors.append(feat['descriptors']) |
| all_rotations.append(rot_indices) |
|
|
| |
| feats_merged = { |
| 'keypoints': torch.cat(all_keypoints, dim=1), |
| 'keypoint_scores': torch.cat(all_scores, dim=1), |
| 'descriptors': torch.cat(all_descriptors, dim=1), |
| 'rotations': torch.cat(all_rotations, dim=1) |
| } |
| |
| num_kpts = feats_merged['keypoints'].shape[1] |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| return feats_merged , feats, h, w |
|
|
| def lightglue_matching(feats0, feats1, matcher = None): |
| if matcher is None: |
| device = 'cuda' if torch.cuda.is_available() else 'cpu' |
| matcher = LightGlue(features='superpoint').eval().to(device) |
| |
| out_k = matcher({'image0': feats0, 'image1': feats1}) |
| _, _, out_k = [rbd(x) for x in [feats0, feats1, out_k]] |
| return out_k['matches'] |
|
|
| def feature_matching(feats0, feats1, matcher = None, exhaustive = True): |
| best_rot = 0 |
| best_num_matches = 0 |
| matches_tensor = None |
| |
| |
| for rot in [0,1,2,3]: |
| matches_tensor_rot = lightglue_matching(feats0[0], feats1[rot], matcher = matcher) |
| if (len(matches_tensor_rot) > best_num_matches): |
| best_num_matches = len(matches_tensor_rot) |
| best_rot = rot |
| matches_tensor = matches_tensor_rot |
|
|
| if matches_tensor is not None and len(matches_tensor) > 0: |
| matches_np = matches_tensor.cpu().numpy().astype(np.uint32) |
| else: |
| return None |
|
|
| |
| for k in range(best_rot): |
| matches_np[:,1] += feats1[k]['keypoints'].shape[1] |
| all_matches = [matches_np] |
|
|
| if not exhaustive: |
| return matches_np |
| |
| |
| rots = [] |
| for rot in [1, 2, 3]: |
| rot_i = best_rot + rot |
| if rot_i >=4: |
| rot_i = rot_i -4 |
| rots.append(rot_i) |
|
|
| |
| for rot_i in [1,2,3]: |
| rot_j = rots[rot_i-1] |
|
|
| matches_tensor_rot = lightglue_matching(feats0[rot_i], feats1[rot_j], matcher = matcher) |
| matches_np_i = matches_tensor_rot.cpu().numpy().astype(np.uint32) |
| if rot_i > 0: |
| for k in range(rot_i): |
| matches_np_i[:,0] += feats0[k]['keypoints'].shape[1] |
| if rot_j > 0: |
| for k in range(rot_j): |
| matches_np_i[:,1] += feats1[k]['keypoints'].shape[1] |
|
|
| all_matches.append(matches_np_i) |
| print(f"Rotation {rot_i} vs {rot_j}: {len(matches_tensor_rot)} matches") |
|
|
| |
| matches_stacked = ( |
| np.vstack(all_matches) if len(all_matches) and all_matches[0].size else |
| np.empty((0, 2), dtype=np.uint32) |
| ) |
| |
| |
| |
| |
| |
|
|
| |
| return matches_stacked |
| |
|
|
| |