7

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

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

我做了一些实验。我修改了一个内存预算测试应用程序以在扩展中工作,并得出以下结果(显示崩溃前可以分配的 RAM 量(以 MB 为单位)):

╔═══════════════════════╦═════╦═══════════╦══════════════════╗
║        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 ║
╚═══════════════════════╩═════╩═══════════╩══════════════════╝

几点观察:

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

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

你们中的一个人也达到了那个障碍吗?您是否找到了规避此限制的解决方案?

4

3 回答 3

3

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

我们只找到了两种解决方法,但不是真正的解决方案。

  1. 缩小图像并应用滤镜。这将需要更少的内存,但图像结果很糟糕。至少扩展不会崩溃。

或者

  1. 使用 CoreImage 或 Metal 进行图像处理。正如我们分析 Apple 的Sample Photo Editing Extension一样,它使用 CoreImage,可以处理非常大的图像甚至全景图,而不会造成质量或分辨率损失。实际上,我们无法通过加载非常大的图像来使扩展程序崩溃。示例代码可以处理具有 40mb 内存窥视的全景图,这非常令人印象深刻。

根据 Apple 的App Extension Programming Guide,第 55 页,“处理内存约束”一章,扩展内存压力的解决方案是查看您的图像处理代码。到目前为止,我们正在将我们的图像处理引擎移植到 CoreImage,结果比我们之前的引擎要好得多。

我希望我能帮上一点忙。马可·派瓦

于 2014-10-27T15:49:34.303 回答
0

正如 Marco 所说,如果您使用的是 Core Image “配方”,则根本不必担心内存。在图像对象返回到视图之前,不会渲染应用了核心图像过滤器的图像。

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

通过将自己定位于所选编程语言、开发环境和硬件平台的核心概念,可以轻松纠正对内存管理和溢出等的误解。

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

于 2015-04-23T04:12:37.743 回答
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;

}

于 2015-05-21T08:42:27.523 回答