Why FirebaseVisionImage.fromMediaImage() produces OutOfMemoryError
Asked Answered
A

1

7

CameraX is build, analyze() method is called and an image is passed and then closed (deleted) with close() method. From this image FirebaseVisionImage is created and passed for processing (text recognition). Code samples and code labs differs and not implement TextRecognition with CameraX or using old API versions.

Stack trace

  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()
        }
}

In my project I'm using this dependencies

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}"

and this is how I build 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))
}
Aurify answered 22/4, 2020 at 11:17 Comment(0)
N
0

I was able to resolve the issue by switching to mlkit.

First update the app/build.gradle file to use mlkit instead of firebase:

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

Next update the analyzer to use 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()
    }

Then update runTextRecognition to:

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

        ...
}

That should do it.

Here is the codelab that gives more details.

Necaise answered 11/7, 2020 at 19:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.