<a id="easyimage-non-linear-filtering"></a>

# Non-Linear Filtering

These functions use non-linear combinations of neighboring pixels to highlight a shape, or to remove noise. Most functions can be applied in-place (except top-hat and median filters) i.e. the source image is overwritten by the destination image.

All have a gray image and a bi-level equivalent, for example [`EasyImage::ErodeBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ae619199666efda5c28e99116fe1fcc45) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a8be0fbc30c91e2e67cafefa4c0aee58d) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a59b676b87852a1b961c1486da65c3f08) and [`EasyImage::BiLevelErodeBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#aa35c9f180514b1da17f1fee9c2c3dbf6) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#afa8e5c36e24af65656ef29a64afd3839) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a1b2a2c38aa8c817bd32aa3cc4129022d).

To apply the non-linear filtering operation, a kernel is defined. A kernel is defined as a 2D matrix. They slide this Kernel over the image to determine the value of the destination pixel when a match is found:

* **Erosion, Dilation**: shrink (erosion) or grow (dilation) regions.
* **Opening, Closing**:
  * **Opening** removes small foreground protrusions and smooths boundaries.
  * **Closing** fills small holes/gaps and smooths boundaries.
* **Thinning, Thickening**: reduce shapes toward their skeleton (thinning) or expand them (thickening) using pattern-based rules.
* **Top-Hat filters**: preserve small details/features while suppressing the broader background trend.
* **Morphological distance**: measures how many erosions are needed before a pixel becomes background (black).
* **Morphological gradient**: highlights edges by combining dilation and erosion results (inner/outer edge information).
* **Median filter**: removes impulsive (“salt-and-pepper”) noise by replacing each pixel with the median of its neighborhood.
* **Hit-and-Miss transform**: detects specific foreground/background pixel patterns; can be used for pattern detection and skeleton-related operations.

Examples throughout this page use the following image as input:

![](assets/images/easyimage/non_linear_filtering/input.webp)

<a id="easyimage-non-linear-filtering-kernel"></a>

## Kernel

![](assets/images/easyimage/morphological_kernel.svg)

The morphological operators combine the pixel values in a neighborhood of given shape (square, rectangular or circular) and replace the central pixel of the neighborhood by the result.

* Three special cases are most often used **erosion**, **dilation** and **median** filter where : K can be 1 (minimum of the set), N (maximum) or N/2 (median).
* **Erosion**, **Dilation**, **Opening**, **Closing**, **Top-Hat** and **Morphological Gradient** operations all use rectangular or circular kernels of odd size.
* The kernel size has an important impact on the result.

Some dimension examples:

|   `HalfWidth/HalfHeight` |   Actual `width/height` |
|--------------------------|-------------------------|
|                        0 |                       1 |
|                        1 |                       3 |
|                        2 |                       5 |
|                        3 |                       7 |

<a id="easyimage-non-linear-filtering-erosion-dilation"></a>

## Erosion, Dilation

* Erosion reduces white objects and enlarges black objects, dilation does the opposite.

| ![](assets/images/easyimage/non_linear_filtering/erosion_output.webp)![](assets/images/easyimage/non_linear_filtering/erosion_graph.webp)   | ![](assets/images/easyimage/non_linear_filtering/dilation_output.webp)![](assets/images/easyimage/non_linear_filtering/dilation_graph.webp)   |
|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
* **Erosion** thins white objects by removing a layer of pixels along the objects edges: [`EasyImage::ErodeBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ae619199666efda5c28e99116fe1fcc45) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a8be0fbc30c91e2e67cafefa4c0aee58d) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a59b676b87852a1b961c1486da65c3f08), [`EasyImage::ErodeDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a45167648bfc80d5f699c8e61c51abc53) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#aa91dffd109c392cd8c45fd9be6b191d0) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a58aa759d19be3251a538d58dde9be550). As the kernel size increases, white objects disappear and black ones get fatter.
* **Dilation** thickens white objects by adding a layer of pixels along the objects edges: [`EasyImage::DilateBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a6ce0a44c72ad37cd53a8d6bc2eaf9979) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#ac6393f6d5ff03597b7c87dc7971a5ec3) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a1d16c66827c95d538268bbba8fc46edf), [`EasyImage::DilateDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ac933576e17b13d31cf0c2c04deca8d36) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#afc33e78265c1b37d4d440acb529f2952) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#ac8665f556c6a05d59df473ad1dd26007). As the kernel size increases, white objects get fatter and black ones disappear.

<a id="easyimage-non-linear-filtering-opening-closing"></a>

## Opening, Closing

*  ***Opening*** removes tiny white objects / dust.
*  ***Closing*** removes tiny black holes / dust.

| ![](assets/images/easyimage/non_linear_filtering/opening_output.webp)![](assets/images/easyimage/non_linear_filtering/opening_graph.webp)   | ![](assets/images/easyimage/non_linear_filtering/closing_output.webp)![](assets/images/easyimage/non_linear_filtering/closing_graph.webp)   |
|---------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
* An **Opening** is an erosion followed by a dilation using [`EasyImage::OpenBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a3ba574d64ec0c8cdbb2faa3adabc84ee) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#adc7d6b01597e8b64840b252ee7071e87) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#ad6745057183c1948ddfb913e16570eff), [`EasyImage::OpenDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a34cc6cef1372f7cfdff6b9b03722d317) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a1e9713b03e0df5529bf11da4c4a16b7a) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#aedfbda20fc719c1da72fc4206face601).
  * The global effect is to preserve the overall shape of objects, while removing white details that are smaller than the kernel size.
