基于CameraX与Jetpack Compose的相机APP实时滤镜及照片查看功能实现求助
基于CameraX与Jetpack Compose的相机APP实时滤镜及照片查看功能实现求助
你好!看了你基于CameraX和Jetpack Compose写的相机代码,我来给你梳理下实时AR滤镜和照片查看功能的实现思路,帮你解决找不到合适文档的问题。
一、实时AR滤镜(人脸+背景)实现方案
CameraX本身没有直接提供AR滤镜的API,但可以结合ML Kit和自定义帧处理来实现,下面分人脸AR和背景滤镜分别说明:
1. 实时人脸AR滤镜
核心思路是用ML Kit检测人脸关键点,然后在Camera预览帧上叠加AR元素(比如贴纸、表情特效):
- 第一步:集成ML Kit人脸检测库,在
build.gradle(Module level)中添加依赖:dependencies { // ML Kit人脸检测 implementation "com.google.mlkit:face-detection:16.1.5" } - 第二步:用CameraX的
ImageAnalysis分析预览帧,获取人脸数据
在你的CameraContent函数中,新增ImageAnalysis实例并绑定到cameraController:// 初始化ML Kit人脸检测器 val faceDetector = FaceDetection.getClient( FaceDetectorOptions.Builder() .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST) .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL) .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL) .build() ) // 创建ImageAnalysis实例处理预览帧 val imageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() .apply { setAnalyzer(ContextCompat.getMainExecutor(context), ImageAnalysis.Analyzer { imageProxy -> val mediaImage = imageProxy.image ?: return@Analyzer val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) faceDetector.process(inputImage) .addOnSuccessListener { faces -> // 遍历检测到的人脸,获取关键点(眼睛、嘴巴、脸颊等) for (face in faces) { val leftEye = face.getLandmark(FaceLandmark.LEFT_EYE) val rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE) val mouth = face.getLandmark(FaceLandmark.MOUTH_BOTTOM) // 拿到关键点后,可在预览层上层绘制AR元素(比如贴纸) } } .addOnCompleteListener { // 必须关闭imageProxy,否则会导致帧堆积 imageProxy.close() } }) } // 将ImageAnalysis绑定到cameraController cameraController.bindToLifecycle(lifecycleOwner, cameraController.cameraSelector, imageAnalysis) - 第三步:在Compose中叠加AR元素
因为你用AndroidView包裹了PreviewView,所以可以在PreviewView的上层用Composable元素(比如Box、Image)绘制AR贴纸。需要注意把相机帧的坐标转换成Compose的坐标,要结合相机的旋转角度调整位置。
2. 实时背景滤镜
背景滤镜分两种实现方式:
- 简单滤镜(模糊/灰度):直接在ImageAnalysis中对帧的像素进行处理,然后用自定义的
SurfaceView或TextureView替换PreviewView来渲染处理后的帧(PreviewView默认是硬件加速,自定义渲染更灵活)。 - 智能抠图换背景:用ML Kit的人像分割模型,把人像从背景中分离出来,再叠加自定义背景。先添加分割库依赖:
然后在ImageAnalysis中处理帧,分割出人像区域后叠加新背景即可。implementation "com.google.mlkit:segmentation-selfie:16.0.0-beta5"
二、照片查看功能实现
你已经把照片保存到MediaStore的Pictures/AirPic目录,只需查询该目录下的图片,再用Compose展示即可:
1. 查询本地AirPic目录照片
创建一个工具函数或ViewModel方法,查询MediaStore中的目标图片:
fun getAirPicPhotos(contentResolver: ContentResolver): List<Uri> { val photos = mutableListOf<Uri>() // 查询所需字段 val projection = arrayOf(MediaStore.Images.Media._ID) // 指定查询AirPic目录 val selection = "${MediaStore.Images.Media.RELATIVE_PATH} LIKE ?" val selectionArgs = arrayOf("%Pictures/AirPic%") // 按拍摄时间倒序排列 val sortOrder = "${MediaStore.Images.Media.DATE_ADDED} DESC" contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder )?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID) while (cursor.moveToNext()) { val id = cursor.getLong(idColumn) val contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id) photos.add(contentUri) } } return photos }
2. 用Compose展示照片列表
创建一个新的GalleryScreen页面,用LazyColumn展示照片(建议用Coil/Glide的Compose扩展加载图片):
@Composable fun GalleryScreen(contentResolver: ContentResolver) { // 用remember缓存照片列表,避免重复查询 val photos by remember { mutableStateOf(getAirPicPhotos(contentResolver)) } LazyColumn(modifier = Modifier.fillMaxSize()) { items(photos) { photoUri -> Image( painter = rememberAsyncImagePainter(photoUri), contentDescription = "Captured photo", modifier = Modifier .fillMaxWidth() .height(200.dp) .padding(4.dp) .clip(RoundedCornerShape(8.dp)) ) } } }
然后在相机页面的Gallery按钮点击事件中,用Jetpack Navigation跳转到该页面即可。
额外提示
- 处理相机帧时,务必使用
STRATEGY_KEEP_ONLY_LATEST策略,避免帧堆积导致性能下降 - 人脸关键点的坐标转换要考虑相机的旋转角度,参考
imageProxy.imageInfo.rotationDegrees调整 - 照片加载建议用Coil(
io.coil-kt:coil-compose)或Glide的Compose扩展,提升加载效率和体验
备注:内容来源于stack exchange,提问作者Jayashre
相关产品推荐
相关产品推荐

