Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX

By submitting your information, you agree that devx.com may send you DevX offers via email, phone and text message, as well as email offers about other products and services that DevX believes may be of interest to you. DevX will process your information in accordance with the Quinstreet Privacy Policy.


Manipulating Images with .NET Programs : Page 3

Many image processing techniques are simpler than you might expect and can be implemented easily in C# or Visual Basic.




Application Security Testing: An Integral Part of DevOps

Bright Ideas

Now that you have some tools for loading, saving, and manipulating images, you're ready to learn about some point processing techniques. Those techniques modify an image one pixel at a time. When you modify a pixel's value, you don't need to know anything about the values of the surrounding pixels. Most of these techniques are reasonably straightforward once you know what they need to do.

For example, the AdjustBrightness example program shown in Figure 2 lets you adjust an image's brightness. It adjusts each pixel's brightness based on its current value without knowing anything about the other pixels' values.

Figure 2. A Bright Idea: Program AdjustBrightness adjusts the brightness of an image's pixels.

The Bitmap32 class includes several methods that manipulate the image it represents. The AdjustBrightness method shown in the following code adjusts the image's brightness. The brightness parameter should be a value between -1 and 1 to darken or lighten the image.

// Adjust the image's brightness by -100% to 100%. // The brightness value should be between -1 and 1. public void AdjustBrightness(float brightness) {    // Remember if we are locked and lock the bitmap.    bool was_locked = IsLocked;    LockBitmap();    if (brightness < 0)    {        for (int y = 0; y < Height; y++)        {            for (int x = 0; x < Width; x++)            {                byte red, green, blue;                GetPixel(x, y, out red, out green, out blue);                red = (byte)(red + red * brightness);                green = (byte)(green + green * brightness);                blue = (byte)(blue + blue * brightness);                SetPixel(x, y, red, green, blue);            }        }    }    else    {        for (int y = 0; y < Height; y++)        {            for (int x = 0; x < Width; x++)            {                byte red, green, blue;                GetPixel(x, y, out red, out green, out blue);                red = (byte)(red + (255 - red) * brightness);                green = (byte)(green + (255 - green) *                    brightness);                blue = (byte)(blue + (255 - blue) * brightness);                SetPixel(x, y, red, green, blue);            }        }    }    // Unlock if appropriate.    if (!was_locked) UnlockBitmap(); }

The code uses a slightly different approach depending on whether the brightness value is greater than or less than zero. In either case, the code adjusts a pixel's red, green, and blue color components the indicated distance toward 0 or 255. For example, if the brightness parameter is 0.5, then the code adjusts the components halfway between their current values and 255, brightening the image.

The following code shows how the AdjustBrightness program uses this method.

// Adjust the image's brightness. private void AdjustBrightness() {    if (OriginalBitmap == null) return;    // Get the selected brightness.    float brightness = hscrBrightness.Value / 100f;    lblBrightness.Text = brightness.ToString("P0");    // Make a Bitmap24 object.    Bitmap bm = new Bitmap(OriginalBitmap);    Bitmap32 bm32 = new Bitmap32(bm);    // Average the colors.    bm32.AdjustBrightness(brightness);    // Display the result.    picResult.Image = bm; }

This code gets a value between -100 and 100 from the horizontal scrollbar named hscrBrightness. It divides that value by 100 to convert it from a percentage into a fraction. Next the code creates a new Bitmap object that contains a copy of the original version named OriginalBitmap. It makes a corresponding Bitmap32 object, calls its AdjustBrightness method, and displays the result. The AdjustColors example program shown in Figure 3 uses similar techniques to adjust the pixels' red, green, and blue components separately. See the code for the details.

Figure 3. Colorful Pictures: Program AdjustColors lets you adjust the image's red, green, and blue amounts.

Get the Red Out

Adjusting colors globally is interesting but it doesn't have all that many practical applications. For example, you could make your vacation photos bluer to make the sky and ocean look pretty but it would probably also make the people look slightly seasick.

You can refine this method by restricting your adjustments to specific areas in an image. For example, you could increase the blueness only in areas that are already blue.

The RemoveRedEye example program shown in Figure 4 uses this approach to remove red-eye from pictures. Click and drag to select an area. When you release the mouse, the program searches the selected area for pixels that are mostly red and converts them to gray scale. In Figure 4, the eye on the left has been converted and the eye on the right is being selected.

Figure 4. Seeing Red: Program RemoveRedEye converts pixels that are mostly red to gray scale.

