1.camera x使用库
def camerax_version = "1.3.3"//1.0.0-alpha01
implementation "androidx.camera:camera-core:${camerax_version}"//Camera核心库,设计架构的实现
implementation "androidx.camera:camera-camera2:${camerax_version}"//Camera2支持库,相机API的实现
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"//协程库
def camerax_view_version = "1.0.0-alpha03"
def camerax_ext_version = "1.0.0-alpha03"
// If you to use the Camera View class
implementation "androidx.camera:camera-view:$camerax_view_version"//自定义的 CameraView 组件
// If you to use Camera Extensions Camera的扩展,用于访问设备专属供应商效果(例如散景、HDR 及其他功能)的 API
implementation "androidx.camera:camera-extensions:$camerax_ext_version"
2.布局方面主要用androidx.camera.view.PreviewView 来显示预览camera画面
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3.实现预览画面的主要code
一、获取权限
<!-- 摄像头权限¬-->
<uses-permission android:name="android.permission.CAMERA" />
// 检查相机权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { // 请求相机权限
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1005)
} else { // 初始化线程池
cameraExecutor = Executors.newSingleThreadExecutor() // 初始化WindowManager以检索显示指标
manager = getSystemService(WINDOW_SERVICE)as WindowManager
updateCameraUi()
lifecycleScope.launch {
setUpCamera()
}
}
二、初始化CameraX
/** 初始化CameraX,并准备绑定相机用例 */
@SuppressLint("NewApi")
private suspend fun setUpCamera() {
var lsicameraProvider: ListenableFuture<ProcessCameraProvider> = ProcessCameraProvider.getInstance(this)
cameraProvider=lsicameraProvider.get()
lensFacing = when {
hasBackCamera() -> CameraSelector.LENS_FACING_BACK
hasFrontCamera() -> CameraSelector.LENS_FACING_FRONT
else -> throw IllegalStateException("Back and front camera are unavailable")
} // 启用或禁用摄像头之间的切换
updateCameraSwitchButton() // 构建并绑定相机用例
bindCameraUseCases()
}
/**
* 切换摄像头button
*/
private fun updateCameraSwitchButton() {
try {
cameraSwitchButton.isEnabled = hasBackCamera() && hasFrontCamera()
} catch (exception : CameraInfoUnavailableException) {
cameraSwitchButton.isEnabled = false
}
}
三、绑定CameraX到PreviewView
/**
* 绑定Camera
*/
@RequiresApi(Build.VERSION_CODES.R)
private fun bindCameraUseCases() {// 获取用于设置相机全屏分辨率的屏幕指标
// // 获取用于设置相机全屏分辨率的屏幕指标【Android11方法】
// val metrics = windowManager.getCurrentWindowMetrics().bounds
// Log.d(TAG, "Screen metrics: ${metrics.width()} x ${metrics.height()}")
// // 计算宽高比
// val screenAspectRatio = aspectRatio(metrics.width(), metrics.height())
val display = windowManager.defaultDisplay//【老版本方法】
val size = Point()
display.getSize(size)
val width = size.x
val height = size.y
val screenAspectRatio = aspectRatio(width, height)
Log.d(TAG, "Preview aspect ratio: $screenAspectRatio")
//获取 PreviewView方向
val rotation = windowManager.defaultDisplay.rotation
// CameraProvider
val cameraProvider = cameraProvider
?: throw IllegalStateException("Camera initialization failed.")
// CameraSelector
val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
// Preview
preview = Preview.Builder() // 设置宽高比
.setTargetAspectRatio(screenAspectRatio) // 设置方向
.setTargetRotation(rotation).build()
// ImageCapture.Metadata 设置输出文件名和其他相关参数
val metadata = ImageCapture.Metadata().apply { // 使用前置摄像头时,处理镜像
isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
} // imageCapture 用于拍摄照片
imageCapture = ImageCapture.Builder() // CAPTURE_MODE_MINIMIZE_LATENCY:捕获模式为最小化延迟模式,适用于快速响应和低延迟的拍摄场景。
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) // 设定宽高比
.setTargetAspectRatio(screenAspectRatio) //设置方向
.setTargetRotation(rotation) //.setMetadata(metadata)
.build()
// ImageAnalysis:分析相机捕获的实时预览图像数据
imageAnalyzer = ImageAnalysis.Builder() // 设定宽高比
.setTargetAspectRatio(screenAspectRatio) // 设置方向
.setOutputImageFormat(OUTPUT_IMAGE_FORMAT_YUV_420_888)//设置编码方式 质量优先
//OUTPUT_IMAGE_FORMAT_RGBA_8888 速度优先
.setTargetRotation(rotation).build() // 将分析器分配给实例
.also { // 在别的线程做分析 需要传入一个线程对象,和一个分析对象
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
Log.d(TAG, "Average luminosity: $luma")
})
} // 在重新绑定用例之前必须解除绑定
cameraProvider.unbindAll()
if (camera != null) { // 必须从之前的相机实例中移除观察者
removeCameraStateObservers(camera!!.cameraInfo)
}
try { // 通过 cameraProvider 绑定生命周期得到Camera对象
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, imageAnalyzer)
// 设置 surfaceProvider,这样数据就会源源不断流到 viewFinder(PreviewView)控件上了,这样就完成了预览了
preview?.setSurfaceProvider(viewFinder.surfaceProvider) // 观察相机状态
observeCameraState(camera?.cameraInfo!!)
} catch (exc : Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}
private fun aspectRatio(width : Int, height : Int) : Int {
val previewRatio = max(width, height).toDouble() / min(width, height)
if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
return AspectRatio.RATIO_4_3
}
return AspectRatio.RATIO_16_9
}
private fun hasBackCamera() : Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false
}
private fun hasFrontCamera() : Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?: false
}
private fun removeCameraStateObservers(cameraInfo : CameraInfo) {
cameraInfo.cameraState.removeObservers(this)
}
通过 cameraProvider 绑定生命周期得到Camera对象,设置 surfaceProvider,这样数据就会源源不断流到 viewFinder(PreviewView)控件上了,这样就完成了预览了
4.拍照的实现
拍照主要是通过 ImageCapture 来完成。
/*相机拍照按钮监听*/
cameraCaptureButton.setOnClickListener {
// ?.let 用于在imageCapture非null时执行一个闭包,否则不执行任何操作。
// ImageCapture 是用于拍摄照片的用例之一。它提供了一个简单、易于使用的 API,用于控制相机硬件以进行图像捕获操作。
imageCapture?.let { imageCapture ->
// 创建带有时间戳的名称和MediaStore
val name = SimpleDateFormat(FILENAME, Locale.US)
.format(System.currentTimeMillis())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, PHOTO_TYPE)
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
val appName = resources.getString(R.string.app_name)
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/${appName}")
}
}
// Metadata:图像捕获配置和控制的属性
val metadata = ImageCapture.Metadata().apply {
// 使用到前摄像头 镜像值为true
isReversedHorizontal =
lensFacing == CameraSelector.LENS_FACING_FRONT
}
val outputOptions = ImageCapture.OutputFileOptions
.Builder(this.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues)
.setMetadata(metadata)
.build()
// imageCapture.takePicture() 方法是用于拍摄照片的方法。使用此方法,您可以执行图像捕获操作,并将结果传递给指定的 ImageCapture.OnImageSavedCallback。
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
/**
* 当图像成功保存到指定文件时被调用。您可以在该方法中执行任何后续操作,例如显示保存后的图像或上传到服务器等。
*/
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri
val message = "拍照成功: ${output.savedUri}"
Log.d(TAG, message)
this@MainActivity.runOnUiThread {
ToastUtils.showMsg(this@MainActivity, message) }
// Android N 以下的发送一个广播更新相册
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
@Suppress("DEPRECATION")
sendBroadcast(
//发送广播更新相册
Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri)
)
}
}
})
}
}
ImageCapture.takePicture() 方法是用于拍摄照片的方法。使用此方法,您可以执行图像捕获操作,并将结果传递给指定的 ImageCapture.OnImageSavedCallback。onError()此callback接收拍照成功失败的信息 onImageSaved()接收拍照成功后的信息,例如保存的uri地址。
5.切换前后像头
/*前后像头切换*/
cameraSwitchButton.setOnClickListener {
lensFacing = if (CameraSelector.LENS_FACING_FRONT == lensFacing) {
CameraSelector.LENS_FACING_BACK
} else {
CameraSelector.LENS_FACING_FRONT
}
// 更新配置后,重新初始化
bindCameraUseCases()
}
ProcessCameraProvider
ProcessCameraProvider 是 Android Jetpack 中提供的用于相机功能的重要组件之一。它提供了一种简单而强大的方式来管理相机的生命周期、配置和图像捕获操作。其主要功能包括:
- 生命周期管理:ProcessCameraProvider 提供了与生命周期相关的方法,可以在适当的生命周期阶段,例如 onCreate()、onStart() 或 onDestroy() 中创建、启动或释放相机资源。这确保了相机资源的正确管理和释放,避免了内存泄漏和资源浪费。与 Android Jetpack 的生命周期组件整合,能够很好地适配应用程序的生命周期,确保在合适的时机启动和关闭相机。
- 相机配置:允许开发者对相机进行各种配置,包括分辨率、帧率、曝光补偿、白平衡等参数的设置,以满足不同的应用需求。
- 图像捕获:ProcessCameraProvider 提供了接口来进行图像捕获操作,包括拍照、录制视频、实时图像处理等功能。
- 多摄像头支持:支持同时管理设备上的多个摄像头,可以方便地切换前后摄像头,并进行并行捕获。
- 图像分析:集成了 ImageAnalysis 功能,可以方便地进行实时图像分析和处理。
- 相机状态监听:提供相机状态的监听器,可以获取相机启动、错误、配置变化等事件的通知。
- 权限管理:封装了相机权限的处理,简化了相机权限请求和处理逻辑。
- 相机绑定:ProcessCameraProvider 可以将相机绑定到指定的预览视图上,从而实现实时预览。可以通过调用 bindToLifecycle() 方法将相机绑定到指定的生命周期所有者(LifecycleOwner)上,并指定预览视图(如 TextureView、SurfaceView 等),从而在界面上显示相机预览。
- 组合多个用例:ProcessCameraProvider 支持多个用例的组合。可以同时使用预览、图像捕获、图像分析等用例,并将它们一起配置和绑定到相机提供者上。这样,您可以在一个应用程序中实现多种相机功能,并灵活地进行配置。
ImageCapture
ImageCapture 是 Android Jetpack CameraX 中的一个用例,用于拍摄照片。它提供了一个简单、易于使用的 API,用于控制相机硬件并执行图像捕获操作。 ImageCapture 用例的主要功能如下:
- 拍摄照片:使用 ImageCapture 可以轻松地拍摄照片。可以调用 takePicture() 方法来触发照片捕获操作,并指定保存图像的文件和其他参数。
- 录像:除了拍照,ImageCapture 还可以录制视频,捕获连续的图像帧并保存为视频文件。
- 配置捕获设置:ImageCapture 提供了一系列可配置的设置,例如图片格式、闪光灯模式、焦点模式等。可以根据需求来自定义这些设置,以满足特定的应用场景。
- 多媒体存储:将捕获的图像或视频数据保存到设备的存储介质,如硬盘、内存卡或云存储。
- 实时滤镜:对捕获的图像进行实时滤镜处理,例如黑白化、模糊、边缘检测等。
- 多摄像头支持:支持同时使用多个摄像头进行图像捕获,例如前置摄像头和后置摄像头切换。
- 摄像头控制:允许应用程序对摄像头进行控制,例如切换摄像头、调整摄像头方向等。
- 监听拍摄结果:ImageCapture 提供了一个回调接口,通过实现 ImageCapture.OnImageSavedCallback 接口,可以处理图像保存成功或失败时的结果。这样,可以根据需要对保存的图像进行进一步处理或处理可能发生的错误。
- 集成图像分析:ImageCapture 可以与图像分析用例结合使用,实现更复杂的图像处理和分析功能。例如,可以在拍摄照片后,使用图像实时分析来检测人脸或识别物体等。
ImageAnalysis
mageAnalysis 是 Android Jetpack CameraX 库中的一个用例,用于实时分析和处理相机捕获的图像数据。它提供了一种方便的方式来执行实时图像处理、计算和分析的操作。 ImageAnalysis 的功能包括:
- 特征提取:从图像中提取关键特征,如边缘、角点、纹理等,以便后续的分析和处理。
- 图像分割:将图像分割成不同的区域或对象,通常用于目标检测、医学图像分析等领域。
- 目标跟踪:追踪图像中的目标或运动物体,可以用于视频监控、自动驾驶等应用中。
- 图像特征匹配:用于在图像中寻找相似的特征点或模式,通常用于图像配准、拼接和三维重建等任务。
- 图像质量评估:对图像的清晰度、对比度、噪声等进行评估,以确定图像的质量和适用性。
- 图像增强:对图像进行滤波、去噪、增强对比度等处理,以改善图像质量或使特定特征更加突出。
- 图像分析:对图像进行统计、分析和建模,以从中提取有用的信息或进行预测
- 实时图像分析:ImageAnalysis 允许对相机捕获的实时预览图像数据进行实时分析。可以使用图像分析算法来处理和提取图像中的特征、对象或信息。 也可用于识别图像中的对象、特征或模式,包括物体检测、人脸识别、文字识别等。 也可通过对图像进行特征提取和模式识别,可以对图像进行分类,例如将图像分为不同的类别或标签。从而实现图像分类功能。
- 分析器回调:通过设置分析器(Analyzer),可以定义自己的图像分析逻辑,并在每次有新的图像可用时接收回调。在回调中,可以获取图像数据,并对其进行处理、计算和分析。这使得可以实时地获取和处理相机捕获的图像,并根据需要执行相应的操作。
- 图像处理和计算:ImageAnalysis 提供了一个用于处理和计算图像数据的接口。可以使用 OpenCV、TensorFlow 等库进行图像处理、计算和机器学习操作。例如,可以执行人脸检测、图像识别、条码扫描等操作,以实现各种应用场景。
- 背压策略和图像队列:ImageAnalysis 还提供了背压策略和图像队列的支持。背压策略定义了当图像处理速度慢于图像生成速度时的处理方式,而图像队列用于存储等待分析的图像数据。可以根据实际需求配置背压策略和图像队列深度,以平衡性能和系统资源的使用。
CameraState.Type
在 Android 相机开发中,CameraState.Type 是一个枚举类,表示相机的状态类型。以下是 CameraState.Type 的一些常见类型及其含义:
- PENDING_OPEN: 相机正在等待信号以尝试打开相机设备的状态。(别的应用已打开相机)
- OPENED:表示相机已经被打开并且处于活动状态。可以开始预览、拍照或录制视频。
- OPEN: 表示相机设备处于打开状态。
- CLOSED:表示相机已经关闭。不再接收预览请求或拍照请求。
ImageCapture.Metadata
ImageCapture.Metadata 是用于捕获图像时附加元数据的类。它是 CameraX 相机库中的一部分。 ImageCapture.Metadata 类提供了一些可用于图像捕获配置和控制的属性,例如闪光灯模式、曝光补偿、对焦模式、白平衡等。可以通过创建一个 ImageCapture.Metadata 对象,并通过设置相应的属性来自定义图像捕获的行为。 以下是 ImageCapture.Metadata 类的一些常见属性:
- FlashMode:闪光灯模式,指定在图像捕获期间是否使用闪光灯。可选值有自动、关闭、打开等。
- ExposureCompensation:曝光补偿值,用于调整图像的亮度。可以通过设置一个浮点数值来进行曝光补偿。
- FocusMode:对焦模式,用于控制相机的对焦方式。可选值有自动对焦、连续对焦、固定对焦等。
- WhiteBalance:白平衡模式,用于校正图像中的色温。可选值包括自动、日光、阴影、白炽灯等。 除了上述属性外,还可以使用 ImageCapture.Metadata 类来设置其他一些属性,例如图像质量、图像方向等。