【问题标题】:How to handle memory constraints in iOS 8 Photo Extensions?如何处理 iOS 8 照片扩展中的内存限制?
【发布时间】:2014-10-16 13:36:37
【问题描述】:

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

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

我做了一些实验。我修改了memory budget test app 以在扩展中工作并得出以下结果(显示崩溃前可以分配的 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。如果我必须告诉用户只有在使用主应用程序时才能进行全尺寸编辑,那么这种扩展机制的意义何在?

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

【问题讨论】:

  • 我应该指出 UIImage 应该归咎于此; Apple 文档指出,任何由 UIImage 对象引用的 1,920 × 1,080 照片或视频都会不可避免地为照片编辑扩展程序带来内存问题。您不是在使用照片应用程序中的手机限制,而是特定于应用程序的限制(我认为这与保护应用程序崩溃恶意软件有关);指向相机内存的数量或任何不可用的东西。他们说,可悲的是,您必须将媒体尺寸减小到 1,280 × 720 或更小。平铺不是一种选择。
  • 很好奇这在 iOS 10 中有何变化。根据@rickster 在下面提到的CIImageProcessorKernel。特别是在使用 GPUImage 等其他图像技术时。

标签: memory ios8 ios8-extension


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 谢谢,马可。我猜 CoreImage 确实在引擎盖下平铺。我将尝试使用 Instruments 来弄清楚它到底在做什么。我的问题是我需要支持存储有符号值的纹理,据我所知,我现在不能用 CoreImage 做到这一点。另一点是自定义 CoreImage 过滤器仅在 iOS 8 中受支持,我仍然希望在我的主应用程序中支持 iOS 7。我想我最终会自己实现一个平铺机制。但很高兴知道 CoreImage 可以处理大图像。再次感谢。
  • 请注意,Core Image 提供了多种插入您自己的图像处理的机会(同时受益于 CI 内存/GPU/颜色/等管理)。除了编写自古以来就存在的自定义内核的能力之外,iOS 10 还引入了CIImageProcessorKernel ,它允许您将 CI 以外的技术插入 CI 图像处理链中。
【解决方案2】:

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

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

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

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

【讨论】:

  • 对不起,这根本没有帮助。我知道 Core Image 的工作原理以及它的功能。它可以通过将它们编译为一个来优化一些后续的过滤步骤,但它不能总是这样做。例如,两个后续的卷积核不能编译为一个——你需要一个中间结果。正如我对 Marco 所说,Core Image 并不支持我的管道所需的所有功能。我已经将我的管道优化为一次“仅”使用 4 个纹理,但是对于扩展来说这仍然是太多的内存......
  • “一次四个纹理?”你如何在 Core Image 中使用一个“纹理”?这没有意义……
  • 正如我所说,我不能使用 Core Image,因为它不能提供我需要的一切。我使用 OpenGL 编写了自己的图像处理管道。
  • 我应该有 OpenGL 子集 glslang。您必须完整地谈论 OpenGL,如果您正在编写照片编辑扩展程序,您可能不会使用它。运行用 glslang 编写的 OpenGL 内核代码比不运行更容易;您只需将其作为捆绑资源加载到自定义 Core Image 过滤器中。 Core Image 不仅限于内置过滤器,您是对的,它并不适用于所有东西。我不是在 OpenGL 上推荐它。 OpenGL(glslang)内核可以加载到自定义核心图像过滤器中;然后,如果需要,您可以将 Core Image 过滤器与内核结合使用。
  • 对不起,弗兰克;我没有将纹理转换为采样器。在您发表评论时,您是对的; iOS 不允许您将多个采样器对象传递给 CIKernel 类(您也无法创建 CISampler 对象)。在 iOS 9 中,情况发生了变化;就像 MacOS X 一样,可以创建 CISampler 对象,并将多个对象传递给 CIKernel 类。再次,对不起;我应该花更多时间来理解你在说什么(而且,我很想看到你在 iOS 应用程序中使用自定义核心图像过滤器成功传递多个“纹理”。
【解决方案3】:

这是在 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;

}

【讨论】:

  • 正如我所说,我知道 Core Image 的工作原理以及如何应用(非常简单的)卷积过滤器。我在您的另一个答案中试图告诉您的是a)即使Core Image 也需要为某些场景分配多个缓冲区(纹理),并且b)我无法使用Core Image 做我需要的一切。在内存占用方面,我似乎 Core Image 大放异彩——我猜是因为它支持底层平铺。然而,正如 Brad Larson 在他的 GPUImage 框架中指出的那样,这也使得它相当慢。
猜你喜欢
  • 2023-04-06
  • 2014-10-22
  • 2019-09-05
  • 1970-01-01
  • 1970-01-01
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多