### Article purpose

This article provides a technical discussion exploring the topic of ** Gradient Based Edge Detection** and related aspects. Several filtering options are illustrated and explained ranging from pure black and white edge detection to image sharpening.

### Sample Source Code

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

### Using the Sample Application

All of the concepts implemented in this article can be replicated and tested by making use of the sample application included in the associated sample source code. The sample application user interface provides several configurable options to be implemented when performing ** Gradient Based Edge Detection**. The available configuration categories are:

**,**

*Filter Type***,**

*Derivative Level***and**

*Threshold***.**

*Colour Factor Filters*Configurable ** Filter Types** exposed to the end user consist of:

When selecting this option no filtering will be applied. Source/input images are displayed reflecting no change.*None –*This option represents basic*Edge Detect Mono –*. Resulting images are only expressed in terms of black and white pixels.*Gradient Based Edge Detection*revolves around calculating pixel colour gradients. This option signifies a scenario where the pixels forming resulting images express the relevant pixel’s colour gradient, when a pixel has been determined to reflect part of an edge. If a pixel is not considered to be part of an edge, the relevant pixel’s colour value will be set to black.*Edge Detect Gradient – Gradient Based Edge Detection*– In terms of edge detection, image sharpening can be achieved by emphasising detected edges in source/input images. Emphasising edges involves combining a source/input image and an image which express only detected edges.*Sharpen*This option combines calculated colour gradients and the original colour value of a pixel on a per pixel basis when a pixel has been determined to be part of an edge. If a pixel does not form part of an edge, the pixel’s colour value is set to that of the original pixel colour.*Sharpen Gradient –*

The user interface defines two RadioButtons: ** First Derivative** and

**. These user interface options relate to the edge detection method being implemented, either**

*Second Derivative***or**

*First Order***derivative operators.**

*Second Order*Comparing a global threshold and colour gradients on a per pixel scenario forms the basis of ** Gradient Based Edge Detection**. The

**labelled**

*TrackBar***enables the user to adjust the global threshold value implemented in pixel colour gradient comparisons.**

*Threshold*The ** Colour Factor** Filters impact on the level or extent to which colours are expressed in resulting images. The three colour factors,

**,**

*Red***and**

*Green***are intended to be used in combination with the filtering options:**

*Blue***and**

*Filter Type***.**

*Threshold***Filter affects when implemented in combination:**

*Colour Factor*: Not applicable.*Filter Type – Edge Detect Mono*filtering discards all pixel colour data.*Edge Detect Mono*: If a pixel is detected as part of an edge, the pixel’s colour values will be set to the gradient calculated when evaluating edge criteria. Gradient values are multiplied by*Filter Type – Edge Detect Gradient*values before being assigned to a resulting image pixel.*Colour Factor*: The pixels which form part of an edge, in terms of the resulting image the corresponding pixels will be set to the same colour values. In addition pixel colour values in the resulting image are multiplied by with the relevant*Filter Type – Sharpen*. The image pixels not detected as part of an edge will not be multiplied with any*Colour Factor*values.*Colour Factor*: Edge detected pixels in a source/input image will have the effect of corresponding pixels in the resulting image being assigned to the calculated colour gradient value, multiplied with the relevant*Filter Type – Sharpen Gradient*value. In the scenario of a pixel not being detected as forming part of an edge,*Colour Factor*will not be implemented.*Colour Factors*: The global threshold value specified by the user determines the level of sensitivity to which edges will be detected. The degree to which edges are detected through the threshold value impacts upon whether a pixel will be multiplied with the relevant*Threshold*value.*Colour Factor*

The user has the option of saving filtered image to the local file system by clicking the ** Save Image** button. The image below is a screenshot of the

**sample application in action:**

*Gradient Based Edge Detection*### Gradient Based Edge Detection Theory

** Gradient Based Edge Detection** qualifies to be classified as a neighbouring pixel algorithm. When calculating a pixel’s value in order to determine if a pixel should be expressed as part of an edge or not, the result will be determined by:

- The values expressed by neighbouring pixels. The more intense or sudden differences that occur between neighbouring pixels will result in higher accuracy edge detection.
- A user specified global threshold value used in comparison operations acts as a cut-off value, ultimately being the final factor to determine if a pixel should be expressed as part of an edge.

In the sample source code we implement the following steps when calculating whether a pixel should be considered as part of an edge:

- Iterate each pixel that forms part of the source/input image.
- Calculate and combine
and*horizontal*for each of the colour components*vertical gradients*,*Red*and*Green*. If the sum total of each colour component’s calculated gradient exceeds the global threshold value consider the pixel being iterated as part of an edge. If the sum total of colour gradients equate to less than the global threshold implement*Blue*.*step 3* - Calculate a pixel’s
per colour component. When comparing the gradient sum total against the global threshold consider the pixel being iterated part of an edge if the sum total of gradient values exceed that of the threshold. If the total of colour gradient value do not exceed the threshold value continue to*horizontal gradient*.*step 4* - Calculate a pixel’s
per colour component. When comparing the gradient sum total against the global threshold consider the pixel being iterated part of an edge if the sum total of gradient values exceed that of the threshold. If the sum total of colour gradients do not exceed the threshold value continue to*vertical gradient*.*step 5* - Calculate and
for each of the colour components*combine diagonal gradients*,*Red*and*Green*. If the sum total of each colour component’s calculated gradient exceeds the global threshold value consider the pixel being iterated as being part of an edge. If the sum total of colour gradients equate to less than the global threshold the pixel being iterated should not be considered as part of an edge.*Blue*

If we determined that a pixel forms part of an edge, the value expressed by the corresponding pixel in the resulting image will be determined by the ** Image Filter** configuration value:

– All pixels will be set to white.*Edge Detect Mono*– Each colour component will be assigned to the related colour gradient calculated when performing edge detection. Each Colour gradient will be multiplied with the related colour factor.*Edge Detect Gradient*– The value of a resulting pixel will be calculated as the product of the corresponding source pixel and the related colour factor value.*Sharpen*– Results are calculated in terms of the sum total of the corresponding input pixel and the product of the related colour gradient and colour factor value.*Sharpen Gradient*

### Implementing Gradient Based Edge Detection

The sample source code associated with this article provides the defines of the ** GradientBasedEdgeDetectionFilter **extension method targeting the Bitmap class. This method iterates every pixel contained in the source/input image. Whilst iterating pixels the method creates a

**window/kernel covering the neighbouring pixels surrounding the pixel currently being iterated. Colour gradients are calculated from every pixel’s neighbouring pixels.**

*3×3*The following Code snippet provides the definition of the ** GradientBasedEdgeDetectionFilter **extension method:

public static Bitmap GradientBasedEdgeDetectionFilter( this Bitmap sourceBitmap, EdgeFilterType filterType, DerivativeLevel derivativeLevel, float redFactor = 1.0f, float greenFactor = 1.0f, float blueFactor = 1.0f, byte threshold = 0) { 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 derivative = (int)derivativeLevel; int byteOffset = 0; int blueGradient, greenGradient, redGradient = 0; double blue = 0, green = 0, red = 0;

bool exceedsThreshold = false;

for(int offsetY = 1; offsetY < sourceBitmap.Height - 1; offsetY++) { for (int offsetX = 1; offsetX < sourceBitmap.Width - 1; offsetX++) { byteOffset = offsetY * sourceData.Stride + offsetX * 4;

blueGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]) / derivative;

blueGradient += Math.Abs(pixelBuffer[byteOffset - sourceData.Stride] - pixelBuffer[byteOffset + sourceData.Stride]) / derivative;

byteOffset++;

greenGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]) / derivative;

greenGradient += Math.Abs(pixelBuffer[byteOffset - sourceData.Stride] - pixelBuffer[byteOffset + sourceData.Stride]) / derivative;

byteOffset++;

redGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]) / derivative;

redGradient += Math.Abs(pixelBuffer[byteOffset - sourceData.Stride] - pixelBuffer[byteOffset + sourceData.Stride]) / derivative;

if (blueGradient + greenGradient + redGradient > threshold) { exceedsThreshold = true ; } else { byteOffset -= 2;

blueGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); byteOffset++;

greenGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); byteOffset++;

redGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]);

if (blueGradient + greenGradient + redGradient > threshold) { exceedsThreshold = true ; } else { byteOffset -= 2;

blueGradient = Math.Abs(pixelBuffer[byteOffset - sourceData.Stride] - pixelBuffer[byteOffset + sourceData.Stride]);

byteOffset++;

greenGradient = Math.Abs(pixelBuffer[byteOffset - sourceData.Stride] - pixelBuffer[byteOffset + sourceData.Stride]);

byteOffset++;

redGradient = Math.Abs(pixelBuffer[byteOffset - sourceData.Stride] - pixelBuffer[byteOffset + sourceData.Stride]);

if (blueGradient + greenGradient + redGradient > threshold) { exceedsThreshold = true ; } else { byteOffset -= 2;

blueGradient = Math.Abs(pixelBuffer[byteOffset - 4 - sourceData.Stride] - pixelBuffer[byteOffset + 4 + sourceData.Stride]) / derivative;

blueGradient += Math.Abs(pixelBuffer[byteOffset - sourceData.Stride + 4] - pixelBuffer[byteOffset + sourceData.Stride - 4]) / derivative;

byteOffset++;

greenGradient = Math.Abs(pixelBuffer[byteOffset - 4 - sourceData.Stride] - pixelBuffer[byteOffset + 4 + sourceData.Stride]) / derivative;

greenGradient += Math.Abs(pixelBuffer[byteOffset - sourceData.Stride + 4] - pixelBuffer[byteOffset + sourceData.Stride - 4]) / derivative;

byteOffset++;

redGradient = Math.Abs(pixelBuffer[byteOffset - 4 - sourceData.Stride] - pixelBuffer[byteOffset + 4 + sourceData.Stride]) / derivative;

redGradient += Math.Abs(pixelBuffer[byteOffset - sourceData.Stride + 4] - pixelBuffer[byteOffset + sourceData.Stride - 4]) / derivative;

if (blueGradient + greenGradient + redGradient > threshold) { exceedsThreshold = true ; } else { exceedsThreshold = false ; } } } }

byteOffset -= 2;

if (exceedsThreshold) { if (filterType == EdgeFilterType.EdgeDetectMono) { blue = green = red = 255; } else if (filterType == EdgeFilterType.EdgeDetectGradient) { blue = blueGradient * blueFactor; green = greenGradient * greenFactor; red = redGradient * redFactor; } else if (filterType == EdgeFilterType.Sharpen) { blue = pixelBuffer[byteOffset] * blueFactor; green = pixelBuffer[byteOffset + 1] * greenFactor; red = pixelBuffer[byteOffset + 2] * redFactor; } else if (filterType == EdgeFilterType.SharpenGradient) { blue = pixelBuffer[byteOffset] + blueGradient * blueFactor; green = pixelBuffer[byteOffset + 1] + greenGradient * greenFactor; red = pixelBuffer[byteOffset + 2] + redGradient * redFactor; } } else { if (filterType == EdgeFilterType.EdgeDetectMono || filterType == EdgeFilterType.EdgeDetectGradient) { blue = green = red = 0; } else if (filterType == EdgeFilterType.Sharpen || filterType == EdgeFilterType.SharpenGradient) { 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 banner images depicting a butterfly featured throughout this article were generated using the sample application. The original image has been licenced 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 Wikipedia.

The sample image featuring a ** Scarlet Macaw** has been licensed under the Creative Commons Attribution-Share Alike 3.0 Germany license. The original image can be downloaded from Wikipedia.

*The Original Image*

*Edge Detect, Second Derivative, Threshold 50*

*Edge Detect Gradient, First Derivative, Blue*

*Edge Detect Gradient, First Derivative, Green*

*Edge Detect Gradient, First Derivative, Green and Blue*

*Edge Detect Gradient, First Derivative, Red*

*Edge Detect Gradient, First Derivative, Red and Blue*

*Edge Detect Gradient, First Derivative, Red and Green*

*Edge Detect Gradient, First Derivative, Red, Green and Blue*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Black*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Blue*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Green*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Green and Blue*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Red*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Red and Blue*

*Edge Detect Sharpen, Second Derivative, Threshold 40, Red and Green*

*Edge Detect Sharpen, Second Derivative, Threshold 40, White*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, Blue*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, Green*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, Green and Blue*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, Red*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, Red and Blue*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, Red and Green*

*Edge Detect Sharpen Gradient, First Derivative, Threshold 0, White*

### 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 filtering by directly manipulating Pixel ARGB values
- C# How to: Image filtering implemented using a ColorMatrix
- C# How to: Blending Bitmap images using colour filters
- C# How to: Bitmap Colour Substitution implementing thresholds
- C# How to: Generating Icons from Images
- C# How to: Swapping Bitmap ARGB Colour Channels
- C# How to: Bitmap Pixel manipulation using LINQ Queries
- C# How to: Linq to Bitmaps – Partial Colour Inversion
- C# How to: Bitmap Colour Balance
- C# How to: Bi-tonal Bitmaps
- C# How to: Bitmap Colour Tint
- C# How to: Bitmap Colour Shading
- C# How to: Image Solarise
- C# How to: Image Contrast
- C# How to: Bitwise Bitmap Blending
- C# How to: Image Arithmetic
- C# How to: Image Convolution
- C# How to: Image Edge Detection
- C# How to: Difference Of Gaussians
- C# How to: Image Median Filter
- C# How to: Image Unsharp Mask
- C# How to: Image Colour Average
- C# How to: Image Erosion and Dilation
- C# How to: Morphological Edge Detection
- C# How to: Boolean Edge Detection