C#中应用JASC-PAL调色板到256色图像色彩异常如何解决?
图像调色板应用错误问题解决方案
问题根因
你当前代码的核心问题有两个:
- 直接修改图像的
Palette属性仅对索引色格式的图像有效,如果你的原图是24/32位真彩色格式,修改后的调色板不会在保存时生效。 - 即使你处理的是索引色图像,直接替换调色板的颜色条目不会修改图像像素存储的索引值,等于强行把每个索引对应的颜色替换成了新调色板的颜色,显示结果自然完全错位。你需要的是将图像每个像素的颜色匹配到新调色板中最接近的颜色,再重新生成索引色图像。
你原有的JASC-PAL调色板加载逻辑是正确的,无需修改。
修正后实现方案
你需要新增颜色匹配逻辑,生成对应新调色板的256色索引图像,示例代码如下:
using System.Drawing.Imaging; using System.Runtime.InteropServices; // 计算RGB颜色的欧氏距离,找调色板中最接近的颜色索引 private int GetNearestPaletteIndex(Color target, List<Color> palette) { int minDistance = int.MaxValue; int nearestIndex = 0; for (int i = 0; i < palette.Count; i++) { int rDiff = target.R - palette[i].R; int gDiff = target.G - palette[i].G; int bDiff = target.B - palette[i].B; int distance = rDiff * rDiff + gDiff * gDiff + bDiff * bDiff; if (distance < minDistance) { minDistance = distance; nearestIndex = i; // 完全匹配直接返回,优化性能 if (distance == 0) break; } } return nearestIndex; }
替换你原有图像保存逻辑的代码:
byte[] bytes; using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) using (var sourceImage = System.Drawing.Image.FromStream(fs)) { // 先将原图转成32位RGB格式的位图,方便逐像素读取颜色 using (Bitmap rgbBmp = new Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb)) { using (Graphics g = Graphics.FromImage(rgbBmp)) { g.DrawImage(sourceImage, new Rectangle(0, 0, rgbBmp.Width, rgbBmp.Height)); } // 创建256色索引格式的新位图 Bitmap indexedBmp = new Bitmap(rgbBmp.Width, rgbBmp.Height, PixelFormat.Format8bppIndexed); // 给新位图赋值你的自定义调色板 var palette = indexedBmp.Palette; for (int i = 0; i < ColourPalette.Count; i++) { palette.Entries[i] = ColourPalette[i]; } indexedBmp.Palette = palette; // 锁定位图数据,逐像素写入索引值 var bmpData = indexedBmp.LockBits(new Rectangle(0, 0, indexedBmp.Width, indexedBmp.Height), ImageLockMode.WriteOnly, indexedBmp.PixelFormat); IntPtr scan0 = bmpData.Scan0; int stride = bmpData.Stride; // 存储所有像素的索引值 byte[] indexBytes = new byte[stride * indexedBmp.Height]; for (int y = 0; y < rgbBmp.Height; y++) { for (int x = 0; x < rgbBmp.Width; x++) { Color pixelColor = rgbBmp.GetPixel(x, y); int nearestIndex = GetNearestPaletteIndex(pixelColor, ColourPalette); indexBytes[y * stride + x] = (byte)nearestIndex; } } // 把索引值写回新位图 Marshal.Copy(indexBytes, 0, scan0, indexBytes.Length); indexedBmp.UnlockBits(bmpData); // 保存为BMP using (MemoryStream memory = new MemoryStream()) { indexedBmp.Save(memory, ImageFormat.Bmp); bytes = memory.ToArray(); } indexedBmp.Dispose(); } } using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { fs.Write(bytes, 0, bytes.Length); }
如果对性能有要求,可以把GetPixel替换为直接读取锁定位的像素数据,避免GDI+的方法调用开销。
内容的提问来源于stack exchange,提问作者Gaz83
相关产品推荐
相关产品推荐

