1
1
from __future__ import annotations
2
2
3
+ import argparse
3
4
import os
4
- import pathlib
5
5
import tarfile
6
+ from pathlib import Path
6
7
8
+ import numpy as np
7
9
import pandas as pd
8
10
import requests
9
11
import rerun as rr
10
12
from rerun import blueprint as rrb
13
+ from tqdm .auto import tqdm
11
14
12
- cwd = pathlib . Path (__file__ ).parent . resolve ()
15
+ DATA_DIR = Path (__file__ ).parent / "dataset"
13
16
14
- DATASET_URL = "https://vision.in.tum.de/tumvi/exported/euroc/512_16/dataset-corridor4_512_16 .tar"
17
+ DATASET_URL = "https://storage.googleapis.com/rerun-example-datasets/imu_signals/tum_vi_corridor4_512_16 .tar"
15
18
DATASET_NAME = "dataset-corridor4_512_16"
16
19
XYZ_AXIS_NAMES = ["x" , "y" , "z" ]
17
20
XYZ_AXIS_COLORS = [[(231 , 76 , 60 ), (39 , 174 , 96 ), (52 , 120 , 219 )]]
18
21
19
22
20
23
def main () -> None :
21
- dataset_path = cwd / DATASET_NAME
24
+ dataset_path = DATA_DIR / DATASET_NAME
22
25
if not dataset_path .exists ():
23
- _download_dataset (cwd )
26
+ _download_dataset (DATA_DIR )
27
+
28
+ parser = argparse .ArgumentParser (description = "Visualizes the TUM Visual-Inertial dataset using the Rerun SDK." )
29
+ parser .add_argument (
30
+ "--seconds" ,
31
+ type = float ,
32
+ default = float ("inf" ),
33
+ help = "If specified, limits the number of seconds logged" ,
34
+ )
35
+ rr .script_add_args (parser )
36
+ args = parser .parse_args ()
37
+
38
+ blueprint = rrb .Horizontal (
39
+ rrb .Vertical (
40
+ rrb .TimeSeriesView (
41
+ origin = "gyroscope" ,
42
+ name = "Gyroscope" ,
43
+ overrides = {
44
+ # TODO(#9022): Pluralize series line type.
45
+ "/gyroscope" : rr .SeriesLine .from_fields (name = XYZ_AXIS_NAMES , color = XYZ_AXIS_COLORS ), # type: ignore[arg-type]
46
+ },
47
+ ),
48
+ rrb .TimeSeriesView (
49
+ origin = "accelerometer" ,
50
+ name = "Accelerometer" ,
51
+ overrides = {
52
+ # TODO(#9022): Pluralize series line type.
53
+ "/accelerometer" : rr .SeriesLine .from_fields (name = XYZ_AXIS_NAMES , color = XYZ_AXIS_COLORS ), # type: ignore[arg-type]
54
+ },
55
+ ),
56
+ ),
57
+ rrb .Spatial3DView (origin = "/" , name = "World position" ),
58
+ column_shares = [0.45 , 0.55 ],
59
+ )
60
+
61
+ rr .script_setup (args , "rerun_example_imu_signals" , default_blueprint = blueprint )
24
62
25
- _setup_rerun ()
26
- _log_imu_data ()
27
- _log_image_data ()
28
- _log_gt_imu ()
63
+ _log_imu_data (args .seconds )
64
+ _log_image_data (args .seconds )
65
+ _log_gt_imu (args .seconds )
29
66
30
67
31
- def _download_dataset (root : pathlib . Path , dataset_url : str = DATASET_URL ) -> None :
68
+ def _download_dataset (root : Path , dataset_url : str = DATASET_URL ) -> None :
32
69
os .makedirs (root , exist_ok = True )
33
- tar_path = os .path .join (root , "dataset-corridor4_512_16.tar" )
34
- print ("Downloading dataset..." )
35
- with requests .get (dataset_url , stream = True ) as r :
36
- r .raise_for_status ()
37
- with open (tar_path , "wb" ) as f :
38
- for chunk in r .iter_content (chunk_size = 8192 ):
39
- if chunk :
40
- f .write (chunk )
70
+ tar_path = os .path .join (root , f"{ DATASET_NAME } .tar" )
71
+ response = requests .get (dataset_url , stream = True )
72
+
73
+ total_size = int (response .headers .get ("content-length" , 0 ))
74
+ block_size = 1024
75
+
76
+ with tqdm (desc = "Downloading dataset" , total = total_size , unit = "B" , unit_scale = True ) as pb :
77
+ with open (tar_path , "wb" ) as file :
78
+ for data in response .iter_content (chunk_size = block_size ):
79
+ pb .update (len (data ))
80
+ file .write (data )
81
+
82
+ if total_size != 0 and pb .n != total_size :
83
+ raise RuntimeError ("Failed to download complete dataset!" )
84
+
41
85
print ("Extracting dataset..." )
42
86
with tarfile .open (tar_path , "r:" ) as tar :
43
87
tar .extractall (path = root )
44
88
os .remove (tar_path )
45
89
46
90
47
- def _setup_rerun () -> None :
48
- rr .init ("rerun_example_imu_data" , spawn = True )
49
-
50
- rr .send_blueprint (
51
- rrb .Horizontal (
52
- rrb .Vertical (
53
- rrb .TimeSeriesView (
54
- origin = "gyroscope" ,
55
- name = "Gyroscope" ,
56
- overrides = {
57
- # TODO(#9022): Pluralize series line type.
58
- "/gyroscope" : rr .SeriesLine .from_fields (name = XYZ_AXIS_NAMES , color = XYZ_AXIS_COLORS ), # type: ignore[arg-type]
59
- },
60
- ),
61
- rrb .TimeSeriesView (
62
- origin = "accelerometer" ,
63
- name = "Accelerometer" ,
64
- overrides = {
65
- # TODO(#9022): Pluralize series line type.
66
- "/accelerometer" : rr .SeriesLine .from_fields (name = XYZ_AXIS_NAMES , color = XYZ_AXIS_COLORS ), # type: ignore[arg-type]
67
- },
68
- ),
69
- ),
70
- rrb .Spatial3DView (origin = "/" , name = "World position" ),
71
- column_shares = [0.45 , 0.55 ],
72
- ),
73
- )
74
-
75
-
76
- def _log_imu_data () -> None :
91
+ def _log_imu_data (max_time_sec : float ) -> None :
77
92
imu_data = pd .read_csv (
78
- cwd / DATASET_NAME / "dso/imu.txt" ,
93
+ DATA_DIR / DATASET_NAME / "dso/imu.txt" ,
79
94
sep = " " ,
80
95
header = 0 ,
81
96
names = ["timestamp" , "gyro.x" , "gyro.y" , "gyro.z" , "accel.x" , "accel.y" , "accel.z" ],
82
97
comment = "#" ,
83
98
)
84
99
85
- times = rr .TimeColumn ("timestamp" , timestamp = imu_data ["timestamp" ])
100
+ timestamps = imu_data ["timestamp" ].to_numpy ()
101
+ max_time_ns = imu_data ["timestamp" ][0 ] + max_time_sec * 1e9
102
+ selected = imu_data [imu_data ["timestamp" ] <= max_time_ns ]
103
+
104
+ timestamps = selected ["timestamp" ].astype ("datetime64[ns]" )
105
+ times = rr .TimeColumn ("timestamp" , timestamp = timestamps )
86
106
87
- gyro = imu_data [["gyro.x" , "gyro.y" , "gyro.z" ]]
107
+ gyro = selected [["gyro.x" , "gyro.y" , "gyro.z" ]]. to_numpy ()
88
108
rr .send_columns ("/gyroscope" , indexes = [times ], columns = rr .Scalar .columns (scalar = gyro ))
89
109
90
- accel = imu_data [["accel.x" , "accel.y" , "accel.z" ]]
110
+ accel = selected [["accel.x" , "accel.y" , "accel.z" ]]
91
111
rr .send_columns ("/accelerometer" , indexes = [times ], columns = rr .Scalar .columns (scalar = accel ))
92
112
93
113
94
- def _log_image_data () -> None :
114
+ def _log_image_data (max_time_sec : float ) -> None :
95
115
times = pd .read_csv (
96
- cwd / DATASET_NAME / "dso/cam0/times.txt" ,
116
+ DATA_DIR / DATASET_NAME / "dso/cam0/times.txt" ,
97
117
sep = " " ,
98
118
header = 0 ,
99
119
names = ["filename" , "timestamp" , "exposure_time" ],
@@ -103,35 +123,48 @@ def _log_image_data() -> None:
103
123
104
124
rr .set_time ("timestamp" , timestamp = times ["timestamp" ][0 ])
105
125
rr .log (
106
- "/cam0" ,
126
+ "/world" ,
127
+ rr .Transform3D (rotation_axis_angle = rr .RotationAxisAngle (axis = (1 , 0 , 0 ), angle = - np .pi / 2 )),
128
+ static = True ,
129
+ )
130
+ rr .log (
131
+ "/world/cam0" ,
107
132
rr .Pinhole (
108
133
focal_length = (0.373 * 512 , 0.373 * 512 ),
109
134
resolution = (512 , 512 ),
110
- camera_xyz = rr .components .ViewCoordinates .FLU ,
111
135
image_plane_distance = 0.4 ,
112
136
),
113
137
static = True ,
114
138
)
115
139
140
+ max_time_sec = times ["timestamp" ][0 ] + max_time_sec
116
141
for _ , (filename , timestamp , _ ) in times .iterrows ():
117
- image_path = cwd / DATASET_NAME / "dso/cam0/images" / f"{ filename } .png"
142
+ if timestamp > max_time_sec :
143
+ break
144
+
145
+ image_path = DATA_DIR / DATASET_NAME / "dso/cam0/images" / f"{ filename } .png"
118
146
rr .set_time ("timestamp" , timestamp = timestamp )
119
- rr .log ("/cam0/image" , rr .ImageEncoded (path = image_path ))
147
+ rr .log ("/world/ cam0/image" , rr .EncodedImage (path = image_path ))
120
148
121
149
122
- def _log_gt_imu () -> None :
150
+ def _log_gt_imu (max_time_sec : float ) -> None :
123
151
gt_imu = pd .read_csv (
124
- cwd / DATASET_NAME / "dso/gt_imu.csv" ,
152
+ DATA_DIR / DATASET_NAME / "dso/gt_imu.csv" ,
125
153
sep = "," ,
126
154
header = 0 ,
127
155
names = ["timestamp" , "t.x" , "t.y" , "t.z" , "q.w" , "q.x" , "q.y" , "q.z" ],
128
156
comment = "#" ,
129
157
)
130
158
131
- times = rr .TimeColumn ("timestamp" , timestamp = gt_imu ["timestamp" ])
159
+ timestamps = gt_imu ["timestamp" ].to_numpy ()
160
+ max_time_ns = gt_imu ["timestamp" ][0 ] + max_time_sec * 1e9
161
+ selected = gt_imu [gt_imu ["timestamp" ] <= max_time_ns ]
132
162
133
- translations = gt_imu [["t.x" , "t.y" , "t.z" ]]
134
- quaternions = gt_imu [
163
+ timestamps = selected ["timestamp" ].astype ("datetime64[ns]" )
164
+ times = rr .TimeColumn ("timestamp" , timestamp = timestamps )
165
+
166
+ translations = selected [["t.x" , "t.y" , "t.z" ]]
167
+ quaternions = selected [
135
168
[
136
169
"q.x" ,
137
170
"q.y" ,
@@ -140,9 +173,12 @@ def _log_gt_imu() -> None:
140
173
]
141
174
]
142
175
rr .send_columns (
143
- "/cam0" ,
176
+ "/world/ cam0" ,
144
177
indexes = [times ],
145
- columns = rr .Transform3D .columns (translation = translations , quaternion = quaternions ),
178
+ columns = rr .Transform3D .columns (
179
+ translation = translations ,
180
+ quaternion = quaternions ,
181
+ ),
146
182
)
147
183
148
184
0 commit comments