C#将32bpp图像转换为8bpp



我正在尝试使用C#将32bpp的屏幕截图图像转换为8bpp(或4bpp,或1bpp)格式。我已经看过几个关于类似主题的stackoverflow答案,大多数都建议使用以下代码进行变体:

public static Bitmap Convert(Bitmap oldbmp) 
{
    Bitmap newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format8bppIndexed);
    Graphics gr = Graphics.FromImage(newbmp);
    gr.PageUnit = GraphicsUnit.Pixel;
    gr.DrawImageUnscaled(oldbmp, 0, 0);
    return newbmp;
}

然而,当执行此操作时,我会得到一个异常:A graphics object cannot be created from an image that has an indexed pixel format。我知道8、4和1bpp图像具有颜色表映射,而不是实际的颜色像素本身(如在32或16bpp图像中),所以我认为我在某个地方错过了一些转换步骤,但我对C#(来自C++背景)相当陌生,更希望能够使用本机C#调用来实现这一点,而不是求助于PInvooking BitBltGetDIBits等。有人能帮我解决这个问题吗?谢谢

EDIT:我应该指出,我需要它向后兼容.NET framework 2.0

GDI+通常对索引像素格式的支持非常差。没有简单的方法可以将65536或1600万色的图像转换为只有2、16或256色的图像。必须从源图像中去除颜色,这是一种有损的转换,可能会产生非常糟糕的结果。有多种算法可以实现这一点,但没有一种算法对每种图像都是完美的。这是图形编辑器的工作。

我发现了一个窍门。GDI+有一个GIF文件的图像编码器。这是一种只有256种颜色的图形格式,编码器必须限制颜色的数量。它使用了一种适用于照片的抖动算法。它确实有生成网格模式的诀窍,当它生成网格模式时,你会不那么激动。这样使用:

public static Image Convert(Bitmap oldbmp) {
    using (var ms = new MemoryStream()) {
        oldbmp.Save(ms, ImageFormat.Gif);
        ms.Position = 0;
        return Image.FromStream(ms);
    }
}

返回的图像具有8ppp像素格式,其中Palette条目由编码器计算。如果需要,可以将其转换为"位图"。到目前为止,最好的做法就是不必为索引格式而烦恼。它们可以追溯到计算的石器时代,当时内存受到严重限制。或者使用专业的图形编辑器。

AForge库使用Grayscale非常完美。

var bmp8bpp = Grayscale.CommonAlgorithms.BT709.Apply(bmp);

这个类是图像灰度的基类〔…〕滤波器接受24、32、48和64bpp彩色图像,并产生8个(如果源是24或32 bpp图像)或16(如果源为48或64 bppimage)bpp灰度图像。

负步幅表示图像是自下而上的(倒置的)。如果你不在乎,就用绝对步幅。我知道这适用于24bpp的图像,不知道它是否适用于其他图像。

您可以在PresentationCore Assembly中使用System.Windows.Media.Imaging查看此处以获取更多信息

最新更新