format and refactor the SFM BAL example
parent
a634a91c1a
commit
1bcb44784a
|
@ -7,49 +7,58 @@
|
|||
See LICENSE for the license information
|
||||
|
||||
Solve a structure-from-motion problem from a "Bundle Adjustment in the Large" file
|
||||
Author: Frank Dellaert (Python: Akshay Krishnan, John Lambert)
|
||||
Author: Frank Dellaert (Python: Akshay Krishnan, John Lambert, Varun Agrawal)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
import gtsam
|
||||
from gtsam import (
|
||||
GeneralSFMFactorCal3Bundler,
|
||||
PinholeCameraCal3Bundler,
|
||||
PriorFactorPinholeCameraCal3Bundler,
|
||||
readBal,
|
||||
symbol_shorthand
|
||||
)
|
||||
from gtsam import (GeneralSFMFactorCal3Bundler,
|
||||
PriorFactorPinholeCameraCal3Bundler, PriorFactorPoint3,
|
||||
readBal)
|
||||
from gtsam.symbol_shorthand import C, P
|
||||
from gtsam.utils.plot import plot_3d_points, plot_trajectory
|
||||
|
||||
C = symbol_shorthand.C
|
||||
P = symbol_shorthand.P
|
||||
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
||||
|
||||
|
||||
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
||||
def plot(scene_data: gtsam.SfmData, result: gtsam.Values):
|
||||
"""Plot the trajectory."""
|
||||
plot_vals = gtsam.Values()
|
||||
for cam_idx in range(scene_data.number_cameras()):
|
||||
plot_vals.insert(C(cam_idx),
|
||||
result.atPinholeCameraCal3Bundler(C(cam_idx)).pose())
|
||||
for t_idx in range(scene_data.number_tracks()):
|
||||
plot_vals.insert(P(t_idx), result.atPoint3(P(t_idx)))
|
||||
|
||||
def run(args):
|
||||
plot_3d_points(0, plot_vals, linespec="g.")
|
||||
plot_trajectory(0, plot_vals, show=True)
|
||||
|
||||
|
||||
def run(args: argparse.Namespace):
|
||||
""" Run LM optimization with BAL input data and report resulting error """
|
||||
input_file = gtsam.findExampleDataFile(args.input_file)
|
||||
if args.input_file:
|
||||
input_file = args.input_file
|
||||
else:
|
||||
input_file = gtsam.findExampleDataFile("dubrovnik-3-7-pre")
|
||||
|
||||
# Load the SfM data from file
|
||||
scene_data = readBal(input_file)
|
||||
logging.info(f"read {scene_data.number_tracks()} tracks on {scene_data.number_cameras()} cameras\n")
|
||||
logging.info("read %d tracks on %d cameras\n", scene_data.number_tracks(),
|
||||
scene_data.number_cameras())
|
||||
|
||||
# Create a factor graph
|
||||
graph = gtsam.NonlinearFactorGraph()
|
||||
|
||||
# We share *one* noiseModel between all projection factors
|
||||
noise = gtsam.noiseModel.Isotropic.Sigma(2, 1.0) # one pixel in u and v
|
||||
noise = gtsam.noiseModel.Isotropic.Sigma(2, 1.0) # one pixel in u and v
|
||||
|
||||
# Add measurements to the factor graph
|
||||
j = 0
|
||||
for t_idx in range(scene_data.number_tracks()):
|
||||
track = scene_data.track(t_idx) # SfmTrack
|
||||
track = scene_data.track(t_idx) # SfmTrack
|
||||
# retrieve the SfmMeasurement objects
|
||||
for m_idx in range(track.number_measurements()):
|
||||
# i represents the camera index, and uv is the 2d measurement
|
||||
|
@ -60,20 +69,18 @@ def run(args):
|
|||
|
||||
# Add a prior on pose x1. This indirectly specifies where the origin is.
|
||||
graph.push_back(
|
||||
gtsam.PriorFactorPinholeCameraCal3Bundler(
|
||||
C(0), scene_data.camera(0), gtsam.noiseModel.Isotropic.Sigma(9, 0.1)
|
||||
)
|
||||
)
|
||||
PriorFactorPinholeCameraCal3Bundler(
|
||||
C(0), scene_data.camera(0),
|
||||
gtsam.noiseModel.Isotropic.Sigma(9, 0.1)))
|
||||
# Also add a prior on the position of the first landmark to fix the scale
|
||||
graph.push_back(
|
||||
gtsam.PriorFactorPoint3(
|
||||
P(0), scene_data.track(0).point3(), gtsam.noiseModel.Isotropic.Sigma(3, 0.1)
|
||||
)
|
||||
)
|
||||
PriorFactorPoint3(P(0),
|
||||
scene_data.track(0).point3(),
|
||||
gtsam.noiseModel.Isotropic.Sigma(3, 0.1)))
|
||||
|
||||
# Create initial estimate
|
||||
initial = gtsam.Values()
|
||||
|
||||
|
||||
i = 0
|
||||
# add each PinholeCameraCal3Bundler
|
||||
for cam_idx in range(scene_data.number_cameras()):
|
||||
|
@ -81,12 +88,10 @@ def run(args):
|
|||
initial.insert(C(i), camera)
|
||||
i += 1
|
||||
|
||||
j = 0
|
||||
# add each SfmTrack
|
||||
for t_idx in range(scene_data.number_tracks()):
|
||||
track = scene_data.track(t_idx)
|
||||
initial.insert(P(j), track.point3())
|
||||
j += 1
|
||||
track = scene_data.track(t_idx)
|
||||
initial.insert(P(t_idx), track.point3())
|
||||
|
||||
# Optimize the graph and print results
|
||||
try:
|
||||
|
@ -94,25 +99,31 @@ def run(args):
|
|||
params.setVerbosityLM("ERROR")
|
||||
lm = gtsam.LevenbergMarquardtOptimizer(graph, initial, params)
|
||||
result = lm.optimize()
|
||||
except Exception as e:
|
||||
except RuntimeError:
|
||||
logging.exception("LM Optimization failed")
|
||||
return
|
||||
|
||||
# Error drops from ~2764.22 to ~0.046
|
||||
logging.info(f"final error: {graph.error(result)}")
|
||||
logging.info("initial error: %f", graph.error(initial))
|
||||
logging.info("final error: %f", graph.error(result))
|
||||
|
||||
plot(scene_data, result)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main runner."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-i',
|
||||
'--input_file',
|
||||
type=str,
|
||||
default="",
|
||||
help="""Read SFM data from the specified BAL file.
|
||||
The data format is described here: https://grail.cs.washington.edu/projects/bal/.
|
||||
BAL files contain (nrPoses, nrPoints, nrObservations), followed by (i,j,u,v) tuples,
|
||||
then (wx,wy,wz,tx,ty,tz,f,k1,k1) as Bundler camera calibrations w/ Rodrigues vector
|
||||
and (x,y,z) 3d point initializations.""")
|
||||
run(parser.parse_args())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'-i',
|
||||
'--input_file',
|
||||
type=str,
|
||||
default="dubrovnik-3-7-pre",
|
||||
help='Read SFM data from the specified BAL file'
|
||||
'The data format is described here: https://grail.cs.washington.edu/projects/bal/.'
|
||||
'BAL files contain (nrPoses, nrPoints, nrObservations), followed by (i,j,u,v) tuples, '
|
||||
'then (wx,wy,wz,tx,ty,tz,f,k1,k1) as Bundler camera calibrations w/ Rodrigues vector'
|
||||
'and (x,y,z) 3d point initializations.'
|
||||
)
|
||||
run(parser.parse_args())
|
||||
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue