Introduction
I created this as a part of my icon builder project. Let me back up a sec and explain. In my icon project I deal extensively with icons. A problem creeps up when the user resizes a lowres icon to a larger size. What ends up happening is edges of the icon get frayed. My objective here was to write something to un fray the edges. Ok fast forward a bit :-)
I was contemplating how to accomplish my goal when I though "What I need here is a gausian blur function". Unfortunately, there is nothing like this in the standard GDI+ functions. So (yet again) I decided to roll my own solution of creating a gausian blur (like) function. The reason it isn't exactly the same as the real mccoy is it doesn't take into account a transform matrix, so as a result the colors all have the same weighted values (but you can't really tell a difference).
Demystifying the "Image"
Image stuff has always made me step back and say ouch. Until you really think about it, it can seem like a really complex issue when in reality it is no harder then that address book program everyone wrote in school (Many eon's ago, in pascal). I will try to break this concept down and reduce it to bear bones for you.
If nothing else remember this:
An image is just a 2D array of colors, and we "could" represent an image in an array, like so:
For PixelX = 0 To Image.Width - 1
For PixelY = 0 To Image.Height - 1
ImagePixel(PixelX, PixelY) = Image.getpixel(PixelX, PixelY)
Next
Next
This will loop picture and create an array of colors for the entire image (luckily we don't have to do this, I am just trying to make a point). An image is nothing more then an array of colors that we can modify.
Blurring the line
What happens when we take red and add green to it? (we get yellow) What happens when we take the same concept and apply it to a 2D array? (we get a blur effect). In the last sample snipit of code we learned that GetPixel returns the color of a point in an image. So consider this:
For PixelX = 0 To Image.Width - 2
For PixelY = 0 To Image.Height - 2
Dim Color1 As Color = Image.getpixel(PixelX, PixelY)
Dim Color2 As Color = Image.getpixel(PixelX + 1, PixelY)
Dim Merge As Color = Color.FromArgb( _
(Color1.A + Color2.A) / 2, _
(Color1.R + Color2.R) / 2, _
(Color1.G + Color2.G) / 2, _
(Color1.B + Color2.B) / 2)
SetPixel(PixelX, PixelY, Merge)
Next
Next
Each color (for our purposes here) is made up of four values A = Alpha, R = Red, G = Green, B = Blue. So in essence what we are doing is taking these "color channels" and averaging them with the next pixel. So lets say pixel (1, 1) is red and pixel (2, 1) is green, we mix then and set pixel (1, 1) to the resulting color, yellow. Congrats, you just made a blur! Actually it resembles more of a streak.
Building a better blur
Instead of blurring two pixels lets take a greater sampling and average a 3X3 group of pixels, this will give you a better blur effect. The first thing we need to do is have a function that weeds out those pesky pixels that don't exist. As I have demonstrated above the 3X3 sample of pixels for (1,1) will render five points that don't exist.
dim pixels as new arraylist
dim x as integer, y as integer
dim size as new size(3, 3)
dim imageSize as new size(10, 10)
For x = PixelX - CInt(Size.Width / 2) To PixelX + _
CInt(Size.Width / 2)
For y = Pixely - CInt(Size.Height / 2) To Pixely + _
CInt(Size.Height / 2)
If (x > 0 And x < imageSize.Width) And _
(y > 0 And y < imageSize.Height) Then
pixels.Add(bmp.GetPixel(x, y))
End If
Next
Next
What this little gem does is take PixelX and PixelY (1, 1 in this case) and create an array of the pixel colors. This array of pixel colors will contain the color information for four pixels (1,1) (2,1) (1,2) and (2,2). Here are two more examples:
Example one (2,1) will yeild 6 points. Example 2 will yield 9 points.
As you can see this is a multi-purpose function that will return up to nine pixels color, depending on where the pixel is located.
Now that we have an array of the pixels to use in this blur lets see how we can average them together.
Dim thisColor As Color
Dim alpha As Integer = 0
Dim red As Integer = 0
Dim green As Integer = 0
Dim blue As Integer = 0
For Each thisColor In pixels
alpha += thisColor.A
red += thisColor.R
green += thisColor.G
blue += thisColor.B
Next
Return Color.FromArgb(alpha / pixels.Count, _
red / pixels.Count, _
green / pixels.Count, _
blue / pixels.Count)
This will loop the collection of pixels adding up the color channels as it goes along. Then when its done it will return a color with the averages of all the colors, This resulting color is what you set that pixel (1, 1) to.
What is an alpha channel
An alpha channel is a color channel that represents the transparency of that color. In the above all three squares are red but the alpha channels are different. The first square is solid and the alpha channel is set to 255. The second square is 50% solid or (127.5) and the last square is completely transparent with an alpha of 0% or (0).
Alpha channels are used most commonly in GIF, PNG and ICO files. For this reason you can load up a transparent gif and check the alpha channel of every pixel and get an outline of the solid parts of the image.
Alpha Blur
Using the same technique as before we can test to see if the pixel is not solid and send it to our blur function thus blurring the alpha channels or edges of the object.
Note the object must be transparent for this to work!
Putting it all together
Here is a full example of the program I built that does this gausianesk blurring.
Private Function Average(ByVal Size As Size, ByVal imageSize As SizeF, _
ByVal PixelX As Integer, ByVal Pixely As Integer) As Color
Dim pixels As New ArrayList
Dim x As Integer, y As Integer
Dim bmp As Bitmap = PictureBox1.Image.Clone
For x = PixelX - CInt(Size.Width / 2) To PixelX + _
CInt(Size.Width / 2)
For y = Pixely - CInt(Size.Height / 2) To Pixely + _
CInt(Size.Height / 2)
If (x > 0 And x < imageSize.Width) And _
(y > 0 And y < imageSize.Height) Then
pixels.Add(bmp.GetPixel(x, y))
End If
Next
Next
Dim thisColor As Color
Dim alpha As Integer = 0
Dim red As Integer = 0
Dim green As Integer = 0
Dim blue As Integer = 0
For Each thisColor In pixels
alpha += thisColor.A
red += thisColor.R
green += thisColor.G
blue += thisColor.B
Next
Return Color.FromArgb(alpha / pixels.Count, _
red / pixels.Count, _
green / pixels.Count, _
blue / pixels.Count)
End Function
Private Sub gausianBlur(ByVal alphaEdgesOnly As Boolean, _
ByVal blurSize As Size)
Dim PixelY As Integer
Dim PixelX As Integer
Dim bmp As Bitmap = PictureBox1.Image.Clone
Label1.Text = "Applying Gausian Blur of " & blurSize.ToString
Progress.Maximum = bmp.Height * bmp.Width
Progress.Minimum = 0
Progress.Value = 0
For PixelY = 0 To bmp.Width - 1
For PixelX = 0 To bmp.Height - 1
If Not alphaEdgesOnly Then
bmp.SetPixel(PixelX, PixelY, Average(blurSize, _
bmp.PhysicalDimension, PixelX, PixelY))
ElseIf bmp.GetPixel(PixelX, PixelY).A _
<> 255 Then
bmp.SetPixel(PixelX, PixelY, Average(blurSize, _
bmp.PhysicalDimension, PixelX, PixelY))
End If
Progress.Value += 1
Application.DoEvents()
Next
Next
PictureBox1.Image = bmp.Clone
bmp.Dispose()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
gausianBlur(False, New Size(6, 6))
End Sub
It looks kind of mind numbing at first until you break it down like I have just done for you. Basically when you click button1
it sends the information to the gausianBlur
sub to build the blur. The gausianBlur
sub checks if that part of the image needs to be blurred and if it does sets that pixel to the output color of the adverage
function.