Domanda

Attualmente sto cercando di ottenere il valore alfa di un pixel in UIImageView. Ho ottenuto il CGImage da [UIImageView image] e da questo ho creato un array di byte RGBA. Alpha è premoltiplicato.

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

Calcolo quindi l'indice di array per il dato canale alfa usando le coordinate di UIImageView.

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

Tuttavia non ottengo i valori che mi aspetto. Per un'area trasparente completamente nera dell'immagine ottengo valori diversi da zero per il canale alfa. Devo tradurre le coordinate tra UIKit e Core Graphics, ovvero: l'asse y è invertito? O ho frainteso i valori alfa premoltiplicati?

Aggiornamento:

Il suggerimento di @Nikolai Ruhe è stato fondamentale. In realtà non avevo bisogno di tradurre tra le coordinate UIKit e le coordinate Core Graphics. Tuttavia, dopo aver impostato la modalità di fusione i miei valori alfa erano quelli che mi aspettavo:

CGContextSetBlendMode(context, kCGBlendModeCopy);
È stato utile?

Soluzione

Sì, i CGContexts hanno il loro asse y rivolto verso l'alto mentre in UIKit punta verso il basso. Consulta i documenti .

Modifica dopo aver letto il codice:

Devi anche impostare la modalità di fusione da sostituire prima di disegnare l'immagine poiché vuoi il valore alfa dell'immagine, non quello che era nel buffer del contesto prima:

CGContextSetBlendMode(context, kCGBlendModeCopy);

Modifica dopo aver pensato:

Potresti fare la ricerca molto in modo più efficiente costruendo il CGBitmapContext più piccolo possibile (1x1 pixel? forse 8x8? provalo) e traducendo il contesto nella posizione desiderata prima di disegnare:

CGContextTranslateCTM(context, xOffset, yOffset);

Altri suggerimenti

Se tutto ciò che vuoi è il valore alfa di un singolo punto, tutto ciò che serve è un buffer a punto singolo solo alfa. Credo che questo dovrebbe bastare:

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

Se UIImage non deve essere ricreato ogni volta, questo è molto efficiente.

EDIT 8 dicembre 2011:

Un commentatore sottolinea che in determinate circostanze l'immagine può essere capovolta. Ci ho pensato, e mi dispiace un po 'di non aver scritto direttamente il codice usando UIImage, in questo modo (penso che il motivo sia che al momento non ho capito 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;

Penso che avrebbe risolto il problema del lancio.

Ho trovato questa domanda / risposta mentre cercavo come fare il rilevamento delle collisioni tra gli sprite usando il valore alfa dei dati dell'immagine, piuttosto che un rettangolo di selezione rettangolare. Il contesto è un'app per iPhone ... Sto provando a disegnare 1 pixel suggerito sopra e ho ancora problemi a farlo funzionare, ma ho trovato un modo più semplice di creare un CGContextRef usando i dati dall'immagine stessa, e il funzioni di supporto qui:

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

Questo ignora tutti i brutti hardcoding nell'esempio sopra. L'ultimo valore può essere recuperato chiamando CGImageGetBitmapInfo () ma nel mio caso restituisce un valore dall'immagine che ha causato un errore nella funzione ContextCreate. Solo alcune combinazioni sono valide come documentato qui: http://developer.apple.com/qa /qa2001/qa1037.html

Spero che sia utile!

  

Devo tradurre le coordinate tra UIKit e Core Graphics, ovvero: l'asse y è invertito?

È possibile. In CGImage, i dati dei pixel sono in ordine di lettura inglese: da sinistra a destra, dall'alto verso il basso. Quindi, il primo pixel nell'array è in alto a sinistra; il secondo pixel è uno da sinistra nella riga superiore; ecc.

Supponendo che tu abbia questo diritto, dovresti anche assicurarti di guardare il componente corretto all'interno di un pixel. Forse ti aspetti RGBA ma stai chiedendo ARGB o viceversa. O forse hai sbagliato l'ordine dei byte (non so quale sia l'endianness dell'iPhone).

  

O ho frainteso i valori alfa premoltiplicati?

Non sembra.

Per chi non lo sapesse: Premoltiplicato significa che i componenti del colore sono premoltiplicati per l'alfa; il componente alfa è lo stesso indipendentemente dal fatto che i componenti del colore siano premoltiplicati o meno. Puoi invertire questo (non premettere) dividendo i componenti del colore per l'alfa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top