从32-BPP转换为8-BPP索引(C#)

时间:2009-11-05 23:21:08

标签: c# .net image gdi

我需要拍摄全彩色JPG图像并将其颜色重新映射到索引调色板。调色板将包含从数据库填充的特定颜色。我需要将图像的每种颜色映射到索引中的“最接近”值。我确信有不同的算法可用于比较和计算“最接近”的值。只寻找C#,.NET托管代码库。

(它将在我们有120个左右特定颜色的按钮的过程中使用,我们希望将任何图像映射到120种颜色以制作拼贴画。)

4 个答案:

答案 0 :(得分:3)

没有什么能帮助你解决GDI问题。对于微软来说,索引图像似乎过于落后了。您所能做的就是读取和写入索引图像文件。

在量化图像中的颜色时通常有两个步骤:
1)找到图像的最佳调色板(颜色量化)
2)将源solors映射到找到的调色板(Color Mapping)

根据我的理解,您已经拥有数据库中的调色板,这意味着最难完成的部分已经为您完成。您需要做的就是将24位颜色映射到提供的调色板颜色。如果你没有起始调色板,那么你将不得不使用量化算法自己计算:八分之一或中位数切是最为人所知的。 Median Cut可以提供更好的结果,但是更慢,更难以实现和微调。

要映射颜色,在您的情况下最简单的算法是计算从源颜色到所有调色板颜色的距离并选择最近的颜色。

float ColorDistanceSquared(Color c1, Color c2)
{
    float deltaR = c2.R - c1.R;
    float deltaG = c2.G - c1.G;
    float deltaB = c2.B - c1.B;
    return deltaR*deltaR + deltaG*deltaG + deltaB*deltaB;
}

你也可以考虑通道,使蓝色重量减轻,不要过于夸张,否则会产生可怕的结果,特别是30/59/11根本不起作用:

float ColorDistanceSquared(Color c1, Color c2)
{
    float deltaR = (c2.R - c1.R) * 3;
    float deltaG = (c2.G - c1.G) * 3;
    float deltaB = (c2.B - c1.B) * 2;
    return deltaR*deltaR + deltaG*deltaG + deltaB*deltaB;
}

为所有源和调色板颜色调用该东西并找到Min。如果您在地图中缓存结果,这将非常快。

此外,源颜色很少适合调色板颜色,不足以在图像中创建条带和平面区域以及细节丢失。为避免这种情况,您可以使用抖动。最简单的算法和给出最佳结果的算法是Error Diffusion Dithering。

绘制颜色后,您必须手动锁定位图并在其中写入索引,因为.Net不会让您写入索引图像。

答案 1 :(得分:2)

此过程称为Quantization。由于每种颜色代表3个打包值,因此您需要使用八进制来解决此问题。

使用示例代码查看此article

本文的重点是获取图像的最终调色板,但是你的过程对于第二部分来说是相反的,只会减少最接近给定调色板的颜色。

答案 2 :(得分:0)

我必须在一个大型.NET项目中执行此操作。框架中没有任何内容,但本文很快引出了一个解决方案:http://codebetter.com/blogs/brendan.tompkins/archive/2004/01/26/6103.aspx

答案 3 :(得分:0)

JPEG字应该敲响警钟。图像很可能已经处于高度量化的色彩空间中,并且进一步的重采样可能会引入混叠。如果可以,请使用未压缩的图像来减少这种影响。

您的问题的答案是肯定的 - 您可以以其他格式保存图像 - 但我不确定原生功能是否适合听起来非常复杂的要求。如果您能够从图像集合中定义调色板,则可能会提高输出的质量。

已经引用的博客文章名为“使用'GDI +保存带有.NET的Crystal-Clear GIF图像''包含对代码的有用参考。