Incorrect orientation on 180 phone rotations when setOutputImageRotationEnabled(true) is enabled

70 views
Skip to first unread message

Javier Gerardo Martinez Salomon

unread,
Feb 16, 2026, 1:45:00 PMFeb 16
to Android CameraX Discussion Group
Hello CameraX team,

First of all, thanks for this amazing library, I'm currently using CameraX 1.4.0 and my primary use case is the analysis of images to select one given a set of conditions. 

Normally, I handle the orientation changes as per the best practices and adjust the orientation of the selected frame as per `rotationDegrees` which allows me to handle every case of rotation and device orientation.

I've been exploring the use of the setOutputImageRotationEnabled API to simplify my logic which fixes the orientation of the frame to always match the display orientation, as a result, the `rotationDegrees` are always 0 regardless of how the device is being held, this works in almost all cases, I noticed that when doing a 180 degree rotation for example, from Landscape Left to Landscape Right without triggering an activity reconstruction, the `ImageProxy` objects continue to report 0 rotation degrees, but the contents of the image do not match the reported rotation.

I tested it in the CameraXBasic sample too with the same results, building the use case like this:
imageAnalyzer = ImageAnalysis.Builder()
    // We request aspect ratio but no resolution
    .setTargetAspectRatio(screenAspectRatio)
    // Set initial target rotation, we will have to call this again if rotation changes
    // during the lifecycle of this use case
    .setTargetRotation(rotation)
    .setOutputImageRotationEnabled(true)
    .build()
    // The analyzer can then be assigned to the instance
    .also {
        it.setAnalyzer(cameraExecutor, LuminosityAnalyzer(requireContext()) { luma ->
        })
    }

I believe that the issue is, that on a landscape left orientation, the device would normally return images with 0 rotation degrees, but when rotating to landscape right it would output the images with 180 rotation degrees(upside-down), I can handle this rotation correctly when not using `setOutputImageRotationEnabled` but when it's enabled, the reported rotation is 0, while my example is for landscape rotation, this could also affect portrait/reverse portrait.

Questions:
  • Is there something I'm doing wrong to handle these cases?
  • Is this a limitation of the feature or a known issue?
  • Is there a workaround for making setOutputImageRotationEnabled work in all cases?
  • Assuming I can't get it to work, is there a utility that would allow me to rotate the YUV_420_888 if I decided to handle it myself by disabling setOutputImageRotationEnabled?
Thanks in advance

Charcoal Chen

unread,
Feb 23, 2026, 8:48:15 PM (14 days ago) Feb 23
to Android CameraX Discussion Group, javiermart...@gmail.com
The issue you're observing—where images appear incorrectly oriented after a 180-degree rotation (e.g., from ROTATION_90 to ROTATION_270)—is a result of how the Android system handles certain orientation changes.

Why this happens
By default, the Android system does not always trigger an activity re-creation or a configuration change when a device is rotated 180 degrees (such as flipping from Landscape Left to Landscape Right).

When you configure your ImageAnalysis use case, CameraX sets a targetRotation based on the display's rotation at that moment. Because the activity is not re-created during the 180-degree flip, the targetRotation remains at its initial value. When setOutputImageRotationEnabled(true) is enabled, CameraX continues to rotate the frames based on that original (now stale) rotation, leading to the orientation mismatch you've described.

Recommended Solution: RotationProvider
To ensure your use cases always have the correct orientation information, even when the activity is not re-created, we recommend using the RotationProvider utility from the androidx.camera:camera-view artifact.

RotationProvider monitors the device's motion sensors to detect physical orientation changes and provides the corresponding Surface rotation values directly ( source). This allows you to dynamically update your use case's targetRotation in real-time.

Implementation Example:

1. Ensure you have the dependency:

    implementation "androidx.camera:camera-view:1.x.x"

2. Update your code to listen for rotation changes:

    // 1. Initialize the RotationProvider
    val rotationProvider = RotationProvider(context)

    // 2. Add a listener to update the use case rotation
    rotationProvider.addListener(executor) { rotation ->
        // Dynamically update the target rotation of the ImageAnalysis use case
        imageAnalyzer.targetRotation = rotation
    }

    // 3. (Optional) Remove the listener in onDestroy or when the use case is unbound rotationProvider.clearListener()
    By manually updating imageAnalyzer.targetRotation via the RotationProvider listener, CameraX will be informed of the new physical orientation even without an activity restart ( source). This ensures that the internal rotation performed by setOutputImageRotationEnabled(true) always produces correctly oriented frames for your analyzer.

