代码之家  ›  专栏  ›  技术社区  ›  user366312

颜色到单色转换

  •  -1
  • user366312  · 技术社区  · 6 年前

    参见: 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 回复  |  直到 6 年前
        1
  •  1
  •   Nyerguds    6 年前

    这两者都过于复杂。 LockBits 可以 转换 数据到1bpp。只需打开源代码1bpp,将其数据复制到新的1bpp映像中,就可以了。

    我也很困惑于 GetPixel 锁具 .通常,使用 锁具 意味着你意识到 获取像素 是一种非常缓慢的浪费时间 锁具 内部打开 每次通话 .

    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) 构造函数,通过这样调用代码可以很容易地避免这个问题:

    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);
    }