Android Camera2 API 显示经过处理的预览图像

9
新的相机2 API与旧API有很大不同。在管道中向用户显示处理后的摄像头帧使我感到困惑。我知道这个链接上有非常好的解释,但是如何显示帧仍不清楚。我的问题是,在保持Camera2 API 管道效率和速度的同时,如何展示图像读取器回调函数返回的经过处理的屏幕帧?
示例流程:
camera.add_target(imagereader.getsurface) -> 在imagereader的回调函数中进行一些处理 ->(显示经过处理的图像在屏幕上?)
解决思路:每次处理新帧时向ImageView发送位图。

1
你是想针对每一帧预览都这样做,还是只是偶尔这样做(例如每次高分辨率静态捕捉)?根据速率和分辨率,不同的显示方法可能更合适。 - Eddy Talvala
每一帧都很重要。我知道由于图像处理的时间损失,可能会有无法显示的帧。如果YUV格式在预览中给我30帧每秒,并且我能够每秒处理其中的20帧,我想在屏幕上显示这20帧。 - rcmalli
2个回答

17

在澄清问题后进行编辑;原始回答在底部

取决于您执行处理的位置。

如果使用RenderScript,则可以使用setSurface将SurfaceView或TextureView中的Surface连接到Allocation上,然后将处理后的输出写入该Allocation,并使用Allocation.ioSend()发送。 HDR Viewfinder demo 使用了这种方法。

如果正在进行EGL基于着色器的处理,则可以使用eglCreateWindowSurface将Surface连接到EGLSurface上,其中Surface作为native_window参数。 然后,可以将最终输出呈现到该EGLSurface上,当调用eglSwapBuffers时,缓冲区将发送到屏幕。

如果您正在进行本地处理,可以使用NDK ANativeWindow方法将数据写入从Java传递的Surface,并convert为ANativeWindow。
如果您正在进行Java级别的处理,则速度非常慢,您可能不想这样做。但是可以使用新的Android M ImageWriter类,或每帧上传纹理到EGL。
或者如您所说,每帧绘制到ImageView上,但这会很慢。

原始回答:

如果您正在捕获JPEG图像,则可以将Image.getPlanes()[0].getBuffer()中的ByteBuffer内容简单地复制到byte[]中,然后使用BitmapFactory.decodeByteArray将其转换为位图。

如果您正在捕获YUV_420_888图像,则需要编写自己的转换代码,将3平面YCbCr 4:2:0格式转换为您可以显示的内容,例如一个RGB值的int[],以便从中创建位图;不幸的是,目前还没有方便的API可用。

如果您正在捕获RAW_SENSOR图像(Bayer模式未经处理的传感器数据),则需要进行大量的图像处理或仅保存DNG。


就像我在问题中所说的那样,我问的是如何在屏幕上显示已经读取和处理过的帧,而不是如何转换数据类型,不过还是谢谢。 - rcmalli
1
讲解得非常清楚。我相信这个答案会指导那些使用新API的人们。对于一开始不太清楚的问题,我感到抱歉。 - rcmalli
1
@EddyTalvala,您建议使用什么来进行带有预览的筛选视频录制?或者我应该采用完全不同的方法? - Oleksandr
2
如果您想要过滤录制的视频,您需要从相机接收帧,对其进行过滤,然后将其发送到屏幕和视频编码器。您可以从MediaRecorder或MediaCodec获取Surface,并使用Surface创建新的EGLImage从OpenGL发送数据,或者使用ImageWriter从Java发送数据,或者使用Allocation.ioSend()从RenderScript发送数据。哪种方法最好取决于您想如何编写您的过滤器。 - Eddy Talvala
你把图片放在哪里展示?是在TextureView上面还是TextureView的一部分? - Daniel Viglione
@EddyTalvala,我一直在尝试弄清楚如何使用ImageWriter来实现类似OP的功能,但我无法看到如何修改图像,无论是从ImageWriter出队列的还是从ImageReader接收的。您能否指出一个好的资源,展示如何使用ImageWriter来提供一些自定义内容? - LB2

1
我有同样的需求,并希望快速且简单地进行演示。我不担心最终产品的高效处理。可以使用以下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);

这个很好用,但预览图像被旋转了-90度,你知道为什么吗? - spartygw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接