【用androidx.camera拍摄景深合成照片】
用androidx.camera拍摄景深合成照片
- 用androidx.camera拍摄景深合成照片
- 项目概述
- 系统结构
- 相机控制基础
- 核心实现方法
- 技术栈
用androidx.camera拍摄景深合成照片
androidx.camera的不断完善,使得原来复杂繁琐的安卓相机开发容易了许多。很多传统相机上有称之为景深包围的拍照功能,一次拍摄完成多张不同焦距的照片,后期用软件把多张照片合成为一张大景深或全景深照片,这种拍摄方式在安卓系统中也可以实现。
项目概述
DeepCamera 是一个基于 Android 平台的相机应用项目,采用 Kotlin 语言和 Jetpack Compose 框架开发,集成 CameraX 库实现相机功能。模块app用CameraControl+Camera2CameraControl实现对相机的控制。
一次拍摄可以得到多张不同焦距的照片,后期可以用软件(例如photoshop)合成一张大景深照片。
系统结构
整体结构如下:
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对于相机的控制有三种途径
- CameraController
CameraController是androidx.camera.view中的抽象类,它的实现是LifecycleCameraController,这是一个高级控制器,在单个类中提供了大多数 CameraX 核心功能。它负责相机初始化,创建并配置用例,并在准备就绪时将它们绑定到生命周期所有者。它还会监听设备运动传感器,并为UseCase用例设置目标旋转角度。 - CameraControl
CameraControl是androidx.camera.core中的接口,它提供各种异步操作,如变焦、对焦和测光,这些操作会影响当前绑定到该相机的所有UseCase用例的输出。CameraControl 的每种方法都会返回一个 ListenableFuture,应用程序可以用它来检查异步结果。 - Camera2CameraControl
Camera2CameraControl是androidx.camera.camera2.interop中的不可重写类,它提供与 android.hardware.camera2 API 的互操作能力,可以实现CameraControl所不能提供的相机底层操作,例如手动设定焦距。
本程序使用CameraControl+Camera2CameraControl实现对相机的控制,没有使用CameraController。
获取CameraControl和Camera2CameraControl的步骤如下:
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)
核心实现方法
- 获取Preview
fun getPreview(): Preview {// 定义ResolutionStrategyval resolutionStrategy = ResolutionStrategy(Size(1920, 1080), FALLBACK_RULE_CLOSEST_LOWER)// 定义AspectRatioStrategyval aspectRatioStrategy =AspectRatioStrategy(AspectRatio.RATIO_16_9, AspectRatioStrategy.FALLBACK_RULE_AUTO)// 定义ResolutionSelectorval resolutionSelector =ResolutionSelector.Builder().setAspectRatioStrategy(aspectRatioStrategy).setResolutionStrategy(resolutionStrategy).build()return Preview.Builder().setResolutionSelector(resolutionSelector).build()}
- 获取ImageCapture
fun getImageCapture(): ImageCapture {// 定义ResolutionStrategyval resolutionStrategy = ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY// 定义AspectRatioStrategyval aspectRatioStrategy =AspectRatioStrategy(AspectRatio.RATIO_16_9, AspectRatioStrategy.FALLBACK_RULE_AUTO)// 定义ResolutionSelectorval resolutionSelector =ResolutionSelector.Builder().setAspectRatioStrategy(aspectRatioStrategy).setResolutionStrategy(resolutionStrategy).build()return ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY).setResolutionSelector(resolutionSelector).build()}
- 获取ImageCapture的输出文件选项
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()}
- 设置缩放比例
fun setZoomRatio(cameraControl: CameraControl, zoomRatio: Float?): ListenableFuture<Void?> {val clampedZoomRatio = zoomRatio?.coerceIn(0f, 1f) ?: 0freturn cameraControl.setLinearZoom(clampedZoomRatio)}
- 设置焦距
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)}
- 获取相机的对焦信息
fun getFocusDistanceInfo(context: Context): FocusDistanceInfo {val cameraManager =context.getSystemService(Context.CAMERA_SERVICE) as android.hardware.camera2.CameraManagerval 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) ?: 0fLog.i("getMinFocusDistance", "minFocusDistance: $minFocusDistance")// 获取超焦距val hyperFocalDistance = characteristic.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE) ?: 0fLog.i("getMinFocusDistance", "hyperFocalDistance: $hyperFocalDistance")return FocusDistanceInfo(minFocusDistance, hyperFocalDistance, focusDistanceCalibration)}
技术栈
- 开发语言:Kotlin
- UI 框架:Jetpack Compose
- 相机库:CameraX
- 架构组件:Lifecycle、Navigation
- 生命周期处理:Composable Effect
- 图片存储:MediaStore
源代码