* A **Closing** is a dilation followed by an erosion using [`EasyImage::CloseBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a0f82f022b37f66dad434cd6bc0f8adda) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a8d88823f96b5a27d341f4693f141dc99) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a41cc7c3eaab8b1967cbe17b75182b3f4), [`EasyImage::CloseDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ac1a0403528ac3afbba1022699be07cbc) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#acb3e25c4daf31e2974e5d01f764de09a) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a648feab427aefb3e312f6e2bbd385d9f).
  * The global effect is to preserve the overall shape of objects, while removing the black details that are smaller than the kernel size.

<a id="easyimage-non-linear-filtering-thinning"></a>

## Thinning, Thickening

* [`EasyImage::Thick`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#af6eb3aa1f91a9fa784a3c086ab7c9c5a) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#ad8f02ec1ca1b556b60176545fb2a0a4e) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a0eb8067b685183e54e9999ba3083b4aa) and [`EasyImage::Thin`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a6479edf25f0298ce2b851599879da9f9) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#ae7046dfe0efa5ff429429482d1d5028f) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#af0b41a261eb47e6493aa3182d08cc5bf) can respectively grow or remove pixels from a binary image:
  * Thinning: can help edge detectors by reducing lines to single pixel thickness.
  * Thickening: can help determine approximate shape, or skeleton.
* When a match is found between the kernel coefficients and the neighborhood of a pixel, the pixel value is set to 255 if thickening, or 0 if thinning. The kernel coefficients are:
  * 0: matching black pixel, value 0
  * 1: matching non-black pixel, value > 0
  * -1: don’t care
* [`EasyImage::Thinning`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a699bf8dffe8276e34a47ce67142543f0) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a573cf7358f6127f81469ae7ad78d20af) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a7d8c4ef41c8f882a5d1b4516088607ee) computes the skeleton of a binary image without necessitating multiple iterations or a kernel. It uses the Zhang-Suen thinning algorithm.

![image](assets/images/easyimage/non_linear_filtering/thinning_input.webp)![image](assets/images/easyimage/non_linear_filtering/thinning_output.webp)

<a id="easyimage-non-linear-filtering-top-hat"></a>

## Top-Hat Filters

*  ***Top-hat filters*** are excellent for improving non-uniform illumination.

![](assets/images/easyimage/non_linear_filtering/tophat_output.webp)![](assets/images/easyimage/non_linear_filtering/tophat_graph.webp)
* They take the difference between an image and its opening (or closure). Thus, they keep the features that an opening (or closing) would erase. The result is a perfectly flat background where only black or white features smaller than the kernel size appear.
  * **White top-hat filter** enhances thin white features: [`EasyImage::WhiteTopHatBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a0ceb8024d75a1d86fe98a702892197ed) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#ac232eaf5bd3efecb916e3ce7a5b13f77) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a3697d3db838928168f17995694ebe30b), [`EasyImage::WhiteTopHatDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#aa651464f89abffbf1e048fccdfe811da) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a706640e52510dacc0d763db392c7a71d) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a2daba0d526ca869360b77f1180a03e4f).
  * **Black top-hat filter** enhances thin black features:[`EasyImage::BlackTopHatBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ab1e8c74da41110b6ec1c951eb637c50f) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a3b1df6eaaa938b71ed438c0723804cbf) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a0e289c21a674b9c336e30118512eff8d)[`EasyImage::BlackTopHatDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#aa4b1f5156adfde349d20563e5dac69a4) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#ac8e6763f35808b3638c5c1893ba3f161) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#ae9a6369ee24ddf146eb84650273b3ff9).

<a id="easyimage-non-linear-filtering-distance"></a>

## Morphological Distance

* [`EasyImage::Distance`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#a2c967086f99f192d44d0fb00295663ed) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a7bd4ba5df669dfca7a65cf2ffb19b54d) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a130fff221555a8904ac545de4aca19a9) computes the morphological distance (number of erosion passes to set a pixel to black) of a binary image (0 for black, non 0 for white) and creates a destination image, where each pixel contains the morphological distance of the corresponding pixel in the source image.

<a id="easyimage-non-linear-filtering-gradient"></a>

## Morphological Gradient

To get the morphological gradient, use [`EasyImage::MorphoGradientBox`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#aed0af79c6a4a0a40de761e2d5e0c3cd2) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#af6f74aa9e8d619f22333b11748304c30) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a88f7b437e5882c18c0335943694dd8e6) or [`EasyImage::MorphoGradientDisk`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ae8c6a8e6d3f65b398db28002d8b798b6) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#acb4f3c1d8e07275d7049c5dc7dd44fd6) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a4240bb86726ea16aae2bc146eb938439)

* The morphological gradient performs edge detection, it removes everything in the image but the edges.
* The morphological gradient is the difference between the dilation and the erosion of the image, using the same structuring element.

![](assets/images/easyimage/non_linear_filtering/gradient_output.webp)

<a id="easyimage-non-linear-filtering-median"></a>

## Median

* The [`EasyImage::Median`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#af5779432e45e2124fe02abd7a7cc0dce) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a31fed78390e85ab08e732068ed0e0714) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a8aa88f40a2da316f54fef3c515a4f2f3) filter removes impulse noise, whilst preserving edges and image sharpness.
* It replaces every pixel by the median (central value) of its neighbors in a 3x3 or larger kernel, thus, outer pixels are discarded.

![](assets/images/easyimage/non_linear_filtering/median_output.webp)

<a id="easyimage-non-linear-filtering-hit-and-miss"></a>

## Hit-and-Miss Transform

*  ***Hit-and-miss transform*** detects a particular pattern of foreground and background pixels in an image.

The [`EasyImage::HitAndMiss`](../../../doxygen_cpp/classeVision_1_1EasyImage.html#ad1a82e31eb4e8e21aacf1206e89637fb) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EasyImage.html#a1536ef6830ac0e682ff8b59f5be67839) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EasyImage.html#a6350444306ec7113cde7a5619d23a317) function has three arguments:

* A pointer to the source image of type [`EROIBW8`](../../../doxygen_cpp/classeVision_1_1EROIBW8.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EROIBW8.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EROIBW8.html), [`EROIBW16`](../../../doxygen_cpp/classeVision_1_1EROIBW16.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EROIBW16.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EROIBW16.html), or [`EROIC24`](../../../doxygen_cpp/classeVision_1_1EROIC24.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EROIC24.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EROIC24.html)
* A pointer to the destination image of type corresponding to the type of the source image.<br />
  \\\\
  The sizes of the source and destination images must be identical.
* A kernel of type [`EHitAndMissKernel`](../../../doxygen_cpp/classeVision_1_1EHitAndMissKernel.html) [<sub>.NET</sub>](../../../doxygen_cs/classeVision_1_1EHitAndMissKernel.html) [<sub>Python</sub>](../../../doxygen_python/classeVision_1_1EHitAndMissKernel.html) Two constructors are available for the kernel object:
  * `EHitAndMissKernel(int startX, int startY, int endX, int endY)` where coordinates are relative to the kernel center (the current pixel):
    * `startX`, `startY` are coordinates of the top-left corner of the kernel, must be ≤ 0.
    * `endX`, `endY` are coordinates of the bottom-right corner of the kernel, must be ≥ 0.
    * The resulting kernel dimensions are: $\text{width} = \text{endX} - \text{startX} + 1$, $\text{height} = \text{endY} - \text{startY} + 1$
  * `EHitAndMissKernel(unsigned int halfSizeX, unsigned int halfSizeY)` where the kernel is centered on the current pixel:
    * `halfSizeX` is the kernel half-width: $\text{halfSizeX} = (\text{width} - 1) / 2$, must be greater than zero.
    * `halfSizeY` is the kernel half-height: $\text{halfSizeY} = (\text{height} - 1) / 2$, must be greater than zero.
    * The resulting kernel dimensions and origin are:<br />
      \\\\
      $\text{width} = 2 \times \text{halfSizeX} + 1$, $\text{height} = 2 \times \text{halfSizeY} + 1$<br />
      \\\\
      $\text{startX} = -\text{halfSizeX}$, $\text{startY} = -\text{halfSizeY}$

Here’s an example: the  ***hit-and-miss transform*** can be used to locate corners.

![image](assets/images/easyimage/non_linear_filtering/hitandmisssourceimage.webp)
1. Define the kernel by detecting the left corner. The left corner pixel has black pixels on its immediate left, top and bottom; and it has white pixels on its right. The following hit-and-miss kernel will detect the left corner:

```c
  - +
- + +
  - +
```

1. Apply the filter on the source image. Note that the resulting image should be properly sized.

![image](assets/images/easyimage/non_linear_filtering/hitandmissleftcorner.webp)
1. Locate the three remaining corners in the same way. Declare three kernels that are the rotation of the filter above and apply them.

![](assets/images/easyimage/non_linear_filtering/hitandmissrightcorner.webp)![](assets/images/easyimage/non_linear_filtering/hitandmisstopcorner.webp)![](assets/images/easyimage/non_linear_filtering/hitandmissbottomcorner.webp)

<a id="easyimage-non-linear-filtering-quick-links"></a>

## See Also

Easy to understand, ready to copy code snippets.

Conceptual guide to spatial and temporal noise reduction techniques.
