如何通过CameraX实现MLKit图像标注后仅传狗图像至Flutter?
CameraX串联MLKit图像标注与Flutter Event Channel实现方案
需求背景
需要实现CameraX下的串联逻辑:
- 第一步:用MLKit Image Labeling分析摄像头画面
- 第二步:仅当检测到狗时,通过Event Channel将图像数据发送至Flutter做后续分析
现有两段独立运行的分析代码,但无法整合串联逻辑,以下是可行的解决思路与代码实现:
核心思路
将MLKit标注检测与图像转码发送逻辑整合到同一个MlKitAnalyzer回调中,避免创建两个独立的ImageAnalysis实例。在MLKit返回标注结果后,先判断是否存在狗的标签,再执行图像格式转换与Flutter发送操作。
完整代码实现
1. 初始化依赖组件
// 初始化MLKit图像标注器(置信度阈值设为0.7) val labelerOptions = ImageLabelerOptions.Builder() .setConfidenceThreshold(0.7f) .build() val imageLabeler = ImageLabeling.getClient(labelerOptions) // 创建单线程执行器,用于处理图像分析任务 val analyzerExecutor = Executors.newSingleThreadExecutor()
2. 创建整合逻辑的MlKitAnalyzer
val combinedAnalyzer = MlKitAnalyzer( listOf(imageLabeler), ImageAnalysis.COORDINATE_SYSTEM_ORIGINAL, analyzerExecutor ) { resultBundle -> // 获取MLKit的图像标注结果 val labels = resultBundle.getValue(imageLabeler) val imageProxy = resultBundle.imageProxy try { // 无标注结果或未检测到狗时,直接释放图像资源返回 if (labels.isNullOrEmpty() || !labels.any { it.text.equals("Dog", ignoreCase = true) || it.text.equals("狗", ignoreCase = true) }) { return@MlKitAnalyzer } // 检测到狗,将YUV格式图像转成RGBA_8888并提取字节数组 convertYuvToRgba(imageProxy)?.let { rgbaByteArray -> // 通过Event Channel的sink发送数据到Flutter sink?.success(rgbaByteArray) } } finally { // 必须释放ImageProxy资源,避免内存泄漏 imageProxy.close() } }
3. YUV转RGBA工具函数
/** * 将YUV_420_888格式的ImageProxy转换为RGBA_8888字节数组 */ private fun convertYuvToRgba(imageProxy: ImageProxy): ByteArray? { val yuvImage = imageProxy.image?.let { yuvImg -> YuvImage( yuvImg.planes[0].buffer.array(), ImageFormat.NV21, imageProxy.width, imageProxy.height, null ) } ?: return null val outputStream = ByteArrayOutputStream() yuvImage.compressToJpeg(Rect(0, 0, imageProxy.width, imageProxy.height), 100, outputStream) val bitmap = BitmapFactory.decodeByteArray(outputStream.toByteArray(), 0, outputStream.size()) // 将Bitmap转为RGBA字节数组 val byteArray = ByteArray(bitmap.width * bitmap.height * 4) bitmap.copyPixelsToBuffer(ByteBuffer.wrap(byteArray)) bitmap.recycle() return byteArray }
4. 配置并启动ImageAnalysis
val imageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(analyzerExecutor, combinedAnalyzer) // 将imageAnalysis绑定到CameraX生命周期(需确保已初始化CameraX) CameraX.bindToLifecycle(this, imageAnalysis)
关键注意事项
- 资源释放:所有
ImageProxy、Bitmap使用后必须及时释放,防止内存泄漏 - 标签匹配:MLKit的标签文本会受设备语言影响,建议同时匹配中英文标签(如"Dog"和"狗")
- 线程安全:Event Channel的
sink操作需确保在正确线程执行,若需主线程可包裹Handler(Looper.getMainLooper()).post { ... } - 性能优化:如果对延迟敏感,可直接发送YUV格式字节数组到Flutter,跳过格式转换步骤,由Flutter端处理YUV解码
内容的提问来源于stack exchange,提问作者Bo Hellgren
相关产品推荐
相关产品推荐

