22

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

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

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

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

4

6 回答 6

7

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

于 2012-11-11T16:11:27.840 回答
5

With libjpeg-turbo you can use the scale_num and scale_denom fields of jpeg_decompress_struct, and it will decode only needed blocks of an image. It gave me 250 ms decoding+scaling time in background thread on 4S with 3264x2448 original image (from camera, image data placed in memory) to iPhone's display resolution. I guess it's OK for an image that large, but still not great.

(And yes, that is memory efficient. You can decode and store the image almost line by line)

于 2012-11-10T21:45:28.020 回答
4

你在推特上说的和你的问题不符。

如果您遇到内存峰值,请查看 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);
}
于 2012-11-11T03:49:34.073 回答
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。还要确保检查编译器优化标志。

于 2012-11-10T20:45:53.513 回答
3

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

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

相反,如果你画到 aCGBitmapContext然后在某个时候你明确地将整个图像带入内存。

因此,第二种方法肯定会在某个时候将整个图像一次存储在内存中。相反,前者不一定需要(在实践中,ImageIO 中毫无疑问会进行某种猜测,以确定最佳的进行方式)。因此,在操作系统的所有可能实现中,要么前者是有利的,要么两者之间没有区别。

于 2012-11-07T00:30:44.520 回答
1

我想如果你想节省内存,你可以从一个瓦片到另一个瓦片读取源图像并压缩瓦片并保存到目标瓦片。

苹果有一个例子。它是执行的方式。

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

你可以下载这个项目并运行它。它是 MRC,因此您可以非常流畅地使用它。

愿它有所帮助。:)

于 2015-05-05T10:55:58.600 回答