【用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

源代码

相关推荐
jzlhll1233 小时前
deepseek Kotlin Flow 全面详解
android·kotlin·flow
heeheeai3 小时前
kotlin图算法
算法·kotlin·图论
天花板之恋6 小时前
Compose Navigation总结
android jetpack
用户096 小时前
Android面试基础篇(一):基础架构与核心组件深度剖析
android·面试·kotlin
Kapaseker11 小时前
每个Kotlin开发者应该掌握的最佳实践,最后一趴
android·kotlin
alexhilton1 天前
灵活、现代的Android应用架构:完整分步指南
android·kotlin·android jetpack
雨白1 天前
初识协程: 为什么需要它以及如何启动第一个协程
android·kotlin
heeheeai1 天前
Kotlinx Serialization 指南
kotlin·序列化
用户091 天前
MVI架构如何改变Android开发模式
android·面试·kotlin