1

I have a 3D label matrix. Using ndimage.sum I can get the labeled objects sizes, which is great for filtering based on volume. My question is : can I easily get objects sizes along each axis, to eliminate those that are only in one plane, for instance ?

A little code might be clearer...

from scipy import ndimage
labmat,n = ndimage.label(np.random.rand(30,30,30) > 0.99)
sizes = ndimage.sum(labmat.astype(bool),labmat,range(n+1))

Now instead of a 1-dimensional representing the volumes of the labeled objects, is there a way to have 3D array representing their surfaces in each dimension ? A 30-D array representing their surfaces in each plane would also be OK, though I would prefer the first option.

nicoco
  • 1,421
  • 9
  • 30

1 Answers1

4

You could use ndimage.find_objects to find the bounding box for each label. The bounding box is given by a tuple of slices. For example,

data_slices = ndimage.find_objects(labmat)
# [(slice(0L, 1L, None), slice(4L, 5L, None), slice(28L, 29L, None)),
#  (slice(0L, 1L, None), slice(25L, 26L, None), slice(19L, 20L, None)),
#  (slice(0L, 1L, None), slice(27L, 28L, None), slice(10L, 11L, None)),
#  (slice(0L, 1L, None), slice(28L, 29L, None), slice(7L, 8L, None)),
# ...

You could then find the size of each bounding box using

sizes = np.array([[s.stop-s.start for s in object_slice] 
                  for object_slice in data_slices])
# array([[1, 1, 1],
#        [1, 1, 1],
#        [1, 1, 1],
#        [1, 1, 1],
# ...

and create a boolean mask which is True for each box whose length is greater than 1 in all 3 dimensions:

mask = (sizes > 1).all(axis=1)

Use np.flatnonzero to find the corresponding indices:

idx = np.flatnonzero(mask)

You can also use the slices to select the region of values from labmat (or the original array). For example,

for i in idx:
    print(labmat[data_slices[i]])

import numpy as np
from scipy import ndimage
np.random.seed(2016)

labmat, n = ndimage.label(np.random.rand(30,30,30) > 0.5)
data_slices = ndimage.find_objects(labmat)
sizes = np.array([[s.stop-s.start for s in object_slice] 
                  for object_slice in data_slices])
mask = (sizes > 1).all(axis=1)
idx = np.flatnonzero(mask)
for i in idx:
    print(labmat[data_slices[i]])
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • `find_objects` [ignores labels with value 0](http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.ndimage.measurements.find_objects.html#scipy.ndimage.measurements.find_objects). So the first row of sizes corresponds to label value 1. Be sure to remove/change `np.random.seed(2016)` to test other random inputs. `mask = (sizes > 1).all(axis=1)` selects boxes which appear in more than one plane in every dimension. To select boxes that appear in more than 1 plane along just the first dimension use `mask = (sizes[:, 0] > 1)`. – unutbu Mar 24 '16 at 14:50
  • Oh OK I got confused then realized it and deleted my comment before seeing yours. Thanks. – nicoco Mar 24 '16 at 15:02