Quality problems when converting tiff to JPG in flutter - flutter

I have a tiff image file. This is stored in cache memory using the path_provider package.
final cacheDir = await getTemporaryDirectory();
File _tiffFile = File('$cacheDir/img1.tiff'); // /data/user/0/package_name/cache/img1.tiff
Now convert it to a 'jpg' file.
import 'dart:io';
import 'package:image/image.dart' as imageLib;
imageLib.Image? _image = imageLib.decodeImage(_file.readAsBytesSync());
imageLib.Image _tempImg = imageLib.copyResize(_image!, width: (size.width * 0.9).toInt(), height: (size.height * 0.7).toInt());
File _pngImg = File('$cacheDir/img1.jpg')..writeAsBytesSync(imageLib.encodeJpg(_tempImg, quality: 100));
It is set to 100 even if you don't enter the quality of the encodeJpg method. But the quality of my 'jpg' image is very poor. Is there a way to convert it to high definition?

Related

Dart how to resize image height and width when its in bytes

I have the following code and I need a way to resize the image height and width that it in ByteData here is my code
Future<ByteData?> _createChartImage() async {
var data = await _chartKey.currentState?.toImage(pixelRatio: 3.0);
var byteData = await data!.toByteData(format: ImageByteFormat.png);
return byteData;
}
You can't resize byte data, especially not byte data encoded in PNG format. So you have to first parse the byte data from PNG format back into a bitmap. And then resize the bitmap and then encode it back to PNG.
I would suggest taking a look at the image package.
Something like the following should work:
final image = decodePng(byteData!.buffer.asUint8List());
final resized = copyResize(image, width: 120);
final resizedByteData = encodePng(image);
return ByteData.sublistView(resizedByteData);

Flutter - How to convert an SVG file to Bitmap

How to convert an SVG file from the Flutter Assets to a Bitmap?
You need flutter_svg library, that you probably already use if you are playing with SVG.
You need also bitmap library.
import 'package:bitmap/bitmap.dart';
import 'package:flutter_svg/flutter_svg.dart';
String svgString = await DefaultAssetBundle.of(context).loadString(svg_path);
DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, null);
// to have a nice rendering it is important to have the exact original height and width,
// the easier way to retrieve it is directly from the svg string
// but be careful, this is an ugly fix for a flutter_svg problem that works
// with my images
String temp = svgString.substring(svgString.indexOf('height="')+8);
int originalHeight = int.parse(temp.substring(0, temp.indexOf('p')));
temp = svgString.substring(svgString.indexOf('width="')+7);
int originalWidth = int.parse(temp.substring(0, temp.indexOf('p')));
// toPicture() and toImage() don't seem to be pixel ratio aware, so we calculate the actual sizes here
double devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
double width = originalHeight * devicePixelRatio; // where 32 is your SVG's original width
double height = originalWidth * devicePixelRatio; // same thing
// Convert to ui.Picture
ui.Picture picture = svgDrawableRoot.toPicture(size: Size(width, height));
// Convert to ui.Image. toImage() takes width and height as parameters
// you need to find the best size to suit your needs and take into account the screen DPI
ui.Image image = await picture.toImage(width.toInt(), height.toInt());
ByteData bytes = await image.toByteData(format: ui.ImageByteFormat.png);
// finally to Bitmap
Bitmap bitmap = await Bitmap.fromHeadless(width.toInt(), height.toInt(),
bytes.buffer.asUint8List()
);
// if you need to save it:
File file = File('temporary_file_path/unique_name.bmp');
file.writeAsBytesSync(bitmap.content);
Some credit to this StackOverflow answer

How can I store widget screenshot as jpg file in flutter?

I have taken a widget screenshot and I want to save it as .jpg file. I was able to save it as .png file below but I don't know how to save it as .jpg file. Here is my code:
RenderRepaintBoundary boundary = _repainkey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage();
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
final directory = (await getExternalStorageDirectory());
print(directory.path);
File imgFile = new File('${directory.path}/flutter2.png');
await imgFile.writeAsBytes(pngBytes);
You could use the jpeg_encode library. It would allow you to convert your ui.Image object to a jpeg without the intermediary step of converting to a png, and is thus likely to be faster.
You have to do a file type conversion. You can use the image package to help you do this. This answer is very similar to what you're looking for, just doing the conversion reversed from what you want.
Example from the linked answer:
import 'dart:io';
import 'package:image/image.dart';
void main() {
// Read a jpeg image from file.
Image image = decodeImage(new File('test.jpg').readAsBytesSync());
// Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
Image thumbnail = copyResize(image, 120);
// Save the thumbnail as a PNG.
new File('out/thumbnail-test.png')
..writeAsBytesSync(encodePng(thumbnail));
}
import 'dart:io';
import 'package:image/image.dart';
void main() {
// Read a jpeg image from file.
Image image = decodeImage(new File('test.png').readAsBytesSync());
// Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
Image thumbnail = copyResize(image, 120);
// Save the thumbnail as a PNG.
new File('out/thumbnail-test.jpg')
..writeAsBytesSync(encodeJpg(thumbnail));
}

Passing a base64 string encoded image/ byte image as an image for processsing in Firebase ML Vision in Flutter

I want to OCR text from a base64 encoded image.
I know the image works because I can display it using
Image.memory(base64Decode(captchaEncodedImgFetched))
Now, the problem is I need to pass this image to Firebase ML Vision for processing.
The library firebase_ml_vision has an example for using a image from file
final File imageFile = getImageFile();
final FirebaseVisionImage visionImage = FirebaseVisionImage.fromFile(imageFile);`
However I have a base64 encoded image.
I tried the following
final FirebaseVisionImage visionImage = FirebaseVisionImage.fromBytes(
base64Decode(captchaEncodedImgFetched));
But it seems to need FirebaseVisionImageMetadata() as a argument, but I know nothing about byte images.
This class needs a lot more arguments which I don't understand.
For example, it needs a size : Size(width, height) argument. Isn't the image supposed to have a size already? Why do I need to specify it again?
For now I set it to Size(200, 50). Then there are the other arugments and I don't know what to pass to them. For exmaple the planeData and rawFormat.
Here are the docs for these:
https://pub.dev/documentation/firebase_ml_vision/latest/firebase_ml_vision/FirebaseVisionImageMetadata-class.html
https://pub.dev/documentation/firebase_ml_vision/latest/firebase_ml_vision/FirebaseVisionImagePlaneMetadata-class.html
https://pub.dev/documentation/firebase_ml_vision/latest/
FirebaseVisionImage.fromBytes needs FirebaseVisionImageMetadata which intern needs FirebaseVisionImagePlaneMetadata. Example below :
// Below example uses metadata values based on an RGBA-encoded 1080x1080 image
final planeMetadata = FirebaseVisionImagePlaneMetadata(
width: 1080,
height: 1080,
bytesPerRow: 1080 * 4,
);
final imageMetadata = FirebaseVisionImageMetadata(
size: Size(1080, 1080),
planeData: planeMetadata,
rawFormat: 'RGBA',
);
final visionImage = FirebaseVisionImage.fromBytes(decoded, metadata);
The simpler workaround though at a cost of performance is to write the bytes to the disk and read the image from there, as :
File imgFile = File('myimage.png');
imageFile.writeAsBytesSync(decoded.ToList());
final visionImage = FirebaseVisionImage.fromFile(imageFile);

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

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