【问题标题】:CGImageSourceCreateThumbnailAtIndex crashes with 20MB ImageCGImageSourceCreateThumbnailAtIndex 与 20MB 图像崩溃
【发布时间】:2013-09-12 17:42:30
【问题描述】:

当我使用大图像(即 > 10 MB)进行测试时,以下用于在 iPad 上创建缩略图的方法崩溃。我已经对其进行了分析,Allocations 没有报告任何大的内存峰值 - 在操作期间它始终保持在 5 MB 的活动内存。

如何为这么大的图像创建缩略图?我尝试使用 Core Graphics 对其进行缩放,但内存效率较低且不起作用。

+(UIImage*) thumbnailImageAtPath:(NSString*) path withSize:(CGSize) size{

    @autoreleasepool{

        CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:path], NULL);

        if(!src){
            return nil;
        }

        NSDictionary* options = @{
                                  (id)kCGImageSourceShouldAllowFloat : (id)kCFBooleanTrue,
                                  (id)kCGImageSourceCreateThumbnailWithTransform : (id)kCFBooleanFalse,
                                  (id)kCGImageSourceCreateThumbnailFromImageIfAbsent : (id)kCFBooleanTrue,
                                  (id)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithDouble:1024]
                                  };

        CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, (__bridge CFDictionaryRef)options);

        // Doesn't reach here :(

        UIImage* img = [[UIImage alloc] initWithCGImage:thumbnail];

        NSLog(@"Size: %f, %f", size.width, size.height);

        CGImageRelease(thumbnail);
        CFRelease(src);

        return img;

    }

}

我已经在主线程、工作线程、并发、非并发等上进行了尝试 - 它似乎无法在实际设备上运行。

同样奇怪的是,它可以出色地处理大于 60 MB 的 PDF。

【问题讨论】:

  • 几个问题 1) 究竟是什么崩溃? 2)你可以托管有问题的图像吗?我手头没有任何非常大的图像 3) 什么设备和 iOS 版本?谢谢!
  • 1) 它只是停止 - 没有内存警告或异常 2) 任何图像似乎在 10MB 的访问中都会出现问题.. 试试这个cdn.hdwallpaperspics.com/uploads/2012/11/iceHD_hires.jpg 3) iPad mini iOS 6,我应该注意我只在此设备和模拟器上尝试过,因为这是应用程序将在其上运行的最低规格设备
  • 在装有 iOS 6.1.3 的 iPad Mini 上运行此代码,使用该图像,工作正常。我传递了{100,100} 的大小。你能解释更多关于上下文的信息吗?您还可以通过“停止”来解释更多吗?会挂吗?是否产生任何崩溃日志?编辑:我应该注意到墙纸只有 10 兆。我正在寻找一个更大的,但如果你能主持一个,那将是一个帮助。谢谢
  • 抱歉,再次更新 - 我确实在 wikipedia 上找到了 18 兆字节的 jpg。到目前为止,通过大小为 100*100、40*40 和 20*20 的速度似乎较慢,但没有崩溃或挂起。当然,现在我仔细查看代码,它只记录大小。我认为您的问题中缺少一些有助于重现问题的上下文。
  • 没有日志,没有挂起,什么都没有。它只是停止。我已经对其进行了分析,并且 Instruments 中没有异常的内存峰值。我已经通过了 200x200 和 100x100 的尺寸,并且两者都崩溃了。就上下文而言:它是一个 PNG 文件(这会有所不同吗?),并且在操作中调用“缩略图创建”方法并在非并发后台队列中运行。我必须这样做,因为如果我在主线程上生成缩略图,UI 会挂起。谢谢你的帮助顺便说一句:)

标签: ios objective-c image


【解决方案1】:

尝试删除选项 kCGImageSourceThumbnailMaxPixelSize... 如果没有,请询​​问我。

编辑: 试试这个代码:

+ (UIImage*)scaleAndRotateImage:(UIImage *)image{

int kMaxResolution = 1024; // Or whatever 320

CGImageRef imgRef = image.CGImage;

CGFloat width;
CGFloat height;

width = CGImageGetWidth(imgRef);
height = CGImageGetHeight(imgRef);

CGAffineTransform transform = CGAffineTransformIdentity;
CGRect bounds = CGRectMake(0, 0, width, height);
if (width > kMaxResolution || height > kMaxResolution) {
    CGFloat ratio = width/height;
    if (ratio > 1) {
        bounds.size.width = kMaxResolution;
        bounds.size.height = bounds.size.width / ratio;
    }
    else {
        bounds.size.height = kMaxResolution;
        bounds.size.width = bounds.size.height * ratio;
    }
}

CGFloat scaleRatio = bounds.size.width / width;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
CGFloat boundHeight;
UIImageOrientation orient = image.imageOrientation;
switch(orient) {

    case UIImageOrientationUp: //EXIF = 1
        transform = CGAffineTransformIdentity;
        break;

    case UIImageOrientationUpMirrored: //EXIF = 2
        transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
        transform = CGAffineTransformScale(transform, -1.0, 1.0);
        break;

    case UIImageOrientationDown: //EXIF = 3
        transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
        transform = CGAffineTransformRotate(transform, M_PI);
        break;

    case UIImageOrientationDownMirrored: //EXIF = 4
        transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
        transform = CGAffineTransformScale(transform, 1.0, -1.0);
        break;

    case UIImageOrientationLeftMirrored: //EXIF = 5
        boundHeight = bounds.size.height;
        bounds.size.height = bounds.size.width;
        bounds.size.width = boundHeight;
        transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
        transform = CGAffineTransformScale(transform, -1.0, 1.0);
        transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
        break;

    case UIImageOrientationLeft: //EXIF = 6
        boundHeight = bounds.size.height;
        bounds.size.height = bounds.size.width;
        bounds.size.width = boundHeight;
        transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
        transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
        break;

    case UIImageOrientationRightMirrored: //EXIF = 7
        boundHeight = bounds.size.height;
        bounds.size.height = bounds.size.width;
        bounds.size.width = boundHeight;
        transform = CGAffineTransformMakeScale(-1.0, 1.0);
        transform = CGAffineTransformRotate(transform, M_PI / 2.0);
        break;

    case UIImageOrientationRight: //EXIF = 8
        boundHeight = bounds.size.height;
        bounds.size.height = bounds.size.width;
        bounds.size.width = boundHeight;
        transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
        transform = CGAffineTransformRotate(transform, M_PI / 2.0);
        break;

    default:
        [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

}

UIGraphicsBeginImageContext(bounds.size);

CGContextRef context = UIGraphicsGetCurrentContext();

if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
    CGContextScaleCTM(context, -scaleRatio, scaleRatio);
    CGContextTranslateCTM(context, -height, 0);
}
else {
    CGContextScaleCTM(context, scaleRatio, -scaleRatio);
    CGContextTranslateCTM(context, 0, -height);
}

CGContextConcatCTM(context, transform);

CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return imageCopy;}

【讨论】:

  • 这不起作用 - 它继续崩溃并为所有其他加载的缩略图吐出内存警告。
【解决方案2】:

认为我知道问题出在哪里。

我使用的 png 有很多 alpha 透明像素。

不过,我将不得不对不同的 alpha/non-alpha 图像进行一些明确的测试,以确认这是问题所在。当我有机会时会这样做。

【讨论】:

    【解决方案3】:
    NSString *path = [[NSBundle mainBundle] pathForResource:@"LARGE_elevation" ofType:@"jpg"];
    
    UIImage* image = [UIImage imageWithContentsOfFile:path];
    image = [self scaleImage:image toSize:CGSizeMake(400, 200)];
    
    - (UIImage *)scaleImage:(UIImage *)image
                            toSize:(CGSize)size
    {
        CGSize sz = image.size;
        if(sz.width > size.width || sz.height > size.height) {
            double ratio = MIN(sz.width / size.width, sz.height / size.height);
            sz.width /= ratio;
            sz.height /= ratio;
        }
        CGContextRef context;
        UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
        context = UIGraphicsGetCurrentContext();
        CGContextSetGrayFillColor(context, 0.0, 1.0);
        CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
        [image drawInRect:CGRectMake(0, 0, sz.width, sz.height)];
        UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
    

    LARGE_elevation.jpg - 14.3 mb。

    【讨论】:

    • 这不是内存效率,因为您将整个图像加载到内存中。
    猜你喜欢
    • 1970-01-01
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 2012-06-28
    • 1970-01-01
    • 2011-07-05
    • 2020-07-10
    • 1970-01-01
    相关资源
    最近更新 更多