Extreme Lag After Loading Palette from Image - flutter

I am currently making a wallpaper app in Flutter. I am using the palette_generator package to extract colors from the image. The problem is while the app is extracting colors from the image. The UI freezes for a solid 3 seconds, after which the colors are shown on the screen. After the colors have been rendered, the whole screen suffers from jank and lag. All animations on that screen perform very badly. Popping off that screen removes the jank and lag. Here is the code I am using:
Future<PaletteGenerator?> updatePalette() async {
var cache = DefaultCacheManager();
File file = await cache.getSingleFile(widget.wall.url);
ui.Image image = await load(file);
palette = await PaletteGenerator.fromImage(
image,
maximumColorCount: 7,
);
return palette;
}
Future<ui.Image> load(File file) async {
var data = await file.readAsBytes();
ui.Codec codec = await ui.instantiateImageCodec(data);
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
FutureBuilder<PaletteGenerator?>(
future: updatePalette(),
builder: (context, snapshot) {
if (snapshot.hasData)
return MyBottomSheet(
wall: widget.wall, palette: snapshot.data);
return LinearProgressIndicator();
}),
Is there anything I can do to fix this issue?

I recommend using the flutter_isolate package to implement Isolates. The function you are using is a heavy function. Using an Isolated function avoids putting too much pressure on the main thread.
Example code:
void updatePalette() async {
var cache = DefaultCacheManager();
File file = await cache.getSingleFile(url);
var port = ReceivePort();
var isolate = await FlutterIsolate.spawn(
computePalette,
[file.readAsBytesSync(), port.sendPort],
);
port.listen((msg) {
List<int> data = msg;
data.forEach((d) => colorList.add(Color(d)));
isolate.kill();
port.close();
});
}
void computePalette(List<Object> args) async {
var image = args[0] as Uint8List;
var port = args[1] as SendPort;
var img = Image.memory(image);
var palette = await PaletteGenerator.fromImageProvider(
img.image,
maximumColorCount: 7,
);
List<int> colors = [];
palette.colors.forEach((element) => colors.add(element.value));
port.send(colors);
}

Related

In Flutter, how to extract frame by frame from an animated gif?

I'm trying to extract frames from an animated gif using Flutter. By researching the Flutter API, I thought the following code should work but it only gives me the first frame although it gives me the correct frames count.
static Future<List<ui.Image>> loadAnimatedGif(String assetPath) async {
List<ui.Image> gifImage = [];
ByteData data = await rootBundle.load(assetPath);
var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
int frmCnt = codec.frameCount;
print("frmcnt is $frmCnt"); //it gives correct frames count
for (int i = 0; i < frmCnt; i++) {
var frame = await codec.getNextFrame();
gifImage.add(frame.Image);
}
return gifImage;
}
I made a simple app out of this and it works using the same code but added a converter in it.
What I notice at first because nothing
Using the ui.Image loads the frames but it delays for about 10secs before showing all of them using a breakpoint to debug it.
Converting ui.Image to Uint8List takes time as much as the frames but the format shows all frames as soon the extraction is done.
Here is the dart pad to quickly run and test it.
Here is the code if you will like to scan through it, I am loading the gif from Network.
class GifFramesDisplay extends StatefulWidget {
const GifFramesDisplay({super.key});
#override
State<GifFramesDisplay> createState() => _GifFramesDisplayState();
}
class _GifFramesDisplayState extends State<GifFramesDisplay> {
List<Uint8List> _frames = [];
#override
void initState() {
super.initState();
loadGif();
}
Future<void> loadGif() async {
// Load the gif image from the network url and store the bytes in a ByteData object
final url = Uri.parse(
'https://raw.githubusercontent.com/Aman-Malhotra/animate_icons/main/demo/animate_icons.gif');
final ByteData data = await NetworkAssetBundle(url).load(url.path);
// Using the _extractGifFrames function to extract the frames
_extractGifFrames(data).then((List<Uint8List> frames) {
// Set the state to update the UI
setState(() {
_frames = frames;
});
});
}
// Function to extract gif image frames
Future<List<Uint8List>> _extractGifFrames(ByteData data) async {
// Create a list to store the frames
final List<Uint8List> frames = <Uint8List>[];
// Create a codec to decode the gif
final ui.Codec codec =
await ui.instantiateImageCodec(data.buffer.asUint8List());
// Count the number of frames in the gif
final int frameCount = codec.frameCount;
print('Total frameCount: $frameCount');
// Loop through the frames and add them to the list
for (int i = 0; i < frameCount; i++) {
// Get the next frame
final ui.FrameInfo fi = await codec.getNextFrame();
// Add the frame to the list
final frame = await loadImage(fi.image);
// Add the frame to the list if it is not null
if (frame != null) {
frames.add(frame);
}
}
return frames;
}
Future<Uint8List?> loadImage(ui.Image image) async {
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return byteData?.buffer.asUint8List();
}
#override
Widget build(BuildContext context) {
return Center(
child: _frames.isEmpty
? const CircularProgressIndicator()
: ListView.builder(
itemCount: _frames.length,
itemBuilder: (BuildContext context, int index) {
return Image.memory(_frames[index]);
},
),
);
}
}
Click to watch a preview of the loaded gif frames of the code above

how to handle the time lag after capturing the image ImagePicker Flutter

im learning flutter and now tried to capture the photo with ImagePicker package from the following method, but after I successfully capture the photo, there is always 1 sec until app get the data and jump to next page:
Future pickImage(ImageSource source) async {
try {
var image = await ImagePicker().pickImage(source: source);
if (image == null) return;
final imagePermanent = await saveImagePermanently(image.path);
selectedImage = File(imagePermanent.path);
isUploaded.value = !isUploaded.value;
update();
} on PlatformException catch (e) {
print('Failed to pick image: $e');
}
}
Future<File> saveImagePermanently(String imagePath) async {
final directory = await getApplicationDocumentsDirectory();
final name = basename(imagePath);
final image = File('${directory.path}/$name');
return File(imagePath).copy(image.path);
}
now my solution is adding a listener onInit when image is created with GetX:
#override
void onInit() {
super.onInit();
ever(isUploaded, (value) {
Get.to(
() => AddPersonProfileAddDetails(),
);
});
}
So is there a way to detect the status of capturing the image like finished/failed/progressing, thanks for any clue or let me know a better way to jump to next page after capturing the image, thanks a lot!

Implementation of Fashion-Mnist Model with Flutter using tflite, Output error

So I followed a tutorial on creating a model for the Image Classification on Fashion-Mnist dataset and used the tflite-convertor to convert into a .tflite file that can be used.
I also followed a tutorial on how to use tflite with flutter but i am not getting the expected results.
I'm using the method here as :
loadModel() async {
await Tflite.loadModel(
model: 'assets/tf_lite_model.tflite',
labels: 'assets/labels.txt',
numThreads: 1);
}
classifyImage(File image) async {
var output = await Tflite.runModelOnImage(
path: image.path,
imageMean: 0.0,
imageStd: 255.0,
numResults: 1,
threshold: 0.2,
asynch: true);
setState(() {
_loading = false;
_outputs = output;
});
}
And to pick images I'm using the image picker package and the code goes like
pickImageFromCamera() async{
var image =
await ImagePicker.platform.pickImage(source: ImageSource.camera);
if (image == null) return null;
setState(() {
_loading = true;
_image = image as File;
});
classifyImage(_image!);
}
pickImage() async {
var image =
await ImagePicker.platform.pickImage(source: ImageSource.gallery);
if (image == null) return null;
setState(() {
_loading = true;
_image = image as File;
});
classifyImage(_image!);
}
The labels.txt file has outputs like
T-shirt/top
Trouser
Pullover
Dress
Coat
Sandal
Shirt
Sneaker
Bag
Ankle boot
Neither the image is getting selected nor the image is being shown.
Link to the code base: https://github.com/Vasudev-2308/tflite_with_flutter
Please feel free to make corrections in code and raise pull requests, If i get the desired output I will merge them.
Thanks in advance

Get multiple Image download URLs from Firebase Storage- I can successfully upload multiple images

I am trying to upload multiple images to firebase storage at once. I can upload successfully but cannot retrieve the download URLs;
Future<void> saveImages() async {
int index = images.length;
setState(() {
isLoading = true;
});
try {
final ref = FirebaseStorage.instance
.ref()
.child('images/${FirebaseAuth.instance.currentUser!.uid}');
images.forEach((element) async {
final file = await element.file;
final refPut = ref.child('$element$index');
await refPut.putFile(
File(file!.path),
);
});
final ref2 = ref.child('${imagePciker.path.split('/')}');
await ref2.putFile(File(imagePciker.path));
final ListResult listData = await ref.listAll();
final data =await ref2.getDownloadURL();
print('Im HERE!=Line 95');
Future.forEach(listData.items, (Reference element) async {
await element.getDownloadURL().then((value) => listUrls.add(value));
print('line 101');
print(listUrls.length);
});
print('Line 104');
await FirebaseFirestore.instance.collection('test').add({
'titleDownloadUrl': data,
'ListOfDownloadUrl': listUrls,
});
print('Line 108');
setState(() {
isLoading = false;
});
} catch (e) {
/* setState(() {
isLoading = false;
}); */
print(e);
}
}
The print statements are to debug. This whole function returns and no errors are thrown.
However in the firebase collection 'test' the URLs are not stored correctly.
On the first/fresh run after restart of the app.
the array of ListOfDownloadUrl is empty
On a hot restart(with the no images saved in firebase storage)
the array of ListOfDownloadUrl has one URL
then on multiple restarts, the amount of URLs saved becomes huge(ie. 4 images uploaded-10 URLs saved).
Please comment if I need to add anything else;
Thanks for any Help
.forEach() is NOT ASYNCHRONOUS - it WILL NOT wait. Use await Promise.all(images.map()) (thus creating an array of promises, which must all resolve).
Solved!
Following the thinking of #LeadDreamer
The forEach isn't ASYNCHRONOUS, the problem with his answer is that flutter does not have
Promise.all()
What it does have if Future.wait(), which acts(in my case) the same way.
Future<void> saveImages() async {
int index = images.length;
setState(() {
isLoading = true;
});
try {
final refMain = FirebaseStorage.instance
.ref()
.child('images${FirebaseAuth.instance.currentUser!.uid}');
AssetEntity eleId =
new AssetEntity(id: '', typeInt: 5, width: 5, height: 5);
await Future.wait(images.map((element) async { <-- here instead of the for each
setState(() {
eleId = element;
});
final file = await element.file;
final refPut = refMain.child('suportImage$_counter/$element$index');
await refPut.putFile(
File(file!.path),
);
}));
final ref2 =
refMain.child('headingImage$_counter/${imagePciker.path.split('/')}');
await ref2.putFile(File(imagePciker.path));
final ListResult listData =
await refMain.child('suportImage$_counter').listAll();
final data = await ref2.getDownloadURL();
await Future.wait(listData.items.map((e) async { <-- here instead of for each
await e.getDownloadURL().then((value) => listUrls.add(value));
}));

How to upload a video picked from user into firebase?

Im trying to upload a video into firebase .But theres a problem ,the more videos I upload, the more space a video needs. Im only uploading one video the same time . The video is picked from user . And when user picked the first video and when video duration is about 2 seconds long, it gets very fast uploaded. Then on the next also 2 or 3 seconds duration it takes a bit more time then the first but still ok . And then like on the 4 video ,its need very much time. And the video ist again 2 or 3 seconds recording but storage like 13 minutes . And im not getting why. This only is resolved when im using a new android emulator. it dont care when im deleting all data from firebase and like recording the first video into it .
Hope anyone can example why this error happen.
Heres my code
final allvideos = FirebaseStorage.instance.ref().child('allvideos');
final allimages = FirebaseStorage.instance.ref().child('allimages');
uploadVideo() async {
setState(() {
isuploading = true;
});
try {
var firebaseuseruid = FirebaseAuth.instance.currentUser.uid;
DocumentSnapshot userdoc = await FirebaseFirestore.instance
.collection('meinprofilsettings')
.doc(firebaseuseruid)
.get();
var alldocs = await FirebaseFirestore.instance.collection('videos').get();
int length = alldocs.docs.length;
String videourl = await uploadvideotostorage("Video $length");
String previewimage = await uploadimagetostorage("Video $length");
FirebaseFirestore.instance.collection('videos').doc("Video $length").set({
'username': userdoc.data()['username'],
'uid': firebaseuseruid,
'profilepic': userdoc.data()['url'],
'id':"Video $length",
'likes': [],
'commentcount': 0,
'sharecount': 0,
'hashtag1': hashtagcontroller.text,
'hashtag2': hashtagcontroller2.text,
'hashtag3': hashtagcontroller3.text,
'videourl': videourl,
'previewimage': previewimage,
'ratings': [],
});
Navigator.pop(context);
} catch (e) {
print(e.toString());
}
}
}
Heres how I upload it
the picture is for preview picture
getpreviewimage() async {
final previewimage = await flutterVideoCompress.getThumbnailWithFile(
widget.videopath_asstring,
);
return previewimage;
}
compressvideo() async {
if (widget.imageSource == ImageSource.gallery) {
return widget.videofile;
} else {
final compressvideo = await flutterVideoCompress.compressVideo(
widget.videopath_asstring,
quality: VideoQuality.MediumQuality);
return File(compressvideo.path);
}
}
uploadvideotostorage(String id) async {
final video = await allvideos.child(id).putFile(await compressvideo());
String url = await video.ref.getDownloadURL();
return url;
}
uploadimagetostorage(String id) async {
final video = await allimages.child(id).putFile(await getpreviewimage());
String url = await video.ref.getDownloadURL();
id=url;
return url;
}
Use something like this to generate your random ID:
import 'dart:math';
import 'package:intl/intl.dart';
String generateId() {
int randomNumber = Random().nextInt(9999999);
String now = DateTime.now().millisecondsSinceEpoch.toString().substring(7);
String formatted = DateFormat('MMdh').format(DateTime.now());
return randomNumber.toString() + formatted + now;
}
Change your upload function to look like this:
Future<String> uploadvideotostorage(String id) async {
final video = await allvideos.child(id).putFile(await compressvideo());
String url = await video.ref.getDownloadURL();
return url;
}
When you create a video, assign it a random id like this:
String randomlyGeneratedId = generateId();
Finally, to get your download URL back:
String finalVideoURL = await uploadvideotostorage(randomlyGeneratedId);