xyz
xyz

Reputation: 27867

"Colourizing" a Bitmap in .NET

If you have a System.Drawing.Bitmap instance that contains a greyscale image, is there a built in way to "colourize" it with the influence of another colour?

For example, if you had a black and white (greyscale) picture of a coffee mug and you wanted to create separate images of red, green and purple versions programmatically.

Upvotes: 2

Views: 1417

Answers (5)

Niki
Niki

Reputation: 15868

If it's an 8 bit image, you can just use a different the palette (Image.Palette). That's essentially a lookup table that assigns a Color value to each possible pixel byte value. Much faster than changing all pixels in a loop.

Upvotes: 2

Steve
Steve

Reputation: 2193

I don't have a code example to give but here's a way to do this. Convert each pixel from RGB to HSV and change the Hue and Saturation component on each pixel. The Hue controls the Color. The Value should stay the same. The result will be a Bitmap with the same lightness and darkness but with a different color.

Edit: here's an example. Notice the Hue and Saturation update.

        public static Color ColorFromAhsb(int a, float h, float s, float b)
    {

        if (0 > a || 255 < a)
        {
            throw new Exception("a");
        }
        if (0f > h || 360f < h)
        {
            throw new Exception("h");
        }
        if (0f > s || 1f < s)
        {
            throw new Exception("s");
        }
        if (0f > b || 1f < b)
        {
            throw new Exception("b");
        }

        if (0 == s)
        {
            return Color.FromArgb(a, Convert.ToInt32(b * 255),
              Convert.ToInt32(b * 255), Convert.ToInt32(b * 255));
        }

        float fMax, fMid, fMin;
        int iSextant, iMax, iMid, iMin;

        if (0.5 < b)
        {
            fMax = b - (b * s) + s;
            fMin = b + (b * s) - s;
        }
        else
        {
            fMax = b + (b * s);
            fMin = b - (b * s);
        }

        iSextant = (int)Math.Floor(h / 60f);
        if (300f <= h)
        {
            h -= 360f;
        }
        h /= 60f;
        h -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
        if (0 == iSextant % 2)
        {
            fMid = h * (fMax - fMin) + fMin;
        }
        else
        {
            fMid = fMin - h * (fMax - fMin);
        }

        iMax = Convert.ToInt32(fMax * 255);
        iMid = Convert.ToInt32(fMid * 255);
        iMin = Convert.ToInt32(fMin * 255);

        switch (iSextant)
        {
            case 1:
                return Color.FromArgb(a, iMid, iMax, iMin);
            case 2:
                return Color.FromArgb(a, iMin, iMax, iMid);
            case 3:
                return Color.FromArgb(a, iMin, iMid, iMax);
            case 4:
                return Color.FromArgb(a, iMid, iMin, iMax);
            case 5:
                return Color.FromArgb(a, iMax, iMin, iMid);
            default:
                return Color.FromArgb(a, iMax, iMid, iMin);
        }

    }

    private void Form1_Load(object sender, EventArgs e)
    {
        var bmp = new Bitmap("c:\\bw.bmp");

        foreach (int y in Enumerable.Range(0, bmp.Height))
        { 
            foreach (int x in Enumerable.Range(0,bmp.Width))
            {
                var p = bmp.GetPixel(x, y);
                var h = p.GetHue();

                var c = ColorFromAhsb(p.A, p.GetHue() + 200, p.GetSaturation() + 0.5f, p.GetBrightness());
                bmp.SetPixel(x, y, c);                    
            }
        }
        pictureBox1.Image = bmp;
        //bmp.Dispose();

    }

Upvotes: 5

user164771
user164771

Reputation:

I am unsure of a built in way but, if you represent each colour as a float rather than a byte (255 becomes 1 - full intensity), multiplying the each channel with your desired colour should give the effect you are talking about.

(1,1,1) "white" * (1,0,0) "red" = (1,0,0) "red"

(0.5,0.5, 0.5) "grey" * (0,1,0) "green" = (0,0.5,0) "dark green"

You do need to apply this per pixel though.

Upvotes: 1

Matt Lacey
Matt Lacey

Reputation: 65586

I'd create a copy of the original image and them put a separate semi transparent image of the desired color of the top.

Update: see example at http://www.codeproject.com/KB/cs/Merge_Images_in_C_.aspx

Upvotes: 1

Rippo
Rippo

Reputation: 22424

See here

I have used this in the past. You are wanting to look specifically at ToSepia. You may need to deconstruct this a bit but it has worked for me.

Upvotes: 1

Related Questions