Search code examples
pythonimage-processingscipyndimage

Objects' sizes along dimension?


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.


Solution

  • 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]])