繁体   English   中英

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

[英]The memory-efficient way of using Core Image on iOS?

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

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

仪器内存使用情况

但是,当我尝试在iPhone 4s上运行相同的代码时,即使整个内存使用量非常低(大约8 MB),这些仪器也始终向我发出内存不足警告。 这是下面的截图。

iphone 4s的内存使用情况

这是代码,基本上,我从应用程序的documents文件夹中加载了两个图像,并连续应用了两个过滤器:

    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];

还有什么更有效的内存方式来处理这种情况,或者我在这里做错了什么?

非常感谢!

简洁版本:

尽管在概念上看似微不足道,但对于相关设备而言,这实际上是一项相当耗费内存的任务。

长版:

考虑一下:2个图像* RGBA每个8位* 2448 * 3264〜= 64MB。 然后,CoreImage将需要另一个〜32MB的过滤器操作输出。 然后将其从CIContext导入到CGImage中可能会消耗另外32MB。 我希望UIImage副本至少通过使用VM和写时复制来映射图像来共享CGImage的内存表示形式,尽管尽管尽管您不消耗“实际”内存,但无论如何您都可能会为双重用途感到厌烦,取决于映射的页面。

因此,在最低限度,你使用128MB(加上任何其他存储您的应用程序碰巧使用)。 对于像4S这样的设备来说,这是相当大的内存,而4S最初仅以512MB开始。 IME,我想这将是在可能范围之内。 我希望它至少在某些时候能工作,但是听到它收到内存警告和内存压力下降的消息并不令我感到惊讶。 您将要确保在CIContext制作CGImage之后,以及从CGImage制作UIImage之前,立即释放/处置CIContext和所有输入图像。

通常,可以通过缩小图像尺寸来简化此操作。

如果不进行测试,并假设使用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张图像存储在内存中。 要使高水位线进一步下降,您必须按部分/平铺图像或将其缩小到较小的尺寸。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM