Archive for the 'Morphology Filters' Category



C# How to: Morphological Edge Detection

Article purpose

The objective of this article is to explore   implemented by means of and  . In addition we explore the concept of implementing morphological .

Sample source code

This article is accompanied by a sample source code Visual Studio project which is available for download .

Using the sample application

This article is accompanied by a Sample Application intended to implement all of the concepts illustrated throughout this article. Using the sample application users can easily test and replicate concepts.

Clicking the Load Image button allows users to select source/input from the local system. Filter option categories are: Colour(s), morphology type, edge options and filter size.

This article and sample source code can process colour as source . The user can specify which colour components to include in resulting . The three labelled Red, Green and Blue indicate whether the related colour component features in result .

The four labelled Dilate, Erode, Open and Closed enable the user to select the type of morphological filter to apply.

options include: None, Edge Detection and Image Sharpening. Selecting None results in only the selected morphological filter being applied.

Filter sizes range from 3×3 up to 17×17. The filter size specified determines the intensity of the morphological filter applied.

If desired users are able to save filter result images to the local file system by clicking the Save Image button. The image below is a screenshot of the Morphological Edge Detection sample application in action:

Morphological_Edge_Detection_Sample_Application

Morphology – Image Erosion and Dilation

and are implementations of , a subset of . In simpler terms can be defined by this :

Dilation is one of the two basic operators in the area of , the other being . It is typically applied to , but there are versions that work on . The basic effect of the operator on a binary image is to gradually enlarge the boundaries of regions of foreground (i.e. white pixels, typically). Thus areas of foreground pixels grow in size while holes within those regions become smaller.

being a related concept is defined by this :

Erosion is one of the two basic operators in the area of , the other being . It is typically applied to , but there are versions that work on . The basic effect of the operator on a binary image is to erode away the boundaries of regions of foreground (i.e. white pixels, typically). Thus areas of foreground pixels shrink in size, and holes within those areas become larger.

From the definitions listed above we gather that increases the size of edges contained in an . In contrast decreases or shrinks the size of an ’s edges.

Image Edge Detection

We gain a good definition of from ’s article on :

Edge detection is the name for a set of mathematical methods which aim at identifying points in a at which the changes sharply or, more formally, has discontinuities. The points at which image brightness changes sharply are typically organized into a set of curved line segments termed edges. The same problem of finding discontinuities in 1D signals is known as and the problem of finding signal discontinuities over time is known as . Edge detection is a fundamental tool in , and , particularly in the areas of and .

In this article we implement based on the type of being performed. In the case of the eroded is subtracted from the original resulting in an with pronounced edges. When implementing , is achieved by subtracting the original from the dilated .

Image Sharpening

is often referred to by the term , from Wikipedia we gain the following :

Edge enhancement is an filter that enhances the edge contrast of an or in an attempt to improve its acutance (apparent sharpness).

The filter works by identifying sharp edge boundaries in the image, such as the edge between a subject and a background of a contrasting color, and increasing the image contrast in the area immediately around the edge. This has the effect of creating subtle bright and dark highlights on either side of any edges in the image, called and undershoot, leading the edge to look more defined when viewed from a typical viewing distance.

In this article we implement by first creating an which we then add to the original , resulting in an with enhanced edges.

Implementing Morphological Filters

The sample source code provides the definition of the DilateAndErodeFilter targeting the class. The DilateAndErodeFilter as a single method implementation is capable of applying a specified morphological filter, and . The following code snippet details the implementation of the the DilateAndErodeFilter :

public static Bitmap DilateAndErodeFilter(this Bitmap sourceBitmap,  
                                        int matrixSize, 
                                        MorphologyType morphType, 
                                        bool applyBlue = true, 
                                        bool applyGreen = true, 
                                        bool applyRed = true,
                                        MorphologyEdgeType edgeType = 
                                        MorphologyEdgeType.None)  
{ 
    BitmapData sourceData =  
               sourceBitmap.LockBits(new Rectangle (0, 0, 
               sourceBitmap.Width, sourceBitmap.Height), 
               ImageLockMode.ReadOnly,  
               PixelFormat.Format32bppArgb); 

byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
int filterOffset = (matrixSize - 1) / 2; int calcOffset = 0;
int byteOffset = 0;
int blue = 0; int green = 0; int red = 0;
byte morphResetValue = 0;
if (morphType == MorphologyType.Erosion) { morphResetValue = 255; }
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++) { byteOffset = offsetY * sourceData.Stride + offsetX * 4;
blue = morphResetValue; green = morphResetValue; red = morphResetValue;
if (morphType == MorphologyType.Dilation) { for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
if (pixelBuffer[calcOffset] > blue) { blue = pixelBuffer[calcOffset]; }
if (pixelBuffer[calcOffset + 1] > green) { green = pixelBuffer[calcOffset + 1]; }
if (pixelBuffer[calcOffset + 2] > red) { red = pixelBuffer[calcOffset + 2]; } } } } else if (morphType == MorphologyType.Erosion) { for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
if (pixelBuffer[calcOffset] < blue) { blue = pixelBuffer[calcOffset]; }
if (pixelBuffer[calcOffset + 1] < green) { green = pixelBuffer[calcOffset + 1]; }
if (pixelBuffer[calcOffset + 2] < red) { red = pixelBuffer[calcOffset + 2]; } } } }
if (applyBlue == false ) { blue = pixelBuffer[byteOffset]; }
if (applyGreen == false ) { green = pixelBuffer[byteOffset + 1]; }
if (applyRed == false ) { red = pixelBuffer[byteOffset + 2]; }
if (edgeType == MorphologyEdgeType.EdgeDetection || edgeType == MorphologyEdgeType.SharpenEdgeDetection) { if (morphType == MorphologyType.Dilation) { blue = blue - pixelBuffer[byteOffset]; green = green - pixelBuffer[byteOffset + 1]; red = red - pixelBuffer[byteOffset + 2]; } else if (morphType == MorphologyType.Erosion) { blue = pixelBuffer[byteOffset] - blue; green = pixelBuffer[byteOffset + 1] - green; red = pixelBuffer[byteOffset + 2] - red; }
if (edgeType == MorphologyEdgeType.SharpenEdgeDetection) { blue += pixelBuffer[byteOffset]; green += pixelBuffer[byteOffset + 1]; red += pixelBuffer[byteOffset + 2]; } }
blue = (blue > 255 ? 255 : (blue < 0 ? 0 : blue)); green = (green > 255 ? 255 : (green < 0 ? 0 : green)); red = (red > 255 ? 255 : (red < 0 ? 0 : red));
resultBuffer[byteOffset] = (byte)blue; resultBuffer[byteOffset + 1] = (byte)green; resultBuffer[byteOffset + 2] = (byte)red; resultBuffer[byteOffset + 3] = 255; } }
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap; }

Sample Images

The source/input used in this article is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license and can be downloaded from Wikipedia: http://en.wikipedia.org/wiki/File:Bathroom_with_bathtube.jpg

Original Image

1280px-Bathroom_with_bathtube

Erosion 3×3, Edge Detect, Red, Green and Blue

Erosion 3x3 Edge Detect Red, Green and Blue

Erosion 3×3, Edge Detect, Blue

Erosion 3x3, Edge Detect, Blue

Erosion 3×3, Edge Detect, Green and Blue

Erosion 3x3, Edge Detect, Green and Blue

Erosion 3×3, Edge Detect, Red

Erosion 3x3, Edge Detect, Red

Erosion 3×3, Edge Detect, Red and Blue

Erosion 3x3, Edge Detect, Red and Blue

Erosion 3×3, Edge Detect, Red and Green

Erosion 3x3, Edge Detect, Red and Green

Erosion 7×7, Sharpen, Red, Green and Blue

Erosion 7x7, Sharpen, Red, Green and Blue

Erosion 7×7, Sharpen, Blue

Erosion 7x7, Sharpen, Blue

Erosion 7×7, Sharpen, Green

Erosion 7x7, Sharpen, Green

Erosion 7×7, Sharpen, Green and Blue

Erosion 7x7, Sharpen, Green and Blue

Erosion 7×7, Sharpen, Red

Erosion 7x7, Sharpen, Red

Erosion 7×7, Sharpen, Red and Blue

Erosion 7x7, Sharpen, Red and Blue

Erosion 7×7, Sharpen, Red and Green

Erosion 7x7, Sharpen, Red and Green

Related Articles and Feedback

Feedback and questions are always encouraged. If you know of an alternative implementation or have ideas on a more efficient implementation please share in the comments section.

I’ve published a number of articles related to imaging and images of which you can find URL links here:

C# How to: Image Erosion and Dilation

Article purpose

The purpose of this article is aimed at exploring the concepts of , , and . In addition this article extends conventional and implementations through partial colour variations of and .

Sample source code

This article is accompanied by a sample source code Visual Studio project which is available for download .

Using the sample application

Included in this article’s sample source code you’ll find a based sample application. The sample application can be used to test and replicate the concepts we explore in this article.

When executing the sample application source/input can selected from the local system by clicking the Load Image button. On the right-hand side of the sample application’s user interface users can adjust the provided controls in order to modify the method of filtering being implemented.

The three labelled Red, Green and Blue relate to whether the relevant colour component will be regarded or not when implementing the configured filter.

Users are required to select an filter: , or . The interface selection is expressed by means of four respectively labelled Dilate, Erode, Open and Closed.

