You'd like to convert a picture from color to grayscale from within a Visual Basic 2005 application.
Sample code folder: Chapter 10\LockImage
Use the MakeGray() method of the LockImage class, described in Recipe 10.18.
The MakeGray() method of the LockImage class (whose full source code is listed in Recipe 10.21) provides a working example that processes the individual color bytes stored in the class's ImageBuffer() integer array.
Here's the code for the MakeGray() procedure:
Public Sub MakeGray( ) ' ----- Make a grayscale version of the image. Dim pixelIndex As Integer Dim onePixel As Integer Dim alphaPart As Integer Dim redPart As Integer Dim greenPart As Integer Dim bluePart As Integer Dim maxColor As Integer Dim minColor As Integer Dim down As Integer Dim across As Integer ' ----- Lock the image for speed. LockTheImage( )
All processing methods added to the LockImage class should call the private method LockTheImage() as the first step and the corresponding UnlockTheImage() method as the last step.
The following two nested loops process all pixels in all rows of the image. pixelIndex walks the pixels across each row and then down the image:
' ----- Process each pixel in the grid. For down = 0 To BaseImageHeight - 1 For across = 0 To BaseImageWidth - 1 ' ----- Locate the pixel's color. pixelIndex = down * BaseImageWidth + across
Each pixel is split up into its parts if the processing requires access to them. The Mirror( ) method processed the pixels as whole units, but to compute grayscale values, you need to access the individual color components of each pixel:
onePixel = ImageBuffer(pixelIndex) ' ----- Extract the color values. alphaPart = (onePixel >> 24) And &HFF redPart = (onePixel >> 16) And &HFF greenPart = (onePixel >> 8) And &HFF bluePart = onePixel And &HFF
The next lines convert the color information to grayscale using an algorithm that averages using the two maximum and minimum values for red, green, and blue. There are other algorithms available for converting to grayscale, and you might want to experiment with others to best meet your requirements. All three colors are assigned the same byte value, which is what forces all pixels to become some shade of gray:
' ----- Get the general color intensity. maxColor = Math.Max(redPart, Math.Max(greenPart, _ bluePart)) minColor = Math.Min(redPart, Math.Min(greenPart, _ bluePart)) onePixel = (maxColor + minColor) \ 2 ' ----- Use a common intensity for all colors. bluePart = onePixel greenPart = onePixel redPart = onePixel ' ----- Set the pixel to the new color. Retain ' the original alpha channel. ImageBuffer(pixelIndex) = (alphaPart << 24) + _ (redPart << 16) + (greenPart << 8) + bluePart Next across Next down
As a last step, it's important to call UnlockTheImage( ) when the processing of ImageBuffer( ) is complete:
' ----- Finished. Unlock the image. UnlockTheImage( ) End Sub
In this example, 256 shades of gray are created. If you want to convert to 16 shades, or even just 2(black-and-white monochrome), simply round off onePixel to the nearest shade values desired. For example, for two-level black-and-white images all values of onePixel less than 128 are rounded to zero, and all other byte values are set to 255.
Figure 10-28 shows the results of converting the original color image to grayscale. Although the difference can be hard to discern in the grayscale figures used in this book, it can easily be seen in Figure 10-29, where a two-level grayscale (or monochrome black-and-white) conversion was used. This result was obtained by inserting the following lines to adjust onePixel just before it is assigned to the red, blue, and green variables:
If (onePixel < 128) Then onePixel = 0 Else onePixel = 255 End If
Figure 10-28. A color picture converted to grayscale
Figure 10-29. The same image with a 2-level grayscale (black-and-white monochrome) conversion performed instead of a 256-level conversion
Recipe 10.18 describes the LockImage class used in this recipe. Recipe 10.21 includes the full source code for the LockImage class.