在iOS上缩小图像的内存效率最高的方法是什么?

时间:2012-11-06 12:39:16

标签: ios memory-management core-graphics javax.imageio cgimage

在后台线程中,我的应用程序需要从磁盘读取图像,将它们缩小到屏幕大小(1024x768或2048x1536)并将它们保存回磁盘。原始图像主要来自相机胶卷,但其中一些可能有较大的尺寸(例如3000x3000)。

稍后,在另一个帖子中,这些图像会经常缩小到500x500左右的不同大小并再次保存到磁盘。

这让我想知道:在iOS,性能和内存方面,最有效的方法是什么?我使用了两种不同的API:

两者都适合我,但我想知道它们在性能和内存使用方面是否有任何差异。如果有更好的选择,当然。

6 个答案:

答案 0 :(得分:7)

虽然我无法肯定地说它会有所帮助,但我认为值得尝试将工作推向GPU。您可以通过渲染给定大小的纹理四边形,或使用GPUImage及其调整大小功能来自行完成此操作。虽然它在旧设备上有一些纹理大小限制,但它应该比基于CPU的解决方案具有更好的性能

答案 1 :(得分:5)

使用libjpeg-turbo,您可以使用scale_num的{​​{1}}和scale_denom字段,它只会解码所需的图像块。它给了我250S解码+ 4S背景线程的缩放时间,3264x2448原始图像(从相机,存储器中的图像数据)到iPhone的显示分辨率。我想这对于大而且不太好的图像来说是可以的。

(是的,这对内存有效。你可以逐行解码和存储图像)

答案 2 :(得分:4)

你在twitter上说的内容与你的问题不符。

如果您有内存峰值,请查看Instruments以了解消耗内存的内容。仅高分辨率图像的数据就是10兆,如果它们不包含alpha通道,那么你得到的图像将大约为750k。

第一个问题是保持较低的内存使用率,为此,请确保在您使用它们后立即处理所有加载的映像,这将确保底层的C / Objective-C API处理立即记忆,而不是等待GC运行,如下所示:

 using (var img = UIImage.FromFile ("..."){
     using (var scaled = Scaler (img)){
          scaled.Save (...);
     }
 }

对于缩放,有多种缩放图像的方法。最简单的方法是创建一个上下文,然后在上面绘制,然后从上下文中获取图像。这就是MonoTouch的UIImage.Scale方法的实现方式:

public UIImage Scale (SizeF newSize)
{
    UIGraphics.BeginImageContext (newSize);

    Draw (new RectangleF (0, 0, newSize.Width, newSize.Height));

    var scaledImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();

    return scaledImage;            
}

性能将受您启用的上下文功能的约束。例如,更高质量的缩放需要更改插值质量:

 context.InterpolationQuality = CGInterpolationQuality.High

另一种选择是不在CPU上运行缩放,而是在GPU上运行缩放。为此,您将使用CoreImage API并使用CIAffineTransform过滤器。

至于哪一个更快,它是留给其他人进行基准测试的东西

CGImage Scale (string file)
{
    var ciimage = CIImage.FromCGImage (UIImage.FromFile (file));

    // Create an AffineTransform that makes the image 1/5th of the size
    var transform = CGAffineTransform.MakeScale (0.5f, 0.5f);

    var affineTransform = new CIAffineTransform () { 
        Image = ciimage,
        Transform = transform
    };
    var output = affineTransform.OutputImage;
    var context = CIContext.FromOptions (null);
    return context.CreateCGImage (output, output.Extent);
}

答案 3 :(得分:3)

如果两者中的两者更有效,那么它将成为前者。

当您创建CGImageSource时,您只需创建名称所指的内容 - 某种不透明的东西,可以从中获取图像。在你的情况下,它将是对磁盘上的东西的引用。当您要求ImageIO创建缩略图时,您明确告诉它“尽可能多地输出这么多像素”。

相反,如果您绘制到CGBitmapContext,那么在某些时候您会明确地将整个图像带入内存。

所以第二种方法肯定会在某个时刻将整个图像同时存储在内存中。相反,前者不一定(在实践中,毫无疑问,对于最佳的进行方式,在ImageIO中会有某种猜测)。因此,在OS的所有可能实现中,前者将是有利的,或者两者之间没有区别。

答案 4 :(得分:3)

我会尝试使用像leptonica这样的基于c的库。我不确定ios是否使用相对较新的Accelerate Framework优化Core Graphics,但CoreGraphics可能只是为了重新调整图像大小而需要更多的开销。最后......如果你想推出自己的实现,请尝试使用vImageScale _ ?? format ??支持一些内存映射文件,我看不到任何更快的内容。

http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/vImage/Introduction/Introduction.html

PS。还要确保检查编译器优化标志。

答案 5 :(得分:1)

我认为如果您想保存内存,可以从图块到图块读取源图像并压缩图块并保存到目标图块。

苹果有一个例子。这是实施的方式。

https://developer.apple.com/library/ios/samplecode/LargeImageDownsizing/Introduction/Intro.html

您可以下载此项目并运行它。这是MRC所以你可以非常顺利地使用它。

愿它有所帮助。 :)