【用androidx.camera拍摄景深合成照片】

用androidx.camera拍摄景深合成照片

用androidx.camera拍摄景深合成照片

androidx.camera的不断完善,使得原来复杂繁琐的安卓相机开发容易了许多。很多传统相机上有称之为景深包围的拍照功能,一次拍摄完成多张不同焦距的照片,后期用软件把多张照片合成为一张大景深或全景深照片,这种拍摄方式在安卓系统中也可以实现。

项目概述

DeepCamera 是一个基于 Android 平台的相机应用项目,采用 Kotlin 语言和 Jetpack Compose 框架开发,集成 CameraX 库实现相机功能。模块app用CameraControl+Camera2CameraControl实现对相机的控制。

一次拍摄可以得到多张不同焦距的照片,后期可以用软件(例如photoshop)合成一张大景深照片。

系统结构

整体结构如下:

language 复制代码
DeepCamera/app/  
├── theme/					#主题
├── CameraScreen			#相机预览和拍照界面
├── FocusDistanceInfo		#相机焦距信息
├── FocusItem				#焦距项
├── MainActivity			#主界面
├── MainSurface				#相机页面	
├── SettingsSurface			#焦距设置页面
├── ShutterSound			#快门声效类
└── Util         			#静态子程序类  

核心组件包括:

  • UI 层:使用 Jetpack Compose 构建响应式界面
  • 相机控制层:基于 CameraX 实现相机硬件交互
  • 数据层:处理图片存储与系统媒体库交互

相机控制基础

androidX用抽象类UseCase(用例)管理相机的应用。UseCase目前只有ImageAnalysis, ImageCapture, Preview, VideoCapture四种子类,对应四种应用:图像分析、拍照、预览、拍视频。在程序中需要把UseCase绑定到CameraProvider,以实现对相机应用场景的控制。本程序只用到UseCase的两种应用场景:ImageCapture和Preview。

androidX对于相机的控制有三种途径

  1. CameraController
    CameraController是androidx.camera.view中的抽象类,它的实现是LifecycleCameraController,这是一个高级控制器,在单个类中提供了大多数 CameraX 核心功能。它负责相机初始化,创建并配置用例,并在准备就绪时将它们绑定到生命周期所有者。它还会监听设备运动传感器,并为UseCase用例设置目标旋转角度。
  2. CameraControl
    CameraControl是androidx.camera.core中的接口,它提供各种异步操作,如变焦、对焦和测光,这些操作会影响当前绑定到该相机的所有UseCase用例的输出。CameraControl 的每种方法都会返回一个 ListenableFuture,应用程序可以用它来检查异步结果。
  3. Camera2CameraControl
    Camera2CameraControl是androidx.camera.camera2.interop中的不可重写类,它提供与 android.hardware.camera2 API 的互操作能力,可以实现CameraControl所不能提供的相机底层操作,例如手动设定焦距。

本程序使用CameraControl+Camera2CameraControl实现对相机的控制,没有使用CameraController。

获取CameraControl和Camera2CameraControl的步骤如下:

kotlin 复制代码
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
// 初始化CameraProvider的监听器
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
// 初始化CameraProvider
var cameraProvider by remember { mutableStateOf<ProcessCameraProvider?>(null) }
// 初始化PreviewView
val previewView = remember {
    PreviewView(context).apply {
        implementationMode = PreviewView.ImplementationMode.PERFORMANCE
    }
}
// 初始化相机
var camera by remember { mutableStateOf<Camera?>(null) }
// 创建拍照用例
val imageCapture = remember { Util.getImageCapture() }
// 创建预览用例
val preview = remember { Util.getPreview() }
// 使用previewView的SurfaceProvider来配置预览用例
preview.setSurfaceProvider(previewView.surfaceProvider)
// 监听CameraProvider的变化,从监听器中获取CameraProvider实例
cameraProvider = cameraProviderFuture.get()
//定义后摄像头的选择器
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// 解除既有绑定
cameraProvider.unbindAll()
// 绑定新的用例
camera = cameraProvider.bindToLifecycle(lifecycleOwner,cameraSelector, imageCapture, preview)
// 取得cameraControl
val cameraControl = camera!!.cameraControl
// 取得camera2CameraControl
val camera2CameraControl = Camera2CameraControl.from(cameraControl)

核心实现方法

  1. 获取Preview
kotlin 复制代码
fun getPreview(): Preview {
        // 定义ResolutionStrategy
        val resolutionStrategy = ResolutionStrategy(Size(1920, 1080), FALLBACK_RULE_CLOSEST_LOWER)
        // 定义AspectRatioStrategy
        val aspectRatioStrategy =
            AspectRatioStrategy(AspectRatio.RATIO_16_9, AspectRatioStrategy.FALLBACK_RULE_AUTO)
        // 定义ResolutionSelector
        val resolutionSelector =
            ResolutionSelector.Builder().setAspectRatioStrategy(aspectRatioStrategy)
                .setResolutionStrategy(resolutionStrategy).build()
        return Preview.Builder().setResolutionSelector(resolutionSelector).build()
    }
  1. 获取ImageCapture
