Pergunta

Atualmente, estou tentando obter o valor alfa de um pixel em um UIImageView. Obtive o CGImage [imagem UIImageView] e a partir de uma matriz criada rgba byte a partir deste. Alpha é premultiplied.

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

I, em seguida, calcular o índice de matriz para o canal alfa administrada utilizando as coordenadas do UIImageView.

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

No entanto, eu não obter os valores que eu esperava. Para uma área transparente completamente preto da imagem que eu obter valores diferentes de zero para o canal alfa. Eu preciso traduzir as coordenadas entre UIKit e Core Graphics - ou seja: é o eixo Y invertido? Ou ter eu entendi mal valores alfa premultiplied?

Update:

A sugestão de @Nikolai Ruhe foi fundamental para isso. Eu não fez, de facto necessidade de traduzir entre as coordenadas UIKit e coordenadas núcleo gráfico. No entanto, depois de definir o modo de mesclagem meus valores alfa foram o que eu esperava:

CGContextSetBlendMode(context, kCGBlendModeCopy);
Foi útil?

Solução

Sim, CGContexts têm o seu eixo y apontando para cima, enquanto em UIKit ele aponta para baixo. Veja as docs .

Editar após a leitura do código:

Você também deseja definir o modo de mistura para substituir antes de desenhar a imagem desde que você quer o valor da imagem alpha, não aquele que estava em buffer do contexto antes:

CGContextSetBlendMode(context, kCGBlendModeCopy);

Editar Depois de pensar:

Você poderia fazer o lookup muito mais eficiente através da construção o menor possível CGBitmapContext (pixel 1x1 talvez 8x8 ter uma tentativa?) E traduzir o contexto para a posição desejada antes de desenhar:

CGContextTranslateCTM(context, xOffset, yOffset);

Outras dicas

Se tudo que você quer é o valor alfa de um único ponto, tudo que você precisa é de um buffer alfa somente de um único ponto. Eu acredito que este deve 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;

Se o UIImage não tem de ser recriado a cada vez, isso é muito eficiente.

EDIT 8 dezembro de 2011:

A Pontos comentarista que, sob certas circunstâncias, a imagem pode ser invertida. Eu estive pensando sobre isso, e eu sou um pouco de pena que eu não escrever o código usando o UIImage diretamente, como este (eu acho que a razão é que na época eu não entendia 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;

Eu acho que teria resolvido o problema de inversão.

Eu encontrei esta pergunta / resposta ao pesquisar como fazer a detecção de colisão entre sprites usando o valor alfa dos dados de imagem, em vez de uma caixa delimitadora retangular. O contexto é um aplicativo para iPhone ... Eu estou tentando fazer o acima sugerido 1 pixel desenhar e ainda estou tendo problemas para fazer isso para trabalho, mas eu achei uma maneira mais fácil de criar um CGContextRef usando dados de imagem em si, ea funções auxiliares aqui:

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

Isso ignora todo o hardcode feia no exemplo acima. O último valor pode ser recuperado chamando CGImageGetBitmapInfo (), mas no meu caso, ele retorna um valor a partir da imagem que causou um erro na função ContextCreate. Somente certas combinações são válidos como documentado aqui: http://developer.apple.com/qa /qa2001/qa1037.html

Hope isso é útil!

Do I necessidade de traduzir as coordenadas entre UIKit e Core Graphics - ou seja: é o eixo Y invertido

É possível. Em CGImage, os dados de pixel é na ordem de leitura Inglês: da esquerda para a direita, de cima para baixo. Assim, o primeiro pixel na matriz é o superior esquerdo; o segundo pixel é a partir da esquerda na linha superior; etc.

Assumindo que você tem esse direito, você também deve se certificar de que você está olhando para o componente correto dentro de um pixel. Talvez você está esperando RGBA, mas pedindo ARGB, ou vice-versa. Ou, talvez você tem o errado ordem de bytes (Eu não sei o que endianness do iPhone é).

Ou eu mal interpretado premultiplied valores alfa?

Ele não soa como ele.

Para aqueles que não sabem: meios premultiplied que os componentes de cor são não multiplicados pelo alpha; o componente alfa é a mesma, quer os componentes de cor são recalculados por ele ou não. Você pode reverter esta (unpremultiply) dividindo os componentes de cor pela alfa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top