How to fix wrong rotation of photo from camera in flutter?

17,632

Solution 1

You can use package https://pub.dev/packages/flutter_exif_rotation
Support iOS and Android
In some devices the exif data shows picture in landscape mode when they're actually in portrait.
This plugin fixes the orientation for pictures taken with those devices.

For Android
Add this in your AndroidManifest.xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

code snippet

image = await FlutterExifRotation.rotateImage(path: image.path);

//Note : iOS not implemented
image = await FlutterExifRotation.rotateAndSaveImage(path: image.path);

Solution 2

This worked for me:

import 'package:image/image.dart' as img;

...

final img.Image capturedImage = img.decodeImage(await File(path).readAsBytes());
final img.Image orientedImage = img.bakeOrientation(capturedImage);
await File(path).writeAsBytes(img.encodeJpg(orientedImage));

Solution 3

This is my solution that works cross-platform and doesn't use plugins.

import 'dart:io';
import 'package:exif/exif.dart';
import 'package:image/image.dart' as img;

Future<File> fixExifRotation(String imagePath) async {
    final originalFile = File(imagePath);
    List<int> imageBytes = await originalFile.readAsBytes();

    final originalImage = img.decodeImage(imageBytes);

    final height = originalImage.height;
    final width = originalImage.width;

    // Let's check for the image size
    // This will be true also for upside-down photos but it's ok for me
    if (height >= width) {
      // I'm interested in portrait photos so
      // I'll just return here
      return originalFile;
    }

    // We'll use the exif package to read exif data
    // This is map of several exif properties
    // Let's check 'Image Orientation'
    final exifData = await readExifFromBytes(imageBytes);

    img.Image fixedImage;

    if (height < width) {
      logger.logInfo('Rotating image necessary');
      // rotate
      if (exifData['Image Orientation'].printable.contains('Horizontal')) {
        fixedImage = img.copyRotate(originalImage, 90);
      } else if (exifData['Image Orientation'].printable.contains('180')) {
        fixedImage = img.copyRotate(originalImage, -90);
      } else if (exifData['Image Orientation'].printable.contains('CCW')) {
        fixedImage = img.copyRotate(originalImage, 180);
      } else {
        fixedImage = img.copyRotate(originalImage, 0);
      }
    }

    // Here you can select whether you'd like to save it as png
    // or jpg with some compression
    // I choose jpg with 100% quality
    final fixedFile =
        await originalFile.writeAsBytes(img.encodeJpg(fixedImage));

    return fixedFile;
  }

Source

Solution 4

I know this is late, but I just wanna to share how I fix my issue, you can call this function after it's initialized or every time before you take a photo, and here is the code:

await _camCtrl.lockCaptureOrientation(DeviceOrientation.portraitUp);

This fix my issue, somebody says it won't work on iOS, but I haven't test that it, so you can test it out and see if it is compatible with iOS or not.

Share:
17,632
Sprowk
Author by

Sprowk

Updated on June 07, 2022

