Question

J'essaie actuellement d'obtenir la valeur alpha d'un pixel dans un UIImageView. J'ai obtenu le CGImage à partir de [image UIImageView] et créé un tableau d'octets RGBA à partir de celui-ci. L'alpha est prémultiplié.

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

Je calcule ensuite l'index de tableau pour le canal alpha donné en utilisant les coordonnées du UIImageView.

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

Cependant, je ne reçois pas les valeurs que j'attends. Pour une zone totalement noire et transparente de l'image, j'obtiens des valeurs non nulles pour le canal alpha. Dois-je traduire les coordonnées entre UIKit et Core Graphics - c.-à-d. L'axe des ordonnées est-il inversé? Ou ai-je mal compris les valeurs alpha prémultipliées?

Mise à jour:

La suggestion de @Nikolai Ruhe était essentielle à cet égard. En fait, je n'avais pas besoin de traduire entre les coordonnées UIKit et les coordonnées Core Graphics. Cependant, après avoir défini le mode de fusion, mes valeurs alpha correspondaient à ce à quoi je m'attendais:

CGContextSetBlendMode(context, kCGBlendModeCopy);
Était-ce utile?

La solution

Oui, l’axe des y des CGContexts est dirigé vers le haut, alors que celui de l’UIKit est dirigé vers le bas. Voir les docs .

Modifier après avoir lu le code:

Vous souhaitez également définir le mode de fusion à remplacer avant de dessiner l'image car vous voulez la valeur alpha de l'image, pas celle qui était dans la mémoire tampon du contexte auparavant:

CGContextSetBlendMode(context, kCGBlendModeCopy);

Modifiez après avoir réfléchi:

Vous pourriez rendre la recherche beaucoup plus efficace en construisant le plus petit CGBitmapContext possible (1x1 pixel? peut-être 8x8? essayez-vous) et en traduisant le contexte à la position souhaitée avant de dessiner:

CGContextTranslateCTM(context, xOffset, yOffset);

Autres conseils

Si tout ce que vous voulez, c'est la valeur alpha d'un seul point, vous avez simplement besoin d'un tampon à un seul point alpha. Je pense 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;

S'il n'est pas nécessaire de recréer UIImage à chaque fois, c'est très efficace.

EDIT le 8 décembre 2011:

Un intervenant souligne que, dans certaines circonstances, l’image peut être retournée. J'ai réfléchi à cela et je suis un peu désolé de n'avoir pas écrit le code directement à l'aide de UIImage, comme ceci (je pense que la raison en est qu'à l'époque je n'avais pas compris le code ):

// 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 cela aurait résolu le problème de retournement.

J'ai trouvé cette question / réponse alors que je cherchais un moyen de détecter les collisions entre des images-objets à l'aide de la valeur alpha des données d'image, plutôt que d'un cadre de sélection rectangulaire. Le contexte est une application iPhone ... J'essaie de dessiner le dessin de 1 pixel suggéré ci-dessus et j'ai toujours du mal à le faire fonctionner, 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 le fonctions d'assistance ici:

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

Ceci contourne tous les codes durs laids dans l'exemple ci-dessus. La dernière valeur peut être récupérée en appelant CGImageGetBitmapInfo () mais dans mon cas, elle renvoie une valeur de l'image qui a provoqué une erreur dans la fonction ContextCreate. Seules certaines combinaisons sont valables comme indiqué ici: http://developer.apple.com/qa /qa2001/qa1037.html

J'espère que cela vous sera utile!

  

Dois-je traduire les coordonnées entre UIKit et Core Graphics - c.-à-d. l'axe des ordonnées est-il inversé?

C'est possible. Dans CGImage, les données de pixel sont dans l'ordre de lecture en anglais: de gauche à droite, de haut en bas. Ainsi, le premier pixel de la matrice est le coin supérieur gauche; le deuxième pixel est l'un de la gauche sur la rangée supérieure; etc.

En supposant que vous avez ce droit, vous devez également vous assurer que vous regardez le bon composant dans un pixel. Peut-être que vous vous attendez à RGBA mais que vous demandez ARGB, ou inversement. Ou alors, vous avez peut-être un mauvais ordre d'octet (je ne sais pas ce qu'est l'endianité de l'iPhone).

  

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

Cela ne sonne pas comme ça.

Pour ceux qui ne savent pas: Prémultipliée signifie que les composantes de couleur sont prémultipliées par l’alpha; la composante alpha est la même, que les composantes de couleur soient prémultipliées par elle ou non. Vous pouvez inverser cette étape (sans multiplication) en divisant les composantes de la couleur par l'alpha.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top