Color Transformation Applications in C# GDI+ Programming

In this last installment of the series related to color transformation in GDI+, we will delve into two typical types of color transformation applications: one is color remapping and the other is color channel isolating.

Now, let’s start with what color remapping is and how we can use it in real scenarios.

Color Remapping

As far as color mapping is concerned, the commonly-used color palette under the GDI environment may come to mind. By adjusting the standard colors via the color palette, we can redefine the values of the color indexes in the color palette. A typical application is the fading in and out effects with an image via the color palette technique. However, the color palette can only be utilized when the number of the colors is less than 256. When the number of the colors on the screen is greater than 256, the color palette technique can no longer take effect. GDI+ introduces a new form of ‘color remapping’ that redefines the specified colors. It is mainly based on the idea of the color palette,

In GDI+, the colors of an image can be adjusted when they are rendered according to the facts of the requests. The key to achieving this result is to redefine the colors, i.e. color remapping. In fact, color remapping is the process of converting the colors in an image according to a color remap table, which, in fact, is an array of ColorMap structures. Each ColorMap structure in the array has an oldColor member and a newColor member.

Let’s consider the following typical scenario. During the course of making a television program, the background color is generally set blue. In this case, when the background needs to be changed or the videos need to be synthesized, the blue color in the background can be weeded out so as to maintain the main picture. Based on this, let’s consider how to weed out the background color of an image.

The commonly-used solution to solve the above problem is to replace the background color with a transparent color. In fact, GDI+ has introduced a class named ColorMap to perform this function, where some kind of color redirection relation is defined. Furthermore, we can substitute any of the other colors in the image using ColorMap.

When GDI+ draws an image, each pixel of the image is compared to the array of old colors. If a pixel’s color matches an old color, its color is changed to the corresponding new color. The colors are changed only for rendering—the color values of the image itself (stored in an Image or Bitmap object) are not changed.

To draw a remapped image, initialize an array of ColorMap structures. Pass the address of that array to the SetRemapTable method of an ImageAttributes object, and then pass the address of the ImageAttributes object to the Graphics::DrawImage method of a Graphics object.

Let’s examine several signatures of the SetRemapTable method in detail.

public void SetRemapTable (ColorMap[] map);

public void SetRemapTable ( ColorMap[] map,ColorAdjustType type);

For easy use, let’s also list the definition of the ColorMap class disassembled using Luts Roeder’s .NET Reflector:

public sealed class ColorMap

{

// Fields

private Color newColor = new Color();

private Color oldColor = new Color();

// Properties

public Color NewColor

{

get

{

return this.newColor;

}

set

{

this.newColor = value;

}

}

public Color OldColor

{

get

{

return this.oldColor;

}

set

{

this.oldColor = value;

}

}

}

Now let’s construct a sample application that pulls the foreground Image object out of the original image named Nemo_Blue.bmp.

{mospagebreak title=Pulling Out the Foreground Image Demo}

In this sample, the code creates a color remap table that consists of a single ColorMap structure. The oldColor member of the ColorMap structure is blue and the newColor member is white. The image is drawn once without remapping and once with remapping. This will help us indirectly pull the foreground image out of the original one. The remapping process changes all the blue pixels to white.

Take a look at the running-time snapshot, as is shown in Figure 1.


Figure 1—the running-time snapshot

Apparently, the right image in Figure 1 corresponds to the one in which the blue background is cleared away after the color remapping. 

Look more carefully at the associated programming:

Graphics graphics = this.CreateGraphics();

graphics.Clear(Color.White);


//load the original image with blue background

Bitmap image = new Bitmap("Nemo_Blue.bmp");

ImageAttributes imageAttributes = new ImageAttributes();


int width = image.Width;

int height = image.Height;

//substitute blue for white to achieve the expected aim

ColorMap colorMap = new ColorMap();

colorMap.OldColor = Color.FromArgb(255, 0, 0, 255);

colorMap.NewColor = Color.FromArgb(255, 255, 255, 255);

//set up color remapping table

ColorMap[] remapTable = { colorMap };


//set up the color info for the image

imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);

//render the source image

graphics.DrawImage(image, 50, 50, width, height);

//render the new image with the background color weeded out

graphics.DrawImage(image,

new Rectangle(width + 80, 50, width, height), //target rectangle

0, 0, // upper left corner of the source image

width, // width of the source image

height, // height of the source image

GraphicsUnit.Pixel,

//color info

imageAttributes);

Note that we’ve only built one ColorMap object, meaning we only execute the color remapping once. So, if you want to eleminate other colors you have to define more ColorMap objects and continue to apply the related color remapping.

With the color remapping application discussed, let’s take a look at another use of color transformation — isolating the color channel.

{mospagebreak title=About the Color Channel}

