Skip to content

CameraX Image Analysis — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about CameraX Image Analysis. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your CameraX image analyzer lags behind the camera feed, or onAnalyze is called faster than you can Process frames.

Wrong Approach ❌

// No cooldown — analyzer called for every frame
class BarcodeAnalyzer : ImageAnalysis.Analyzer {
    override fun analyze(image: ImageProxy) {
        // Heavy ML processing on every frame — can't keep up!
        val barcodes = detectBarcodes(image)
        image.close()
    }
}

Output: Frames queued up, memory grows, analysis runs on stale frames.

Right Approach ✅

class EfficientAnalyzer : ImageAnalysis.Analyzer {
    private var isProcessing = false
    private val fpsController = FpsController()

    override fun analyze(image: ImageProxy) {
        // Skip frames if still processing
        if (isProcessing) {
            image.close()
            return
        }

        isProcessing = true

        // Convert to Bitmap for processing
        val bitmap = imageProxyToBitmap(image)

        // Process on background thread
        Thread {
            try {
                val result = processFrame(bitmap)

                // Post result back to UI
                Handler(Looper.getMainLooper()).post {
                    onResult(result)
                }
            } finally {
                image.close() // Always close!
                isProcessing = false
            }
        }.start()
    }

    // Alternative: use executor for background processing
    fun setupImageAnalysis(imageAnalysis: ImageAnalysis) {
        imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor()) { image ->
            processOnBackground(image)
            image.close()
        }
    }

    // Set target frame rate
    fun setupWithBackpressure(imageAnalysis: ImageAnalysis) {
        // STRATEGY_KEEP_ONLY_LATEST: drops frames if analyzer is busy
        ImageAnalysis.Builder()
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .setTargetFrameRate(15) // Process 15 fps max
            .build()
    }

    private var lastAnalyzedTime = 0L
    private fun shouldAnalyze(): Boolean {
        val now = System.currentTimeMillis()
        return if (now - lastAnalyzedTime > 100) { // 10 fps max
            lastAnalyzedTime = now
            true
        } else false
    }

    private class FpsController {
        private val frameWindow = ArrayDeque<Long>(30)

        fun canProcess(currentFps: Int = 15): Boolean {
            val now = System.currentTimeMillis()
            frameWindow.addLast(now)
            if (frameWindow.size > 30) frameWindow.removeFirst()
            val elapsed = now - (frameWindow.firstOrNull() ?: now)
            return elapsed > 0 && frameWindow.size * 1000 / elapsed < currentFps
        }
    }
}

Output: Frame analysis at sustainable rate without memory growth.

Prevention

  • Use <a href="/design-patterns/strategy/">Strategy</a>_KEEP_ONLY_LATEST for backpressure.
  • Run analysis on a background executor, not the main thread.
  • Always close ImageProxy in a finally block.
  • Implement frame skipping for heavy processing.
  • Set a reasonable target frame rate.

Common Mistakes with camera x analyze

  1. Misunderstanding that String is [Char] with poor performance for large text operations
  2. Using foldl instead of foldl' causing stack overflow on large lists
  3. Forgetting deriving (Show, Eq) on custom data types needed for debugging

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What is backpressure Strategy?

<a href="/design-patterns/strategy/">Strategy</a>_KEEP_ONLY_LATEST drops frames if the analyzer is busy. STRATEGY_BLOCK_PRODUCER blocks the camera until processing completes (can cause jank).

### Why must I close ImageProxy?

Each frame holds a reference to a native buffer. Not closing it leaks memory rapidly — the camera will stop producing frames after ~10 unclosed frames.

### Can I use CameraX Analyzer with ML Kit?

Yes. ML Kit accepts InputImage.fromMediaImage(image.image, image.imageInfo.rotationDegrees). Pass the ImageProxy directly — ML Kit handles the conversion.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro