Sunday 3 December 2023

Erosion operation in OpenCV

Erosion operation shrinks the objects in an image, reduce the size of foreground object in a binary image. In general, Erosion is applied on binary, Grayscale images. Refer this video for short introduction.

The erosion operation works by using a structuring element, which is a small matrix of pixels. The structuring element is centered on each pixel in the input image, and the output pixel value is set to the 1, when all the one’s of structured element are matched.

 

Let me explain it with an example.

 


 

We take the center element of structuring element, keep the center pixel of structuring element on each input image pixel. Keep the structuring element on top of the input image, and set the output pixel value to 1, when all 1’s of the structuring elements are matched, else to 0.

 

 

For example,

a.   when I place the center value of structuring element on input_image[2][1], the left, top values relative to input_image[2][1] are 1, then this pixel is updated as 0.

b.   When I place the center value of structuring element on input_image[4][4] , left, top, right and bottom values are one, right so the pixel input_image[4][4] set to the value 1.

 

How the corners are handled during erosion process?

When you place the center of a structuring element on the corner pixels of the input image, a portion of the structuring element will indeed extend beyond the bounds of the image.

 

For example, when I place the center of structuring element on input_image[0][0], then the elements at indexes [0][0], [1][0], [2][0] and [0] [1] are spill over the boundary. There are couple of approaches to handle the corner values.

 

a.   Zero Padding: Pixels outside the image boundary are considered to have a value of zero.

b.   Wrap-Around or Periodic Boundary: While dealing with cyclic or periodic data, boundary condition assumes that the image repeats or wraps around. When the structuring element crosses the corner, it continues on the opposite side of the image.

 

After applying the erosion the input image is changed like below.




Find the below working application in Numpy.

 

array_erosion.py

import numpy as np

# Create a binary array (0s and 1s)
binary_array = np.array([
    [0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 1, 1, 1, 0],
    [1, 0, 0, 1, 0, 1, 0],
    [0, 0, 1, 0, 1, 0, 0],
    [1, 0, 0, 1, 1, 1, 0],
    [0, 1, 1, 1, 1, 0, 0]
], dtype=np.uint8)

# Create a square structuring element (kernel)
kernel = np.array([
    [0, 1, 0],
    [1, 1, 1],
    [0, 1, 0]
], dtype=np.uint8)

# Get the dimensions of the binary array and kernel
input_image_height, input_image_width = binary_array.shape
kernel_height, kernel_width = kernel.shape
kernel_center = (kernel_height // 2, kernel_width // 2)

# Create an empty array to store the result of erosion
eroded_array = np.zeros_like(binary_array)

# Iterate through each pixel in the binary array
for i in range(input_image_height):
    for j in range(input_image_width):
        # Initialize the flag to check if all kernel elements match
        match_all = True

        # Check the overlapped region
        for m in range(kernel_height):
            for n in range(kernel_width):
                if kernel[m, n] == 1:
                    if (i + m - kernel_center[0] < 0 or i + m - kernel_center[0] >= input_image_height or
                        j + n - kernel_center[1] < 0 or j + n - kernel_center[1] >= input_image_width or
                        binary_array[i + m - kernel_center[0], j + n - kernel_center[1]] != 1):
                        match_all = False
                        break

        # If all kernel elements match, set the corresponding pixel in the eroded array to 1
        if match_all:
            eroded_array[i, j] = 1

# Print the original and eroded binary arrays
print("Original Binary Array:")
print(binary_array)

print("\nEroded Binary Array:")
print(eroded_array)

Output

Original Binary Array:
[[0 0 0 0 0 0 0]
 [0 1 1 1 1 1 0]
 [1 0 0 1 0 1 0]
 [0 0 1 0 1 0 0]
 [1 0 0 1 1 1 0]
 [0 1 1 1 1 0 0]]

Eroded Binary Array:
[[0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]
 [0 0 0 0 1 0 0]
 [0 0 0 0 0 0 0]]

Applying erosion affect on Image

Using cv2.erode method, we can apply erosion affect on an Image.

 

Signature

cv2.erode(src, kernel, dst=None, anchor=None, iterations=1, borderType=None, borderValue=None)

Following table summarizes the parameters of erode method.

 

 

Parameter

Description

src

Source image on which the erosion operation should be performed.

kernel

It represents the structuring element used for erosion process

dst

It is optional parameter, and specifies destination image where the result of the erosion operation will be stored.

anchor

It is optional parameter, specifies the anchor point within the kernel. It is a tuple of two integers that defines the relative position of the anchor within the kernel.

 

Default value is (-1, -1), which means the anchor is at the center of the kernel.

iterations

It is optional parameter, specifies how many times the erosion operation should be applied.

borderType

It is optional parameter, specifies the type of border to use when the kernel goes outside the image boundaries.

 

The default is cv2.BORDER_CONSTANT. Other values cv2.BORDER_REPLICATE, and

cv2.BORDER_REFLECT are also supported.

borderValue

It is optional, If you choose cv2.BORDER_CONSTANT for the border type, you can specify a constant value to be used for the border. The default is 0.

 

Example

eroded_image = cv.erode(image, kernel, iterations=2)

 

Find the below working application.

 

erosion.py

import cv2
import cv2 as cv
import numpy as np

def resize_frame(frame, scale_width=0.5, scale_height=0.5):
    width = int(frame.shape[1] * scale_width)
    height = int(frame.shape[0] * scale_height)
    new_dimensions = (width, height)
    return cv.resize(frame, new_dimensions, interpolation=cv.INTER_AREA)

# Load the image
 # Load as grayscale image
image = cv.imread('pyramids.png', cv2.IMREAD_GRAYSCALE)
image = resize_frame(image)

# Define the structuring element (square in this case)
kernel = np.array([
    [0, 1, 0],
    [1, 1, 1],
    [0, 1, 0]
], 'uint8')

# Perform dilation
eroded_image = cv.erode(image, kernel, iterations=2)

# Display the original and dilated images
cv.imshow('Original Image', image)

cv.imshow('Eroded Image', eroded_image)

cv.waitKey(0)
cv.destroyAllWindows()

Output

Original Image



Eroded Image



References

https://www.youtube.com/watch?v=2LAooUu1IjQ

https://www.youtube.com/watch?v=xO3ED27rMHs

  

Previous                                                    Next                                                    Home

No comments:

Post a Comment