我正在尝试访问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();
如何存储线性1D buffer.array()中的像素?
>第一个元素是左上角像素还是左下角像素(或其他东西)?
>行主要(一行一行)或列主要(列后一列)?
>频道订单ARGB或BGRA?
>每个通道的行主要或列主要分别?
>别的
解决方法:
要在大小为widthxheight且每像素bytesPerPixel字节的图像中为给定像素x,y获取偏移到buffer.array(),请使用以下公式:
offsetForPixel = (y * width + x) * bytesPerPixel
换句话说,数组中的第一个元素是左上角像素,以下元素是行主要元素.像素的所有数据都存储在相邻的字节中,并且不会基于通道展开.这是上面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);
}
当我运行它时,我得到这个输出:
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指定如何格式化返回的数据.