6

I'm creating a tool to capture every frame from preview using cameraX (for face recognition purpose)

I found that using ImageAnalysis was the way to go.

Until I tried using the code as recommended :

val imageAnalysisConfig = ImageAnalysisConfig.Builder()
            .setTargetResolution(Size(1280, 720))
            .setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
            .build()
        val imageAnalysis = ImageAnalysis(imageAnalysisConfig)

        imageAnalysis.setAnalyzer({ image: ImageProxy, rotationDegrees: Int ->
            viewModel.onAnalyzeImage(image)
        })

To which I get the following compile error on setAnalyser method :

None of the following function can be called with the arguments supplied
setAnalyser((command : Runnable!) -> Unit,  (image: ImageProxy!, rotationDegrees: Int) -> Unit)

2 Answers2

15

I was also facing the same issue today, so , found that there is one missing parameter which is Executor which we need to pass otherwise same compilation error comes.

As as I worked with AsyncTasks in my past, I recognized that for doing tasks in multiple threads in AsyncTasks, we need to use its static method executeOnExecutor() which takes an Executor as its parameter, so I used the same parameter i.e. I used AsyncTask.THREAD_POOL_EXECUTOR as first parameter in setAnalyser() method. And it worked like a charm!! After putting this as first parameter you need to perform some minor changes in your previous code.

Like this

 imageAnalysis.setAnalyzer(AsyncTask.THREAD_POOL_EXECUTOR,
 object : ImageAnalysis.Analyzer {    // changes to be done in this line
                    override fun analyze(imageProxy: ImageProxy, rotationDegrees: Int) {
                        val image = FirebaseVisionImage.fromMediaImage(
                                imageProxy.image!!, getFirebaseRotation(rotationDegrees)
                        )
        
                        if (processingBarcode.get() ||
                                !lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
                            return
                        }
    ..................
    .............
    .......BLA BLA BLA
    }

Try it and tell me if this approach works with your use case.

EDIT

If you don't like AsyncTask, then I have found an alternative for getting an Executor instance without using AsyncTask.THREAD_POOL_EXECUTOR.

You can use Executors.newFixedThreadPool(n), to get an Executor instance. Here, n stands for the number of threads you want to create in the thread pool.It varies depending upon your use-case.

Tell me if it worked for you.

Community
  • 1
  • 1
Divya Gupta
  • 494
  • 8
  • 24
  • Thanks, but maybe AsyncTask not the best solution for this? – kostyabakay Oct 30 '19 at 13:53
  • Yeah, it might not be the best solution @Kostya, but I don't know any other way other than AsyncTask.THREAD_POOL_EXECUTOR which returns an Executor instance. I have also searched the web for any solution which provides an Executor instance, but did not succeed in finding it till now – Divya Gupta Oct 31 '19 at 14:23
  • 1
    I have updated the answer with a new alternative other than `AsyncTask`. Do check it out if you want @KostyaBakay – Divya Gupta Nov 05 '19 at 09:57
0

You can also find the implementation in the official CameraX sample app: CameraFragment.kt.

The part which you need is this:

// Executor field
private lateinit var analysisExecutor: Executor

// in onCreate()
analysisExecutor = Executors.newSingleThreadExecutor()

// after initializing imageAnalysis
imageAnalysis.setAnalyzer(analysisExecutor, ImageAnalysis.Analyzer {
    // TODO analyze
})

If you're wondering whether to use Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(n) or something else, take a look at the Executors documentation.

Sharp
  • 1,335
  • 1
  • 12
  • 27