Posts Tagged 'Image Blur'

C# How to: Image Distortion Blur

Article Purpose

This article explores the process of implementing an Image Distortion Blur filter. This image filter is classified as a non-photo realistic image filter, primarily implemented in rendering artistic effects.

Flower: Distortion Factor 15

Flower: Distortion Factor 15

Sample Source Code

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

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Using the Sample Application

The sample source code that accompanies this article includes a based sample application. The concepts explored in this article have all been implemented as part of the sample application. From an end user perspective the following configurable options are available:

  • Load/Save Images – Clicking the Load Image button allows a user to specify a source/input . If desired, output filtered can be saved to the local system by clicking the Save Image button.
  • Distortion Factor – The level or intensity of distortion applied when implementing the filter can be specified when adjusting the Distortion Factor through the user interface. Lower factor values result in less distortion being evident in resulting . Specifying higher factor values result in more intense distortion being applied.

The following image is screenshot of the Image Distortion Blur sample application:

ImageDistortionBlur_SampleApplication

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Image Distortion

In this article and the accompanying sample source code are distorted through slightly adjusting each individual ’s coordinates. The direction and distance by which coordinates are adjusted differ per as a result of being randomly selected. The maximum distance offset applied depends on the user specified Distortion Factor. Once all coordinates have been updated, implementing a provides smoothing and an effect.

Applying an Image Distortion Filter requires implementing the following steps:

  1. Iterate Pixels – Each forming part of the source/input should be iterated.
  2. Calculate new Coordinates – For every being iterated generate two random values representing XY-coordinate offsets to be applied to a ’s current coordinates. Offset values can equate to less than zero in order to represent coordinates above or to the left of the current .
  3. Apply Median Filter – The newly offset will appear somewhat speckled in the resulting . Applying a reduces the speckled appearance whilst retaining a distortion effect.

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Median Filter

Applying a is the final step required when implementing an Image Distortion Blur filter. are often implemented in reducing . The method of image distortion illustrated in this article express similarities when compared to . In order to soften the appearance of we implement a .

A can be applied through implementing the following steps:

  1. Iterate Pixels – Each forming part of the source/input should be iterated.
  2. Inspect Pixel Neighbourhood – Each neighbouring in relation to the currently being iterated should be added to a temporary collection.
  3. Determine Neighbourhood Median – Once all neighbourhood have been added to a temporary collection, sort the collection by value. The element value located at the middle of the collection represents the neighbourhood’s value.

Flower: Distortion Factor 10

Flower: Distortion Factor 10

Flower: Distortion Factor 15

Flower: Distortion Factor 15

Implementing Image Distortion

The sample source code defines the DistortionBlurFilter method, an targeting the class. The following code snippet illustrates the implementation:

public static Bitmap DistortionBlurFilter( 
         this Bitmap sourceBitmap, int distortFactor) 
{
    byte[] pixelBuffer = sourceBitmap.GetByteArray(); 
    byte[] resultBuffer = sourceBitmap.GetByteArray(); 

int imageStride = sourceBitmap.Width * 4; int calcOffset = 0, filterY = 0, filterX = 0; int factorMax = (distortFactor + 1) * 2; Random rand = new Random();
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { filterY = distortFactor - rand.Next(0, factorMax); filterX = distortFactor - rand.Next(0, factorMax);
if (filterX * 4 + (k % imageStride) < imageStride && filterX * 4 + (k % imageStride) > 0) { calcOffset = k + filterY * imageStride + 4 * filterX;
if (calcOffset >= 0 && calcOffset + 4 < resultBuffer.Length) { resultBuffer[calcOffset] = pixelBuffer[k]; resultBuffer[calcOffset + 1] = pixelBuffer[k + 1]; resultBuffer[calcOffset + 2] = pixelBuffer[k + 2]; } } }
return resultBuffer.GetImage(sourceBitmap.Width, sourceBitmap.Height).MedianFilter(3); }

Flower: Distortion Factor 15

Flower: Distortion Factor 15

Implementing a Median Filter

The MedianFilter targets the class. The implementation as follows:

public static Bitmap MedianFilter(this Bitmap sourceBitmap, 
                                  int matrixSize) 
{ 
    byte[] pixelBuffer = sourceBitmap.GetByteArray(); 
    byte[] resultBuffer = new byte[pixelBuffer.Length]; 
    byte[] middlePixel; 

int imageStride = sourceBitmap.Width * 4; int filterOffset = (matrixSize - 1) / 2; int calcOffset = 0, filterY = 0, filterX = 0; List<int> neighbourPixels = new List<int>();
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { filterY = -filterOffset; filterX = -filterOffset; neighbourPixels.Clear();
while (filterY <= filterOffset) { calcOffset = k + (filterX * 4) + (filterY * imageStride);
if (calcOffset > 0 && calcOffset + 4 < pixelBuffer.Length) { neighbourPixels.Add(BitConverter.ToInt32( pixelBuffer, calcOffset)); }
filterX++;
if (filterX > filterOffset) { filterX = -filterOffset; filterY++; } }
neighbourPixels.Sort(); middlePixel = BitConverter.GetBytes( neighbourPixels[filterOffset]);
resultBuffer[k] = middlePixel[0]; resultBuffer[k + 1] = middlePixel[1]; resultBuffer[k + 2] = middlePixel[2]; resultBuffer[k + 3] = middlePixel[3]; }
return resultBuffer.GetImage(sourceBitmap.Width, sourceBitmap.Height); }

Flower: Distortion Factor 25

Flower: Distortion Factor 25

Sample Images

