将位图转换为单色

我试图将图像保存为单色(黑白,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(); } 

这里有一些代码,我把一个全彩色(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; } 

你想要的是像Floyd-Steinberg或拜耳订购的好的抖动算法。 您可以自己实现二值化,也可以使用像AForge.NET这样的库来为您完成(下载图像处理示例)。 您可以在此处找到二值化文档。