-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for hloc #6
Comments
Thank you for your PR. I also noticed that you are working on integrating our model into hloc and training a new model that takes SuperPoint+LightGlue as input, in the discussion at cvg/Hierarchical-Localization#309. We will look into the code. Thank you! |
When I try to train, I sometimes get warnings like:
|
I got an AP of 84.3 when training without flip aug on superpoint + lightglue. Initialized from you checkpoint, trained for 20 epochs. I made it so that Learning rate is the same as if it started at zero. Warmup helps with transfer learning a lot, but I didn't use it. |
Thanks! Yes, it would be good to try the flip augmentation. Also, I'm wondering if training from scratch would make any difference. Also, is it easy to share some example visualizations of images + matches produced by superpoint + lightglue on images from the dataset? I'm trying to build intuition about how the matches you get from superpoint + lightglue might differ from the other correspondences we've used. |
Sure thing, here are the keypoints produced with superpoint. Below is your imlementation of read loftr_matches. I added some debug code for visualization at the bottom of the fuction. # your original code
def read_loftr_matches(path0, path1,
resize=None,
df=None,
padding=False,
keypoints0=None, keypoints1=None,
matches=None,
warp=False,
conf=None):
"""
Args:
path0, path1 (str): image path.
resize (int, optional): the longer edge of resized images. None for no resize.
padding (bool): If set to 'True', zero-pad resized images to squared size.
keypoints0, keypoints1 (numpy.array (n, 2)): keypoint pixel coordiantes of images.
matches (numpy.array (n, 2)): list of matched keypoint index.
warp (bool): If the first image is aligned with the second image.
conf (numpy.array (n,)): loftr matches confidence score
Returns:
image (torch.tensor): (2+2+3+3, h, w)
"""
image0 = imread_rgb(path0).astype(np.float32)
w0, h0 = image0.shape[1], image0.shape[0]
w_new0, h_new0 = get_resized_wh(w0, h0, resize)
w_new0, h_new0 = get_divisible_wh(w_new0, h_new0, df)
image1 = imread_rgb(path1).astype(np.float32)
w1, h1 = image1.shape[1], image1.shape[0]
w_new1, h_new1 = get_resized_wh(w1, h1, resize)
w_new1, h_new1 = get_divisible_wh(w_new1, h_new1, df)
valid_warp_pts = [False]
warp_rgb = True
# align image pair by estimating affine transformation from matches
if (warp == True) and (matches is not None):
src_pts = np.float32(keypoints0[matches[:, 0]]).reshape(-1, 1, 2)
dst_pts = np.float32(keypoints1[matches[:, 1]]).reshape(-1, 1, 2)
M, mask = cv2.estimateAffinePartial2D(src_pts, dst_pts, method=cv2.RANSAC, ransacReprojThreshold = 20.0)
warp_keypoints0 = np.float32(keypoints0).reshape(-1, 1, 2)
warp_keypoints0 = cv2.transform(warp_keypoints0, M)
warp_keypoints0 = warp_keypoints0.reshape(-1, 2)
w_kpt = w_new1
h_kpt = h_new1
valid_warp_pts = (warp_keypoints0[:, 0] >= 0) & (warp_keypoints0[:, 0] < w_kpt) & (warp_keypoints0[:, 1] >= 0) & (warp_keypoints0[:, 1] < h_kpt)
if valid_warp_pts.size == 0:
valid_warp_pts = [False]
warp_rgb = False
# do not align image pair when warp == Flase, or when matches is not available, or when no valid keypoint after warping
if (warp == False) or (matches is None) or (np.sum(valid_warp_pts)==0):
w_kpt = w_new0
h_kpt = h_new0
warp_keypoints0 = keypoints0
valid_warp_pts = (warp_keypoints0[:,0]>=0) & (warp_keypoints0[:,0]<w_kpt) & (warp_keypoints0[:,1]>=0) & (warp_keypoints0[:,1]<h_kpt)
warp_rgb = False
if padding:
pad_to = max(h_new1, w_new1)
keypoint_match_mask = np.zeros((4, pad_to, pad_to), dtype=np.float32)
else:
keypoint_match_mask = np.zeros((4, h_new1, w_new1), dtype=np.float32)
def f(x, max):
return math.floor(x) if math.floor(x)<max else max-1
int_array = np.vectorize(f)
# Create a mask of keypoints: keypoint_match_mask[0] for image 0, keypoint_match_mask[2] for image 1.
# Pixels corresponding to keypoints are set to one or confidence when available, and all other pixels are set to zero.
if conf is None:
if np.sum(valid_warp_pts)>0:
keypoint_match_mask[0, int_array(warp_keypoints0[valid_warp_pts,1], h_kpt), int_array(warp_keypoints0[valid_warp_pts,0], w_kpt)] = 1.
if len(keypoints1[:,1])>0:
keypoint_match_mask[2, int_array(keypoints1[:,1], h_new1), int_array(keypoints1[:,0], w_new1)] = 1.
else:
if np.sum(valid_warp_pts)>0:
keypoint_match_mask[0, int_array(warp_keypoints0[valid_warp_pts,1], h_kpt), int_array(warp_keypoints0[valid_warp_pts,0], w_kpt)] = conf[valid_warp_pts]
if len(keypoints1[:,1])>0:
keypoint_match_mask[2, int_array(keypoints1[:,1], h_new1), int_array(keypoints1[:,0], w_new1)] = conf
# Create a mask of matches: keypoint_match_mask[1] for image 0, keypoint_match_mask[3] for image 1.
# Pixels corresponding to matches are set to one, and all other pixels are set to zero.
if matches is not None:
if np.sum(valid_warp_pts[matches[:, 0]]) > 0:
match_y0 = int_array(warp_keypoints0[matches[:,0],1][valid_warp_pts[matches[:,0]]], h_kpt)
match_x0 = int_array(warp_keypoints0[matches[:,0],0][valid_warp_pts[matches[:,0]]], w_kpt)
keypoint_match_mask[1,match_y0,match_x0] = 1.
match_y1 = int_array(keypoints1[matches[:,1],1], h_new1)
match_x1 = int_array(keypoints1[matches[:,1],0], w_new1)
keypoint_match_mask[3,match_y1,match_x1] = 1.
# Concatenate keypoint and match masks with images.
image1 = cv2.resize(image1, (w_new1, h_new1))
image1 = np.transpose(image1, (2,0,1)) /255.
rgb_image = np.zeros((6, keypoint_match_mask.shape[1], keypoint_match_mask.shape[2]), dtype=np.float32)
rgb_image[:3,:h_new1,:w_new1] = image1
if matches is not None and warp_rgb==True:
warp_image0 = cv2.resize(image0, (w_new0, h_new0))
warp_image0 = cv2.warpAffine(warp_image0, M, (w_new1,h_new1), flags=cv2.INTER_AREA)
warp_image0 = np.transpose(warp_image0, (2,0,1))
rgb_image[3:,:h_new1,:w_new1] = warp_image0/255.
else:
image0 = cv2.resize(image0, (w_new0, h_new0))
image0 = np.transpose(image0, (2,0,1)) /255.
rgb_image[3:,:h_new0,:w_new0] = image0
keypoint_match_image = np.concatenate((keypoint_match_mask, rgb_image), axis=0)
# I will add code here: visualization i = np.copy(rgb_image[3:]).transpose([1, 2, 0])
keypoints = np.copy(keypoint_match_mask[0])
keypoints = np.array([keypoints] * 3).transpose([1, 2, 0])
kernel = np.array([
], dtype=np.uint8)
mask = keypoints[..., 0] > 0
keypoints[mask] = (0, 0, 1)
keypoints = cv2.dilate(keypoints, kernel, iterations=2)
dst = cv2.addWeighted(i, 0.5, keypoints, 0.5, 0.0) i = np.copy(rgb_image[3:]).transpose([1, 2, 0])
keypoints = np.copy(keypoint_match_mask[1])
keypoints = np.array([keypoints] * 3).transpose([1, 2, 0])
kernel = np.array([
], dtype=np.uint8)
mask = keypoints[..., 0] > 0
keypoints[mask] = (0, 0, 1)
keypoints = cv2.dilate(keypoints, kernel, iterations=2)
dst = cv2.addWeighted(i, 0.5, keypoints, 0.5, 0.0) i = np.copy(rgb_image[:3]).transpose([1, 2, 0])
keypoints = np.copy(keypoint_match_mask[2])
keypoints = np.array([keypoints] * 3).transpose([1, 2, 0])
kernel = np.array([
], dtype=np.uint8)
mask = keypoints[..., 0] > 0
keypoints[mask] = (0, 0, 1)
keypoints = cv2.dilate(keypoints, kernel, iterations=2)
dst = cv2.addWeighted(i, 0.5, keypoints, 0.5, 0.0) i = np.copy(rgb_image[:3]).transpose([1, 2, 0])
keypoints = np.copy(keypoint_match_mask[3])
keypoints = np.array([keypoints] * 3).transpose([1, 2, 0])
kernel = np.array([
], dtype=np.uint8)
mask = keypoints[..., 0] > 0
keypoints[mask] = (0, 0, 1)
keypoints = cv2.dilate(keypoints, kernel, iterations=2)
dst = cv2.addWeighted(i, 0.5, keypoints, 0.5, 0.0)
# cv2.imwrite('/media/mscherbina/4THDD/work/doppelgangers-hloc/tmp.jpg', dst)
|
So, obviously, it has smaller number of keypoints than disk / loftr |
It would be real nice to integrate your tool with hloc since that they support programmable pipeline with python, and your tool can be inserted here:
I am working on it and hopefully will add it, but, just as always, some collab would be real nice
The text was updated successfully, but these errors were encountered: