184 lines
5.5 KiB
Python
184 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import tarfile
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import requests
|
|
from tqdm.auto import tqdm
|
|
|
|
import rerun as rr
|
|
from rerun import blueprint as rrb
|
|
|
|
DATA_DIR = Path(__file__).parent / "dataset"
|
|
|
|
DATASET_URL = "https://storage.googleapis.com/rerun-example-datasets/imu_signals/tum_vi_corridor4_512_16.tar"
|
|
DATASET_NAME = "dataset-corridor4_512_16"
|
|
XYZ_AXIS_NAMES = ["x", "y", "z"]
|
|
XYZ_AXIS_COLORS = [[(231, 76, 60), (39, 174, 96), (52, 120, 219)]]
|
|
|
|
|
|
def main() -> None:
|
|
dataset_path = DATA_DIR / DATASET_NAME
|
|
if not dataset_path.exists():
|
|
_download_dataset(DATA_DIR)
|
|
|
|
parser = argparse.ArgumentParser(description="Visualizes the TUM Visual-Inertial dataset using the Rerun SDK.")
|
|
parser.add_argument(
|
|
"--seconds",
|
|
type=float,
|
|
default=float("inf"),
|
|
help="If specified, limits the number of seconds logged",
|
|
)
|
|
rr.script_add_args(parser)
|
|
args = parser.parse_args()
|
|
|
|
blueprint = rrb.Horizontal(
|
|
rrb.Vertical(
|
|
rrb.TimeSeriesView(
|
|
origin="gyroscope",
|
|
name="Gyroscope",
|
|
overrides={"/gyroscope": rr.SeriesLines(names=XYZ_AXIS_NAMES, colors=XYZ_AXIS_COLORS)},
|
|
),
|
|
rrb.TimeSeriesView(
|
|
origin="accelerometer",
|
|
name="Accelerometer",
|
|
overrides={"/accelerometer": rr.SeriesLines(names=XYZ_AXIS_NAMES, colors=XYZ_AXIS_COLORS)},
|
|
),
|
|
),
|
|
rrb.Spatial3DView(origin="/", name="World position"),
|
|
column_shares=[0.45, 0.55],
|
|
)
|
|
|
|
rr.script_setup(args, "rerun_example_imu_signals", default_blueprint=blueprint)
|
|
|
|
_log_imu_data(args.seconds)
|
|
_log_image_data(args.seconds)
|
|
_log_gt_imu(args.seconds)
|
|
|
|
|
|
def _download_dataset(root: Path, dataset_url: str = DATASET_URL) -> None:
|
|
os.makedirs(root, exist_ok=True)
|
|
tar_path = os.path.join(root, f"{DATASET_NAME}.tar")
|
|
response = requests.get(dataset_url, stream=True)
|
|
|
|
total_size = int(response.headers.get("content-length", 0))
|
|
block_size = 1024
|
|
|
|
with (
|
|
tqdm(desc="Downloading dataset", total=total_size, unit="B", unit_scale=True) as pb,
|
|
open(tar_path, "wb") as file,
|
|
):
|
|
for data in response.iter_content(chunk_size=block_size):
|
|
pb.update(len(data))
|
|
file.write(data)
|
|
|
|
if total_size not in (0, pb.n):
|
|
raise RuntimeError("Failed to download complete dataset!")
|
|
|
|
print("Extracting dataset…")
|
|
with tarfile.open(tar_path, "r:") as tar:
|
|
tar.extractall(path=root)
|
|
os.remove(tar_path)
|
|
|
|
|
|
def _log_imu_data(max_time_sec: float) -> None:
|
|
imu_data = pd.read_csv(
|
|
DATA_DIR / DATASET_NAME / "dso/imu.txt",
|
|
sep=" ",
|
|
header=0,
|
|
names=["timestamp", "gyro.x", "gyro.y", "gyro.z", "accel.x", "accel.y", "accel.z"],
|
|
comment="#",
|
|
)
|
|
|
|
timestamps = imu_data["timestamp"].to_numpy()
|
|
max_time_ns = imu_data["timestamp"][0] + max_time_sec * 1e9
|
|
selected = imu_data[imu_data["timestamp"] <= max_time_ns]
|
|
|
|
timestamps = selected["timestamp"].astype("datetime64[ns]")
|
|
times = rr.TimeColumn("timestamp", timestamp=timestamps)
|
|
|
|
gyro = selected[["gyro.x", "gyro.y", "gyro.z"]].to_numpy()
|
|
rr.send_columns("/gyroscope", indexes=[times], columns=rr.Scalars.columns(scalars=gyro))
|
|
|
|
accel = selected[["accel.x", "accel.y", "accel.z"]]
|
|
rr.send_columns("/accelerometer", indexes=[times], columns=rr.Scalars.columns(scalars=accel))
|
|
|
|
|
|
def _log_image_data(max_time_sec: float) -> None:
|
|
times = pd.read_csv(
|
|
DATA_DIR / DATASET_NAME / "dso/cam0/times.txt",
|
|
sep=" ",
|
|
header=0,
|
|
names=["filename", "timestamp", "exposure_time"],
|
|
comment="#",
|
|
dtype={"filename": str},
|
|
)
|
|
|
|
rr.set_time("timestamp", timestamp=times["timestamp"][0])
|
|
rr.log(
|
|
"/world",
|
|
rr.Transform3D(rotation_axis_angle=rr.RotationAxisAngle(axis=(1, 0, 0), angle=-np.pi / 2)),
|
|
static=True,
|
|
)
|
|
rr.log(
|
|
"/world/cam0",
|
|
rr.Pinhole(
|
|
focal_length=(0.373 * 512, 0.373 * 512),
|
|
resolution=(512, 512),
|
|
image_plane_distance=0.4,
|
|
),
|
|
static=True,
|
|
)
|
|
|
|
max_time_sec = times["timestamp"][0] + max_time_sec
|
|
for _, (filename, timestamp, _) in times.iterrows():
|
|
if timestamp > max_time_sec:
|
|
break
|
|
|
|
image_path = DATA_DIR / DATASET_NAME / "dso/cam0/images" / f"{filename}.png"
|
|
rr.set_time("timestamp", timestamp=timestamp)
|
|
rr.log("/world/cam0/image", rr.EncodedImage(path=image_path))
|
|
|
|
|
|
def _log_gt_imu(max_time_sec: float) -> None:
|
|
gt_imu = pd.read_csv(
|
|
DATA_DIR / DATASET_NAME / "dso/gt_imu.csv",
|
|
sep=",",
|
|
header=0,
|
|
names=["timestamp", "t.x", "t.y", "t.z", "q.w", "q.x", "q.y", "q.z"],
|
|
comment="#",
|
|
)
|
|
|
|
timestamps = gt_imu["timestamp"].to_numpy()
|
|
max_time_ns = gt_imu["timestamp"][0] + max_time_sec * 1e9
|
|
selected = gt_imu[gt_imu["timestamp"] <= max_time_ns]
|
|
|
|
timestamps = selected["timestamp"].astype("datetime64[ns]")
|
|
times = rr.TimeColumn("timestamp", timestamp=timestamps)
|
|
|
|
translations = selected[["t.x", "t.y", "t.z"]]
|
|
quaternions = selected[
|
|
[
|
|
"q.x",
|
|
"q.y",
|
|
"q.z",
|
|
"q.w",
|
|
]
|
|
]
|
|
rr.send_columns(
|
|
"/world/cam0",
|
|
indexes=[times],
|
|
columns=rr.Transform3D.columns(
|
|
translation=translations,
|
|
quaternion=quaternions,
|
|
),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|