"Colourizing" Bitmap in .NET

If you have an instance System.Drawing.Bitmap

that contains a grayscale image, is there a built-in way to "paint" it with a different color?

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

+2


source to share


5 answers


I don't have any sample code to give, but here's a way to do it. Convert each pixel from RGB to HSV and change the Hue and Saturation component for each pixel. Hue controls color. The value must remain unchanged. The result will be a bitmap with the same lightness and darkness, but with a different color.

Edit: here's an example. Pay attention to 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();

    }

      

+5


source


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



+2


source


I would create a copy of the original image and they would place a separate semitransparent image of the desired color on top.

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

+1


source


See here

I've used this in the past. You want to look specifically at ToSepia. You may have to parse this a bit, but it worked for me.

+1


source


I'm not sure about the built-in way, but if you represent each color as a float and not a byte (255 becomes 1 - full intensity), multiplying each channel by the color you want 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 need to apply this parameter to every pixel.

+1


source







All Articles