Flutter Image Compression - image

I wanted to do some image compression before uploading to my Firestore and ran into following thread: Flutter & Firebase: Compression before upload image
The uploading works totally fine, but I cannot recognize any compression regarding the file size... Even if I decrease the quality from 85 to 1, the file still has the same size. Same thing if I upload the image without calling the compression method at all. Here is the code snippet:
void compressImage() async {
print('starting compression');
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
int rand = new Math.Random().nextInt(10000);
Im.Image image = Im.decodeImage(file.readAsBytesSync());
Im.copyResize(image, 500);
// image.format = Im.Image.RGBA;
// Im.Image newim = Im.remapColors(image, alpha: Im.LUMINANCE);
Im.Image smallerImage = Im.copyResize(image, 500); // choose the size here, it will maintain aspect ratio
var newim2 = new File('$path/img_$rand.jpg')
..writeAsBytesSync(Im.encodeJpg(smallerImage, quality: 85));
setState(() {
file = newim2;
});
print('done');
}
Any idea what I have to change, to make the compression working?
Best regards!

you can use below plugin, same working fine without any issue, even faster and what i expected
https://github.com/btastic/flutter_native_image.git
steps and method available in above link.

Related

How to detect if image is all black using flutter?

I'm building an app that user required to send a selfie but some user just block the camera to take a picture and the result is all black, is there a way to detect if image is black?
I'm thinking of using face detection but I haven't tried it and its way to much for my simple app.
One way you can try is using palette_generator package to extract colors in the image and then computing average luminance of the image, like so.
import 'package:palette_generator/palette_generator.dart';
Future<bool> isImageBlack(ImageProvider image) async {
final double threshold = 0.2; //<-- play around with different images and set appropriate threshold
final double imageLuminance = await getAvgLuminance(image);
print(imageLuminance);
return imageLuminance < threshold;
}
Future<double> getAvgLuminance(ImageProvider image) async {
final List<Color> colors = await getImagePalette(image);
double totalLuminance = 0;
colors.forEach((color) => totalLuminance += color.computeLuminance());
return totalLuminance / colors.length;
}
Before use camera check if your app has permission for that.
For this porpouse i've recommend to use the permission handler
Few lines from official documentation
var status = await Permission.camera.status;
if (status.denied) {
// We didn't ask for permission yet or the permission has been denied before but not permanently.
}

High quality book covers using Google Books API

According to the Google Books API documentation each volume yields 6 different imageLinks (smallThumbnail, thumbnail, small, medium, large, extraLarge).
Unfortunately, for all the queries I've tried (and I've tried a lot) only smallThumbnail and thumbnail were returned. (Example query)
Also, apart from being fairly small those two images have this little fake dog-ears in the bottom right corner
Did they deprecate the high quality image links? Is there another way to fetch those images? Other APIs I tried are either deprecated (Goodreads) or less extensive (Open Library)
I'm having the same issue. The search query only yields the smallThumbnail and thumbnail keys for the imageLinks. If you query the volume directly (like this), then you'll get all the options for the imageLinks.
This won't 100% solve your problem though. For some of the books, the small/medium/large links point to the first page of the book instead of the actual cover.
You might not be able to get high-resolution images all the time. Also, for some book results, you might not get any images at all.
I am working on a similar project and I had to manually check the resolution of each of the images I got in the response and I chose only the 'good' ones.
Here's what I had to do:
let newArr = [];
let lowResImage = 0;
let dataWithoutImage = 0;
let highResImage = 0;
genreResults?.forEach((book, index) => {
if (book?.volumeInfo?.imageLinks?.smallThumbnail) {//the current book has an image
mypromise(book.volumeInfo.imageLinks.smallThumbnail).then((res) => {
++highResImage;
newArr.push(book)
}).catch(error => {
++lowResImage;
}).finally(() => {
if (dataWithoutImage + highResImage + lowResImage === genreResults.length) {
console.log("---------data populated----------");
console.log("highResolutionImages", highResImage);
console.log("low resolution images:", lowResImage);
console.log("booksWithoutImages", dataWithoutImage);
console.log("genreResults size", genreResults.length)
setBooks(newArr)
}
});
}
else //this book does not contain an image
++dataWithoutImage
})
The function to check the resolution looks like this:
let mypromise = function checkImageResolution(url) {
return new Promise((resolve, reject) => {
var img = new Image();
img.src = url;
img.onload = function () {
console.log("image dimensions", this.width, this.height);
if (this.width < 125 || this.height < 100) //if either of these is true, reject the image
reject("low quality image");
else
resolve("high quality ,let's have this one");
}
});
}
Replace the following:
thumbnail=thumbnail.replaceAll("1","10");
But it works only for some books and for the remaining it will only load the first page of the book.

ABCPdf - Image not a suitable format

In the end, my goal is to send a raw image data from the front-end, then split that image into however many pages, and lastly send that pdf back to the front-end for download.
But every time I use the theDoc.addImageFile(), it tells me that the "Image is not in a suitable format". I'm using this as reference: https://www.websupergoo.com/helppdfnet/source/5-abcpdf/doc/1-methods/addimagefile.htm
To troubleshoot, I thought that the image might not be rendering correctly, so I added a File.WriteAllBytes to view the rendered image and it was exactly what I wanted, but still not adding to the PDF. I also tried sending the actual path of a previously rendered image thinking that the new image might not have been fully created yet, but it also gave me the same error. Lastly, I thought PNGs might be problematic and changed to JPG but it did not work.
Here is the code:
[HttpPost]
public IActionResult PrintToPDF(string imageString)
{
// Converts dataUri to bytes
var base64Data = Regex.Match(imageString, #"data:image/(?<type>.+?),(?<data>.+)").Groups["data"].Value;
var binData = Convert.FromBase64String(base64Data);
/* Ultimately will be removed, but used for debugging image */
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string imgName= "Test.jpg";
string filename = Path.Combine(path, imgName);
System.IO.File.WriteAllBytes(filename, binData);
/***********************************************************/
using (Doc theDoc = new Doc())
{
// Using explicit path
theDoc.AddImageFile(#"C:\Users\User\Documents\Test.jpg", 1);
// Using variable
//theDoc.AddImageFile(filename, 1);
// What I really want
//theDoc.AddImageFile(binData , 1);
theDoc.Page = theDoc.AddPage();
theDoc.AddText("Thanks");
Response.Headers.Clear();
Response.Headers.Add("content-disposition", "attachment; filename=test.pdf");
return new FileStreamResult(theDoc.GetStream(), "application/pdf");
}
}
Try something like this (not tested, but cleaned up from my own code):
public int AddImageFile(Doc doc, byte[] data, int insertBeforePageID)
{
int pageid;
using (var img = new XImage())
{
img.SetData(data);
doc.Page = doc.AddPage(insertBeforePageID);
pageid = doc.Page;
doc.AddImage(img);
img.Clear();
}
return pageid;
}
To add a JPEG from a byte array you need Doc.AddImageData instead of Doc.AddImageFile. Note that AddImageFile / AddImageData do not support PNG - for that you would definitely need to use an XImage. The XImage.SetData documentation has the currently supported image formats.

XAML Image source has issues displaying a deep nested path

This is quite vexing.
I am working on an app for image management. Part of the value is the ability to store images in sub-folders based on image properties, eg. creation date.
If I store the image source in a shallow folder (app\images\img.jpg), everything works fine.
If I store the image in KnownFolders.Pictures\source\year\month\day\img.jpg, Image does not render. (Yes, that specific path won't work, I am trying to give you a sense of how the path is constructed)...
The file is actually there. The path is correct (I can open it in a browser, e.g.). The app has access to the file.
But it does not render the bitmap.
I tried to render the bitmap manually using
new BitmapImage(new Uri("KnownFolders.Pictures\source\year\month\day\img.jpg"),UriKind.Absolute))
That does not render anything. (Again, assume the path is valid and has a file at its bottom).
What Am I Missing?
The head scratcher: for GIF anims, I am using Thomas Levesque's useful component: https://github.com/XamlAnimatedGif. That one, unfortunately, does only render gifs... and it does so even when the path is the one given above. So the Standard IMAGE control does not render correctly, but Thomas's control does... infuriating.
An UWP app can't load a BitmapImage from an absolute URL to a file in a folder structure below the Pictures Library Folder.
So this won't work:
var relativePath = #"source\year\month\day\img.jpg";
var imageFile = await KnownFolders.PicturesLibrary.GetFileAsync(relativePath);
var bitmapImage = new BitmapImage(new Uri(imageFile.Path));
However, you could do this:
var relativePath= #"source\year\month\day\img.jpg";
var imageFile = await KnownFolders.PicturesLibrary.GetFileAsync(relativePath);
var bitmapImage = new BitmapImage();
using (var stream = await imageFile.OpenAsync(FileAccessMode.Read))
{
await bitmapImage.SetSourceAsync(stream);
}
So, after way too much time spent on this...
First, link to DataContextChanged of the IMAGE element. In there, parse the DataContext out. If you are using the IMAGE outside of an ItemsControl etc, this is not required...
private async void ImageView_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
if (sender is Image)
{
Image img = (Image)sender;
if (img.DataContext is ImageView)
{
MyViewDataContext dc = (MyViewDataContext)img.DataContext;
img.Source = await dc.bitmap();
}
}
}
And here the implementation of MyViewDataContext.bitmap() which has a property called source that yields, you guessed it, absolute paths:
public async Task<BitmapImage> MyViewDataContext.bitmap()
{
if (_bitmap == null)
{
try
{
StorageFile file = await StorageFile.GetFileFromPathAsync(source);
bool r = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.CheckAccess(file);
if (r)
{
using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// create a new bitmap, coz the old one must be done for...
_bitmap = new BitmapImage();
// And get that bitmap sucked in from stream.
await _bitmap.SetSourceAsync(fileStream);
}
}
}
catch (Exception e)
{
_bitmap = null;
}
}
return _bitmap;
}
BitmapImage _bitmap;
I cache the resulting bitmap until I dispose of this MyViewDataContext.
I am now most concerned about memory. This one worries me:
How to dispose BitmapImage cache?
So, as a tech debt, I am going to address the potential mem leaks later, once this whole thing is on the test bench and I can take a look at its runtime behavior...
To access the folders and libraries represented by the properties of this class, specify the corresponding capabilities in your app manifest. For example, to access KnownFolders.PicturesLibrary, specify the Pictures Library capability in the app manifest.
Hope this will help
KnowFolders

How to dynamically load images in a layer using actionscript

I used the below code to load images that are present in my library:
Create_Image_With_Description();
function Create_Image_With_Description():Void {
this.createEmptyMovieClip("imageHolder", 600);
// Put Here Images URoL
imageHolder.loadMovie("Symbol 1");
// Position
imageHolder._x = 0;
imageHolder._y = 0;
// Size
imageHolder._xscale = 210;
imageHolder._yscale = 215;
// Example: my_mc.createTextField(instanceName:String, depth:Number, x:Number,
y:Number,width:Number, height:Number) : Void
this.createTextField("description1Field",1,10,203,120,20);
// Put Here Images description
description1Field.text = "Resident Evil: Afterlife";
}
Here symbol 1 is the image that i've converted into a symbol. When i tried running this i got an error saying "Error opening URL 'file:///E|/Symbol 1'".
I've also tried giving imageHolder.loadMovie("img1.jpg"); But still it didn't worked. Can anyone help me with this
Try replacing these lines:
this.createEmptyMovieClip("imageHolder", 1000);
// Put Here Images URL
this.attachMovie("img1", "img1", {_x:100, _y:100});

Resources