Skip to content
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

Apply Umeyama alignment on a time window #714

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions evo/core/trajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,84 @@ def speeds(self) -> np.ndarray:
for i in range(len(self.positions_xyz) - 1)
])

def align_on_window(self, traj_ref: 'PoseTrajectory3D', correct_scale: bool = False,
correct_only_scale: bool = False, n: int = -1,
start_time: typing.Optional[float] = None,
end_time: typing.Optional[float] = None) -> geometry.UmeyamaResult:
"""
align to a reference trajectory using Umeyama alignment
:param traj_ref: reference trajectory
:param correct_scale: set to True to adjust also the scale
:param correct_only_scale: set to True to correct the scale, but not the pose
:param n: the number of poses to use, counted from the start (default: all)
:param start_time: the time to start the Umeyama alignment window
(default: start of the trajectory)
:param end_time: the time to end the Umeyama alignment window
(default: end of the trajectory)
:return: the result parameters of the Umeyama algorithm
"""
if start_time is None and end_time is None:
return self.align(traj_ref, correct_scale, correct_only_scale, n)

if n != -1:
# Cannot have start_time not None or end_time not None, and n != 1
raise TrajectoryException("start_time or end_time with n is not implemented")

with_scale = correct_scale or correct_only_scale
if correct_only_scale:
logger.debug("Correcting scale...")
else:
logger.debug(f"Aligning using Umeyama's method... "
f"{'(with scale correction)' if with_scale else ''}")

relative_timestamps = self.timestamps - np.min(self.timestamps)

if start_time is None:
start_index = 0
elif np.all(relative_timestamps < start_time):
logger.warning(f"Align start time ({start_time}s) is after end of trajectory"
f" ({np.max(relative_timestamps)}s), ignoring start time")
start_index = 0
else:
# Find first value that is less or equal to start_time
start_index = np.flatnonzero(start_time <= relative_timestamps)[0]
logger.debug(f"Start of alignment: in reference {traj_ref.timestamps[start_index]}s, "
f"in trajectory {self.timestamps[start_index]}s")

if end_time is None:
end_index = self.positions_xyz.shape[0]
elif np.all(relative_timestamps < end_time):
logger.warning(f"Align end time ({end_time}s) is after end of trajectory "
f"({np.max(relative_timestamps)}s), ignoring end time")
end_index = self.timestamps.shape[0]
else:
# Find first value that is greater or equal to end_time
end_index = np.flatnonzero(end_time <= relative_timestamps)[0]
logger.debug(f"End of alignment: in reference {traj_ref.timestamps[end_index]}s, "
f"in trajectory {self.timestamps[end_index]}s")

if end_index <= start_index:
raise TrajectoryException("alignment is empty")

r_a, t_a, s = geometry.umeyama_alignment(self.positions_xyz[start_index:end_index, :].T,
traj_ref.positions_xyz[start_index:end_index, :].T,
with_scale)

if not correct_only_scale:
logger.debug(f"Rotation of alignment:\n{r_a}"
f"\nTranslation of alignment:\n{t_a}")
logger.debug(f"Scale correction: {s}")

if correct_only_scale:
self.scale(s)
elif correct_scale:
self.scale(s)
self.transform(lie.se3(r_a, t_a))
else:
self.transform(lie.se3(r_a, t_a))

return r_a, t_a, s

def reduce_to_ids(
self, ids: typing.Union[typing.Sequence[int], np.ndarray]) -> None:
super(PoseTrajectory3D, self).reduce_to_ids(ids)
Expand Down
9 changes: 7 additions & 2 deletions evo/main_traj.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ def run(args):

if args.n_to_align != -1 and not (args.align or args.correct_scale):
die("--n_to_align is useless without --align or/and --correct_scale")
if args.n_to_align != -1 and (args.start_t_to_align is not None or args.end_t_to_align is not None):
die("--start_t_to_align or --end_t_to_align with --n_to_align is not implemented")
if (args.start_t_to_align is not None or args.end_t_to_align is not None) and not (args.align or args.correct_scale):
die("--start_t_to_align and --end_t_to_align are useless without --align or/and --correct_scale")

# TODO: this is fugly, but is a quick solution for remembering each synced
# reference when plotting pose correspondences later...
Expand All @@ -257,10 +261,11 @@ def run(args):
if args.align or args.correct_scale:
logger.debug(SEP)
logger.debug("Aligning {} to reference.".format(name))
trajectories[name].align(
trajectories[name].align_on_window(
ref_traj_tmp, correct_scale=args.correct_scale,
correct_only_scale=args.correct_scale and not args.align,
n=args.n_to_align)
n=args.n_to_align, start_time=args.start_t_to_align,
end_time=args.end_t_to_align)
if args.align_origin:
logger.debug(SEP)
logger.debug("Aligning {}'s origin to reference.".format(name))
Expand Down
8 changes: 8 additions & 0 deletions evo/main_traj_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ def parser() -> argparse.ArgumentParser:
"--n_to_align",
help="the number of poses to use for Umeyama alignment, "
"counted from the start (default: all)", default=-1, type=int)
algo_opts.add_argument(
"--start_t_to_align",
help="the start of the time window to use for Umeyama alignment, "
"in seconds relative to the first timestamp of the file", default=None, type=float)
algo_opts.add_argument(
"--end_t_to_align",
help="the end of the time window to use for Umeyama alignment, "
"in seconds relative to the first timestamp of the file", default=None, type=float)
algo_opts.add_argument(
"--sync",
help="associate trajectories via matching timestamps - requires --ref",
Expand Down
Loading