Android:将位图转换为单色位图(每像素1位)

26

我想将一个位图打印到一台移动蓝牙打印机(Bixolon SPP-R200)上 - SDK没有提供直接打印内存中图像的方法。因此我考虑像这样转换位图:

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

将位图转换为单色位图。我正在使用画布在上面绘制黑色文本,这很有效。然而,当我将上述位图转换为字节数组时,打印机似乎无法处理这些字节。我怀疑我需要一个每个像素有一个比特的数组(一个像素可以是白色=1或黑色=0)。

由于似乎没有方便的、开箱即用的方法来实现这一点,我曾想过使用:

bitmap.getPixels(pixels, offset, stride, x, y, width, height)

要获取像素值。我认为,我需要按照以下方式使用它:

int width = bitmap.getWidth();
int height = bitmap.getHeight();

int [] pixels = new int [width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);

不过,我对以下几点不太确定:

  • 在getPixels中,直接将宽度作为“ Stride”参数传递是否有意义?
  • 我想我必须评估每个像素的颜色信息,并将其切换为黑色或白色(然后将此值写入新的目标字节数组中,最终将其传递给打印机)?
  • 如何最好地评估每个像素的颜色信息,以便决定它应该是黑色还是白色?(渲染的位图是白色背景上的黑色痛点)

这种方法有意义吗?有更简单的方法吗?仅将位图变成黑白是不够的,主要问题是将每个像素的颜色信息减少为一位。

更新

如Reuben所建议的那样,我将先将位图转换为单色位图。然后我会遍历每个像素:

    int width = bitmap.getWidth();
    int height = bitmap.getHeight();

    int[] pixels = new int[width * height];
    bitmap.getPixels(pixels, 0, width, 0, 0, width, height);

    // Iterate over height
    for (int y = 0; y < height; y++) {
        int offset = y * height;
        // Iterate over width
        for (int x = 0; x < width; x++) {
            int pixel = bitmap.getPixel(x, y);
        }
    }

现在Reuben建议“读取每个32位像素的最低字节”-这与我关于如何评估像素颜色的问题有关。在这方面,我最后一个问题是:我是否可以通过简单地执行以下操作来获取最低字节:

// Using the pixel from bitmap.getPixel(x,y)
int lowestByte = pixel & 0xff;

我将这个QA标记为星号,因为我正在研究一个类似的概念,即通过编程方式操作数组以成为单色位图。我在这里提出了一个问题及其最终答案:http://stackoverflow.com/questions/17918978/plot-an-array-into-bitmap-in-c-c-for-thermal-printer 如果这对人们有所帮助,请给我发电子邮件,我会想出一个合适的答案来回答这个问题。 - Krista K
1
为什么要使用GetPixels将所有像素加载到单个数组中...然后再次嵌套循环遍历位图,调用单个GetPixel调用(最低效的方式)?只需循环遍历原始数组,然后使用SetPixels将整个数组推回位图即可。 - Clint StLaurent
@ClintStLaurent 很好的观点 - 这段代码真的很老 xD - 我甚至认为我们不再以这种形式使用它了,但正如你正确指出的那样,它的编写方式非常低效。请根据您的建议随意编辑它。 - AgentKnopf
4个回答

33

您可以使用ColorMatrix将图像转换为单色32bpp。

Bitmap bmpMonochrome = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmpMonochrome);
ColorMatrix ma = new ColorMatrix();
ma.setSaturation(0);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(ma));
canvas.drawBitmap(bmpSrc, 0, 0, paint);

这简化了从彩色到单色的转换。现在你只需执行getPixels()并读取每个32位像素的最低字节。如果它小于128,则为0,否则为1。


1
是的。实际上,最低字节的第7位恰好是您想要的1bpp像素位。表达式“(pixel&0x80)>> 7”将为您提供该值的“0”或“1”。 - Reuben Scratton
2
首先,Android不支持1bpp位图,其次BitmapFactory.decode*存在是为了解码PNG和JPEG格式。您需要参考打印机的文档,并确定它确切支持什么。 - Reuben Scratton
感谢澄清 - 是的,打印机的SDK有点乱 :( 它可以从网络和文件系统打印图像,但无法打印内存中的图像,所以我考虑直接发送位数据... 我会继续尝试 :) - AgentKnopf
谢谢你的提示!我正在考虑通过套接字发送图像,但是SDK说明不支持32位图像深度,因此我必须将图像缩小为1位深度的图像。我们已经在C#代码中进行了位逐位地发送,通过原始套接字流发送,但我真的想避免这样做,而宁愿使用SDK... - AgentKnopf
@dispake:你尝试过你提到的方法吗?如果是,请问它可行吗?如果没有,请告诉我,我会尝试想出一些解决方案... - AgentKnopf
显示剩余6条评论

13

虽然现在回复这个帖子可能有些晚了,但我也曾经在做这方面的工作,并决定建立自己的库,将任何jpg或png图像转换为1bpp的.bmp格式。大多数需要1bpp图像的打印机都支持这种图像(在其中一台上测试过:)。 您可以在这里找到库以及使用它制作单通道单色图像的测试项目。请随意更改它..:)

https://github.com/acdevs/1bpp-monochrome-android

尽情享用..!! :)


感谢您的努力!我相信这对其他人会有帮助,我也会去看一下您发布的项目 :)。 - AgentKnopf

11
你应该将每个像素转换为HSV空间,并使用该值来确定目标图像上的像素应该是黑色还是白色:
  Bitmap bwBitmap = Bitmap.createBitmap( bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565 );
  float[] hsv = new float[ 3 ];
  for( int col = 0; col < bitmap.getWidth(); col++ ) {
    for( int row = 0; row < bitmap.getHeight(); row++ ) {
      Color.colorToHSV( bitmap.getPixel( col, row ), hsv );
      if( hsv[ 2 ] > 0.5f ) {
        bwBitmap.setPixel( col, row, 0xffffffff );
      } else {
        bwBitmap.setPixel( col, row, 0xff000000 );
      }
    }
  }
  return bwBitmap;

1
谢谢,这是一个很酷的实现。 - King Of The Jungle

5
将原始位图转换为完全相同大小的单色位图是不足以打印的。 打印机只能将每个“像素”(点)作为单色打印,因为每个油墨点只有1种颜色,所以它们必须使用更多的点并调整其大小、密度等来模拟类似灰度的感觉。这种技术称为半色调处理。您可以看到,打印机通常具有至少600dpi的分辨率,通常为1200-4800dpi,而显示屏通常最高达200-300ppi。

Halftone

所以,您的单色位图在每个边上至少应该是原始分辨率的3倍

你是指抖动吗?我已经将抖动集成到我的代码中了 :) - AgentKnopf
不,抖动处理是完全不同的业务。在8位或更多级灰度位图中,有255个(或更多,取决于使用多少位)灰度级别,因此原始分辨率很好。但是,如果您使用单色位图,则几乎所有细节都会显着丢失,因为您将从超过1670万种颜色减少到仅2种。半色调增加了分辨率,并使用额外的像素来给出灰度效果,以克服这种限制。您可以参考我上面提供的链接。 - phuclv
啊,谢谢你澄清这个问题 :)!在我的特定用例中,我已经在黑白画布上绘制了。然而,也可能会出现打印照片的情况,因此在这些情况下半色调可能真的很有用 - 再次感谢 :)! - AgentKnopf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接