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);