Using Funky Image Filters with Visual Basic.NET

Visual Basic has come a long way since the introduction of .NET into the BASIC Language. It is difficult to imagine a world without the .NET Framework, but there once was. In the olden days, it was so difficult doing any sort of image manipulation in Visual Basic. This is where Photoshop has earned my respect.

With the advent of the .NET Framework, applying image filters has become relatively easy, as you will learn in this article. You will learn how to create the following image filters:

  • Grayscale
  • Sepia tone
  • Negative
  • X-tay
  • Edge

Let the games begin.

Practical

Open Visual Studio and create a new Visual Basic Windows Forms project. Design your form to resemble Figure 1.

Design
Figure 1: Design

I am including the picture of the windmill in this project, but you are welcome to use your own. There should be two pictureboxes and five buttons. Keep in mind that my object names will be different from yours.

The beautiful picture (well, at least I think it is) of the windmill was taken by me in April 2017 whilst on a road trip through South Africa’s beautiful Free State province. I digress.

Add the necessary namespace to do image manipulation:

Imports System.Drawing.Imaging

Grayscale

A grayscale (black-and-white, or monochrome) image is one in which the value of each pixel is a single sample representing only a certain amount of light, meaning that the image is composed exclusively of shades of gray, from weak to strong.

Add the following code behind the button labeled ‘Grayscale’:

   Private Sub bGray_Click(sender As Object, e As EventArgs) _
         Handles bGray.Click

      Dim iX As Integer
      Dim iY As Integer

      Dim bmpImage As Bitmap 'Source '

      bmpImage = New Bitmap(picOriginal.Image)

      Dim intPrevColor As Integer

      For iX = 0 To bmpImage.Width - 1

         For iY = 0 To bmpImage.Height - 1

            intPrevColor = (CInt(bmpImage.GetPixel(iX, iY).R) +
               bmpImage.GetPixel(iX, iY).G +
               bmpImage.GetPixel(iX, iY).B) \ 3

            bmpImage.SetPixel(iX, iY, _
               Color.FromArgb(intPrevColor, intPrevColor, _
               intPrevColor))

         Next iY

      Next iX

      picModified.Image = bmpImage

   End Sub

The picture from the first picturebox gets looped through. Each pixel gets modified to a lighter shade. Lastly, the bottom picturebox displays the results, as shown in Figure 2:

Grayscale
Figure 2: Grayscale

Sepia Tone

Sepia is a reddish-brown color. Sepia tones resemble the effect of aging in old photographs and of photographs chemically treated for archival purposes.

Add the following code for the ‘Sepia’ button:

   Private Sub bSepia_Click(sender As Object, e As EventArgs) _
         Handles bSepia.Click

      Dim bmpImage As Bitmap 'Source '
      bmpImage = New Bitmap(picOriginal.Image)

      Dim cCurrColor As Color

      For iY As Integer = 0 To bmpImage.Height - 1

         For iX As Integer = 0 To bmpImage.Width - 1

            cCurrColor = bmpImage.GetPixel(iX, iY)

            Dim intAlpha As Integer = cCurrColor.A
            Dim intRed As Integer = cCurrColor.R
            Dim intGreen As Integer = cCurrColor.G
            Dim intBlue As Integer = cCurrColor.B

            Dim intSRed As Integer = CInt((0.393 * intRed + _
               0.769 * intGreen + 0.189 * intBlue))
            Dim intSGreen As Integer = CInt((0.349 * intRed + _
               0.686 * intGreen + 0.168 * intBlue))
            Dim intSBlue As Integer = CInt((0.272 * intRed + _
               0.534 * intGreen + 0.131 * intBlue))

            If intSRed > 255 Then

               intRed = 255

            Else

               intRed = intSRed

            End If

            If intSGreen > 255 Then

               intGreen = 255

            Else

               intGreen = intSGreen

            End If

            If intSBlue > 255 Then

               intBlue = 255

            Else

               intBlue = intSBlue

            End If

            bmpImage.SetPixel(iX, iY, Color.FromArgb(intAlpha, _
               intRed, intGreen, intBlue))

         Next

      Next

      picModified.Image = bmpImage

   End Sub

You loop here again through the source image (which is the image in the first picturebox). Then, you calculate the transparency for each of the color values:

  • Red: tr = 0.393(100) + 0.769(150) + 0.189(200)
  • Green: tg = 0.349(100) + 0.686(150) + 0.168(200) = 171
  • Blue: tb = 0.272(100) + 0.534(150) + 0.131(200) = 133

The result is shown next (see Figure 3):

Sepia-toned image Negative
Figure 3: Sepia-toned image

Negative

Add the following code to create a Negative effect:

   Private Sub bNegative_Click(sender As Object, e As EventArgs) _
         Handles bNegative.Click

      Dim iaAttributes As New ImageAttributes

      Dim cmMatrix As New ColorMatrix

      cmMatrix.Matrix00 = -1 : cmMatrix.Matrix11 = -1 : _
         cmMatrix.Matrix22 = -1
      cmMatrix.Matrix40 = 1 : cmMatrix.Matrix41 = 1 : _
         cmMatrix.Matrix42 = 1

      iaAttributes.SetColorMatrix(cmMatrix)

      Dim bmpImage As Bitmap = picOriginal.Image

      Dim rect As New Rectangle(0, 0, bmpImage.Width, _
         bmpImage.Height)

      Dim graph As Graphics = Graphics.FromImage(bmpImage)

      graph.DrawImage(bmpImage, rect, rect.X, rect.Y, rect.Width, _
         rect.Height, GraphicsUnit.Pixel, iaAttributes)

      picModified.Image = bmpImage

   End Sub

Here, I have made use of a ColorMatrix object to reverse the original colors. The result is shown in Figure 4.

Negative
Figure 4: Negative

Edge

To determine the edges inside this image, I have opted to make use of the Sobel Edge algorithm. Sobel, sometimes called the Sobel–Feldman operator or Sobel filter, is used in image processing, especially for edge detection where it creates an image that emphasizes the edges.

Add the following code:

   Private Sub bEdge_Click(sender As Object, e As EventArgs) _
         Handles bEdge.Click

      'Sobel Edge'
      Dim tmpImage As Bitmap = New Bitmap(picOriginal.Image)
      Dim bmpImage As Bitmap = New Bitmap(picOriginal.Image)

      Dim intWidth As Integer = tmpImage.Width
      Dim intHeight As Integer = tmpImage.Height

      Dim intOldX As Integer(,) = New Integer(,) {{-1, 0, 1}, _
         {-2, 0, 2}, {-1, 0, 1}}
      Dim intOldY As Integer(,) = New Integer(,) {{1, 2, 1}, _
         {0, 0, 0}, {-1, -2, -1}}

      Dim intR As Integer(,) = New Integer(intWidth - 1, _
         intHeight - 1) {}
      Dim intG As Integer(,) = New Integer(intWidth - 1, _
         intHeight - 1) {}
      Dim intB As Integer(,) = New Integer(intWidth - 1, _
         intHeight - 1) {}

      Dim intMax As Integer = 128 * 128

      For i As Integer = 0 To intWidth - 1

         For j As Integer = 0 To intHeight - 1

            intR(i, j) = tmpImage.GetPixel(i, j).R
            intG(i, j) = tmpImage.GetPixel(i, j).G
            intB(i, j) = tmpImage.GetPixel(i, j).B

         Next

      Next

      Dim intRX As Integer = 0
      Dim intRY As Integer = 0
      Dim intGX As Integer = 0
      Dim intGY As Integer = 0
      Dim intBX As Integer = 0
      Dim intBY As Integer = 0

      Dim intRTot As Integer
      Dim intGTot As Integer
      Dim intBTot As Integer

      For i As Integer = 1 To tmpImage.Width - 1 - 1

         For j As Integer = 1 To tmpImage.Height - 1 - 1

            intRX = 0
            intRY = 0
            intGX = 0
            intGY = 0
            intBX = 0
            intBY = 0

            intRTot = 0
            intGTot = 0
            intBTot = 0

            For width As Integer = -1 To 2 - 1

               For height As Integer = -1 To 2 - 1

                  intRTot = intR(i + height, j + width)
                  intRX += intOldX(width + 1, height + 1) * intRTot
                  intRY += intOldY(width + 1, height + 1) * intRTot

                  intGTot = intG(i + height, j + width)
                  intGX += intOldX(width + 1, height + 1) * intGTot
                  intGY += intOldY(width + 1, height + 1) * intGTot

                  intBTot = intB(i + height, j + width)
                  intBX += intOldX(width + 1, height + 1) * intBTot
                  intBY += intOldY(width + 1, height + 1) * intBTot

               Next

            Next

            If intRX * intRX + intRY * intRY > intMax OrElse
               intGX * intGX + intGY * intGY > intMax OrElse
               intBX * intBX + intBY * intBY > intMax Then

               bmpImage.SetPixel(i, j, Color.Black)

            Else

               bmpImage.SetPixel(i, j, Color.Transparent)

            End If

         Next

      Next

      picModified.Image = bmpImage

   End Sub

The resulting image is shown in Figure 5.

Edge
Figure 5: Edge

The picture had too much detail, so as you can see it was quite difficult obtaining all edges. I should have chosen a simpler image, but I wanted to brag about my beautiful country.

X-ray

The X-ray effect is simply a combination of the Grayscale effect and the Negative effect because an X-ray is a negative of a grayscale picture. Add the following code:

   Private Sub bXRay_Click(sender As Object, e As EventArgs) _
         Handles bXRay.Click

      Dim iX As Integer
      Dim iY As Integer

      Dim bmpImage As Bitmap 'Source '

      bmpImage = New Bitmap(picOriginal.Image)

      Dim intPrevColor As Integer

      For iX = 0 To bmpImage.Width - 1

         For iY = 0 To bmpImage.Height - 1

            intPrevColor = (CInt(bmpImage.GetPixel(iX, iY).R) +
               bmpImage.GetPixel(iX, iY).G +
               bmpImage.GetPixel(iX, iY).B) \ 3

            bmpImage.SetPixel(iX, iY, _
               Color.FromArgb(intPrevColor, intPrevColor, _
               intPrevColor))

         Next iY

      Next iX

      Dim iaAttributes As New ImageAttributes

      Dim cmMatrix As New ColorMatrix

      cmMatrix.Matrix00 = -1 : cmMatrix.Matrix11 = -1 : _
         cmMatrix.Matrix22 = -1
      cmMatrix.Matrix40 = 1 : cmMatrix.Matrix41 = 1 : _
         cmMatrix.Matrix42 = 1

      iaAttributes.SetColorMatrix(cmMatrix)

      Dim rect As New Rectangle(0, 0, bmpImage.Width, _
         bmpImage.Height)

      Dim graph As Graphics = Graphics.FromImage(bmpImage)

      graph.DrawImage(bmpImage, rect, rect.X, rect.Y, rect.Width, _
         rect.Height, GraphicsUnit.Pixel, iaAttributes)

      picModified.Image = bmpImage

   End Sub

The result is shown in Figure 6.

X-ray
Figure 6: X-ray

Keep in mind: Some of the demonstrated effects do take a significant amount of time to process; I know this from experience. This article serves to explain just the basics and logic behind these effects; obviously, there are numerous ways to optimize the speed.

The code for this article is available on GitHub.

Conclusion

This was fun! On Codeguru, there are several graphics-related articles that I have written. Please feel free to have a look at them as well. Until next time, cheers!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read