Recuperando um valor alfa de pixel para um UIImage
-
22-07-2019 - |
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);
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.