The following code shows how the Bitmap32 class removes red-eye from an area.

// Remove red eye from the indicated area. public void RemoveRedEye(int x1, int y1, int x2, int y2) {    // Lock the bitmap.    bool this_locked = this.IsLocked;    this.LockBitmap();    // Process the indicated area.    for (int x = x1; x <= x2; x++)    {        for (int y = y1; y <= y2; y++)        {            // Get this pixel's components.            byte red, green, blue;            GetPixel(x, y, out red, out green, out blue);            // See if it has more red than green and blue.            if ((red > green) && (red > blue))            {                // Convert to grayscale.                byte clr = (byte)((red + green + blue) / 3);                SetPixel(x, y, clr, clr, clr);            }        }    }    // Unlock the bitmap.    if (!this_locked) UnlockBitmap(); }

This code loops thorough the pixels in the selected area. If a pixel's red component is larger than its green and blue components, the code converts it into gray scale.

A Sharp Contrast

In image processing, contrast is the difference in colors between various pieces of an image. If the colors are very similar, then the image has low contrast and it can be hard to distinguish features. If the colors vary widely, then the image has sharp contrast and different parts of the image tend to stand out.

For example, in Figure 5 the AdjustContrast example program is displaying a normal image on the left and a high-contrast version on the right. Compared to the high-contrast version, the original image looks sort of washed out.

Figure 5. A Study in Contrasts: Program AdjustContrast lets you adjust an image's contrast.

The Bitmap32 class uses the following code to adjust an image's contrast. You could easily modify it to intensify colors, for example, to make blues bluer and greens greener.

// Change contrast by spreading values linearly // to or from an origin value. // Use: //      To decrease contrasst   0 <= amount < 1 //      To increase contrasst   amount > 1 // The origin can be 128 to spread to or from a middle value, // or use the median brightness value. public void AdjustContrast(float amount, int origin) {    // Lock the bitmap.    bool was_locked = IsLocked;    LockBitmap();    for (int x = 0; x < Width; x++)    {        for (int y = 0; y < Height; y++)        {            int red = (int)(origin +                (GetRed(x, y) - origin) * amount);            if (red < 0) red = 0;            if (red > 255) red = 255;            int green = (int)(origin +                (GetGreen(x, y) - origin) * amount);            if (green < 0) green = 0;            if (green > 255) green = 255;            int blue = (int)(origin +                (GetBlue(x, y) - origin) * amount);            if (blue < 0) blue = 0;            if (blue > 255) blue = 255;            SetPixel(x, y, (byte)red, (byte)green, (byte)blue);        }    }    // Unlock the bitmap.    if (!was_locked) UnlockBitmap(); }

This code moves each pixel's color values away from an origin value. For example, suppose the average value of all of the image's pixels' red, green, and blue color components is 128. Then the image is roughly halfway between completely dark (black) and completely bright (white), at least in brightness. If you pass 128 to the AdjustContrast method as the origin, then the code makes bright pixels brighter and dark pixels darker. That tends to spread the color values farther apart and increases contrast.

There are lots of other ways you can adjust contrast. For example, the AdjustContrast example program uses a hard-coded origin of 128 but you could use some other value such as the image's average or median brightness. If you look closely at Figure 5, you'll also see that some areas are so bright or dark that their detail is lost. For example, the folds in the green jacket on the right are hidden in the high-contrast version. You can reduce that affect by spreading the colors out in a different way. For example, instead of simply scaling color values by a linear factor, you could use a function that adjusts colors less when they are close to 0 or 255. Values that are closer to the origin get spread out more and those far away get spread out less to preserve more detail.

Area Processes

This article explains several useful techniques for working with images and modifying them one pixel at a time. It explains how to load and save images in different formats and with different compression levels. It also explains some useful point processes such as adjusting an image's contrast, brightness, and color levels.

The second part of this article will explain area processes that use the values of many pixels to set a pixel's new value. Those techniques let you perform such operations as sharpening or blurring an image, highlighting edges, or creating an embossed effect.

Rod Stephens is a consultant and author who has written more than a dozen books and two hundred magazine articles, mostly about Visual Basic. During his career he has worked on an eclectic assortment of applications for repair dispatch, fuel tax tracking, professional football training, wastewater treatment, geographic mapping, and ticket sales. His VB Helper web site receives more than 7 million hits per month and provides three newsletters and thousands of tips, tricks, and examples for Visual Basic programmers.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.
Thanks for your registration, follow us on our social networks to keep up-to-date