由 Camera2 字节巨大的位图
Bitmap made from Camera2 bytes huge
我正在使用 Google 给出的示例 here。我按以下方式更改了 OnImageAvailableListener
:
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= reader -> {
Image image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
String pictureId = "asdasd";
Bitmap bitmap = createMutableBitmap(bytes);
StorageClass.getInstance().put(pictureId, bitmap);
Intent intent = new Intent(Camera2MainActivity.this, TPCameraPreviewSnapActivity.class);
intent.setType("image/*");
intent.putExtra("pictureId", pictureId);
startActivityForResult(intent, 1123);
};
我开始的新 Intent 是使用 PhotoView 来显示位图。 StorageClass
提供了一种在意图之间共享大数据(例如这些位图)的静态方法。
另外,方法createMutableBitmap
是:
public static Bitmap createMutableBitmap(byte[] data){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
Log.d(TAG, "BT1: " + data.length);
Bitmap mutableBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
Log.d(TAG, "BT2: " + mutableBitmap.getByteCount());
return mutableBitmap;
}
在华为 P30 Pro 上执行此代码后,我得到如下输出:
W/ImageReader_JNI: Unable to acquire a buffer item, very likely client tried to acquire more than maxImages buffers
E/ion: ioctl c0044901 failed with code -1: Invalid argument
D/Camera2BasicFragment: BT1: 1019119
W/ServiceManagement: getService: unable to call into hwbinder service for vendor.huawei.hardware.jpegdec@1.0::IJpegDecode/default.
D/HwAppInnerBoostImpl: asyncReportData rs.tp.camera2verifiedbypi,1,1,10,0 interval=2774
D/Camera2BasicFragment: BT2: 159694848
无论我如何更改图像的 JPEG 质量,都会弹出数字 159694848
。 159694848 字节约为 160MB。
之后,当尝试将 Bitmap
设置为
的来源时
`PhotoView`, I get an exception:
E/BitmapDrawable: Canvas: trying to use a recycled bitmap
W/System.err: java.lang.RuntimeException: Canvas: trying to draw too large(159694848bytes) bitmap.
这个例子,经过我的更改,在我试过的所有其他手机上都能正常工作,但它们是 运行 Android 8 或更低。
那么,这个问题的原因是什么,我该如何缓解呢?
位图始终是未压缩的,与JPEG压缩无关。 ARGB 格式的位图始终是其大小的像素数 x 4(alpha、红色、绿色、蓝色各 1 个字节)。因此,如果您有一个 1000x1000 像素的位图,加载到内存中时它的大小始终为 4MB。 JPEG 压缩仅适用于存储在磁盘上的文件,内存中的图像始终未压缩。
您可以通过使用每个通道 4 位来使用较小的位图,但会牺牲颜色保真度,或者您可以调整它们的大小。
您正在捕获的 JPEG 的分辨率是多少?假设 ARGB 内部存储,一个 160 MB 的位图是每个像素 4 个字节,因此 160 /4 = 4000 万像素。
来自https://www.gsmarena.com/huawei_p30_pro-9635.php,华为P30 Pro确实有一个40MP的主摄像头(在quad-bayer模式下使用),所以这一切都是一致的。
就是说,Android 视图组件并不真正期望传递价值 40 百万像素的位图;由于屏幕分辨率仅为 1080 x 2340 像素 = 2.5 MP,因此尝试在该屏幕上绘制 40 MP 的理由为零。
如果您只想在屏幕上显示 JPEG,则应在解码时使用 BitmapFactory 选项将图像重新调整为更接近屏幕分辨率的分辨率。这也会为您节省大量内存。
我正在使用 Google 给出的示例 here。我按以下方式更改了 OnImageAvailableListener
:
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= reader -> {
Image image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
String pictureId = "asdasd";
Bitmap bitmap = createMutableBitmap(bytes);
StorageClass.getInstance().put(pictureId, bitmap);
Intent intent = new Intent(Camera2MainActivity.this, TPCameraPreviewSnapActivity.class);
intent.setType("image/*");
intent.putExtra("pictureId", pictureId);
startActivityForResult(intent, 1123);
};
我开始的新 Intent 是使用 PhotoView 来显示位图。 StorageClass
提供了一种在意图之间共享大数据(例如这些位图)的静态方法。
另外,方法createMutableBitmap
是:
public static Bitmap createMutableBitmap(byte[] data){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
Log.d(TAG, "BT1: " + data.length);
Bitmap mutableBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
Log.d(TAG, "BT2: " + mutableBitmap.getByteCount());
return mutableBitmap;
}
在华为 P30 Pro 上执行此代码后,我得到如下输出:
W/ImageReader_JNI: Unable to acquire a buffer item, very likely client tried to acquire more than maxImages buffers
E/ion: ioctl c0044901 failed with code -1: Invalid argument
D/Camera2BasicFragment: BT1: 1019119
W/ServiceManagement: getService: unable to call into hwbinder service for vendor.huawei.hardware.jpegdec@1.0::IJpegDecode/default.
D/HwAppInnerBoostImpl: asyncReportData rs.tp.camera2verifiedbypi,1,1,10,0 interval=2774
D/Camera2BasicFragment: BT2: 159694848
无论我如何更改图像的 JPEG 质量,都会弹出数字 159694848
。 159694848 字节约为 160MB。
之后,当尝试将 Bitmap
设置为
`PhotoView`, I get an exception:
E/BitmapDrawable: Canvas: trying to use a recycled bitmap
W/System.err: java.lang.RuntimeException: Canvas: trying to draw too large(159694848bytes) bitmap.
这个例子,经过我的更改,在我试过的所有其他手机上都能正常工作,但它们是 运行 Android 8 或更低。
那么,这个问题的原因是什么,我该如何缓解呢?
位图始终是未压缩的,与JPEG压缩无关。 ARGB 格式的位图始终是其大小的像素数 x 4(alpha、红色、绿色、蓝色各 1 个字节)。因此,如果您有一个 1000x1000 像素的位图,加载到内存中时它的大小始终为 4MB。 JPEG 压缩仅适用于存储在磁盘上的文件,内存中的图像始终未压缩。
您可以通过使用每个通道 4 位来使用较小的位图,但会牺牲颜色保真度,或者您可以调整它们的大小。
您正在捕获的 JPEG 的分辨率是多少?假设 ARGB 内部存储,一个 160 MB 的位图是每个像素 4 个字节,因此 160 /4 = 4000 万像素。
来自https://www.gsmarena.com/huawei_p30_pro-9635.php,华为P30 Pro确实有一个40MP的主摄像头(在quad-bayer模式下使用),所以这一切都是一致的。
就是说,Android 视图组件并不真正期望传递价值 40 百万像素的位图;由于屏幕分辨率仅为 1080 x 2340 像素 = 2.5 MP,因此尝试在该屏幕上绘制 40 MP 的理由为零。
如果您只想在屏幕上显示 JPEG,则应在解码时使用 BitmapFactory 选项将图像重新调整为更接近屏幕分辨率的分辨率。这也会为您节省大量内存。