Hello,
I have been using OpenCV (in Python) for quite a while now, and have been using morphological filter for a long time. Today, I ran some code performing morphological dilations and erosions with a non symmetrical structuring element (not equal to itself when turned 180° around the origin), and found out that OpenCV results were not what was expected…
First, here is some code for morphological erosion:
import numpy
import cv2
el = [[0,1,1]]
image = [[2,3,5,4,6],[4,3,2,4,3],[3,4,3,2,0]]
r = cv2.erode(numpy.array(image, dtype=numpy.uint8), numpy.array(el, dtype=numpy.uint8)*255)
print(numpy.array(image, dtype=numpy.uint8))
print("***")
print(r)
The result is the following
[[2 3 5 4 6]
[4 3 2 4 3]
[3 4 3 2 0]]
***
[[2 3 4 4 6]
[3 2 2 3 3]
[3 3 2 0 0]]
The structuring element is actually simple: for each pixel, we consider the pixel itself and its right neighbor. Performing an erosion of the image with such structuring element gives the expected result: each pixel is replace by the minimum value of the pixel itself and its right neighbor. So, for the erosion, everything works as expected.
When performing the dilation, there is a problem…
r2 = cv2.dilate(numpy.array(image, dtype=numpy.uint8), numpy.array(el, dtype=numpy.uint8)*255)
print(numpy.array(image, dtype=numpy.uint8))
print("***")
print(r2)
The result is
[[2 3 5 4 6]
[4 3 2 4 3]
[3 4 3 2 0]]
***
[[3 5 5 6 6]
[4 3 4 4 3]
[4 4 3 2 0]]
The result looks like each pixel was replaced by the maximum value between the pixel and its right neighbor. However, it should not be that.
Dilation is defined as a maximum, but using the symmetric of the input structuring element. In fact, the structuring element should be turned 180° around the center by the dilation function before being used for computing a maximum… In this example, each pixel should be replaced by the maximum value between the pixel and its left neighbor. But it is not the case, as OpenCV dilate function seems to fail to perform this rotation.
The definition of morphological dilation can be found on various source (“Hands-on morphological Image Processing” by Dougherty and Lotufo for example, or Dilation (morphology) - Wikipedia for a more direct source), and the implementation of morphological dilation of OpenCV seems to fail to take into account this rotation of the structuring element.
To support this, the same code on Matlab with the image processing toolbox gives the expected result:
image = [2 3 5 4 6; 4 3 2 4 3; 3 4 3 2 0];
se = strel([0 1 1]);
r = imerode(image,se);
r2 = imdilate(image,se);
image
disp('***')
r
disp('***')
r2
The output is
image =
2 3 5 4 6
4 3 2 4 3
3 4 3 2 0
***
r =
2 3 4 4 6
3 2 2 3 3
3 3 2 0 0
***
r2 =
2 3 5 5 6
4 4 3 4 4
3 4 4 3 2
Matlab and OpenCV have the same result for the erosion, but differ for the dilation…
The consequences are, for example, that some results of the book “Hands-on morphological Image Processing” cannot be reproduced (for example, Figure 1.9 of chapter 1).
Moreover, results of transformations relying on dilation are also false. For example, morphological opening should be anti-extensive (Opening (morphology) - Wikipedia), meaning that the result of the morphological opening of an image A by a structuring element B should be pixel-wise inferior or equal to A. And OpenCV fails to exhibit such property:
r3 = cv2.morphologyEx(numpy.array(image, dtype=numpy.uint8), cv2.MORPH_OPEN, numpy.array(el, dtype=numpy.uint8) * 255)
print(numpy.array(image, dtype=numpy.uint8))
print("***")
print(r3)
The output is
[[2 3 5 4 6]
[4 3 2 4 3]
[3 4 3 2 0]]
***
[[3 4 4 6 6]
[3 2 3 3 3]
[3 3 2 0 0]]
The top left pixel of r3 has a higher value that the top left pixel of the image. On Matlab, we don’t have such anomaly:
image = [2 3 5 4 6; 4 3 2 4 3; 3 4 3 2 0];
se = strel([0 1 1]);
r3 = imopen(image,se);
image
disp('***')
r3
The output is
image =
2 3 5 4 6
4 3 2 4 3
3 4 3 2 0
***
r3 =
2 3 4 4 6
3 3 2 3 3
3 3 3 2 0
Here, we see that the result of the opening transform respect the anti-extensive property: each pixel of the result if inferior or equal to the corresponding pixel of the input.
I am therefore quite convinced that OpenCV does not implement correctly the definition of the dilation. When using a symmetrical structuring element (disk, line, cross, square, …), the problem does not appear as rotating the structuring element 180° around its origin does not change the structuring element. However, when using non symmetrical structuring element, the problem arises…
I searched to see if this problem has already been brought up, but I couldn’t find anything on this matter… That’s why I am writing this topic today. If this is indeed an error of implementation of the dilation in OpenCV, how could the message be passed to the developers ?
Thanks for reading up to here!
edit : I am using opencv-python 4.7.0.72