This article features a number of sample images. All featured images have been licensed allowing for reproduction. The following images feature as sample images:

674px-Lil_chalcedonicum_01EB_Griechenland_Hrisomiglia_17_07_01

683px-Lil_carniolicum_subsp_ponticum_01EB_Tuerkei_Ikizdere_02_07_93

1022px-LiliumSargentiae

1024px-Lilium_longiflorum_(Easter_Lily)

1280px-LiliumBulbiferumCroceumBologna

LiliumSuperbum1

Orange_Lilium_-_Relic38_-_Ontario_Canada

White_and_yellow_flower

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: Fuzzy Blur Filter

Article Purpose

This article serves to illustrate the concepts involved in implementing a Fuzzy Blur Filter. This filter results in rendering  non-photo realistic images which express a certain artistic effect.

Frog: Filter Size 19×19

Frog: Filter Size 19x19

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

The sample source code accompanying this article includes a based test application. The concepts explored throughout this article can be replicated/tested using the sample application.

When executing the sample application the user interface exposes a number of configurable options:

  • Loading and Saving Images – Users are able to load source/input from the local system by clicking the Load Image button. Clicking the Save Image button allow users to save filter result .
  • Filter Size – The specified filter size affects the filter intensity. Smaller filter sizes result in less blurry being rendered, whereas larger filter sizes result in more blurry being rendered.
  • Edge Factors – The contrast of fuzzy expressed in resulting depend on the specified edge factor values. Values less than one result in detected being darkened and values greater than one result in detected image edges being lightened.

The following image is a screenshot of the Fuzzy Blur Filter sample application in action:

Fuzzy Blur Filter Sample Application

Frog: Filter Size 9×9

Frog: Filter Size 9x9

Fuzzy Blur Overview

The Fuzzy Blur Filter relies on the interference of when performing in order to create a fuzzy effect. In addition results from performing a .

The steps involved in performing a Fuzzy Blur Filter can be described as follows:

  1. Edge Detection and Enhancement – Using the first edge factor specified enhance by performing Boolean Edge detection. Being sensitive to , a fair amount of detected will actually be in addition to actual .
  2. Mean Filter Blur – Using the edge enhanced created in the previous step perform a blur. The enhanced edges will be blurred since a does not have edge preservation properties. The size of the implemented depends on a user specified value.
  3. Edge Detection and Enhancement –  Using the blurred created in the previous step once again perform Boolean Edge detection, enhancing detected edges according to the second edge factor specified.

Frog: Filter Size 9×9

Frog: Filter Size 9x9

Mean Filter

A Blur, also known as a , can be performed through . The size of the / implemented when preforming will be determined through user input.

Every / element should be set to one. The resulting value should be multiplied by a factor value equating to one divided by the / size. As an example, a / size of 3×3 can be expressed as follows:

Mean Kernel

An alternative expression can also be:

Mean Kernel

Frog: Filter Size 9×9

Frog: Filter Size 9x9

Boolean Edge Detection without a local threshold

When performing Boolean Edge Detection a local threshold should be implemented in order to exclude . In this article we rely on the interference of in order to render a fuzzy effect. By not implementing a local threshold when performing Boolean Edge detection the sample source code ensures sufficient interference from .

The steps involved in performing Boolean Edge Detection without a local threshold can be described as follows:

  1. Calculate Neighbourhood Mean – Iterate each forming part of the source/input . Using a 3×3 size calculate the mean value of the neighbourhood surrounding the currently being iterated.
  2. Create Mean comparison Matrix – Once again using a 3×3 size compare each neighbourhood to the newly calculated mean value. Create a temporary 3×3 size , each element’s value should be the result of mean comparison. Should the value expressed by a neighbourhood exceed the mean value the corresponding temporary element should be set to one. When the calculated mean value exceeds the value of a neighbourhood the corresponding temporary  element should be set to zero.
  3. Compare Edge Masks – Using sixteen predefined edge masks compare the temporary created in the previous step to each edge mask. If the temporary matches one of the predefined edge masks multiply the specified factor to the currently being iterated.

Note: A detailed article on Boolean Edge detection implementing a local threshold can be found here:

Frog: Filter Size 9×9

Frog: Filter Size 9x9

The sixteen predefined edge masks each represent an in a different direction. The predefined edge masks can be expressed as:

Boolean Edge Masks

Frog: Filter Size 13×13

Frog: Filter Size 13x13

Implementing a Mean Filter

The sample source code defines the MeanFilter method, an targeting the class. The definition listed as follows:

private static Bitmap MeanFilter(this Bitmap sourceBitmap, 
                                 int meanSize)
{
    byte[] pixelBuffer = sourceBitmap.GetByteArray(); 
    byte[] resultBuffer = new byte[pixelBuffer.Length];

double blue = 0.0, green = 0.0, red = 0.0; double factor = 1.0 / (meanSize * meanSize);
int imageStride = sourceBitmap.Width * 4; int filterOffset = meanSize / 2; int calcOffset = 0, filterY = 0, filterX = 0;
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { blue = 0; green = 0; red = 0; filterY = -filterOffset; filterX = -filterOffset;
while (filterY <= filterOffset) { calcOffset = k + (filterX * 4) + (filterY * imageStride);
calcOffset = (calcOffset < 0 ? 0 : (calcOffset >= pixelBuffer.Length - 2 ? pixelBuffer.Length - 3 : calcOffset));
blue += pixelBuffer[calcOffset]; green += pixelBuffer[calcOffset + 1]; red += pixelBuffer[calcOffset + 2];
filterX++;
if (filterX > filterOffset) { filterX = -filterOffset; filterY++; } }
resultBuffer[k] = ClipByte(factor * blue); resultBuffer[k + 1] = ClipByte(factor * green); resultBuffer[k + 2] = ClipByte(factor * red); resultBuffer[k + 3] = 255; }
return resultBuffer.GetImage(sourceBitmap.Width, sourceBitmap.Height); }

Frog: Filter Size 19×19

Frog: Filter Size 19x19

Implementing Boolean Edge Detection

Boolean Edge detection is performed in the sample source code through the implementation of the BooleanEdgeDetectionFilter method. This method has been defined as an targeting the class.

The following code snippet provides the definition of the BooleanEdgeDetectionFilter :

public static Bitmap BooleanEdgeDetectionFilter( 
       this Bitmap sourceBitmap, float edgeFactor) 
{
    byte[] pixelBuffer = sourceBitmap.GetByteArray(); 
    byte[] resultBuffer = new byte[pixelBuffer.Length]; 
    Buffer.BlockCopy(pixelBuffer, 0, resultBuffer, 
                     0, pixelBuffer.Length); 

List<string> edgeMasks = GetBooleanEdgeMasks(); int imageStride = sourceBitmap.Width * 4; int matrixMean = 0, pixelTotal = 0; int filterY = 0, filterX = 0, calcOffset = 0; string matrixPatern = String.Empty;
for (int k = 0; k + 4 < pixelBuffer.Length; k += 4) { matrixPatern = String.Empty; matrixMean = 0; pixelTotal = 0; filterY = -1; filterX = -1;
while (filterY < 2) { calcOffset = k + (filterX * 4) + (filterY * imageStride);
calcOffset = (calcOffset < 0 ? 0 : (calcOffset >= pixelBuffer.Length - 2 ? pixelBuffer.Length - 3 : calcOffset)); matrixMean += pixelBuffer[calcOffset]; matrixMean += pixelBuffer[calcOffset + 1]; matrixMean += pixelBuffer[calcOffset + 2];
filterX += 1;
if (filterX > 1) { filterX = -1; filterY += 1; } }
matrixMean = matrixMean / 9; filterY = -1; filterX = -1;
while (filterY < 2) { calcOffset = k + (filterX * 4) + (filterY * imageStride);
calcOffset = (calcOffset < 0 ? 0 : (calcOffset >= pixelBuffer.Length - 2 ? pixelBuffer.Length - 3 : calcOffset));
pixelTotal = pixelBuffer[calcOffset]; pixelTotal += pixelBuffer[calcOffset + 1]; pixelTotal += pixelBuffer[calcOffset + 2]; matrixPatern += (pixelTotal > matrixMean ? "1" : "0"); filterX += 1;
if (filterX > 1) { filterX = -1; filterY += 1; } }
if (edgeMasks.Contains(matrixPatern)) { resultBuffer[k] = ClipByte(resultBuffer[k] * edgeFactor);
resultBuffer[k + 1] = ClipByte(resultBuffer[k + 1] * edgeFactor);
resultBuffer[k + 2] = ClipByte(resultBuffer[k + 2] * edgeFactor); } }
return resultBuffer.GetImage(sourceBitmap.Width, sourceBitmap.Height); }

Frog: Filter Size 13×13

Frog: Filter Size 13x13

The predefined edge masks implemented in mean comparison have been wrapped by the GetBooleanEdgeMasks method. The definition as follows:

public static List<string> GetBooleanEdgeMasks() 
{
    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("110110100");
return edgeMasks; }

Frog: Filter Size 19×19

Frog: Filter Size 19x19

Implementing a Fuzzy Blur Filter

The FuzzyEdgeBlurFilter method serves as the implementation of a Fuzzy Blur Filter. As discussed earlier a Fuzzy Blur Filter involves enhancing through Boolean Edge detection, performing a blur and then once again performing Boolean Edge detection. This method has been defined as an extension method targeting the class.

The following code snippet provides the definition of the FuzzyEdgeBlurFilter method:

public static Bitmap FuzzyEdgeBlurFilter(this Bitmap sourceBitmap,  
                                         int filterSize,  
                                         float edgeFactor1,  
                                         float edgeFactor2) 
{
    return  
    sourceBitmap.BooleanEdgeDetectionFilter(edgeFactor1). 
    MeanFilter(filterSize).BooleanEdgeDetectionFilter(edgeFactor2); 
}

Frog: Filter Size 3×3

Frog: Filter Size 3x3

Sample Images

This article features a number of sample images. All featured images have been licensed allowing for reproduction. The following images feature as sample images:

Litoria_tyleri

Schrecklicherpfeilgiftfrosch-01

Dendropsophus_microcephalus_-_calling_male_(Cope,_1886)

Atelopus_zeteki1

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 Transform Rotate

Article Purpose

This article provides a discussion exploring the concept of rotation as a . In addition to conventional rotation this article illustrates the concept of individual colour channel rotation.

Daisy: Rotate Red 0o, Green 10o, Blue 20o

Daisy Rotate Red 0 Green 10 Blue 20 

Sample Source Code

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

Using the Sample Application

A Sample Application has been included in the sample source code that accompanies this article. The sample application serves as an implementation of the concepts discussed throughout this article. Concepts can be easily tested and replicated using the sample application.

Daisy: Rotate Red 15o, Green 5o, Blue 10o

Daisy Rotate Red 15 Green 5 Blue 10

When using the sample application users are able to load source/input from the local system by clicking the Load Image button. Required user input via the user interface can be found in the form of three numeric up/down controls labelled Blue, Green and Red respectively. Each control represents the degree to which the related colour component should be rotated. Possible input values range from –360 to 360. Positive values result in clockwise rotation, whereas negative values result in counter clockwise rotation. The sample application enables users to save result to the local file system by clicking the Save Image button.

The following image is a screenshot of the Image Transform Rotate sample application in action:

Image Transform Rotate Sample Application

Image Rotation Transformation

A applied to an from a theoretical point of view is based in . From we learn the following :

In mathematics, transformation geometry (or transformational geometry) is the name of a mathematical and approach to the study of by focusing on groups of , and the properties of figures that are under them. It is opposed to the classical synthetic geometry approach of Euclidean geometry, that focus on geometric constructions.

Rose: Rotate Red –20o, Green 0o, Blue 20o

Rose Rotate Red -20 Green 0 Blue 20

In this article rotation is implemented through applying a set algorithm to the coordinates of each pixel forming part of a source/input . In the corresponding result the calculated rotated pixel coordinates in terms of colour channel values will be assigned to the colour channel values of the original pixel.

The algorithms implemented when calculating  a pixel’s rotated coordinates can be expressed as follows:

RotateX_Algorithm

RotateY_Algorithm

Symbols/variables contained in the algorithms:

  • R (x) : The result of rotating a pixel’s x-coordinate.
  • R (y) : The result of rotating a pixel’s y-coordinate.
  • x : The source pixel’s x-coordinate.
  • y : The source pixel’s y-coordinate.
  • W : The width in pixels of the source .
  • H : The height in pixels of the source .
  • ɑ : The lower case Greek alphabet letter alpha. The value represented by alpha reflects the degree of rotation.

Butterfly: Rotate Red 10o, Green 0o, Blue 0o

Butterfly Rotate Red 10 Green 0 Blue 0

In order to apply a each pixel forming part of the source/input should be iterated. The algorithms expressed above should be applied to each pixel.

The pixel coordinates located at exactly the middle of an can be calculated through dividing the width with a factor of two in regards to the X-coordinate. The Y-coordinate can be calculated through dividing the height also with a factor of two. The algorithms calculate the coordinates of the middle pixel and implements the coordinates as offsets. Implementing the pixel offsets  results in being rotated around the ’s middle, as opposed to the the top left pixel (0,0).

This article and the associated sample source code extends the concept of traditional rotation through implementing rotation on a per colour channel basis. Through user input the individual degree of rotation can be specified for each colour channel, namely Red, Green and Blue. Functionality has been implemented allowing each colour channel to be rotated to a different degree. In essence the algorithms described above have to be implemented three times per pixel iterated.

Daisy: Rotate Red 30o, Green 0o, Blue 180o

Daisy Rotate Red 30 Green 0 Blue 180 

Implementing a Rotation Transformation

The sample source code implements a through the of two : RotateXY and RotateImage.

The RotateXY targets the structure. This method serves as an encapsulation of the logic behind calculating rotating coordinates at a specified angle. The practical C# code implementation of the algorithms discussed in the previous section can be found within this method. The definition as follows:

public static Point RotateXY(this Point source, double degrees,
                                       int offsetX, int offsetY)
{ 
   Point result = new Point();
 
   result.X = (int)(Math.Round((source.X - offsetX) *
              Math.Cos(degrees) - (source.Y - offsetY) *
              Math.Sin(degrees))) + offsetX;

result.Y = (int)(Math.Round((source.X - offsetX) * Math.Sin(degrees) + (source.Y - offsetY) * Math.Cos(degrees))) + offsetY;
return result; }

Rose: Rotate Red –60o, Green 0o, Blue 60o

Rose Rotate Red -60 Green 0 Blue 60

The RotateImage targets the class. This method expects three rotation degree/angle values, each corresponding to a colour channel. Positive degrees result in clockwise rotation and negative values result in counter clockwise rotation.  The definition as follows:

public static Bitmap RotateImage(this Bitmap sourceBitmap,  
                                       double degreesBlue, 
                                      double degreesGreen, 
                                        double degreesRed) 
{ 
    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);
