安卓图像表示

3
我正在尝试在Android中访问图像的原始像素数据。
代码大致如下:
   Bitmap bitmap =  BitmapFactory.decodeFile("image.png"); 
   // assert valid input
   if ((bitmap.getConfig() == null) || bitmap.getConfig() == Config.ARGB_8888)
      throw new Exception("bad config");

  ByteBuffer buffer = ByteBuffer.allocate(4 * bitmap.getWidth() * bitmap.getHeight());
  bitmap.copyPixelsToBuffer(buffer);        
  return buffer.array();

线性一维缓冲区buffer.array()中的像素是如何存储的?

  1. 第一个元素是左上角像素还是左下角像素(或其他)?
  2. 按行主序(逐行)还是按列主序(逐列)?
  3. 通道顺序为ARGB还是BGRA?
  4. 每个通道单独按行主序还是按列主序?
  5. 其他内容
1个回答

5

要获取图像大小为widthxheight,每个像素有bytesPerPixel字节的给定像素xybuffer.array()中的偏移量,请使用以下公式:

offsetForPixel = (y * width + x) * bytesPerPixel

换句话说,数组中的第一个元素是左上角像素,接下来的元素是行优先的。每个像素的所有数据都存储在相邻的字节中,而不是基于通道分散开来的。这就是问题1、2和4的答案。现在让我们讨论第3个问题,这是事情变得复杂的地方。
使用Bitmap.copyPixelsToBuffer()获得的是Android低级绘图库skia使用的原始位图数据表示 skia。这有三个重要后果:
- 通道顺序取决于字节序 - 通道预乘以阿尔法值 - 将通道打包到包含数据类型中的方式是可配置的
最后一点使得如果您想要检查单独的像素,则完全使用Bitmap.copyPixelsToBuffer()变得困难,因为您根本无法知道skia已被配置为如何打包通道。作为一个实验,请尝试此代码:
int inputPixel = 0x336699cc;
int[] pixels = new int[] { inputPixel };
Bitmap bm = Bitmap.createBitmap(pixels, 1, 1, Config.ARGB_8888);
ByteBuffer bb = ByteBuffer.allocate(4);
bm.copyPixelsToBuffer(bb);
Log.i("TAG", "inputPixel = 0x" + Integer.toHexString(inputPixel));
for (int i = 0; i < 4; i++) {
    String byteString = "0x" + Integer.toHexString(bb.array()[i] & 0xff);
    Log.i("TAG", "outputPixel byte " + i + " = " + byteString);
}

当我运行它时,我得到这个输出:
I/TAG ( 1995): inputPixel = 0x336699cc
I/TAG ( 1995): outputPixel byte 0 = 0x14
I/TAG ( 1995): outputPixel byte 1 = 0x1f
I/TAG ( 1995): outputPixel byte 2 = 0x29
I/TAG ( 1995): outputPixel byte 3 = 0x33

我们可以看到,这里涉及到大端序,内存中的表示是预乘的,并且通道已从ARGB重新排列为RGBA(在skia源代码中的动机是因为这与OpenGL相同的内存表示方式)。
如果您想读取像素数据,我建议您使用Bitmap.getPixels()。虽然会涉及一些复制,但至少API指定了返回的数据格式。

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