Reputation: 347
I try to calculate morphology erosion using FFT Convolution. I know erosion is dual operation to dilation. The first problem is I cannot use 0 as background as usualy I do. So I biased my values. Lets 0.1 value denotes background and 1.0 denotes foreground. Inverting background to foreground and perform FFT convolution with structure element (using scipy.signal.fftconvolve) I obtained result which I cannot interpret further. I know I should threshold the solution somehow and inverse again. How to do?
My 2D signal A:
1 1 0 1 1
1 1 1 1 1
0 1 1 1 0
1 1 1 1 1
1 1 0 1 1
Stucture element B:
0 1 0
1 1 1
0 1 0
Erode(A,B):
0 0 0 0 0
0 1 0 1 0
0 0 1 0 0
0 1 0 1 0
0 0 0 0 0
Using FFT Convolution, inv(A):
0.1 0.1 1.0 0.1 0.1
0.1 0.1 0.1 0.1 0.1
1.0 0.1 0.1 0.1 1.0
0.1 0.1 0.1 0.1 0.1
0.1 0.1 1.0 0.1 0.1
and B:
0.1 1.0 0.1
1.0 1.0 1.0
0.1 1.0 0.1
The result as below:
0.31 1.32 1.32 1.32 0.31
1.32 0.72 1.44 0.72 1.32
1.32 1.44 0.54 1.44 1.32
1.32 0.72 1.44 0.72 1.32
0.31 1.32 1.32 1.32 0.31
What next? normalize/threshold then inverse?
Best regards
Upvotes: 1
Views: 1603
Reputation: 159
After performing the convolution by multiplying in frequency-space and inverse-transforming back into real space, you must then threshold the result above a certain value. According to the white paper Dilation and Erosion of Gray Images with Spherical Masks by J. Kukal, D. Majerova, A. Prochazka, that threshold is >0.5
for dilation, and for erosion it's >m-0.5
where m the structure element's volume (the number of 1s in B, 5 in this case).
In brief: This code will give you the expected result.
from scipy.signal import fftconvolve
import numpy as np
def erode(A,B):
thresh = np.count_nonzero(B)-0.5
return fftconvolve(A,B,'same') > thresh
This will work in any dimension and reproduce exactly the results of scipy.ndimage.binary_erosion - at least, I've tested it in 2D and 3D.
As for the other answer posted here that disputes the expected result of an erosion: it depends on the boundary condition. Both scipy.ndimage.binary_erosion and the custom erode(A,B)
function written here assume that erosion may occur from all edges of the input A - i.e. A is padded out with 0s before the erosion. If you don't like this boundary condition - if you think, for example, that it should be treated as a reflected boundary condition - then you should consider padding the array yourself using e.g. np.pad(A,np.shape(B)[0],'reflect')
. Then you'd need to un-pad the result afterwards.
I was light on details here because I wrote a more comprehensive answer on using frequency-space convolution to perform both erosion and dilation over at another question you asked, but I thought it was worthwhile to have a conclusive answer posted here for anyone seeking it.
Upvotes: 1
Reputation: 5888
My answer arrives really late, but I still give it.
1 0 0 0 1
0 1 0 1 0
0 0 1 0 0
0 1 0 1 0
1 0 0 0 1
Upvotes: 1