【问题标题】:Converting a bitmap to monochrome将位图转换为单色
【发布时间】:2023-03-17 18:27:01
【问题描述】:

我正在尝试将图像保存为单色(黑白,1 位深度),但我不知道该怎么做。

我从 png 开始并转换为用于打印的位图(它是热敏打印机,无论如何只支持黑色 - 加上如果我尝试以彩色/灰度发送大图像,它的速度非常慢)。

到目前为止,我的代码将其转换为位图非常简单,但它保留了原始颜色深度。

Image image = Image.FromFile("C:\\test.png");

byte[] bitmapFileData = null;
int bitsPerPixel = 1;
int bitmapDataLength;

using (MemoryStream str = new MemoryStream())
{
    image.Save(str, ImageFormat.Bmp);
    bitmapFileData = str.ToArray();
}

【问题讨论】:

标签: c# image bitmap thermal-printer


【解决方案1】:

您想要的是一个好的抖动算法,例如Floyd-SteinbergBayer ordered。您可以自己实现二值化,也可以使用 AForge.NET 之类的库为您完成(下载图像处理示例)。你可以找到二值化文档here

【讨论】:

  • 这取决于您的目标....如果您想要一个与原始彩色图像非常相似但灰度的图像,对于人眼来说,那么是的,您想要抖动。如果您尝试存储最少的数据量(灰度是带 Alpha 的全 RGB 颜色数据的 1/4)以进行比较分析,那么您可能不希望任何抖动,因为它正在改变图像。
【解决方案2】:

这是我整理的一些代码,它采用全彩色(24 位/像素)图像,并将其转换为 1 位/像素输出位图,应用标准 RGB 到灰度转换,然后使用 Floyd-Steinberg 进行转换灰度到 1 位/像素输出。

请注意,这绝不应被视为“理想”实现,但它确实有效。如果您愿意,可以应用许多改进。例如,它将整个输入图像复制到data 数组中,而我们实际上只需要在内存中保留两行(“当前”和“下”行)来累积错误数据。尽管如此,性能似乎可以接受。

public static Bitmap ConvertTo1Bit(Bitmap input)
{
    var masks = new byte[] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
    var output = new Bitmap(input.Width, input.Height, PixelFormat.Format1bppIndexed);
    var data = new sbyte[input.Width, input.Height];
    var inputData = input.LockBits(new Rectangle(0, 0, input.Width, input.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    try
    {
        var scanLine = inputData.Scan0;
        var line = new byte[inputData.Stride];
        for (var y = 0; y < inputData.Height; y++, scanLine += inputData.Stride)
        {
            Marshal.Copy(scanLine, line, 0, line.Length);
            for (var x = 0; x < input.Width; x++)
            {
                data[x, y] = (sbyte)(64 * (GetGreyLevel(line[x * 3 + 2], line[x * 3 + 1], line[x * 3 + 0]) - 0.5));
            }
        }
    }
    finally
    {
        input.UnlockBits(inputData);
    }
    var outputData = output.LockBits(new Rectangle(0, 0, output.Width, output.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
    try
    {
        var scanLine = outputData.Scan0;
        for (var y = 0; y < outputData.Height; y++, scanLine += outputData.Stride)
        {
            var line = new byte[outputData.Stride];
            for (var x = 0; x < input.Width; x++)
            {
                var j = data[x, y] > 0;
                if (j) line[x / 8] |= masks[x % 8];
                var error = (sbyte)(data[x, y] - (j ? 32 : -32));
                if (x < input.Width - 1) data[x + 1, y] += (sbyte)(7 * error / 16);
                if (y < input.Height - 1)
                {
                    if (x > 0) data[x - 1, y + 1] += (sbyte)(3 * error / 16);
                    data[x, y + 1] += (sbyte)(5 * error / 16);
                    if (x < input.Width - 1) data[x + 1, y + 1] += (sbyte)(1 * error / 16);
                }
            }
            Marshal.Copy(line, 0, scanLine, outputData.Stride);
        }
    }
    finally
    {
        output.UnlockBits(outputData);
    }
    return output;
}

public static double GetGreyLevel(byte r, byte g, byte b)
{
    return (r * 0.299 + g * 0.587 + b * 0.114) / 255;
}

【讨论】:

    猜你喜欢
    • 2022-09-28
    • 2012-03-11
    • 2020-01-24
    • 2011-12-20
    • 2020-05-07
    • 2014-03-17
    • 2015-09-22
    • 2020-02-28
    • 2013-04-03
    相关资源
    最近更新 更多