位图转换为单色
问题说明
我正在尝试将图像保存为单色(黑白,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;
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /reply/detail/tangicbak
-
YouTube API 不能在 iOS (iPhone/iPad) 工作,但在桌面浏览器工作正常?
it1352 07-30 -
iPhone,一张图像叠加到另一张图像上以创建要保存的新图像?(水印)
it1352 07-17 -
保持在后台运行的 iPhone 应用程序完全可操作
it1352 07-25 -
在android同时打开手电筒和前置摄像头
it1352 09-28 -
使用c++17更新时出现G++编译器警告
it1352 06-18 -
使用 iPhone 进行移动设备管理
it1352 07-23 -
扫描 NFC 标签时是否可以启动应用程序?
it1352 08-02 -
复制文件夹/文件而不修改属性?
it1352 07-15 -
Android App 和三星 Galaxy S4 不兼容
it1352 07-20 -
Android微调工具-删除当前选择
it1352 06-20