question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Incorrect image orientation using MediaPicker CapturePhoto on iOS

See original GitHub issue

Description

When using MediaPicker.Default.CapturePhotoAsync() on iOS, the image data from FileResult.OpenReadAsync() gets converted to PNG format with no orientation flag to correctly display the final result.

Many smartphones capture photos using one consistent orientation, and then set the orientation using a flag in the EXIF metadata for the image. This is usually not a problem if the orientation flag is allowed to remain inside the image. Unfortunately, when the PNG conversion occurs with CapturePhotoAsync(), the resulting image data does not contain any orientation flag to properly rotate the photo.

There are two ways to fix this:

  1. Provide the orientation flag within the PNG data
  2. Perform the correct image rotate+flip internally before returning PNG data without an orientation flag

Option 1 would probably be the quickest way to fix this and require less processing time.

Steps to Reproduce

Just set up a quick Maui project with a button and an image view. Use MediaPicker.CapturePhoto to take a photo with an iOS device (might need to try different device rotations/orientations). Set the “Source” property of the image to the file result. Code for button click handler method body is below:

var photo = await MediaPicker.Default.CapturePhotoAsync();
var stream = await photo.OpenReadAsync();
var img = ImageSource.FromStream(() => { return stream; });
imgTest.Source = img;

Version with bug

.NET 6.0.400-preview.22330.6

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

15.6

Did you find any workaround?

The only workaround is to take the photo outside the app, then use MediaPicker.Default.PickPhotoAsync() to choose the file (no PNG conversion happens that way).

Relevant log output

No response

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:9
  • Comments:13

github_iconTop GitHub Comments

3reactions
ianpowell2017commented, Mar 28, 2023

I am having the same problem. It baffles me why this has been moved to the backlog when it is a serious problem. There is currently no feasible work around

1reaction
angelrucommented, Apr 26, 2023

With Xamarin Forms I solved it this way.

Android:

public class PhotoPickerService : IPhotoPickerService
    {
        public async Task<byte[]> ResizeImageAsync(string path)
        {
            try
            {
                var exif = new Android.Media.ExifInterface(path);
                string orientation = exif.GetAttribute(Android.Media.ExifInterface.TagOrientation);

                //Get the bitmap.
                var originalImage = BitmapFactory.DecodeFile(path);

                //Set imageSize and imageCompression parameters.
                var imageSize = .40;
                var imageCompression = 45;

                //Resize it and then compress it to Jpeg.
                var width = originalImage.Width * imageSize;
                var height = originalImage.Height * imageSize;
                var scaledImage = Bitmap.CreateScaledBitmap(originalImage, (int)width, (int)height, true);

                var matrix = new Matrix();

                switch (orientation)
                {
                    case "1": // landscape
                        break;
                    case "3":
                        matrix.PreRotate(180);
                        scaledImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
                        matrix.Dispose();
                        matrix = null;
                        break;
                    case "4":
                        matrix.PreRotate(180);
                        scaledImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
                        matrix.Dispose();
                        matrix = null;
                        break;
                    case "5":
                        matrix.PreRotate(90);
                        scaledImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
                        matrix.Dispose();
                        matrix = null;
                        break;
                    case "6": // portrait
                        matrix.PreRotate(90);
                        scaledImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
                        matrix.Dispose();
                        matrix = null;
                        break;
                    case "7":
                        matrix.PreRotate(-90);
                        scaledImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
                        matrix.Dispose();
                        matrix = null;
                        break;
                    case "8":
                        matrix.PreRotate(-90);
                        scaledImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
                        matrix.Dispose();
                        matrix = null;
                        break;
                }

                byte[] imageBytes;

                using (MemoryStream ms = new MemoryStream())
                {
                    scaledImage.Compress(Bitmap.CompressFormat.Jpeg, imageCompression, ms);
                    imageBytes = ms.ToArray();
                    await File.WriteAllBytesAsync(path, imageBytes);
                }

                originalImage.Recycle();
                scaledImage.Recycle();
                originalImage.Dispose();
                scaledImage.Dispose();

                return imageBytes;
            }

            catch (IOException ex)
            {
                _ = ex.Message;
                return null;
            }
        }
    }

iOS:

  public class PhotoPickerService : IPhotoPickerService
    {
        public async Task<byte[]> ResizeImageAsync(string path)
        {
            try
            {
                UIImage originalImage = UIImage.FromFile(path);
                var resizedImage = MaxResizeImage(originalImage, 1920f, 1080f);
                NSData imgData = resizedImage.AsJPEG(0.46f);
                byte[] imageByte = imgData.ToArray();
                if (imgData.Save(path, NSDataWritingOptions.Atomic, out NSError err))
                {
                    //Dispose of objects.
                    originalImage.Dispose();
                    resizedImage.Dispose();
                    imgData.Dispose();
                    await Task.FromResult(imageByte);
                }
            }

            catch (Exception ex)
            {
                _ = ex.Message;
            }

            return null;
        }

        private UIImage MaxResizeImage(UIImage sourceImage, float maxWidth, float maxHeight)
        {
            var sourceSize = sourceImage.Size;
            var maxResizeFactor = Math.Max(maxWidth / sourceSize.Width, maxHeight / sourceSize.Height);
            if (maxResizeFactor > 1) return sourceImage;
            var width = maxResizeFactor * sourceSize.Width;
            var height = maxResizeFactor * sourceSize.Height;
            UIGraphics.BeginImageContext(new System.Drawing.SizeF((float)width, (float)height));
            sourceImage.Draw(new System.Drawing.RectangleF(0, 0, (float)width, (float)height));
            var resultImage = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();
            return resultImage;
        }
    }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Photos taken using MediaPicker.CapturePhotoAsync are ...
If want to adjust the photo orientation, try to rotate the photo stream in the shared project using SKBitmap directly. Here is the...
Read more >
MediaPicker.CapturePhotoAsync() Image rotated 90 degree
Then look at the exif orientation info in the source and rotate the bitmap accordingly. – blackapps. Oct 7, 2022 at 5:25.
Read more >
Another photo orientation question | Apple Developer Forums
I.e. if the iphone is in portrait orientation, the preview image is rotated 90 degrees anti-clockwise, in landscape the same happens so the...
Read more >
Is MediaPicker.CapturePhotoAsync() broken on iOS?
I'm using a MediaPicker to snap pictures of users to set as their profile pics in a details page. I've ran my app...
Read more >
photos orientation in iOS when using camera phone?
i've experimented with this and i don't understand why it is happening. i do understand there is a gps chip in the phone....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found