Android Camera2 API 显示处理过的预览图像
Android Camera2 API Showing Processed Preview Image
新相机 2 API 与旧相机有很大不同 one.Showing 管道用户部分的操纵相机帧让我感到困惑。我知道 Camera preview image data processing with Android L and Camera2 API 上有很好的解释,但显示帧仍然不清楚。我的问题是在 Camera2 api 管道中保持效率和速度的同时在一些处理后来自 ImageReaders 回调函数的屏幕上显示帧的方式是什么?
示例流程:
camera.add_target(imagereader.getsurface) -> 在图像阅读器回调上做一些处理 -> (在屏幕上显示处理过的图像?)
解决方法:每次处理新帧时都将位图发送到 imageview。
澄清问题后编辑;底部原始答案
取决于您在哪里进行处理。
如果您使用的是 RenderScript,则可以将 Surface 从 SurfaceView 或 TextureView 连接到 Allocation(setSurface), and then write your processed output to that Allocation and send it out with Allocation.ioSend(). The HDR Viewfinder demo 使用此方法。
如果您正在进行基于 EGL 着色器的处理,则可以使用 eglCreateWindowSurface 将 Surface 连接到 EGLSurface,并将 Surface 作为 native_window 参数。然后您可以将最终输出渲染到该 EGLSurface,当您调用 eglSwapBuffers 时,缓冲区将被发送到屏幕。
如果您正在进行本机处理,您可以将 NDK ANativeWindow methods to write to a Surface you pass from Java and convert 用于 ANativeWindow。
如果您正在进行 Java 级别的处理,那会非常慢,您可能不想这样做。但是可以使用新的Android M ImageWriter class,或者每帧上传一个纹理到EGL。
或者如您所说,每帧都绘制到 ImageView,但这会很慢。
原回答:
如果您正在捕获 JPEG 图像,您可以简单地将 ByteBuffer 的内容从 Image.getPlanes()[0].getBuffer()
复制到 byte[]
,然后使用 BitmapFactory.decodeByteArray
将其转换为位图。
如果您正在捕获 YUV_420_888 图像,那么您需要编写自己的从 3 平面 YCbCr 4:2:0 格式到您可以显示的格式的转换代码,例如 int[]用于创建位图的 RGB 值;不幸的是,还没有一个方便的API。
如果您正在捕获 RAW_SENSOR 图像(拜耳模式未处理的传感器数据),那么您需要进行大量图像处理或只保存 DNG。
我有同样的需求,想要一个快速而肮脏的演示操作。我并不担心最终产品的高效加工。使用以下 java 解决方案可以轻松实现这一点。
我将 camera2 预览连接到 TextureView 的原始代码已被注释掉并替换为 ImageReader 的表面:
// Get the surface of the TextureView on the layout
//SurfaceTexture texture = mTextureView.getSurfaceTexture();
//if (null == texture) {
// return;
//}
//texture.setDefaultBufferSize(mPreviewWidth, mPreviewHeight);
//Surface surface = new Surface(texture);
// Capture the preview to the memory reader instead of a UI element
mPreviewReader = ImageReader.newInstance(mPreviewWidth, mPreviewHeight, ImageFormat.JPEG, 1);
Surface surface = mPreviewReader.getSurface();
// This part stays the same regardless of where we render
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(...
然后我为图片注册了一个监听器:
mPreviewReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
Image.Plane plane = image.getPlanes()[0];
ByteBuffer buffer = plane.getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
Bitmap preview = BitmapFactory.decodeByteArray(bytes, 0, buffer.capacity());
image.close();
if(preview != null ) {
// This gets the canvas for the same mTextureView we would have connected to the
// Camera2 preview directly above.
Canvas canvas = mTextureView.lockCanvas();
if (canvas != null) {
float[] colorTransform = {
0, 0, 0, 0, 0,
.35f, .45f, .25f, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0};
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.set(colorTransform); //Apply the monochrome green
ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix);
Paint paint = new Paint();
paint.setColorFilter(colorFilter);
canvas.drawBitmap(preview, 0, 0, paint);
mTextureView.unlockCanvasAndPost(canvas);
}
}
}
}
}, mBackgroundPreviewHandler);
新相机 2 API 与旧相机有很大不同 one.Showing 管道用户部分的操纵相机帧让我感到困惑。我知道 Camera preview image data processing with Android L and Camera2 API 上有很好的解释,但显示帧仍然不清楚。我的问题是在 Camera2 api 管道中保持效率和速度的同时在一些处理后来自 ImageReaders 回调函数的屏幕上显示帧的方式是什么?
示例流程:
camera.add_target(imagereader.getsurface) -> 在图像阅读器回调上做一些处理 -> (在屏幕上显示处理过的图像?)
解决方法:每次处理新帧时都将位图发送到 imageview。
澄清问题后编辑;底部原始答案
取决于您在哪里进行处理。
如果您使用的是 RenderScript,则可以将 Surface 从 SurfaceView 或 TextureView 连接到 Allocation(setSurface), and then write your processed output to that Allocation and send it out with Allocation.ioSend(). The HDR Viewfinder demo 使用此方法。
如果您正在进行基于 EGL 着色器的处理,则可以使用 eglCreateWindowSurface 将 Surface 连接到 EGLSurface,并将 Surface 作为 native_window 参数。然后您可以将最终输出渲染到该 EGLSurface,当您调用 eglSwapBuffers 时,缓冲区将被发送到屏幕。
如果您正在进行本机处理,您可以将 NDK ANativeWindow methods to write to a Surface you pass from Java and convert 用于 ANativeWindow。
如果您正在进行 Java 级别的处理,那会非常慢,您可能不想这样做。但是可以使用新的Android M ImageWriter class,或者每帧上传一个纹理到EGL。
或者如您所说,每帧都绘制到 ImageView,但这会很慢。
原回答:
如果您正在捕获 JPEG 图像,您可以简单地将 ByteBuffer 的内容从 Image.getPlanes()[0].getBuffer()
复制到 byte[]
,然后使用 BitmapFactory.decodeByteArray
将其转换为位图。
如果您正在捕获 YUV_420_888 图像,那么您需要编写自己的从 3 平面 YCbCr 4:2:0 格式到您可以显示的格式的转换代码,例如 int[]用于创建位图的 RGB 值;不幸的是,还没有一个方便的API。
如果您正在捕获 RAW_SENSOR 图像(拜耳模式未处理的传感器数据),那么您需要进行大量图像处理或只保存 DNG。
我有同样的需求,想要一个快速而肮脏的演示操作。我并不担心最终产品的高效加工。使用以下 java 解决方案可以轻松实现这一点。
我将 camera2 预览连接到 TextureView 的原始代码已被注释掉并替换为 ImageReader 的表面:
// Get the surface of the TextureView on the layout
//SurfaceTexture texture = mTextureView.getSurfaceTexture();
//if (null == texture) {
// return;
//}
//texture.setDefaultBufferSize(mPreviewWidth, mPreviewHeight);
//Surface surface = new Surface(texture);
// Capture the preview to the memory reader instead of a UI element
mPreviewReader = ImageReader.newInstance(mPreviewWidth, mPreviewHeight, ImageFormat.JPEG, 1);
Surface surface = mPreviewReader.getSurface();
// This part stays the same regardless of where we render
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(...
然后我为图片注册了一个监听器:
mPreviewReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
Image.Plane plane = image.getPlanes()[0];
ByteBuffer buffer = plane.getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
Bitmap preview = BitmapFactory.decodeByteArray(bytes, 0, buffer.capacity());
image.close();
if(preview != null ) {
// This gets the canvas for the same mTextureView we would have connected to the
// Camera2 preview directly above.
Canvas canvas = mTextureView.lockCanvas();
if (canvas != null) {
float[] colorTransform = {
0, 0, 0, 0, 0,
.35f, .45f, .25f, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0};
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.set(colorTransform); //Apply the monochrome green
ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix);
Paint paint = new Paint();
paint.setColorFilter(colorFilter);
canvas.drawBitmap(preview, 0, 0, paint);
mTextureView.unlockCanvasAndPost(canvas);
}
}
}
}
}, mBackgroundPreviewHandler);