Comments

  • Sprowk
    Sprowk almost 2 years

    I'm taking a photo with the newest camera plugin version and I'm using code from flutter example. This is how I pick a camera:

    final cameras = await availableCameras();
    final firstCamera = cameras.first;
    

    This is inside init:

    _cameraController = CameraController(
      widget.camera,
      ResolutionPreset.medium,
      enableAudio: false,
    );
    

    This is the rest of the relevant code:

    Future _takePhoto(BuildContext context) async {
      try {
        await _initializeControllerFuture;
        final path = join(
          (await getTemporaryDirectory()).path,
          '${DateTime.now()}.png',
        );
    
        await _cameraController.takePicture(path);
    
        setState(() {
          _imagePath = path;
        });
      } catch (e) {
        print(e);
      }
    }
    

    Afterwards, I show the photo to the user with Image.file(File(_imagePath)) and later I send the photo to API. The problem is that the photo is sometimes captured with a wrong orientation. (I'm sure about this because the photo is also rotated in the database.) For example, on 3 years old Xiaomi phone, it works flawlessly, but on a certain new Samsung phone, the photo is always rotated.

    How to make sure that the rotation is always correct? (Even on ios devices)

  • Sprowk
    Sprowk about 4 years
    Thank you for your answer but how would I use the rotateImage function if I want to overwrite the original image?
  • chunhunghan
    chunhunghan about 4 years
    image = await FlutterExifRotation.rotateAndSaveImage(path: image.path); but iOS not support.
  • Sprowk
    Sprowk about 4 years
    Is there any solution for ios? For my specific case, I would only need to always set the image to portrait.
  • Sprowk
    Sprowk about 4 years
    File image = await FlutterExifRotation.rotateImage(path: path); Doesn't work. The async function never returns anything
  • chunhunghan
    chunhunghan about 4 years
    could you try example code Future getImageAndSave() async { File image = await ImagePicker.pickImage(source: ImageSource.gallery); if (image != null && image.path != null) { // Note : iOS not implemented image = await FlutterExifRotation.rotateAndSaveImage(path: image.path); if (image != null) { setState(() { _image = image; }); } } }
  • Sprowk
    Sprowk about 4 years
    Your function above worked (instead of changing state inside the function I returned the image and then setState with image.path) File image = await getImageAndSave();
  • Sprowk
    Sprowk about 4 years
    Future fixExif(String path) async { File image = File(path); image = await FlutterExifRotation.rotateImage(path: image.path); return image; } This code ran on my phone. I'll test it on the problematic device today. Will it work on ios though?
  • chunhunghan
    chunhunghan about 4 years
    according to package's description. FlutterExifRotation.rotateImage also work on iOS.
  • Sprowk
    Sprowk about 4 years
    Just tried it on the problematic device and it's still rotating the photo.
  • chunhunghan
    chunhunghan about 4 years
    I suggest post an issue to allow package owner check it.
  • Russo
    Russo almost 4 years
    to use this code. add await to make your function accept Future<File>: File file2 = await fixExifRotation(imageFile.path);
  • Russo
    Russo almost 4 years
    if (height == width) { print('height == width'); fixedImage = img.copyRotate(originalImage, 90); return await originalFile.writeAsBytes(img.encodeJpg(fixedImage));
  • Russo
    Russo almost 4 years
    image_picker: ^0.6.6+1 exif: ^1.0.2 image: ^2.1.12
  • teh_raab
    teh_raab almost 4 years
    @Russo Did you get this working? I am using an iPad Mini 2, and when taking a photo in portrait if falls into the bottom else{} condition and does not rotation the image. If you have it working would you please be able to publish a working example
  • teh_raab
    teh_raab almost 4 years
    @Dominik I dont think the if condition is exhaustive. I printed out the exifData['Image Orientation'].printable and it is returning '90 CW' for me. I think this also needs to be considered in the If statement. For reference, i am testing on an iPad Mini 2.
  • Dominik Roszkowski
    Dominik Roszkowski almost 4 years
    Thanks @teh_raab for clarification!
  • Russo
    Russo almost 4 years
    @teh_raab after taking the photo, it was rotated, but then after clicking on "confirm photo", then it becomes correctly oriented.
  • Maks
    Maks over 3 years
    A post on the exiftool forum provides a full list of all the orientation values: 1 = Horizontal (normal) 2 = Mirror horizontal 3 = Rotate 180 4 = Mirror vertical 5 = Mirror horizontal and rotate 270 CW 6 = Rotate 90 CW 7 = Mirror horizontal and rotate 90 CW 8 = Rotate 270 CW
  • Maks
    Maks over 3 years
    This an acceptable solution @DominikRoszkowski but ideally just the exif orientation metadata should be corrected and written out instead of actually modifying the rotation of the underlying image data which will not be a necessarily a lossless operation.
  • Chris
    Chris over 3 years
    Hi Dominik, this solution is fantastic. However, I'm wondering why this same method can't be used to rotate a file that's already been selected? e.g. I use ImagePicker and rotate with this method, then I try to rotate my new file with a similar method but it doesn't work .. Please check you check this question? stackoverflow.com/questions/64498774/…
  • Ricardo Chen He
    Ricardo Chen He over 3 years
    simple and effective
  • Felix
    Felix over 3 years
    this comment helped me so much! THANKS!
  • ChillBroDev
    ChillBroDev over 3 years
    This saved the day! Thanks for this
  • Nero
    Nero over 3 years
    This doesn't work for me and also the exif and image are both external plugins. Cannot say this solution doesn't use plugins. :)
  • HTMHell
    HTMHell over 3 years
    This solution worked for me, while bakeOrientation didn't.
  • Andreas Toresäter
    Andreas Toresäter about 3 years
    This worked out good for me as I was already using the image plugin, thanks! Too bad the already slow capturing of an image gets even slower and worse why is such a severe bug still live in flutter after 2+ years
  • meteors
    meteors about 3 years
    This is the simplest solution which just worked.
  • Silverbaq
    Silverbaq almost 3 years
    Thanks! This was exactly what I was looking for.
  • OnlyTarg
    OnlyTarg over 2 years
    Thanks a lot, sir!
  • leylekseven
    leylekseven over 2 years
    you saved me a lot! thank you, and alsı, we can do same operation process without file; like; final XFile image=await cameraController!.takePicture(); img.Image? capturedImage=img.decodeImage(await image.readAsBytes()); capturedImage=img.copyRotate(capturedImage!, 90); takenImage=Uint8List.fromList(img.encodePng(capturedImage));
  • Noldy Nayoan
    Noldy Nayoan over 2 years
    Up until dec 2021, using the latest update of camera plugin, the orientation bug still exists (on Samsung A10 phone). This solution by Mark Laughton simply works!. Thanks.!
  • Aditya Patil
    Aditya Patil over 2 years
    this rotates every image by 180 degree..
  • Muhammad Tameem Rafay
    Muhammad Tameem Rafay over 2 years
    this is the example. you can use img.flipHorizantal function to flip to other side which you needed.
  • Mohammad Hosein
    Mohammad Hosein about 2 years
    it is not for rotating its for flipping image. I had problem with front camera returning images flipped. This helped me a lot thanks