简体   繁体   中英

How can I transform only pixels of a specific colour using GDI+?

Let's say I have an image composed of black and white RGB pixels, with varying levels of transparency. I then want to use GDI+ to maintain the transparency levels of all pixels and transform just the black pixels to red, leaving the white ones unaffected.

I think the colormap would look something like:

 |0 0 0 0 0|
 |0 0 0 0 0|
 |0 0 0 0 0|
 |0 0 0 1 0|
 |1 0 0 0 1|

but how, short of looping over each pixel in turn and testing its colour, can I apply that map just to the black pixels?

To answer you main question: there is no way to use matrices to replace a specific color , because matrices are merely transformations without conditions .

To solve your problem: because of the combination you are using (Black and White), matrices can be used! See detailed answer below...

A very good reference: Color Transformations and the Color Matrix .


You are very close in your matrix guess, however, you weren't keeping all of the RGBA values intact. Take a look here:

  R   G   B   A   W
 ___________________
| 1 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 |
| 0 | 0 | 0 | 1 | 0 |
| 1 | 0 | 0 | 0 | 1 |
 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

Through the diagonal 1 's, you are maintaining the current RGBA values. By specifying a 1 on [4][0] , you are also saying that you want that value at its maximum !

In your specific case of using Black and White, replacing only the Black color will work, because White has its RGB values already at maximum (255, 255, 255).

Hence, through the above matrix you are basically saying: keep all RGB at their current values, but I also want Red to always be 255!


Detailed example using sequence (R, G, B, A), and applying the above rules: Red at maximum = 255, everything else the same.

  • Found (0, 0, 0, 128) = Results (255, 0, 0, 128);
  • Found (0, 0, 0, 192) = Results (255, 0, 0, 192);
  • Found (255, 255, 255, 128) = Results (255, 255, 255, 128);
  • Found (255, 255, 255, 255) = Results (255, 255, 255, 255);
  • etc.

As you can see, the Red component is always set to its maximum, and the rest is maintained.


Sample picture:

样品颜色转换

The left side contains original colors with an alpha value specified, while the right contains the same image with the above transformation applied. The circles on the left side are organized in the following manner:

                  R    G    B    A   |   R    G    B    A
               ______________________|_______________________
circle[0,0] = | (255,   0,   0,   0) | (255,   0,   0,  64) | = circle[0,1]
      [1,0] = | (  0,   0,   0,   0) | (  0,   0,   0,  64) | =       [1,1]
      [2,0] = | (255, 255, 255,   0) | (255, 255, 255,  64) | =       [2,1]
      [3,0] = | (255,   0,   0, 128) | (255,   0,   0, 255) | =       [3,1]
      [4,0] = | (  0,   0,   0, 128) | (  0,   0,   0, 255) | =       [4,1]
      [5,0] = | (255, 255, 255, 128) | (255, 255, 255, 255) | =       [5,1]
               ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

C# code to generate the sample image:

using (Bitmap myBitmap = new Bitmap(102, 302))
{
    Graphics g = Graphics.FromImage(myBitmap);
    g.Clear(Color.Transparent);
    g.FillEllipse(new SolidBrush(Color.FromArgb(0, Color.Red)), new Rectangle(1, 1, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(0, Color.Black)), new Rectangle(1, 51, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(0, Color.White)), new Rectangle(1, 101, 50, 50));

    g.FillEllipse(new SolidBrush(Color.FromArgb(64, Color.Red)), new Rectangle(51, 1, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(64, Color.Black)), new Rectangle(51, 51, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(64, Color.White)), new Rectangle(51, 101, 50, 50));

    g.FillEllipse(new SolidBrush(Color.FromArgb(128, Color.Red)), new Rectangle(1, 151, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(128, Color.Black)), new Rectangle(1, 201, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(128, Color.White)), new Rectangle(1, 251, 50, 50));

    g.FillEllipse(new SolidBrush(Color.FromArgb(255, Color.Red)), new Rectangle(51, 151, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(255, Color.Black)), new Rectangle(51, 201, 50, 50));
    g.FillEllipse(new SolidBrush(Color.FromArgb(255, Color.White)), new Rectangle(51, 251, 50, 50));
    g.DrawRectangle(Pens.Black, 0, 0, myBitmap.Width - 1, myBitmap.Height - 1);

    e.Graphics.DrawImage(myBitmap, 0, 0);

    ColorMatrix colorMatrix = new ColorMatrix(
        new Single[][]
        {
            new [] { 1f, 0, 0, 0, 0 },
            new [] { 0f, 1, 0, 0, 0 },
            new [] { 0f, 0, 1, 0, 0 },
            new [] { 0f, 0, 0, 1, 0 },
            new [] { 1f, 0, 0, 0, 1 }
        });

    using (ImageAttributes imageAttr = new ImageAttributes())
    {
        imageAttr.SetColorMatrix(colorMatrix);

        Rectangle rect = new Rectangle(150, 0, myBitmap.Width, myBitmap.Height);
        e.Graphics.DrawImage(myBitmap, rect, 0, 0, myBitmap.Width, myBitmap.Height, GraphicsUnit.Pixel, imageAttr);
    }
}

The black color is represented by 0, The white color is represented by 1

To get your mathematics work with the black pixels only you should the following:

  1. Invert the colors of your image (without loosing the transparency).
  2. Transform the white colors (black in the original) to Aqua (inverse of red).
  3. Invert the colors back to the original.

your colormap in step 2 will look like:

|0 0 0 0 0|
|0 1 0 0 0|
|0 0 1 0 0|
|0 0 0 1 0|
|0 0 0 0 1|

The result now should be as the following:

  1. Black colors are red.
  2. White colors still white.

In addition to the other techniques described, a Color Remap Table can be used to transform (a short list of) specific individual colors in an image. The remap table is part of an ImageAttributes object passed in to the DrawImage methods of a Graphics object. From the MSDN documentation:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM