如何从 Android MediaImage 以 OUTPUT_IMAGE_FORMAT_RGBA_8888 格式创建位图?
How to create Bitmap from Android MediaImage in OUTPUT_IMAGE_FORMAT_RGBA_8888 format?
我正在尝试使用 CameraX 图像分析(版本 1.1.0-alpha08)的这个新功能:使用 setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888),发送到分析器的图像将具有 RGBA 格式。
我需要将发送到分析器的图像转换为位图,以便我可以将其输入到 TensorFlow 分类器。
如果没有这个新功能,我将收到标准 YUV_420_888 格式的图像,然后我将不得不使用可以在 google 上搜索的几种解决方案之一,以便将 YUV_420_888 转换为 RGBA,然后转换为位图。像这样:https://blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg/。
我假设直接以 RGBA 格式获取媒体图像应该可以帮助我避免实施那些痛苦的解决方案(我实际上已经尝试过但到目前为止似乎对我来说效果不佳)。
问题是我不知道如何将此 RGBA 媒体图像转换为位图。我注意到调用 mediaImage.getFormat() returns 1 不是 ImageFormat 值而是 PixelFormat 值,逻辑上对应于 RGBA_8888 格式,这与文档一致:“发送到 ImageAnalysis.Analyzer.analyze(ImageProxy) 的所有 ImageProxy 都将采用 PixelFormat.RGBA_8888".
格式
我试过这个:
private Bitmap toBitmapRGBA(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
buffer.rewind();
int size = buffer.remaining();
byte[] bytes = new byte[size];
buffer.get(bytes);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
return bitmapImage;
}
此 returns null 表示 decodeByteArray 不起作用。 (我注意到图像只有一架飞机)。
private Bitmap toBitmapRGBA2(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
buffer.rewind();
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
这个 returns 一个看起来很明显但有噪音的位图。
请帮忙!
亲切的问候
迈克尔
我实际上自己找到了解决方案,所以我 post 如果有人感兴趣,我会在这里:
private Bitmap toBitmap(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * image.getWidth();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth()+rowPadding/pixelStride,
image.getHeight(), Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
如果你想在不创建位图对象的情况下进一步处理像素数组,你可以这样做:
val data = imageProxy.planes[0].buffer.toByteArray()
val pixels = IntArray(data.size / imageProxy.planes[0].pixelStride) {
var index = it * imageProxy.planes[0].pixelStride
(data[index++].toInt() and 0xff.shl(16)) or
(data[index++].toInt() and 0xff).shl(8) or
(data[index++].toInt() and 0xff).shl(0) or
(data[index].toInt() and 0xff).shl(24)
}
然后你可以这样创建位图:
Bitmap.createBitmap(
pixels,
0,
imageProxy.planes[0].rowStride / imageProxy.planes[0].pixelStride,
imageProxy.width,
imageProxy.height,
Bitmap.Config.ARGB_8888
)
我正在尝试使用 CameraX 图像分析(版本 1.1.0-alpha08)的这个新功能:使用 setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888),发送到分析器的图像将具有 RGBA 格式。
我需要将发送到分析器的图像转换为位图,以便我可以将其输入到 TensorFlow 分类器。 如果没有这个新功能,我将收到标准 YUV_420_888 格式的图像,然后我将不得不使用可以在 google 上搜索的几种解决方案之一,以便将 YUV_420_888 转换为 RGBA,然后转换为位图。像这样:https://blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg/。 我假设直接以 RGBA 格式获取媒体图像应该可以帮助我避免实施那些痛苦的解决方案(我实际上已经尝试过但到目前为止似乎对我来说效果不佳)。
问题是我不知道如何将此 RGBA 媒体图像转换为位图。我注意到调用 mediaImage.getFormat() returns 1 不是 ImageFormat 值而是 PixelFormat 值,逻辑上对应于 RGBA_8888 格式,这与文档一致:“发送到 ImageAnalysis.Analyzer.analyze(ImageProxy) 的所有 ImageProxy 都将采用 PixelFormat.RGBA_8888".
格式我试过这个:
private Bitmap toBitmapRGBA(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
buffer.rewind();
int size = buffer.remaining();
byte[] bytes = new byte[size];
buffer.get(bytes);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
return bitmapImage;
}
此 returns null 表示 decodeByteArray 不起作用。 (我注意到图像只有一架飞机)。
private Bitmap toBitmapRGBA2(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
buffer.rewind();
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
这个 returns 一个看起来很明显但有噪音的位图。
请帮忙! 亲切的问候 迈克尔
我实际上自己找到了解决方案,所以我 post 如果有人感兴趣,我会在这里:
private Bitmap toBitmap(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * image.getWidth();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth()+rowPadding/pixelStride,
image.getHeight(), Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
如果你想在不创建位图对象的情况下进一步处理像素数组,你可以这样做:
val data = imageProxy.planes[0].buffer.toByteArray()
val pixels = IntArray(data.size / imageProxy.planes[0].pixelStride) {
var index = it * imageProxy.planes[0].pixelStride
(data[index++].toInt() and 0xff.shl(16)) or
(data[index++].toInt() and 0xff).shl(8) or
(data[index++].toInt() and 0xff).shl(0) or
(data[index].toInt() and 0xff).shl(24)
}
然后你可以这样创建位图:
Bitmap.createBitmap(
pixels,
0,
imageProxy.planes[0].rowStride / imageProxy.planes[0].pixelStride,
imageProxy.width,
imageProxy.height,
Bitmap.Config.ARGB_8888
)