使用Android L和Camera2 API处理相机预览图像数据

bub*_*ubo 39 camera android image-processing preview android-5.0-lollipop

我正在开发一个Android应用程序,它正在处理来自摄像头的输入图像并将其显示给用户.这很简单,我PreviewCallback在相机对象上注册了一个setPreviewCallbackWithBuffer.这很简单,并且可以与旧的相机API一起使用

public void onPreviewFrame(byte[] data, Camera cam) {
    // custom image data processing
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试移植我的应用程序以利用新的Camera2 API,我不确定我该怎么做.我在L预览示例中使用了Camera2Video,可以录制视频.但是,样本中没有直接的图像数据传输,因此我不明白应该在何处获取图像像素数据以及如何处理它.

有人可以帮我或建议如何获得PreviewCallbackandroid L 的功能,或者如何在将其显示到屏幕之前处理相机的预览数据?(相机对象上没有预览回调)

谢谢!

VP.*_*VP. 27

由于Camera2API与当前CameraAPI 非常不同,因此阅读文档可能会有所帮助.

一个很好的起点就是camera2basic例子.它演示了如何使用Camera2API并配置ImageReader获取JPEG图像并注册ImageReader.OnImageAvailableListener以接收这些图像

要接收预览画面,您需要将您添加ImageReader的表面setRepeatingRequestCaptureRequest.Builder.

此外,您应该将ImageReader格式设置为YUV_420_888,这将在8MP时为您提供30fps(文档保证在8MP时为Nexus 5提供30fps).

  • @VP:请你用代码向我们解释一下 (2认同)

Ang*_*loS 26

将一些答案组合成一个更容易消化的答案,因为@ VP的答案虽然技术上很清楚,但如果你是第一次从Camera转到Camera2那么很难理解:

使用https://github.com/googlesamples/android-Camera2Basic作为起点,修改以下内容:

createCameraPreviewSession()初始化一个新SurfacemImageReader

Surface mImageSurface = mImageReader.getSurface();
Run Code Online (Sandbox Code Playgroud)

将该新曲面添加为CaptureRequest.Builder变量的输出目标.使用Camera2Basic示例,变量将是mPreviewRequestBuilder

mPreviewRequestBuilder.addTarget(mImageSurface);
Run Code Online (Sandbox Code Playgroud)

这是带有新行的片段(请参阅我的@AngeloS评论):

private void createCameraPreviewSession() {

    try {

        SurfaceTexture texture = mTextureView.getSurfaceTexture();
        assert texture != null;

        // We configure the size of default buffer to be the size of camera preview we want.
        texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

        // This is the output Surface we need to start preview.
        Surface surface = new Surface(texture);

        //@AngeloS - Our new output surface for preview frame data
        Surface mImageSurface = mImageReader.getSurface();

        // We set up a CaptureRequest.Builder with the output Surface.
        mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

        //@AngeloS - Add the new target to our CaptureRequest.Builder
        mPreviewRequestBuilder.addTarget(mImageSurface);

        mPreviewRequestBuilder.addTarget(surface);

        ...
Run Code Online (Sandbox Code Playgroud)

接下来,在setUpCameraOutputs(),格式从改变ImageFormat.JPEGImageFormat.YUV_420_888,当你初始化你的ImageReader.(PS,我还建议删除预览尺寸,以便更顺畅地操作 - Camera2的一个不错的功能)

mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2);
Run Code Online (Sandbox Code Playgroud)

最后,在你的onImageAvailable()方法中ImageReader.OnImageAvailableListener,一定要使用@Kamala的建议,因为如果不关闭它,预览将在几帧后停止

    @Override
    public void onImageAvailable(ImageReader reader) {

        Log.d(TAG, "I'm an image frame!");

        Image image =  reader.acquireNextImage();

        ...

        if (image != null)
            image.close();
    }
Run Code Online (Sandbox Code Playgroud)

  • 在上述代码段中,可以在onImageAvailable函数中处理/修改图像。但是,这不会在预览中显示图像。对?处理完成后如何在预览(TextureView`)上显示图像? (2认同)

小智 14

在ImageReader.OnImageAvailableListener类中,读取后关闭图像,如下所示(这将释放缓冲区以进行下一次捕获).您必须在关闭时处理异常

      Image image =  imageReader.acquireNextImage();
      ByteBuffer buffer = image.getPlanes()[0].getBuffer();
      byte[] bytes = new byte[buffer.remaining()];
      buffer.get(bytes);
      image.close();
Run Code Online (Sandbox Code Playgroud)


pan*_*ski 8

我需要相同的东西,所以我使用他们的例子,并在相机处于预览状态时添加了对新功能的调用.

private CameraCaptureSession.CaptureCallback mCaptureCallback
            = new CameraCaptureSession.CaptureCallback()
    private void process(CaptureResult result) {
        switch (mState) {
            case STATE_PREVIEW: {
                    if (buttonPressed){
                        savePreviewShot();
                    }
                break;
            }
Run Code Online (Sandbox Code Playgroud)

savePreviewShot()仅仅是原来的再生版本captureStillPicture()适用于使用预览模板.

   private void savePreviewShot(){
        try {
            final Activity activity = getActivity();
            if (null == activity || null == mCameraDevice) {
                return;
            }
            // This is the CaptureRequest.Builder that we use to take a picture.
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureBuilder.addTarget(mImageReader.getSurface());

            // Orientation
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

            CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback() {

                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                               TotalCaptureResult result) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss:SSS");
                    Date resultdate = new Date(System.currentTimeMillis());
                    String mFileName = sdf.format(resultdate);
                    mFile = new File(getActivity().getExternalFilesDir(null), "pic "+mFileName+" preview.jpg");

                    Log.i("Saved file", ""+mFile.toString());
                    unlockFocus();
                }
            };

            mCaptureSession.stopRepeating();
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    };
Run Code Online (Sandbox Code Playgroud)