Quite a few readers may be familiar with Adobe’s PhotoShop, the famous two dimensional picture and image processing software. One thing PhotoShop supports is color channel isolating. By decomposing the original compound image into three isolated r/g/b channels, we can edit the current image more easily. With all this work done, we can compose the three channels again.

In fact, color mapping is a kind of color filtering. If we use the color transformation matrices to set the related color saturation to zero, then this color will be invisible when outputed.

Using the tools in GDI+, we can use the SetOutputChannel method of the ImageAttributes class to output the individual color channels. But, regrettably, the SetOutputChannel method only supports outputting the C/M/Y/K channels. In fact, with the help of different color transformation matrices, we can also succeed in outputting the individual R/G/B channels. Next, we will see an example that accomplishes this using the color transformation matrix technique.

Isolating R/G/B Channel Demo

As usual, let’s visualize the finished line. Figure 2 demonstrates the related running-time snapshot.

Figure 2—using the color transformation matrix to isolate the R/G/B channel


In Figure 2, the original compound image is rendered at the upper left corner, with the other three being the images maintaining the R/G/B channels.

Now, let’s dig into the "how-to’s" of the above sample. The following lists the souce code with detailed explanations.

Graphics graphics = this.CreateGraphics();

graphics.Clear(Color.White);


//load the image

Bitmap image = new Bitmap("jieba.bmp");

//render the source image

graphics.DrawImage(image, 50, 50);

//get the necessary data required by the later rendering operation

int width = image.Width;

int height = image.Height;

ImageAttributes imageAttributes = new ImageAttributes();


//set up the red channel

float[][] colorMatrixElements =

{

new float[]{1.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 1.0f}

};

ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);


//enable the color transformation matrix

imageAttributes.SetColorMatrix(

colorMatrix,

ColorMatrixFlag.Default,

ColorAdjustType.Bitmap);


//use the above color transformation matrix to render the new picture

//corresponding to the one in the red channel

graphics.TranslateTransform(width + 60, 50);

graphics.DrawImage(image, new Rectangle(0, 0, width, height),

0, 0, width, height, GraphicsUnit.Pixel, imageAttributes);


//clear out the already-used color transformation

imageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);


//set up the second channel—the green channel

float[][] colorMatrixElements2 =

{

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 1.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 1.0f}

};

ColorMatrix colorMatrix2 = new ColorMatrix(colorMatrixElements2);


//enable the color transformation matrix

imageAttributes.SetColorMatrix(

colorMatrix2,

ColorMatrixFlag.Default,

ColorAdjustType.Bitmap);

//use the color transformation matrix to render the new picture

//corresponding to the one in the green channel

//first reset transformation matrix to identity

graphics.ResetTransform();

graphics.TranslateTransform(50, height + 60);

graphics.DrawImage(image, new Rectangle(0, 0, width, height),

0, 0, width, height, GraphicsUnit.Pixel, imageAttributes);


//clear out the used color transformation

imageAttributes.ClearColorMatrix(ColorAdjustType.Bitmap);


//set up the blue channel

float[][] colorMatrixElements3 =

{

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 1.0f, 0.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},

new float[]{0.0f, 0.0f, 0.0f, 0.0f, 1.0f}

};

ColorMatrix colorMatrix3 = new ColorMatrix(colorMatrixElements3);


//enable the color transformation matrix

imageAttributes.SetColorMatrix(

colorMatrix3,

ColorMatrixFlag.Default,

ColorAdjustType.Bitmap);


//use the color transformation matrix to output picture

graphics.TranslateTransform(width+10 , 0);

graphics.DrawImage(image, new Rectangle(0, 0, width, height),

0, 0, width, height, GraphicsUnit.Pixel, imageAttributes);

By using the color transformation matrices we’ve easily taken out the independent R/G/B channels. This resembles the function that PhotoShop has provided. However, the above sample is the simplest application of color transformation. You can easily find out that if you modify the R/G/B/A component at the main diagonal in the color transformation matrix, you can obtain some special and even miraculous effects.

To further grasp the above idea, I recommend that you test the SetOutputChannel method of the ImageAttributes class so you can output the C/M/Y/K channels in order to make a good comparison with the idea supplied in this article.

{mospagebreak title=Summary}

In this series of articles, we mainly discussed the color transformation matrices related calculations, such as translating, scaling, rotating, and shearing. We realized that the 5×5 color transformation matrix plays the central role. Within each sample, we also became familiar with several classes provided by GDI+. In fact, what GDI+ can do is far more than what these few samples can accomplish.

In the last installment, we examined the two most common ways to use color transformation matrices and gave two sample applications. Although it’s easy to weed out the background of an image and take out the three independent R/G/B channels, it’s rather difficult for GDI+ to provide the popular drag & drop support for editing such intermediate or even end images. In the following articles, I’m going to write a screen capturing application using both GDI+ and GDI, through which you will acquaint yourself with the shortcomings of GDI+.

DOWNLOAD SOURCE

[gp-comments width="770" linklove="off" ]