簡體   English   中英

需要在使用 MLKit 和 Camera2 進行人臉檢測期間捕獲靜止圖像

[英]Need to capture a still image during face detection with MLKit and Camera2

我正在使用Camera2MLKit開發人臉檢測功能。

在開發人員指南的性能提示部分,他們說如果使用 Camera2 API,則以ImageFormat.YUV_420_888格式捕獲圖像,這是我的情況。

然后,在人臉檢測器部分,他們建議使用尺寸至少為 480x360 像素的圖像進行實時人臉識別,這也是我的情況。

好的,讓我們 go,這是我的代碼,運行良好

private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {

    // Open the selected camera
    cameraDevice = openCamera(cameraManager, getCameraId(), cameraHandler)

    val previewSize = if (isPortrait) {
        Size(RECOMMANDED_CAPTURE_SIZE.width, RECOMMANDED_CAPTURE_SIZE.height)
    } else {
        Size(RECOMMANDED_CAPTURE_SIZE.height, RECOMMANDED_CAPTURE_SIZE.width)
    }

    // Initialize an image reader which will be used to display a preview
    imageReader = ImageReader.newInstance(
            previewSize.width, previewSize.height, ImageFormat.YUV_420_888, IMAGE_BUFFER_SIZE)

    // Retrieve preview's frame and run detector
    imageReader.setOnImageAvailableListener({ reader ->
        lifecycleScope.launch(Dispatchers.Main) {
            val image = reader.acquireNextImage()
            logD { "Image available: ${image.timestamp}" }
            faceDetector.runFaceDetection(image, getRotationCompensation())
            image.close()
        }
    }, imageReaderHandler)

    // Creates list of Surfaces where the camera will output frames
    val targets = listOf(viewfinder.holder.surface, imageReader.surface)

    // Start a capture session using our open camera and list of Surfaces where frames will go
    session = createCaptureSession(cameraDevice, targets, cameraHandler)
    val captureRequest = cameraDevice.createCaptureRequest(
            CameraDevice.TEMPLATE_PREVIEW).apply {
        addTarget(viewfinder.holder.surface)
        addTarget(imageReader.surface)
    }

    // This will keep sending the capture request as frequently as possible until the
    // session is torn down or session.stopRepeating() is called
    session.setRepeatingRequest(captureRequest.build(), null, cameraHandler)
}

現在,我想捕捉靜止圖像......這是我的問題,因為理想情況下,我想要:

  • 全分辨率圖像,或至少大於 480x360
  • 以JPEG格式保存

Camera2Basic 示例演示了如何捕獲圖像(視頻和慢動作的示例正在崩潰),而MLKit 示例使用了如此古老的相機 API,! 幸運的是,我成功地混合了這些樣本來開發我的功能,但我未能捕捉到具有不同分辨率的靜止圖像

我想我必須停止預覽 session 才能重新創建一個用於圖像捕獲,但我不確定......

我所做的是以下,但它是在 480x360 中捕獲圖像:

session.stopRepeating()

 // Unset the image reader listener
 imageReader.setOnImageAvailableListener(null, null)
 // Initialize an new image reader which will be used to capture still photos
 // imageReader = ImageReader.newInstance(768, 1024, ImageFormat.JPEG, IMAGE_BUFFER_SIZE)

 // Start a new image queue
 val imageQueue = ArrayBlockingQueue<Image>(IMAGE_BUFFER_SIZE)
 imageReader.setOnImageAvailableListener({ reader - >
    val image = reader.acquireNextImage()
    logD {"[Still] Image available in queue: ${image.timestamp}"}
    if (imageQueue.size >= IMAGE_BUFFER_SIZE - 1) {
        imageQueue.take().close()
    }
    imageQueue.add(image)
}, imageReaderHandler)

 // Creates list of Surfaces where the camera will output frames
 val targets = listOf(viewfinder.holder.surface, imageReader.surface)
 val captureRequest = createStillCaptureRequest(cameraDevice, targets)
 session.capture(captureRequest, object: CameraCaptureSession.CaptureCallback() {
    override fun onCaptureCompleted(
        session: CameraCaptureSession,
        request: CaptureRequest,
        result: TotalCaptureResult) {
        super.onCaptureCompleted(session, request, result)
        val resultTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP)
        logD {"Capture result received: $resultTimestamp"}
        // Set a timeout in case image captured is dropped from the pipeline
        val exc = TimeoutException("Image dequeuing took too long")
        val timeoutRunnable = Runnable {
            continuation.resumeWithException(exc)
        }
        imageReaderHandler.postDelayed(timeoutRunnable, IMAGE_CAPTURE_TIMEOUT_MILLIS)
        // Loop in the coroutine's context until an image with matching timestamp comes
        // We need to launch the coroutine context again because the callback is done in
        //  the handler provided to the `capture` method, not in our coroutine context
        @ Suppress("BlockingMethodInNonBlockingContext")
        lifecycleScope.launch(continuation.context) {
            while (true) {
                // Dequeue images while timestamps don't match
                val image = imageQueue.take()
                if (image.timestamp != resultTimestamp)
                  continue
                logD {"Matching image dequeued: ${image.timestamp}"}

                // Unset the image reader listener
                imageReaderHandler.removeCallbacks(timeoutRunnable)
                imageReader.setOnImageAvailableListener(null, null)

                // Clear the queue of images, if there are left
                while (imageQueue.size > 0) {
                    imageQueue.take()
                        .close()
                }
                // Compute EXIF orientation metadata
                val rotation = getRotationCompensation()
                val mirrored = cameraFacing == CameraCharacteristics.LENS_FACING_FRONT
                val exifOrientation = computeExifOrientation(rotation, mirrored)
                logE {"captured image size (w/h): ${image.width} / ${image.height}"}
                // Build the result and resume progress
                continuation.resume(CombinedCaptureResult(
                    image, result, exifOrientation, imageReader.imageFormat))
                // There is no need to break out of the loop, this coroutine will suspend
            }
        }
    }
}, cameraHandler)
}

如果我取消注釋新的 ImageReader 實例,我有這個例外:

java.lang.IllegalArgumentException:CaptureRequest 包含未配置的輸入/輸出表面!

誰能幫我?

這個IllegalArgumentException

java.lang.IllegalArgumentException:CaptureRequest 包含未配置的輸入/輸出表面!

...顯然是指imageReader.surface


Meanhile(使用 CameraX)這工作方式不同,請參閱CameraFragment.kt ...

問題 #197: Firebase 人臉檢測 Api 問題,同時使用 cameraX API

可能很快就會有一個與您的用例匹配的示例應用程序。

ImageReader 對格式的選擇和/或使用標志的組合很敏感。 文檔指出某些格式組合可能不受支持。 對於某些 Android 設備(可能是一些較舊的手機型號),您可能會發現使用 JPEG 格式不會引發IllegalArgumentException 但這並沒有多大幫助——你想要多才多藝的東西。

我過去所做的是使用ImageFormat.YUV_420_888格式(這將由硬件和 ImageReader 實現支持)。 此格式不包含阻止應用程序通過內部平面陣列訪問圖像的預優化。 我注意到您已經在您的initializeCamera()方法中成功使用了它。

然后,您可以從您想要的幀中提取圖像數據

Image.Plane[] planes = img.getPlanes();
byte[] data = planes[0].getBuffer().array();

然后通過 Bitmap 使用 JPEG 壓縮、PNG 或您選擇的任何編碼創建靜止圖像。

ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null);
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, out);
byte[] imageBytes = out.toByteArray();
Bitmap bitmap= BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, out2);

暫無
暫無

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

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