如何在camera2 API中使用reprocessCaptureRequest [英] How to use a reprocessCaptureRequest with camera2 API

查看:3162
本文介绍了如何在camera2 API中使用reprocessCaptureRequest的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试更新摄像头proyect到Android N并且在连续性中移动我的旧 CameraCaptureSession ReprocessableCaptureSession 。我做到了它工作正常,但有了这个新功能,我可以在我的设备中使用 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG 模板,我可以使用 reprocessCaptureRequest

I'm trying to update a camera proyect to Android N and in consecuense Im moving my old CameraCaptureSession to a ReprocessableCaptureSession. I did it and it is working fine, but with this new feature I can use the CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG template in my device and I can reprocess frames with the reprocessCaptureRequest.

这是我的问题出现的地方。因为我没有找到任何示例,我真的不明白有关如何使用 reprocessCaptureRequest 的小文档:

Here is where my problem appear. Cause I don't find any example,and I don't really understand the little documentation about how to use a reprocessCaptureRequest:


每个重新处理CaptureRequest处理一个缓冲区,从CameraCaptureSession的输入Surface到再处理捕获请求中包含的所有输出Surface。必须从从同一相机设备捕获的一个或多个输出图像生成再处理输入图像。应用程序可以通过queueInputImage(Image)向摄像机设备提供输入图像。应用程序必须使用其中一个输出图像的捕获结果来创建重新处理捕获请求,以便摄像机设备可以使用该信息来实现最佳的再处理图像质量。对于仅支持1个输出Surface的相机设备,提交具有多个输出目标的重新处理CaptureRequest将导致CaptureFailure。

Each reprocess CaptureRequest processes one buffer from CameraCaptureSession's input Surface to all output Surfaces included in the reprocess capture request. The reprocess input images must be generated from one or multiple output images captured from the same camera device. The application can provide input images to camera device via queueInputImage(Image). The application must use the capture result of one of those output images to create a reprocess capture request so that the camera device can use the information to achieve optimal reprocess image quality. For camera devices that support only 1 output Surface, submitting a reprocess CaptureRequest with multiple output targets will result in a CaptureFailure.

我试过在 google.sources 但他们做的与我相同。使用倍数imageReaders,在 LinkedBlockingQueue< TotalCaptureResult> 中保存图片的 TotalCaptureResult 然后只需调用:

I tried to have a look to the CTS tests about the camera in google.sources but they do the same than me. Using multiples imageReaders, saving the TotalCaptureResult of the pictures in a LinkedBlockingQueue<TotalCaptureResult> And later just calling:

TotalCaptureResult totalCaptureResult = state.captureCallback.getTotalCaptureResult();
CaptureRequest.Builder reprocessCaptureRequest = cameraStore.state().cameraDevice.createReprocessCaptureRequest(totalCaptureResult);
reprocessCaptureRequest.addTarget(state.yuvImageReader.getSurface());
sessionStore.state().session.capture(reprocessCaptureRequest.build(), null, this.handlers.bg());

但它总是给我一个RuntimeException:
java.lang .RuntimeException:捕获失败:第170帧中的原因0,

But it always throw me a RuntimeException: java.lang.RuntimeException: Capture failed: Reason 0 in frame 170,

我只是不知道哪种方法适合使用ReprocessableCaptureSession因为我已经尝试了一切,我不知道我做错了什么。希望有人能够帮助我!

I just wan't to know which is the right way to work with the ReprocessableCaptureSession cause I already tried everything and I don't know what I'm doing wrong. Hope that someone will be able to help me!

非常感谢

推荐答案

最后我找到了使 reprocessableCaptureSession 工作的解决方案。
我使用Flux架构,所以当你看到 Dispatcher.dispatch(action)时不要混淆,只是把它看作一个回调。所以,这是我的代码:

Finally I found the solution to make my reprocessableCaptureSession work. I use with Flux architecture so don't be confused when you see Dispatcher.dispatch(action), just see it as a callback. So, here is my code:

首先如何创建会话:

 //Configure preview surface
     Size previewSize = previewState.previewSize;
     previewState.previewTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());

     ArrayList<Surface> targets = new ArrayList<>();
     for (SessionOutputTarget outputTarget : state.outputTargets) {
        Surface surface = outputTarget.getSurface();
        if (surface != null) targets.add(surface);
     }
     targets.add(previewState.previewSurface);
     CameraCharacteristics cameraCharacteristics = cameraStore.state().availableCameras.get(cameraStore.state().selectedCamera);
     Size size = CameraCharacteristicsUtil.getYuvOutputSizes(cameraCharacteristics).get(0);

     InputConfiguration inputConfiguration = new InputConfiguration(size.getWidth(),
        size.getHeight(), ImageFormat.YUV_420_888);

     CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
           if (sessionId != currentSessionId) {
              Timber.e("Session opened for an old open request, skipping. Current %d, Request %d", currentSessionId, sessionId);
              //performClose(session);
              return;
           }

           try {
              session.getInputSurface();
              //This call is irrelevant,
              //however session might have closed and this will throw an IllegalStateException.
              //This happens if another camera app (or this one in another PID) takes control
              //of the camera while its opening
           } catch (IllegalStateException e) {
              Timber.e("Another process took control of the camera while creating the session, aborting!");
           }

           Dispatcher.dispatchOnUi(new SessionOpenedAction(session));
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
           if (sessionId != currentSessionId) {
              Timber.e("Configure failed for an old open request, skipping. Current %d, request %d", currentSessionId, sessionId);
              return;
           }

           Timber.e("Failed to configure the session");
           Dispatcher.dispatchOnUi(new SessionFailedAction(session, new IllegalStateException("onConfigureFailed")));
        }
     };

     if (state.outputMode == OutputMode.PHOTO) {
        cameraState.cameraDevice.createReprocessableCaptureSession(inputConfiguration, targets, sessionStateCallback, handlers.bg());
     } else if (state.outputMode == OutputMode.VIDEO) {
        cameraState.cameraDevice.createCaptureSession(targets, sessionStateCallback, handlers.bg());
     }

  } catch (IllegalStateException | IllegalArgumentException e) {
     Timber.e(e, "Something went wrong trying to start the session");
  } catch (CameraAccessException e) {
     //Camera will throw CameraAccessException if another we try to open / close the
     //session very fast.
     Timber.e("Failed to access camera, it was closed");
  }

