In Flutter, how to reduce the image data before saving it to a file? - flutter

I'm working on a screen recording app in Flutter. In order to keep the quality of the captured screen image, I have to scale the image as shown below. However, it makes the image data very large. How can I reduce the data size (scale back to the original size) before saving it to a file? For performance reasons, I want to work with raw RGBA instead of encoding it PNG.
double dpr = ui.window.devicePixelRatio;
final boundary =
key.currentContext!.findRenderObject() as RenderRepaintBoundary;
return getBufferSync(key);
}
ui.Image image = boundary.toImageSync(pixelRatio: dpr);
var rawBytes = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
Uint8List uData = rawBytes!.buffer.asUint8List();

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

Reading / converting camera stream images in Flutter

On my iOS device, I'm accessing the camera through a CameraController (provided by the camera package) with controller.startImageStream((CameraImage img) {...}
The data coming out of the camera is in a bgra8888 format on my phone, but I've read that it's in yuv420 on android devices. To convert the image stream data to a usable, consistent format, I'm using:
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:image/image.dart' as im;
Uint8List concatenatePlanes(List<Plane> planes) {
final WriteBuffer allBytes = WriteBuffer();
planes.forEach((Plane plane) => allBytes.putUint8List(plane.bytes));
return allBytes.done().buffer.asUint8List();
}
List<CameraDescription> cameras = await availableCameras();
CameraController controller = CameraController(cameras[0], ResolutionPreset.medium);
controller.startImageStream((CameraImage img) {
print(img.format.group); // returns ImageFormatGroup.bgra8888
List<int> imgData = concatenatePlanes(img.planes);
im.Image image = im.decodeImage(imgData);
});
The imgData variable is full of data streaming off the camera, but the converted image returned from decodeImage is null.
I had read on other posts that the image package would be up to the task of decoding bgra8888/yuv420 images (https://stackoverflow.com/a/57635827/479947) but I'm not seeing support in its formats.dart source (https://github.com/brendan-duncan/image/blob/master/lib/src/formats/formats.dart).
The target Image format is defined as:
An image buffer where pixels are encoded into 32-bit unsigned ints
(Uint32). Pixels are stored in 32-bit unsigned integers in #AARRGGBB
format. This is to be consistent with the Flutter image data.
How would I get my image stream image in bgra8888/yuv420 converted into the desired Image format?
Have you check this? convertImage
If you convert from bgra8888 to Image, it's kinda fast but yuv420 to Image take much more time, so if you have problem about performance, I can help a little bit with my experience. By the way, if you have performance problem and trying using Isolate, u will meet the memory issue.