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