//Convert to Radians degreesBlue = degreesBlue * Math.PI / 180.0; degreesGreen = degreesGreen * Math.PI / 180.0; degreesRed = degreesRed * Math.PI / 180.0;
//Calculate Offset in order to rotate on image middle int xOffset = (int )(sourceBitmap.Width / 2.0); int yOffset = (int )(sourceBitmap.Height / 2.0);
int sourceXY = 0; int resultXY = 0;
Point sourcePoint = new Point(); Point resultPoint = new Point();
Rectangle imageBounds = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
for (int row = 0; row < sourceBitmap.Height; row++) { for (int col = 0; col < sourceBitmap.Width; col++) { sourceXY = row * sourceData.Stride + col * 4;
sourcePoint.X = col; sourcePoint.Y = row;
if (sourceXY >= 0 && sourceXY + 3 < pixelBuffer.Length) { //Calculate Blue Rotation
resultPoint = sourcePoint.RotateXY(degreesBlue, xOffset, yOffset);
resultXY = (int)(Math.Round( (resultPoint.Y * sourceData.Stride) + (resultPoint.X * 4.0)));
if (imageBounds.Contains(resultPoint) && resultXY >= 0) { if (resultXY + 6 < resultBuffer.Length) { resultBuffer[resultXY + 4] = pixelBuffer[sourceXY];
resultBuffer[resultXY + 7] = 255; }
if (resultXY + 3 < resultBuffer.Length) { resultBuffer[resultXY] = pixelBuffer[sourceXY];
resultBuffer[resultXY + 3] = 255; } }
//Calculate Green Rotation
resultPoint = sourcePoint.RotateXY(degreesGreen, xOffset, yOffset);
resultXY = (int)(Math.Round( (resultPoint.Y * sourceData.Stride) + (resultPoint.X * 4.0)));
if (imageBounds.Contains(resultPoint) && resultXY >= 0) { if (resultXY + 6 < resultBuffer.Length) { resultBuffer[resultXY + 5] = pixelBuffer[sourceXY + 1];
resultBuffer[resultXY + 7] = 255; }
if (resultXY + 3 < resultBuffer.Length) { resultBuffer[resultXY + 1] = pixelBuffer[sourceXY + 1];
resultBuffer[resultXY + 3] = 255; } }
//Calculate Red Rotation
resultPoint = sourcePoint.RotateXY(degreesRed, xOffset, yOffset);
resultXY = (int)(Math.Round( (resultPoint.Y * sourceData.Stride) + (resultPoint.X * 4.0)));
if (imageBounds.Contains(resultPoint) && resultXY >= 0) { if (resultXY + 6 < resultBuffer.Length) { resultBuffer[resultXY + 6] = pixelBuffer[sourceXY + 2]; resultBuffer[resultXY + 7] = 255; }
if (resultXY + 3 < resultBuffer.Length) { resultBuffer[resultXY + 2] = pixelBuffer[sourceXY + 2];
resultBuffer[resultXY + 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; }

Daisy: Rotate Red 15o, Green 5o, Blue 5o

Daisy Rotate Red 15 Green 5 Blue 5

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 yellow daisy is licensed under the Creative Commons Attribution-Share Alike 2.5 Generic license and can be downloaded from Wikimedia.org.

The sample images featuring an image of a white daisy 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 a CPU is licensed under the Creative Commons Attribution-Share Alike 2.0 Generic license. The original author is credited as Andrew Dunn. The original image can be downloaded from .

The sample images featuring an image of a rose is 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 can be downloaded from .

The sample images featuring an image of a butterfly is licensed under the Creative Commons Attribution 3.0 Unported license and can be downloaded from Wikimedia.org.

The Original Image

Intel_80486DX2_bottom

CPU: Rotate Red 90o, Green 0o, Blue –30o

CPU Rotate Red 90 Green 0 Blue -30

CPU: Rotate Red 0o, Green 10o, Blue 0o

CPU Rotate Red 0 Green 10 Blue 0

CPU: Rotate Red –4o, Green 4o, Blue 6o

CPU Rotate Red -4 Green 4 Blue 6

CPU: Rotate Red 10o, Green 0o, Blue 0o

CPU Rotate Red 10 Green 0 Blue 0

CPU: Rotate Red 10o, Green –5o, Blue 0o

CPU Rotate Red 10 Green -5 Blue 0

CPU: Rotate Red 10o, Green 0o, Blue 10o

CPU Rotate Red 10 Green 0 Blue 10

CPU: Rotate Red –10o, Green 10o, Blue 0o

CPU Rotate Red -10 Green 10 Blue 0

CPU: Rotate Red 30o, Green –30o, Blue 0o

CPU Rotate Red 30 Green -30 Blue 0

CPU: Rotate Red 40o, Green 20o, Blue 0o

CPU Rotate Red 40 Green 20 Blue 0

CPU: Rotate Red 40o, Green 20o, Blue 0o

CPU Rotate Red 60 Green 30 Blue 0

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 Blur

Article Purpose

This article serves to provides an introduction and discussion relating to methods and techniques. The Image Blur methods covered in this article include: , , , and  .

Daisy: Mean 9×9

Daisy Mean 9x9

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 provide a means of testing and replicating topics discussed in this article. The sample application is a based application of which the user interface enables the user to select an type to implement.

When clicking the Load Image button users are able to browse the local file system in order to select source/input . In addition users are also able to save blurred result when clicking the Save Image button and browsing the local file system.

Daisy: Mean 7×7

Daisy Mean 7x7

The sample application provides the user with the ability to select the method of to implement. The dropdown located on the right-hand side of the user interface lists all of the supported methods of . When a user selects an item from the , the associated blur method will be implemented on the preview .

The image below is a screenshot of the Image Blur Filter sample application in action:

Image Blur Filter Sample Application

Image Blur Overview

The process of can be regarded as reducing the sharpness or crispness defined by an . results in detail/ being perceived as less distinct. are often blurred as a method of smoothing an .

perceived as too crisp/sharp can be softened by applying a variety of techniques and intensity levels. Often are smoothed/blurred in order to remove/reduce . In implementations better results are often achieved when first implementing through smoothing/. can even be implemented in a fashion where results reflect , a method known as .

In this article and the accompanying sample source code all methods of supported have been implemented through , with the exception of the filter. Each of the supported methods in essence only represent a different   . The technique capable of achieving optimal results will to varying degrees be dependent on the features present in the specified source/input . Each method provides a different set of desired properties and compromises. In the following sections an overview of each method will be discussed.

Daisy: Mean 9×9

Daisy Mean 9x9

Mean Filter/Box Blur

The also sometimes referred to as a represents a fairly simplistic implementation and definition. A definition can be found on as follows:

A box blur is an in which each pixel in the resulting image has a value equal to the average value of its neighbouring pixels in the input image. It is a form of low-pass ("blurring") filter and is a .

Due to its property of using equal weights it can be implemented using a much simpler accumulation algorithm which is significantly faster than using a sliding window algorithm.

as a title relates to all weight values in a being equal, therefore the alternate title of . In most cases a will only contain the value one. When performing implementing a , the factor value equates to the 1 being divided by the sum of all values.

The following is an example of a 5×5 convolution kernel:

Mean Filter Blur 5x5 Kernel

The consist of 25 elements, therefore the factor value equates to one divided by twenty five.

The Blur does not result in the same level of smoothing achieved by other methods. The method can also be susceptible to directional artefacts.

Daisy Mean 5×5

Daisy Mean 5x5

Gaussian Blur

The method of is a popular and often implemented filter. In contrast to the method produce resulting appearing to contain a more uniform level of smoothing. When implementing a is often applied to source/input resulting in . The has a good level of edge preservation, hence being used in operations.

From we gain the following :

A Gaussian blur (also known as Gaussian smoothing) is the result of blurring an image by a . It is a widely used effect in graphics software, typically to reduce image noise and reduce detail. The visual effect of this blurring technique is a smooth blur resembling that of viewing the image through a translucent screen, distinctly different from the bokeh effect produced by an out-of-focus lens or the shadow of an object under usual illumination. Gaussian smoothing is also used as a pre-processing stage in computer vision algorithms in order to enhance image structures at different scales

A potential drawback to implementing a results from the filter being computationally intensive. The following represents a 5×5 . The sum total of all elements in the equate to 159, therefore a factor value of 1.0 / 159.0 will be implemented.

Guassian Blur 5x5 Kernel

Daisy: Gaussian 5×5

Daisy Gaussian 5x5

Median Filter Blur

The is classified as a non-linear filter. In contrast to the other methods of discussed in this article the implementation does not involve or a predefined matrix . The following can be found on :

In signal processing, it is often desirable to be able to perform some kind of on an image or signal. The median filter is a nonlinear technique, often used to remove . Such noise reduction is a typical pre-processing step to improve the results of later processing (for example, on an image). Median filtering is very widely used in digital because, under certain conditions, it preserves edges while removing noise.

Daisy: Median 7×7

Daisy Median 7x7

As the name implies, the operates by calculating the value of a pixel group also referred to as a window. Calculating a value involves a number of steps. The required steps are listed as follows:

  1. Iterate each pixel that forms part of the source/input .
  2. In relation to the pixel currently being iterated determine neighbouring pixels located within the bounds defined by the window size. The window location should be offset in order to align the window’s middle pixel and the pixel currently being iterated.
  3. Neighbouring pixels located within the bounds  defined by the window should be added to a one dimensional neighbourhood array. Once all value have been added, the array should be sorted by value.
  4. The pixel value located at the middle of the sorted neighbourhood array qualifies as the value. The newly determined value should be assigned to the pixel currently being iterated.
  5. Repeat the steps listed above until all pixels within the source/input have been iterated.

Similar to the filter the has the ability to smooth whilst providing edge preservation. Depending on the window size implemented and the physical dimensions of input/source the can be computationally expensive.

Daisy: Median 9×9

Daisy Median 9x9

Motion Blur

The sample source implements filters. in the traditional sense has been association with photography and video capturing. can often be observed in scenarios where rapid movements are being captured to photographs or video recording. When recording a single frame, rapid movements could result in the changing  before the frame being captured has completed.

can be synthetically imitated through the implementation of Digital filters. The size of the provided when implementing affects the filter intensity perceived in result . Relating to filters the size of the specified in influences the perception and appearance of how rapidly movement had occurred to have blurred the resulting . Larger produce the appearance of more rapid motion, whereas smaller result in less rapid motion being perceived.

Daisy: Motion Blur 7×7 135 Degrees

Daisy Motion Blur 7x7 135 Degrees

Depending on the specified the ability exists to create the appearance of movement having occurred in a certain direction. The sample source code implements filters at 45 degrees, 135 degrees and in both directions simultaneously.

The listed below represents a 5×5 filter occurring at  45 degrees and 135 degrees:

MotionBlur5x5

Image Blur Implementation

The sample source code implements all of the concepts explored throughout this article. The source code definition can be grouped into 4 sections: ImageBlurFilter method, ConvolutionFilter method, MedianFilter method and the Matrix class. The following article sections relate to the 4 main source code sections.

The ImageBlurFilter has the purpose of invoking the correct blur filter method and relevant method parameters. This method acts as a method wrapper providing the technical implementation details required when performing a specified blur filter.

The definition of the ImageBlurFilter as follows:

 public static Bitmap ImageBlurFilter(this Bitmap sourceBitmap,  
                                             BlurType blurType) 
{  
     Bitmap resultBitmap = null; 

switch (blurType) { case BlurType.Mean3x3: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.Mean3x3, 1.0 / 9.0, 0); } break; case BlurType.Mean5x5: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.Mean5x5, 1.0 / 25.0, 0); } break; case BlurType.Mean7x7: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.Mean7x7, 1.0 / 49.0, 0); } break; case BlurType.Mean9x9: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.Mean9x9, 1.0 / 81.0, 0); } break; case BlurType.GaussianBlur3x3: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.GaussianBlur3x3, 1.0 / 16.0, 0); } break; case BlurType.GaussianBlur5x5: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.GaussianBlur5x5, 1.0 / 159.0, 0); } break; case BlurType.MotionBlur5x5: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur5x5, 1.0 / 10.0, 0); } break; case BlurType.MotionBlur5x5At45Degrees: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur5x5At45Degrees, 1.0 / 5.0, 0); } break; case BlurType.MotionBlur5x5At135Degrees: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur5x5At135Degrees, 1.0 / 5.0, 0); } break; case BlurType.MotionBlur7x7: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur7x7, 1.0 / 14.0, 0); } break; case BlurType.MotionBlur7x7At45Degrees: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur7x7At45Degrees, 1.0 / 7.0, 0); } break; case BlurType.MotionBlur7x7At135Degrees: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur7x7At135Degrees, 1.0 / 7.0, 0); } break; case BlurType.MotionBlur9x9: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur9x9, 1.0 / 18.0, 0); } break; case BlurType.MotionBlur9x9At45Degrees: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur9x9At45Degrees, 1.0 / 9.0, 0); } break; case BlurType.MotionBlur9x9At135Degrees: { resultBitmap = sourceBitmap.ConvolutionFilter( Matrix.MotionBlur9x9At135Degrees, 1.0 / 9.0, 0); } break; case BlurType.Median3x3: { resultBitmap = sourceBitmap.MedianFilter(3); } break; case BlurType.Median5x5: { resultBitmap = sourceBitmap.MedianFilter(5); } break; case BlurType.Median7x7: { resultBitmap = sourceBitmap.MedianFilter(7); } break; case BlurType.Median9x9: { resultBitmap = sourceBitmap.MedianFilter(9); } break; case BlurType.Median11x11: { resultBitmap = sourceBitmap.MedianFilter(11); } break; }
return resultBitmap; }

