簡體   English   中英

使用Android L和Camera2 API處理相機預覽圖像數據

[英]Camera preview image data processing with Android L and Camera2 API

我正在開發一個Android應用程序,它正在處理來自攝像頭的輸入圖像並將其顯示給用戶。 這很簡單,我使用setPreviewCallbackWithBuffer在相機對象上注冊了一個PreviewCallback 使用舊的相機API,這很容易並且運行順暢

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

我正在嘗試移植我的應用程序以利用新的Camera2 API,我不確定我該怎么做。 我在L預覽示例中使用了Camera2Video,可以錄制視頻。 但是,樣本中沒有直接的圖像數據傳輸,因此我不明白應該在何處獲取圖像像素數據以及如何處理它。

有人可以幫我或建議如何在android L中獲得PreviewCallback的功能,或者如何在將其顯示到屏幕之前處理相機的預覽數據? (相機對象上沒有預覽回調)

謝謝!

由於Camera2 API與當前的Camera API非常不同,因此閱讀文檔可能會有所幫助。

一個很好的起點是camera2basic示例。 它演示了如何使用Camera2 API並配置ImageReader來獲取JPEG圖像並注冊ImageReader.OnImageAvailableListener以接收這些圖像

要接收預覽幀,您需要將ImageReader的表面添加到setRepeatingRequestCaptureRequest.Builder

此外,您應該將ImageReader的格式設置為YUV_420_888 ,這將在8MP時為您提供30fps(Nexus 5的文檔保證在8MP時為30fps)。

將一些答案組合成一個更容易消化的答案,因為@ 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();
    }

在ImageReader.OnImageAvailableListener類中,讀取后關閉圖像,如下所示(這將釋放緩沖區以進行下一次捕獲)。 您必須在關閉時處理異常

      Image image =  imageReader.acquireNextImage();
      ByteBuffer buffer = image.getPlanes()[0].getBuffer();
      byte[] bytes = new byte[buffer.remaining()];
      buffer.get(bytes);
      image.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 ,最大圖像緩沖區2然后用reader.acquireLatestImage()onImageAvailable()

因為acquireLatestImage()將從ImageReader的隊列中獲取最新的Image,然后刪除舊的。 對於大多數用例,建議將此函數用於acquireNextImage() ,因為它更適合實時處理。 請注意,最大圖像緩沖區應至少為2

並記得在處理后close()您的圖像。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM