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

9

27

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)

Schoolmaster answered 11/2, 2020 at 19:23 Comment(0)
M
4

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);
Maggi answered 12/2, 2020 at 1:49 Comment(10)
Thank you for your answer but how would I use the rotateImage function if I want to overwrite the original image?Schoolmaster
image = await FlutterExifRotation.rotateAndSaveImage(path: image.path); but iOS not support.Maggi
Is there any solution for ios? For my specific case, I would only need to always set the image to portrait.Schoolmaster
File image = await FlutterExifRotation.rotateImage(path: path); Doesn't work. The async function never returns anythingSchoolmaster
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; }); } } }Maggi
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();Schoolmaster
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?Schoolmaster
according to package's description. FlutterExifRotation.rotateImage also work on iOS.Maggi
Just tried it on the problematic device and it's still rotating the photo.Schoolmaster
I suggest post an issue to allow package owner check it.Maggi
C
47

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));
Carbylamine answered 9/7, 2020 at 4:10 Comment(9)
simple and effectiveSimilarity
this comment helped me so much! THANKS!Cleisthenes
This saved the day! Thanks for thisOecd
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+ yearsPga
This is the simplest solution which just worked.Larghetto
Thanks a lot, sir!Hydride
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));Bother
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.!Tuff
I have a problem: Image size increases n times after RotatingPaige
P
14

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

Perianth answered 18/3, 2020 at 8:37 Comment(12)
to use this code. add await to make your function accept Future<File>: File file2 = await fixExifRotation(imageFile.path);Sciamachy
if (height == width) { print('height == width'); fixedImage = img.copyRotate(originalImage, 90); return await originalFile.writeAsBytes(img.encodeJpg(fixedImage));Sciamachy
image_picker: ^0.6.6+1 exif: ^1.0.2 image: ^2.1.12Sciamachy
@Sciamachy 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 exampleErlking
@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.Erlking
Thanks @Erlking for clarification!Perianth
@Erlking after taking the photo, it was rotated, but then after clicking on "confirm photo", then it becomes correctly oriented.Sciamachy
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 CWChokefull
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.Chokefull
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? #64499274Exorcist
This doesn't work for me and also the exif and image are both external plugins. Cannot say this solution doesn't use plugins. :)Nullifidian
This solution worked for me, while bakeOrientation didn't.Cradle
M
4

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);
Maggi answered 12/2, 2020 at 1:49 Comment(10)
Thank you for your answer but how would I use the rotateImage function if I want to overwrite the original image?Schoolmaster
image = await FlutterExifRotation.rotateAndSaveImage(path: image.path); but iOS not support.Maggi
Is there any solution for ios? For my specific case, I would only need to always set the image to portrait.Schoolmaster
File image = await FlutterExifRotation.rotateImage(path: path); Doesn't work. The async function never returns anythingSchoolmaster
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; }); } } }Maggi
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();Schoolmaster
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?Schoolmaster
according to package's description. FlutterExifRotation.rotateImage also work on iOS.Maggi
Just tried it on the problematic device and it's still rotating the photo.Schoolmaster
I suggest post an issue to allow package owner check it.Maggi
I
4

You can use this package flutter_image_compress to resolve the camera image orientation problem. Note that this approach is faster than using the image package approach.

Use it this way:

import 'package:flutter_image_compress/flutter_image_compress.dart';

........

final fixedImageBytes = await FlutterImageCompress.compressWithFile(
  image.path,
  rotate: 0,
  quality: 100,
  keepExif: false,
  autoCorrectionAngle: true,
  format: CompressFormat.jpeg,
);

Note: Make sure to set autoCorrectionAngle to true, keepExif to false and rotate to 0

Infantryman answered 1/2, 2022 at 10:26 Comment(1)
notice this package does not support webHagride
I
3

if you want to rotate image, you can use https://pub.dev/packages/image to manipulate images:

import 'package:image/image.dart';

If you use package "camera", you can't rotate image (remove mirror effect) using bakeOrientation because don't have exif data.

this works for me using "image_picker" or "camera".

File originalFile             = File.fromUri(Uri(path: file.path));
List<int> imageBytes           = await originalFile.readAsBytes();
final Image originalImage      = decodeImage(imageBytes)!;
final Image orientedImage      = flipHorizontal(originalImage);
List<int> imageBytesOrientated = encodeJpg(orientedImage);

For Write in same path:

await File(path).writeAsBytes(imageBytesOrientated);
Intrauterine answered 15/11, 2021 at 13:53 Comment(0)
A
1

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.

Anhydrous answered 13/4, 2021 at 17:21 Comment(1)
Thanks! This was exactly what I was looking for.Murky
N
1

The mentioned solutions did not work for me because I want the camera to use the lockCaptureOrientation() feature. What worked for me, after couple hours of trying and failing, was to use the accelerometer and figure out the actual device orientation no matter whether the phone was locked in portrait or not.

Add the sensors_plus package using:

$ flutter pub add sensors_plus

Then, consider this sample code:

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

class Camera extends StatefulWidget {
  const Camera({super.key});

  @override
  State<Camera> createState() => CameraState();
}

class CameraState extends State<Camera> {

  DeviceOrientation _deviceOrientation = DeviceOrientation.portraitUp;

  @override
  void initState() {
    super.initState();

    const sampling = const Duration(milliseconds: 500);
    accelerometerEventStream(samplingPeriod: sampling).listen((event) {
      if (event.y >= 5) {
        _deviceOrientation = DeviceOrientation.portraitUp;
      } else if (event.y <= -5) {
        _deviceOrientation = DeviceOrientation.portraitDown;
      } else if (event.x >= 0) {
        _deviceOrientation = DeviceOrientation.landscapeLeft;
      } else {
        _deviceOrientation = DeviceOrientation.landscapeRight;
      }
      print(_deviceOrientation);
    });
  }

  // rest of your code to capture a photo using the camera
  // controller (out of scope for this question)
  
  void _adjustPhoto(XFile photoFile, String destPath) async {
    final img.Image originalImage = (await img.decodeImageFile(photoFile.path))!;

    img.Image orientedImage = originalImage;
    if (orientation == DeviceOrientation.landscapeLeft) {
      orientedImage = img.copyRotate(originalImage, angle: -90);
    } else if (orientation == DeviceOrientation.landscapeRight) {
      orientedImage = img.copyRotate(originalImage, angle: 90);
    } else if (orientation == DeviceOrientation.portraitDown) {
      orientedImage = img.copyRotate(originalImage, angle: 180);
    }

    await img.encodeJpgFile(destPath, orientedImage);
  }
}
Natural answered 23/4 at 8:39 Comment(0)
W
0

A bit late to the party. It is really bad idea to ask Flutter to rotate images or similar operations because mobile apps are not designed for these purposes. The only reasonable use case is to ask Flutter to resize the image because you don't want to send a high resolution image to your server over the mobile connection. My solution is send your full size (if you prefer and resize in the back end) or resized images from Flutter to the back end where you can do auto orientation. I use MagickNET lib which is cross platform to do the job and it works perfectly. By doing this, your Flutter code is much cleaner and tidier.

Wauters answered 5/3, 2021 at 16:10 Comment(0)
F
0

--- VERY SIMPLE SOLUTION ---

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

XFile xfile = await _cameraController.takePicture();

List<int> imageBytes = await xfile.readAsBytes();

img.Image? originalImage = img.decodeImage(imageBytes);
img.Image fixedImage = img.flipVertical(originalImage!);

File file = File(xfile.path);

File fixedFile = await file.writeAsBytes(
  img.encodeJpg(fixedImage),
  flush: true,
);
Furnishings answered 29/10, 2021 at 15:49 Comment(3)
this rotates every image by 180 degree..Bonis
this is the example. you can use img.flipHorizantal function to flip to other side which you needed.Furnishings
it is not for rotating its for flipping image. I had problem with front camera returning images flipped. This helped me a lot thanksDormeuse

© 2022 - 2024 — McMap. All rights reserved.