Получение альфа-значения пикселя для UIImage - PullRequest
35 голосов
/ 25 июня 2009

В настоящее время я пытаюсь получить альфа-значение пикселя в UIImageView. Я получил CGImage из [UIImageView image] и создал байтовый массив RGBA из этого. Альфа предварительно умножается.

CGImageRef image = uiImage.CGImage;
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
rawData = malloc(height * width * 4);
bytesPerPixel = 4;
bytesPerRow = bytesPerPixel * width;

NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(
    rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace,
    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big
);
CGColorSpaceRelease(colorSpace);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
CGContextRelease(context);

Затем я вычисляю индекс массива для данного альфа-канала, используя координаты из UIImageView.

int byteIndex = (bytesPerRow * uiViewPoint.y) + uiViewPoint.x * bytesPerPixel;
unsigned char alpha = rawData[byteIndex + 3];

Однако я не получаю ожидаемых значений. Для полностью черной прозрачной области изображения я получаю ненулевые значения для альфа-канала. Нужно ли переводить координаты между UIKit и Core Graphics - т.е. инвертирована ли ось Y? Или я неправильно понял предварительно умноженные альфа-значения?

Обновление:

@ Предложение Николая Рюэ имело ключевое значение для этого. На самом деле мне не нужно было переводить координаты UIKit и координаты Core Graphics. Однако после установки режима наложения мои альфа-значения оказались такими, как я ожидал:

CGContextSetBlendMode(context, kCGBlendModeCopy);

Ответы [ 4 ]

36 голосов
/ 21 сентября 2010

Если все, что вам нужно, это альфа-значение одной точки, все, что вам нужно, это альфа-единственный одноточечный буфер. Я считаю, что этого должно быть достаточно:

// assume im is a UIImage, point is the CGPoint to test
CGImageRef cgim = im.CGImage;
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel, 
                                         1, 1, 8, 1, NULL,
                                         kCGImageAlphaOnly);
CGContextDrawImage(context, CGRectMake(-point.x, 
                                   -point.y, 
                                   CGImageGetWidth(cgim), 
                                   CGImageGetHeight(cgim)), 
               cgim);
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
BOOL transparent = alpha < 0.01;

Если UIImage не нужно воссоздавать каждый раз, это очень эффективно.

РЕДАКТИРОВАТЬ 8 декабря 2011 г .:

Комментатор указывает, что при определенных обстоятельствах изображение может быть перевернуто. Я думал об этом, и мне немного жаль, что я не написал код с использованием UIImage напрямую, как это (я думаю, причина в том, что в то время я не понимал о UIGraphicsPushContext) :

// assume im is a UIImage, point is the CGPoint to test
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel, 
                                             1, 1, 8, 1, NULL,
                                             kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[im drawAtPoint:CGPointMake(-point.x, -point.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
BOOL transparent = alpha < 0.01;

Думаю, это решило бы проблему с переворотом.

10 голосов
/ 25 июня 2009

Да, у CGContexts ось Y направлена ​​вверх, а в UIKit - вниз. См. Документы .

Редактировать после прочтения кода:

Вы также хотите установить режим наложения, чтобы заменить перед рисованием изображения, так как вы хотите альфа-значение изображения, а не то, которое было в буфере контекста раньше:

CGContextSetBlendMode(context, kCGBlendModeCopy);

Изменить после размышления:

Вы можете сделать поиск намного более эффективным, построив наименьший возможный CGBitmapContext (1x1 пиксель, может быть, 8x8, попробуйте) и переведя контекст в нужную вам позицию перед рисованием:

CGContextTranslateCTM(context, xOffset, yOffset);
3 голосов
/ 13 июля 2009

Я нашел этот вопрос / ответ, исследуя, как сделать обнаружение столкновений между спрайтами, используя альфа-значение данных изображения, а не прямоугольную ограничивающую рамку. Контекстом является приложение для iPhone ... Я пытаюсь выполнить вышеизложенное 1-пиксельное рисование, и у меня все еще возникают проблемы с его работой, но я нашел более простой способ создания CGContextRef с использованием данных из самого изображения вспомогательные функции здесь:

CGContextRef context = CGBitmapContextCreate(
                 rawData, 
                 CGImageGetWidth(cgiRef), 
                 CGImageGetHeight(cgiRef), 
                 CGImageGetBitsPerComponent(cgiRef), 
                 CGImageGetBytesPerRow(cgiRef), 
                 CGImageGetColorSpace(cgiRef),
                 kCGImageAlphaPremultipliedLast     
    );

Это обходит все ужасное жесткое кодирование в приведенном выше примере. Последнее значение можно получить, вызвав CGImageGetBitmapInfo (), но в моем случае оно возвращает значение из изображения, которое вызвало ошибку в функции ContextCreate. Только определенные комбинации действительны, как описано здесь: http://developer.apple.com/qa/qa2001/qa1037.html

Надеюсь, это полезно!

3 голосов
/ 25 июня 2009

Нужно ли переводить координаты между UIKit и Core Graphics - т. Е. Инвертирована ли ось Y?

Это возможно. В CGImage данные пикселей в английском порядке чтения: слева направо, сверху вниз. Итак, первый пиксель в массиве - верхний левый; второй пиксель - один слева в верхнем ряду; и т.д.

Предполагая, что у вас есть это право, вы также должны убедиться, что вы смотрите на правильный компонент в пикселе. Возможно, вы ожидаете RGBA, но просите ARGB, или наоборот. Или, возможно, у вас неправильный порядок байтов (я не знаю, что такое порядок байтов в iPhone).

Или я неправильно понял предварительно умноженные альфа-значения?

Это не похоже на это.

Для тех, кто не знает: Предварительно умноженный означает, что цветовые компоненты предварительно умножены альфа; альфа-компонент одинаков, независимо от того, предварительно ли он умножен на цветовые компоненты. Вы можете изменить это (без умножения), разделив компоненты цвета на альфа.

...