繁体   English   中英

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

[英]What is the most memory-efficient way of downscaling images on iOS?

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

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

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

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

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

随着libjpeg的涡轮增压可以使用scale_numscale_denom领域jpeg_decompress_struct ,它将解码图像仅需块。 它给了我250S解码+ 4S背景线程的缩放时间,3264x2448原始图像(从相机,存储器中的图像数据)到iPhone的显示分辨率。 我想这对于大而且仍然不太好的图像来说是可以的。

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

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

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

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

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

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

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

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所以你可以非常顺利地使用它。

愿它有所帮助。 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM