Search code examples
androidkotlingpuimageandroid-camerax

Android GPUImage setImage and getBitmapWithFilterApplied cause screen to flicker


I have been using this github as a start of my code: https://github.com/xizhang/camerax-gpuimage

The code is a way to show the camera view with GPUImage filters on it. I want to be able to also analyze the bitmap with the filter applied on it to get some analytics (percent red/green/blue in the image).

I have been successful in showing the default camera view to the user as well as the filter I created.

By commenting out the setImage line of code, I have been able to get the analytics of the filtered image, but when I try to do both at the same time the screen flickers. I changed the StartCameraIfReady function to get the filtered image as follows:

    @SuppressLint("UnsafeExperimentalUsageError")
    private fun startCameraIfReady() {
        if (!isPermissionsGranted() || cameraProvider == null) {
            return;
        }
        val imageAnalysis = ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build()
        imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer {
            var bitmap = allocateBitmapIfNecessary(it.width, it.height)
            converter.yuvToRgb(it.image!!, bitmap)
            it.close()
            gpuImageView.post {
                // These two lines conflict causing the screen to flicker
                // I can comment out one or the other and it works great
                // But running both at the same time causes issues
                gpuImageView.gpuImage.setImage(bitmap)
                val filtered = gpuImageView.gpuImage.getBitmapWithFilterApplied(bitmap)

                /*
                Analyze the filtered image...
                Print details about image here
                 */
            }
        })
        cameraProvider!!.unbindAll()
        cameraProvider!!.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, imageAnalysis)
    }

When I try to get the filtered bitmap, it seems to conflict with the setImage line of code and cause the screen to flicker as shown in the video below. I can either show the preview to the user, or I can analyze the image, but not both at the same time. I have tried running them synchronized as well as each on their own background thread. I have also tried adding another Image Analyzer and binding it to the camera lifecycle (one for the preview and the other to get the filtered bitmap), the screen still flickers but less often.

https://imgur.com/a/mXeuEhe

<blockquote class="imgur-embed-pub" lang="en" data-id="a/mXeuEhe"  ><a href="//imgur.com/a/mXeuEhe">screenRocord</a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>


Solution

  • If you are going to get the Bitmap with filter applied, you don't even need the GPUImageView. You can just get the Bitmap, and then set it on a regular ImageView. This is how your analyzer should look like:

    ImageAnalysis.Analyzer {
       var bitmap = allocateBitmapIfNecessary(it.width, it.height)
       converter.yuvToRgb(it.image!!, bitmap)
       it.close()
       val filteredBitmap = gpuImage.getBitmapWithFilterApplied(bitmap)
       regularImageView.post {
          regularImageView.setImageBitmap(filteredBitmap)
       }
    }
    

    Please be aware that the original GitHub sample is inefficient, and the sample above is even worse, because they convert the output to a Bitmap before feeding it back to GPU. For the best performance to argument the preview stream, please see CameraX's core test app on how to access preview Surface via OpenGL.