Skip to content

Commit 0baf3cf

Browse files
committed
v053 release
1 parent 8060e62 commit 0baf3cf

File tree

4 files changed

+173
-50
lines changed

4 files changed

+173
-50
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@ For further details, we refer to the usage in our [EMSANet repository](https://g
238238
The dataset can be used as an iterator (detectron2 usually does this) and can then be mapped with the custom mappers to generate the correct layout of the data.
239239

240240
## Changelog
241+
**Version 0.5.3 (Mar 31, 2023)**
242+
- *no dataset preparation related changes*
243+
- minor changes to `nicr_sa_prepare_labeled_point_clouds` and `nicr_sa_labeled_pc_viewer`
244+
241245
**Version 0.5.2 (Mar 28, 2023)**
242246
- hypersim: change instance encoding: do not view G and B channel as uint16 use bit shifting instead
243247
- add new scripts and update entry points:

nicr_scene_analysis_datasets/scripts/prepare_labeled_point_clouds.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def _parse_args():
8282
help="Whether to map semantic classes to benchmark format."
8383
)
8484
parser.add_argument(
85-
'--write-scannet-label',
85+
'--write-scannet-benchmark-ground-truth',
8686
action="store_true",
8787
help="If set, ground-truth label files that can be used for ScanNet "
8888
"benchmarking scripts will be written."
@@ -209,9 +209,9 @@ def main():
209209
args = _parse_args()
210210

211211
# base output directories
212-
output_path = osp.join(args.output_path, args.dataset + '_ply', args.split)
213-
scannet_output_path = osp.join(args.output_path, args.dataset + '_gt',
214-
'3d', args.split)
212+
output_path = osp.join(args.output_path, args.split, 'ply')
213+
scannet_output_path = osp.join(args.output_path, args.split,
214+
'scannet_benchmark_gt')
215215

216216
# helper function to load dataset
217217
def load_dataset(sample_keys):
@@ -320,7 +320,7 @@ def load_dataset(sample_keys):
320320
_save_ply(fp, filtered_pc, filtered_attributes, shift=args.shift)
321321

322322
# write ScanNet ground-truth label files
323-
if args.write_scannet_label:
323+
if args.write_scannet_benchmark_ground_truth:
324324
filename = '_'.join(last_identifier[:-1]) + '.txt'
325325

326326
# pure semantic labels

nicr_scene_analysis_datasets/scripts/viewer_labeled_point_cloud.py

Lines changed: 163 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424

2525
USAGE = inspect.cleandoc(
2626
"""
27-
Use the keys 1-5 to switch between displaying:
28-
1: color,
29-
2: semantic ground-truth annotations,
30-
3: instance ground-truth annotations,
31-
4: predicted semantic labels (see --semantic-label-file`, and
32-
5: predicted instance labels (see --instance-label-file`).
27+
Use the keys 1-7 to switch between displaying:
28+
1: color given in ply file,
29+
2: semantic ground-truth annotations given in label field in ply file,
30+
3: instance ground-truth annotations given in label field in ply file,
31+
4: additional (predicted) semantic labels given by `--semantic-label-filepath`,
32+
5: additional (predicted) instance labels given by `--instance-label-filepath`,
33+
6: additional (predicted) semantic labels given by `--panoptic-label-filepath`,
34+
7: additional (predicted) instance labels given by `--panoptic-label-filepath`.
3335
3436
Press h to show further help and q to quit.
3537
"""
@@ -53,10 +55,12 @@ def _parse_args():
5355
parser.add_argument(
5456
'--mode',
5557
type=str,
56-
choices=('color',
57-
'semantic', 'instance',
58-
'additional_semantic', 'additional_instance'),
59-
default='color',
58+
choices=(
59+
'ply_color', 'ply_semantic', 'ply_instance',
60+
'additional_semantic', 'additional_instance',
61+
'additional_panoptic_semantic', 'additional_panoptic_instance'
62+
),
63+
default='ply_color',
6064
help='Render mode (use keys to switch)'
6165
)
6266
parser.add_argument(
@@ -87,12 +91,35 @@ def _parse_args():
8791
help="Path to txt file containing the instance label for each vertex. "
8892
"Useful for visualizing predictions."
8993
)
94+
parser.add_argument(
95+
'--panoptic-label-filepath',
96+
type=str,
97+
default=None,
98+
help="Path to txt file containing the panoptic label as sem*shift+ins "
99+
"for each vertex. See `--use-scannet-format` for 'shift'. Useful "
100+
"for visualizing predictions."
101+
)
90102
parser.add_argument(
91103
'--use-scannet-format',
92104
action='store_true',
93105
help="If specified, labels will be taken as sem*1000+inst instead of "
94106
"our sem*(1<<16)+ins encoding."
95107
)
108+
parser.add_argument(
109+
'--use-panoptic-labels-as-instance-labels',
110+
action='store_true',
111+
help="If specified, panoptic labels will be used as instance labels. "
112+
"This is useful if panoptic labels with 0-based instance ids per "
113+
"semantic class are given, which is common after panoptic "
114+
"merging. Consider using `--enumerate-instances` as well, to get "
115+
"smaller instance ids."
116+
)
117+
parser.add_argument(
118+
'--enumerate-instances',
119+
action='store_true',
120+
help="If specified, instance labels will be enumerated before "
121+
"visualization. This is useful to get smaller instance ids. "
122+
)
96123

97124
# experimental
98125
parser.add_argument(
@@ -112,11 +139,20 @@ def _parse_args():
112139
return parser.parse_args()
113140

114141

115-
def _get_instance_colors(instance_labels, instance_cmap):
116-
# convert to continuos labels
117-
# instance_labels = np.where(
118-
# np.unique(instance_labels) == instance_labels[:, np.newaxis]
119-
# )[1]
142+
def _get_instance_colors(instance_labels,
143+
instance_cmap,
144+
enumerate_instances=False):
145+
146+
# enumerate instances
147+
if enumerate_instances:
148+
instance_labels_ = np.zeros_like(instance_labels) # 0: no instance
149+
for new_id, old_id in enumerate(np.unique(instance_labels), start=1):
150+
if old_id == 0:
151+
# no instance label
152+
continue
153+
instance_labels_[instance_labels == old_id] = new_id
154+
instance_labels = instance_labels_
155+
120156
if instance_labels.max() >= len(instance_cmap):
121157
warnings.warn(
122158
f"Color map has only {len(instance_cmap)} colors, but the "
@@ -126,7 +162,51 @@ def _get_instance_colors(instance_labels, instance_cmap):
126162
return instance_cmap[instance_labels % len(instance_cmap)]
127163

128164

129-
def _load_ply(filepath, semantic_cmap, instance_cmap, use_scannet_format=False):
165+
def split_panoptic_labels(panoptic_labels,
166+
use_scannet_format=False,
167+
use_panoptic_as_instance=False):
168+
if use_scannet_format:
169+
# uint16
170+
semantic_labels = panoptic_labels // 1000
171+
instance_labels = panoptic_labels - semantic_labels * 1000
172+
else:
173+
# uint32
174+
semantic_labels = panoptic_labels >> 16
175+
instance_labels = panoptic_labels & 0xFFFF
176+
177+
if not use_panoptic_as_instance:
178+
# check for same instance ids with multiple semantic classes (typically
179+
# happens when merging semantic and instance to panoptic labels)
180+
uniques, indices = np.unique(instance_labels, return_index=True)
181+
for instance_id, mask in zip(uniques, indices):
182+
if instance_id == 0:
183+
# no instance label
184+
continue
185+
186+
semantic_classes = np.unique(semantic_labels[mask])
187+
if len(semantic_classes) > 1:
188+
# multiple semantic labels for the same instance id
189+
warnings.warn(
190+
f"Instance id {instance_id} has multiple semantic labels: "
191+
f"{semantic_classes}. Consider adding "
192+
"`--use-panoptic-labels-as-instance-labels` to get unique "
193+
"instance ids and `--enumerate-instances` to get smaller "
194+
"instance ids."
195+
)
196+
break
197+
else:
198+
# use panoptic labels as instance labels
199+
instance_labels = panoptic_labels
200+
201+
return semantic_labels, instance_labels
202+
203+
204+
def _load_ply(filepath,
205+
semantic_cmap,
206+
instance_cmap,
207+
use_scannet_format=False,
208+
use_panoptic_as_instance=False,
209+
enumerate_instances=False):
130210
plydata = plyfile.PlyData.read(filepath)
131211

132212
num_verts = plydata['vertex'].count
@@ -150,29 +230,31 @@ def _load_ply(filepath, semantic_cmap, instance_cmap, use_scannet_format=False):
150230
if 'label' in plydata['vertex']:
151231
labels = plydata['vertex'].data['label'] # uint16 / uint32
152232

153-
if use_scannet_format:
154-
# uint16
155-
semantic_labels = labels // 1000
156-
instance_labels = labels - semantic_labels * 1000
157-
else:
158-
# uint32
159-
semantic_labels = labels >> 16
160-
instance_labels = labels & 0xFFFF
233+
# split labels (panoptic / semantic-instance)
234+
semantic_labels, instance_labels = split_panoptic_labels(
235+
panoptic_labels=labels,
236+
use_scannet_format=use_scannet_format,
237+
use_panoptic_as_instance=use_panoptic_as_instance
238+
)
161239

162240
# semantic colors
163241
semantic_colors = semantic_cmap[semantic_labels]
164242

165243
# instance colors
166-
instance_colors = _get_instance_colors(instance_labels, instance_cmap)
244+
instance_colors = _get_instance_colors(
245+
instance_labels=instance_labels,
246+
instance_cmap=instance_cmap,
247+
enumerate_instances=enumerate_instances
248+
)
167249
else:
168250
# we do not have annotations
169251
instance_colors = np.zeros_like(colors)
170252
semantic_colors = np.zeros_like(colors)
171253

172254
labels = {
173-
'color': colors.astype('float64') / 255,
174-
'instance': instance_colors.astype('float64') / 255,
175-
'semantic': semantic_colors.astype('float64') / 255
255+
'ply_color': colors.astype('float64') / 255,
256+
'ply_instance': instance_colors.astype('float64') / 255,
257+
'ply_semantic': semantic_colors.astype('float64') / 255
176258
}
177259

178260
pc.colors = o3d.utility.Vector3dVector(colors)
@@ -193,7 +275,9 @@ def main():
193275
filepath=args.filepath,
194276
semantic_cmap=semantic_cmap,
195277
instance_cmap=instance_cmap,
196-
use_scannet_format=args.use_scannet_format
278+
use_scannet_format=args.use_scannet_format,
279+
use_panoptic_as_instance=args.use_panoptic_labels_as_instance_labels,
280+
enumerate_instances=args.enumerate_instances
197281
)
198282
pc.colors = o3d.utility.Vector3dVector(labels[args.mode])
199283

@@ -208,23 +292,41 @@ def main():
208292
# print usage
209293
print_section("Usage", USAGE)
210294

211-
# load additional predicted labels
295+
# load additional (predicted) labels
212296
if args.semantic_label_filepath is not None:
213297
semantic_labels = np.loadtxt(args.semantic_label_filepath,
214298
dtype=np.uint64)
215-
216299
assert len(semantic_labels) == len(np.array(pc.points))
217300

218301
labels['additional_semantic'] = semantic_cmap[semantic_labels] / 255
219302

220303
if args.instance_label_filepath is not None:
221304
instance_labels = np.loadtxt(args.instance_label_filepath,
222305
dtype=np.uint64)
223-
224306
assert len(instance_labels) == len(np.array(pc.points))
225307

226308
labels['additional_instance'] = _get_instance_colors(
227-
instance_labels, instance_cmap
309+
instance_labels=instance_labels,
310+
instance_cmap=instance_cmap,
311+
enumerate_instances=args.enumerate_instances
312+
) / 255
313+
314+
if args.panoptic_label_filepath is not None:
315+
panoptic_labels = np.loadtxt(args.panoptic_label_filepath,
316+
dtype=np.uint64)
317+
assert len(panoptic_labels) == len(np.array(pc.points))
318+
319+
semantic_labels, instance_labels = split_panoptic_labels(
320+
panoptic_labels=panoptic_labels,
321+
use_scannet_format=args.use_scannet_format,
322+
use_panoptic_as_instance=args.use_panoptic_labels_as_instance_labels
323+
)
324+
labels['additional_panoptic_semantic'] = \
325+
semantic_cmap[semantic_labels] / 255
326+
labels['additional_panoptic_instance'] = _get_instance_colors(
327+
instance_labels=instance_labels,
328+
instance_cmap=instance_cmap,
329+
enumerate_instances=args.enumerate_instances
228330
) / 255
229331

230332
visualizer = o3d.visualization.VisualizerWithKeyCallback()
@@ -248,17 +350,34 @@ def _func(vis):
248350
visualizer.add_geometry(pc)
249351

250352
# add callback for switching between modes
251-
visualizer.register_key_callback(ord('1'),
252-
_change_mode('color'))
253-
visualizer.register_key_callback(ord('2'),
254-
_change_mode('semantic'))
255-
visualizer.register_key_callback(ord('3'),
256-
_change_mode('instance'))
257-
visualizer.register_key_callback(ord('4'),
258-
_change_mode('additional_semantic'))
259-
visualizer.register_key_callback(ord('5'),
260-
_change_mode('additional_instance'))
261-
353+
visualizer.register_key_callback(
354+
ord('1'),
355+
_change_mode('ply_color')
356+
)
357+
visualizer.register_key_callback(
358+
ord('2'),
359+
_change_mode('ply_semantic')
360+
)
361+
visualizer.register_key_callback(
362+
ord('3'),
363+
_change_mode('ply_instance')
364+
)
365+
visualizer.register_key_callback(
366+
ord('4'),
367+
_change_mode('additional_semantic')
368+
)
369+
visualizer.register_key_callback(
370+
ord('5'),
371+
_change_mode('additional_instance')
372+
)
373+
visualizer.register_key_callback(
374+
ord('6'),
375+
_change_mode('additional_panoptic_semantic')
376+
)
377+
visualizer.register_key_callback(
378+
ord('7'),
379+
_change_mode('additional_panoptic_instance')
380+
)
262381
# experimental: visualize correspondences between the (grund-truth) point
263382
# cloud given by args.filepath and a second point cloud given by
264383
# args.second_pc_filepath, e.g., a mapped representation

nicr_scene_analysis_datasets/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
_VERSION_MAJOR = 0
1010
_VERSION_MINOR = 5
11-
_VERSION_MICRO = 2
11+
_VERSION_MICRO = 3
1212

1313

1414
def get_version(with_suffix=False): # pragma no cover

0 commit comments

Comments
 (0)