Search code examples
androidandroid-camerax

How to draw rectangle at CameraX TextureView


I wrote the below code to draw a red rectangle, but apparently it is wrong :( I did not get any error, in same time did not get anything drawn, how can I fix it

    private fun updateTransform() {
        val matrix = Matrix()

        // Compute the center of the view finder
        val centerX = viewFinder.width / 2f
        val centerY = viewFinder.height / 2f

        // Correct preview output to account for display rotation
        val rotationDegrees = when(viewFinder.display.rotation) {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> return
        }
        matrix.postRotate(-rotationDegrees.toFloat(), centerX, centerY)

        val bmp = Bitmap.createBitmap(1425, 1425, Bitmap.Config.ARGB_8888)
        val paint = Paint().apply {
            isAntiAlias = true
            style = Paint.Style.STROKE
            color = Color.RED
            strokeWidth = 10f
        }
        // x: {559, y: 1901}, height: 1425, width: 1425
        var canvas = Canvas(bmp)
        canvas.apply {
            drawRect(
                25.toFloat(), // faceRectangle.left,
                25.toFloat(), //faceRectangle.top,
                250.toFloat(),
                250.toFloat(),
                paint
            )
        }

        viewFinder.unlockCanvasAndPost(canvas)

        viewFinder.setTransform(matrix)

    }

Solution

  • I found a work around to do this, not sure if this is the only way, anyhow, I'll post my way here in case it is helpful for someone in the future:

    • The solution is to create an image view, with the same dimension, and location (constrains) of the cameeraX texture view, toact as overlay, and do all the drawings on it, you just need to be sure that this over lay is above not under the texture view, i.e. following it in the layout xml file.
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextureView
            android:id="@+id/view_finder"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            android:visibility="visible" />
    
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@string/pic_desc"
            android:visibility="visible" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    And the drawing code is:

    lateinit var overlay: Bitmap
    private fun startCamera() {
            val analyzerConfig = ImageAnalysisConfig.Builder().apply {
                setImageReaderMode(
                    ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
            }.build()
    
            val analyzerUseCase = ImageAnalysis(analyzerConfig).apply {
                analyzer = ImageAnalysis.Analyzer { image, rotationDegrees ->
                    val bitmap = view_finder.bitmap ?: return@Analyzer
                    overlay = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
    
                    scope.launch(Dispatchers.Unconfined) {
                        val mat = Mat()
                        Utils.bitmapToMat(bitmap!!, mat)
    
                        val detectedFaces = FaceDetection.detectFaces(bitmap!!)
    
                       if (detectedFaces.toArray().isNotEmpty()) {
                           val paint = Paint().apply {
                               isAntiAlias = true
                               style = Paint.Style.STROKE
                               color = Color.RED
                               strokeWidth = 10f
                           }
    
                           for (rect in detectedFaces.toArray()) {
                               var canvas = Canvas(overlay)
                               canvas.drawRect(
                                   rect.x.toFloat(), 
                                   rect.y.toFloat(), 
                                   rect.x.toFloat() + rect.width,
                                   rect.y.toFloat() + rect.height,
                                   paint
                               )
                               overlay?.let { Canvas(it) }?.apply {
                                   canvas
                               }
                           }
                       }
                    }
    
                    runOnUiThread {
                        imageView.setImageBitmap(overlay)
                    }
                }
            }
       CameraX.bindToLifecycle(this, preview, imageCapture, analyzerUseCase)
    }
    

    Below is an output of my app as of now showing this drawing:

    enter image description here