opensfm/actions/export_bundler.py (83 lines of code) (raw):
import os
import numpy as np
from opensfm import io
from opensfm.dataset import DataSet
def run_dataset(data: DataSet, list_path, bundle_path, undistorted) -> None:
"""Export reconstruction to bundler format.
Args:
list_path: txt list of images to export
bundle_path : output path
undistorted : export undistorted reconstruction
"""
udata = data.undistorted_dataset()
default_path = os.path.join(data.data_path, "bundler")
list_file_path = list_path if list_path else default_path
bundle_file_path = bundle_path if bundle_path else default_path
if undistorted:
reconstructions = udata.load_undistorted_reconstruction()
track_manager = udata.load_undistorted_tracks_manager()
images = reconstructions[0].shots.keys()
else:
reconstructions = data.load_reconstruction()
track_manager = data.load_tracks_manager()
images = data.images()
export_bundler(
images, reconstructions, track_manager, bundle_file_path, list_file_path
)
def export_bundler(
image_list, reconstructions, track_manager, bundle_file_path: str, list_file_path: str
) -> None:
"""
Generate a reconstruction file that is consistent with Bundler's format
"""
io.mkdir_p(bundle_file_path)
io.mkdir_p(list_file_path)
for j, reconstruction in enumerate(reconstructions):
lines = []
lines.append("# Bundle file v0.3")
points = reconstruction.points
shots = reconstruction.shots
num_point = len(points)
num_shot = len(image_list)
lines.append(" ".join(map(str, [num_shot, num_point])))
shots_order = {key: i for i, key in enumerate(image_list)}
# cameras
for shot_id in image_list:
if shot_id in shots:
shot = shots[shot_id]
camera = shot.camera
if shot.camera.projection_type == "brown":
# Will aproximate Brown model, not optimal
focal_normalized = camera.focal_x
else:
focal_normalized = camera.focal
scale = max(camera.width, camera.height)
focal = focal_normalized * scale
k1 = camera.k1
k2 = camera.k2
R = shot.pose.get_rotation_matrix()
t = np.array(shot.pose.translation)
R[1], R[2] = -R[1], -R[2] # Reverse y and z
t[1], t[2] = -t[1], -t[2]
lines.append(" ".join(map(str, [focal, k1, k2])))
for i in range(3):
lines.append(" ".join(map(str, R[i])))
t = " ".join(map(str, t))
lines.append(t)
else:
for _ in range(5):
lines.append("0 0 0")
# tracks
for point in points.values():
coord = point.coordinates
color = list(map(int, point.color))
view_list = track_manager.get_track_observations(point.id)
lines.append(" ".join(map(str, coord)))
lines.append(" ".join(map(str, color)))
view_line = []
for shot_key, obs in view_list.items():
if shot_key in shots.keys():
v = obs.point
shot_index = shots_order[shot_key]
camera = shots[shot_key].camera
scale = max(camera.width, camera.height)
x = v[0] * scale
y = -v[1] * scale
view_line.append(" ".join(map(str, [shot_index, obs.id, x, y])))
lines.append(str(len(view_line)) + " " + " ".join(view_line))
bundle_file = os.path.join(
bundle_file_path, "bundle_r" + str(j).zfill(3) + ".out"
)
with io.open_wt(bundle_file) as fout:
fout.writelines("\n".join(lines) + "\n")
list_file = os.path.join(list_file_path, "list_r" + str(j).zfill(3) + ".out")
with io.open_wt(list_file) as fout:
fout.writelines("\n".join(map(str, image_list)))