为啥 FirebaseVisionImage.fromMediaImage() 会产生 OutOfMemoryError

Posted

技术标签:

【中文标题】为啥 FirebaseVisionImage.fromMediaImage() 会产生 OutOfMemoryError【英文标题】:Why FirebaseVisionImage.fromMediaImage() produces OutOfMemoryError为什么 FirebaseVisionImage.fromMediaImage() 会产生 OutOfMemoryError 【发布时间】:2020-08-05 09:29:33 【问题描述】:

构建CameraX,调用analyze()方法并传递图像,然后使用close()方法关闭(删除)。从此图像创建并传递 FirebaseVisionImage 以进行处理(文本识别)。代码示例和代码实验室不同,不使用 CameraX 或旧 API 版本实现 TextRecognition。

  override fun analyze(imageProxy: ImageProxy) 
    if (isValidText) 
        imageProxy.close()
        return
    
    val mediaImage = imageProxy.image // requires annotation
    val degrees = imageProxy.imageInfo.rotationDegrees
    val rotation = rotationDegreesToFirebaseRotation(degrees)
    if (mediaImage != null) 
        runTextRecognition(mediaImage, rotation)  // line 44
    
    imageProxy.close()
 


private fun runTextRecognition(mediaImage: Image, rotation: Int) 
    // Create FirebaseVisionImage from frame
    val visionImage = FirebaseVisionImage.fromMediaImage(mediaImage, rotation) // line 64
    val recognizer = FirebaseVision.getInstance()
        .onDeviceTextRecognizer
    recognizer.processImage(visionImage)
        .addOnSuccessListener  texts ->
            processTextRecognitionResult(texts!!, recognizer)
            if (isValidText) 
                recognizer.close()
                return@addOnSuccessListener
            
        
        .addOnFailureListener  e -> // Task failed with an exception
            e.printStackTrace()
        

在我的项目中,我正在使用这个依赖项

def firebase_version = '24.0.2'
def camerax_version = '1.0.0-beta02'
implementation "com.google.firebase:firebase-ml-vision:$firebase_version"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha09"
implementation "androidx.camera:camera-lifecycle:$camerax_version"

这就是我构建 CameraX 的方式

 private fun bindPreview(cameraProvider: ProcessCameraProvider) 
    // Get screen metrics used to setup camera for full screen resolution
    val metrics = DisplayMetrics().also  viewFinder?.display?.getRealMetrics(it) 
    val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
    val rotation = viewFinder?.display?.rotation
    // Set up the preview use case to display camera preview
    val preview = Preview.Builder()// Request aspect ratio but no resolution
        .setTargetAspectRatio(screenAspectRatio)
        // Set initial target rotation
        .setTargetRotation(rotation!!)
        .build()

    // Choose the camera by requiring a lens facing
    val cameraSelector = CameraSelector.Builder()
        .requireLensFacing(CameraSelector.LENS_FACING_BACK)
        .build()

    val executor = Executors.newSingleThreadExecutor()

    // Must unbind the use-cases before rebinding them
    cameraProvider.unbindAll()


    val imageAnalyzer = ImageAnalysis.Builder()
        // Request aspect ratio but no resolution
        .setTargetAspectRatio(screenAspectRatio)
        // Set initial target rotation, have to call this again if rotation changes
        // during the lifecycle of this use case
        .setTargetRotation(rotation)
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .build()
    imageAnalyzer.setAnalyzer(executor, analyzer)

    var camera = cameraProvider.bindToLifecycle(viewFinder?.context as LifecycleOwner, cameraSelector, preview, imageAnalyzer)
    // Attach the viewfinder's surface provider to preview use case
    preview.setSurfaceProvider(viewFinder?.createSurfaceProvider(camera.cameraInfo))

【问题讨论】:

【参考方案1】:

我能够通过切换到 mlkit 来解决问题。

首先更新 app/build.gradle 文件以使用 mlkit 而不是 firebase:

 // Add ML Kit dependencies
 implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.1.0'

接下来更新分析器以使用 InputImage:

@androidx.camera.core.ExperimentalGetImage
private class TextAnalyzer(private val listener: TextListener) : ImageAnalysis.Analyzer 

    override fun analyze(imageProxy: ImageProxy) 
        val mediaImage: Image = imageProxy.image ?: return
        val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

        runTextRecognition(image)

        imageProxy.close()
    

然后将runTextRecognition 更新为:

private fun runTextRecognition(image: InputImage) 
    val recognizer = TextRecognition.getClient()
    recognizer.process(image)

        ...

应该可以的。

Here 是提供更多细节的代码实验室。

【讨论】:

以上是关于为啥 FirebaseVisionImage.fromMediaImage() 会产生 OutOfMemoryError的主要内容,如果未能解决你的问题,请参考以下文章

为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?

为啥需要softmax函数?为啥不简单归一化?

为啥 g++ 需要 libstdc++.a?为啥不是默认值?

为啥或为啥不在 C++ 中使用 memset? [关闭]

为啥临时变量需要更改数组元素以及为啥需要在最后取消设置?

为啥 CAP 定理中的 RDBMS 分区不能容忍,为啥它可用?