How to convert 32-bit RGBA Image to Grayscale pres

2019-02-21 00:14发布

问题:

I'd like to, in code and on demand, convert a 32-bit RGBA Image object (originally a 32-bit PNG) to its 32-bit grayscale counterpart.

I've already read several other questions here, as well as many articles online. I've tried using ColorMatrix to do it, but it doesn't seem to handle the alpha very well. Pixels that are entirely opaque grayscale perfectly. Any pixel that is partially transparent seems not to translate well as there are still tinges of color in those pixels. It is enough to be noticeable.

The ColorMatrix I use is as follows:

new System.Drawing.Imaging.ColorMatrix(new float[][]{
                    new float[] {0.299f, 0.299f, 0.299f, 0, 0},
                    new float[] {0.587f, 0.587f, 0.587f, 0, 0},
                    new float[] {0.114f, 0.114f, 0.114f, 0, 0},
                    new float[] {     0,      0,      0, 1, 0},
                    new float[] {     0,      0,      0, 0, 1}
                    });

This is, as I've read, a pretty standard NTSC weighted matrix. I then use it, along with Graphics.DrawImage, but as I said the partially transparent pixels are still colored. I should point out this is displaying the Image object via a WinForms PictureBox on a white background. Could it be perhaps just the way PictureBox's draw their images and handle the transparent parts? The background colors are not affecting it (the tinge of color is from the original image for sure), but perhaps PictureBox isn't redrawing the transparent pixels correctly?

I've seen some methods that use a FormatConvertedBitmap along with an OpacityMask. I haven't tried it, mainly because I'd really prefer not to have to import PresentationCore.dll (not to mention that means it won't work in .NET 2.0 limited apps). Surely the basic System.Drawing.* stuff can do this simple procedure? Or not?

回答1:

Are you by any chance painting the image onto itself using a ColorMatrix? That won't work of course (because if you paint something semi-transparent-gray over a green pixel, some green will shine through). You need to paint it onto a new, empty bitmap containing only transparent pixels.



回答2:

Thanks to danbystrom's idle curiosity, I was indeed redrawing on top of the original. For anyone interested, here's the corrected method I used:

using System.Drawing;
using System.Drawing.Imaging;

public Image ConvertToGrayscale(Image image)
{
    Image grayscaleImage = new Bitmap(image.Width, image.Height, image.PixelFormat);

    // Create the ImageAttributes object and apply the ColorMatrix
    ImageAttributes attributes = new System.Drawing.Imaging.ImageAttributes();
    ColorMatrix grayscaleMatrix = new ColorMatrix(new float[][]{
        new float[] {0.299f, 0.299f, 0.299f, 0, 0},
        new float[] {0.587f, 0.587f, 0.587f, 0, 0},
        new float[] {0.114f, 0.114f, 0.114f, 0, 0},
        new float[] {     0,      0,      0, 1, 0},
        new float[] {     0,      0,      0, 0, 1}
        });
    attributes.SetColorMatrix(grayscaleMatrix);

    // Use a new Graphics object from the new image.
    using (Graphics g = Graphics.FromImage(grayscaleImage))
    {
        // Draw the original image using the ImageAttributes created above.
        g.DrawImage(image,
                    new Rectangle(0, 0, grayscaleImage.Width, grayscaleImage.Height),
                    0, 0, grayscaleImage.Width, grayscaleImage.Height,
                    GraphicsUnit.Pixel,
                    attributes);
    }

    return grayscaleImage;
}


回答3:

If you convert the image to TGA, an uncompressed imaeg format you can use "RubyPixels" to edit the pixel data directly, doing whatever you please. You can then convert it back to PNG.

I recomend doing to conversion with ImageMagick, also from ruby.