Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Color Scale Filter

0.00/5 (No votes)
17 Jul 2007 1  
Grayscale and color scale filters.

Screenshot - Faster_Grayscale_Filter.jpg

Introduction

Here are some Visual Basic .NET classes that implement image filters for changing RGB images to color and grayscale images. A small demo project is added to show the filters in use.

Background

I was playing with a grayscale filter described in the article "How to convert a color image to grayscale" and getting the idea to make a filter that would make images look like some old brown and white pictures. When solving the problem, I found out it is easy to convert an image to any color scale.

In the article mentioned, we can find two methods for converting a color image to a grayscale image, favoring the method using a color matrix being "an effective and quick method of performing the monochrome manipulation". This is true if we take methods as written, but with some effort, the other method proves to be much faster.

Improving the grayscale filter

The method mentioned in the above article looks like this:

Public Function ConvertToGrayscale(ByVal source As Bitmap) as Bitmap
  Dim bm as new Bitmap(source.Width,source.Height)
  Dim x
  Dim y
  For y=0 To bm.Height
    For x=0 To bm.Width
      Dim c as Color = source.GetPixel(x,y)
      Dim luma as Integer = CInt(c.R*0.3 + c.G*0.59 + c.B*0.11)
      bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma))
    Next
  Next
  Return bm
End Function

In the main loop, we have three function calls that make the execution time longer than necessary. We can get rid of getting and setting the pixel if we copy the bitmap data to an array and then manipulate the pixel bytes directly. Calling the function Color.FromArgb(luma,luma,luma) is not necessary, since it always returns R=G=B=luma. The final method for conversion looks like this:

Public Function ExecuteRgb8( _
         ByVal img As System.Drawing.Image) As System.Drawing.Image
   Dim result As Bitmap = New Bitmap(img)
   Dim bmpData As BitmapData = result.LockBits( _
                  New Rectangle(0, 0, result.Width, result.Height), _
                  ImageLockMode.ReadWrite, img.PixelFormat)
   Dim pixelBytes As Integer = _
            System.Drawing.Image.GetPixelFormatSize(img.PixelFormat) \ 8
   'Get the address of the first line.
   Dim ptr As IntPtr = bmpData.Scan0
   Dim size As Integer = bmpData.Stride * result.Height
   Dim pixels(size - 1) As Byte
   Dim index As Integer
   Dim Y As Integer
   Dim mulR As Double = _factorRed / 100
   Dim mulG As Double = _factorGreen / 100
   Dim mulB As Double = _factorBlue / 100
   'Copy the RGB values into the array.
   System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, size)
   'Main loop.
   For row As Integer = 0 To result.Height - 1
      For col As Integer = 0 To result.Width - 1
         index = (row * bmpData.Stride) + (col * pixelBytes)
         Y = CInt(System.Math.Round( _
                  mulR * pixels(index + 2) + _
                  mulG * pixels(index + 1) + _
                  mulB * pixels(index + 0)))
         If (Y > 255) Then Y = 255
         'Save new values.
         pixels(index + 2) = CByte(Y)
         pixels(index + 1) = CByte(Y)
         pixels(index + 0) = CByte(Y)
      Next
   Next
   'Copy the RGB values back to the bitmap
   System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, size)
   'Unlock the bits.
   result.UnlockBits(bmpData)
   Return result
End Function

The method uses separate factors for color components defining the influence of the color component on a shade of gray.

The function has more program steps and looks more complicated, but the execution time is 15 to 20 percent shorter than using the method with the color matrix.

The conversion function is wrapped in a suitable class allowing changes of color factors for conversion. The filter basics are covered in my previous article: Hue Saturation Lightness Filter. Two standard sets of color factors can be set with the Brightness property.

The class implements a method with the color matrix as well; you can control which method will be used for conversion with the UseColorMatrix property.

Extending the filter to any color scale

You can find old pictures that are not black and white but brown and white. I wanted to change the grayscale filter to do such a conversion. When finished, I found out that the conversion can be done to any color scale so filter is not named brown-scale but color-scale.

There are two small changes done to the grayscale filter to perform the color scale conversion. First, we prepare the color tables for each color component outside the main loop:

'Prepare color scale table
Dim paletteR(255) As Byte
Dim paletteG(255) As Byte
Dim paletteB(255) As Byte
Dim c As Integer
For i As Integer = 0 To 255
   c = CInt(Math.Round((CDbl(_endColor.R) - CDbl(_startColor.R)) * _
            CDbl(i) / 255.0 + _startColor.R, 0))
   If c < 0 Then c = 0
   If c > 255 Then c = 255
   paletteR(i) = CByte(c)
   c = CInt(Math.Round((CDbl(_endColor.G) - CDbl(_startColor.G)) * _
            CDbl(i) / 255.0 + _startColor.G, 0))
   If c < 0 Then c = 0
   If c > 255 Then c = 255
   paletteG(i) = CByte(c)
   c = CInt(Math.Round((CDbl(_endColor.B) - CDbl(_startColor.B)) * _
            CDbl(i) / 255.0 + _startColor.B, 0))
   If c < 0 Then c = 0
   If c > 255 Then c = 255
   paletteB(i) = CByte(c)
Next

The color table calculation was at first simpler, but when playing with different start and end colors, I had to switch to doubles and integers to avoid overflows.

In the main loop, we search the values in tables.

'Save new values.
pixels(index + 2) = paletteR(Y)
pixels(index + 1) = paletteG(Y)
pixels(index + 0) = paletteB(Y)

Using the code

Using the filters is simple. Set the properties and call the execution function.

I have provided a small demo project where you can play with both the filters, change the color factors, colors, and finally, save the filtered images.

Two tabs are used, one for RGB to grayscale, and an other for RGB to color scale conversion. On both tabs, sliders are used to change thresholds for color components if we select custom thresholds. On the "Color" tab, we can use color selection dialogs to select the start and end colors.

The method for RGB to color scale conversion is as fast as that for RGB to grayscale conversion since the color table is prepared only once outside the main loop. Anyway, a small trick is used to make the program faster. The image is scaled before display and all conversions are done on the scaled image. When the user is satisfied with the filtered image and saves it, the conversion is done on the original image before saving.

Points of interest

What is found as a program example is not always the best solution. It is always worth experimenting, checking, and improving.

When you learn something, it is up to your imagination how to use this knowledge.

Programmers converting code to C will probably get even faster execution if using "dirty" programming without copying the bitmap data to an array.

History

  • 2007-06-19: Version 1.0.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here