Article Purpose
This purpose of this article is to explain and illustrate in detail the requirements involved in calculating Gaussian Kernels intended for use in image convolution when implementing Gaussian Blur filters. This article’s discussion spans from exploring concepts in theory and continues on to implement concepts through C# sample source code.
Ant: Gaussian Kernel 5×5 Weight 19
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
A Sample Application forms part of the accompanying sample source code, intended to implement the topics discussed and also provides the means to replicate and test the concepts being illustrated.
The sample application is a Windows Forms based application which provides functionality enabling users to generate/calculate Gaussian Kernels. Calculation results are influenced through user specified options in the form of: Kernel Size and Weight.
Ladybird: Gaussian Kernel 5×5 Weight 5.5
In the sample application and related sample source code when referring to Kernel Size, a reference is being made relating to the physical size dimensions of the kernel/matrix used in convolution. When higher values are specified in setting the Kernel Size, the resulting output image will reflect a greater degree of blurring. Kernel Sizes being specified as lower values result in the output image reflecting a lesser degree of blurring.
In a similar fashion to the Kernel size value, the Weight value provided when generating a Kernel results in smoother/more blurred images when specified as higher values. Lower values assigned to the Weight value has the expected result of less blurring being evident in output images.
Prey Mantis: Gaussian Kernel 13×13 Weight 13
The sample application has the ability to provide the user with a visual representation implementing the calculated kernel value blurring. Users are able to select source/input image from the local file system by clicking the Load Image button. When desired, users are able to save blurred/filtered images to the local file system by clicking the Save Image button.
The image below is screenshot of the Gaussian Kernel Calculator sample application in action:
Calculating Gaussian Convolution Kernels
The formula implemented in calculating Gaussian Kernels can be implemented in C# source code fairly easily. Once the method in which the formula operates has been grasped the actual code implementation becomes straight forward.
The Gaussian Kernel formula can be expressed as follows:
The formula contains a number of symbols, which define how the filter will be implemented. The symbols forming part of the Gaussian Kernel formula are described in the following list:
- G(x y) – A value calculated using the Gaussian Kernel formula. This value forms part of a Kernel, representing a single element.
- π – Pi, one of the better known members of the Greek alphabet. The mathematical constant defined as 22 / 7.
- σ – The lower case version of the Greek alphabet letter Sigma. This symbol simply represents a threshold or factor value, as specified by the user.
- e – The formula references a lower case e symbol. The symbol represents Euler’s number. The value of Euler’s number has been defined as a mathematical constant equating to 2.71828182846.
- x, y – The variables referenced as x and y relate to pixel coordinates within an image. y Representing the vertical offset or row and x represents the horizontal offset or column.
Note: The formula’s implementation expects x and y to equal zero values when representing the coordinates of the pixel located in the middle of the kernel.
Ladybird: Gaussian Kernel 13×13 Weight 9.5
When calculating the kernel elements, the coordinate values expressed by x and y should reflect the distance in pixels from the middle pixel. All coordinate values must be greater than zero.
In order to gain a better grasp on the Gaussian kernel formula we can implement the formula in steps. If we were to create a 3×3 kernel and specified a weighting value of 5.5 our calculations can start off as indicated by the following illustration:
The formula has been implement on each element forming part of the kernel, 9 values in total. Coordinate values have now been replaced with actual values, differing for each position/element. Calculating zero to the power of two equates to zero. In the scenario above indicating zeros which express exponential values might help to ease initial understanding, as opposed to providing simplified values and potentially causing confusing scenarios. The following image illustrates the calculated values of each kernel element:
Ant: Gaussian Kernel 9×9 Weight 19
An important requirement to take note of at this point being that the sum total of all the elements contained as part of a kernel/matrix must equate to one. Looking at our calculated results that is not the case. The kernel needs to be modified in order to satisfy the requirement of having a sum total value of 1 when adding together all the elements of the kernel.
At this point the sum total of the kernel equates to 0.046322548968. We can correct the kernel values, ensuring the sum total of all kernel elements equate to 1. The kernel values should be updated by multiplying each element by one divided by the current kernel sum. In other words each item should be multiplied by:
1.0 / 0.046322548968
After updating the kernel by multiplying each element with the values mentioned above, the result as follows:
We have now successfully calculated a 3×3 Gaussian Blur kernel matrix which implements a weight value of 5.5. Implementing the Gaussian blur has the following effect:
Rose: Gaussian Kernel 3×3 Weight 5.5
The Original Image
The calculated Gaussian Kernel can now be implemented when performing image convolution.
Implementing Gaussian Kernel Calculations
In this section of the article we will be exploring how to implement Gaussian Blur kernel calculations in terms of C# code. Defined as part of the sample source code the definition of the static MatrixCalculator class, exposing the static Calculate method. All of the formula calculation tasks discussed in the previous section have been implemented within this method.
As parameter values the method expects a value indicating the kernel size and a value representing the Weight value. The Calculate method returns a two dimensional array of type double. The return value array represents the calculated kernel.
The definition of the MatrixCalculator.Calculate method as follows:
public static double[,] Calculate(int length, double weight) { double[,] Kernel = new double [length, lenght]; double sumTotal = 0; int kernelRadius = lenght / 2; double distance = 0; double calculatedEuler = 1.0 / (2.0 * Math.PI * Math.Pow(weight, 2)); for (int filterY = -kernelRadius; filterY <= kernelRadius; filterY++) { for (int filterX = -kernelRadius; filterX <= kernelRadius; filterX++) { distance = ((filterX * filterX) + (filterY * filterY)) / (2 * (weight * weight)); Kernel[filterY + kernelRadius, filterX + kernelRadius] = calculatedEuler * Math.Exp(-distance); sumTotal += Kernel[filterY + kernelRadius, filterX + kernelRadius]; } } for (int y = 0; y < lenght; y++) { for (int x = 0; x < lenght; x++) { Kernel[y, x] = Kernel[y, x] * (1.0 / sumTotal); } } return Kernel; }
Ladybird: Gaussian Kernel 19×19 Weight 9.5
The sample source code provides the definition of the ConvolutionFilter extension method, targeting the Bitmap class. This method accepts as a parameter a two dimensional array representing the matrix kernel to implement when performing image convolution. The matrix kernel value passed to this function originates from the calculated Gaussian kernel.
Detailed below is the definition of the ConvolutionFilter extension method:
public static Bitmap ConvolutionFilter(this Bitmap sourceBitmap, double[,] filterMatrix, double factor = 1, int bias = 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); double blue = 0.0; double green = 0.0; double red = 0.0; int filterWidth = filterMatrix.GetLength(1); int filterHeight = filterMatrix.GetLength(0); int filterOffset = (filterWidth-1) / 2; int calcOffset = 0; int byteOffset = 0; for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++) { blue = 0; green = 0; red = 0; byteOffset = offsetY * sourceData.Stride + offsetX * 4; for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride); blue += (double )(pixelBuffer[calcOffset]) * filterMatrix[filterY + filterOffset, filterX + filterOffset]; green += (double )(pixelBuffer[calcOffset + 1]) * filterMatrix[filterY + filterOffset, filterX + filterOffset]; red += (double )(pixelBuffer[calcOffset + 2]) * filterMatrix[filterY + filterOffset, filterX + filterOffset]; } } blue = factor * blue + bias; green = factor * green + bias; red = factor * red + bias; blue = (blue > 255 ? 255 : (blue < 0 ? 0 : blue)); green = (green > 255 ? 255 : (green < 0 ? 0 : green)); red = (red > 255 ? 255 : (red < 0 ? 0 : blue)); 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; }
Ant: Gaussian Kernel 7×7 Weight 19
Sample Images
This article features a number of sample images. All featured images have been licensed allowing for reproduction.
The sample images featuring an image of a prey mantis is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license and can be downloaded from Wikipedia.
The sample images featuring an image of an ant has been released into the public domain by its author, Sean.hoyland. This applies worldwide. In some countries this may not be legally possible; if so: Sean.hoyland grants anyone the right to use this work for any purpose, without any conditions, unless such conditions are required by law. The original image can be downloaded from Wikipedia.
The sample images featuring an image of a ladybird (ladybug or lady beetle) is licensed under the Creative Commons Attribution-Share Alike 2.0 Generic license and can be downloaded from Wikipedia.
The sample images featuring an image of a wasp is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license and can be downloaded from Wikimedia.org.
The Original Image
Wasp Gaussian Kernel 3×3 Weight 9.25
Wasp Gaussian Kernel 5×5 Weight 9.25
Wasp Gaussian Kernel 7×7 Weight 9.25
Wasp Gaussian Kernel 9×9 Weight 9.25
Wasp Gaussian Kernel 11×11 Weight 9.25
Wasp Gaussian Kernel 13×13 Weight 9.25
Wasp Gaussian Kernel 15×15 Weight 9.25
Wasp Gaussian Kernel 17×17 Weight 9.25
Wasp Gaussian Kernel 19×19 Weight 9.25
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
- C# How to: Gradient Based Edge Detection
- C# How to: Sharpen Edge Detection
- C# How to: Image Blur
- C# How to: Image Transform Rotate
- C# How to: Image Transform Shear
- C# How to: Compass Edge Detection
- C# How to: Oil Painting and Cartoon Filter
- C# How to: Stained Glass Image Filter