编辑8bpp索引位图

5
我正在尝试编辑一个8bpp像素。由于这个PixelFormatindexed,我知道它使用颜色表来映射像素值。
尽管我可以通过将其转换为24bpp来编辑位图,但8bpp的编辑速度更快(13ms vs 3ms)。但是,在访问8bpp位图时更改每个值会导致一些随机的RGB颜色,即使PixelFormat仍然是8bpp。
我目前在C#中进行开发,算法如下:
  1. 以8bpp加载原始位图
  2. 创建与原始大小相同的8bpp空临时位图
  3. 锁定两个位图的位并使用P/Invoke调用C++方法,在其中传递每个BitmapData对象的Scan0。(我使用C++方法是因为在迭代位图的像素时提供了更好的性能)
  4. 根据某些参数创建一个int[256]调色板,并通过调色板将原始像素值传递到临时位图字节中进行编辑。
  5. 解锁位。
我的问题是如何在没有奇怪的RGB颜色的情况下编辑像素值。或者,更好的是,如何编辑8bpp位图的颜色表?
3个回答

12

你完全不需要进入C++领域或使用P/Invoke; C#完美支持指针和不安全代码; 我甚至可以猜测这可能导致了你的问题。

颜色可能来自默认调色板。你会发现改变调色板不应该很慢。以下是我创建灰度调色板的方法:

image = new Bitmap( _size.Width, _size.Height, PixelFormat.Format8bppIndexed);
ColorPalette pal = image.Palette;
for(int i=0;i<=255;i++) {
    // create greyscale color table
    pal.Entries[i] = Color.FromArgb(i, i, i);
}
image.Palette = pal; // you need to re-set this property to force the new ColorPalette

我如何将新调色板应用于旧图像?同时,改变旧图像的大小怎么样? - Leon
1
只需将“Palette”属性设置回来即可。调整图像大小是不同的,要使用正确尺寸的新图像上的“Graphics.DrawImage”。 - Dai
Graphics.DrawImage不支持索引像素格式,而转换为非索引会抵消使用索引格式的速度优势。 - marknuzz
1
如果你想在C#中对索引图像进行调整大小,那么你只能自己编写算法来处理原始字节数组,并使用LockBits将结果导入新的位图。或者先将其调整为高色彩,然后将像素匹配回它们最接近的调色板匹配。 - Nyerguds
1
@nyerguds,来自C&C的问候:3 - Dai

4

想象一下,你的索引为8ppp的灰度图像存储在一个线性数组dataB中(为了速度)

尝试使用以下代码:

//Create 8bpp bitmap and look bitmap data
bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
bmp.SetResolution(horizontalResolution, verticalResolution);
bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

//Create grayscale color table
ColorPalette palette = bmp.Palette;
for (int i = 0; i < 256; i++)
    palette.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = palette;

//write data to bitmap
int dataCount = 0;
int stride = bmpData.Stride < 0 ? -bmpData.Stride : bmpData.Stride;
unsafe
{
    byte* row = (byte*)bmpData.Scan0;
    for (int f = 0; f < height; f++)
    {
        for (int w = 0; w < width; w++)
        {
            row[w] = (byte)Math.Min(255, Math.Max(0, dataB[dataCount]));
            dataCount++;
        }
        row += stride;
    }
}

//Unlock bitmap data
bmp.UnlockBits(bmpData);

0
你尝试过加载一个 System.Drawing.Image 吗?这个类可以让你访问颜色调色板。一旦设置了调色板,你就可以将 System.Drawing.Image 包装成 System.Drawing.Bitmap
我不确定 System.Drawing.BitMap.SetPixel() 如何处理索引彩色图像。它可能会尝试将其映射到调色板中最接近的颜色。

谢谢你的回答。我知道可以通过改变位图的调色板来修改位图的调色板属性。但这样做太慢了。我希望有人能告诉我如何访问颜色表指针,以便我可以直接修改它。SetPixel 不起作用,而且速度非常慢。我的疑问是,为什么当我将像素的值从 0(黑色)更改为 100(应该是灰色,但却是红色)时,它会被分配一个 RGB 颜色。 - Pedro Sá
嗯。Bitmap 继承自 Image... 因此具有相同的调色板。您可以使用 bm2.Palette = bm1.Palette 简单地复制调色板。 - Nyerguds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接