Structural element used in erosion and dilation

Hi there,

Do we know what structural element is used in erosion and dilation?
I tried going through the codes:

and erode.h

but may be my C++ is a little rusty to so couldn’t figure this out.

Please see the two pages in scipy to know what a structural element is and how that applies to morphological operation:

Tagging the authors of mrtrix/core/filter/erode.h
@ThijsDhollander @jdtournier

Your response is much appreciated. :slight_smile:

Hey there @tbillah,

Tagging sure gets my attention. :wink: Sorry, extremely busy with way too many things at once… I notice I still have a number of posts flagged here as well to still reply to. In any case, this one is simple enough; here goes:

The dilation and erosion is done by acting on the 6 directly adjacent voxels I believe. So while the code isn’t written to work with a structural element in some kind of generalised way, the (“equivalent”) structural element here would be an octahedron. Note the relevant option to have more or less dilation or erosion is expressed as “passes” of this operation. In actuality, e.g. for the case of dilation, this will grow into a larger octahedron.

By the way, no need to dig into the code to figure out exactly what the (“equivalent”) structural element looks like: you can get this directly out by a simple experiment: create a binary image (e.g. using the ROI drawing tool in mrview, after loading any other 3D image as the main image) and fill just 1 single voxel. Save this, and then apply dilation to it (with any number of passes for which you’d like to see the structural element as if it was 1 single dilation with a structural element). The result is the structural element itself. :sunglasses:


Hi @ThijsDhollander, thanks for your reply. I understand you don’t support generalized structural element. I also understand the theory of multiple passes.

In the meantime, I did what you said.

I think the following is your structural element:

array([[[0, 0, 0],
        [0, 1, 0],
        [0, 0, 0]],

       [[0, 1, 0],
        [1, 1, 1],
        [0, 1, 0]],

       [[0, 0, 0],
        [0, 1, 0],
        [0, 0, 0]]])

Note the 1 in center (1,1,1) position (0 indexed). If it was just an octahedron, I wouldn’t expect the 1 to be there i.e.

array([[[0, 0, 0],
        [0, 1, 0],
        [0, 0, 0]],

       [[0, 1, 0],
        [1, 0, 1],
        [0, 1, 0]],

       [[0, 0, 0],
        [0, 1, 0],
        [0, 0, 0]]])

Can you please confirm?

Yes, looking at the code, the structural element is as you think it is: the central point is indeed tested (it’s 1 in your array).

Not sure what do you mean but thanks for confirming the former is the structural element.

Tagging the authors of mrtrix/core/filter/erode.h
@ThijsDhollander @jdtournier

Strange the GitHub GUI would assign the authors as such given that git blame attributes most of the file to @Dave :man_shrugging:

Yeah, I’ve got very little to do with that one. I remember writing the “cleaning” filter, for dwi2mask and in maskfilter as well. Don’t think I’ve done anything in erode.h at that time; it’s been that mechanism for dilation/erosion since forever. Whatever, as long as questions get answered. :man_shrugging:

I’m guessing @jdtournier was confirming it’s the version with the 1 in the centre.

Ok, let’s avoid confusion in wording: so to be very specific, I meant a filled / solid octahedron. So indeed, 1 in the centre, not zero. If you run the little experiment with a good number of passes, you’ll see it just becomes a larger (filled / solid) octahedron. In practice, this would most of the time mean it’s not all that well suited for applications where you want to dilate / erode by a very large amount (unless you have an application where an octahedron would be uniquely meaningful; that’d be pretty exotic though). But for small amounts, or e.g. when building opening or closing filters, it works well enough, I reckon (and it’s very fast).

That’s because of the renaming that happened during the FHS restructuring. If you ask git nicely, it’ll give you the full history, tracking through the rename:

$ git log --pretty='%h %an (%as): %s' --follow -- core/filter/dilate.h
6c6c553ed Thijs Dhollander (2017-12-13): Copyright update for upcoming RC which will probably end up around New Year's Also includes a change of mention of MRtrix to MRtrix3, in line with the phrase MRtrix3 developers, which was already in there/ Also includes removal of 2 .s, because they hindered clickability of the links in certain environments.
729dd6cfc Thijs Dhollander (2017-05-15): copyright update and cleanup
62846cf01 J-Donald Tournier (2017-04-09): new MR::make_shared() replacement for std::make_shared
57e4f1440 J-Donald Tournier (2017-01-25): FHS restructure: rename lib/ -> core/
654b7281c Thijs Dhollander (2017-01-25): header files
e2630d70a J-Donald Tournier (2016-11-23): Eigen MEMALIGN: updated lib/ folder
9268f0874 Daan Christiaens (2015-12-11): Update copyright header in all source files, as discussed in #9 and #31.
0951d8a40 Dave Raffelt (2015-08-23): Fix to erode and dilate filters
c65e83463 Dave Raffelt (2015-08-21): Port of maskfilter to updated syntax - Not yet thoroughly tested
0aee11746 J-Donald Tournier (2015-04-21): syntax overhaul: move files around ahead to where they might end up
b02097163 J-Donald Tournier (2015-04-09): Remove Ptr - replace with std::unique_ptr or new copy_ptr
eb1c53032 J-Donald Tournier (2015-04-08): Remove RefPtr - replace with std::shared_ptr
c760019ab J-Donald Tournier (2014-09-18): C++11: use auto & decltype where possible
54473491e J-Donald Tournier (2014-09-16): Use new image looping constructs throughout
3f0eb026b Robert Smith (2014-03-31): Cleaned up some errors in Image::Filter documentation
ab7a8f1b3 Robert Smith (2014-03-28): Modified Image::Filter::Dilate / Erode to work natively in boolean
a77f389f9 Robert Smith (2014-03-28): Created Image::Filter::Base class
50b0df645 Dave Raffelt (2012-06-08): Ported mrerode app to new code base. Added new erode image filter Split out dilate into separate app and filter

It would be good if the GitHub UI did that by default…