Android CameraX 如何实现广角/缩小

xin*_*aiz 8 android kotlin android-camera2 android-camerax

我尝试使用CameraX api为我的应用内相机实现广角选项,但遇到了一个问题 -CameraControl.setZoomRatio允许在ZoomState.getMinZoomRatio()和之间设置变焦ZoomState.getMaxZoomRatio(),在我测试的手机上minZoomRatio1.0f。同一款手机支持缩小到0.5f系统相机内。

当前片段我如何初始化相机:

private var camera: Camera? = null
private var imageCapture: ImageCapture? = null

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            val preview = Preview.Builder()
                .build()
                .also {
                    it.setSurfaceProvider(binding.viewFinder.surfaceProvider)
                }

            imageCapture = ImageCapture.Builder()
                .setFlashMode(ImageCapture.FLASH_MODE_AUTO)
                .build()

            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                cameraProvider.unbindAll()

                camera = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture
                )
                viewModel.onCameraStarted()
            } catch (exc: Exception) {
                Timber.e(exc)
            }
        },
        ContextCompat.getMainExecutor(this)
    )
}
Run Code Online (Sandbox Code Playgroud)

当前捏合缩放实现:

val scaleGestureDetector = ScaleGestureDetector(this,
    object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            val camera = camera ?: return false
            val zoomState = camera.cameraInfo.zoomState.value ?: return false
            val scale = zoomState.zoomRatio * detector.scaleFactor
            val finalScale =
                scale.coerceIn(MIN_ZOOM, MAX_ZOOM).coerceIn(zoomState.minZoomRatio, zoomState.maxZoomRatio)
            camera.cameraControl.setZoomRatio(finalScale)
            return true
        }
    })

binding.viewFinder.setOnTouchListener { view, event ->
    view.performClick()
    scaleGestureDetector.onTouchEvent(event)
    return@setOnTouchListener true
}
Run Code Online (Sandbox Code Playgroud)

我的问题是可以使用CameraX api 使用广角相机(或实现0.5f变焦),还是必须使用Camera2重写整个实现。

xin*_*aiz 2

正如问题下面的评论中所解释的,广角镜头相机(以及任何其他相机,例如,如果我们尝试实现变焦 x20,则为大变焦)是单独的相机,因此为了允许在普通相机和(例如)广角镜头相机之间切换,我们必须使用不同的相机选择器重新启动相机视图。我们如何重新启动它,这取决于我们(例如,在某些捏合后重新启动或通过切换相机的按钮重新启动)。

这是相机选择器,根据startCameraData.ultraWide参数获取后置摄像头或广角镜头后置摄像头:

val cameraSelector = if (startCameraData.ultraWide) {
    CameraSelector.Builder()
        .addCameraFilter { cameraInfos ->
            // filter back cameras with minimum sensor pixel size
            val backCameras = cameraInfos.filterIsInstance<Camera2CameraInfoImpl>()
                .filter {
                    val pixelWidth = it.cameraCharacteristicsCompat.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)?.width ?: 0
                    it.lensFacing == CameraSelector.LENS_FACING_BACK && pixelWidth >= 2000 // arbitrary number resolved empirically
                }  

            // try to find wide lens camera, if not present, default to general backCameras
            backCameras.minByOrNull {
                val focalLengths = it.cameraCharacteristicsCompat.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
                focalLengths?.getOrNull(0) ?: 0f
            }
                ?.let { listOf(it) } ?: backCameras
        }
        .build()
} else {
    CameraSelector.DEFAULT_BACK_CAMERA
}
Run Code Online (Sandbox Code Playgroud)

注意: Camera2 api 可能无法让我们访问某些摄像头,具体取决于手机,但有时也会,除非应用程序包被 Google 列入白名单。例如,“打开相机”应用程序 ( net.sourceforge.opencamera) 已列入白名单,并且可以访问所有相机。我通过将应用程序包更改为进行了测试net.sourceforge.opencamera,突然得到了 6 个相机 ID,而不是 3 个。