forked from cvg/Hierarchical-Localization
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
8,790 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,5 @@ __pycache__ | |
.ipynb_checkpoints | ||
outputs/ | ||
third_party/netvlad | ||
datasets/* | ||
!datasets/sacre_coeur/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +0,0 @@ | ||
# Ignore everything in this directory | ||
* | ||
# Except this file | ||
!.gitignore | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Sacre Coeur demo | ||
|
||
We provide here a subset of images depicting the Sacre Coeur. These images were obtained from the [Image Matching Challenge 2021](https://www.cs.ubc.ca/research/image-matching-challenge/2021/data/) and were originally collected by the [Yahoo Flickr Creative Commons 100M (YFCC) dataset](https://multimediacommons.wordpress.com/yfcc100m-core-dataset/). |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
""" | ||
3D visualization based on plotly. | ||
Works for a small number of points and cameras, might be slow otherwise. | ||
1) Initialize a figure with `init_figure` | ||
2) Add 3D points, camera frustums, or both as a pycolmap.Reconstruction | ||
Written by Paul-Edouard Sarlin and Philipp Lindenberger. | ||
""" | ||
|
||
from typing import Optional | ||
import numpy as np | ||
import pycolmap | ||
import plotly.graph_objects as go | ||
|
||
|
||
def to_homogeneous(points): | ||
pad = np.ones((points.shape[:-1]+(1,)), dtype=points.dtype) | ||
return np.concatenate([points, pad], axis=-1) | ||
|
||
|
||
def init_figure(height: int = 800) -> go.Figure: | ||
"""Initialize a 3D figure.""" | ||
fig = go.Figure() | ||
axes = dict( | ||
visible=False, | ||
showbackground=False, | ||
showgrid=False, | ||
showline=False, | ||
showticklabels=True, | ||
autorange=True, | ||
) | ||
fig.update_layout( | ||
template="plotly_dark", | ||
height=height, | ||
scene_camera=dict( | ||
eye=dict(x=0., y=-.1, z=-2), | ||
up=dict(x=0, y=-1., z=0), | ||
projection=dict(type="orthographic")), | ||
scene=dict( | ||
xaxis=axes, | ||
yaxis=axes, | ||
zaxis=axes, | ||
aspectmode='data', | ||
dragmode='orbit', | ||
), | ||
margin=dict(l=0, r=0, b=0, t=0, pad=0), | ||
legend=dict( | ||
orientation="h", | ||
yanchor="top", | ||
y=0.99, | ||
xanchor="left", | ||
x=0.1 | ||
), | ||
) | ||
return fig | ||
|
||
|
||
def plot_points( | ||
fig: go.Figure, | ||
pts: np.ndarray, | ||
color: str = 'rgba(255, 0, 0, 1)', | ||
ps: int = 2, | ||
colorscale: Optional[str] = None, | ||
name: Optional[str] = None): | ||
"""Plot a set of 3D points.""" | ||
x, y, z = pts.T | ||
tr = go.Scatter3d( | ||
x=x, y=y, z=z, mode='markers', name=name, legendgroup=name, | ||
marker=dict( | ||
size=ps, color=color, line_width=0.0, colorscale=colorscale)) | ||
fig.add_trace(tr) | ||
|
||
|
||
def plot_camera( | ||
fig: go.Figure, | ||
R: np.ndarray, | ||
t: np.ndarray, | ||
K: np.ndarray, | ||
color: str = 'rgb(0, 0, 255)', | ||
name: Optional[str] = None, | ||
legendgroup: Optional[str] = None, | ||
size: float = 1.0): | ||
"""Plot a camera frustum from pose and intrinsic matrix.""" | ||
W, H = K[0, 2]*2, K[1, 2]*2 | ||
corners = np.array([[0, 0], [W, 0], [W, H], [0, H], [0, 0]]) | ||
if size is not None: | ||
image_extent = max(size * W / 1024.0, size * H / 1024.0) | ||
world_extent = max(W, H) / (K[0, 0] + K[1, 1]) / 0.5 | ||
scale = 0.5 * image_extent / world_extent | ||
else: | ||
scale = 1.0 | ||
corners = to_homogeneous(corners) @ np.linalg.inv(K).T | ||
corners = (corners / 2 * scale) @ R.T + t | ||
|
||
x, y, z = corners.T | ||
rect = go.Scatter3d( | ||
x=x, y=y, z=z, line=dict(color=color), legendgroup=legendgroup, | ||
name=name, marker=dict(size=0.0001), showlegend=False) | ||
fig.add_trace(rect) | ||
|
||
x, y, z = np.concatenate(([t], corners)).T | ||
i = [0, 0, 0, 0] | ||
j = [1, 2, 3, 4] | ||
k = [2, 3, 4, 1] | ||
|
||
pyramid = go.Mesh3d( | ||
x=x, y=y, z=z, color=color, i=i, j=j, k=k, | ||
legendgroup=legendgroup, name=name, showlegend=False) | ||
fig.add_trace(pyramid) | ||
triangles = np.vstack((i, j, k)).T | ||
vertices = np.concatenate(([t], corners)) | ||
tri_points = np.array([ | ||
vertices[i] for i in triangles.reshape(-1) | ||
]) | ||
|
||
x, y, z = tri_points.T | ||
pyramid = go.Scatter3d( | ||
x=x, y=y, z=z, mode='lines', legendgroup=legendgroup, | ||
name=name, line=dict(color=color, width=1), showlegend=False) | ||
fig.add_trace(pyramid) | ||
|
||
|
||
def plot_camera_colmap( | ||
fig: go.Figure, | ||
image: pycolmap.Image, | ||
camera: pycolmap.Camera, | ||
name: Optional[str] = None, | ||
**kwargs): | ||
"""Plot a camera frustum from PyCOLMAP objects""" | ||
plot_camera( | ||
fig, | ||
image.rotmat().T, | ||
image.projection_center(), | ||
camera.calibration_matrix(), | ||
name=name or str(image.image_id), | ||
**kwargs) | ||
|
||
|
||
def plot_cameras( | ||
fig: go.Figure, | ||
reconstruction: pycolmap.Reconstruction, | ||
**kwargs): | ||
"""Plot a camera as a cone with camera frustum.""" | ||
for image_id, image in reconstruction.images.items(): | ||
plot_camera_colmap( | ||
fig, image, reconstruction.cameras[image.camera_id], **kwargs) | ||
|
||
|
||
def plot_reconstruction( | ||
fig: go.Figure, | ||
rec: pycolmap.Reconstruction, | ||
max_reproj_error: float = 6.0, | ||
color: str = 'rgb(0, 0, 255)', | ||
name: Optional[str] = None, | ||
min_track_length: int = 2, | ||
points: bool = True, | ||
cameras: bool = True, | ||
cs: float = 1.0): | ||
# Filter outliers | ||
bbs = rec.compute_bounding_box(0.001, 0.999) | ||
# Filter points, use original reproj error here | ||
xyzs = [p3D.xyz for _, p3D in rec.points3D.items() if ( | ||
(p3D.xyz >= bbs[0]).all() and | ||
(p3D.xyz <= bbs[1]).all() and | ||
p3D.error <= max_reproj_error and | ||
p3D.track.length() >= min_track_length)] | ||
if points: | ||
plot_points(fig, np.array(xyzs), color=color, ps=1, name=name) | ||
if cameras: | ||
plot_cameras(fig, rec, color=color, legendgroup=name, size=cs) |