seung-lab / euclidean-distance-transform-3d

Euclidean distance & signed distance transform for multi-label 3D anisotropic images using marching parabolas.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possibility to implement fast binary / multilabel dilations and erosions with edt.

turtleizzy opened this issue · comments

Fast binary / multilabel morphological library is currently absent in the numpy world. Implementation in scipy.ndimage is extremely inefficient. Morphological operation with spherical structuring element (SE) is equivalent to thresholding with the radius of SE in euclidean distance transform(EDT).

Since labels are sparse in most applications, a majority of voxels with large distance values are irrelevant. I am wondering if it is possible to set a 'clamp value' to signed distance function by not calculating/propagating the distance values larger than a given threshold to improve the performance just for the purpose of morphological operation.

For reference, ITKLabelErodeDilate is a parallel, multi-label aware morphological library that also utilized hyperbolic(quadratic) structuring function along with its separability in dimension and 'point of contact' 1d-dilating algorithm to achieve high-performance binary dilation & erosion. However, using this library required itk-base libraries and redundant conversation to and from the ITK Image class.

I have tested following implementation for binary operations and found them identical in result and similar in performance against ITKLabelErodeDilate.

def binary_spherical_dilate(arr, rad, parallel=0, anisotropy=None):
    resEDT = edt.edt(arr == 0, parallel=parallel, anisotropy=anisotropy)
    return resEDT < rad

def binary_spherical_erode(arr, rad, parallel=0, anisotropy=None):
    resEDT = edt.edt(arr > 0, parallel=parallel, anisotropy=anisotropy)
    return resEDT >= rad

Labeled erosions were also easy to implement as following.

def label_spherical_erode(arr, rad, parallel=0, anisotropy=None):
    resEDT = edt.edt(arr, parallel=parallel, anisotropy=anisotropy)
    return arr * (resEDT >= rad)

However, labeled dilations would not be possible if dilated labels were to collide. Is it possible to extract additional information from propagation process that reflects information of the nearest voxel, or is it possible to modify the code so that the label propagation could be done simultaneously with EDT calculation process?

Hi turtleizzy,

This is an interesting concept. I am not very familiar with ITK, but i'll have to look more into it. I was not aware of the multi-label capabilities there.

I think for labeled dilatation, the problem becomes simple if you can extract the voronoi diagram. Label propagation is also an interesting idea, though I suspect that's just another variation of a voronoi algorithm. Label propagation using a stencil might be fast, but if it's done though a global priority queue, it will be relatively slow (~3-5 Megavoxels/sec).

It might be possible to extract the voronoi diagram from the EDT by finding ridgelines. This might save some calculation versus computing it from scratch.

As an aside, as dialations and erosions can be used to do void filling, I would be remiss to not mention my other work here: https://github.com/seung-lab/fill_voids

Would it be okay with you if I made a morphology pypi package based on edt? I seem to be running into people in my lab that may need this dilation operator so I may want to implement it. Thank you so much for your suggestions and insights.

Would it be okay with you if I made a morphology pypi package based on edt? I seem to be running into people in my lab that may need this dilation operator so I may want to implement it. Thank you so much for your suggestions and insights.

That will be great! Really look forward to your new package.

By the way, I am also a heavy user of fill_voids :P

I think for labeled dilatation, the problem becomes simple if you can extract the voronoi diagram. Label propagation is also an interesting idea, though I suspect that's just another variation of a voronoi algorithm. Label propagation using a stencil might be fast, but if it's done though a global priority queue, it will be relatively slow (~3-5 Megavoxels/sec).

It might be possible to extract the voronoi diagram from the EDT by finding ridgelines. This might save some calculation versus computing it from scratch.

ITKLabelSetErodeDilate also generates discrete voronoi-like ridgelines when multiple labels collide. Since ridgeline finding seems to be more easily parallelized than label propagation that required mutex lock, ridgeline extraction -> floodfilling might be a possible way to start with.

Label assignment on the ridgeline might be tricky. I would expect the ridgeline to be consistently labeled (either side would be fine), rather than a dotted line of interleaving labels ( 1 2 1 1 1 2 2 2 2 ... ).

That's a good point about the ridgelines. I'm not an expert on mathematical morphology, so I think I should ask a question before making the package. I have an intuition for why you called your operators spherical, but I thought most structuring elements in erode and dialate are rectangular. Are the operators you wrote non-standard? I get that this is a different way of computing the operators, but I am trying to understand if there is a corresponding similar operator using structuring elements.

I'm putting this collection of operators in a library called fastmorph

That's a good point about the ridgelines. I'm not an expert on mathematical morphology, so I think I should ask a question before making the package. I have an intuition for why you called your operators spherical, but I thought most structuring elements in erode and dialate are rectangular. Are the operators you wrote non-standard? I get that this is a different way of computing the operators, but I am trying to understand if there is a corresponding similar operator using structuring elements.

Rectangular elements, along with circle ( spherical ) and cross elements are all popular structuring elements (SE). The result of morphological operations is influenced by the shape of SE, especially around the sharp corners. There is a good example on Wolfram that illustrated the impact of the shape of SE if you are interested in this topic. A rule of thumb is to use a SE with similar shape properties as the target object you are working with, especially when the SE is comparable in size to the object. If you need to dilate an axis-aligned cuboid that necessitates preservation of the sharp, 90-degree angle, you must use rectangular elements. However, if you are working with nodules, dilating with rectangular elements (especially at a large radius) will give you a cuboid with curved angles.

However, at least for the most use cases that I encountered, morphological operations were mostly used in post-processing steps ( dilate -> connected-components -> erode, etc.) that performance may be the first priority. In these cases, the shape of SE didn't have much impact due to the small radius compared with the object.

I'm putting this collection of operators in a library called fastmorph

Great to hear that!