在iOS上使用Core Image的内存有效方式?

Voi*_*ain 9 memory iphone objective-c core-image ios

我在我的应用程序中使用Core Image过滤器,在运行iOS 7的iPhone 5设备上一切正常,但是当我在iPhone 4s上测试时,它只有512MB的总内存,应用程序崩溃了.

在这种情况下,我从相机拍摄了2张图像,每张图像的分辨率为2448x3264.在我的iPhone 5中,根据乐器,整个过程在峰值时占用150MB.

仪器内存使用

但是,当我尝试在iPhone 4s上运行相同的代码时,即使整个内存使用率非常低(大约8 MB),仪器也会一直给我内存低警告.这是下面的截图.

iphone 4s内存使用情况

这是代码,基本上,我从我的应用程序的文档文件夹中加载了两个图像,并连续应用了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;
Run Code Online (Sandbox Code Playgroud)

该应用程序在此行崩溃: CGImageRef cgImage = [context createCGImage:foreground fromRect:extent];

有没有更有效的内存处理方式,或者我在这里做错了什么?

十分感谢!

ipm*_*mcc 15

精简版:

虽然它在概念上似乎微不足道,但对于有问题的设备而言,这实际上是一项非常耗费内存的任务.

长版:

考虑一下:2个图像*每个8位用于RGBA*2448*3264~ = 64MB.然后CoreImage将需要另外~32MB的输出过滤操作.然后从a CIContext进入a CGImage可能会消耗另外32MB.我希望UIImage副本CGImage至少可以通过使用具有写时复制的VM映射图像来共享内存表示,尽管你可能会因为不使用"真实"内存而被用于双重用途,但它仍然是反对的页面映射.

因此,在一个光秃秃的最小,你使用128MB(加上您的应用程序发生的任何其他内存来使用).对于像4S这样的设备来说,这是一个相当大的RAM,开始时只有512MB.IME,我会说,这将是可能的外部边缘.我希望它至少在某些时候能够工作,但是听到它会得到内存警告和内存压力会让我感到惊讶.您将要确保的是,CIContext和所有的输入图像将被释放/配置,尽快使后CGImage成为可能,并使得前UIImageCGImage.

通常,通过缩小图像大小可以使这更容易.

如果没有测试,并假设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;
}
Run Code Online (Sandbox Code Playgroud)

如评论中所示,高水印将是采用两个输入图像并创建一个输出图像的操作.无论如何,这总是需要3张图像存储在内存中.要从那里进一步获得高水印,您必须在部分/瓦片中进行图像处理或将其缩小到更小的尺寸.

  • 前两个自动释放池没有任何效果.CIImage本身不分配内存(将它们视为食谱).内存分配峰值出现在示例中的第三个自动释放池中,如果该峰值超出可用内存,则会崩溃.最后一个自动释放池可能会使CIContext内部资源更早地解除分配,这仍然是一个改进而不是没有它. (3认同)