Iterations vs. Kernel Size in Morphological Operations (OpenCV)

给你一囗甜甜゛ 提交于 2019-12-04 13:14:13

问题


I've been using morph. opening in OpenCV to reduce noise outside of my ROI in images via opencv, and until now, whenever I need a higher degree of noise reduction I just randomly increase kernel size or increase the number of iterations until I'm happy. But is there a significant difference in results depending on which you increase / how would you decide which to change in a given situation? I'm trying to come up with a better approach to which parameter I change (by how much) other than guess-and-check.


回答1:


It depends on the kernel type. For dilating or eroding with an odd-square kernel, there is no difference whether you increase the size or increase the iterations (assuming values which would make them equal are used). For example:

>>> M = np.zeros((7,7), dtype=np.uint8)
>>> M[3,3] = 1

>>> k1 = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
>>> M1 = cv2.dilate(M, k1, iterations=2)

>>> k2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
>>> M2 = cv2.dilate(M, k2, iterations=1)

>>> M1
[[0 0 0 0 0 0 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 0 0 0 0 0 0]]

>>> M2
[[0 0 0 0 0 0 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 1 1 1 1 1 0]
 [0 0 0 0 0 0 0]]

And this is fairly intuitive. A 3x3 rectangular kernel for dilating will find any white pixel, and turn the neighboring pixels white. So it's easy to see that doing this twice will make any single white pixel turn into a 5x5 block of white pixels. Here we're assuming the center pixel is the one that is compared---the anchor---but this could be changed, which could affect the result. For example, suppose you were comparing two iterations of a (2, 2) kernel with a single iteration of a (3, 3) kernel:

>>> M = np.zeros((5, 5), dtype=np.uint8)
>>> M[2,2] = 1

>>> k1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
>>> M1 = cv2.dilate(M, k1, iterations=2)

>>> k2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
>>> M2 = cv2.dilate(M, k2, iterations=1)

>>> M1
[[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 1 1 1]
 [0 0 1 1 1]
 [0 0 1 1 1]]

>>> M2
[[0 0 0 0 0]
 [0 1 1 1 0]
 [0 1 1 1 0]
 [0 1 1 1 0]
 [0 0 0 0 0]]

You can see that while it creates the shape (intuitive), they're not in the same place (non-intuitive). And that's because the anchor of a (2, 2) kernel cannot be in the center of the kernel---in this case we see that with a centered pixel, the neighbors that dilate are only to the bottom-right, since it has to choose a direction because it can only expand the single pixel to fill up a (2, 2) square.

Things become even more tricky with non-rectangular shaped kernels. For example:

>>> M = np.zeros((5, 5), dtype=np.uint8)
>>> M[2,2] = 1

>>> k1 = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
>>> M1 = cv2.dilate(M, k1, iterations=2)

>>> k2 = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
>>> M2 = cv2.dilate(M, k2, iterations=1)

>>> M1
[[0 0 1 0 0]
 [0 1 1 1 0]
 [1 1 1 1 1]
 [0 1 1 1 0]
 [0 0 1 0 0]]

>>> M2
[[0 0 1 0 0]
 [0 0 1 0 0]
 [1 1 1 1 1]
 [0 0 1 0 0]
 [0 0 1 0 0]]

The first pass of M1 creates a small cross 3 pixels high, 3 pixels wide. But then each one of those pixels creates a cross at their location, which actually creates a diamond pattern.

So to sum up for basic morphological operations, with rectangular kernels, at least odd-dimensioned ones, the result is the same---but for other kernels, the result is different. You can apply the other morphological operations to simple examples like this to get a hang of how they behave and which you should be using and how to increase their effects.



来源:https://stackoverflow.com/questions/45070004/iterations-vs-kernel-size-in-morphological-operations-opencv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!