The unage application presented in Chapter 7 processes images by manipulating each and every pixel of the image individually. This application easily performs a few million operations for a small image, so it’s no surprise that it’s slow. For instance, it takes over 20 seconds to sharpen a 400 x 400 image. This time is clearly unacceptable for a commercial or shareware-grade application. My goal in Chapter 7 was to demonstrate the basics of image processing and how the Image project can become your vehicle for experimenting with other image processing techniques. The code presented in Chapter 7 is straightforward VISual Basic code.
If you really like image processing and you’re ready to build your own image processing application, then you probably want to make it as fast as typical image processing applications like PaintShop or PhotoS tyler. That’s not easy. Even after optimization, our Image application won’t be as fast, but we’ll make it as fast as it can get. Professional image processing applications are written in C++ with partsof the code written in machine language for maximum. speed. Our application won’t process tl.1esame image in a second or so, but we.can drop the processing time from 30 seconds down to two or three seconds, which will make a world of difference. This is as fast as you can make it with Visual Basic (but that shouldn’t stop you from trying to prove me wrong).
This section is rather lengthy but it will pay to read it. If you like, you’can jumpto end orthe section and read the optimized code, but I’m going to o ptimize the code one step at a time. It’s a process you’ll find useful in optimizing your applications later on. I’ll start by locating the code that’s responsible for most of the delay (the code that contributes the most to the execution of the application) and then I’ll try to improve this code
Optimization Targets
Let’s start by timing the Image application. Open an image file (like the NYSTRT.GIF file, which you’ll find on the CD) and select an effect from th e Process menu.On a 166MHz Pentium with 64MB of RAM, it takes 30 seconds to sh arpen the image and slightly more to smooth it. The code that implements the sharpening operation is shown next:
This is the double loop that scans the rows of the image and the pixels within each row and processes them. There’s not much room for optimizing the calculations (at least, not to the point that it would make the application run significantly faster); after all, they are operations with integers. The If statements introduce some delay, but as you’ll see, they aren’t responsible for much of the execution time. As you may have guessed, the PSet method is a slow one. Break the application and’comment out the line that sets the current pixel’s value by inserting the comment character in front of it.
Picturel.PSet (‘i,j), RGB (red: green, blue)
Run the application again and time it. This time it takes 9.34 seconds to complete the processing. The processed image isn’t displayed, but the required calculations do take place. It may come as a surprise, but the PSet method is responsible for two-thirds of the execution time. Visual Basic can handle the math quite efficiently, but painting is a real hog. (The times mentioned were measured in the Visual Basic IDE and they will be better if the application is compiled to native code). ”
Since drawing operations are so time consuming, let’s also remove the line that refreshes the Form by commenting out the line:
Pieturel. Refresh
This time, the smoothing operation is completed in 2.42 seconds, which is an impressive speed. Let’s see how close to this time we can get. Paint Shop is even faster, but unless you’re willing to code parts of the application in Visual C++ or assembly language, you can’t do any better. So far, we have figured out what part of the code we can improve. At this point, it doesn’t make sense to even consider optimizing the math. Even if you could get rid of the math calculations, you’d bring the execution time from 30 seconds down to slightly less than 28. This is not nearly as time-saving as speeding the display operations.
The SetPixelV() API Function
In Chapter 13, The Windows APJ, you’ll learn about the SetPixelVO function, which does the S!!mething as the PSet method, but faster. Let’s replace the call to the PSel method in the code with a call to the SetPixelVO function. Just replace all the instances of the line:
Pieturel.PSet (j, i), RGB(red, green, blue)
with the line
setPixelV Pieturel.hde, j, i, RGB(red, green, blue)
This is an API function, and in order to use it, you must first declare it in a Module. The SetPixelVO function’s declaration is:
To revise the Image application you must first add a Module (with the Project > Add Module command) and insert the two declarations in it. Then replace the instances of the Pset method with the SetPixelVO function. Now run the application . and time it again. The total processing time on the same system is 16.25 seconds. By
replacing a Visual Basic method with a call to an API function, we improved the speed of the application by more than 40%.
The revised application can be found in the folder IMAGE 1 on the CD. It’s also called Image and it’s identical to the Image application, except for the statement that reads pixel values (in the Open command) and the statement that paints the processed pixel in the processing commands.
Comment out the statement that refreshes the Form and repeat the sharpen operation. It will take 9.9 seconds. This statement is executed as many times are there are rows in the image. If you omit it altogether, you’ll gain another five seconds or so, but this comes with a price: the image won’t be updated on the screen as it’s processed.
You could add a progress bar, as many image processing applications do, but displaying the processing progress for nearly 10 seconds isn’t the best you can do. Ten seconds is a long time for a user to wait without receiving clues about the result of the processing. It would be a reasonable choice if the total time was a secosd or two, but 10 seconds is probably too much. So, what do you do next? If you’re happy with the application’s speed, you can stop here. Compile the application to native code to gain another second or two and you’ll have a reasonably fast application you can use for your own custom image processing algorithms. To save a few more seconds, you can avoid refreshing the screen and display a progress bar that indicates the progress of the operation. This way, users won’t think that the application has stopped responding. But from your experience with image processing applications, you know that this application can be made faster.There’s room for improvement, and you probably know what the next step is. The painting of the pixels is still responsible for most of the application’s execution time, so this is where we should focus our efforts next. Let’s redesign the part of the program that paints the Form.
Drawing on Device Contexts
To/speed up the drawing operations, we are going to draw directly on the bitmap that corresponds to the rectangle of the control on the screen. Visual Basic doesn’t draw directly on the PictureBox control. Instead, it updates a special area in the memory where a copy of the image is stored. Whenever Windows gets a chance, it transfers the contents of this memory area to the screen. This happens when no code is executed or when the DoEvents or Picture1.Refre5h statements are executed. This indirect method of drawing on controls places a significant burden on Visual Basic
It’s possible to access the memory where the copy of the bitmap is stored and update the pixel values there. After the entire bitmap has been modified, we can transfer it to the Picture Box control. The drawback of this method is that while drawing on the bitmap, the image ‘on the PictureBox control won’t be updated. But if the image is processed in a few seconds, that’s something we can live with. As you’ll see sho~ly, updating the bitmap in memory is quite fast. Before we start coding though.let’s review the process. In Chapter 13 you’ll find more information on the API functions used here. In this section we are going to outline the process and briefly discuss what each API function does. In order to access the bitmap that stores the image of the Picturebox control, you must create a bitmap structure. This is done with theCreateCompatibleBitmapO API function, which creates a bitmap of specific dimensions and makes it compatible with the bitmap displayed on the control. The variable hBMP~reated with the following statement points to a bitmap that’s compatible with the bitmap displayed on the Picturel control and has the same dimensions as the control.
The hBMP structure can’t be accessed directly by Visual Basic either. The drawing API functions, in particular, the SetPixelVO function, can only write to a device context. To u’;e the SetPixelVO function, you must also create a device context with .<11 the CreateCompatibleDCO API function:
The device context is compatible with the PictureBox control and you can use any of the drawing functions to draw on it. However, your drawing won’t end up on the PictureBox control unless you connect the hDestDC device context and the compatible bitmap hBMP. This is taken care of by the SelectObjectO API function, which connects the two objects (or mounts the bitmap on the device context):
The copied portion of the device context is slightly smaller than the original image, because the edge pixels aren’t processed. The corresponding pixels in the device context have random values. By omitting them, we keep the original values of the edge pixels on the PictureBox control instead of displaying random values.
Sharpening an Image on the Device Context
You have seen all the statements that are new to the revised Image application. The new application is called Image and can be found in the IMAGE2 folder on the CD. It’s nearly identical to the initial Image application, with the exception of the statements that change the pixel values. Here’s the code that implements the Sharpen command:
If you run the revised Image application, you’ll see that it’s quite fast. Most of the processing algorithms finish in less than five seconds in the Visual Basic IDE. If you compile it and turn on all the optimization switches (it’s safe to do so), the same image will be processed in less than three seconds. This is a very good speed for a Visual g!.i~fqapplication. It’s still slower than professional image processing applications, sALted is acceptable. Of course, my goal was to demonstrate how to optimizual.Basic application. Image can’t be considered a typical VB application, but lj”S the type of application that benefits immensely from the optimization process. It shows what some additional programming effort can do. Wif1 the exception of the lines that read and write the pixels, the rest of the application is fairly simple, and you can modify it to use with your own custom image processing techniques.