Skip to content

Commit d5ad83a

Browse files
Kin-Zhanglisiyi777
andauthored
Refactor Open3D Visualizer and Add Utilities for Method Comparison (#28)
* chore(visualization): refactor the open3d visualization, merge fn together. * Reset fire from function to class usage directly. * add save screenshot easily with multi-view. * sync viewpoint through diff windows. * visual lidar center tf if set slc to True. * fix(flow): add index_flow for 2hz gt view etc. --------- Co-authored-by: lisiyi777 <lisiyi777@users.noreply.github.com>
1 parent a3467bb commit d5ad83a

File tree

7 files changed

+783
-380
lines changed

7 files changed

+783
-380
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ pip install "evalai"
301301
evalai set-token <your token>
302302

303303
# Step 3: Copy the command pop above and submit to leaderboard
304-
evalai challenge 2010 phase 4018 submit --file av2_submit.zip --large --private
304+
# evalai challenge 2010 phase 4018 submit --file av2_submit.zip --large --private
305305
evalai challenge 2210 phase 4396 submit --file av2_submit_v2.zip --large --private
306306
```
307307

@@ -318,29 +318,31 @@ python eval.py model=nsfp dataset_path=/home/kin/data/av2/h5py/demo/val
318318
# The output of above command will be like:
319319
Model: DeFlow, Checkpoint from: /home/kin/model_zoo/v2/seflow_best.ckpt
320320
We already write the flow_est into the dataset, please run following commend to visualize the flow. Copy and paste it to your terminal:
321-
python tools/visualization.py --res_name 'seflow_best' --data_dir /home/kin/data/av2/preprocess_v2/sensor/vis
321+
python tools/visualization.py vis --res_name 'seflow_best' --data_dir /home/kin/data/av2/preprocess_v2/sensor/vis
322322
Enjoy! ^v^ ------
323323

324324
# Then run the command in the terminal:
325-
python tools/visualization.py --res_name 'seflow_best' --data_dir /home/kin/data/av2/preprocess_v2/sensor/vis
325+
python tools/visualization.py vis --res_name 'seflow_best' --data_dir /home/kin/data/av2/preprocess_v2/sensor/vis
326326
```
327327

328328
https://github.com/user-attachments/assets/f031d1a2-2d2f-4947-a01f-834ed1c146e6
329329

330330
For exporting easy comparsion with ground truth and other methods, we also provided multi-visulization open3d window:
331331
```bash
332-
python tools/visualization.py --mode mul --res_name "['flow', 'seflow_best']" --data_dir /home/kin/data/av2/preprocess_v2/sensor/vis
332+
python tools/visualization.py vis --res_name "['flow', 'seflow_best']" --data_dir /home/kin/data/av2/preprocess_v2/sensor/vis
333333
```
334334

335-
Or another way to interact with [rerun](https://github.com/rerun-io/rerun) but please only vis scene by scene, not all at once.
335+
**Tips**: To quickly create qualitative results for all methods, you can use multiple results comparison mode, select a good viewpoint and then save screenshots for all frames by pressing `P` key. You will found all methods' results are saved in the output folder (default is `logs/imgs`). Enjoy it!
336+
337+
338+
_Rerun_: Another way to interact with [rerun](https://github.com/rerun-io/rerun) but please only vis scene by scene, not all at once.
336339

337340
```bash
338341
python tools/visualization_rerun.py --data_dir /home/kin/data/av2/h5py/demo/train --res_name "['flow', 'deflow']"
339342
```
340343

341344
https://github.com/user-attachments/assets/07e8d430-a867-42b7-900a-11755949de21
342345

343-
344346
## Cite Us
345347

346348
[*OpenSceneFlow*](https://github.com/KTH-RPL/OpenSceneFlow) is originally designed by [Qingwen Zhang](https://kin-zhang.github.io/) from DeFlow and SeFlow.

src/dataset.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class HDF5Dataset(Dataset):
187187
def __init__(self, directory, \
188188
transform=None, n_frames=2, ssl_label=None, \
189189
eval = False, leaderboard_version=1, \
190-
vis_name=''):
190+
vis_name='', index_flow=False):
191191
'''
192192
Args:
193193
directory: the directory of the dataset, the folder should contain some .h5 file and index_total.pkl.
@@ -199,6 +199,7 @@ def __init__(self, directory, \
199199
* eval: if True, use the eval index (only used it for leaderboard evaluation)
200200
* leaderboard_version: 1st or 2nd, default is 1. If '2', we will use the index_eval_v2.pkl from assets/docs.
201201
* vis_name: the data of the visualization, default is ''.
202+
* index_flow: if True, use the flow index for training or visualization.
202203
'''
203204
super(HDF5Dataset, self).__init__()
204205
self.directory = directory
@@ -247,7 +248,7 @@ def __init__(self, directory, \
247248

248249
# for some dataset that annotated HZ is different.... like truckscene and nuscene etc.
249250
self.train_index = None
250-
if not eval and ssl_label is None and transform is not None: # transform indicates whether we are in training mode.
251+
if (not eval and ssl_label is None and transform is not None) or index_flow: # transform indicates whether we are in training mode.
251252
# check if train seq all have gt.
252253
one_scene_id = list(self.scene_id_bounds.keys())[0]
253254
check_flow_exist = True
@@ -347,7 +348,7 @@ def __getitem__(self, index_):
347348
data_dict[f'gmh{i+1}'] = past_gm
348349
data_dict[f'poseh{i+1}'] = past_pose
349350

350-
for data_key in self.vis_name + ['ego_motion', 'lidar_dt',
351+
for data_key in self.vis_name + ['ego_motion', 'lidar_dt', 'lidar_center',
351352
# ground truth information:
352353
'flow', 'flow_is_valid', 'flow_category_indices', 'flow_instance_id', 'dufo']:
353354
if data_key in f[key]:

src/trainer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ def on_validation_epoch_end(self):
243243
with open(str(self.save_res_path)+'.pkl', 'wb') as f:
244244
pickle.dump((self.metrics.epe_3way, self.metrics.bucketed, self.metrics.epe_ssf), f)
245245
print(f"We already write the {self.res_name} into the dataset, please run following commend to visualize the flow. Copy and paste it to your terminal:")
246-
print(f"python tools/visualization.py --res_name '{self.res_name}' --data_dir {self.dataset_path}")
246+
print(f"python tools/visualization.py vis --res_name '{self.res_name}' --data_dir {self.dataset_path}")
247247
print(f"Enjoy! ^v^ ------ \n")
248248

249249
self.metrics = OfficialMetrics()

src/utils/mics.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,78 @@ def make_colorwheel(transitions: tuple=DEFAULT_TRANSITIONS) -> np.ndarray:
172172
return colorwheel
173173

174174

175+
def error_to_color(
176+
error_magnitude: np.ndarray,
177+
max_error: float,
178+
color_map: str = "jet"
179+
) -> np.ndarray:
180+
"""
181+
Convert flow error to RGB color visualization.
182+
Args:
183+
color_map: Color map to use for visualization ("jet" recommended for error visualization)
184+
185+
Returns:
186+
RGB color representation of the error of shape (..., 3)
187+
"""
188+
if max_error > 0:
189+
normalized_error = np.clip(error_magnitude / max_error, 0, 1)
190+
else:
191+
normalized_error = np.zeros_like(error_magnitude)
192+
193+
# Create colormap
194+
if color_map == "jet":
195+
# Simple jet colormap implementation
196+
colors = np.zeros((*normalized_error.shape, 3), dtype=np.uint8)
197+
198+
# Blue to cyan to green to yellow to red
199+
# Blue (low error)
200+
idx = normalized_error < 0.25
201+
colors[idx, 2] = 255
202+
colors[idx, 0] = 0
203+
colors[idx, 1] = np.uint8(255 * normalized_error[idx] * 4)
204+
205+
# Cyan to green
206+
idx = (normalized_error >= 0.25) & (normalized_error < 0.5)
207+
colors[idx, 1] = 255
208+
colors[idx, 0] = 0
209+
colors[idx, 2] = np.uint8(255 * (1 - (normalized_error[idx] - 0.25) * 4))
210+
211+
# Green to yellow
212+
idx = (normalized_error >= 0.5) & (normalized_error < 0.75)
213+
colors[idx, 1] = 255
214+
colors[idx, 2] = 0
215+
colors[idx, 0] = np.uint8(255 * (normalized_error[idx] - 0.5) * 4)
216+
217+
# Yellow to red (high error)
218+
idx = normalized_error >= 0.75
219+
colors[idx, 0] = 255
220+
colors[idx, 2] = 0
221+
colors[idx, 1] = np.uint8(255 * (1 - (normalized_error[idx] - 0.75) * 4))
222+
223+
elif color_map == "hot":
224+
# Hot colormap: black -> red -> yellow -> white
225+
colors = np.zeros((*normalized_error.shape, 3), dtype=np.uint8)
226+
227+
# Black to red
228+
idx = normalized_error < 0.33
229+
colors[idx, 0] = np.uint8(255 * normalized_error[idx] * 3)
230+
231+
# Red to yellow
232+
idx = (normalized_error >= 0.33) & (normalized_error < 0.67)
233+
colors[idx, 0] = 255
234+
colors[idx, 1] = np.uint8(255 * (normalized_error[idx] - 0.33) * 3)
235+
236+
# Yellow to white
237+
idx = normalized_error >= 0.67
238+
colors[idx, 0] = 255
239+
colors[idx, 1] = 255
240+
colors[idx, 2] = np.uint8(255 * (normalized_error[idx] - 0.67) * 3)
241+
242+
else:
243+
raise ValueError(f"Unsupported color map: {color_map}. Use 'jet' or 'hot'.")
244+
245+
return colors
246+
175247
def flow_to_rgb(
176248
flow: np.ndarray,
177249
flow_max_radius: Optional[float]=None,

0 commit comments

Comments
 (0)