将位图转换为单色 [英] Converting a bitmap to monochrome

查看:32
本文介绍了将位图转换为单色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to save an image as monochrome (black&white, 1 bit-depth) but I'm coming up lost how to do it.

我从 png 开始并转换为位图进行打印(它是热敏打印机,无论如何只支持黑色 - 如果我尝试将它们作为彩色/灰度发送,它对于大图像的速度会很慢).

I am starting with a png and converting to a bitmap for printing (it's a thermal printer and only supports black anyway - plus its slow as hell for large images if I try to send them as color/grayscale).

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

My code so far is dead simple to convert it to a bitmap, but it is retaining the original colour depth.

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

推荐答案

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

Here's some code I put together that takes a full colour (24 bits/pixel) image, and converts it to a 1 bit/pixel output bitmap, applying a standard RGB to greyscale conversion, and then using Floyd-Steinberg to convert greyscale to the 1 bit/pixel output.

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

Note that this should by no means be considered an "ideal" implementation, but it does work. There are a number of improvements that could be applied if you wanted. For example, it copies the entire input image into the data array, whereas we really only need to keep two lines in memory (the "current" and "next" lines) for accumulating the error data. Despite this, performance seems acceptable.

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

这篇关于将位图转换为单色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