Unable to properly crop image from CameraX in Android

1k Views Asked by At

I am trying to capture image within the rectangle as shown in image, using CameraX. enter image description here

The way I am going on about this is, by having the ratio of Screen to Caputred image calculated and then mapping the rectangle visible in camera to another rectangle(equivalent to the captured image) and then cropping the bitmap. The code is as follows.

fragment_scan_doc.xml

<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:id="@+id/clRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/gdl_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="28dp" />


    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin_25dp"
        android:text="@string/app_name"
        android:textColor="@color/black"
        android:textSize="@dimen/text_size_24sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="@id/gdl_right"
        app:layout_constraintStart_toStartOf="@id/gdl_left"
        app:layout_constraintTop_toTopOf="parent" />


    <TextView
        android:id="@+id/tvDesc"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin_16dp"
        android:text="@string/place_doc"
        android:textColor="@color/black"
        app:layout_constraintEnd_toEndOf="@+id/gdl_right"
        app:layout_constraintStart_toStartOf="@+id/gdl_left"
        app:layout_constraintTop_toBottomOf="@+id/tvTitle" />

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="@dimen/dimen_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvDesc" />

    <TextView
        android:id="@+id/tvCamera"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="13dp"
        android:background="@drawable/bg_round_white_solid_yellow_stripe"
        android:gravity="center"
        android:paddingStart="@dimen/dimen_25dp"
        android:paddingTop="@dimen/dimen_10dp"
        android:paddingEnd="@dimen/dimen_25dp"
        android:paddingBottom="@dimen/dimen_10dp"
        android:text="Front"
        android:textColor="@color/black"
        app:layout_constraintEnd_toEndOf="@id/gdl_right"
        app:layout_constraintStart_toStartOf="@id/gdl_left"
        app:layout_constraintTop_toTopOf="@+id/previewView" />

    <View
        android:id="@+id/border_view"
        android:layout_width="325dp"
        android:layout_height="202.5010436dp"
        android:background="@drawable/bg_round_grey_stripe"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@id/gdl_right"
        app:layout_constraintStart_toStartOf="@id/gdl_left"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvPoweredBy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="Powered by"
        android:textColor="@color/white"
        app:layout_constraintEnd_toEndOf="@id/gdl_right"
        app:layout_constraintStart_toStartOf="@id/gdl_left"
        app:layout_constraintTop_toBottomOf="@+id/border_view" />

    <View
        android:id="@+id/vTakePhoto"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginBottom="20dp"
        android:background="@drawable/bg_round_white_solid_stripe"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
         />

    <ImageView
        android:id="@+id/ivFlash"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="40dp"
        android:background="@drawable/bg_circle_white_solid"
        android:padding="10dp"
        android:src="@drawable/ic_baseline_flash_off_24"
        app:layout_constraintBottom_toBottomOf="@+id/vTakePhoto"
        app:layout_constraintStart_toEndOf="@+id/vTakePhoto"
        app:layout_constraintTop_toTopOf="@+id/vTakePhoto" />

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:elevation="50dp"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/gdl_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_end="28dp" />


</androidx.constraintlayout.widget.ConstraintLayout>

This is the place where I get Image from camerax and I submit it to the cropImage() function

private fun takePhoto() {

        imageCapture?.takePicture(ContextCompat.getMainExecutor(requireContext()),
            object : ImageCapture.OnImageCapturedCallback() {
                @SuppressLint("UnsafeOptInUsageError")
                override fun onCaptureSuccess(imageProxy: ImageProxy) {

                    

                    val image = imageProxy.image?.let { image ->
                        InputImage.fromMediaImage(
                            image,
                            imageProxy.imageInfo.rotationDegrees
                        )
                    }
                    cropImage(imageProxy)
                }

                override fun onError(exception: ImageCaptureException) {
                    exception.printStackTrace()
                }
            })

}

This is the cropImage() function

private fun cropImage(imageProxy: ImageProxy) {
        val ori = imageProxy.convertImageProxyToBitmap().rotate(90f)


        val rect = Rect()

        mViewBinding.borderView.getGlobalVisibleRect(rect)

        val screenWidth = mViewBinding.root.width
        val screenHeight = mViewBinding.root.height

        val ratioW = screenWidth.toFloat() / ori.width // I am trying to get the ratio of screen to image width
        val ratioH = screenHeight.toFloat() / ori.height

        val x1 = rect.left / ratioW
        val y1 = rect.top / ratioH
        val x1W = rect.right / ratioW
        val x1H = rect.bottom / ratioH

        val rect1 = Rect(x1.toInt(), y1.toInt(), x1W.toInt(), x1H.toInt())

        val cropedBm = Bitmap.createBitmap(
            ori,
            rect1.left,
            rect1.top,
            rect1.right - rect1.left,
            rect1.bottom - rect1.top
        )
        processNewBitmap(cropedBm) // this is where I display the cropped bitmap and it appears like in the image below
    }

enter image description here

The image is cropped a bit below the rectangle frame.

I am not quite sure what I am doing wrong. Any help or direction would be highly appreciated. Thanks

1

There are 1 best solutions below

0
Tuan On

your ImageProxy has a bitmap you want to crop with a view overlay that does not same width or height. You must calculate with scale ratio.

You can refer to this link: https://www.codeproject.com/Articles/1276135/Crop-Image-from-Camera-on-Android