分类 代码与算法 下的文章

源码地址:https://github.com/cvg/Hierarchical-Localization/


在本笔记本中,我们将从一小组图像建立一个场景的3D地图,然后本地化从互联网下载的图像。 本演示由Philipp Lindenberger贡献。

%load_ext autoreload
%autoreload 2
import tqdm, tqdm.notebook
tqdm.tqdm = tqdm.notebook.tqdm  # notebook-friendly progress bars
from pathlib import Path

from hloc import extract_features, match_features, reconstruction, visualization, pairs_from_exhaustive
from hloc.visualization import plot_images, read_image
from hloc.utils import viz_3d

设置

这里我们定义了一些输出路径。

images = Path('datasets/sacre_coeur')
outputs = Path('outputs/demo/')
!rm -rf $outputs
sfm_pairs = outputs / 'pairs-sfm.txt'
loc_pairs = outputs / 'pairs-loc.txt'
sfm_dir = outputs / 'sfm'
features = outputs / 'features.h5'
matches = outputs / 'matches.h5'

feature_conf = extract_features.confs['superpoint_aachen']
matcher_conf = match_features.confs['superglue']

3D映射

首先,我们列出用于映射的图像。 这些都是白天拍摄的圣心大教堂。

references = [str(p.relative_to(images)) for p in (images / 'mapping/').iterdir()]
print(len(references), "mapping images")
plot_images([read_image(images / r) for r in references[:4]], dpi=50)

然后我们提取特征并在图像对之间进行匹配。 由于我们处理的图像很少,所以我们只是竭尽所能地匹配所有对。 对于更大的场景,我们将使用图像检索,正如在其他笔记本中演示的那样。

extract_features.main(feature_conf, images, image_list=references, feature_path=features)
pairs_from_exhaustive.main(sfm_pairs, image_list=references)
match_features.main(matcher_conf, sfm_pairs, features=features, matches=matches);


在此基础上,进行Structure-From-Motion和显示重建的三维模型。

model = reconstruction.main(sfm_dir, images, sfm_pairs, features, matches, image_list=references)
fig = viz_3d.init_figure()
viz_3d.plot_reconstruction(fig, model, color='rgba(255,0,0,0.5)', name="mapping")
fig.show()



我们还可视化那些被三角化到3D模型中的关键点。

visualization.visualize_sfm_2d(model, images, color_by='visibility', n=2)

定位

现在我们有了场景的3D地图,我们可以定位任何图像。 为了证明这一点,我们从维基媒体下载了一张夜间图片(https://commons.wikimedia.org/wiki/File:Paris_-_Basilique_du_Sacr%C3%A9_Coeur,_Montmartre_-_panoramio.jpg)。

url = "https://upload.wikimedia.org/wikipedia/commons/5/53/Paris_-_Basilique_du_Sacr%C3%A9_Coeur%2C_Montmartre_-_panoramio.jpg"
# try other queries by uncommenting their url
# url = "https://upload.wikimedia.org/wikipedia/commons/5/59/Basilique_du_Sacr%C3%A9-C%C5%93ur_%285430392880%29.jpg"
# url = "https://upload.wikimedia.org/wikipedia/commons/8/8e/Sacr%C3%A9_C%C5%93ur_at_night%21_%285865355326%29.jpg"
query = 'query/night.jpg'
#!mkdir -p $images/query && wget $url -O $images/$query -q
plot_images([read_image(images / query)], dpi=75)

同样,我们为查询提取特征并全力匹配它们。

extract_features.main(feature_conf, images, image_list=[query], feature_path=features, overwrite=True)
pairs_from_exhaustive.main(loc_pairs, image_list=[query], ref_list=references)
match_features.main(matcher_conf, loc_pairs, features=features, matches=matches, overwrite=True);

我们读取query的EXIF数据来推断相机参数(如焦距)的粗略初始估计。 然后利用PnP+RANSAC估计相机的绝对位姿,并对相机参数进行优化。

import pycolmap
from hloc.localize_sfm import QueryLocalizer, pose_from_cluster

camera = pycolmap.infer_camera_from_image(images / query)
ref_ids = [model.find_image_with_name(r).image_id for r in references]
conf = {
    'estimation': {'ransac': {'max_error': 12}},
    'refinement': {'refine_focal_length': True, 'refine_extra_params': True},
}
localizer = QueryLocalizer(model, conf)
ret, log = pose_from_cluster(localizer, query, camera, ref_ids, features, matches)

print(f'found {ret["num_inliers"]}/{len(ret["inliers"])} inlier correspondences.')
visualization.visualize_loc_from_log(images, query, log, model)

我们将查询图像和一些映射图像之间的对应关系可视化。 我们也可以在3D地图中可视化估计的相机姿态。

pose = pycolmap.Image(tvec=ret['tvec'], qvec=ret['qvec'])
viz_3d.plot_camera_colmap(fig, pose, camera, color='rgba(0,255,0,0.5)', name=query)
fig.show()


前言

  网上一堆狂吹PCA和讲原理的,可就是不讲怎么用。
  给了代码的,拿过来用效果贼差,毕竟那些只是为了画图的代码而已。。。


上代码

  真正使用,分3步即可:
1、数据预处理(非常重要!!!)——零均值化和缩放
原因可以看网上原理教程,这里只关心怎么做。
很简单:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler().fit(des_query)
des_query = scaler.transform(des_query)
des_train = scaler.transform(des_train)

2、真正执行PCA

from sklearn.decomposition import PCA

pca = PCA(n_components=32, whiten=True).fit(des_query_new)
des_query_pca = pca.transform(des_query)
des_train_pca = pca.transform(des_train)

3、对结果再做归一化(欧氏距离就用l2)

import torch

def desc_l2norm(desc):
    eps_l2_norm = 1e-10
    return (desc / torch.Tensor(desc).pow(2).sum(dim=1, keepdim=True).add(eps_l2_norm).pow(0.5)).numpy()

des_query_new = desc_l2norm(des_query_pca)
des_train_new = desc_l2norm(des_train_pca)

4、注意train和query要用同一个fit,因为不然分开学习,肯定最后结果是个“0”。这里具体fit内容怎么设置,我还没整明白,随大流填个query的吧。。。

运行结果

  这时候再去做匹配,发现维度下降,有效果且精度甚至会上升。(图就不画了,懒。。。)
  注意:这里只是粗匹配上升了啊,它很可能有很多的误匹配的!!!

后言

其他还有umap可以玩玩。