4

圧縮のために画像を SQLite データ ストアに格納し、ユーザーに制御を返す最も速い方法は何ですか?

  • UIImagePickerControllerアプリケーションで写真を撮るために使用しています。問題は、の速度のために、画像の使用が非常に遅いことですUIImageJPEGRepresentation
  • JPEG 圧縮をバックグラウンド スレッドにプッシュしたいのですが、これを試す前に、複数の実行にわたって存続する方法で画像を保持できることを確認する必要があります。これは、SQLite の BLOB またはファイルのいずれかを意味します。私が知る限り、すぐに遅い画像のエンコードを行うことに戻ります。

私が達成したいのは、ユーザーがすぐに感じられるほど十分に速い速度です。

これをどのように処理すればよいですか?他に知っておくべきことはありますか?

4

3 に答える 3

4

コメントとテストに基づいて、私が現在行っていることは次のとおりです。

から画像を取得したら、UIImageControllerそれをクラス ivar に保持し、画像ピッカーを閉じます。メイン ビューをブロックするビューを表示し、NSTimer イベントをスケジュールして 1 秒で圧縮を実行してから、呼び出し元に戻ります。

これにより、イメージ コントローラーを閉じるアニメーションが実行されます。その下に私のブロッカー ビューが表示されます。

(ブロッカー ビューは、ナビゲーション コントローラーのコンテンツ領域全体を埋め尽くし、黒一色で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;
}

これにより、処理に 1 秒かかりますが、イメージ ピッカーの処理が速くなるため、アプリケーションがフリーズしたように感じることはなくなりました。のアニメーションUIActivityIndicatorViewは動作中に実行されUIImageJPEGRepresentationます。

NSTimer1 秒の遅延を使用するよりも良い答えは、アニメーションがdismissModalViewControllerAnimated:終了したときにイベントを取得することですが、これを行う方法がわかりません。

(これはまだ解決されていないと思います。)

于 2009-10-03T01:01:07.830 に答える
2

サイズが非常に小さい場合を除き、画像をデータベースに保存しないでください。画像が十分に小さいかどうかを判断するしきい値は、もちろん非常に主観的なものです。私の謙虚な意見 (および iPhone での経験) では、1 メガバイトを超えるべきではありません。したがって、アイコンやサムネイルなどの小さいサイズの画像のみをデータベースに保存する必要があります。1 メガバイトを超える画像の場合は、それらをファイルとしてファイル システムに保存し、ファイル名 (画像パス) をデータベースに配置する必要があります。ところで、イメージをファイルシステムに保存し、そのパス名をデータベースに保存するのは非常に高速です。

圧縮について: 確かに別のスレッドを使用して画像を圧縮できますが、これを行う価値があるかどうかを検討してください。スレッドを使用して画像をファイルに保存し、パス名をデータベースに保存して、すぐにコントロールをユーザーに返すことができます。(通常は) 十分なスペースがありますが、最新の iPhone 3GS でも計算能力は非常に小さくなります。また、UIImageViewを介して圧縮画像をロードするのに、PNGなどの非圧縮画像よりも時間がかかるかどうかを確認する必要があります(私は本当に知りません)。圧縮された画像をロードするときにアプリケーションで追加のオーバーヘッドが発生する場合、画像を圧縮する価値はまったくありません。これは基本的に、スペースと速度のトレードオフです。これが決定に役立つことを願っています。

于 2009-10-01T17:56:58.167 に答える
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 メガピクセルの画像を選択すると、UI がブロックされるのはわずか 0.015 秒です。

一方、画像をロードすると、かなりの時間 UI もブロックされるため、バックグラウンドでロードすることもできます。

于 2012-12-31T15:46:01.150 に答える