Home:ALL Converter>Android CameraX image rotated

Android CameraX image rotated

Ask Time:2020-09-16T21:35:50         Author:Nouman Bhatti

Json Formatter

I have followed Google CameraX code lab to implement custom camera. Camera preview is fine but when i take image after image capture image is rotated. I am taking image in portrait mode but saved image is in landscape. Here is the method to configure camera

private fun startCamera() {

    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

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

        // Preview
        val preview = Preview.Builder()
            .setTargetRotation(this.windowManager.defaultDisplay.rotation)
            .build()
            .also {
                it.setSurfaceProvider(viewFinder.createSurfaceProvider())
            }

        imageCapture = ImageCapture.Builder()
            .setTargetRotation(this.windowManager.defaultDisplay.rotation)
            .build()

        val imageAnalyzer = ImageAnalysis.Builder()
            .build()
            .also {
                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
                    Log.d(TAG, "Average luminosity: $luma")
                })
            }

        // Select back camera as a default
        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

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

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

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

    }, ContextCompat.getMainExecutor(this))
}

Here is the method to capture image:

private fun takePhoto() {
    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(this), object : ImageCapture.OnImageSavedCallback {
            override fun onError(exc: ImageCaptureException) {
                Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
            }

            override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                val savedUri = Uri.fromFile(photoFile)
                val msg = "Photo capture succeeded: $savedUri"
                val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, savedUri)
                ivCapturedImage.setImageBitmap(bitmap)
                setCaptureUI(false)
                Log.d(TAG, msg)
            }
        })
}

Do i need to rotate the image by myself after it is taken using EXIF or i can fix it while configuring camera?

Author:Nouman Bhatti,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/63921260/android-camerax-image-rotated
furkanbzkurt :

I'm suffering from same situation. I solved this with hacky way.\nMy solution is:\n fun Bitmap.rotate(degrees: Float): Bitmap {\n val matrix = Matrix().apply { postRotate(degrees) }\n return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)\n }\n\nUsage :\nimageViewCapturedImage.setImageBitmap(bitmap?.rotate(90F))\n",
2020-09-17T17:21:49
Nouman Bhatti :

I have used this class to rotate image\nobject CaptureImageHelper {\n\n/**\n * This method is responsible for solving the rotation issue if exist. Also scale the images to\n * 1024x1024 resolution\n *\n * @param context The current context\n * @param selectedImage The Image URI\n * @return Bitmap image results\n * @throws IOException\n */\n@Throws(IOException::class)\nfun handleSamplingAndRotationBitmap(context: Context, selectedImage: Uri?): Bitmap? {\n val MAX_HEIGHT = 1024\n val MAX_WIDTH = 1024\n\n // First decode with inJustDecodeBounds=true to check dimensions\n val options = BitmapFactory.Options()\n options.inJustDecodeBounds = true\n var imageStream: InputStream = context.getContentResolver().openInputStream(selectedImage!!)!!\n BitmapFactory.decodeStream(imageStream, null, options)\n imageStream.close()\n\n // Calculate inSampleSize\n options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT)\n\n // Decode bitmap with inSampleSize set\n options.inJustDecodeBounds = false\n imageStream = context.getContentResolver().openInputStream(selectedImage!!)!!\n var img = BitmapFactory.decodeStream(imageStream, null, options)\n img = rotateImageIfRequired(img!!, selectedImage)\n return img\n}\n\n/**\n * Calculate an inSampleSize for use in a [BitmapFactory.Options] object when decoding\n * bitmaps using the decode* methods from [BitmapFactory]. This implementation calculates\n * the closest inSampleSize that will result in the final decoded bitmap having a width and\n * height equal to or larger than the requested width and height. This implementation does not\n * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but\n * results in a larger bitmap which isn't as useful for caching purposes.\n *\n * @param options An options object with out* params already populated (run through a decode*\n * method with inJustDecodeBounds==true\n * @param reqWidth The requested width of the resulting bitmap\n * @param reqHeight The requested height of the resulting bitmap\n * @return The value to be used for inSampleSize\n */\nprivate fun calculateInSampleSize(\n options: BitmapFactory.Options,\n reqWidth: Int, reqHeight: Int\n): Int {\n // Raw height and width of image\n val height = options.outHeight\n val width = options.outWidth\n var inSampleSize = 1\n if (height > reqHeight || width > reqWidth) {\n\n // Calculate ratios of height and width to requested height and width\n val heightRatio =\n Math.round(height.toFloat() / reqHeight.toFloat())\n val widthRatio =\n Math.round(width.toFloat() / reqWidth.toFloat())\n\n // Choose the smallest ratio as inSampleSize value, this will guarantee a final image\n // with both dimensions larger than or equal to the requested height and width.\n inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio\n\n // This offers some additional logic in case the image has a strange\n // aspect ratio. For example, a panorama may have a much larger\n // width than height. In these cases the total pixels might still\n // end up being too large to fit comfortably in memory, so we should\n // be more aggressive with sample down the image (=larger inSampleSize).\n val totalPixels = width * height.toFloat()\n\n // Anything more than 2x the requested pixels we'll sample down further\n val totalReqPixelsCap = reqWidth * reqHeight * 2.toFloat()\n while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {\n inSampleSize++\n }\n }\n return inSampleSize\n}\n\n/**\n * Rotate an image if required.\n *\n * @param img The image bitmap\n * @param selectedImage Image URI\n * @return The resulted Bitmap after manipulation\n */\n@Throws(IOException::class)\nprivate fun rotateImageIfRequired(img: Bitmap, selectedImage: Uri): Bitmap? {\n val ei = ExifInterface(selectedImage.path)\n val orientation: Int =\n ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)\n return when (orientation) {\n ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage(img, 90)\n ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage(img, 180)\n ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage(img, 270)\n else -> img\n }\n}\n\nprivate fun rotateImage(img: Bitmap, degree: Int): Bitmap? {\n val matrix = Matrix()\n matrix.postRotate(degree.toFloat())\n val rotatedImg =\n Bitmap.createBitmap(img, 0, 0, img.width, img.height, matrix, true)\n img.recycle()\n return rotatedImg\n}\n\n}\n",
2020-09-23T07:53:46
Ismail Osunlana :

The simplest solution that works for me.\nGet the rotationDegrees from imageProxy and rotate your bitmap by that degree.\nMatrix matrix = new Matrix(); \nmatrix.postRotate((float)imageProxy.getImageInfo().getRotationDegrees());\nBitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);\nbinding.imgPreview.setImageBitmap(bitmap2);\n",
2022-07-20T15:30:40
Cristian Davide Conte :

This simple code worked for me: \nJava Version:\nContext context = ... //The current Context\nCamera camera = cameraProvider.bindToLifecycle(...); //The one you get after initializing the camera\nImageProxy image = ... //The one that takePicture or Analyze give you\nint currentLensOrientation = ... //CameraSelector.LENS_FACING_BACK or CameraSelector.LENS_FACING_FRONT\n\nint rotationDirection = currentLensOrientation == CameraSelector.LENS_FACING_BACK ? 1 : -1;\nint constantRotation = image.getImageInfo().getRotationDegrees() - camera.getCameraInfo().getSensorRotationDegrees();\nint rotationDegrees = camera.getCameraInfo().getSensorRotationDegrees() - context.getDisplay().getRotation() * 90 + constantRotation * rotationDirection;\n\nKotlin Version:\nval context: Context = ... //The current Context\nval camera: Camera? = cameraProvider.bindToLifecycle(...) //The one you get after initializing the camera\nval image: ImageProxy = ... //The one that takePicture or Analyze give you\nval currentLensOrientation: Int = ... //CameraSelector.LENS_FACING_BACK or CameraSelector.LENS_FACING_FRONT\n\nval rotationDirection = if (currentLensOrientation == CameraSelector.LENS_FACING_BACK) 1 else -1\nval constantRotation = image.imageInfo.rotationDegrees - camera!!.cameraInfo.sensorRotationDegrees\nval rotationDegrees = camera!!.cameraInfo.sensorRotationDegrees - context.display!!.rotation * 90 + constantRotation * rotationDirection\n\nThen I used the rotationDegrees to rotate the ImageProxy that CameraX passes you in the takePicture and analyze's callbacks. \nHere you can find the full Java code if you need it: https://github.com/CristianDavideConte/SistemiDigitali/blob/7b40e50d8b2fbdf4e4a61edba7443da92b96c58d/app/src/main/java/com/example/sistemidigitali/views/CameraProviderView.java#L207",
2022-01-20T22:13:48
Xi 张熹 :

By default, ImageCapture set the orientation of the capture to the display rotation. If the image is saved to disk, the rotation will be in the EXIF.\nIs your device in locked portrait mode? In that case, display rotation does not match the device's orientation, and you will need to set the target rotation yourself. Example.\n// The value is whatever the display rotation should be, if the device orientation is not locked.\nimageCapture.setTargetRotation(...) \n\nOr, you could simply use the LifecycleCameraController API. It handles the rotation for you and make all the use cases consistent in a WYSIWYG way.",
2020-12-02T19:19:02
yy