使用4个曲面创建的照片会话(预览,YUV(输入), JPEG和RAW)。之后,我配置了我的imageWriter:

Photo session as been created with 4 surfaces(Preview, YUV(input), JPEG and RAW). After it, I configure my imageWriter:

   Dispatcher.subscribe(Dispatcher.VERY_HIGH_PRIORITY, SessionOpenedAction.class)
     .filter(a -> isInPhotoMode())
     .subscribe(action -> {
           PhotoState newState = new PhotoState(state());
           newState.zslImageWriter = ImageWriter.newInstance(action.session.getInputSurface(), MAX_REPROCESS_IMAGES);
           setState(newState);
     });

好的,现在我们已经创建了ImageWriter和会话。不,我们用重复请求开始流式传输:

Ok, now we have the ImageWriter and the session created. No we start the streaming with the repeating request:

         CaptureRequest.Builder captureRequestBuilder =
            cameraStore.state().cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);

         captureRequestBuilder.addTarget(previewStore.state().previewSurface);
         captureRequestBuilder.addTarget(photoStore.state().yuvImageReader.getSurface());
         state.session.setRepeatingRequest(captureRequestBuilder.build(), state.zslCaptureCallback, handlers.bg());

要不添加大量代码,只需说zslCaptureCallback是自定义回调即可保存在 LinkedBlockingQueue< TotalCaptureRequest> X最后的TotalCaptureRequests。另外,我使用yuvImageReader(输入一个)保存队列中的最后X个图像。

To don't add a lot of code, just say that the zslCaptureCallback is a custom callback which save in a LinkedBlockingQueue<TotalCaptureRequest> the X last TotalCaptureRequests. Also, I do the same with the yuvImageReader(input one) saving the last X images in a queue.

最后这是我的拍照方法:

Finally here is my "take photo" method:

try {
        //Retrieve the last image stored by the zslImageReader
        Image image = zslImageReaderListener.getImage();
        //Retrieve the last totalCaptureResult from the zslCaptureCallback and create a reprocessableCaptureRequest with it
        TotalCaptureResult captureResult = sessionStore.state().zslCaptureCallback.getCaptureResult(image.getTimestamp());
        CaptureRequest.Builder captureRequest = cameraStore.state().cameraDevice.createReprocessCaptureRequest(captureResult);
        //Add the desired target and values to the captureRequest
        captureRequest.addTarget(state().jpegImageReader.getSurface());
        //Queued back to ImageWriter for future consumption.
        state.zslImageWriter.queueInputImage(image);
        //Drain all the unused and queued CapturedResult from the CaptureCallback
        sessionStore.state().zslCaptureCallback.drain();
        //Capture the desired frame
        CaptureRequest futureCaptureResult = captureRequest.build();
        sessionStore.state().session.capture(futureCaptureResult, new CameraCaptureSession.CaptureCallback() {
           @Override
           public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                          @NonNull CaptureRequest request,
                                          @NonNull TotalCaptureResult result) {
              Dispatcher.dispatchOnUi(new PhotoStatusChangedAction(PhotoState.Status.SUCCESS));
           }

           @Override
           public void onCaptureFailed(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull CaptureFailure failure) {
              super.onCaptureFailed(session, request, failure);
              Exception captureFailedException = new RuntimeException(
                 String.format("Capture failed: Reason %s in frame %d, was image captured? -> %s",
                    failure.getReason(),
                    failure.getFrameNumber(),
                    failure.wasImageCaptured()));
              Timber.e(captureFailedException, "Cannot take mediaType, capture failed!");

              Dispatcher.dispatchOnUi(new PhotoStatusChangedAction(PhotoState.Status.ERROR, captureFailedException));
           }
        }, this.handlers.bg());

        //Capture did not blow up, we are taking the photo now.
        newState.status = PhotoState.Status.TAKING;

     } catch (CameraAccessException | InterruptedException| IllegalStateException | IllegalArgumentException | SecurityException e) {
        Timber.e(e, "Cannot take picture, capture error!");
        newState.status = PhotoState.Status.ERROR;
     }

这篇关于如何在camera2 API中使用reprocessCaptureRequest的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