使用相同的自定义调色板将 8bpp 索引位图转换为 24bpp 并再次转换

Posted

技术标签:

【中文标题】使用相同的自定义调色板将 8bpp 索引位图转换为 24bpp 并再次转换【英文标题】:Converting 8bpp Indexed Bitmap to 24bpp and back again with the same custom color palette 【发布时间】:2018-05-09 02:42:06 【问题描述】:

我正在制作一个对一些位图进行图形编辑的工具。我有一些带有现有调色板的 8bppIndexed 位图,我必须将其转换为 24bppRgb 以允许我在其上绘制并根据用户选择的某些选项编辑像素颜色。位图图形为原8bpp .bmp文件:

//Get Bitmap from file
Bitmap graphic = new Bitmap(source);

//Get palette and pixel format
ColorPalette pal = graphic.Palette;
PixelFormat pix = graphic.PixelFormat; //Format8bppIndexed

//Create datagraphic in 24bppRgb to allow for editing
PixelFormat datapix = PixelFormat.Format24bppRgb;
Bitmap datagraphic = graphic.Clone(new Rectangle(0, 0, graphic.Width, graphic.Height), datapix);

在绘制和编辑位图数据图形后,我尝试将其转换回 8bppIndexed,并使用与位图图形相同的调色板。我可以这样做:

//Convert back to graphic pixel format
datagraphic = datagraphic.Clone(new Rectangle(0, 0, datagraphic.Width, datagraphic.Height), pix); //Format8bppIndexed

//Apply color palette
datagraphic.Palette = pal;

但是,在克隆具有与图形相同像素格式的数据图形后,它会创建一个全新的调色板。应用 ColorPalette pal 后,datagraphic 中的颜色全部不正确。它们仅通过两个调色板之间的索引号匹配。有没有其他方法可以保留颜色?

是的,位图需要使用自定义调色板进行 8bppIndexed。我试图避免需要通过 Photoshop 使用正确的调色板更改所有最终结果数据图形的颜色索引。

我应该说我对 C# 还是很陌生。我希望我清楚我想要做什么。我很感激这里的任何帮助。提前致谢。

【问题讨论】:

var bmp = new Bitmap(img);从 8bpp img 中得到一个 32bpp 位图。十分简单。将其转换回 8bpp 是您应该始终、始终避免的火箭科学。您可以将其保存为 GIF 格式。 使用相同的调色板将位图转换回 8bpp 是我所追求的。不幸的是,我找不到任何方法来使颜色与原始调色板相匹配。将位图克隆为 8bpp 会创建它自己的调色板,并且颜色索引不同,因此当我应用正确的调色板时,颜色的索引颜色是错误的。我想我必须将其保留为 24bpp,然后使用抖动在 Photoshop 中使用正确的调色板将位图转换回 8bpp。 如果您的图像确实包含 256 种颜色或更少,您可以检查所有像素,重建您的调色板,然后将图像缩小到该调色板。但是,颜色的原始顺序将完全被破坏。除了将原始调色板提供给转换方法之外,没有其他办法。 也就是说,如果你只需要从一个高彩色图像和一个调色板构建一个 8 位图像......我知道该怎么做 那个。跨度> 我要做的是使用自定义调色板将高彩色图像转换为 8 位图像,以便颜色与自定义调色板中最接近的颜色索引相匹配。基本上,如果您转到图像 > 模式 > 索引颜色并将自定义调色板加载到图像,则与 Photoshop 的过程相同。 【参考方案1】:

将 8 位图像转换为 24bpp 很简单:您只需将其绘制在具有相同尺寸和首选像素格式的新图像上。

public static Bitmap PaintOn24bpp(Image image)

    Bitmap bp = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);
    using (Graphics gr = Graphics.FromImage(bp))
        gr.DrawImage(image, new Rectangle(0, 0, bp.Width, bp.Height));
    return bp;

这适用于任何源图像,无论其像素格式如何,但对于包含透明度的图像,您可能需要对其进行预处理以填充任何透明区域,因为我不知道如果图像考虑到透明度进行绘画是否会起作用您正在绘制的不支持 alpha。

反过来,将高色彩图像转换为索引图像要复杂得多,但如果你有调色板,那当然是可能的。这种情况下的主要问题是使用Graphics 不起作用,因为索引图形没有彩色像素;它们有一个调色板,以及只引用这些颜色的像素。

编辑 8 位数据通常归结为构建包含图像数据的字节数组,并将其转换为带有调色板的图像。实际转换是通过创建一个新的 8bpp 位图,使用 LockBits 函数打开其支持字节数组,然后使用 Marshal.Copy 将新数据复制到其中来完成的。

之后,最后一个缺失的链接是一种将图像的彩色像素与其最接近的调色板匹配匹配的方法,因此您可以获取结果匹配数组并将其烘焙成 8bpp 图像。这通常通过计算您的颜色到每种调色板颜色的毕达哥拉斯距离(在 3D RGB 颜色空间中),然后获取该距离最小的颜色的索引来完成。

从头到尾的整个过程在这个答案中有详细的解释:

A: How to convert a colored image to a image that has only two predefined colors?

这处理 2 种颜色,但处理 2 种颜色或 256 种颜色的方法完全相同,因为两种情况下的最终结果都是 8bpp 图像。

它可以针对直接处理 24bpp 图像进行优化,而不是将输入转换为 32bpp,但这可能不值得付出努力,尤其是因为那里发布的方法绝对适用于任何输入。

【讨论】:

以上是关于使用相同的自定义调色板将 8bpp 索引位图转换为 24bpp 并再次转换的主要内容,如果未能解决你的问题,请参考以下文章

Pillow 将 png 转换为 8bit 位图

将索引 BMP 保存为 PNG 不会在 Mac 上保存透明度

将位图数组转换为 YUV(420 平面)?

如何在画布上绘制并转换为位图?

BMP文件中图像数据是按啥顺序存放的?

如何将调色板应用于位图