【问题标题】:Android: Converting a Bitmap to a Monochrome Bitmap (1 Bit per Pixel)Android:将位图转换为单色位图(每像素 1 位)
【发布时间】:2012-03-11 18:38:36
【问题描述】:

我想将位图打印到移动蓝牙打印机 (Bixolon SPP-R200) - SDK 不提供打印内存中图像的直接方法。所以我想像这样转换位图:

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

到单色位图。我正在使用画布在上面给定的位图上绘制黑色文本,效果很好。但是,当我将上述位图转换为 ByteArray 时,打印机似乎无法处理这些字节。我怀疑我需要一个每个像素一个位的数组(一个像素可以是白色 = 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”参数传递是否有意义?
  • 我想我必须评估每个像素的颜色信息并将其切换为黑色或白色(我会将此值写入一个新的目标字节数组中,最终将传递给打印机)?
  • 如何最好地评估每个像素颜色信息以确定它应该是黑色还是白色? (渲染出来的Bitmap是白底黑疼)

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

更新

按照 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 加星标是因为我正在研究一个类似的概念,即以编程方式将数组操作为单色位图。我有一个问题及其最终答案:stackoverflow.com/questions/17918978/… 如果这对人们有帮助,请给我发电子邮件,我会为这个问题提供合适的答案。
  • 为什么要使用 GetPixels 将所有像素加载到单个数组中......然后通过位图嵌套循环再次调用单个 GetPixel 调用(可能是效率最低的方法)?只需遍历原始数组,然后使用 SetPixels 将整个数组推回位图中。
  • @ClintStLaurent 好点 - 代码真的很旧 xD - 我什至认为我们不再以这种形式使用它,但正如你正确指出的那样,它的效率非常低写在那里。随意根据您的建议进行编辑。

标签: android bitmap monochrome


【解决方案1】:

您可以使用 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 位像素的最低字节。如果

【讨论】:

  • 非常感谢您的回答!只是为了澄清像素评估,我基本上更新了我的问题(在更新下方):我是否通过这样做获得了 32 位 int 的最低字节: int lowerByte = pixel & 0xff; ?
  • 是的。实际上,最低字节的第 7 位正是您想要的 1bpp 像素的位。表达式 '(pixel & 0x80) >> 7' 会给你一个 '0' 或 '1'。
  • 另外说明:我生成的字节数组可能无法被 android 读取吗?使用 BitmapFactory.decodeByteArray(imageInBytes, 0, imageInBytes.length) 返回没有可用的位图(在 android 中显示),打印机也不喜欢它......
  • 首先,Android 不支持 1bpp 位图,其次存在 BitmapFactory.decode* 来解码 PNG 和 JPEG 格式。您需要参考打印机的文档并准确了解它所支持的内容。
  • 感谢您的澄清 - jepp 打印机 SDk 有点乱 :( 它可以打印来自网络和文件系统的图像,但无法打印内存中的图像,这就是我考虑发送的原因直接的位......我会继续尝试:)
【解决方案2】:

嗯,我认为现在回复这个帖子已经很晚了,但我有时也在研究这些东西,并决定建立自己的库,将任何 jpg 或 png 图像转换为 1bpp .bmp。大多数需要 1bpp 图像的打印机都将支持此图像(在其中一个上进行了测试:))。 在这里您可以找到库以及使用它制作单色单通道图像的测试项目。随意更改它..:)

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

享受..!! :)

【讨论】:

  • 感谢您的努力!我相信它会对其他人有所帮助,我也会去看看你发布的项目:)。
【解决方案3】:

您应该将每个像素转换为 HSV 空间,并使用该值来确定目标图像上的 Pixel 应该是黑色还是白色:

  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;

【讨论】:

  • 谢谢,这是一个很酷的实现。
【解决方案4】:

转换为与原始位图大小完全相同的单色不够打印。

打印机只能将每个“像素”(点)打印为单色,因为每个墨水点只有一种颜色,所以他们必须使用比足够多的点更多的点并调整它们的大小、密度......以模拟灰度感觉。这种技术称为halftoning。您可以看到打印机的分辨率通常至少为 600dpi,通常为 1200-4800dpi,而显示屏通常最高为 200-300ppi。

因此,您的单色位图应至少是每边原始分辨率的 3 倍

【讨论】:

  • 你的意思是抖动?我已经将抖动集成到我的代码中:)
  • 没有。抖动是完全不同的业务。在 8 位或更多灰度位图中,有 255 个(或更多,取决于您使用多少位)灰度级,因此原始分辨率很好。但是,如果您使用单色位图,那么几乎所有细节都会显着丢失,因为您将从超过 1670 万种颜色减少到仅 2 种。半色调增加了分辨率并使用额外的像素来提供灰度效果,以此作为通过此限制的一种方式。你可以参考我上面给出的链接。
  • 啊,谢谢你把它弄明白 :) !在我的特定用例中,我已经在黑白画布上绘制。然而,我们打印照片也可能发生,所以对于这些情况,半色调可能真的会派上用场 - 再次感谢:)!
猜你喜欢
  • 2022-09-28
  • 2012-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-09
  • 1970-01-01
  • 2020-01-24
  • 2020-02-28
相关资源
最近更新 更多