在使用MLKit和Camera2进行人脸检测时,需要捕捉静态图像。

问题描述 投票:0回答:1

我正在开发一个 人脸检测 功能与 摄像机2MLKit.

在《开发者指南》中,在 性能提示 部分,他们说要捕捉图像在 ImageFormat.YUV_420_888 格式,如果使用Camera2 API,我的情况就是这样。

然后,在 人脸检测器 部分,他们建议使用至少480x360像素的图像来进行实时的人脸识别,这也是我的情况。

好了,我们开始吧!这是我的代码。这是我的代码,运行良好

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的图像。

  • 一个全分辨率的图像 或者,至少,大于480x360的图像
  • 的JPEG格式,以便能够将其保存

Camera2Basic样品 演示如何捕捉图像(视频和SlowMotion的样本正在崩溃)和 MLKit示例 使用的是老式的Camera API ! 幸运的是,我已经成功地混合这些样本来开发我的功能,但我未能捕获一个 静态图像 用不同的分辨率。

我想我必须停止预览会话来重新创建一个图像捕获,但我不确定... ...

我所做的是下面的工作,但它捕获的图像是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包含未配置的InputOutput Surface!

谁能帮帮我?

android face-detection android-camera2 firebase-mlkit firebase-vision
1个回答
0
投票

这个 IllegalArgumentException:

java.lang.IllegalArgumentException: CaptureRequest包含未配置的InputOutput Surface!

... 显然是指 imageReader.surface.


而(CameraX)的工作原理则不同,请参见 摄像机碎片.kt ...

问题#197: 使用CameraX API时Firebase人脸检测Api的问题。;

也许很快就会有一个与您的使用情况相匹配的示例应用程序。


0
投票

ImageReader对格式的选择和使用标志的组合很敏感。在 文件 点,某些格式组合可能不被支持。对于一些Android设备(可能是一些旧的手机型号),你可能会发现以下几点 IllegalArgumentException 并不是使用JPEG格式抛出的。但这并没有什么帮助--你想要一些多功能的东西。

我过去做的是使用 ImageFormat.YUV_420_888 格式(这将由硬件和ImageReader实现支持)。这种格式不包含阻止应用程序通过内部平面数组访问图像的预优化。我注意到你已经在你的 initializeCamera() 方法,然后您可以从您想要的帧中提取图像数据。

然后,您可以从您想要的帧中提取图像数据。

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

然后通过位图使用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);
© www.soinside.com 2019 - 2024. All rights reserved.