0

我有一个使用相机拍摄图像的应用程序。应用程序可以工作,但速度很慢,处理图像需要大约 10 秒。

我的意思是,旋转、创建缩略图并将它们都保存到闪存中。

应用程序基于 Cordova,但我没有参与其中。这是在旧型号 iPhone 3G 上运行的,但在新型号上尝试这个应用程序并没有产生更好的结果。

主要瓶颈是前面提到的 CGContextDrawImage 和 UIImageJPEGRepresentation 函数。

我不能将该代码分离到第二个线程中,因为结果必须尽快可见。

我搜索了两天,没有找到适用的解决方案。

我还发现了苹果的私有 API,但我不能使用它们。

这是时间关键代码的代码示例,如果您对如何加快速度有任何建议,那就太好了。

- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
    NSLog(@"TEST imagePickerController");
    NSDate* dateFunctionStart = [NSDate date];

    NSLog(@"respond...");
    if ([picker respondsToSelector:@selector(presentingViewController)]) {
        [[picker presentingViewController] dismissModalViewControllerAnimated:YES];
    } else {
        [[picker parentViewController] dismissModalViewControllerAnimated:YES];
    }
    NSLog(@"done");

    // get the image
    NSLog(@"get the image...");
    UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage];
    NSLog(@"done");
    NSLog(@"correct image for orientation...");
    image = [self imageCorrectedForCaptureOrientation:image]; // 3 sec
    //image = [image rotate:[image imageOrientation]]; // 1 - 6 sec
    NSLog(@"done");

    NSLog(@"make thumbnail...");
    CGSize thumbnailSize = CGSizeMake(200, 200);
    UIImage *thumbnailImage = [self imageByScalingNotCroppingForSize:image toSize:thumbnailSize];
    NSLog(@"done");

    NSLog(@"jpeg representation 1...");
    NSData* imageData = UIImageJPEGRepresentation(image, 1.0); // 4 sec
    NSLog(@"done");
    NSLog(@"jpeg representation 2...");
    NSData* thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 1.0);
    NSLog(@"done");

    NSLog(@"save image and thumbnail...");
    NSString* imageRet = [self saveData:imageData toFile:self.imageName];   
    NSString* thumbRet = [self saveData:thumbnailData toFile:self.thumbnailName];
    NSLog(@"done");

    NSLog(@"array two images...");
    //NSString *jsResult = [NSString stringWithFormat:@"{%@, %@}", imageRet, thumbRet];
    NSArray *jsResultA = [NSArray arrayWithObjects:imageRet, thumbRet, nil];
    NSLog(@"done");

    NSLog(@"plugin stuff...");
    CDVPluginResult* result = [CDVPluginResult resultWithStatus: CDVCommandStatus_OK messageAsArray:jsResultA];
    NSString* jsString = [result toSuccessCallbackString:self.callBackID];
    [self.webView stringByEvaluatingJavaScriptFromString:jsString];
    NSLog(@"done");

    timeFunc(dateFunctionStart, @"total time");
    NSLog(@"TEST END imagePickerController");

}

这是imageCorrectedForCaptureOrientation方法:

- (UIImage*) imageCorrectedForCaptureOrientation:(UIImage*)anImage
{
    NSLog(@"IMAGE CORRECTION IN");
    NSDate* start = nil;

    float rotation_radians = 0;
    bool perpendicular = false;

    switch ([anImage imageOrientation]) {
        case UIImageOrientationUp:
            rotation_radians = 0.0;
            break;
        case UIImageOrientationDown:
            rotation_radians = M_PI; 
            break;
        case UIImageOrientationRight:
            rotation_radians = M_PI_2;
            perpendicular = true;
            break;
        case UIImageOrientationLeft:
            rotation_radians = -M_PI_2;
            perpendicular = true;
            break;
        default:
            break;
    }

    UIGraphicsBeginImageContext(CGSizeMake(anImage.size.width, anImage.size.height));
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Rotate around the center point
    CGContextTranslateCTM(context, anImage.size.width/2, anImage.size.height/2);
    CGContextRotateCTM(context, rotation_radians);

    CGContextScaleCTM(context, 1.0, -1.0);
    float width = perpendicular ? anImage.size.height : anImage.size.width;
    float height = perpendicular ? anImage.size.width : anImage.size.height;
    CGContextDrawImage(context, CGRectMake(-width / 2, -height / 2, width, height), [anImage CGImage]);

    // Move the origin back since the rotation might've change it (if its 90 degrees)
    if (perpendicular) {
        CGContextTranslateCTM(context, -anImage.size.height/2, -anImage.size.width/2);
    }

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    NSLog(@"IMAGE CORRECTION OUT");

    return newImage;
}

