Image Filtering Techniques in Image Processing — Part 2

Henrique Vedoveli
13 min readSep 14, 2023

--

Non-Linear Image Filters

Continuing our exploration of image filtering techniques (see Part 1 — Linear Image Filters), we dive into the realm of nonlinear filters, which offer powerful solutions for noise reduction and feature preservation in scenarios where linear methods may fall short. The use of nonlinear filters adds another layer of sophistication to image processing, allowing for improved denoising results while preserving important edges and significant image components.

Erosion

The Erosion algorithm (usually represented by ) plays a pivotal role in the realm of digital image processing, demonstrating remarkable efficiency in executing morphological operations. Rooted in the mathematical foundations of morphology, this fundamental technique finds wide-ranging applications encompassing noise reduction, feature extraction, and image filtering. Its historical genesis can be traced back to the pioneering work of Georges Matheron in the 1960s, which ignited the exploration of morphological transformations. Over the years, the erosion algorithm has surged in importance due to its capacity to enhance image processing by skillfully eliminating undesirable noise and intricate details, all the while safeguarding vital structural attributes.

Erosion primarily finds its domain in binary images, although adaptations for grayscale images are also available. In the realm of binary image processing, the operator’s primary mission is to gradually wear away the boundaries enclosing regions of foreground pixels, often referred to as white pixels. This gradual process results in the reduction of the sizes of these regions, concurrently expanding the internal voids they contain.

Erosion, a cornerstone of morphological image processing, empowers the alteration of binary images while preserving their indispensable characteristics. This algorithm unfolds by sequentially processing each pixel within the input image, guided by a structuring element denoted as “S.” This pivotal component steers the assessment of pixel neighborhoods, thus orchestrating the erosion procedure.

The essence of erosion lies in its mission to ascertain the status of the current pixel (x, y) by scrutinizing the pixels within the ambit of “S.” The algorithm deduces whether the pixel should be integrated into an unbroken white region. If all corresponding pixels within “S” align with the white pixels in the image, the pixel retains its whiteness in the output. Conversely, in cases of mismatches, the pixel undergoes erosion and is substituted with the background value.

This iterative process unfolds for all input pixels, potentially eroding smaller regions and eliminating isolated pixels. The size and shape of the structuring element wield influence over the extent of erosion, with smaller elements producing a more conservative erosion and larger elements magnifying size reduction. Various element shapes can be used to achieve customized erosion effects tailored to specific image enhancement goals.

In essence, erosion stands as an indispensable technique within the realm of morphological image processing. It molds binary images through meticulous pixel-wise evaluations guided by a structuring element. This versatile algorithm enhances image attributes, curtails noise, and holds utility across an array of image analysis tasks.

Figure 1 — Effect of erode using a 3×3 square structuring element. Source: https://homepages.inf.ed.ac.uk/rbf/HIPR2/erode.htm

Algorithm Description

The Erosion Algorithm follows a systematic set of steps to achieve its objective:

For each pixel (x, y) within the input image, progress through the subsequent process:

  1. Position the structuring element S at pixel (x,y) within the input image.
  2. Examine each pixel (s_x, s_y) in the structuring element:
  • Check if the pixel I(x + s_x, y + s_y) exists and is a white (foreground) pixel.
  • If all the structuring element pixels align with white pixels in the input image, preserve the foreground at the corresponding pixel (x, y) in the output image.
  • Repeat this sequence for every pixel across the input image.

Commonly, the input image is represented in a binary format, where foreground pixels are valued 255 and background pixels are valued 0. Such a binary representation is usually obtained through thresholding a grayscale image. It’s crucial to set the proper polarity for the erosion implementation.

The structuring element can be supplied in various forms, including a small binary image, a specialized matrix, or it could be seamlessly integrated into the algorithm without explicit specification. The default choice is often a 3×3 square structuring element, which results in the contraction effect described earlier.

While the 3×3 square remains the standard structuring element for erosion, there are alternative options. A larger structuring element enhances the erosion effect. Comparable outcomes can be achieved by iteratively applying erosion with a smaller, similarly shaped element. For more pronounced effects, larger elements — often approximating a disk shape — are common.

