我在我的应用程序中使用Core Image过滤器,在运行iOS 7的iPhone 5设备上一切正常,但是当我在iPhone 4s上测试它时,它只有512MB的总内存,应用程序崩溃了。
在这种情况下,我从相机拍摄了2张图像,每张图像的分辨率为2448x3264。在我的iPhone 5中,根据乐器,整个过程在峰值时占用150MB。
但是,当我尝试在iPhone 4s上运行相同的代码时,即使整个内存使用率非常低(大约8 MB),仪器也会一直给我内存低警告。这是下面的截图。
这是代码,基本上,我从我的应用程序的文档文件夹中加载了两个图像,并连续应用了2个过滤器:
CIImage *foreground = [[CIImage alloc] initWithContentsOfURL:foregroundURL];
CIImage *background = [[CIImage alloc] initWithContentsOfURL:backgroundURL];
CIFilter *softLightBlendFilter = [CIFilter filterWithName:@"CISoftLightBlendMode"];
[softLightBlendFilter setDefaults];
[softLightBlendFilter setValue:foreground forKey:kCIInputImageKey];
[softLightBlendFilter setValue:background forKey:kCIInputBackgroundImageKey];
foreground = [softLightBlendFilter outputImage];
background = nil;
softLightBlendFilter = nil;
CIFilter *gammaAdjustFilter = [CIFilter filterWithName:@"CIGammaAdjust"];
[gammaAdjustFilter setDefaults];
[gammaAdjustFilter setValue:foreground forKey:kCIInputImageKey];
[gammaAdjustFilter setValue:[NSNumber numberWithFloat:value] forKey:@"inputPower"];
foreground = [gammaAdjustFilter valueForKey:kCIOutputImageKey];
gammaAdjustFilter = nil;
CIContext *context = [CIContext contextWithOptions:nil];
CGRect extent = [foreground extent];
CGImageRef cgImage = [context createCGImage:foreground fromRect:extent];
UIImage *image = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:imgOrientation];
CFRelease(cgImage);
foreground = nil;
return image;
应用程序在此行崩溃:CGImageRef cgImage = [context createCGImage:foreground fromRect:extent];
有没有更有效率的方法来处理这种情况,或者我在这里做错了什么?
非常感谢!
答案 0 :(得分:15)
简短版:
虽然它在概念上似乎微不足道,但对于有问题的设备而言,这实际上是一项非常耗费内存的任务。
长版:
考虑这个:2个图像*每个8位用于RGBA * 2448 * 3264~ = 64MB。然后CoreImage将需要另外~32MB的输出过滤操作。然后从CIContext
转换为CGImage
可能会消耗另外32MB。我希望UIImage
副本至少可以通过使用具有写时复制功能的VM映射图像来共享CGImage
的内存表示,尽管你可能会因为双重使用而感到厌烦,尽管不是消耗“真实”内存时,它仍会计入映射的页面。
因此,在裸最小值时,您使用的是128MB(加上您的应用正好使用的任何其他内存)。对于像4S这样的设备来说,这是一个相当大的RAM,开始时只有512MB。 IME,我会说,这将是可能的外部边缘。我希望它至少在某些时候能够工作,但是听到它会得到内存警告和内存压力会让我感到惊讶。您需要确保CIContext
和所有输入图片在尽可能快地CGImage
之后,以及UIImage
之前CGImage
取消分配/处置}。
通常,通过缩小图像大小可以使这更容易。
如果没有测试,并假设ARC,我将以下内容作为潜在的改进:
- (UIImage*)imageWithForeground: (NSURL*)foregroundURL background: (NSURL*)backgroundURL orientation:(UIImageOrientation)orientation value: (float)value
{
CIImage* holder = nil;
@autoreleasepool
{
CIImage *foreground = [[CIImage alloc] initWithContentsOfURL:foregroundURL];
CIImage *background = [[CIImage alloc] initWithContentsOfURL:backgroundURL];
CIFilter *softLightBlendFilter = [CIFilter filterWithName:@"CISoftLightBlendMode"];
[softLightBlendFilter setDefaults];
[softLightBlendFilter setValue:foreground forKey:kCIInputImageKey];
[softLightBlendFilter setValue:background forKey:kCIInputBackgroundImageKey];
holder = [softLightBlendFilter outputImage];
// This probably the peak usage moment -- I expect both source images as well as the output to be in memory.
}
// At this point, I expect the two source images to be flushed, leaving the one output image
@autoreleasepool
{
CIFilter *gammaAdjustFilter = [CIFilter filterWithName:@"CIGammaAdjust"];
[gammaAdjustFilter setDefaults];
[gammaAdjustFilter setValue:holder forKey:kCIInputImageKey];
[gammaAdjustFilter setValue:[NSNumber numberWithFloat:value] forKey:@"inputPower"];
holder = [gammaAdjustFilter outputImage];
// At this point, I expect us to have two images in memory, input and output
}
// Here we should be back down to just one image in memory
CGImageRef cgImage = NULL;
@autoreleasepool
{
CIContext *context = [CIContext contextWithOptions:nil];
CGRect extent = [holder extent];
cgImage = [context createCGImage: holder fromRect:extent];
// One would hope that CG and CI would be sharing memory via VM, but they probably aren't. So we probably have two images in memory at this point too
}
// Now I expect all the CIImages to have gone away, and for us to have one image in memory (just the CGImage)
UIImage *image = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:orientation];
// I expect UIImage to almost certainly be sharing the image data with the CGImageRef via VM, but even if it's not, we only have two images in memory
CFRelease(cgImage);
// Now we should have only one image in memory, the one we're returning.
return image;
}
如评论中所示,高水印将是采用两个输入图像并创建一个输出图像的操作。无论如何,这总是需要3张图像存储在内存中。要从那里进一步获得高水印,你必须在部分/瓦片中进行图像处理,或者将它们缩小到更小的尺寸。