ImageJ max and min filters for erosion and dilation

I have been using the max and min filters in ImageJ to erode and dilate images.

Now I am trying to replicate this functionality.

Using Square matrixes for the neighborhood, the erosion and dilation results are not as “smooth” as what ImageJ produces.

What extra technique is ImageJ using to produce “smooth” erosions?

Hi,

I can’t reproduce this.
The only difference is at the edges ; min/max filters take edges into account, erode/dilate don’t.
Have you noticed that parameters are not the same ? Filter min/max of radius 2 is the same as erode/dilate of size 1 ?

Nico

Thank you for your response!
I am using MATLAB’s imerode function with a square neighborhood matrix.
I believe this matrix is what ImageJ uses.
However, the results are not as good.
I was wondering if anyone knew specifically how ImageJ’s min and max rankfilters worked so that their functionality could be replicated in programs outside of ImageJ because using ImageJ isnt super practical for my application.

All ImageJ source code is freely available on Github:

For the RankFilters specifically - check here:

1 Like

The min and max filters in IJ use a disc kernel. Maybe that is a difference that you note against the square kernels you are using in matlab?

1 Like

Does this mean that if you set a neighborhood of n they use a circle of radius n?

“Radius” is what I see when I apply Process>Filters>Minimum…
Is that the command that you are using?

Yes, but are you sure that it is using a circle?
Im not sure that “radius” necessarily means a circle matrix is being used in this context.
But im definitely no expert

Hey @db_1070,

I recently digged deeper into this, because there appears a little discrepancy in ImageJ when processing 2D or 3D images, the neighborhood is a bit different:


This macro allows to reproduce this, just set radius = 1

But technically, ImageJs uses a circle-like mask - even though it does look like a square when using r=1:

run("Close All");


// -----------------------------------------------------------------

// create 2D example image
input = "example2d";
newImage(input, "8-bit black", 10, 10, 1);
makeRectangle(4, 5, 1, 1);
run("Add...", "value=255");
run("Select None");

// visualise input image
zoom(10);
setWindowPosition(0, 0);
setMinAndMax(0, 1);

for (radius = 1; radius < 4; radius++) {
	// do operation on CPU
	selectWindow(input);
	run("Duplicate...", "title=mean2D_CPU");
	run("Mean...", "radius=" + radius);
	
	// visualise result
	zoom(10);
	setWindowPosition(radius , 0);
	setMinAndMax(0, 1);
}


// -----------------------------------------------------------------

function zoom(count) {
	for (i = 0; i < count; i++) {	
		run("In [+]");
	}	
}

function setWindowPosition(x, y) {
	setLocation(x * 330, y * 370);
}

Cheers,
Robert

2 Likes

In case people are not aware of this, one can specify a Radius of 0.5 to get the “cross” kernel.
There is also a “Show circular masks” command that shows the disc kernels.

2 Likes

Thank you! It turns out that Matlab uses different disk kernels than ImageJ. So a disk of radius 4 in Matlab is not the same shape as a disk of radius 4 in ImageJ.

1 Like

Hi @db_1070

could you post a screenshot of matlabs bitmask? For curiousity :wink:

Thanks!
Robert

Hi,

I have generated the shape of default disk-shape structuring elements one obtains with Matlab, for radius 1, 2, 3 4, 5 and 6.

disk_strels_matlab

For structuring elements with radius >= 3, different results may be obtained depending on the choice of approximation for decomposition of structuring elements. The displayed image correspond to the default value of 4.

Script:

img = zeros(15, 15);
img(8, 8) = 1;
imgDil1 = imdilate(img, strel('disk', 1));
imgDil2 = imdilate(img, strel('disk', 2));
imgDil3 = imdilate(img, strel('disk', 3));
imgDil4 = imdilate(img, strel('disk', 4));
imgDil5 = imdilate(img, strel('disk', 5));
imgDil6 = imdilate(img, strel('disk', 6));
res = [imgDil1 imgDil2 imgDil3 ; imgDil4 imgDil5 imgDil6];
figure; imshow(res);

In practice, I sometimes use structuring elements defined directly from the Euclidean distance to central pixel/voxel:

[x, y] = meshgrid(-5:5, -5:5);
R = 3; se = hypot(x, y) < R + .5;

This results in structuring elements with size equal to 2*R+1 in each dimension, that can be convenient for later processing or interpretation.

2 Likes

So far, I have not found a preconfigured MATLAB function that produces the same kernel as ImageJ for a given radius. However, I can hardcode them in the meantime now that I can see what IJ uses. Eventually, I can figure out what method IJ uses to generate its masks and implement this in MATLAB.
@haesleinhuepf
I can confirm the results posted by @dlegland

I will now try to figure out what algorithm ImageJ uses to draw its circles.
Please let me know if anyone already understands how this works.

As already mentioned, check the code for “Show circular masks” command.

I meant the algorithm that imageJ uses.
I have done some testing and it does not seem to be related to the Euclidean distance, midpoint circle algorithm, or Manhattan Distance.

… what I just said above!

Is this code in the github ?
I do not see a show circular masks section of the github

RankFilters.java line 766 onwards.

1 Like