Continuing the tour of the samples included in the Parallel Extensions June 2008 CTP, we now turn our attention to the “Image Colorizer” application.
This sample uses the following constructs from Parallel Extensions:
and also the following from the standard .NET libraries:
The Image Colorizer was originally written by Stephen Toub to demonstrate Tablet PC APIs functionality (see http://msdn.microsoft.com/en-us/library/bb332387.aspx) and requires the Microsoft.Ink.DLL to compile. If you cannot compile this sample, please obtain the DLL via the Microsoft XP Tablet SDK.
The application demonstrates how to recolor an image to preserve either one or more master colors, and optionally to limit such preservation to specific regions selected via ink. For this discussion, we will only consider the simple behavior, for which the basic flow is:
1. The user selects a master color by clicking on an image pixel (or multiple colors if the Shift-key is held down while clicking)
2. The application copies the image bitmap, and kicks off a worker to colorize the image. The colorization involves altering each image pixel color towards grayscale depending on its ‘closeness’ to the master color(s).
3. When the new image bitmap has been created, it is drawn to the screen.
For example, on the standard Windows Vista image “Frangipani Flowers”, we can see the effect of choosing a to colorize on the red of the unopened flower:
Colorized using red as the master color
In particular, this sample application provides a good example of a GUI that performs significant computation whilst providing progress updates. Even before we consider parallelizing the work, the application already involves multi-threaded code to ensure that the foreground GUI thread never blocks and remains responsive whilst the computation is occurring. The standard mechanism to achieve this is to use a System.Component.BackgroundWorker for performing the expensive computation. It is a good strategy to build your applications in two phases like this:
Phase 1: GUI + BackgroundWorker
Phase 2: GUI + BackgroundWorker with parallelized computation
Once the basic application is in place with a background worker performing sequential computation, it is relatively straight-forward to extend it to use parallelism. Note: Parallel computation is turned on in the application via the ‘check-mark’ button – you may also wish to add a Stopwatch around the final if-block in Colorize() to validate the speedup if the progress bar is not sufficient for observing the timing.
The first issue to address when moving to a parallel version of Colorize() is achieving multi-threaded access to a System.Drawing.Bitmap. This is conveniently handled already via the FastBitmap class which locks the bitmap and provides access to a regular array during manipulation; please see the code for the details. The only other change is to ensure that the progress counter manipulation is thread-safe which is achieved by using Interlocked.Add(ref pixelsProcessed, width) rather than the non-thread-safe alternative: pixelsProcessed += width.
Because the workload is easily parallelizable and there is significant computation performed in each iteration, the Colorize() function achieves essentially a linear speedup, eg 4x on a 4-core machine.