You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.19 02:06:09