如何在Android Kotlin中实现类似Snapchat、Instagram的实时人脸滤镜?
Android Kotlin 实时AR人脸滤镜实现指南
一、核心技术选型
不用从零造轮子,优先选择Android生态成熟工具组合:
- CameraX:Google官方相机库,简化相机预览、帧捕获的设备适配工作
- ML Kit 人脸检测:轻量级人脸关键点检测工具,可快速获取人脸轮廓、眼睛、鼻子等200+关键点坐标,是AR滤镜定位的核心
- OpenGL ES:负责实时帧的滤镜渲染(颜色滤镜、贴纸叠加),性能优于RenderScript更适合实时场景
- 可选:若需复杂3D AR效果,可使用ARCore,它内置人脸追踪与3D渲染能力
二、项目基础搭建
1. 权限配置
在AndroidManifest.xml中添加相机权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />
在Activity中动态申请相机权限:
private fun requestCameraPermission() { ActivityResultContracts.RequestPermission().launch(this, Manifest.permission.CAMERA) } // 初始化时检查权限 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestCameraPermission() } else { startCamera() } }
2. 集成CameraX预览
在build.gradle(Module)添加依赖:
dependencies { def camerax_version = "1.3.0" implementation "androidx.camera:camera-core:$camerax_version" implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-view:$camerax_version" }
布局中添加PreviewView用于相机预览:
<androidx.camera.view.PreviewView android:id="@+id/previewView" android:layout_width="match_parent" android:layout_height="match_parent" />
初始化CameraX并启动预览:
private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() val preview = Preview.Builder().build().also { it.setSurfaceProvider(previewView.surfaceProvider) } val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA try { cameraProvider.unbindAll() cameraProvider.bindToLifecycle(this, cameraSelector, preview) } catch(exc: Exception) { // 处理相机绑定异常 } }, ContextCompat.getMainExecutor(this)) }
三、人脸检测集成
1. 添加ML Kit依赖
dependencies { implementation "com.google.mlkit:face-detection:16.1.5" }
2. 实现人脸检测分析器
创建ImageAnalysis.Analyzer处理相机帧,提取人脸关键点:
class FaceDetectionAnalyzer(private val onFacesDetected: (List<Face>) -> Unit) : ImageAnalysis.Analyzer { private val detectorOptions = FaceDetectorOptions.Builder() .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST) .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL) .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL) .build() private val faceDetector = FaceDetection.getClient(detectorOptions) @SuppressLint("UnsafeOptInUsageError") override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image ?: run { imageProxy.close() return } val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) faceDetector.process(inputImage) .addOnSuccessListener { faces -> onFacesDetected(faces) imageProxy.close() } .addOnFailureListener { imageProxy.close() } } }
将分析器绑定到CameraX:
val imageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() .also { it.setAnalyzer(ContextCompat.getMainExecutor(this), FaceDetectionAnalyzer { faces -> // 实时更新人脸关键点数据 updateFaceLandmarks(faces) }) } // 绑定到相机生命周期 cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
四、实时滤镜渲染
1. 基础颜色滤镜(以灰度为例)
用OpenGL ES编写片段着色器实现颜色转换:
// 灰度滤镜片段着色器 precision mediump float; varying vec2 v_TexCoord; uniform sampler2D u_Texture; void main() { vec4 color = texture2D(u_Texture, v_TexCoord); float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); gl_FragColor = vec4(gray, gray, gray, color.a); }
自定义GLSurfaceView或TextureView,将CameraX帧纹理传入OpenGL渲染器,应用着色器效果。
2. AR贴纸滤镜实现
根据ML Kit检测到的人脸关键点,计算贴纸的位置与缩放比例:
// 示例:基于左眼关键点定位眼镜贴纸 fun attachGlassesSticker(face: Face) { val leftEye = face.landmarks[FaceLandmark.LEFT_EYE] ?: return val leftEyePos = leftEye.position // 根据人脸宽度计算贴纸缩放比例 val faceWidth = face.boundingBox.width() val stickerScale = faceWidth / glassesBitmap.width.toFloat() // 将计算后的参数传入OpenGL,在对应位置绘制贴纸 }
通过OpenGL纹理混合,将贴纸叠加到相机预览帧上。
五、滤镜切换UI实现
用RecyclerView实现底部滤镜选择栏:
// 滤镜数据类 data class Filter(val name: String, val thumbnailRes: Int, val shaderId: Int) // 滤镜选择适配器 class FilterAdapter(private val filters: List<Filter>, private val onFilterSelected: (Filter) -> Unit) : RecyclerView.Adapter<FilterAdapter.ViewHolder>() { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val thumbnail: ImageView = itemView.findViewById(R.id.filter_thumbnail) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_filter, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val filter = filters[position] holder.thumbnail.setImageResource(filter.thumbnailRes) holder.itemView.setOnClickListener { onFilterSelected(filter) } } override fun getItemCount() = filters.size }
点击滤镜Item时,切换OpenGL渲染器使用的着色器或贴纸资源。
关键优化点
- 降低人脸检测频率:比如每3帧检测一次,减少CPU占用
- 开启硬件加速:确保CameraX与OpenGL均启用硬件加速
- 适配相机分辨率:选择与屏幕匹配的预览分辨率,减少缩放损耗
内容的提问来源于stack exchange,提问作者Dev Dhande
相关产品推荐
相关产品推荐

