如何在Flutter中修复相机拍摄照片的错误旋转?

24

我正在使用最新的相机插件版本拍照,并使用来自Flutter示例的代码。 这是我选择相机的方式:

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

这是 init 内部的内容:

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

这是相关代码的其余部分:

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);
  }
}
之后,我用 Image.file(File(_imagePath)) 将照片展示给用户,并将照片发送到API。 问题在于有时照片的方向不正确。(我很确定这一点,因为数据库中的照片也被旋转了。)例如,在三年前的小米手机上,它运行得非常完美,但在某些新的三星手机上,照片总是旋转的。 如何确保旋转始终正确?(即使在iOS设备上也是如此)
8个回答

45

这对我有用:

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));

1
简单而有效 - Ricardo Chen He
1
这个评论帮了我很多!谢谢! - Felix
2
这对我来说很有效,因为我已经在使用图像插件了,谢谢!太糟糕了,本来就很慢的图像捕获变得更慢更差了,为什么这样一个严重的错误在Flutter中仍然存在2年以上? - Andreas Toresäter
这是最简单的解决方案,它很有效。 - meteors
你帮我省了很多事,谢谢!同时,我们也可以在没有文件的情况下执行相同的操作过程;例如: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)); - leylekseven
显示剩余4条评论

13

这是我跨平台且不使用插件的解决方案。

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;
  }

来源


使用此代码,请添加await使函数接受Future<File>:File file2 = await fixExifRotation(imageFile.path); - Russo
1
@Dominik 我认为if条件不够全面。我打印了exifData ['Image Orientation'] .printable,它对我返回了“90 CW”。我认为这也需要在If语句中考虑到。供参考,我正在iPad Mini 2上进行测试。 - teh_raab
2
这是一个可以接受的解决方案@DominikRoszkowski,但理想情况下,应该仅纠正exif方向元数据并将其写出,而不是实际修改底层图像数据的旋转,这将不一定是无损操作。 - Maks
1
嗨Dominik,这个解决方案真是太棒了。但是,我想知道为什么不能使用相同的方法旋转已经选择的文件呢?例如,我使用ImagePicker并使用此方法进行旋转,然后尝试使用类似的方法旋转我的新文件,但不起作用..请问你能看一下这个问题吗?https://dev59.com/XL3pa4cB1Zd3GeqPmtoT - Chris
1
这对我不起作用,而且exif和图像都是外部插件。不能说这个解决方案不使用插件。 :) - Nero
显示剩余7条评论

4
你可以使用包https://pub.dev/packages/flutter_exif_rotation
支持iOSAndroid
在一些设备上,exif数据会将竖屏拍摄的图片显示为横屏模式。
该插件可以修正这些设备拍摄的照片方向问题。
对于Android:
请在你的AndroidManifest.xml文件中添加以下内容。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

代码片段

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

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

有没有针对iOS的解决方案?对于我的特定情况,我只需要始终将图像设置为纵向。 - Sprowk
1
文件 image = await FlutterExifRotation.rotateImage(path: path); 不起作用。异步函数没有返回任何内容。 - Sprowk
你上面的函数运行成功了(我没有在函数内部改变状态,而是返回了图像,并使用图像路径设置了setState)。文件图像= await getImageAndSave(); - Sprowk
未来修复Exif(String路径)异步{ 文件图像=文件(路径); 图像=等待FlutterExifRotation.rotateImage(path: image.path); 返回图像; } 这段代码在我的手机上运行。我今天会在有问题的设备上测试它。但是它能在iOS上工作吗? - Sprowk
3
刚刚在有问题的设备上试了一下,它仍然在旋转照片。 - Sprowk
显示剩余5条评论

3

如果您想旋转图像,您可以使用 https://pub.dev/packages/image 来操作图片:

import 'package:image/image.dart';

如果您使用“camera”包,由于没有exif数据,您不能使用bakeOrientation来旋转图像(去除镜像效果)。
对我而言,使用“image_picker”或“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);

对于在同一路径下写入:

await File(path).writeAsBytes(imageBytesOrientated);

3
您可以使用这个包flutter_image_compress来解决摄像头图像方向问题。请注意,这种方法比使用image包的方法更快。
使用方法如下:
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,
);

注意: 确保将autoCorrectionAngle设置为truekeepExif设置为false ,并将rotate设置为0

请注意,此软件包不支持网络。 - syonip

1

我知道这有点晚了,但是我想分享一下如何解决我的问题。你可以在初始化后或每次拍照前调用此函数,以下是代码:

await _camCtrl.lockCaptureOrientation(DeviceOrientation.portraitUp);

这解决了我的问题,有人说它在iOS上不起作用,但我还没有测试它,所以你可以尝试一下,看它是否与iOS兼容。

1
谢谢!这正是我在寻找的。 - Silverbaq

0

--- 非常简单的解决方案 ---

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,
);

这将每张图片旋转180度。 - Aditya Patil
这是一个例子。您可以使用img.flipHorizontal函数将其翻转到所需的另一侧。 - Muhammad Tameem Rafay
1
这不是用于旋转,而是用于翻转图像。我遇到了前置摄像头返回翻转图像的问题。这对我帮助很大,谢谢。 - Mohammad Hosein

0
有点晚了。请求Flutter旋转图像或类似操作是一个非常糟糕的想法,因为移动应用程序并不是为这些目的而设计的。唯一合理的用例是要求Flutter调整图像大小,因为您不想通过移动连接将高分辨率图像发送到服务器。我的解决方案是将完整尺寸的图像(如果您愿意并在后端进行调整大小)或调整大小的图像从Flutter发送到后端,其中可以进行自动定向。我使用的是跨平台的MagickNET库来完成此工作,并且它的效果非常好。通过这样做,您的Flutter代码会更加清洁和整洁。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接