【问题标题】:How do I release a CGImageRef in iOS如何在 iOS 中发布 CGImageRef
【发布时间】:2012-08-22 06:48:01
【问题描述】:

我正在编写这个方法来计算图像的平均 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;
}

我尝试了两件事: 选项1: 我保持原样,但是在几个周期(5+)之后程序崩溃并且我收到“内存不足警告错误”

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

我应该在哪里以及如何释放 CGImageRef?

谢谢!

【问题讨论】:

  • 找到这些东西的一种方法是去Product→Analyze,分析器会为你找到这个内存处理错误。

标签: ios memory-management memory-leaks cgimageref


【解决方案1】:

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

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

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

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

至少值得一试。

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

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

我认为巨大的性能提升是由于 Quartz 注意到它不必完全解压缩 JPEG,但它可以只使用 DCT 的较低频率部分。在组合比例低于 0.5 的 JPEG 压缩像素时,这是一个有趣的优化策略。但我只是在这里猜测。

有趣的是,在使用你的方法时,70% 的时间都花在了CGDataProviderCopyData 上,而只有 30% 的时间花在了像素数据的遍历上。这表明在 JPEG 解压缩上花费了很多时间。

注意:Here's a late follow up 在上面的示例图片中。

【讨论】:

  • @Jonny 计算非矩形区域的平均值可以通过在绘制之前设置剪辑(路径或蒙版)轻松完成。剪辑会使一些像素透明,这意味着它们不会影响最终结果。多田!
  • 好的,我可能希望用这样的特性扩展这个类。哦,我只是想编写一个“魔杖”来选择具有相似肤色/色调的面部区域。
  • 测试似乎表明 Core Graphics 完成的 alpha 采样不会产生预期的平均值。如果不进一步深入研究这个主题,我不建议将这种方法用于非矩形区域。
  • 平均颜色和合并颜色有什么区别??感谢您的代码!
  • @Daniel "Average" 只是遍历所有像素并计算平均值的简单代码。 “合并”是实际绘制图像并将结果用作平均颜色的建议实现。
【解决方案2】:

您不拥有CGImageRef rawImageRef,因为您使用 [image CGImage] 获得它。所以你不需要释放它。

但是,您拥有rawPixelData,因为您使用CGDataProviderCopyData 获得它并且必须释放它。

CGDataProviderCopyData

返回值: 包含提供者数据副本的新数据对象。你有责任释放这个对象。

【讨论】:

  • ... 你可以通过保持CFDataRef 它返回并在适当的时候调用CFRelease 来做到这一点。注意这里的规范与 Objective-C 规范完全相反;如果你通过CFRelease(NULL) 那么Core Foundation 会非常故意地抛出一个异常。
  • @Tommy 当然,因为 Obj-C 中的 nil 和普通 C 中的 NULL 之间存在很大差异。
【解决方案3】:

我相信您的问题在此声明中:

const UInt8 *rawPixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef)));

您应该释放 CGDataProviderCopyData 的返回值。

【讨论】:

    【解决方案4】:

    您的合并颜色在从文件加载的图像上效果很好,但不适用于相机捕获的图像。因为从捕获的样本缓冲区创建的上下文中的CGBitmapContextGetData() 不会返回位图。我将您的代码更改为如下。它适用于任何图像,并且与您的代码一样快。

    - (UIColor *)mergedColor
    {
         CGImageRef rawImageRef = [self CGImage];
    
        // scale image to an one pixel image
    
        uint8_t  bitmapData[4];
        int bitmapByteCount;
        int bitmapBytesPerRow;
        int width = 1;
        int height = 1;
    
        bitmapBytesPerRow = (width * 4);
        bitmapByteCount = (bitmapBytesPerRow * height);
        memset(bitmapData, 0, bitmapByteCount);
        CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
                 colorspace,kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);
        CGColorSpaceRelease(colorspace);
        CGContextSetBlendMode(context, kCGBlendModeCopy);
        CGContextSetInterpolationQuality(context, kCGInterpolationMedium);
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), rawImageRef);
        CGContextRelease(context);
        return [UIColor colorWithRed:bitmapData[2] / 255.0f
                        green:bitmapData[1] / 255.0f
                                 blue:bitmapData[0] / 255.0f
                                alpha:1];
    }
    

    【讨论】:

      【解决方案5】:
      CFDataRef abgrData = CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef));
      
      const UInt8 *rawPixelData = CFDataGetBytePtr(abgrData);
      
      ...
      
      CFRelease(abgrData);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-04
        • 1970-01-01
        • 2016-07-20
        相关资源
        最近更新 更多