如何在iOS中发布CGImageRef

sam*_*fer 10 memory-leaks memory-management ios cgimageref

我正在编写这种方法来计算图像的平均R,G,B值.以下方法将UIImage作为输入,并返回包含输入图像的R,G,B值的数组.我有一个问题:如何/我在哪里正确发布CGImageRef?

-(NSArray *)getAverageRGBValuesFromImage:(UIImage *)image
{
    CGImageRef rawImageRef = [image CGImage];

    //This function returns the raw pixel values
    const UInt8 *rawPixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef)));

    NSUInteger imageHeight = CGImageGetHeight(rawImageRef);
    NSUInteger imageWidth = CGImageGetWidth(rawImageRef);

    //Here I sort the R,G,B, values and get the average over the whole image
    int i = 0;
    unsigned int red = 0;
    unsigned int green = 0;
    unsigned int blue = 0;

    for (int column = 0; column< imageWidth; column++)
    {
        int r_temp = 0;
        int g_temp = 0;
        int b_temp = 0;

        for (int row = 0; row < imageHeight; row++) {
            i = (row * imageWidth + column)*4;
            r_temp += (unsigned int)rawPixelData[i];
            g_temp += (unsigned int)rawPixelData[i+1];
            b_temp += (unsigned int)rawPixelData[i+2];

        }

        red += r_temp;
        green += g_temp;
        blue += b_temp;

    }

    NSNumber *averageRed = [NSNumber numberWithFloat:(1.0*red)/(imageHeight*imageWidth)];
    NSNumber *averageGreen = [NSNumber numberWithFloat:(1.0*green)/(imageHeight*imageWidth)];
    NSNumber *averageBlue = [NSNumber numberWithFloat:(1.0*blue)/(imageHeight*imageWidth)];


    //Then I store the result in an array
    NSArray *result = [NSArray arrayWithObjects:averageRed,averageGreen,averageBlue, nil];


    return result;
}
Run Code Online (Sandbox Code Playgroud)

我尝试了两件事:选项1:我保持原样,但是经过几个周期(5+)后程序崩溃,我得到"低内存警告错误"

选项2:我在方法返回之前添加一行CGImageRelease(rawImageRef).现在它在第二个周期后崩溃,我得到了传递给方法的UIImage的EXC_BAD_ACCESS错误.当我尝试在Xcode中分析(而不是RUN)时,我在此行中收到以下警告"调用者此时不拥有的对象的引用计数的错误减少"

我应该在哪里以及如何发布CGImageRef?

谢谢!

Nik*_*uhe 68

正如其他人所说,你的记忆问题是由复制的数据引起的.但这是另一个想法:使用Core Graphics的优化像素插值来计算平均值.

  1. 创建1x1位图上下文.
  2. 将插值质量设置为中等(见下文).
  3. 将您的图像缩小到恰好这一个像素.
  4. 从上下文缓冲区中读取RGB值.
  5. (当然,释放上下文.)

这可能会带来更好的性能,因为Core Graphics经过高度优化,甚至可以使用GPU进行缩减.

测试表明,中等质量似乎通过取颜色值的平均值来插值像素.这就是我们想要的.

值得一试,至少.

编辑:好的,这个想法似乎太有趣了,不要尝试.所以这是一个显示差异的示例项目.使用包含的512x512测试图像进​​行以下测量,但您可以根据需要更改图像.

通过迭代图像数据中的所有像素来计算平均值大约需要12.2ms.绘制到一个像素的方法需要3毫秒,所以它大约快4倍.使用时似乎产生相同的结果kCGInterpolationQualityMedium.

我认为巨大的性能提升是由于Quartz注意到它不必完全解压缩JPEG,而是只能使用DCT的低频部分.当编写尺寸小于0.5的JPEG压缩像素时,这是一个有趣的优化策略.但我只是在这里猜测.

有趣的是,在使用您的方法时,70%的时间花费在CGDataProviderCopyData像素数据遍历中,而在像素数据遍历中只有30%.这暗示了花在JPEG解压缩上的大量时间.

Pixel迭代截图 Draw-To-One-Pixel截图


sch*_*sch 8

你不拥有,CGImageRef rawImageRef因为你使用它[image CGImage].所以你不需要发布它.

但是,您拥有,rawPixelData因为您使用它CGDataProviderCopyData并且必须将其释放.

CGDataProviderCopyData

返回值:包含提供者数据副本的新数据对象.您负责释放此对象.

  • ...你要做的就是保持`CFDataRef`返回并在适当的时候调用`CFRelease`.注意这里的规范与Objective-C规范完全相反; 如果你通过`CFRelease(NULL)`那么Core Foundation将故意提出异常. (2认同)