Agung
Agung

Reputation: 13803

how to automatically rotate the image after capturing image using Camera X library?

I am new in using camera X library. I am trying to follow the codelab from Google in here

I can show the preview and capture the image. but the problem is ......

when I capture the image in landscape, the image result will be still in portrait. I want to make it automatically rotate. if I take the image in landscape then the result should be in landscape, and if I take it in portrait then the result should be in portrait. like the camera in 'Camera' app

how to do that ?

I am using Redmi Note 7, Android 10.

the gradle I use:

implementation "androidx.camera:camera-camera2:1.0.0-beta11"
implementation "androidx.camera:camera-lifecycle:1.0.0-beta11"
implementation "androidx.camera:camera-view:1.0.0-alpha18"

here is my code to show the preview and to capture the image

class CameraFragment : Fragment() {

    private var imageCapture: ImageCapture? = null
    private lateinit var outputDirectory: File
    private lateinit var cameraExecutor: ExecutorService
    private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

    companion object {
        private const val TAG = "CameraFragment"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val CAMERA_REQUEST_CODE_PERMISSIONS = 10
    }

    lateinit var mContext : Context
    lateinit var mActivity : FragmentActivity

   

    override fun onAttach(context: Context) {
        super.onAttach(context)

        mContext = context
        activity?.let { mActivity = it }

    }


    private fun getOutputDirectory(): File {
        val mediaDir = mActivity.externalMediaDirs.firstOrNull()?.let {
            File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
        }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else mActivity.filesDir
    }


    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(mContext)

        cameraProviderFuture.addListener(Runnable {
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview
            val preview = Preview.Builder()
                    .build()
                    .also {
                        it.setSurfaceProvider(previewView.surfaceProvider)
                    }


            imageCapture = ImageCapture.Builder().build()


            try {
                // Unbind use cases before rebinding
                cameraProvider.unbindAll()

                // Bind use cases to camera
                cameraProvider.bindToLifecycle(
                        this, cameraSelector, preview, imageCapture)

            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }

        }, ContextCompat.getMainExecutor(mContext))
    }


    private fun takePhoto() {

        // Get a stable reference of the modifiable image capture use case
        val imageCapture = imageCapture ?: return


        // Create time-stamped output file to hold the image
        val photoFile = File(
                outputDirectory,
                SimpleDateFormat(FILENAME_FORMAT, Locale.US
                ).format(System.currentTimeMillis()) + ".jpg")

        // Create output options object which contains file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()


        // Set up image capture listener, which is triggered after photo has
        // been taken
        imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(mContext), object : ImageCapture.OnImageSavedCallback {

            override fun onError(exc: ImageCaptureException) {
                Log.d("agungxxx", "2: ${exc.localizedMessage}")
                Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
            }

            override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                cameraSharedViewModel.sendImageUriPathToPreviousDestination(photoFile)
                findNavController().navigateUp()
            }
        })

    }

}

Upvotes: 1

Views: 1390

Answers (1)

Husayn Hakeem
Husayn Hakeem

Reputation: 4570

The rotation of the captured image depends on the target rotation of the ImageCapture use case. By default, when it isn't set by the application, it is equal to Display.getRotation(), where Display is the default display at the time the ImageCapture use case is created.

This means that you need to updated the ImageCapture's target rotation every time the display's orientation changes, e.g. when the device is physically rotated from portrait to landscape.

I'm assuming your activity has a locked orientation (?). In this case, you can use an OrientationEventListener to continuously get updates on the device's rotation, and then update the use case's target rotation accordingly.

val orientationEventListener = object : OrientationEventListener(this) {
    override fun onOrientationChanged(orientation: Int) {
        if (orientation == OrientationEventListener.UNKNOWN_ORIENTATION) {
            return
        }

        val rotation = when (orientation) {
            in 45 until 135 -> Surface.ROTATION_270
            in 135 until 225 -> Surface.ROTATION_180
            in 225 until 315 -> Surface.ROTATION_90
            else -> Surface.ROTATION_0
        }

        imageCapture.targetRotation = rotation
    }
}

You should start/stop the orientationEventListener when the Activity's lifecycle is started/stopped, this also matches when the camera's started/stopped. You can see an example of this here.

You can also learn more about CameraX's use cases and rotation in the official documentation.

Upvotes: 2

Related Questions