4

将图片放入 SQLite 数据存储进行压缩的最快方法是什么,以便我可以将控制权交还给用户?

  • UIImagePickerController用来在我的应用程序中拍照。问题是使用图片的速度很慢,因为UIImageJPEGRepresentation.
  • 我想将 JPEG 压缩推入后台线程,但在尝试此操作之前,我需要让自己满意,我可以以一种可以在运行中存活的方式保存图片。这意味着 SQLite 中的 blob 或文件。据我所知,这让我马上回到了慢速图片编码。

我想要实现的是速度足够快,让用户感觉即时。

我应该如何处理这个?还有什么我应该知道的吗?

4

3 回答 3

4

根据评论和测试,这是我目前正在做的事情:

当我从 中获取图像时UIImageController,我将其保留在类 ivar 中并关闭图像选择器。我展示了一个阻止我的主视图的视图,并安排了一个 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

比使用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 回答