Подтвердить что ты не робот

Получение значения альфа-пикселя для UIImage

В настоящее время я пытаюсь получить альфа-значение пикселя в UIImageView. Я получил CGImage из изображения [UIImageView] и создал из него массив байтов 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 - i.e: инвертирована ли ось y? Или я неправильно понял предварительные значения альфа?

Update:

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

CGContextSetBlendMode(context, kCGBlendModeCopy);
4b9b3361

Ответ 1

Да, CGContexts имеют свою ось y, указывающую вверх, в то время как в UIKit он указывает вниз. См. документы.

Изменить код чтения:

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

CGContextSetBlendMode(context, kCGBlendModeCopy);

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

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

CGContextTranslateCTM(context, xOffset, yOffset);

Ответ 2

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

// 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 не нужно воссоздавать каждый раз, это очень эффективно.

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

Я думаю, что это решило бы проблему с переворачиванием.

Ответ 3

Нужно ли переводить координаты между UIKit и Core Graphics - i.e: инвертирована ли ось y?

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

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

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

Звучит не так.

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

Ответ 4

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

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

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

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