Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to decrease frame rate of Android CameraX ImageAnalysis?

How to decrease the frame rate to 1 fps in image analysis so that I don't detect barcode multiple times. In my use-case, scanning the same barcode multiple times with a 1-second interval should increment a counter. So I want it to work correctly. (Similar to product scanner at shop tills)

cameraX version : 1.0.0-beta02

Similar questions :

  • How to increase frame rate with Android CameraX ImageAnalysis?
    In this, the answer is based on the preview config & analysis config Builders, which was in cameraX alpha versions. But it is not found in beta versions.

  • How to pause barcode scanning in android ML-kit when using ByteBuffer from SurfaceView
    In this, the image analysis is throttled, but I want the call to the image analyzer to be at a lower frame rate. i.e Call from image analysis usecase to image analyzer should be once in a second.

Current implementation :

https://proandroiddev.com/update-android-camerax-4a44c3e4cdcc
Following this doc, to throttle image analysis.

override fun analyze(image: ImageProxy) {
    val currentTimestamp = System.currentTimeMillis()
    if (currentTimestamp - lastAnalyzedTimestamp >= TimeUnit.SECONDS.toMillis(1)) {
        // Image analysis code
    }
    image.close()
}

A better solution would be helpful.

like image 938
Abhimanyu Avatar asked Nov 07 '22 08:11

Abhimanyu


2 Answers

Tried bmdelacruz's solution. Had issues with closing the image.
Was getting an error similar to this.
Couldn't get it working.

Using delay worked well for me.

Code

CoroutineScope(Dispatchers.IO).launch {
    delay(1000 - (System.currentTimeMillis() - currentTimestamp))
    imageProxy.close()
}

Complete BarcodeAnalyser code

class BarcodeAnalyser(
    private val onBarcodesDetected: (barcodes: List<Barcode>) -> Unit,
) : ImageAnalysis.Analyzer {
    private val barcodeScannerOptions = BarcodeScannerOptions.Builder()
        .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
        .build()
    private val barcodeScanner = BarcodeScanning.getClient(barcodeScannerOptions)
    var currentTimestamp: Long = 0

    override fun analyze(
        imageProxy: ImageProxy,
    ) {
        currentTimestamp = System.currentTimeMillis()
        imageProxy.image?.let { imageToAnalyze ->
            val imageToProcess =
                InputImage.fromMediaImage(imageToAnalyze, imageProxy.imageInfo.rotationDegrees)
            barcodeScanner.process(imageToProcess)
                .addOnSuccessListener { barcodes ->
                    // Success handling
                }
                .addOnFailureListener { exception ->
                    // Failure handling
                }
                .addOnCompleteListener {
                    CoroutineScope(Dispatchers.IO).launch {
                        delay(1000 - (System.currentTimeMillis() - currentTimestamp))
                        imageProxy.close()
                    }
                }
        }
    }
}
like image 187
Abhimanyu Avatar answered Nov 14 '22 22:11

Abhimanyu


You can take advantage of the fact that the next analysis will not begin until you close the provided ImageProxy.

In my case, I just put the thread to sleep since my analyzer's executor is a single thread executor.

class MyAnalyzer : ImageAnalysis.Analyzer {
    override fun analyze(image: ImageProxy) {
        val elapsedAnalysisTime = measureTimeMillis {
            // do your stuff here
        }
        image.use {
            if (elapsedAnalysisTime < 1000) {
                Thread.sleep(1000 - elapsedAnalysisTime)
            }
        }
    }
}
like image 27
bmdelacruz Avatar answered Nov 14 '22 21:11

bmdelacruz