How can I observe the intermediate process of cv2.erode()?

Question:

I’ve been observing the results when I apply cv2.erode() with different kernel values. In the code below, it is (3, 3), but it is changed to various ways such as (1, 3) or (5, 1). The reason for this observation is to understand kernel.

I understand it in theory. And through practice, I can see what kind of results I get. But I want to go a little deeper.

I would like to see what happens every time the pixel targeted by kernel changes.

It’s okay if you have thousands of images stored.

How can I observe the intermediate process of cv2.erode()? Am I asking too much?

image = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
_, thresholded_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
inverted_image = cv2.bitwise_not(thresholded_image)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
eroded_image = cv2.erode(inverted_image, kernel, iterations=5)

cv2.imshow('image', eroded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Asked By: isakgo_

||

Answers:

When you call cv2.erode from Python it eventually gets down to one C++ API call of cv::erode.

As you can see in the documentation, this API does not support inspecting intermediate result from the process. This means it is also not available from the Python wrapper.

The only way you can achieve what you want is to download the C++ source code for opencv (as it is open-source), change it to support inspecting intermediate result (e.g. by adding callbacks, or additional output images), compile it to a library and wrap for Python.
Keep in mind however that doing so is far from being trivial.

Answered By: wohlstad

I like this question. I will post my approach here, but for the record, I know that it is not the fastest and that it might be wrong in some places? So far I tested in some images and it seems to give the same results as cv2.erode.

First I would recommend for you to understand the morphoplogical operation.

And so I come to my approach, which is the same as the classic one: we go through the image from top left to bottom right and we replace the centre of the kernel with the minimum value found in the kernel, sounds simple enough:

animation

Here’s the code minus the animation, as there’s another code that I use to generate them:

im = np.zeros((10,10)) # create image
im[2:8, 2:10] = 1 # put a box
kernel = np.ones((5, 5), dtype=np.uint8)  # create kernel
imHeight, imWidth = im.shape  # get the dimensions of the image
kHeight, kWidth = kernel.shape  # get the dimensions of the kernel
padHeight, padWidth = kHeight // 2, kWidth // 2  # calculate padding for height and width
paddedIm = np.pad(im,
                  ((padHeight, padHeight), (padWidth, padWidth)),
                  mode='constant', constant_values=(1,))  # pad the image in case kernel does not fit
eroded = im  # copy original image
imEroded = cv2.erode(im, kernel) # the classic
for i in range(imHeight): # for every row
    for j in range(imWidth): # for every column
        ROI = paddedIm[i:i+kHeight, j:j+kWidth]  # extract the region of interest (ROI) for the kernel
        eroded[i, j] = np.min(ROI[kernel == 1])  # apply the erosion operation (minimum value where kernel is 1)

For the more experienced opencv users: if anything is wrong with this please let me know

With a kernel = np.ones((5, 1), dtype=np.uint8):

5,1

With a kernel = np.ones((1, 5), dtype=np.uint8):

1,5

With a kernel = np.ones((5, 3), dtype=np.uint8):

5,3

Answered By: Tino D
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.