You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:06:04