The only other input required from a user comes in the form of selecting the filter intensity/filter size. The dropdown indicated as Filter Size provides the user with several intensity levels ranging from 3×3 to 17×17. Note: Larger filter sizes result in additional processing required when implementing the filter. Large set to implement large sized filters may require more processor cycles.

Resulting filtered can be saved to the local file system by clicking the Save Image button. The screenshot below illustrates the Image Erosion and Dilation sample application in action:

Image_Erosion_Dilation_Sample_Application

Mathematical Morphology

A description of as expressed on :

Mathematical morphology (MM) is a theory and technique for the analysis and processing of geometrical structures, based on , , , and . MM is most commonly applied to , but it can be employed as well on , , , and many other spatial structures.

and -space concepts such as size, , , , and , were introduced by MM on both continuous and . MM is also the foundation of morphological image processing, which consists of a set of operators that transform images according to the above characterizations.

MM was originally developed for , and was later extended to and images. The subsequent generalization to is widely accepted today as MM’s theoretical foundation.

In this article we explore , , as well as and . The implementation of these filters are significantly easier to grasp when compared to most definitions of .

Image Erosion and Dilation

and are implementations of , a subset of . In simpler terms can be defined by this :

Dilation is one of the two basic operators in the area of , the other being . It is typically applied to , but there are versions that work on . The basic effect of the operator on a binary image is to gradually enlarge the boundaries of regions of foreground (i.e. white pixels, typically). Thus areas of foreground pixels grow in size while holes within those regions become smaller.

being a related concept is defined by this :

Erosion is one of the two basic operators in the area of mathematical morphology, the other being . It is typically applied to binary images, but there are versions that work on . The basic effect of the operator on a binary image is to erode away the boundaries of regions of foreground (i.e. white pixels, typically). Thus areas of foreground pixels shrink in size, and holes within those areas become larger.

From the definitions listed above we gather that increases the size of edges contained in an image. In contrast decreases or shrinks the size of an Image’s edges.

Open and Closed Morphology

Building upon the concepts of and this section explores and . A good definition of can be expressed as :

The basic effect of an opening is somewhat like erosion in that it tends to remove some of the foreground (bright) pixels from the edges of regions of foreground pixels. However it is less destructive than erosion in general. As with other morphological operators, the exact operation is determined by a . The effect of the operator is to preserve foreground regions that have a similar shape to this structuring element, or that can completely contain the structuring element, while eliminating all other regions of foreground pixels.

In turn can be defined as :

Closing is similar in some ways to dilation in that it tends to enlarge the boundaries of foreground (bright) regions in an image (and shrink background color holes in such regions), but it is less destructive of the original boundary shape. As with other , the exact operation is determined by a . The effect of the operator is to preserve background regions that have a similar shape to this structuring element, or that can completely contain the structuring element, while eliminating all other regions of background pixels.

Implementing Image Erosion and Dilation

In this article we implement and by iterating each pixel contained within an image. The colour of each pixel is determined by taking into regard a pixel’s neighbouring pixels.

When implementing a pixel’s value is determined by comparing neighbouring pixels’ colour values, determining the highest colour value expressed amongst neighbouring pixels.

In contrast to we implement by also inspecting neighbouring pixels’ colour values, determining the lowest colour value expressed amongst neighbouring pixels.

In addition to conventional and the sample source code provides the ability to perform  and targeting only specific colour components. The result of specific colour and produces images which express the effects of and only in certain colours. Depending on filter parameters specified edges appear to have a coloured glow or shadow.

The sample source code provides the definition for the DilateAndErodeFilter , targeting the class. The following code snippet details the implementation of the DilateAndErodeFilter :

