如何在iOS 8 Photo Extensions中处理内存限制?

时间:2014-10-16 13:36:38

标签: memory ios8 ios8-extension

我在现有的照片编辑应用程序中添加了一个新的iOS 8 Photo Extension。我的应用程序有一个非常复杂的过滤器管道,需要一次在内存中保留多个纹理。但是,在具有1 GB RAM的设备上,我可以轻松处理8 MP图像。

然而,在扩展中,存在更高的内存约束。我不得不将图像缩小到2 MP以下,以便在不破坏扩展名的情况下对其进行处理。我还认为只有在没有连接到扩展的调试器时才会出现内存问题。有了它,一切正常。

我做了一些实验。我修改了memory budget test app以在扩展中工作,并得出以下结果(显示崩溃前可以分配的MB的RAM数量):

╔═══════════════════════╦═════╦═══════════╦══════════════════╗
║        Device         ║ App ║ Extension ║ Ext. (+Debugger) ║
╠═══════════════════════╬═════╬═══════════╬══════════════════╣
║ iPhone 6 Plus (8.0.2) ║ 646 ║       115 ║              645 ║
║ iPhone 5 (8.1 beta 2) ║ 647 ║        97 ║              646 ║
║ iPhone 4s (8.0.2)     ║ 305 ║        97 ║              246 ║
╚═══════════════════════╩═════╩═══════════╩══════════════════╝

一些观察结果:

  • 附加调试器后,扩展程序的行为类似于" normal"应用
  • 尽管4s只有一半的内存总量(512 MB),但与其他设备相比,它从扩展系统获得的相同数量为100 MB。

现在我的问题:我应该如何使用照片编辑扩展程序中的少量内存?一个包含8 MP(相机分辨率)RGBA图像的纹理单独吃~31 MB。如果我必须告诉用户只有在使用主应用程序时才能进行全尺寸编辑,这个扩展机制有什么意义呢?

你们其中一个人是否也达到了这个障碍?您是否找到了解决此约束的解决方案?

3 个答案:

答案 0 :(得分:3)

我正在为我的公司开发照片编辑扩展程序,我们正面临同样的问题。我们的内部图像处理引擎需要超过150mb才能将某些效果应用于图像。这甚至不计算全景图像,每个副本大约需要100mb的内存。

我们发现只有两种解决方法,但不是实际解决方案。

  1. 缩小图像,然后应用滤镜。这将需要更少的内存,但图像结果是可怕的。至少扩展名不会崩溃。
    1. 使用CoreImage或Metal进行图像处理。我们分析了使用CoreImage的Apple的Sample Photo Editing Extension,可以处理非常大的图像甚至全景图,而不会丢失质量或分辨率。实际上,我们无法通过加载非常大的图像来破坏扩展。示例代码可以处理内存为40mb的全景图,这非常令人印象深刻。
    2. 根据Apple的App Extension Programming Guide,第55页,“处理内存约束”一章,扩展内存压力的解决方案是检查图像处理代码。到目前为止,我们正在将我们的图像处理引擎移植到CoreImage,结果远远优于我们以前的引擎。

      我希望我可以帮助一下。 Marco Paiva

答案 1 :(得分:0)

如果你正在使用核心图像“食谱”,你根本不必担心记忆,就像马可说。在图像对象返回视图之前,不会渲染应用Core Image滤镜的图像。

这意味着您可以将一百万个过滤器应用于高速公路广告牌大小的照片,并且内存不会成为问题。过滤器规范将简单地编译成卷积或内核,无论如何都会降低到相同的大小。

通过使用所选编程语言,开发环境和硬件平台的核心概念,可以轻松解决有关内存管理和溢出等问题的误解。

Apple的文档介绍了Core Image过滤器编程就足够了;如果您想要特别提及我认为与您的问题有关的文档部分,请询问。

答案 2 :(得分:0)

以下是如何在Core Image中应用两个连续卷积内核,其中包含"中间结果"他们之间:

- (CIImage *)outputImage { 

const double g = self.inputIntensity.doubleValue;
const CGFloat weights_v[] = { -1*g, 0*g, 1*g,
                              -1*g, 0*g, 1*g,
                              -1*g, 0*g, 1*g};

CIImage *result = [CIFilter filterWithName:@"CIConvolution3X3" keysAndValues:
          @"inputImage", self.inputImage,
          @"inputWeights", [CIVector vectorWithValues:weights_v count:9],
          @"inputBias", [NSNumber numberWithFloat:1.0],
          nil].outputImage;

CGRect rect = [self.inputImage extent];
rect.origin = CGPointZero;

CGRect cropRectLeft = CGRectMake(0, 0, rect.size.width, rect.size.height);
CIVector *cropRect = [CIVector vectorWithX:rect.origin.x Y:rect.origin.y Z:rect.size.width W:rect.size.height];
result = [result imageByCroppingToRect:cropRectLeft];

result = [CIFilter filterWithName:@"CICrop" keysAndValues:@"inputImage", result, @"inputRectangle", cropRect, nil].outputImage;


const CGFloat weights_h[] = {-1*g, -1*g, -1*g,
    0*g,   0*g,   0*g,
    1*g,   1*g,     1*g};


result = [CIFilter filterWithName:@"CIConvolution3X3" keysAndValues:
          @"inputImage", result,
          @"inputWeights", [CIVector vectorWithValues:weights_h count:9],
          @"inputBias", [NSNumber numberWithFloat:1.0],
          nil].outputImage;

result = [result imageByCroppingToRect:cropRectLeft];

result = [CIFilter filterWithName:@"CICrop" keysAndValues:@"inputImage", result, @"inputRectangle", cropRect, nil].outputImage;

result = [CIFilter filterWithName:@"CIColorInvert" keysAndValues:kCIInputImageKey, result, nil].outputImage;

return result;

}