The fastest way to handle UIImagePickerController compression

What is the fastest way to get an image in SQLite data warehouse for compression so that I can return control to the user?

  • I am using UIImagePickerController to shoot in my application. The problem is using the image rather slowly, due to the speed of the UIImageJPEGRepresentation .
  • I want JPEG compression in the background stream, but before I try this, I need to make sure that I can save the image in such a way as to survive in all runs. This means either blob in SQLite or a file. That, as far as I can tell, will bring me back to slow image encoding right away.

What I want to achieve is speed fast enough for it to instantly feel the user.

How can I handle this? Is there anything else I should know?

+4
source share
3 answers

Based on the comments and tests, here's what I'm doing right now:

When I get the image from the UIImageController , I save it in the ivar class and reject it. I show a view that locks my main view and assigns an NSTimer event to compress per second, and then returns to the caller.

This allows you to run an animation that rejects the image controller. My lock window is shown below it.

(The blocker image fills the entire content area of ​​the navigation controller and is solid black with the UIActivityIndicatorView .)

 - (void)imagePickerController: (UIImagePickerController *)picker didFinishPickingImage: (UIImage *)selectedImage editingInfo: (NSDictionary *)editingInfo; { busyView.userInteractionEnabled = YES; busyView.alpha = 0.7f; mainView.userInteractionEnabled = NO; [self dismissModalViewControllerAnimated: YES]; [NSTimer scheduledTimerWithTimeInterval: 1.0f target: self selector: @selector(compress:) userInfo: selectedImage repeats: NO]; } 

When the timer fires, I compress the image using JPEG (because it is faster than PNG, despite my intuition) and disappears from the view of the blocker.

 - (void)compress: (NSTimer *)inTimer; { [self gotJPEG: UIImageJPEGRepresentation( inTimer.userInfo, 0.5f )]; [UIView beginAnimations: @"PostCompressFade" context: nil]; [UIView setAnimationDuration: 0.5]; busyView.userInteractionEnabled = NO; busyView.alpha = 0.0f; [UIView commitAnimations]; mainView.userInteractionEnabled = YES; } 

Despite the fact that this adds a second processing process, it speeds up the image picker, so it no longer feels that my application has frozen. Animations from the UIActivityIndicatorView executed while the UIImageJPEGRepresentation .

A better answer than using NSTimer with a 1 second delay is to get an event when the animation from dismissModalViewControllerAnimated: completes, but I'm not sure how to do it.

(I do not consider this resolved yet.)

+4
source

You should not save the image in the database if it is not very small. The threshold that determines whether a picture is sufficient is, of course, very subjective. In my humble opinion (and experience on the iPhone), it should not exceed one megabyte. Therefore, you should only save database images such as icons, thumbnails, etc. in small sizes. For images larger than one megabyte, you just need to store them as files in the file system and put the file name (image path) in the database. By the way, storing an image in the file system and its path name in the database is very fast.

About compression: you can, of course, compress the image using a different thread, but think about whether it is really worth it. You can use the stream to save the image to a file, save the path name in the database, and immediately return the control to your user. You (usually) have a lot of space, but very little processing power, even on the latest iPhone 3GS. In addition, you should check (I really don’t know this) whether to load the compressed image through UIImageView more than wrt uncompressed time, such as PNG. If your application incurs additional overhead when downloading a compressed image, it may not be worth compressing your images. This is basically a compromise between space and speed. Hope this helps solve.

+2
source

Using ios 5 parent, child managed entity:

I have managed object contexts arranged in the following order:

 persistent store coordinator ---> Private Queue Managed Object Context ( for saving to disk in background) -----> Main Queue Managed Object Context (for UI) -----> Misc. Private Managed Object Contexts (for temporary jobs like UIImagePNGRepresentation() for example) 

The model looks like this:

 Image Entity -> title : string , image : relationship(ImageBlob) optional ImageBlob Entity -> image : Binary Data, imageEntity : relationship(Image) 

reverse relationships established.

as soon as the user completes the image selection:

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // get the main queue managed object context NSManagedObjectContext* mainQueueManagedObjectContext = self.managedObjectContext; // get the image UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage]; // create an object, using the managed object context for the main queue NSManagedObject *newImage = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:mainQueueManagedObjectContext]; // edit not expensive properties [newImage setValue:[NSString stringWithFormat:@"new title %i", [self tableView:self.tableView numberOfRowsInSection:0]] forKey:@"title"]; // lets save the main context to get a permanant objectID [self saveContextForManagedObjectContext:mainQueueManagedObjectContext]; // get the permenant objectID, Thread Safe.. NSManagedObjectID* imageObjectID = newImage.objectID; // create a private queue concurrent managed object context NSManagedObjectContext* privateQueueManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; // set the main queue as the parent [privateQueueManagedObjectContext setParentContext:mainQueueManagedObjectContext]; // we have to use blocks here, as this managed object context will work in a private queue [privateQueueManagedObjectContext performBlock: ^{ // get the png representation in background NSData* data = UIImagePNGRepresentation(image); // get the managed object using the thread safe objectID NSManagedObject* imageObjectInPrivateQueue = [privateQueueManagedObjectContext objectWithID:imageObjectID]; // insert a new object for the ImageBlob entity NSManagedObject *imageBlobInPrivateQueue = [NSEntityDescription insertNewObjectForEntityForName:@"ImageBlob" inManagedObjectContext:privateQueueManagedObjectContext]; // set our image data [imageBlobInPrivateQueue setValue:data forKey:@"image"]; // set the relationship to the original record [imageObjectInPrivateQueue setValue:imageBlobInPrivateQueue forKey:@"image"]; // save changes to private queue context to main queue context [self saveContextForManagedObjectContext:privateQueueManagedObjectContext]; // since we are not in the main queue, we have to ask the main managed object context using performBlock [mainQueueManagedObjectContext performBlock: ^{ // what time is it before launching save in main queue NSDate* startDate = [NSDate date]; // launch save on main queue [self saveContextForManagedObjectContext:mainQueueManagedObjectContext]; // what time is it after finishing save in main queue NSDate* finishDate = [NSDate date]; // see how long UI blocked NSLog(@"blocked UI for %f seconds", [finishDate timeIntervalSinceDate:startDate]); }]; }]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { [self.popOverController dismissPopoverAnimated:YES]; } else { [self dismissViewControllerAnimated:YES completion:nil]; } } 

and here is how to do it:

 -(void)saveContextForManagedObjectContext:(NSManagedObjectContext*)managedObjectContext { // Save the context. NSError *error = nil; if (![managedObjectContext save:&error]) { // Replace this implementation with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } 

This greatly reduces the user interface lock, on the iphone 4, choosing a 5-megapixel image blocks the user interface in just 0.015 seconds.

on the other hand, downloading an image will also block the user interface for a noticeable amount of time, so you can also download it in the background.

0
source

All Articles