Daisy: Motion Blur 9×9

Daisy Motion Blur 9x9

The Matrix class serves as a collection of  various definitions. The Matrix class and all public properties are defined as static. The definition of the Matrix class as follows:

     public static class Matrix 
    {  
         public static double[,] Mean3x3 
         {  
             get 
             {  
                 return new double[,]   
                { {  1, 1, 1, },  
                  {  1, 1, 1, },  
                  {  1, 1, 1, }, }; 
             }  
         }  

public static double[,] Mean5x5 { get { return new double[,] { { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, }; } }
public static double[,] Mean7x7 { get { return new double[,] { { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1 }, }; } }
public static double[,] Mean9x9 { get { return new double[,] { { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, }; } }
public static double[,] GaussianBlur3x3 { get { return new double[,] { { 1, 2, 1, }, { 2, 4, 2, }, { 1, 2, 1, }, }; } }
public static double[,] GaussianBlur5x5 { get { return new double[,] { { 2, 04, 05, 04, 2 }, { 4, 09, 12, 09, 4 }, { 5, 12, 15, 12, 5 }, { 4, 09, 12, 09, 4 }, { 2, 04, 05, 04, 2 }, }; } }
public static double[,] MotionBlur5x5 { get { return new double[,] { { 1, 0, 0, 0, 1 }, { 0, 1, 0, 1, 0 }, { 0, 0, 1, 0, 0 }, { 0, 1, 0, 1, 0 }, { 1, 0, 0, 0, 1 }, }; } }
public static double[,] MotionBlur5x5At45Degrees { get { return new double[,] { { 0, 0, 0, 0, 1 }, { 0, 0, 0, 1, 0 }, { 0, 0, 1, 0, 0 }, { 0, 1, 0, 0, 0 }, { 1, 0, 0, 0, 0 }, }; } }
public static double[,] MotionBlur5x5At135Degrees { get { return new double[,] { { 1, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0 }, { 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 1 }, }; } }
public static double[,] MotionBlur7x7 { get { return new double[,] { { 1, 0, 0, 0, 0, 0, 1 }, { 0, 1, 0, 0, 0, 1, 0 }, { 0, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 1, 0, 0 }, { 0, 1, 0, 0, 0, 1, 0 }, { 1, 0, 0, 0, 0, 0, 1 }, }; } }
public static double[,] MotionBlur7x7At45Degrees { get { return new double[,] { { 0, 0, 0, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 0, 0, 0 }, }; } }
public static double[,] MotionBlur7x7At135Degrees { get { return new double[,] { { 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0, 0 }, { 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 1 }, }; } }
public static double[,] MotionBlur9x9 { get { return new double[,] { { 1, 0, 0, 0, 0, 0, 0, 0, 1, }, { 0, 1, 0, 0, 0, 0, 0, 1, 0, }, { 0, 0, 1, 0, 0, 0, 1, 0, 0, }, { 0, 0, 0, 1, 0, 1, 0, 0, 0, }, { 0, 0, 0, 0, 1, 0, 0, 0, 0, }, { 0, 0, 0, 1, 0, 1, 0, 0, 0, }, { 0, 0, 1, 0, 0, 0, 1, 0, 0, }, { 0, 1, 0, 0, 0, 0, 0, 1, 0, }, { 1, 0, 0, 0, 0, 0, 0, 0, 1, }, }; } }
public static double[,] MotionBlur9x9At45Degrees { get { return new double[,] { { 0, 0, 0, 0, 0, 0, 0, 0, 1, }, { 0, 0, 0, 0, 0, 0, 0, 1, 0, }, { 0, 0, 0, 0, 0, 0, 1, 0, 0, }, { 0, 0, 0, 0, 0, 1, 0, 0, 0, }, { 0, 0, 0, 0, 1, 0, 0, 0, 0, }, { 0, 0, 0, 1, 0, 0, 0, 0, 0, }, { 0, 0, 1, 0, 0, 0, 0, 0, 0, }, { 0, 1, 0, 0, 0, 0, 0, 0, 0, }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, }, }; } }
public static double[,] MotionBlur9x9At135Degrees { get { return new double[,] { { 1, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 1, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 1, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 1, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 1, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 1, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 1, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 1, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, }, }; } } }

Daisy: Median 7×7

Daisy Median 7x7

The MedianFilter targets the class. The MedianFilter method applies a using the specified and matrix size (window size), returning a new representing the filtered .

The definition of the MedianFilter as follows:

 public static Bitmap MedianFilter(this Bitmap sourceBitmap, 
                                   int matrixSize) 
{ 
     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;
List<int> neighbourPixels = new List<int>(); byte[] middlePixel;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++) { byteOffset = offsetY * sourceData.Stride + offsetX * 4;
neighbourPixels.Clear();
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) {
calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
neighbourPixels.Add(BitConverter.ToInt32( pixelBuffer, calcOffset)); } }
neighbourPixels.Sort(); middlePixel = BitConverter.GetBytes( neighbourPixels[filterOffset]);
resultBuffer[byteOffset] = middlePixel[0]; resultBuffer[byteOffset + 1] = middlePixel[1]; resultBuffer[byteOffset + 2] = middlePixel[2]; resultBuffer[byteOffset + 3] = middlePixel[3]; } }
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; }

Daisy: Motion Blur 9×9

Daisy Motion Blur 9x9

The sample source code performs by invoking the ConvolutionFilter .

The definition of the ConvolutionFilter as follows:

private 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 : 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

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 yellow daisy is licensed under the Creative Commons Attribution-Share Alike 2.5 Generic license and can be downloaded from Wikimedia.org.

The sample images featuring an image of a white daisy 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 a pink daisy is licensed under the Creative Commons Attribution-Share Alike 2.5 Generic license and can be downloaded from Wikipedia.

The sample images featuring an image of a purple daisy is licensed under the Creative Commons Attribution-ShareAlike 3.0 License and can be downloaded from Wikipedia.

The Original Image

Purple_osteospermum

Daisy: Gaussian 3×3

Daisy Gaussian 3x3

Daisy: Gaussian 5×5

Daisy Gaussian 5x5

Daisy: Mean 3×3

Daisy Mean 3x3

Daisy: Mean 5×5

Daisy Mean 5x5

Daisy: Mean 7×7

Daisy Mean 7x7

Daisy: Mean 9×9

Daisy Mean 9x9

Daisy: Median 3×3

Daisy Median 3x3

Daisy: Median 5×5

Daisy Median 5x5

Daisy: Median 7×7

Daisy Median 7x7

Daisy: Median 9×9

Daisy Median 9x9

Daisy: Median 11×11

Daisy Median 11x11

Daisy: Motion Blur 5×5

Daisy Motion Blur 5x5

Daisy: Motion Blur 5×5 45 Degrees

Daisy Motion Blur 5x5 45 Degrees

Daisy: Motion Blur 5×5 135 Degrees

Daisy Motion Blur 5x5 135 Degrees

Daisy: Motion Blur 7×7

Daisy Motion Blur 7x7

Daisy: Motion Blur 7×7 45 Degrees

Daisy Motion Blur 7x7 45 Degree

Daisy: Motion Blur 7×7 135 Degrees

Daisy Motion Blur 7x7 135 Degrees

Daisy: Motion Blur 9×9

Daisy Motion Blur 9x9

Daisy: Motion Blur 9×9 45 Degrees

Daisy Motion Blur 9x9 45 Degrees

Daisy: Motion Blur 9×9 135 Degrees

Daisy Motion Blur 9x9 135 Degrees

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: Calculating Gaussian Kernels

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

Ant Gaussian Kernel 5x5 Weight 19

Sample Source Code

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

Calculating Gaussian Kernels Sample Source code

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

Gaussian Kernel 5x5 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

Prey Mantis Gaussian Kernel 13x13 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:

Gaussian Kernel Calculator Sample Application

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:

Gaussian Kernel formula

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

Gaussian Kernel 13x13 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:

Gaussian Kernel Formula

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:

Gaussian Kernel Values non summed

Ant: Gaussian Kernel 9×9 Weight 19

Ant Gaussian Kernel 9x9 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:

Calculated Gaussian Kernel Values

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

Rose Gaussian Kernel 3x3 Weight 5.5

The Original Image

Rose_Amber_Flush_20070601

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

Gaussian Kernel 19x19 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

Ant Gaussian Kernel 7x7 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

1280px-Gemeiner_Widderbock_4483

Wasp Gaussian Kernel 3×3 Weight 9.25

Wasp Gaussian Kernel 3x3 Weight 9.25

Wasp Gaussian Kernel 5×5 Weight 9.25

Wasp Gaussian Kernel 5x5 Weight 9.25

Wasp Gaussian Kernel 7×7 Weight 9.25

Wasp Gaussian Kernel 7x7 Weight 9.25

Wasp Gaussian Kernel 9×9 Weight 9.25

Wasp Gaussian Kernel 9x9 Weight 9.25

Wasp Gaussian Kernel 11×11 Weight 9.25

Wasp Gaussian Kernel 11x11 Weight 9.25

Wasp Gaussian Kernel 13×13 Weight 9.25

Wasp Gaussian Kernel 13x13 Weight 9.25

Wasp Gaussian Kernel 15×15 Weight 9.25

Wasp Gaussian Kernel 15x15 Weight 9.25

Wasp Gaussian Kernel 17×17 Weight 9.25

Wasp Gaussian Kernel 17x17 Weight 9.25

Wasp Gaussian Kernel 19×19 Weight 9.25

Wasp Gaussian Kernel 19x19 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.

Dewald Esterhuizen

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


Dewald Esterhuizen

Blog Stats

  • 869,747 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.