如何从 ARCore [Android Studio 中的 session.update() 获取位图]
How to get Bitmap from session.update() in ARCore [Android Studio]
我正在尝试使用 ARCore 从我的 ARSession 的当前帧中获取位图。但它始终等于 null。我已经在网上搜索了很长时间,但无法弄清楚我做错了什么。
try {
capturedImage = mFrame.acquireCameraImage();
ByteBuffer buffer = capturedImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length,null);
if (bitmap == null)
Log.e(TAG,"Bitmap was NOT initialized!");
} catch(Exception e){
}
我从 GLSurfaceView
的 onDrawFrame
中获取 mFrame
,我用它来显示相机图像。一切正常,除了我的位图等于 null。
我用的是Button,所以只用了一个Frame,如下:
scanButton = (Button) findViewById(R.id.scanButton);
scanButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkbox = false;
if (capturedImage!=null) capturedImage.close();
BitmapMethod();
}
});
capturedImage
、buffer
和 bytes
都不等于 null。
mFrame.acquireCameraImage()
可能有问题吗?
非常感谢
Is there probably something wrong with mFrame.acquireCameraImage()?
不,mFrame.acquireCameraImage()
按预期工作。
But it always equals null
Bitmap 将始终等于 null,因为位图工厂不理解传递给它的图像数据。
方法 mFrame.acquireCameraImage()
以 YUV 格式或 YCbCr 类型的 Image
对象响应。这些类型的图像有 3 个平面,here 解释得非常好。这些位面包含的ByteArray
可能直接被native
代码中的CPU/GPU读取。 BitmapFactory
无法读取此类数据。因此,您需要将此 YUV 图像转换为其他图像。
为此,您需要使用 YuvImage
class 创建 YUV 实例,然后使用 compressToJpeg
方法将其转换为 JPEG。从中获得 byteArray 后,您可以简单地执行上面的操作。使用 BitmapFactory
将其转换为位图并将其添加到您的 ImageView
.
注:YUV有3个平面。从所有平面创建一个字节数组,然后将其传递给 YUV 构造函数。虽然没有详细说明,但它看起来应该与此类似:
//The camera image received is in YUV YCbCr Format. Get buffers for each of the planes and use them to create a new bytearray defined by the size of all three buffers combined
val cameraPlaneY = cameraImage.planes[0].buffer
val cameraPlaneU = cameraImage.planes[1].buffer
val cameraPlaneV = cameraImage.planes[2].buffer
//Use the buffers to create a new byteArray that
val compositeByteArray = ByteArray(cameraPlaneY.capacity() + cameraPlaneU.capacity() + cameraPlaneV.capacity())
cameraPlaneY.get(compositeByteArray, 0, cameraPlaneY.capacity())
cameraPlaneU.get(compositeByteArray, cameraPlaneY.capacity(), cameraPlaneU.capacity())
cameraPlaneV.get(compositeByteArray, cameraPlaneY.capacity() + cameraPlaneU.capacity(), cameraPlaneV.capacity())
val baOutputStream = ByteArrayOutputStream()
val yuvImage: YuvImage = YuvImage(compositeByteArray, ImageFormat.NV21, cameraImage.width, cameraImage.height, null)
yuvImage.compressToJpeg(Rect(0, 0, cameraImage.width, cameraImage.height), 75, baOutputStream)
val byteForBitmap = baOutputStream.toByteArray()
val bitmap = BitmapFactory.decodeByteArray(byteForBitmap, 0, byteForBitmap.size)
imageView.setImageBitmap(bitmap)
这只是一个粗略的代码。它也许有改进的余地。另请参阅 here.
我不知道是否还有人在寻找答案,但这是我的代码。
Image image = mFrame.acquireCameraImage();
byte[] nv21;
// Get the three planes.
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv21 = new byte[ySize + uSize + vSize];
//U and V are swapped
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
int width = image.getWidth();
int height = image.getHeight();
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
byte[] byteArray = out.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
我也最终重现了与您面临的相同情况。我将 Image 对象设置为 Null。
经过一番研究,我发现问题出在逻辑流程上。
然后我写了下面的代码,它解决了我的问题:
我定义了以下布尔值以设置为捕获按钮单击时的当前帧:
private static boolean captureCurrentFrame = false;
这段代码是我在onClick()函数中写的,用来获取当前帧的RGB和深度图:
public void captureFrame(View view){
Toast.makeText(getApplicationContext(),"Capturing depth and rgb photo",Toast.LENGTH_SHORT).show();
captureCurrentFrame = true;
}
我在 onDrawFrame() 方法中写的这一部分是在从 session.update() 获取帧之后写的:
if(captureCurrentFrame) {
RGBImage = frame.acquireCameraImage();
DepthImage = frame.acquireDepthImage();
Log.d("Image","Format of the RGB Image: " + RGBImage.getFormat());
Log.d("Image","Format of the Depth Image: " + DepthImage.getFormat());
RGBImage.close();
DepthImage.close();
captureCurrentFrame = false;
}
在我的例子中得到 Null 的原因是 onClick 侦听器中的代码在通过 onDraw() 方法之前被触发,因此图像没有存储在变量中。
因此,我将逻辑转移到 onDraw() 并通过侦听器设置的布尔变量触发该流程。
我正在尝试使用 ARCore 从我的 ARSession 的当前帧中获取位图。但它始终等于 null。我已经在网上搜索了很长时间,但无法弄清楚我做错了什么。
try {
capturedImage = mFrame.acquireCameraImage();
ByteBuffer buffer = capturedImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length,null);
if (bitmap == null)
Log.e(TAG,"Bitmap was NOT initialized!");
} catch(Exception e){
}
我从 GLSurfaceView
的 onDrawFrame
中获取 mFrame
,我用它来显示相机图像。一切正常,除了我的位图等于 null。
我用的是Button,所以只用了一个Frame,如下:
scanButton = (Button) findViewById(R.id.scanButton);
scanButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkbox = false;
if (capturedImage!=null) capturedImage.close();
BitmapMethod();
}
});
capturedImage
、buffer
和 bytes
都不等于 null。
mFrame.acquireCameraImage()
可能有问题吗?
非常感谢
Is there probably something wrong with mFrame.acquireCameraImage()?
不,mFrame.acquireCameraImage()
按预期工作。
But it always equals null
Bitmap 将始终等于 null,因为位图工厂不理解传递给它的图像数据。
方法 mFrame.acquireCameraImage()
以 YUV 格式或 YCbCr 类型的 Image
对象响应。这些类型的图像有 3 个平面,here 解释得非常好。这些位面包含的ByteArray
可能直接被native
代码中的CPU/GPU读取。 BitmapFactory
无法读取此类数据。因此,您需要将此 YUV 图像转换为其他图像。
为此,您需要使用 YuvImage
class 创建 YUV 实例,然后使用 compressToJpeg
方法将其转换为 JPEG。从中获得 byteArray 后,您可以简单地执行上面的操作。使用 BitmapFactory
将其转换为位图并将其添加到您的 ImageView
.
注:YUV有3个平面。从所有平面创建一个字节数组,然后将其传递给 YUV 构造函数。虽然没有详细说明,但它看起来应该与此类似:
//The camera image received is in YUV YCbCr Format. Get buffers for each of the planes and use them to create a new bytearray defined by the size of all three buffers combined
val cameraPlaneY = cameraImage.planes[0].buffer
val cameraPlaneU = cameraImage.planes[1].buffer
val cameraPlaneV = cameraImage.planes[2].buffer
//Use the buffers to create a new byteArray that
val compositeByteArray = ByteArray(cameraPlaneY.capacity() + cameraPlaneU.capacity() + cameraPlaneV.capacity())
cameraPlaneY.get(compositeByteArray, 0, cameraPlaneY.capacity())
cameraPlaneU.get(compositeByteArray, cameraPlaneY.capacity(), cameraPlaneU.capacity())
cameraPlaneV.get(compositeByteArray, cameraPlaneY.capacity() + cameraPlaneU.capacity(), cameraPlaneV.capacity())
val baOutputStream = ByteArrayOutputStream()
val yuvImage: YuvImage = YuvImage(compositeByteArray, ImageFormat.NV21, cameraImage.width, cameraImage.height, null)
yuvImage.compressToJpeg(Rect(0, 0, cameraImage.width, cameraImage.height), 75, baOutputStream)
val byteForBitmap = baOutputStream.toByteArray()
val bitmap = BitmapFactory.decodeByteArray(byteForBitmap, 0, byteForBitmap.size)
imageView.setImageBitmap(bitmap)
这只是一个粗略的代码。它也许有改进的余地。另请参阅 here.
我不知道是否还有人在寻找答案,但这是我的代码。
Image image = mFrame.acquireCameraImage();
byte[] nv21;
// Get the three planes.
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv21 = new byte[ySize + uSize + vSize];
//U and V are swapped
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
int width = image.getWidth();
int height = image.getHeight();
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
byte[] byteArray = out.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
我也最终重现了与您面临的相同情况。我将 Image 对象设置为 Null。
经过一番研究,我发现问题出在逻辑流程上。
然后我写了下面的代码,它解决了我的问题:
我定义了以下布尔值以设置为捕获按钮单击时的当前帧:
private static boolean captureCurrentFrame = false;
这段代码是我在onClick()函数中写的,用来获取当前帧的RGB和深度图:
public void captureFrame(View view){
Toast.makeText(getApplicationContext(),"Capturing depth and rgb photo",Toast.LENGTH_SHORT).show();
captureCurrentFrame = true;
}
我在 onDrawFrame() 方法中写的这一部分是在从 session.update() 获取帧之后写的:
if(captureCurrentFrame) {
RGBImage = frame.acquireCameraImage();
DepthImage = frame.acquireDepthImage();
Log.d("Image","Format of the RGB Image: " + RGBImage.getFormat());
Log.d("Image","Format of the Depth Image: " + DepthImage.getFormat());
RGBImage.close();
DepthImage.close();
captureCurrentFrame = false;
}
在我的例子中得到 Null 的原因是 onClick 侦听器中的代码在通过 onDraw() 方法之前被触发,因此图像没有存储在变量中。
因此,我将逻辑转移到 onDraw() 并通过侦听器设置的布尔变量触发该流程。