【问题标题】:Firebase ML Kit: Label Detector causing rapid increase in memory consumptionFirebase ML Kit:标签检测器导致内存消耗快速增加
【发布时间】:2025-11-27 00:00:01
【问题描述】:

更新 2:

我用来缩放位图的方法。

private fun Bitmap.getResizedBitmap(maxWidth: Int, minWidth: Int, maxHeight: Int, minHeight: Int): Bitmap =
    when {
        width <= minWidth || height <= minHeight -> this
        maxHeight > 0 && maxWidth > 0 -> {
                val bitmapWidth = width
                val bitmapHeight = height
                val ratioBitmap = bitmapWidth.toFloat() / bitmapHeight.toFloat()
                val ratioMax = maxWidth.toFloat() / maxHeight.toFloat()

                var finalWidth = maxWidth.toFloat()
                var finalHeight = maxHeight.toFloat()
                if (ratioMax > ratioBitmap) finalWidth = maxHeight.toFloat() * ratioBitmap
                else finalHeight = maxWidth.toFloat() / ratioBitmap
                Bitmap.createScaledBitmap(this, finalWidth.toInt(), finalHeight.toInt(), true)
            }
            else -> this
        }

更新 1:

override fun detectInPhoto(context: Context, uri: Uri, onLabelled: (List<Label>) -> Unit) {
    try {
        labelDetector.detectInImage(FirebaseVisionImage.fromFilePath(context, uri))
        }catch (e: Exception){
            Log.d("LabelDetector", "${e.message}")
        }

堆栈跟踪:

07-24 12:59:25.566 23076-23164/com.sev7en.curator E/AndroidRuntime: FATAL EXCEPTION: ForkJoinPool.commonPool-worker-3
Process: com.sev7en.curator, PID: 23076
java.lang.OutOfMemoryError: Failed to allocate a 24140812 byte allocation with 4185968 free bytes and 18MB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.BitmapFactory.nativeDecodeByteArray(Native Method)
    at android.graphics.BitmapFactory.decodeByteArray(BitmapFactory.java:564)
    at android.graphics.BitmapFactory.decodeByteArray(BitmapFactory.java:587)
    at com.google.firebase.ml.vision.common.FirebaseVisionImage.getBitmapForDebugging(Unknown Source)
    at com.google.firebase.ml.vision.label.FirebaseVisionLabelDetector.detectInImage(Unknown Source)
    at com.sev7en.curator.ai.image.FirebasePhotoLabelDetector.detectInPhoto(FirebasePhotoLabelDetector.kt:52)
    at com.sev7en.curator.util.impl.PhotoLabelManagerImpl$onUnlabelledPhotosReceived$1$2.doResume(PhotoLabelManagerImpl.kt:119)
    at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
    at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41)
    at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:161)
    at kotlinx.coroutines.experimental.AbstractContinuation.run(AbstractContinuation.kt:31)
    at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1388)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:251)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:845)
    at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1674)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1629)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:108)

错误在这里:com.sev7en.curator.ai.image.FirebasePhotoLabelDetector.detectInPhoto(FirebasePhotoLabelDetector.kt:52)

如果我将其注释掉,则没有问题。我提供的位图也有同样的问题。

原帖

我正在使用以下方法来获取 FirebaseVisionLabelDetector:

fun getLabelDetector(options: FirebaseVisionLabelDetectorOptions?): FirebaseVisionLabelDetector =
    FirebaseVision.getInstance()
        .getVisionLabelDetector(options ?: getLabelDetectorOptions(0.7f))

像这样使用它:

fun getLabels(bitmap : Bitmap){
getLabelDetector.detectInImage(FirebaseVisionImage.fromBitmap(bitmap))
}

但是通过查看 Android Profiler 中的内存,我注意到为提供的位图分配了大量内存,并且它们的引用没有被删除,因此它们没有被垃圾收集。我自己没有对 Bitmap 的引用。每次方法调用时,总内存使用量都在不断增加。

我忽略了函数的结果,因为我想先提高它的性能。

【问题讨论】:

  • 感谢您报告此问题。我们的工程师正在研究这个。很快就会回复您!
  • 好的@PannagSanketi,谢谢!
  • 有什么@PannagSanketi?
  • @ChrisRohitBrendan,我们无法重现您所描述的内容。我反复尝试使用 37MB 图像进行检测,效果很好。你能1)粘贴你的代码你是怎么得到你的位图的? 2)如果图像来自文件,你可以试试 FirebaseVisionImage.fromFilePath 吗? 3)如果您正在运行包含一堆图像的循环,您能否注释掉您对 ML Kit 的调用并查看对内存的影响?让我知道上述1)-3)的结果,我可以进一步看看。谢谢。
  • @IsabellaChen 请查看带有堆栈跟踪的更新帖子。

标签: android firebase firebase-mlkit


【解决方案1】:

位图确实会消耗大量内存。 ML Kit SDK 在启动检测时开始持有对图像的引用,并在检测完成后释放引用。如果您同时启动许多检测,则可能会耗尽内存。

在 SDK 内部,鉴于所有检测都是计算密集型的,所有异步调用都是按顺序执行的。因此,在第一次检测完成后开始第二次检测实际上不会减慢速度。这样做会更节省内存。

ML Kit 为其异步 API 返回一个 Task,您可以轻松(和递归地)链接操作,请参阅任务文档: https://developers.google.com/android/guides/tasks#chaining

或者干脆 addOnCompletetionListener 并调用辅助函数重新开始检测。

【讨论】:

  • 正如您所建议的那样,我已经做到了,以便在提供另一张图像之前等待结果,并且不再获得 OOM。在发送位图之前,我也一直在按比例缩小它们,我得到了不同的结果,如果按比例缩小它们,性能也会受到影响。请让 API 压缩和缩放位图本身,或者告诉我如何在输入之前压缩位图。
  • @ChrisRohitBrendan 您将位图缩放到哪种分辨率?我们不能建议一个绝对值,因为模型可能会在此过程中得到改进。目前,缩放到 > 224 * 224 的分辨率不应影响推理质量。
  • 缩小到 224 * 224,因为大多数其他模型都使用这个标准,所以会遗漏一些结果并得到很多标签错误。如果我改用 .fromFilePath,我会得到更好的结果,或者将位图缩放到大约 448 或 896。我在帖子中包含了我用来缩放的方法。