使用Android L和Camera2 API的相机预览图像数据处理
image-processing
40
0

我正在使用一个Android应用程序,该应用程序正在处理来自相机的输入图像并将其显示给用户。这非常简单,我使用setPreviewCallbackWithBuffer在相机对象上注册了一个PreviewCallback 。这很容易,并且可以与旧相机API一起顺利使用

public void onPreviewFrame(byte[] data, Camera cam) {
    // custom image data processing
}

我正在尝试移植我的应用程序以利用新的Camera2 API,但我不确定该怎么做。我在L预览示例中遵循了Camera2Video,该示例允许录制视频。但是,样本中没有直接的图像数据传输,所以我不知道应该从哪里确切地获取图像像素数据以及如何处理它。

谁能帮我或建议如何在Android L中获得PreviewCallback的功能,或者如何在显示到屏幕之前从相机处理预览数据? (相机对象上没有预览回调)

谢谢!

参考资料:
Stack Overflow
收藏
评论
共 5 个回答
高赞 时间 活跃

这是更好地初始化ImageReader ,最大图像缓冲区2然后用reader.acquireLatestImage()onImageAvailable()

因为acquireLatestImage()将从ImageReader的队列中获取最新的Image,因此会丢弃较旧的Image。对于大多数用例,建议将此函数在acquireNextImage()上使用,因为它更适合实时处理。请注意,最大图像缓冲区应至少为2

并记住在处理后close()图像。

收藏
评论

我需要同样的东西,因此我使用了他们的示例,并在相机处于预览状态时添加了对新函数的调用。

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

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();
        }
    };
收藏
评论

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

      Image image =  imageReader.acquireNextImage();
      ByteBuffer buffer = image.getPlanes()[0].getBuffer();
      byte[] bytes = new byte[buffer.remaining()];
      buffer.get(bytes);
      image.close();
收藏
评论

将一些答案组合成更易于理解的答案,因为@VP的答案虽然技术上很明确,但如果您是第一次从Camera转到Camera2,则很难理解:

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

createCameraPreviewSession()mImageReader初始化一个新的Surface

Surface mImageSurface = mImageReader.getSurface();

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

mPreviewRequestBuilder.addTarget(mImageSurface);

这是带有新行的代码段(请参阅我的@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);

        ...

接下来,在setUpCameraOutputs() ,在初始化ImageReader时将格式从ImageFormat.JPEG更改为ImageFormat.YUV_420_888 。 (PS,我也建议您减小预览尺寸以实现更流畅的操作-Camera2的一项不错功能)

mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2);

最后,在ImageReader.OnImageAvailableListener onImageAvailable()方法中,确保使用onImageAvailable()的建议,因为如果不关闭预览,预览将在几帧后停止

    @Override
    public void onImageAvailable(ImageReader reader) {

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

        Image image =  reader.acquireNextImage();

        ...

        if (image != null)
            image.close();
    }
收藏
评论

由于Camera2 API与当前的Camera API完全不同,因此可能有助于阅读文档。

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

为了接收预览帧,您需要将ImageReader的表面添加到setRepeatingRequestCaptureRequest.Builder

另外,您应该将ImageReader的格式设置为YUV_420_888 ,以8MP的速度提供30fps(Nexus 5的文档保证以8MP的速度提供30fps)。

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题