颜色到单色的转换

时间:2018-06-28 21:30:20

标签: c# bitmap bitmapdata

请参阅: Save a 32-bit Bitmap as 1-bit .bmp file in C#

列出#1

    public static Bitmap BitmapTo1Bpp(Bitmap source)
    {
        int Width = source.Width; 
        int Height = source.Height; 

        Bitmap dest = new Bitmap(Width, Height, PixelFormat.Format1bppIndexed);
        BitmapData destBmpData = dest.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);

        byte[] destBytes = new byte[(Width + 7) / 8];//19 bytes

        for (int y = 0; y < Height; y++)
        {
            for (int x = 0; x < Width; x++)
            {
                Color c = source.GetPixel(x, y);

                if (x % 8 == 0)
                {
                    destBytes[x / 8] = 0;
                }
                if (c.GetBrightness() >= 0.5)
                {
                    destBytes[x / 8] |= (byte)(0x80 >> (x % 8));
                }
            }
            Marshal.Copy(destBytes, 0, (IntPtr)((long)destBmpData.Scan0 + destBmpData.Stride * y), destBytes.Length);
        }

        dest.UnlockBits(destBmpData);
        return dest;
    }

列出#2

    public static Bitmap BitmapTo1Bpp222(Bitmap source)
    {
        int Width = source.Width; 
        int Height = source.Height; 

        Bitmap dest = new Bitmap(Width, Height, PixelFormat.Format1bppIndexed);
        BitmapData destBmpData = dest.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);

        int destStride = destBmpData.Stride;
        int destSize = Math.Abs(destStride) * Height;

        byte[] destBytes = new byte[destSize];

        for (int y = 0; y < Height; y++)
        {
            for (int x = 0; x < Width; x++)
            {
                Color c = source.GetPixel(x, y);

                if (x % 8 == 0)
                {
                    destBytes[x*y / 8] = 0;
                }
                if (c.GetBrightness() >= 0.5)
                {
                    destBytes[x*y / 8] |= (byte)(0x80 >> (x % 8));
                }
            } 
        } 
        Marshal.Copy(destBytes, 0, destBmpData.Scan0, destBytes.Length);
        dest.UnlockBits(destBmpData);
        return dest;
    } 

查看Marshal.Copy()的位置。

为什么列表1起作用,而列表2却不起作用?

哪些修改可以使清单2正常工作?

1 个答案:

答案 0 :(得分:1)

这两种方法都过于复杂。 LockBits可以转换数据到1bpp。只需将源代码打开为1bpp,将其数据复制到新的1bpp图像中,就可以完成。

我对GetPixelLockBits的组合也感到困惑。通常,使用LockBits意味着您意识到GetPixel是非常缓慢的时间浪费,它会在每次通话时在内部执行LockBits

public static Bitmap BitmapTo1Bpp(Bitmap source)
{
    Rectangle rect = new Rectangle(0, 0, source.Width, source.Height);
    Bitmap dest = new Bitmap(rect.Width, rect.Height, PixelFormat.Format1bppIndexed);
    dest.SetResolution(source.HorizontalResolution, source.VerticalResolution);
    BitmapData sourceData = source.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
    BitmapData targetData = dest.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
    Int32 actualDataWidth = (rect.Width + 7) / 8;
    Int32 h = source.Height;
    Int32 origStride = sourceData.Stride;
    Int32 targetStride = targetData.Stride;
    // buffer for one line of image data.
    Byte[] imageData = new Byte[actualDataWidth];
    Int64 sourcePos = sourceData.Scan0.ToInt64();
    Int64 destPos = targetData.Scan0.ToInt64();
    // Copy line by line, skipping by stride but copying actual data width
    for (Int32 y = 0; y < h; y++)
    {
        Marshal.Copy(new IntPtr(sourcePos), imageData, 0, actualDataWidth);
        Marshal.Copy(imageData, 0, new IntPtr(destPos), actualDataWidth);
        sourcePos += origStride;
        destPos += targetStride;
    }
    dest.UnlockBits(targetData);
    source.UnlockBits(sourceData);
    return dest;
}

请注意,如果纯黑白图像的结果不是1bpp,则应避免将数据转换为索引格式。索引格式是调色板的,这样做不会对逼近图像颜色的优化调色板产生任何形式的减少。它将仅将图像上的颜色更改为该位深度下标准调色板上最接近的颜色。对于1bpp,这只是黑白,非常完美,但是对于4bpp和8bpp,将给出非常差的结果。

还要注意,由于某些原因,您不能从较高的索引像素格式转换为较低的索引像素格式;它将引发异常。由于您可以使用new Bitmap(Bitmap)构造函数将位图转换为32位,因此可以通过调用如下代码轻松避免此问题:

public static Bitmap ConvertTo1Bpp(Bitmap source)
{
    PixelFormat sourcePf = source.PixelFormat;
    if ((sourcePf & PixelFormat.Indexed) == 0 || Image.GetPixelFormatSize(sourcePf) == 1)
        return BitmapTo1Bpp(source);
    using (Bitmap bm32 = new Bitmap(source))
        return BitmapTo1Bpp(bm32);
}