iPhone CGContextDrawImage和UIImageJPEGRepresentation大大减慢了应用程序的速度

时间:2012-09-17 14:25:02

标签: iphone objective-c xcode4.2 cordova

我有一个使用相机拍摄图像的应用程序。 应用程序有效,但速度很慢,需要大约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 noob,我会把UIProgressView或其他东西。

无论如何,感谢所有人试图提供帮助。

2 个答案:

答案 0 :(得分: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,但请记住,无论在何处,如何以及如何使用,完整图像大小的操作都将是昂贵的。

最好的想法是只准备ASAP,只在你的应用程序中立即使用,并在后台队列中运行其他任务并在那里完成...

答案 1 :(得分:0)

我还没有太多地使用CoreGraphics来了解如何提高效率,但有一点可以肯定的是,iPhone 3G不是一个基于性能的好模型。此外,图像的大小会极大地影响处理所需的时间。

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