使用CGImageCreate调整图像大小以减少内存使用

4
有时候图片非常大(从互联网下载),因此我需要调整它们的大小,但不想引起内存问题,这意味着我不想将所有图像数据加载到内存中并在内存中调整它们的大小。
我正在研究几种调整图像大小的方法,并监视它们的内存使用情况。具体来说,使用“CGContextDrawImage”、“CGImageSourceCreateThumbnailAtIndex”和带有“CGDataProviderRef”的“CGImageCreate”。
现在我在使用“CGImageCreate”和“CGDataProviderRef”时遇到了一些问题,我的代码如下:
//This method read the data from filePath, resize the image and write the resized data to the destPath
- (void)resizeImageFileUsingCGImageCreate:(NSString*)filePath toDestPath:(NSString*)destPath {

    CGFloat factor = 0.2;
    CGSize originalSize = [self sizeOfImageAtURL:[[NSURL alloc] initFileURLWithPath:filePath]];
    CGSize destSize = CGSizeMake((NSUInteger)(originalSize.width * factor), (NSUInteger)(originalSize.height * factor));



    UIImage *srcImage = [UIImage imageWithContentsOfFile:filePath];
    CGImageRef srcImgRef = srcImage.CGImage;
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    CFDataRef cfData = (__bridge_retained CFDataRef)data;
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(cfData);
    CFRelease(cfData);

    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(srcImgRef);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGImageRef imageRef = CGImageCreate(destSize.width, destSize.height, 8, 32, 4 * destSize.width, colorSpace, bitmapInfo, dataProvider, NULL, NO, 0);

    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colorSpace);
    UIImage *image = nil;
    if (imageRef != NULL) {
        image = [[UIImage alloc] initWithCGImage:imageRef];
        CGImageRelease(imageRef);
        //Why UIImageJPEGRepresentation(image, 0.5) returns nil?
        [UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES];


    }


}

在代码的最后一行,UIImageJPEGRepresentation(image, 0.5)总是返回nil,这让我感到困惑。你能帮助我指出使用CGImageCreateCGDataProviderRef来调整图像大小的正确方法吗?非常感谢!

在使用UIImageJPEGRepresentation之前,你能否将UIImage显示在UIImageView中?我认为CGDataProvider的原始数据应该是未压缩的RGB位图数据,而不是png/jpeg文件的数据。 - Allen Hsu
感谢@Alleen!是的,我也注意到了这个问题,因为如果我使用CGDataProviderRef dataProvider = CGImageGetDataProvider(srcImgRef)更改dataProvider,并将factor = 1更改,我将获得相同大小的图像。但我不知道如何在CGDataProviderRef中缩放数据。 - Hang
也许你应该将数据作为数组进行操作? - Allen Hsu
2个回答

3

不直接回答您的问题,我会向您展示我认为更好的方法:使用ImageIO框架。

#import <ImageIO/ImageIO.h>

以下是如何从磁盘加载图像的缩小版本(imageSource 是一个 NSURL;scalemaxwmaxh 必须事先设置为您想要的比例和最大尺寸):

CGImageSourceRef src = 
    CGImageSourceCreateWithURL((__bridge CFURLRef)imageSource, nil);
NSDictionary* d = @{
    (id)kCGImageSourceShouldAllowFloat: (id)kCFBooleanTrue,
    (id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
    (id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
    (id)kCGImageSourceThumbnailMaxPixelSize: @((int)(maxw > maxh ? maxw : maxh))
};
CGImageRef imref = 
    CGImageSourceCreateThumbnailAtIndex(src, 0, (__bridge CFDictionaryRef)d);
if (NULL != src)
    CFRelease(src);
UIImage* im = 
    [UIImage imageWithCGImage:imref scale:scale orientation:UIImageOrientationUp];
if (NULL != imref)
    CFRelease(imref);

这段代码的优点在于我们从未将大图像保存到内存中,也从未加载过。最终我们会得到一个较小的UIImage,可以直接在界面上使用。
如果你手头有来自互联网的NSData数据,并且没有将其保存到磁盘上,那么创建图像源的方法如下:
src = CGImageSourceCreateWithData((__bridge CFDataRef)imageSource, nil);

但是如果图片很大,建议使用下载任务,这样整个图片就不会在内存中,而是通过到达时保存到磁盘。


谢谢@matt!使用CGImageSourceCreateThumbnailAtIndex是我正在研究的方法之一,这很好。但我需要知道如何使用CGImageCreateCGDataProviderRef,因为我想做一个基准测试来展示它们的优缺点。 - Hang

0

不要这样做:

    [UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES];

你应该这样做:
NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
if(imageData) 
{
    NSError *error = nil;
    BOOL success = [imageData writeToFile:destPath options:NSDataWritingAtomic error:&error];
    if(NO == success)
    {
        NSLog(@"couldn't write image data to file %@ because of error %@", destPath, error)
    }
}

在我的代码版本中,我将图像数据放入NSData对象中,如果写入图像数据失败,则打印出错误信息。

谢谢@Michael!我认为没有必要提取代码[UIImageJPEGRepresentation(image, 0.5) writeToFile:destPath atomically:YES],因为向nil对象发送消息是安全的。 - Hang
当 "image" 在传递到 "UIImageJPEGRepresentation()" 时不为空,会发生什么? - Michael Dautermann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接