我没有找到其他更好的方法来保存图像 NSData* imageData = UIImageJPEGRepresentation(image, 1.0); // 4 sec

普通相机应用程序如何工作得如此之快?它会在一秒钟内拍摄图像、旋转图像并将图像保存到闪存中。如何产生这种效果?

任何帮助表示赞赏。

编辑:没有解决方案。我会放一些自定义 UIView 来播放一些动画来通知用户正在发生的事情。由于图像很大,为 1.5 MB,因此这些操作必须持续一段时间。也许有解决方案可以加快拍摄图像、处理图像然后保存到磁盘的整个过程,但我不知道。

我决定使用https://github.com/matej/MBProgressHUD,因为它似乎是完整的解决方案。如果事实证明它很复杂,因为我是 Objective-c 菜鸟,我会放 UIProgressView 之类的。

无论如何,感谢所有人的帮助。

4

2 回答 2

2

我建议你使用 GCD 并在另一个线程中处理图像......每次调用 UIImageJpegRepresentation 确实会花费很多,因为它确实会将图像解码到设备的内存中......

- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{

NSLog(@"TEST imagePickerController");
NSDate* dateFunctionStart = [NSDate date];

NSLog(@"respond...");
if ([picker respondsToSelector:@selector(presentingViewController)]) {
    [[picker presentingViewController] dismissModalViewControllerAnimated:YES];
} else {
    [[picker parentViewController] dismissModalViewControllerAnimated:YES];
}
NSLog(@"done");

// get the image
NSLog(@"get the image...");
UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSLog(@"done");
NSLog(@"correct image for orientation...");

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^(void) {
    image = [self imageCorrectedForCaptureOrientation:image]; // 3 sec
    //image = [image rotate:[image imageOrientation]]; // 1 - 6 sec
    NSLog(@"done");

    NSLog(@"make thumbnail...");
    CGSize thumbnailSize = CGSizeMake(200, 200);
    UIImage *thumbnailImage = [self imageByScalingNotCroppingForSize:image toSize:thumbnailSize];
    NSLog(@"done");

    NSLog(@"jpeg representation 1...");
    NSData* imageData = UIImageJPEGRepresentation(image, 1.0); // 4 sec
    NSLog(@"done");
    NSLog(@"jpeg representation 2...");
    NSData* thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 1.0);
    NSLog(@"done");

    NSLog(@"save image and thumbnail...");
    NSString* imageRet = [self saveData:imageData toFile:self.imageName];
    NSString* thumbRet = [self saveData:thumbnailData toFile:self.thumbnailName];
    NSLog(@"done");

    NSLog(@"array two images...");
    //NSString *jsResult = [NSString stringWithFormat:@"{%@, %@}", imageRet, thumbRet];
    NSArray *jsResultA = [NSArray arrayWithObjects:imageRet, thumbRet, nil];
    NSLog(@"done");

    dispatch_async(dispatch_get_main_queue(),^ {
        NSLog(@"plugin stuff...");
        CDVPluginResult* result = [CDVPluginResult resultWithStatus: CDVCommandStatus_OK messageAsArray:jsResultA];
        NSString* jsString = [result toSuccessCallbackString:self.callBackID];
        [self.webView stringByEvaluatingJavaScriptFromString:jsString];
        NSLog(@"done");

        timeFunc(dateFunctionStart, @"total time");
        NSLog(@"TEST END imagePickerController");

    });

});

}

另一个想法是查看 Brad Larson 的 GPUImage 并使用它,但是请记住,无论在哪里、如何以及以何种方式,全图像大小的操作都是昂贵的。

最好的办法是只尽快准备您将在您的应用程序中立即使用的内容,并在后台队列中运行其他任务并在那里完成它们......

于 2012-09-18T09:10:32.870 回答
0

我没有过多地使用 CoreGraphics 来了解如何使其更高效,但可以肯定的是,iPhone 3G 并不是一个很好的性能基准模型。此外,图像的大小极大地影响了处理时间。

最后一件事,如果您愿意,您可以使用自定义 UIView 子类进行绘图。采用这种方法,可以将子类的 CALayer 改为 CATiledLayer,有效地在后台绘制。

于 2012-09-17T16:17:25.537 回答