Please have a try to see whether this can resolve the problem. Thanks.

Javier Gerardo Martinez Salomon

unread,
Feb 23, 2026, 9:38:19 PM (14 days ago) Feb 23
to Android CameraX Discussion Group, charco...@google.com, Javier Gerardo Martinez Salomon
Hi,

Thanks for your quick response, I didn't know about the `RotationProvider` API, I gave it a try, but unfortunately I encountered the same results I've been finding when using either a `DisplayManager.DisplayListener` or a `OrientationEventListener` to update the ImageAnalysis usecase.

For OrientationEventListener I've done:
private val orientationEventListener by lazy {
    object : OrientationEventListener(context) {
        override fun onOrientationChanged(orientation: Int) {
            if (orientation == ORIENTATION_UNKNOWN) {
                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
            }

            imageAnalysisUseCase?.targetRotation = rotation
        }
    }
}


Continuously updating the imageAnalysisUseCase results in an upside down image despite updating the targetRotation as described in the logs, you can see how it goes from one landscape to another, even identifying the transition to portrait, the API you suggested resembles a lot to this one described in the  best practices :
// Using OrientationEventListener
OrientationEventListener changed: ROTATION_90
OrientationEventListener changed: ROTATION_180
OrientationEventListener changed: ROTATION_270


For RotationProvider.Listener I tried as you suggested:
private val rotationProviderListener = object: RotationProvider.Listener {
    override fun onRotationChanged(rotation: Int) {

        // Dynamically update the target rotation of the ImageAnalysis use case
        imageAnalysisUseCase?.targetRotation = rotation
    }
}

Aside from the less frequent updates, I got exactly the same reported orientation and applied it to the imageAnalysisUseCase, this also results in an upside-down image despite the last targetRotation being 270 as shown in the logs:
// Using RotationProvider.Listener
RotationProvider.Listener changed: ROTATION_90
RotationProvider.Listener changed: ROTATION_180
RotationProvider.Listener changed: ROTATION_270

For DisplayManager.DisplayListener while, the results were the same, this only identified the change from landscape to landscape, it doesn't use the sensors I guess and can't determine that there was a transition to portrait, hence I only saw a single orientation change to 270, despite all that, the three suggested methods detect the orientation change and update the imageAnalysisUseCase target rotation, but they still produce an upside-down image.
// Using DisplayListener
DisplayManager.DisplayListener changed: ROTATION_270

Are these rotation events the expected result for the RotationProvider.Listener you've mentioned? should I see a different result? is there anything else that could explain why the ImageAnalysisUsecase does not "pickup" the new targetRotation and seems to be unaware that there must be a change?

Charcoal Chen

unread,
Feb 24, 2026, 4:17:15 AM (14 days ago) Feb 24
to Android CameraX Discussion Group, javiermart...@gmail.com, Charcoal Chen
Hi,

I can reproduce the issue by the CameraXBasic sample app.
I have created a buganizer issue (https://issuetracker.google.com/issues/487160584) to track this problem.
Thanks.

Javier Gerardo Martinez Salomon

unread,
Feb 24, 2026, 12:03:43 PM (13 days ago) Feb 24
to Android CameraX Discussion Group, charco...@google.com, Javier Gerardo Martinez Salomon
Hello,

Thanks for confirming, in the meantime the only workaround I could find is to disable `setOutputImageRotationEnabled`, in which case the ImageProxy will always report the correct rotationDegrees transformation needed to adjust the image to match the display orientation, in this particular case 180 degrees. With the correct rotation degrees I'm transforming the output myself, although, it would be nice to have access to the same utility that CameraX is using today to apply the rotation transformation, for example, an ImageRotationHelper class that would accept the plane data(or a YuvImage instance) and a target rotation degrees to apply.

I'll be happy to test once the fix has been deployed in a future version of CameraX.
Reply all
Reply to author
Forward
0 new messages