Android图像表示

Adi*_*vit 3 android image pixels

我正在尝试访问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();
Run Code Online (Sandbox Code Playgroud)

如何buffer.array()存储线性1D中的像素?

  1. 第一个元素是左上角像素还是左下角像素(或其他东西)?
  2. 行主要(一行一行)或列主要(列后一列)?
  3. 频道订单ARGB还是BGRA?
  4. 每个通道的行主要或列主要分别?
  5. 还有别的

Mar*_*lts 5

要获取buffer.array()给定像素的偏移量x,y在大小为widthx heightbytesPerPixel每像素字节数的图像中,请使用以下公式:

offsetForPixel = (y * width + x) * bytesPerPixel
Run Code Online (Sandbox Code Playgroud)

换句话说,数组中的第一个元素是左上角像素,以下元素是行主要元素.像素的所有数据都存储在相邻的字节中,并且不会基于通道展开.这是上面1,2和4的答案.现在让我们讨论3,这是事情变得复杂的地方.

你使用Bitmap.copyPixelsToBuffer()获得的是Android的低级绘图库skia使用的原始位图数据表示.这有三个重要后果:

  • 频道顺序取决于字节顺序
  • 通道与alpha预乘
  • 如何将通道打包到包含数据类型中是可配置的

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);
}
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我得到这个输出:

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
Run Code Online (Sandbox Code Playgroud)

我们可以看到我们正在处理大端,内存表示是预乘的,并且通道已经从ARGB重新排列到RGBA(在skia源代码中由内存表示相同的驱动)作为OpenGL).

如果你想读取像素数据,我建议你改用Bitmap.getPixels().涉及一些复制,但至少API指定如何格式化返回的数据.