kotlin 复制代码
fun getImageCapture(): ImageCapture {
        // 定义ResolutionStrategy
        val resolutionStrategy = ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY
        // 定义AspectRatioStrategy
        val aspectRatioStrategy =
            AspectRatioStrategy(AspectRatio.RATIO_16_9, AspectRatioStrategy.FALLBACK_RULE_AUTO)
        // 定义ResolutionSelector
        val resolutionSelector =
            ResolutionSelector.Builder().setAspectRatioStrategy(aspectRatioStrategy)
                .setResolutionStrategy(resolutionStrategy).build()
        return ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .setResolutionSelector(resolutionSelector).build()
    }
  1. 获取ImageCapture的输出文件选项
kotlin 复制代码
private fun getOutputFileOptions(context: Context): ImageCapture.OutputFileOptions {
        val contentValues = ContentValues().apply {
            val simpleDateFormat = SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.getDefault())
            val currentDateTime = simpleDateFormat.format(Date())
            put(MediaStore.MediaColumns.DISPLAY_NAME, "${currentDateTime}.jpg")
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
        }
        return ImageCapture.OutputFileOptions.Builder(
            context.contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues
        ).build()
    }
  1. 设置缩放比例
kotlin 复制代码
fun setZoomRatio(
        cameraControl: CameraControl, zoomRatio: Float?
    ): ListenableFuture<Void?> {
        val clampedZoomRatio = zoomRatio?.coerceIn(0f, 1f) ?: 0f
        return cameraControl.setLinearZoom(clampedZoomRatio)
    }
  1. 设置焦距
kotlin 复制代码
private fun setFocusDistance(
        cameraControl: CameraControl, focusDistance: Float
    ): ListenableFuture<Void?> {
        val camera2CameraControl = Camera2CameraControl.from(cameraControl)
        val captureRequestOptions = CaptureRequestOptions.Builder().setCaptureRequestOption(
            CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF
        ).setCaptureRequestOption(CaptureRequest.LENS_FOCUS_DISTANCE, focusDistance).build()
        return camera2CameraControl.setCaptureRequestOptions(captureRequestOptions)
    }
  1. 获取相机的对焦信息
kotlin 复制代码
fun getFocusDistanceInfo(context: Context): FocusDistanceInfo {
        val cameraManager =
            context.getSystemService(Context.CAMERA_SERVICE) as android.hardware.camera2.CameraManager
        val cameraId = cameraManager.cameraIdList.firstOrNull {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_BACK
        }
        if (cameraId == null) {
            Log.e("getMinFocusDistance", "No back camera found")
            return FocusDistanceInfo(0f, 0f, "no-camera-found")
        }
        val characteristic = cameraManager.getCameraCharacteristics(cameraId)
        // 检查是否支持手动对焦
        val afAvailability = characteristic.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)
        if (afAvailability == null || !afAvailability.contains(CameraMetadata.CONTROL_AF_MODE_OFF)) {
            Log.e("getMinFocusDistance", "Camera does not support manual focus")
            return FocusDistanceInfo(0f, 0f, "non-focus-support")
        }
        // 获取镜头是否校准
        val focusDistanceCalibration =
            when (characteristic.get(CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION)) {
                CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED -> "uncalibrated"
                CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED -> "calibrated"
                CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE -> "approximate"
                else -> "unknown"
            }
        Log.i("getMinFocusDistance", "isLensCalibrated: $focusDistanceCalibration")
        // 获取焦点范围
        val minFocusDistance = characteristic.get(
            CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE
        ) ?: 0f
        Log.i("getMinFocusDistance", "minFocusDistance: $minFocusDistance")
        // 获取超焦距
        val hyperFocalDistance = characteristic.get(
            CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE
        ) ?: 0f
        Log.i("getMinFocusDistance", "hyperFocalDistance: $hyperFocalDistance")
        return FocusDistanceInfo(minFocusDistance, hyperFocalDistance, focusDistanceCalibration)
    }

技术栈

  • 开发语言:Kotlin
  • UI 框架:Jetpack Compose
  • 相机库:CameraX
  • 架构组件:Lifecycle、Navigation
  • 生命周期处理:Composable Effect
  • 图片存储:MediaStore

源代码

相关推荐
黄林晴2 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
A0微声z2 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton2 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream3 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam3 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
用户985120035833 天前
Compose Navigation 3 深度解析(二):基础用法
android·android jetpack
Kapaseker3 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
bqliang3 天前
Compose 媒体查询 (Media Query API) 🖱️👇🕹️
android·android jetpack
糖猫猫cc4 天前
Kite:两种方式实现动态表名
java·kotlin·orm·kite
如此风景4 天前
kotlin协程学习小计
android·kotlin