The erosion algorithm serves as a fundamental building block in morphological image processing. It is widely employed in image analysis tasks like noise reduction, edge detection, and segmentation. The careful selection of the structuring element shape and size allows for customization of the erosion effect to suit specific image processing goals.

Code Implementation

import numpy as np
import cv2
import matplotlib.pyplot as plt

def generate_kernel(size):
return np.ones((size, size), dtype=np.uint8)

def erode(image, kernel):
height, width = image.shape
kernel_height, kernel_width = kernel.shape
offset_y = kernel_height // 2
offset_x = kernel_width // 2

eroded_image = np.zeros_like(image)

for y in range(offset_y, height - offset_y):
for x in range(offset_x, width - offset_x):
region = image[y - offset_y:y + offset_y + 1, x - offset_x:x + offset_x + 1]
min_value = np.min(region * kernel)
eroded_image[y, x] = min_value

return eroded_image

# Load an actual image using OpenCV
image_path = 'path_to_your_image.jpg'
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# Define the erosion kernel (3x3 square)
kernel_size = 3
kernel = generate_kernel(kernel_size)

# Apply the erosion algorithm
eroded_image = erode(image, kernel)

# Display the original and eroded images using OpenCV
cv2.imshow('Original Image', image)
cv2.imshow('Eroded Image', eroded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Conclusion

The Erosion algorithm is a fundamental component of morphological operations in digital image processing. Based on Georges Matheron’s pioneering work, this algorithm has developed into a flexible tool with diverse applications. By progressively reducing pixel values in designated neighborhoods, the Erode algorithm eliminates fine details and noise, improving image clarity for subsequent analysis. Despite its mathematically simple nature, the Erode algorithm remains a fundamental technique in image processing due to its ability to shape images while maintaining structural components. It has proven to be invaluable in applications ranging from medical imaging to pattern recognition and noise reduction.

Dilation

The Dilation, represented by the symbol ⊕, is a fundamental operation in mathematical morphology, extensively employed in image processing, computer vision, medical imaging, and remote sensing. It stands alongside erosion, opening, and closing as a cornerstone operation. Its roots trace back to the inception of mathematical morphology, evolving to handle not only binary images but also grayscale ones and complete lattices. When applied to binary images, dilation gradually enlarges foreground pixel regions (typically white pixels), simultaneously reducing internal holes. This enlargement of boundaries enhances image features and regions of interest while maintaining overall structure. By utilizing a structuring element, dilation probes and extends shapes within input images. Its applications encompass image segmentation, noise reduction, and feature extraction, thereby driving advancements in diverse real-world scenarios.

At the heart of the Dilation Algorithm lies the expansion of white regions within binary or grayscale images. Achieving this involves the application of a structuring element — often a predefined shape like a square or circle — to each pixel position in the input image. When any part of this element aligns with a white pixel in the original image, the corresponding pixel in the output image becomes white. This iterative process, carried out for each pixel, leads to the enlargement of the white regions.

The dilation operator takes an image to be dilated and a structuring element as inputs. This structuring element, also known as a kernel, determines the specific effect of dilation on the image. For binary images, the dilation definition is as follows: Let X represent the coordinates of the input binary image, and K denote the coordinates of the structuring element. If Kx signifies the translation of K with its origin at x, then the dilation of X by K includes points x where the intersection of Kx and X isn’t empty.

Grayscale dilation follows a similar definition, albeit with 3-D coordinates instead of 2-D. As an example, consider a 3×3 square structuring element. When applied, it sets background pixels adjacent to foreground ones (assuming 8-connectedness) to the foreground color, leading to the growth of foreground regions and reduction of holes within regions. This process exemplifies the practical outcome of dilation, which is a foundational operation in mathematical morphology with applications spanning various domains.

Algorithm Description

The Dilation Algorithm involves a concise series of steps to achieve its objective:

For each pixel (x,y) within the input image, traverse through the following process:

  1. Position the structuring element S at pixel (x, y) within the input image.
  2. Evaluate each pixel (s_x, s_y) in the structuring element:
  • Determine if the pixel I(x + s_x, y + s_y) exists and is a white (foreground) pixel.
  • If overlap occurs between the structuring element pixels and white pixels in the input image, mark the corresponding pixel (x, y) in the output image as white.

4. Repeat this procedure for every pixel across the input image.

Most implementations assume a binary input image with foreground pixels at a value of 255 and background pixels at 0. This binary representation is typically generated from a grayscale image through thresholding. Proper polarity setup for the dilation implementation is vital.

The structuring element might require supply as a small binary image, a specialized matrix format, or it could be integrated into the implementation without explicit specification. Often, a default 3×3 square structuring element is used, resulting in the expansion effect described earlier.

While the 3×3 square is the prevailing structuring element for dilation, alternatives are feasible. A larger structuring element magnifies the dilation effect. Comparable outcomes can be achieved by iterated dilations with a smaller yet similarly shaped element. Larger elements, often approximately disk-shaped rather than square, are common for more pronounced effects. The effect of a dilation using this structuring element on a binary image is shown in Figure 2.

Figure 2 — Effect of dilation using a 3×3 square structuring element. Source: https://homepages.inf.ed.ac.uk/rbf/HIPR2/dilate.htm

Code Implementation

import numpy as np
import cv2
import matplotlib.pyplot as plt

def generate_kernel(size):
return np.ones((size, size), dtype=np.uint8)

def dilate(image, kernel):
height, width = image.shape
kernel_height, kernel_width = kernel.shape
offset_y = kernel_height // 2
offset_x = kernel_width // 2

dilated_image = np.zeros_like(image)

for y in range(offset_y, height - offset_y):
for x in range(offset_x, width - offset_x):
region = image[y - offset_y:y + offset_y + 1, x - offset_x:x + offset_x + 1]
max_value = np.max(region * kernel)
dilated_image[y, x] = max_value

return dilated_image

# Load an actual image using OpenCV
image_path = 'path_to_your_image.jpg'
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# Define the dilation kernel (3x3 square)
kernel_size = 3
kernel = generate_kernel(kernel_size)

# Apply the dilation algorithm
dilated_image = dilate(image, kernel)

# Display the original and dilated images using OpenCV
cv2.imshow('Original Image', image)
cv2.imshow('Dilated Image', dilated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Conclusion

In conclusion, the Dilation Algorithm is a powerful technique in the realm of image processing, offering the means to expand and enhance the regions of interest within an image. Its historical roots in mathematical morphology have paved the way for its application in diverse domains, enabling advancements in fields such as computer vision and medical diagnostics. By understanding how dilation works at its core and following the algorithmic steps, practitioners can harness its capabilities to address various challenges like image segmentation, edge detection, and noise reduction. As technology continues to evolve, the Dilation Algorithm remains a cornerstone in the foundation of image processing techniques.

Median Filter

Originating from Tukey’s work in 1977, the median filter is a non-linear filter that finds wide application in noise reduction within images and signals. This is crucial as a preliminary stage to refine subsequent processes such as image edge detection. The median filter’s ability to preserve edges while effectively removing noise makes it an invaluable technique. In the context of multi-channel images, the Median Filter replaces each pixel strategically with the total median intensity of a neighboring pixel, demonstrating a comprehensive and balanced approach. The median filter importantly doesn’t ignore data boundaries; it uses smaller neighborhoods at these points to ensure a consistent result throughout the entire image.

The algorithm’s elegance and effectiveness have prompted a flurry of optimization efforts over the years. This commitment to improvement is demonstrated by its integration into various image processing methods such as rank-order and morphological processing. Essentially, these methods are derived from the fundamental median algorithm, which cements the filter’s role as a cornerstone for more complex effects. However, despite its benefits, practical implementation may encounter obstacles arising from the algorithm’s inherent slowness. As a result, the real-world application of the median filter is typically restricted to scenarios with small kernel sizes or low-resolution images. However, its influence on image refinement and noise reduction remains considerably significant.

Figure 3 — Image before (left) and after (right) median fliter

The median filter is a widely-used noise reduction technique in signal processing and image analysis. It operates by traversing data, be it a signal or an image, entry by entry. This traversal occurs within a sliding window pattern that systematically moves across the entire data sequence. In one-dimensional signals, the window is simplistic, incorporating adjacent entries. However, in cases with two or more dimensions, such as images, the window encompasses values within a designated radius or ellipsoidal area. This approach contrasts with other filters due to its non-separable nature.

Similar to the mean filter, the median filter focuses on each pixel in an image, examining its local neighborhood to determine its representativeness within that context. However, instead of replacing the pixel value with the average of neighboring pixel values, the median filter replaces it with the median of those values. To calculate the median, the surrounding pixel values are sorted in numerical order, and the middle pixel value becomes the new value for the central pixel. This process adjusts for noise while preserving important features. If the neighborhood contains an even number of pixels, the average of the two middle pixel values is used to compute the median.

The median filter’s operation is characterized by its sliding window pattern, which enables noise reduction by replacing each pixel’s value with the median of its neighboring values. This technique is applied sequentially across the entire signal or image, with one-dimensional signals involving adjacent entries and multi-dimensional data encompassing a defined radius or area. Although the filter’s non-separable nature distinguishes it from other filters, its effectiveness against impulsive noise is notable due to its resistance to extreme values. However, it’s important to note that the median filter’s reliance on neighboring pixel intensities can lead to the smoothing of subtle details, particularly along edges. Despite this limitation, its simple calculation and noise reduction capabilities have established its significance in various domains, often employing optimized libraries like numpy and scipy for efficient computation.

Algorithm Description

The custom median filter algorithm presented here is designed for noise reduction in images and follows a series of well-defined steps to achieve its objective. The algorithm takes an input image and a kernel size as parameters. For each pixel in the image, the following process is executed:

  1. The algorithm iterates through each pixel in the input image, denoted by its coordinates (i, j). It defines a kernel around the current pixel based on the provided kernel size. The kernel is a square region of the image that encompasses neighboring pixels.
  2. Within the defined kernel, the algorithm collects pixel values from the image. It loops over the kernel’s dimensions, denoted by (ki, kj), and calculates the coordinates of the pixel within the kernel as (ni, nj) = (i + ki, j + kj). The algorithm ensures that these coordinates fall within the bounds of the image.
  3. All collected pixel values within the kernel are stored in a list called ‘kernel’. The algorithm then calculates the median value from this list using the numpy function `np.median(kernel)`.
  4. The calculated median value is assigned to the corresponding pixel in the output image, which is initially filled with zeros. This process continues for all pixels in the input image.

This algorithm effectively reduces noise in the image by replacing each pixel’s value with the median value of its neighboring pixels. The kernel size parameter controls the size of the local neighborhood considered for the median calculation. Larger kernel sizes can provide better noise reduction but might also lead to some loss of image details. This custom approach to median filtering allows for fine-tuning and adaptation to specific noise patterns in images. The implementation demonstrates how the algorithm works using OpenCV for image loading, processing, and display.

Code Implementation

import numpy as np
import cv2

def custom_median_filter(image, kernel_size):
height, width = image.shape
filtered_image = np.zeros_like(image)
kernel_half = kernel_size // 2

for i in range(height):
for j in range(width):
kernel = []
for ki in range(-kernel_half, kernel_half + 1):
for kj in range(-kernel_half, kernel_half + 1):
ni = i + ki
nj = j + kj
if 0 <= ni < height and 0 <= nj < width:
kernel.append(image[ni, nj])

median_value = np.median(kernel)
filtered_image[i, j] = median_value

return filtered_image

# Load an image from a PNG file using OpenCV
image_path = 'path_to_your_image.png'
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

kernel_size = 3
filtered_image_custom = custom_median_filter(image, kernel_size)

# Display images using OpenCV
cv2.imshow('Original Image', image)
cv2.imshow('Filtered Image (Custom Median)', filtered_image_custom)
cv2.waitKey(0)
cv2.destroyAllWindows()

Conclusion

In conclusion, the median filter stands as a pivotal tool in the realm of noise reduction within images and signals, with its roots tracing back to Tukey’s pioneering work in 1977. Its unique ability to effectively remove noise while preserving vital image details, especially along edges, has solidified its importance in image processing. The filter’s adaptability and elegance have spurred ongoing optimization efforts, leading to its integration into various image processing methods. However, its practical application can be limited by computational constraints, particularly for large kernel sizes and high-resolution images. Nevertheless, the median filter remains a valuable asset in the pursuit of clearer, less noisy images, and it continues to play a fundamental role in enhancing visual data across diverse domains.

References

--

--

Henrique Vedoveli

Maters Student in Computer Science and Machine Learning Engineer🦜