Recuperando un valor alfa de píxel para un UIImage

Actualmente estoy tratando de obtener el valor alfa de un píxel en un UIImageView. Obtuve el CGImage de [UIImageView image] y creé un arreglo de bytes RGBA a partir de esto. Alpha está premultiplicado.

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

Luego calculo el índice de matriz para el canal alfa dado usando las coordenadas del UIImageView.

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

Sin embargo, no obtengo los valores que espero. Para un área transparente completamente negra de la imagen, obtengo valores distintos de cero para el canal alfa. ¿Necesito traducir las coordenadas entre UIKit y Core Graphics? Por ejemplo: ¿está invertido el eje y? ¿O he entendido mal los valores alfa premultiplicados?

Actualizar:

La sugerencia de @Nikolai Ruhe fue clave para esto. De hecho, no necesité traducir entre las coordenadas de UIKit y las coordenadas de Core Graphics. Sin embargo, después de establecer el modo de mezcla, mis valores alfa fueron los que esperaba:

 CGContextSetBlendMode(context, kCGBlendModeCopy); 

Sí, CGContexts tiene su eje y apuntando hacia arriba mientras está en UIKit apunta hacia abajo. Ver los documentos .

Editar después de leer el código:

También desea establecer el modo de mezcla para reemplazar antes de dibujar la imagen, ya que desea el valor alfa de la imagen, no el que estaba en el búfer del contexto antes:

 CGContextSetBlendMode(context, kCGBlendModeCopy); 

Editar después de pensar:

Podrías hacer la búsqueda mucho más eficiente construyendo el mínimo posible CGBitmapContext (1×1 pixel, quizás 8×8? Try) y traducir el contexto a tu posición deseada antes de dibujar:

 CGContextTranslateCTM(context, xOffset, yOffset); 

Si todo lo que quiere es el valor alfa de un solo punto, todo lo que necesita es un buffer de un solo punto alfabético. Creo que esto debería ser suficiente

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

Si el UIImage no tiene que volver a crearse cada vez, esto es muy eficiente.

EDICION el 8 de diciembre de 2011:

Un comentarista señala que bajo ciertas circunstancias, la imagen puede voltearse. He estado pensando en esto, y siento un poco que no haya escrito el código usando el UIImage directamente, así (creo que la razón es que en ese momento no entendía sobre 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; 

Creo que eso habría resuelto el problema de voltear.

Encontré esta pregunta / respuesta mientras investigaba cómo hacer detección de colisión entre sprites usando el valor alfa de los datos de imagen, en lugar de un cuadro delimitador rectangular. El contexto es una aplicación para iPhone … Intento hacer el dibujo de 1 píxel sugerido anteriormente y todavía tengo problemas para hacer que esto funcione, pero encontré una manera más fácil de crear un CGContextRef usando datos de la imagen misma, y ​​el helper funciona aquí:

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

Esto pasa por alto toda la encoding dura fea en la muestra anterior. El último valor se puede recuperar llamando a CGImageGetBitmapInfo () pero en mi caso, devuelve un valor de la imagen que causó un error en la función ContextCreate. Solo ciertas combinaciones son válidas como se documenta aquí: http://developer.apple.com/qa/qa2001/qa1037.html

Espero que esto sea útil!

¿Necesito traducir las coordenadas entre UIKit y Core Graphics? Por ejemplo: ¿está invertido el eje y?

Es posible. En CGImage, los datos de píxeles están en orden de lectura en inglés: de izquierda a derecha, de arriba a abajo. Por lo tanto, el primer píxel en la matriz es la parte superior izquierda; el segundo píxel es uno de la izquierda en la fila superior; etc.

Suponiendo que tienes ese derecho, también debes asegurarte de que estás viendo el componente correcto dentro de un píxel. Quizás esperas RGBA pero solicitas ARGB o viceversa. O quizás tengas el orden de bytes incorrecto (no sé cuál es la endianidad del iPhone).

¿O he entendido mal los valores alfa premultiplicados?

No suena como eso.

Para aquellos que no saben: Premultiplicado significa que los componentes de color son premultiplicados por el alfa; el componente alfa es el mismo si los componentes de color son premultiplicados por él o no. Puede revertir esto (sin prejuicios) dividiendo las componentes de color por alfa.