35 votes

Récupérer une valeur alpha en pixel pour un UIImage

Je suis en train d'essayer d'obtenir l'alpha de la valeur d'un pixel dans une UIImageView. J'ai obtenu le CGImage de [UIImageView image] et créé un RGBA tableau d'octets à partir de ce. Alpha est prémultipliée.

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

J'ai ensuite calculer l'index de tableau pour le canal alpha en utilisant les coordonnées de la UIImageView.

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

Cependant je n'ai pas les valeurs que j'attends. Pour un tout noir zone transparente de l'image, j'obtiens des valeurs non nulles pour le canal alpha. Ai-je besoin de traduire les coordonnées entre UIKit et Graphiques de Base - je.e: est l'axe y inversé? Ou ai-je mal compris prémultiplié valeurs alpha?

Mise à jour:

@Nicolas Ruhe la suggestion a été la clé de cette. Je n'ai pas besoin de traduire entre les UIKit coordonnées et Graphiques de Base des coordonnées. Cependant, après avoir défini le mode de fusion de mes valeurs alpha ont été ce que j'attendais:

CGContextSetBlendMode(context, kCGBlendModeCopy);

36voto

matt Points 60113

Si tout ce que vous voulez, c'est la valeur alpha d'un seul point, tous vous avez besoin est un alpha-seulement un seul point de la mémoire tampon. Je crois que cela devrait suffire:

// 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 le UIImage n'a pas à être recréé à chaque fois, c'est très efficace.

EDIT 8 décembre 2011:

Un intervenant souligne que, dans certaines circonstances, l'image peut être retourné. J'ai pensé à ce sujet, et je suis un peu désolé de ne pas écrire le code à l'aide de la UIImage directement, comme ça (je pense que la raison est qu'à l'époque je ne comprenais pas sur 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;

Je pense que ça aurait résolu le retournement de question.

10voto

Nikolai Ruhe Points 45433

Oui, CGContexts ont leur axe y vers le haut tandis que dans UIKit il pointe vers le bas. Voir les docs.

Edit après lecture de code:

Vous voulez aussi de définir le mode de fusion pour remplacer avant de dessiner l'image que vous voulez l'image de l'alpha de la valeur, et non pas celui qui était dans le contexte de la mémoire tampon avant:

CGContextSetBlendMode(context, kCGBlendModeCopy);

Edit après réflexion:

Vous pourriez faire la recherche de beaucoup plus efficace par la création de la plus petite possible CGBitmapContext (1x1 pixel? peut-être 8x8? avoir un essai) et traduire le contexte de votre position souhaitée avant le dessin:

CGContextTranslateCTM(context, xOffset, yOffset);

3voto

Peter Hosey Points 66275

Ai-je besoin de traduire les coordonnées entre UIKit et Graphiques de Base - je.e: est l'axe y inversé?

Il est possible de faire. Dans CGImage, les données de pixel est en anglais de l'ordre de lecture: de gauche à droite, de haut en bas. Donc, le premier pixel de la matrice est en haut à gauche; le second pixel est à partir de la gauche sur la ligne supérieure; etc.

En supposant que vous avez ce droit, vous devez également vous assurer que vous êtes en train de regarder le bon composant au sein d'un pixel. Peut-être que vous êtes enceinte RGBA mais demandant de l'ARGB, ou vice versa. Ou, vous avez peut-être l'ordre des octets faux (je ne sais pas ce que l'iPhone est endianness est).

Ou ai-je mal compris prémultiplié valeurs alpha?

Il ne ressemble pas à elle.

Pour ceux qui ne connaissent pas: Prémultiplié signifie que les composantes de couleur sont prémultiplié par l'alpha; la composante alpha est le même, que les composantes de couleur sont prémultiplié par elle ou pas. Vous pouvez inverser cette (unpremultiply) en divisant les composants de la couleur par l'alpha.

3voto

Owyn Points 31

J'ai trouvé cette question/réponse, alors que des recherches sur la façon de faire de la détection de collision entre les sprites à l'aide de la valeur alpha de l'image des données, plutôt que d'un cadre de sélection rectangulaire. Le contexte est une application iPhone... je suis en train de faire le ci-dessus suggère de 1 pixel de tirage au sort et je suis toujours avoir des problèmes pour obtenir que cela fonctionne, mais j'ai trouvé un moyen plus facile de créer un CGContextRef en utilisant les données de l'image elle-même, et les fonctions d'assistance ici:

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

Ce n'est pas le vilain coder en dur dans l'exemple ci-dessus. La dernière valeur peut être récupérée en appelant CGImageGetBitmapInfo() mais dans mon cas, il renvoie une valeur à partir de l'image qui a provoqué une erreur dans le ContextCreate fonction. Seules certaines combinaisons sont valables, comme indiqué ici: http://developer.apple.com/qa/qa2001/qa1037.html

Espérons que cela est utile!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X