Skip to content

Commit d2043e9

Browse files
feat(mesh): exclude certain segids from meshing (#221)
* feat: add object exclusion logic to meshing This can be used to avoid meshing a very large object that needs to be specially treated. * feat(cli): add --exclude-labels to mesh forging
1 parent 597cdb1 commit d2043e9

File tree

4 files changed

+62
-8
lines changed

4 files changed

+62
-8
lines changed

igneous/task_creation/mesh.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,28 @@ def __iter__(self):
156156

157157

158158
def create_meshing_tasks(
159-
layer_path, mip, shape=(448, 448, 448),
160-
simplification=True, max_simplification_error=40,
161-
mesh_dir=None, cdn_cache=False, dust_threshold=None,
162-
object_ids=None, progress=False, fill_missing=False,
163-
encoding='precomputed', spatial_index=True, frag_path=None, sharded=False,
164-
compress='gzip', closed_dataset_edges=True, dust_global=False,
165-
fill_holes=0, dry_run=False,
166-
):
159+
layer_path:str,
160+
mip:int,
161+
shape:tuple[int,int,int] = (448, 448, 448),
162+
simplification:bool = True,
163+
max_simplification_error:float = 40.0,
164+
mesh_dir:Optional[str] = None,
165+
cdn_cache:bool = False,
166+
dust_threshold:Optional[int] = None,
167+
object_ids:Optional[list[int]] = None,
168+
progress:bool = False,
169+
fill_missing:bool = False,
170+
encoding:str = 'precomputed',
171+
spatial_index:bool = True,
172+
frag_path:Optional[str] = None,
173+
sharded:bool = False,
174+
compress:Optional[str] = 'gzip',
175+
closed_dataset_edges:bool = True,
176+
dust_global:bool = False,
177+
fill_holes:int = 0,
178+
dry_run:bool = False,
179+
exclude_object_ids:list[int] = [],
180+
):
167181
shape = Vec(*shape)
168182

169183
assert 0 <= fill_holes <= 103, "fill_holes must be between 0 to 103 inclusive."
@@ -208,6 +222,7 @@ def task(self, shape, offset):
208222
dust_global=bool(dust_global),
209223
progress=progress,
210224
object_ids=object_ids,
225+
exclude_object_ids=exclude_object_ids,
211226
fill_missing=fill_missing,
212227
encoding=encoding,
213228
spatial_index=spatial_index,
@@ -234,6 +249,7 @@ def on_finish(self):
234249
'dust_threshold': dust_threshold,
235250
'encoding': encoding,
236251
'object_ids': object_ids,
252+
'exclude_object_ids': exclude_object_ids,
237253
'spatial_index': spatial_index,
238254
'frag_path': frag_path,
239255
'sharded': sharded,

igneous/tasks/mesh/mesh.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def __init__(self, shape, offset, layer_path, **kwargs):
7676
draco_create_metadata: (bool: False) only applies to draco encoding
7777
progress: (bool: False) show progress bars for meshing
7878
object_ids: (list of ints) if specified, only mesh these ids
79+
exclude_object_ids: (list of ints) exclude any label present in this list from meshing
7980
fill_missing: (bool: False) replace missing segmentation files with zeros instead of erroring
8081
spatial_index: (bool: False) generate a JSON spatial index of which meshes are available in
8182
a given bounding box.
@@ -112,6 +113,7 @@ def __init__(self, shape, offset, layer_path, **kwargs):
112113
'frag_path': kwargs.get('frag_path', None),
113114
'mip': kwargs.get('mip', 0),
114115
'object_ids': kwargs.get('object_ids', None),
116+
'exclude_object_ids': kwargs.get('exclude_object_ids', []),
115117
'parallel_download': kwargs.get('parallel_download', 1),
116118
'progress': kwargs.get('progress', False),
117119
'remap_table': kwargs.get('remap_table', None),
@@ -198,6 +200,9 @@ def execute(self):
198200
if self.options['object_ids']:
199201
data = fastremap.mask_except(data, self.options['object_ids'], in_place=True)
200202

203+
if self.options['exclude_object_ids']:
204+
data = fastremap.mask(data, self.options['exclude_object_ids'], in_place=True)
205+
201206
data, renumbermap = fastremap.renumber(data, in_place=True)
202207
renumbermap = { v:k for k,v in renumbermap.items() }
203208

igneous_cli/cli.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,13 +1046,15 @@ def mesh_xfer(
10461046
@click.option('--sharded', is_flag=True, default=False, help="Generate shard fragments instead of outputing mesh fragments.", show_default=True)
10471047
@click.option('--closed-edge/--open-edge', is_flag=True, default=True, help="Whether meshes are closed on the side that contacts the dataset boundary.", show_default=True)
10481048
@click.option('--labels', type=ListN(), default=None, help="Mesh only this comma separated list of labels.", show_default=True)
1049+
@click.option('--exclude-labels', type=ListN(), default=[], help="Do not mesh labels in this comma separated list.", show_default=True)
10491050
@click.pass_context
10501051
def mesh_forge(
10511052
ctx, path, queue, mip, shape,
10521053
simplify, fill_missing, max_error,
10531054
dust_threshold, dir, compress,
10541055
spatial_index, sharded, closed_edge,
10551056
dust_global, labels, fill_holes,
1057+
exclude_labels,
10561058
):
10571059
"""
10581060
(1) Synthesize meshes from segmentation cutouts.
@@ -1079,6 +1081,7 @@ def mesh_forge(
10791081
encoding='precomputed', spatial_index=spatial_index,
10801082
sharded=sharded, compress=compress, closed_dataset_edges=closed_edge,
10811083
dust_global=dust_global, fill_holes=fill_holes,
1084+
exclude_object_ids=exclude_labels,
10821085
)
10831086

10841087
enqueue_tasks(ctx, queue, tasks)

test/test_tasks.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,36 @@ def test_mesh(compress):
430430
assert cf.get('mesh/10:0:0-64_0-64_0-64') is not None
431431
assert list(cf.list('mesh/')) == ['mesh/10:0:0-64_0-64_0-64']
432432

433+
def test_mesh_object_ids():
434+
delete_layer()
435+
cf, _ = create_layer(size=(64,64,64,1), offset=(0,0,0), layer_type="segmentation")
436+
cv = CloudVolume(cf.cloudpath)
437+
# create a box of ones surrounded by zeroes
438+
data = np.zeros(shape=(64,64,64,1), dtype=np.uint32)
439+
data[1:-1,1:-1,1:-1,:] = 1
440+
data[1:-1,1:-1,32:63,:] = 2
441+
cv[:] = data
442+
cv.info['mesh'] = 'mesh'
443+
cv.commit_info()
444+
445+
MeshTask(
446+
shape=(64,64,64),
447+
offset=(0,0,0),
448+
layer_path=cf.cloudpath,
449+
mip=0,
450+
exclude_object_ids=[2],
451+
).execute()
452+
assert cf.get('mesh/1:0:0-64_0-64_0-64') is not None
453+
assert list(cf.list('mesh/')) == ['mesh/1:0:0-64_0-64_0-64']
454+
455+
MeshTask(
456+
shape=(64,64,64),
457+
offset=(0,0,0),
458+
layer_path=cf.cloudpath,
459+
mip=0,
460+
object_ids=[2],
461+
).execute()
462+
assert cf.get('mesh/2:0:0-64_0-64_0-64') is not None
433463

434464
def test_quantize():
435465
qpath = 'file:///tmp/removeme/quantized/'

0 commit comments

Comments
 (0)