调整一个UIImage的大小而不把它完全载入内存?

问题描述 投票:22回答:2

我正在开发一个应用程序,在这个应用程序中,用户可能会尝试加载非常大的图像。这些图片首先在一个表格视图中以缩略图的形式显示。我原来的代码会在大图片上崩溃,所以我正在重写它,首先将图片直接下载到磁盘上。

有没有已知的方法来调整磁盘上图像的大小,而不完全将其加载到内存中,通过 UIImage? 我目前正在尝试使用分类来调整尺寸,在 UIImage 详见 此处但我的应用程序在尝试缩略一个非常大的图像时崩溃(例如,。这个 - 警告,巨大的图像)。)

objective-c cocoa-touch ios uiimage
2个回答
49
投票

你应该看看ImageIO.framework中的CGImageSource,但它只在iOS 4.0中可用。

快速示例 。

-(UIImage*)resizeImageToMaxSize:(CGFloat)max path:(NSString*)path
{
    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path], NULL);
    if (!imageSource)
        return nil;

    CFDictionaryRef options = (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
        (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
        (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
        (id)@(max),
        (id)kCGImageSourceThumbnailMaxPixelSize,
        nil];
    CGImageRef imgRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options);

    UIImage* scaled = [UIImage imageWithCGImage:imgRef];

    CGImageRelease(imgRef);
    CFRelease(imageSource);

    return scaled;
}

0
投票

根据这个环节。iOS内存深挖我们最好使用 ImageIO 来降低图像的比例。

使用 UIImage 降级图像。

  • 将原始图像解压到内存中
  • 内部坐标空间变换的成本很高

使用 ImageIO

  • ImageIO可以读取图像的大小和元数据信息,而不会弄脏内存。

  • ImageIO可以调整图像的大小,但只需要支付调整图像大小的费用。

关于内存中的图像

  • 内存使用与图像的尺寸有关,而不是文件大小。
  • UIGraphicsBeginImageContextWithOptions 总是使用 SRGB 渲染格式,每像素使用4个字节。
  • 一张图片有 load -> decode -> render 3个阶段。
  • UIImage 上浆和调整尺寸的成本很高。

对于下面的图片,如果你使用 UIGraphicsBeginImageContextWithOptions我们只需要590KB就可以加载一张图片,而我们却需要2048 pixels x 1536 pixels x 4 bytes per pixel 解码时=10MB enter image description here

UIGraphicsImageRenderer在iOS10中引入的iOS12将自动选择最佳的图形格式。这意味着,在iOS12中,你可以通过更换图片来节省75%的内存。UIGraphicsBeginImageContextWithOptionsUIGraphicsImageRenderer 如果你不需要SRGB。

这是我的文章,关于 记忆中的iOS图像

func resize(url: NSURL, maxPixelSize: Int) -> CGImage? {
    let imgSource = CGImageSourceCreateWithURL(url, nil)
    guard let imageSource = imgSource else {
        return nil
    }

    var scaledImage: CGImage?
    let options: [NSString: Any] = [
            // The maximum width and height in pixels of a thumbnail.
            kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            // Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
            kCGImageSourceCreateThumbnailWithTransform: true
    ]
    scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)

    return scaledImage
}


let filePath = Bundle.main.path(forResource:"large_leaves_70mp", ofType: "jpg")

let url = NSURL(fileURLWithPath: filePath ?? "")

let image = resize(url: url, maxPixelSize: 600)

// Downsampling large images for display at smaller size
func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
    let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
    let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!
    let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
    let downsampleOptions =
        [kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceShouldCacheImmediately: true,
        // Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary
    let downsampledImage =
        CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)!
    return UIImage(cgImage: downsampledImage)
}
© www.soinside.com 2019 - 2024. All rights reserved.