如何修复 flutter 中相机照片的错误旋转?
How to fix wrong rotation of photo from camera in flutter?
我正在使用最新的相机插件版本拍照,并且我正在使用来自 flutter 示例的代码。我是这样挑选相机的:
final cameras = await availableCameras();
final firstCamera = cameras.first;
这是在初始化里面:
_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。
问题是照片有时会以错误的方向拍摄。 (我对此很确定,因为照片也在数据库中旋转。)例如,在 3 岁的小米 phone 上,它工作完美,但在某个新的三星 phone 上,照片总是旋转。
如何确保旋转始终正确? (即使在 ios 台设备上)
您可以使用包 https://pub.dev/packages/flutter_exif_rotation
支持 iOS
和 Android
在某些设备中,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);
这是我的跨平台解决方案,不使用插件。
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;
}
这对我有用:
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));
聚会有点晚了。要求 Flutter 旋转图像或类似操作真的是个坏主意,因为移动应用程序不是为这些目的而设计的。唯一合理的用例是要求 Flutter 调整图像大小,因为您不想通过移动连接向服务器发送高分辨率图像。我的解决方案是将您的完整尺寸(如果您愿意并在后端调整大小)或调整大小的图像从 Flutter 发送到后端,您可以在后端进行自动定位。我使用跨平台的 MagickNET lib 来完成这项工作,它工作得很好。通过这样做,您的 Flutter 代码会更干净整洁。
我知道这已经晚了,但我只是想分享我是如何解决我的问题的,您可以在初始化后或每次拍照前调用此函数,代码如下:
await _camCtrl.lockCaptureOrientation(DeviceOrientation.portraitUp);
这解决了我的问题,有人说它不适用于 iOS,但我还没有测试过,所以你可以测试一下,看看它是否与 [=15= 兼容] 与否。
--- 非常简单的解决方案 ---
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,
);
如果要旋转图片,可以使用https://pub.dev/packages/image来操作图片:
import 'package:image/image.dart';
如果您使用包“camera”,则无法使用 bakeOrientation 旋转图像(删除镜像效果),因为没有 exif 数据。
这对我使用“image_picker”或“相机”很有效。
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);
您可以使用此包 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 设置为 true,keepExif 设置为 false 和 旋转 到 0
我正在使用最新的相机插件版本拍照,并且我正在使用来自 flutter 示例的代码。我是这样挑选相机的:
final cameras = await availableCameras();
final firstCamera = cameras.first;
这是在初始化里面:
_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。
问题是照片有时会以错误的方向拍摄。 (我对此很确定,因为照片也在数据库中旋转。)例如,在 3 岁的小米 phone 上,它工作完美,但在某个新的三星 phone 上,照片总是旋转。
如何确保旋转始终正确? (即使在 ios 台设备上)
您可以使用包 https://pub.dev/packages/flutter_exif_rotation
支持 iOS
和 Android
在某些设备中,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);
这是我的跨平台解决方案,不使用插件。
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;
}
这对我有用:
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));
聚会有点晚了。要求 Flutter 旋转图像或类似操作真的是个坏主意,因为移动应用程序不是为这些目的而设计的。唯一合理的用例是要求 Flutter 调整图像大小,因为您不想通过移动连接向服务器发送高分辨率图像。我的解决方案是将您的完整尺寸(如果您愿意并在后端调整大小)或调整大小的图像从 Flutter 发送到后端,您可以在后端进行自动定位。我使用跨平台的 MagickNET lib 来完成这项工作,它工作得很好。通过这样做,您的 Flutter 代码会更干净整洁。
我知道这已经晚了,但我只是想分享我是如何解决我的问题的,您可以在初始化后或每次拍照前调用此函数,代码如下:
await _camCtrl.lockCaptureOrientation(DeviceOrientation.portraitUp);
这解决了我的问题,有人说它不适用于 iOS,但我还没有测试过,所以你可以测试一下,看看它是否与 [=15= 兼容] 与否。
--- 非常简单的解决方案 ---
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,
);
如果要旋转图片,可以使用https://pub.dev/packages/image来操作图片:
import 'package:image/image.dart';
如果您使用包“camera”,则无法使用 bakeOrientation 旋转图像(删除镜像效果),因为没有 exif 数据。
这对我使用“image_picker”或“相机”很有效。
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);
您可以使用此包 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 设置为 true,keepExif 设置为 false 和 旋转 到 0