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

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

<code>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);
</code>

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

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

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

Update:

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

<code>CGContextSetBlendMode(context, kCGBlendModeCopy);
</code>
 Ben Gotow28 июн. 2009 г., 21:06
Привет, teabot - я понимаю, что на этот вопрос уже дан ответ, но я делал нечто похожее на это несколько дней назад. Вместо того, чтобы рисовать все изображение и затем индексировать его в массив байтов, вы должныonly draw 1px of the source image into a 1px by 1px bitmap context, Вы можете использовать параметр rect в CGContextDrawImage, чтобы вставить правильный пиксель, и он будет примерно в 1000 раз быстрее :-)
 Kavya Indi30 окт. 2012 г., 12:56
Здравствуйте! Я пытаюсь использовать код, который вы опубликовали, но не смог получить тип данных «rawData», «bytePerPixel». переменные. Можете ли вы сказать мне ответ?
 teabot28 июн. 2009 г., 22:19
Спасибо за комментарий @Ben Gotow - я рисую изображение только один раз, а затем продолжаю использовать один и тот же байтовый массив. @Nikolai Ruhe также предложил использовать однопиксельную отрисовку, но сказал, что мой подход к массиву будет быстрее, если мне не нужно рисовать изображение более одного раза, но необходимо повторно искать альфа.
 roguenet15 мая 2012 г., 02:24
Просто быстрое обновление (спустя годы) после использования этого и другого вопроса здесь для аналогичной проблемы: Параметр rect в CGContextDrawImage используется для управления & quot; Расположение и размеры в пользовательском пространстве ограничительной рамки, в которой рисуется изображение. & Quot; Таким образом, если вы сделаете этот прямоугольник размером 1x1, он уменьшит все изображение до 1x1 перед его рисованием. Чтобы получить правильный пиксель, вам нужно использовать CGContextTranslateCTM, как в ответе Николая, и оставить размер прямоугольника равным исходному изображению, чтобы предотвратить масштабирование.

Ответы на вопрос(4)

Решение Вопроса

Посмотреть документы.

Edit after reading code:

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

CGContextSetBlendMode(context, kCGBlendModeCopy);

Edit after thinking:

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

CGContextTranslateCTM(context, xOffset, yOffset);
 22 сент. 2011 г., 17:03
Я тоже пробую это (и я делаю много поисков - по одному на каждое нажатие). Он прекрасно работает на симуляторе, но не на iPhone 4. Координаты выглядят хорошо (нижний левый угол - 0,0), но тестирование попаданий - беспорядочный беспорядок. Увидетьstackoverflow.com/questions/7506248/…
 25 июн. 2009 г., 13:15
Нет, конечно, вам понадобится изображение, чтобы рисовать его повторно. Мой подход может быть более эффективным для нескольких поисков. Если вы выполняете много поисков, придерживайтесь своего кода.
 teabot25 июн. 2009 г., 12:38
CGImageRef представляет статическое изображение. Получив байтовый массив, я выбрасываю CGImage, а затем повторно использую байтовый массив для альфа-поиска. Будет ли ваша оптимизация работать в этом сценарии?
 teabot25 июн. 2009 г., 12:10
@ Николай Ruhe - Хороший звонок - я должен был прочитать документы.

исследуя, как сделать обнаружение столкновений между спрайтами, используя альфа-значение данных изображения, а не прямоугольную ограничивающую рамку. Контекстом является приложение для 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

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

 13 сент. 2009 г., 21:09
Это также означает, что ваши rawData, которые вы использовали, могут быть слишком маленькими, чтобы содержать растровое изображение, и может быть переполнение буфера.
 13 сент. 2009 г., 21:07
Это полезно Просто имейте в виду, что здесь ваш BytesPerRow не может иметь ширину * bytesPerPixel. Для оптимизации он может быть дополнен до 16-байтовых границ. Когда вы пересекаете rawData, если вы не учитываете это, вы в конечном итоге будете использовать байты заполнения в качестве данных пикселей.
 22 сент. 2011 г., 17:15
Интересно, вот почему у меня возникают проблемы с тем, чтобы заставить это работать на iPhone, но на симуляторе это работает нормально?stackoverflow.com/questions/7506248/…

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

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

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

 02 нояб. 2011 г., 22:27
@MattLeff: потрясающий совет. Спасибо!
 02 нояб. 2011 г., 22:27
@matt: отличный ответ. Спасибо!
 15 окт. 2010 г., 18:45
работает как шарм для меня!
 12 нояб. 2010 г., 06:39
@MattLeff - это имеет смысл, если ваше изображение перевернуто, что может легко произойти из-за несоответствия импеданса между Core Graphics (где y origin внизу) и UIKit (где y origin вверху).
 12 нояб. 2010 г., 00:28
По какой-то причине мне пришлось использовать следующую строку дляCGContextDrawImage(...: CGContextDrawImage(context, CGRectMake(-point.x, -(image.size.height-point.y), CGImageGetWidth(cgim), CGImageGetHeight(cgim)), cgim);

s - i.e: is the y-axis inverted?

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

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

Or have I misunderstood premultiplied alpha values?

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

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

 teabot25 июн. 2009 г., 12:11
@ Питер Хоси - Спасибо за разъяснение, как работает предварительно умноженная альфа - я был неуверен.

Ваш ответ на вопрос