I am using the file_picker package to import images before uploading them to Firebase Storage, because it allows me to choose the formats what is not possible with image_picker.
I would like to be able to reduce the size of these images to reduce their weight. The flutter_image_compress package allows to compress images but it is not available for Flutter Web.
How can I reduce the size of an image imported in Flutter Web knowing that the format is Uint8List?
You can use the archive package to compress any data. The following is an example function taking Uint8List and compressing it:
Uint8List compress(Uint8List input) {
final encoder = ZLibEncoder();
return encoder.encode(input) as Uint8List;
}
You'll want to do this in a separate isolate since this could noticeably block your UI, but this won't make any difference if your application is web-only.
if you know the image type you can convert it to jpeg and then compress it using the flutter image package.
import 'package:image/image.dart' as img;
Future<List<int>> _pngToJpg(Uint8List pngBytes) async {
final pngImage = img.decodePng(pngBytes);
final pngImageResized = img.copyResize(pngImage!, width: 800);
return img.encodeJpg(pngImageResized, quality: 90);
}
you can use any other decoder depending on your image type. Expect a freeze in UI. You can use compute to minimize it.
Related
The new Flutter 3.3 deprecates the method load of class ImageProvider and now some my code became not working because load had a parameter bytes of type Uit8List but loadBuffer has a parameter buffer of ImmutableBuffer.
I used Uint8List to decode image using image package
final bytes = img.decodeImage(bytes); // bytes is instance of Uit8List
The complete code can be read from the article Remove a color from an image in Flutter starting from section 4. Decode.
And now I need to convert ImmutableBuffer to Uint8List. Does someone know how to do this?
My code is simle;
final XFile image=await cameraController!.takePicture();
takenImage=await image.readAsBytes();
and when I am just using this two line, I can see the image with Image.memory(_vm.takenImage!, fit: BoxFit.fitWidth)
But i need to change picture rotation,
for this reason, I am using image package https://pub.dev/packages/image
so, why I am getting invalid image data after just these lines;
final XFile image=await cameraController!.takePicture();
takenImage=await image.readAsBytes();
img.Image? decodedImage=img.decodeImage(takenImage!);
takenImage=decodedImage!.getBytes();
If I delete the last two sentence it's working correctlye but if I add the last lines I am getting Exceltion: invalid image data, but why ? I mean .getBytes returning Uint8List, takenImage type is Uint8List? ...
if I can convert like my second code review, I can use the; img.copyRotate(originalImage!, 90).getBytes(); function, so please help me for understand.
For my problem related image type cause as you can see on question I wasn't encode my image with any type, so the solution and working code now can seen on below;
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));
I am using the multiple file picker package on pub.dev, the link is below https://pub.dev/packages/multi_image_picker in conjunction with the image cropper package by Yalantis
https://pub.dev/packages/image_cropper
to let my user pick multiple images and then crop them at will.
I am using this code to convert my asset into a file and feed it into the cropper. And it works.
final temp = await Directory.systemTemp.createTemp();
final data = await finalList[index].getByteData();
File failo = await File('${temp.path}/img').writeAsBytes(
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
print("The path is ${temp.path}");
File croppedFailo = await ImageCropper.cropImage(
sourcePath: failo.path,
androidUiSettings: AndroidUiSettings(toolbarTitle: "My App"),
);
The tricky bit is to convert it back to an asset so that i can replace the old uncropped asset with this new cropped one..I read through the Asset documentation of the package and I tried this but it made my app crash
Asset croppedPic = new Asset(
croppedFailo.path,
DateTime.now().millisecondsSinceEpoch.toString(),
300,
300,
);
finalList.replaceRange(index, index + 1, [croppedPic]);
EDIT: When i say "asset", i am not referring to images i manually added to the assets/images folder in the app. The multi image picker plugin has a file- type called asset in which it returns images. That is the type to which i want to convert my file back into.
Never mind. I figured it's too unnecessarily complicated to do that. So, i instead just reformatted my entire code to handle images as files instead of assets. And, it actually made my life a lot simpler coz files give you more versatility and less problems than assets.
I want to find out the size of an Uint8List image before I upload it to firebase storage using
StorageUploadTask uploadTask = reference.putData(uint8ListImage);
wherein uint8ListImage is a Uint8List. Is there a way to find out the size of the Uint8List?
try this,
print(uint8ListImage.lengthInBytes);
this will print the size in bytes
I'm trying to compress image from camera or gallery, but i tried answer in this question Flutter & Firebase: Compression before upload image
But the UI was freeze , so do you guys have any solution for that, and why the image plugin meet that problem ?
UPDATE:
compressImage(imageFile).then((File file) {
imageFile = file;
});
Future<File> compressImage(File imageFile) async {
return compute(decodeImage, imageFile);
}
File decodeImage(File imageFile) {
Im.Image image = Im.decodeImage(imageFile.readAsBytesSync());
Im.Image smallerImage = Im.copyResize(
image, 150); // choose the size here, it will maintain aspect ratio
return new File('123.jpg')
..writeAsBytesSync(Im.encodeJpg(smallerImage, quality: 85));
}
I meet "unhandled exception" in this code
This is because compression is done in the UI thread.
You can move computation to a new thread using compute() https://docs.flutter.io/flutter/foundation/compute.html
There are currently serious limitations what a non-UI thread can do.
If you pass the image data, it is copied from one thread to the other, which can be slow. If you have the image in a file like you get it from image_picker it is better to pass the file path and read the image in the new thread.
You can only pass values that can be encoded as JSON (it's not actually encoded as JSON, but it supports the same types)
You can not use plugins. This means you need to move the compressed data back to the UI thread by passing the data (which again is copied) or by writing in a file and passing back the path to the file, but in this case copying might be faster because writing a file in one thread and reading it in the other is even slower).
Then you can for example invoke image uploading to Firebase Cloud Storage in the UI thread, but because this is a plugin it will run in native code and not in the UI thread. It's just the UI thread that needs to pass the image along.