Camera preview image data processing with Android L and Camera2 API

前端 未结 5 521
失恋的感觉
失恋的感觉 2020-11-27 11:46

I\'m working on an android app that is processing the input image from the camera and displays it to the user. This is fairly simple, I register a PreviewCallback

相关标签:
5条回答
  • 2020-11-27 12:16

    Since the Camera2 API is very different from the current Camera API, it might help to go through the documentation.

    A good starting point is camera2basic example. It demonstrates how to use Camera2 API and configure ImageReader to get JPEG images and register ImageReader.OnImageAvailableListener to receive those images

    To receive preview frames, you need to add your ImageReader's surface to setRepeatingRequest's CaptureRequest.Builder.

    Also, you should set ImageReader's format to YUV_420_888, which will give you 30fps at 8MP (The documentation guarantees 30fps at 8MP for Nexus 5).

    0 讨论(0)
  • 2020-11-27 12:18

    Combining a few answers into a more digestible one because @VP's answer, while technically clear, is difficult to understand if it's your first time moving from Camera to Camera2:

    Using https://github.com/googlesamples/android-Camera2Basic as a starting point, modify the following:

    In createCameraPreviewSession() init a new Surface from mImageReader

    Surface mImageSurface = mImageReader.getSurface();
    

    Add that new surface as a output target of your CaptureRequest.Builder variable. Using the Camera2Basic sample, the variable will be mPreviewRequestBuilder

    mPreviewRequestBuilder.addTarget(mImageSurface);
    

    Here's the snippet with the new lines (see my @AngeloS comments):

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

    Next, in setUpCameraOutputs(), change the format from ImageFormat.JPEG to ImageFormat.YUV_420_888 when you init your ImageReader. (PS, I also recommend dropping your preview size for smoother operation - one nice feature of Camera2)

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

    Finally, in your onImageAvailable() method of ImageReader.OnImageAvailableListener, be sure to use @Kamala's suggestion because the preview will stop after a few frames if you don't close it

        @Override
        public void onImageAvailable(ImageReader reader) {
    
            Log.d(TAG, "I'm an image frame!");
    
            Image image =  reader.acquireNextImage();
    
            ...
    
            if (image != null)
                image.close();
        }
    
    0 讨论(0)
  • 2020-11-27 12:28

    It's better to init ImageReader with max image buffer is 2 then use reader.acquireLatestImage() inside onImageAvailable().

    Because acquireLatestImage() will acquire the latest Image from the ImageReader's queue, dropping older one. This function is recommended to use over acquireNextImage() for most use-cases, as it's more suited for real-time processing. Note that max image buffer should be at least 2.

    And remember to close() your image after processing.

    0 讨论(0)
  • 2020-11-27 12:30

    I needed the same thing, so I used their example and added a call to a new function when the camera is in preview state.

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

    The savePreviewShot() is simply a recycled version of the original captureStillPicture() adapted to use the preview template.

       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();
            }
        };
    
    0 讨论(0)
  • 2020-11-27 12:32

    In the ImageReader.OnImageAvailableListener class, close the image after reading as shown below (this will release the buffer for next capture). You will have to handle exception on close

          Image image =  imageReader.acquireNextImage();
          ByteBuffer buffer = image.getPlanes()[0].getBuffer();
          byte[] bytes = new byte[buffer.remaining()];
          buffer.get(bytes);
          image.close();
    
    0 讨论(0)
提交回复
热议问题