在后台线程中,我的应用程序需要从磁盘读取图像,将它们缩小到屏幕大小(1024x768或2048x1536)并将它们保存回磁盘。原始图像主要来自相机胶卷,但其中一些可能有较大的尺寸(例如3000x3000)。
稍后,在另一个帖子中,这些图像会经常缩小到500x500左右的不同大小并再次保存到磁盘。
这让我想知道:在iOS,性能和内存方面,最有效的方法是什么?我使用了两种不同的API:
CGImageSource
and CGImageSourceCreateThumbnailAtIndex
; CGBitmapContext
并使用CGImageDestination
将结果保存到磁盘。两者都适合我,但我想知道它们在性能和内存使用方面是否有任何差异。如果有更好的选择,当然。
答案 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 ??支持一些内存映射文件,我看不到任何更快的内容。
PS。还要确保检查编译器优化标志。
答案 5 :(得分:1)
我认为如果您想保存内存,可以从图块到图块读取源图像并压缩图块并保存到目标图块。
苹果有一个例子。这是实施的方式。
https://developer.apple.com/library/ios/samplecode/LargeImageDownsizing/Introduction/Intro.html
您可以下载此项目并运行它。这是MRC所以你可以非常顺利地使用它。
愿它有所帮助。 :)