public static Bitmap DilateAndErodeFilter(
                           this Bitmap sourceBitmap,  
                           int matrixSize, 
                           MorphologyType morphType, 
                           bool applyBlue = true, 
                           bool applyGreen = true, 
                           bool applyRed = true )  
{
    BitmapData sourceData =  
               sourceBitmap.LockBits(new Rectangle (0, 0, 
               sourceBitmap.Width, sourceBitmap.Height), 
               ImageLockMode.ReadOnly,  
               PixelFormat.Format32bppArgb); 

byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
int filterOffset = (matrixSize - 1) / 2; int calcOffset = 0;
int byteOffset = 0;
byte blue = 0; byte green = 0; byte red = 0;
byte morphResetValue = 0;
if (morphType == MorphologyType.Erosion) { morphResetValue = 255; }
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++) { byteOffset = offsetY * sourceData.Stride + offsetX * 4;
blue = morphResetValue; green = morphResetValue; red = morphResetValue;
if (morphType == MorphologyType.Dilation) { for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
if (pixelBuffer[calcOffset] > blue) { blue = pixelBuffer[calcOffset]; }
if (pixelBuffer[calcOffset + 1] > green) { green = pixelBuffer[calcOffset + 1]; }
if (pixelBuffer[calcOffset + 2] > red) { red = pixelBuffer[calcOffset + 2]; } } } } else if (morphType == MorphologyType .Erosion) { for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
if (pixelBuffer[calcOffset] < blue) { blue = pixelBuffer[calcOffset]; }
if (pixelBuffer[calcOffset + 1] < green) { green = pixelBuffer[calcOffset + 1]; }
if (pixelBuffer[calcOffset + 2] < red) { red = pixelBuffer[calcOffset + 2]; } } } }
if (applyBlue == false ) { blue = pixelBuffer[byteOffset]; }
if (applyGreen == false ) { green = pixelBuffer[byteOffset + 1]; }
if (applyRed == false ) { red = pixelBuffer[byteOffset + 2]; }
resultBuffer[byteOffset] = blue; resultBuffer[byteOffset + 1] = green; resultBuffer[byteOffset + 2] = red; resultBuffer[byteOffset + 3] = 255; } }
Bitmap resultBitmap = new Bitmap (sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle (0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap; }

Implementing Open and Closed Morphology

The sample source code implements by first implementing on a source image, the resulting image is then filtered by implementing .

In a reverse fashion is achieved by first implementing on a source image, which is then further filtered by implementing .

The sample source code defines the OpenMorphologyFilter and CloseMorphologyFilter , both targeting the class. The implementation as follows:

public static Bitmap OpenMorphologyFilter(
                            this Bitmap sourceBitmap, 
                            int matrixSize,
                            bool applyBlue = true, 
                            bool applyGreen = true, 
                            bool applyRed = true ) 
{ 
    Bitmap resultBitmap = 
           sourceBitmap.DilateAndErodeFilter(
                        matrixSize, MorphologyType.Erosion, 
                        applyBlue, applyGreen, applyRed); 

resultBitmap = resultBitmap.DilateAndErodeFilter( matrixSize, MorphologyType.Dilation, applyBlue, applyGreen, applyRed);
return resultBitmap; }
public static Bitmap CloseMorphologyFilter( this Bitmap sourceBitmap, int matrixSize, bool applyBlue = true, bool applyGreen = true, bool applyRed = true ) { Bitmap resultBitmap = sourceBitmap.DilateAndErodeFilter( matrixSize, MorphologyType.Dilation, applyBlue, applyGreen, applyRed);
resultBitmap = resultBitmap.DilateAndErodeFilter( matrixSize, MorphologyType.Erosion, applyBlue, applyGreen, applyRed);
return resultBitmap; }

Sample Images

The original source image used to create all of the sample images in this article has been licensed under the Creative Commons Attribution-Share Alike 3.0 Unported, 2.5 Generic, 2.0 Generic and 1.0 Generic license. The original image is attributed to Kenneth Dwain Harrelson and can be downloaded from .

The Original Image

Monarch_In_May

Image Dilation 3×3 Blue

Image Dilation 3x3 Blue

Image Dilation 3×3 Blue, Green

Image Dilation 3x3 Blue, Green

Image Dilation 3×3 Green

Image Dilation 3x3 Green

Image Dilation 3×3 Red

Image Dilation 3x3 Red

Image Dilation 3×3 Red, Blue

Image Dilation 3x3 Red, Blue

Image Dilation 3×3 Red, Green, Blue

Image Dilation 3x3 Red, Green, Blue

Image Dilation 13×13 Blue

Image Dilation 13x13 Blue

Image Erosion 3×3 Green, Blue

Image Erosion 3x3 Green, Blue

Image Erosion 3×3 Green

Image Erosion 3x3 Green

Image Erosion 3×3 Red

Image Erosion 3x3 Red

Image Erosion 3×3 Red, Blue

Image Erosion 3x3 Red, Blue

Image Erosion 3×3 Red, Green

Image Erosion 3x3 Red, Green

Image Erosion 3×3 Red, Green, Blue

Image Erosion 3x3 Red, Green, Blue

Image Erosion 9×9 Green

Image Erosion 9x9 Green

Image Erosion 9×9 Red

Image Erosion 9x9 Red

Image Open Morphology 11×11 Green

Image Open Morphology 11x11 Green

Image Open Morphology 11×11 Green Blue

Image Open Morphology 11x11 Green Blue

Image Open Morphology 11×11 Red

Image Open Morphology 11x11 Red

Image Open Morphology 11×11 Red, Blue

Image Open Morphology 11x11 Red, Blue

Related Articles and Feedback

Feedback and questions are always encouraged. If you know of an alternative implementation or have ideas on a more efficient implementation please share in the comments section.

I’ve published a number of articles related to imaging and images of which you can find URL links here:


Dewald Esterhuizen

Blog Stats

  • 813,604 hits

Enter your email address to follow and receive notifications of new posts by email.

Join 226 other followers

Archives

Twitter feed


%d bloggers like this: