C# How to: Boolean Edge Detection

Article purpose

The purpose of this article is to detail Boolean Function Based Edge Detection. The filtering implemented in article occurs on a per pixel basis. The implementation relies on linear algebra. No GDI+ or traditional drawing methods are required.

Sample Source Code

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

Using the Sample Application

Implemented as part of this article’s sample source code is a Sample Application. The concepts detailed in this article have all been implemented and tested using the associated Sample Application.

The first task required in using the Sample Application comes in the form of having to specify a source/input . Select files from the local system by clicking the Load Image button.

On the right-hand side of the Sample Application’s user interface the user will be presented with a set of controls which relate to various filter options. Users are able to specify implementation methods when adjusting filter options.

Filter type values are: None, Edge Detect and Sharpen.  Selecting a filter type of None results in no filtering being implemented, the original input/source will be displayed reflecting no change. When users select the Edge Detect filter type the resulting output reflects a black and white image of which only the detected edges are visible. The Sharpen filter type implements Boolean Edge Detection producing a sharpened by means of highlighting detected edges within the original input/source .

The Trackbar labelled Threshold is intended to allow users the option of reducing expressed as detected edges in the resulting . The level of present differs depending on the input/source specified, hence the option of implementing a threshold.

The three remaining TrackBar controls are labelled Red, Green and Blue. The Colour Factor filter options allows the user to specify the extend to which detected edges are expressed when sharpening an . When all three factor values are set to the same value edges appear white if the factor value exceeds zero. Factor values set to zero results in detected edges appearing as darker/black edge lines. Factor values can be set per colour value, which will have the effect of creating a coloured outline being visible in the result . The colour of the outlining effect can be controlled by adjusting individual colour factor values.

After having implemented image filtering the user has the option of saving the result to the local file system by clicking the Save Image button. The image shown below is screenshot of the Boolean Edge Detection sample application in action:

Boolean Edge Detection Sample Application

The Local Threshold and Boolean Function Based Edge Detection

Boolean Edge Detection is considered a a subset of . This method of employs both a local and global threshold. Implementation of the Boolean Edge Detection algorithm can be achieved by completing the following steps:

  1. Initiate a process of iterating each pixel that forms part of the source/input . Calculate a local threshold value based on a 3×3 /window. The should be positioned in a fashion where the pixel currently being iterated is located in the middle of the . Calculate a mean value using as input the 9 values covered by the . Create a new blank set to 3×3 dimensions. Compare each pixel in the source image to the calculated mean value. If a pixel’s value exceeds that of the mean value set the corresponding location on the blank to one. If a pixel’s value does not exceed that of the mean value set the corresponding location on the blank to zero.
  2. In the next step compare the newly created to the set of 16 edge masks. If the new is represented in the edge masks the middle pixel being iterated should be set to indicate an edge.
  3. The first two steps have to be repeated for each pixel contained in the source , in other words each pixel should be iterated. Edges should now be detected as you progress through image pixels, although false edges will also be present as a result of .
  4. False edges that were detected can be removed when implementing a global threshold. Firstly calculate the variance of each 3×3 . If the pixel currently being iterated was detected as part of an edge in step 2 and the variance calculated exceeds the global threshold the pixel can be considered as part of an edge. If the variance calculated equates to less than the global threshold a pixel does not form part of an edge even if the calculated matches one of the 16 edge masks.

The following image illustrates the 16 edge masks:

Edge Masks

Implementing Boolean Edge Detection

The sample source code implements the BooleanEdgeDetectionFilter targeting the class. This method implements the Boolean Edge Detection theoretical steps discussed in the previous section.

In order to determine if a newly calculated , as described in step 1, matches any of the 16 pre-defined edge masks the BooleanEdgeDetectionFilter implements comparison. The reasoning behind string based edge mask comparison boils down to efficiency, both in terms of reducing code complexity and improving performance. The method defines a generic List of type string and then proceeds to add 16 , each representing an edge mask. Edge mask strings express an edge mask in terms of a row and column format. The following code snippet lists the 16 edge masks strings being defined:

List<string> edgeMasks = new List<string>();

edgeMasks.Add("011011011"); edgeMasks.Add("000111111"); edgeMasks.Add("110110110"); edgeMasks.Add("111111000"); edgeMasks.Add("011011001"); edgeMasks.Add("100110110"); edgeMasks.Add("111011000"); edgeMasks.Add("111110000"); edgeMasks.Add("111011001"); edgeMasks.Add("100110111"); edgeMasks.Add("001011111"); edgeMasks.Add("111110100"); edgeMasks.Add("000011111"); edgeMasks.Add("000110111"); edgeMasks.Add("001011011"); edgeMasks.Add("001011011"); edgeMasks.Add("110110100");

The following code snippet list the complete implementation of the BooleanEdgeDetectionFilter :

public static Bitmap BooleanEdgeDetectionFilter( 
                                this Bitmap sourceBitmap, 
                                BooleanFilterType filterType, 
                                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);
List<string> edgeMasks = new List<string>();
edgeMasks.Add("011011011"); edgeMasks.Add("000111111"); edgeMasks.Add("110110110"); edgeMasks.Add("111111000"); edgeMasks.Add("011011001"); edgeMasks.Add("100110110"); edgeMasks.Add("111011000"); edgeMasks.Add("111110000"); edgeMasks.Add("111011001"); edgeMasks.Add("100110111"); edgeMasks.Add("001011111"); edgeMasks.Add("111110100"); edgeMasks.Add("000011111"); edgeMasks.Add("000110111"); edgeMasks.Add("001011011"); edgeMasks.Add("001011011"); edgeMasks.Add("110110100");
int filterOffset = 1; int calcOffset = 0;
int byteOffset = 0; int matrixMean = 0; int matrixTotal = 0; double matrixVariance = 0;
double blueValue = 0; double greenValue = 0; double redValue = 0;
string matrixPatern = String.Empty;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++) { byteOffset = offsetY * sourceData.Stride + offsetX * 4;
matrixMean = 0; matrixTotal = 0; matrixVariance = 0;
matrixPatern = String.Empty;
//Step 1: Calculate local matrix for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
matrixMean += pixelBuffer[calcOffset]; matrixMean += pixelBuffer[calcOffset + 1]; matrixMean += pixelBuffer[calcOffset + 2]; } }
matrixMean = matrixMean / 9;
//Step 4: Calculate Variance for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
matrixTotal = pixelBuffer[calcOffset]; matrixTotal += pixelBuffer[calcOffset+1]; matrixTotal += pixelBuffer[calcOffset+2];
matrixPatern += (matrixTotal > matrixMean ? "1" : "0" );
matrixVariance += Math.Pow(matrixMean - (pixelBuffer[calcOffset] + pixelBuffer[calcOffset + 1] + pixelBuffer[calcOffset + 2]), 2); } }
matrixVariance = matrixVariance / 9;
if (filterType == BooleanFilterType.Sharpen) { blueValue = pixelBuffer[byteOffset]; greenValue = pixelBuffer[byteOffset + 1]; redValue = pixelBuffer[byteOffset + 2];
//Step 4: Exlclude noise using global // threshold if (matrixVariance > threshold) { //Step 2: Compare newly calculated // matrix and image masks if (edgeMasks.Contains(matrixPatern)) { blueValue = (blueValue * blueFactor); greenValue = (greenValue * greenFactor); redValue = (redValue * redFactor);
blueValue = (blueValue > 255 ? 255 : (blueValue < 0 ? 0 : blueValue));
greenValue = (greenValue > 255 ? 255 : (greenValue < 0 ? 0 : greenValue));
redValue = (redValue > 255 ? 255 : (redValue < 0 ? 0 : redValue)); } } } //Step 4: Exlclude noise using global // threshold //Step 2: Compare newly calculated // matrix and image masks else if (matrixVariance > threshold && edgeMasks.Contains(matrixPatern)) { blueValue = 255; greenValue = 255; redValue = 255; } else { blueValue = 0; greenValue = 0; redValue = 0; }
resultBuffer[byteOffset] = (byte)blueValue; resultBuffer[byteOffset + 1] = (byte)greenValue; resultBuffer[byteOffset + 2] = (byte)redValue; 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

This article features a photograph of The Eiffel Tower used in generating sample . The original image has been licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license and can be downloaded from Wikipedia: Original Image.

The Original Image

Tour_Eiffel_Wikimedia_Commons

Edge Detection, Threshold 50

Boolean Edge Detection Threshold 50

Sharpen, Threshold 50, Blue

Boolean Edge Detection Threshold 50 Sharpen Blue

Sharpen, Threshold 50, Green

Boolean Edge Detection Threshold 50 Sharpen Green

Sharpen, Threshold 50, Green and Blue

Boolean Edge Detection Threshold 50 Sharpen Green Blue

Sharpen, Threshold 50, Red

Boolean Edge Detection Threshold 50 Sharpen Red

Sharpen, Threshold 50, Red and Blue

Boolean Edge Detection Threshold 50 Sharpen Red Blue

Sharpen, Threshold 50, Red and Green

Boolean Edge Detection Threshold 50 Sharpen Red Green

Sharpen, Threshold 50, White – Red, Green and Blue

Boolean Edge Detection Threshold 50 Sharpen 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:

31 Responses to “C# How to: Boolean Edge Detection”



  1. 1 C# How to: Gradient Based Edge Detection | Software by Default Trackback on June 1, 2013 at 4:47 PM
  2. 2 C# How to: Image Cartoon Effect | Software by Default Trackback on June 2, 2013 at 4:13 PM
  3. 3 C# How to: Sharpen Edge Detection | Software by Default Trackback on June 7, 2013 at 5:12 AM
  4. 4 C# How to: Calculating Gaussian Kernels | Software by Default Trackback on June 8, 2013 at 10:59 AM
  5. 5 C# How to: Image Blur | Software by Default Trackback on June 9, 2013 at 10:20 PM
  6. 6 C# How to: Image Transform Rotate | Software by Default Trackback on June 16, 2013 at 10:40 AM
  7. 7 C# How to: Image Transform Shear | Software by Default Trackback on June 16, 2013 at 5:45 PM
  8. 8 C# How to: Compass Edge Detection | Software by Default Trackback on June 22, 2013 at 9:35 PM
  9. 9 C# How to: Oil Painting and Cartoon Filter | Software by Default Trackback on June 30, 2013 at 10:48 AM
  10. 10 C# How to: Stained Glass Image Filter | Software by Default Trackback on June 30, 2013 at 10:50 AM
  11. 11 C# How to: Morphological Edge Detection | Software by Default Trackback on June 30, 2013 at 2:02 PM
  12. 12 C# How to: Image Erosion and Dilation | Software by Default Trackback on June 30, 2013 at 2:10 PM
  13. 13 C# How to: Image Colour Average | Software by Default Trackback on June 30, 2013 at 2:20 PM
  14. 14 C# How to: Image Unsharp Mask | Software by Default Trackback on June 30, 2013 at 2:28 PM
  15. 15 C# How to: Image Median Filter | Software by Default Trackback on June 30, 2013 at 3:16 PM
  16. 16 C# How to: Difference Of Gaussians | Software by Default Trackback on June 30, 2013 at 3:27 PM
  17. 17 C# How to: Image Edge Detection | Software by Default Trackback on June 30, 2013 at 3:33 PM
  18. 18 C# How to: Image Convolution | Software by Default Trackback on June 30, 2013 at 3:55 PM
  19. 19 C# How to: Generate a Web Service from WSDL | Software by Default Trackback on June 30, 2013 at 4:08 PM
  20. 20 C# How to: Decoding/Converting Base64 strings to Bitmap images | Software by Default Trackback on June 30, 2013 at 4:14 PM
  21. 21 C# How to: Bitmap Colour Substitution implementing thresholds | Software by Default Trackback on July 6, 2013 at 4:33 PM
  22. 22 C# How to: Swapping Bitmap ARGB Colour Channels | Software by Default Trackback on July 6, 2013 at 5:02 PM
  23. 23 C# How to: Image filtering by directly manipulating Pixel ARGB values | Software by Default Trackback on July 8, 2013 at 2:58 AM
  24. 24 C# How to: Image ASCII Art | Software by Default Trackback on July 14, 2013 at 7:22 AM
  25. 25 C# How to: Weighted Difference of Gaussians | Software by Default Trackback on July 14, 2013 at 8:11 PM
  26. 26 C# How to: Image Boundary Extraction | Software by Default Trackback on July 21, 2013 at 10:24 AM
  27. 27 C# How to: Image Abstract Colours Filter | Software by Default Trackback on July 28, 2013 at 7:41 PM
  28. 28 C# How to: Fuzzy Blur Filter | Software by Default Trackback on August 9, 2013 at 6:38 AM
  29. 29 C# How to: Image Distortion Blur | Software by Default Trackback on August 9, 2013 at 10:13 PM
  30. 30 C# How to: Standard Deviation Edge Detection | Software by Default Trackback on August 8, 2015 at 8:10 AM
  31. 31 C# How to: Min/Max Edge Detection | Software by Default Trackback on August 9, 2015 at 11:30 AM

Leave a comment




Dewald Esterhuizen

Blog Stats

  • 869,810 hits

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

Join 228 other subscribers

Archives

RSS SoftwareByDefault on MSDN

  • An error has occurred; the feed is probably down. Try again later.