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

我正试图使用​​C#将32bpp截图转换为8bpp(或4bpp或1bpp)格式。 我已经看过几个类似的主题stackoverflow的答案,并且大多数build议使用以下代码的变体:

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#调用,而不是诉诸PInvoking BitBltGetDIBits等任何人能够帮助我解决这个问题? 谢谢。

编辑 :我应该指出,我需要这个向后兼容.NET框架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); } } 

返回的图像具有8bpp像素格式,编码器计算调色板条目。 如有必要,您可以将其转换为位图。 到目前为止,最好的办法是简单地不打扰索引格式。 当记忆受到严重限制时,它们从石器时代算起。 或者使用专业的图形编辑器。

AForge库正在使用灰度完美。

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

这个类是图像灰度化的基类[…]过滤器接受24,32,48和64 bpp彩色图像,并产生8(如果源是24或32 bpp图像)或16(如果源是48或64 bpp图像)bpp灰度图像。

负的步幅意味着图像是自下而上的(倒置的)。 只要使用绝对的步伐,如果你不在乎。 我知道这适用于24bpp图像,不知道它是否适用于其他人。

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