I have been following this question for some time, hoping someone would write a fleshed-out answer, since I am pondering the same problem.
Here is my own attempt so far; I have not tested this, but I think you can do repeated dilation and erosion with any structuring element, by only accessing each pixel twice:
Assumptions: Assume the structuring element/kernel is a KxL rectangle and the image is a NxM rectangle. Assume that K and L are odd.
The basic approach you outlined has four for loops and takes O(K*L*N*M)
time to complete.
Often you want to dilate repeatedly with the same kernel, so the time is again multiplied by the desired number of dilations.
I have three basic ideas for speeding up the dilation:
dilation by a KxL kernel is equal to dilation by a Kx1 kernel followed by dilation by a 1xL kernel. You can do both of these dilations with only three for loops, in O(KNM) and O(LNM)
However you can do a dilation with a Kx1 kernel much faster: You only need to access each pixel once. For this you need a particular data structure, explained below. This allows you to do a single dilation in O(N*M), regardless of the kernel size
repeated dilation by a Kx1 kernel is equal to a single dilation by a larger kernel. If you dilate P times with a Kx1 kernel, this is equal to a single dilation with a ((K-1)*P + 1) x 1
kernel.
So you can do repeated dilation with any kernel size in a single pass, in O(N*M) time.
Now for a detailed description of step 2.
You need a queue with the following properties:
- push an element to the back of the queue in constant time.
- pop an element from the front of the queue in constant time.
- query the current smallest or largest element in the queue in constant time.
How to build such a queue is described in this stackoverflow answer: Implement a queue in which push_rear(), pop_front() and get_min() are all constant time operations.
Unfortunately not much pseudocode, but the basic idea seems sound.
Using such a queue, you can calculate a Kx1 dilation in a single pass:
Assert(StructuringElement.dy()==1);
int kernel_half = (StructuringElement.dx()-1) /2;
for( y < dy ) { // y loop
for( x <= kernel_half ) { // initialize the queue
queue.Push(src(x, y));
}
for( x < dx ) { // x loop
// get the current maximum of all values in the queue
dst(x, y) = queue.GetMaximum();
// remove the first pixel from the queue
if (x > kernel_half)
queue.Pop();
// add the next pixel to the queue
if (x < dx - kernel_half)
queue.Push(src(x + kernel_half, y));
}
}