1

Trying to figure out the most elegant way to render an image inside of a specific color of mask in C# (via System.Drawing or equivalent that will work in both desktop and ASP.NET applications).

The mask image will contain green keys where the image should be 'painted'.

(Desired Result image below is not perfect, hand lasso'd...)

Desired Result

Brandon
  • 13,956
  • 16
  • 72
  • 114

3 Answers3

4

There are various techniques for this:

  1. Scan pixel data and build a mask image (as already suggested by itsme86 and Moby Disk)

  2. A variant of scanning that builds a clipping region from the mask and uses that when drawing (refer to this article by Bob Powell)

  3. Use color keys to mask in the Graphics.DrawImage call.

I'll focus on the third option.

Assuming that the image color that you want to eliminate from your mask is Color.Lime, we can use ImageAttributes.SetColorKey to stop any of that color from being drawn during a call to Graphics.DrawImage like this:

using (Image background = Bitmap.FromFile("tree.png"))
using (Image masksource = Bitmap.FromFile("mask.png"))
using (var imgattr = new ImageAttributes())
{
    // set color key to Lime 
    imgattr.SetColorKey(Color.Lime, Color.Lime);

    // Draw non-lime portions of mask onto original
    using (var g = Graphics.FromImage(background))
    {
        g.DrawImage(
            masksource,
            new Rectangle(0, 0, masksource.Width, masksource.Height),
            0, 0, masksource.Width, masksource.Height,
            GraphicsUnit.Pixel, imgattr
        );
    }

    // Do something with the composited image here...
    background.Save("Composited.png");
}

And the results: Results

You can use the same technique (with color key on Color.Fuchsia) if you want to put those bits of tree into another image.

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
Corey
  • 15,524
  • 2
  • 35
  • 68
  • Awesome and straightforward. The only flaw I see here (which I should have addressed in the question) is if I wanted a transparent background instead of Fuschia, it would draw the image inside the Lime area as well as the transparent area, correct? – Brandon Jan 23 '14 at 15:21
  • If the empty mask area (the fuchsia area in the sample mask) is transparent then you'll need a different method. You can draw the mask onto a fuchsia background to get the mask above, then color key the output above onto a transparent bitmap - one extra color key operation, basically. For small bitmaps it's probably better to build a clipping region - see Bob Powell's article I linked in my answer. – Corey Jan 24 '14 at 22:50
2

You want something like this:

Bitmap original = new Bitmap(@"tree.jpg");
Bitmap mask = new Bitmap(@"mask.jpg");

int width = original.Width;
int height = original.Height;

// This is the color that will be replaced in the mask
Color key = Color.FromArgb(0,255,0);

// Processing one pixel at a time is slow, but easy to understand
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        // Is this pixel "green" ?
        if (mask.GetPixel(x,y) == key)
        {
            // Copy the pixel color from the original
            Color c = original.GetPixel(x,y);

            // Into the mask
            mask.SetPixel(x,y,c);
        }
    }
}
Moby Disk
  • 3,761
  • 1
  • 19
  • 38
0

You could probably read in the mask and translate it into an image that has the alpha channel set to 0 when the pixel is green and the alpha channel set to 0xFF when the pixel is any other color. Then you could draw the mask image over the original image.

itsme86
  • 19,266
  • 4
  • 41
  • 57