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

35

В настоящее время я пытаюсь получить альфа-значение пикселя в 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>
Здравствуйте! Я пытаюсь использовать код, который вы опубликовали, но не смог получить тип данных «rawData», «bytePerPixel». переменные. Можете ли вы сказать мне ответ? Kavya Indi
Привет, teabot - я понимаю, что на этот вопрос уже дан ответ, но я делал нечто похожее на это несколько дней назад. Вместо того, чтобы рисовать все изображение и затем индексировать его в массив байтов, вы должныonly draw 1px of the source image into a 1px by 1px bitmap context, Вы можете использовать параметр rect в CGContextDrawImage, чтобы вставить правильный пиксель, и он будет примерно в 1000 раз быстрее :-) Ben Gotow
Просто быстрое обновление (спустя годы) после использования этого и другого вопроса здесь для аналогичной проблемы: Параметр rect в CGContextDrawImage используется для управления & quot; Расположение и размеры в пользовательском пространстве ограничительной рамки, в которой рисуется изображение. & Quot; Таким образом, если вы сделаете этот прямоугольник размером 1x1, он уменьшит все изображение до 1x1 перед его рисованием. Чтобы получить правильный пиксель, вам нужно использовать CGContextTranslateCTM, как в ответе Николая, и оставить размер прямоугольника равным исходному изображению, чтобы предотвратить масштабирование. roguenet
Спасибо за комментарий @Ben Gotow - я рисую изображение только один раз, а затем продолжаю использовать один и тот же байтовый массив. @Nikolai Ruhe также предложил использовать однопиксельную отрисовку, но сказал, что мой подход к массиву будет быстрее, если мне не нужно рисовать изображение более одного раза, но необходимо повторно искать альфа. teabot

Ваш Ответ

4   ответа
3

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

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

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

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

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

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

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

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

Edit after reading code:

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

CGContextSetBlendMode(context, kCGBlendModeCopy);

Edit after thinking:

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

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

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

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

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

Or have I misunderstood premultiplied alpha values?

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

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

@ Питер Хоси - Спасибо за разъяснение, как работает предварительно умноженная альфа - я был неуверен. teabot

Похожие вопросы