Let’s start with a general discussion of the application’s operation before we getdown to the actual code. Once the image is loaded on a Picture Box control, you can access the values of its pixels with the Point method, which returns a Long integer representing each pixel’s color. The basic color components must be extracted from this Long Integer value and used to implement the algorithms. This is a time-consuming step, and for most algorithms, it must be performed more than once for each pixel. To speed up the processing, you can choose to read the values of all the pixels when the image is loaded and store them in an array. In effect, it introduces a delay while the image is loaded, to speed up the rest of the application. The array that stores the values of the pixels is called ImagePixels and is declared as follows:
Global ImagePixels(2, 800, 800) As Integer The largest image you can process with the Image application can’t exceed these dimensions. Of course, you can either change the dimensions of the array or abandon the array altogether and read the pixel values from the Picture Box, as needed. The application will run slower overall, but it will be able to handle any size image you throw at it
There are two options for reading the image’s pixels into the ImagePixels array:
• Use the Point method
• Read the pixels directly from the file
The Point method, which is simpler and used in the Image application, reads the pixel values directly off the PictureBox. Most image processing applications use the other option, reading the pixels directly from the file. This tectuuque is faster, but you have to suppty different routines for each image type. Visual Basic currently supports three image types: BMP,GIF, and JPEG. Instead of learning the file structure for all three types of images and implementing a different routine for each one, we’ll let Visual Basic read the image into a Picture Box control and then read the control’s pixels with the Point method.
Reading Pixel Values
Next,let’s look at the code behind the Open command of the File menu. The program calls the File Open common dialog box, which lets the user select an image and then loads the image on the PictureBox. The PictureBox’s ScaleMode property is set to 3 (pixels), and its AutoSize property is set to True, so we can find out the image’s dimensions from the PictureBox’s dimensions. If either of the image’s dimensions exceeds 800, the process ends with the appropriate message and the -image isn’t loaded. If the image’s dimensions are smaller, the program proceeds by reading the pixel values into the ImagePixels array. The value returned by the Point method is a.Long Integer, which contains all three color components, but can’t be used in any operation as is. You must first extract the three color components with the Getkedt), GetGreenO, and GetBlueO functions, which were presented in the section “Color Components,” earlier in this chapter.
Once extracted, the color components are stored in the appropriate elements of the Imageriiel» array. The first index in the array corresponds to a color component (0 for red, 1 for green, 2 for blue), the second index corresponds to the pixel’s column, and the third index corresponds to the pixel’s row. While the image’s pixels are read, the program updates a ProgressBar control that acts as a progress indicator. The ProgressBar control is updated after reading an entire row of pixels from the PictureBox control.
The Open Command
Now we’re ready to look at the image processing techniques. One of the simplestand most common operations in all image processing programs is th e smoothing (or blurring) operation. The smoothing operation is equivalent to low-pass filterimg: just as you can cut off a stereo’s high frequency Sounds with the help of an equalizer, you can cut off the high frequencies of an image. If you’re wondering what the high frequencies of an image are, think of them as the areas wi th abrupt changes in the image’s intensity. These are the areas that are mainly affected by the blurring filter
The smoothed image contains less abrupt changes than the original image and looks a lot like the original image seen through a semitransparent glass. Figure 7.8 shows an image and- its smoothed version, obtained with the Image application.
To smooth an image, you must reduce the large differences between adjacent pixels. Let’s take a block of nine pixels, centered around the pixel we want to blur. This block’contains the pixel to be blurred and its eight immediate neighbors. Let’s assume that all the pixels in this block are green, except for the middle one, which is red. This pixel is drastically different from its neighbors, and for it to be blurred, i~must be pulled toward the average value of the other pixels. Taking the average of a block of pixels is; therefore, a good choice for.a blurring operation. If the current pixel’s value is similar to the values of its neighbors, the average won’t affect it significantly. If its value is different, the remaining pixels will pull the current pixel’s value toward them. In other words, if the middle pixel was green, the average wouldn’t affect it. Being the only red pixel in the block though, it’s going to come closer to “the average value of the remaining pixels. It’s going to assume a green tone.
Here’s an example with numbers: if the value of the current pixel is 10 and the values of its eight immediate neighbors are 8, 11,9,10,12,10,11, and 9, the average value of all pixels will be (8+11+9+10+12+10+11+9+10)/9=10. The pixel under consideration happens to be right on the average of its neighboring pixels. The. results would be quite different if the value of the center pixel was drastically different. If the center pixel’s value was 20, the new average would be 11.Because the neighboring pixels have values close to 10, they would pull the “outlier” toward .them. This is how blurring works. By taking the average of a number of pix)els. you force the pixels with values drastically different from their neighbors to get closer to them.
Another factor affecting the amount of blurring is the size of the block over which the average is calculated. We used a 3 x 3 block in our example, which yields an average blur. To blur the image even more, use a 5 x 5 block. Even larger blocks will blur the image to the point that useful information will be lost. The actual code of the Smooth operation scans all the pixels of the image (excluding the edge pixels that don’t have neighbors all around them) and takes the average of their RGB components. It then combines the three values with the RGBO function to produce the new value of the pixel.
In the code, I used a 3 x 3 block, but you can change a few numbers in the code to blur the image with an even larger block. The pixels involved in th€lcalculations are the neighboring pixels on the same, previous, and next roWs, and the pixel being processed is always at the center of the block.,
You may have noticed that the code is quite verbose, I could have avoided the very long line that adds the color components with a couple of For Next loops, but I chose this verbose coding over the more compact version to speed up the computations. Setting up loop counters introduces. some small additional delays, which we can do without. The program is slow as is, and every trick counts. VISual Basic is not the most efficient language for this type of operation, but it surely is the mostconverUent.