Как выпустить CGImageRef в iOS

Я пишу этот метод для вычисления средних значений 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) перед возвратом метода. Теперь он падает после второго цикла, я получаю ошибку EXC_BAD_ACCESS для UIImage, которую я передаю методу. Когда я пытаюсь проанализировать (вместо RUN) в Xcode, я получаю следующее предупреждение в этой строке "Неправильный декремент ссылочного счета объекта, который не принадлежит на данный момент вызывающим"

Где и как мне выпустить CGImageRef?

Спасибо!

Ответ 1

Проблема с памятью возникает из-за скопированных данных, как утверждают другие. Но вот еще одна идея: оптимизировать пиксельную интерполяцию с использованием Core Graphics для вычисления среднего значения.

  • Создайте контекст растрового изображения 1x1.
  • Установите качество интерполяции на средний (см. ниже).
  • Нарисуйте изображение, уменьшенное до этого одного пикселя.
  • Прочитайте значение RGB из контекстного буфера.
  • (Конечно, отпустите контекст.)

Это может привести к повышению производительности, поскольку Core Graphics оптимизирована и может даже использовать графический процессор для уменьшения масштаба.

Тестирование показало, что среднее качество, кажется, интерполирует пиксели, принимая среднее значение цвета. Это то, что мы хотим здесь.

Стоит попробовать, по крайней мере.

Изменить: ОК, эта идея показалась слишком интересной, чтобы не попробовать. Итак вот пример проекта, показывающий разницу. Ниже были сделаны измерения с содержащимся тестовым изображением 512x512, но вы можете изменить изображение, если хотите.

Требуется около 12,2 мс для вычисления среднего значения путем повторения по всем пикселям в данных изображения. Подход "один-один-пиксель" занимает 3 мс, поэтому он примерно в 4 раза быстрее. Кажется, что он дает те же результаты при использовании kCGInterpolationQualityMedium.

Я предполагаю, что огромное увеличение производительности является результатом того, что Quartz замечает, что ему не нужно полностью распаковывать JPEG, но он может использовать только низкочастотные части DCT. Это интересная стратегия оптимизации при составлении сжатых пикселей JPEG со шкалой ниже 0,5. Но я только предполагаю, что здесь.

Интересно, что при использовании вашего метода 70% времени тратится на CGDataProviderCopyData и только 30% на обход данных пикселей. Это указывает на много времени, затраченное на декомпрессию JPEG.

Pixel Iterating ScreenshotDraw-To-One-Pixel Screenshot

Ответ 2

У вас нет CGImageRef rawImageRef, потому что вы получаете его с помощью [image CGImage]. Поэтому вам не нужно его выпускать.

Однако у вас есть rawPixelData, потому что вы получили его с помощью CGDataProviderCopyData и должны его освободить.

CGDataProviderCopyData​​STRONG >

Возвращаемое значение: Новый объект данных, содержащий копию данных поставщиков. Вы несете ответственность за освобождение этого объекта.

Ответ 3

Я считаю, что ваша проблема заключается в этом утверждении:

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

Вы должны освободить возвращаемое значение CGDataProviderCopyData.

Ответ 4

Ваш mergedColor отлично работает на изображении, загруженном из файла, но не на захват изображения камерой. Поскольку 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);

Ответ 6

Другой аналогичный вопрос был задан ранее и содержит некоторые действительно полезные ответы, которые помогут вам лучше понять:

CGImageRef Утечка памяти