在iOS上缩小图像的最省内存的方式是什么?

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

之后,在不同的线程中,这些图像经常会缩小到500×500左右的不同大小,并再次保存到磁盘。

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

  • 使用来自ImageIO的CGImageSourceCGImageSourceCreateThumbnailAtIndex ;
  • 绘制到CGBitmapContext并使用CGImageDestination将结果保存到磁盘。

两者都为我工作,但我想知道他们是否有任何差异的性能和内存使用情况。 如果有更好的select,当然。

虽然我不能肯定地说它会有所帮助,但我认为值得将这项工作推向GPU。 您可以通过渲染给定大小的纹理四边形,或者使用GPUImage及其resize的function来完成这个任务。 虽然它在旧设备上有一些纹理尺寸限制,但它应该比基于CPU的解决scheme有更好的性能

使用libjpeg-turbo,您可以使用scale_numscale_denom字段,并且只会解码图像的所需块。 它给了我250ms的解码+缩放时间在后台线程4S与3264×2448原始图像(从相机,图像数据放在内存中)到iPhone的显示分辨率。 我想这对于一个很大的图像来说还是不错的,但是还不是很好。

(是的,这是高效的内存,你可以解码和存储图像几乎一行一行)

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

如果你有内存尖峰,看看仪器找出什么是消耗内存。 仅仅是你的高分辨率图像的数据是10兆,如果它们不包含阿尔法通道,你的结果图像将是大约750K。

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

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

至于缩放比例,有各种缩放图像的方法。 最简单的方法是创build一个上下文,然后绘制上下文,然后从上下文中获取图像。 这是如何实现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; } 

性能将受您启用的上下文function的支配。 例如,更高质量的缩放将需要改变插值质量:

  context.InterpolationQuality = CGInterpolationQuality.High 

另一种select是在CPU上运行缩放,而不是在GPU上运行。 为此,您可以使用CoreImage API并使用CIAffineTransformfilter。

至于哪一个更快,这是别人的基准

 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); } 

如果两者中的任何一个更有效,那么它将是前者。

当你创build一个CGImageSource你只需要创build一个名字就可以得到一个不透明的东西。 在你的情况下,它将是一个磁盘上的东西的参考。 当您要求ImageIO创build缩略图时,您明确地告诉它“尽可能多地输出这些像素”。

相反,如果你画一个CGBitmapContext那么在某个时候,你明确地把整个图像带入内存。

所以第二种方法在某个时刻一定有记忆的全部形象。 相反,前者不一定需要(实际上,ImageIO内部无疑是猜测最好的方法)。 因此,在操作系统的所有可能的实现中,前者将是有利的,或者两者之间没有区别。

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

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

PS。 另外请确保检查编译器优化标志。

我想如果你想保存内存,你可以读取源图像从瓷砖到瓷砖,并压缩瓷砖并保存到目的地瓷砖。

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

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

你可以下载这个项目并运行它。 这是MRC,所以你可以很顺利地使用它。

愿意帮忙 🙂