4

將圖片存入SQLite數據存儲庫進行壓縮的最快方式是什麼,因此我可以將控制權交還給用戶?處理UIImagePickerController壓縮的最快方法

  • 我使用UIImagePickerController在我的應用程序中拍照。問題是使用圖片速度很慢,因爲UIImageJPEGRepresentation的速度。
  • 我想推JPEG壓縮到後臺線程,但在嘗試之前,我需要滿足自己,我可以堅持圖片的方式,將跨越運行生存。這意味着在SQLite或文件中的blob。據我所知,我馬上回到做慢速圖像編碼馬上。

我想要達到的速度足夠快,讓用戶感受到即時。

我應該如何處理被這個?還有什麼我應該知道的嗎?

回答

4

基於意見和測試,這裏是目前我在做什麼:

當我從UIImageController的形象,我保留它在一個類伊娃和罷免影像選擇器。我顯示一個視圖,阻止我的主視圖,並安排NSTimer事件在一秒鐘內完成壓縮,然後返回給調用者。

這可以讓運行的動畫消除圖像控制器。我的攔截器視圖顯示在其下。

(阻斷劑視圖填充導航控制器的整個內容區域,並且是實心黑色用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]; 
} 

當計時器觸發,我壓縮使用JPEG(圖像,因爲它的速度比PNG,儘管直覺)並消除阻擋視圖。

- (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; 
} 

雖然這增加了第二個做的處理,它得到的圖像拾取出的更快,使之不再感覺就像我的應用程序已經凍結的方式。 UIActivityIndicatorView的動畫確實運行,而UIImageJPEGRepresentation正在運行。

比使用NSTimer 1秒延遲更好的答案是從dismissModalViewControllerAnimated:完成動畫時得到一個事件,但我不知道如何做到這一點。

(我不認爲這是解決目前)

+2

而不是使用計時器,在選取器被解散後顯示的視圖的viewDidAppear方法中執行此操作。 – Shizam 2010-05-17 18:04:21

+2

從iOS 5開始,您可以使用 - (void)dismissViewControllerAnimated:(BOOL)標誌完成的完成塊:(void(^)(void))completion;歸檔相同。 – Klaas 2012-07-31 20:59:21

+0

我最近爲此已經在使用'dispatch_async'。我應該更新我的答案。 – 2012-07-31 21:38:49

2

除非尺寸非常小,否則不應將圖片保存在數據庫中。確定圖片是否足夠小的閾值當然是非常主觀的。根據我的愚見(和iPhone上的經驗),它不應該超過1兆字節。因此,您應該只在數據庫中保存小尺寸的圖像,如圖標,縮略圖等。對於超過一兆字節的圖像,您應該將它們簡單地存儲爲文件系統中的文件,並將文件名(圖像路徑)存儲在數據庫中。順便說一句,將圖像存儲在數據庫中的文件系統及其路徑名上非常快。

關於壓縮:你當然可以使用另一個線程壓縮圖像,但要考慮是否真的值得這樣做。您可以使用線程將圖像保存到文件,將路徑名保存在數據庫中,並立即將控件返回給用戶。你(通常)有足夠的空間,但是計算能力非常小,即使是最新的iPhone 3GS。此外,你應該驗證(我真的不知道這是否)通過UIImageView加載壓縮圖像需要更多的時間w.r.t.一個非壓縮的例如PNG。如果您的應用程序在加載壓縮圖像時會產生額外開銷,那麼它絕對不值得壓縮圖像。這基本上是空間和速度之間的折衷。希望這有助於決定。

+0

由於圖片將被上傳,它將成爲空間,速度和帶寬之間的折衷......如果我不壓縮照片,我需要服務器人員能夠解釋圖像服務器。 我們已經完成了過去SQLite blob速度的測試,所以我並不太擔心文件與blob的選擇。儘管我可能會再次運行它們。 感謝您的有益迴應。從它,我收集我應該看看從UIImagePicker拉未壓縮的數據? – 2009-10-01 19:44:06

+0

您將不得不使用PNG或JPG表示方法來獲取NSData,無論如何您都可以寫入文件。你可以測試PNG是否更快,因爲壓縮比較少,並且有可能內部表示是PNG ... – 2009-10-01 23:51:40

+0

正如Kendall指出的那樣,我認爲PNG會更快。試一試。 – 2009-10-02 07:29:53

0

使用的是iOS 5的父母,子女託管對象上下文:

我已經安排順序我的管理對象上下文:

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) 

該模型看起來像:

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

關係反向設置。

一旦用戶完成採摘的圖像:

- (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]; 
} 
} 

,這是儲蓄是如何完成的:

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

這樣就大大減少UI堵塞,在iPhone 4,選擇5百萬像素圖像將阻止用戶界面僅0.015秒。另一方面,加載圖像也會在明顯的時間阻止UI,因此您可能也會在後臺加載該圖像。