Pergunta

Eu tenho um UIImage (Cocoa Touch). A partir daí, eu estou feliz de ter uma CGImage ou qualquer outra coisa que você gostaria que está disponível. Eu gostaria de escrever esta função:

- (int)getRGBAFromImage:(UIImage *)image atX:(int)xx andY:(int)yy {
  // [...]
  // What do I want to read about to help
  // me fill in this bit, here?
  // [...]

  int result = (red << 24) | (green << 16) | (blue << 8) | alpha;
  return result;
}

Obrigado!

Foi útil?

Solução

FYI, eu combinei resposta de Keremk com meu esboço original, limpa-up os erros, generalizou-lo para retornar uma matriz de cores e tem a coisa toda para compilar. Aqui está o resultado:

+ (NSArray*)getRGBAsFromImage:(UIImage*)image atX:(int)x andY:(int)y count:(int)count
{
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];

    // First get the image into your data buffer
    CGImageRef imageRef = [image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger 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), imageRef);
    CGContextRelease(context);

    // Now your rawData contains the image data in the RGBA8888 pixel format.
    NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
    for (int i = 0 ; i < count ; ++i)
    {
        CGFloat alpha = ((CGFloat) rawData[byteIndex + 3] ) / 255.0f;
        CGFloat red   = ((CGFloat) rawData[byteIndex]     ) / alpha;
        CGFloat green = ((CGFloat) rawData[byteIndex + 1] ) / alpha;
        CGFloat blue  = ((CGFloat) rawData[byteIndex + 2] ) / alpha;
        byteIndex += bytesPerPixel;

        UIColor *acolor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
        [result addObject:acolor];
    }

  free(rawData);

  return result;
}

Outras dicas

Uma maneira de fazer isso é para desenhar a imagem para um contexto de bitmap que é apoiado por um determinado buffer para um determinado espaço de cores (neste caso, é RGB): (note que isso irá copiar os dados de imagem para que buffer, para que você deseja armazenar em cache-lo em vez de fazer esta operação cada vez que você precisa para obter valores de pixel)

Veja abaixo como um exemplo:

// First get the image into your data buffer
CGImageRef image = [myUIImage CGImage];
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger 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));
CGContextRelease(context);

// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
red = rawData[byteIndex];
green = rawData[byteIndex + 1];
blue = rawData[byteIndex + 2];
alpha = rawData[byteIndex + 3];

Técnico Q & A da Apple QA1509 mostra a seguinte abordagem simples:

CFDataRef CopyImagePixels(CGImageRef inImage)
{
    return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}

Use CFDataGetBytePtr para chegar aos bytes reais (e vários métodos CGImageGet* para entender como interpretá-los).

Não foi possível acreditar que existe não uma única resposta correta aqui. Não há necessidade de alocar os ponteiros, e os valores não multiplicados ainda precisa ser normalizado. Para cortar à perseguição, aqui é a versão correta para Swift 4. Para UIImage apenas .cgImage uso.

extension CGImage {
    func colors(at: [CGPoint]) -> [UIColor]? {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bytesPerPixel = 4
        let bytesPerRow = bytesPerPixel * width
        let bitsPerComponent = 8
        let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo),
            let ptr = context.data?.assumingMemoryBound(to: UInt8.self) else {
            return nil
        }

        context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))

        return at.map { p in
            let i = bytesPerRow * Int(p.y) + bytesPerPixel * Int(p.x)

            let a = CGFloat(ptr[i + 3]) / 255.0
            let r = (CGFloat(ptr[i]) / a) / 255.0
            let g = (CGFloat(ptr[i + 1]) / a) / 255.0
            let b = (CGFloat(ptr[i + 2]) / a) / 255.0

            return UIColor(red: r, green: g, blue: b, alpha: a)
        }
    }
}

Aqui é um segmento SO onde @ Matt torna apenas o pixel de desejado num contexto 1x1 deslocando a imagem, de forma que as alinha de pixel desejados com o um pixel no contexto.

NSString * path = [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"jpg"];
UIImage * img = [[UIImage alloc]initWithContentsOfFile:path];
CGImageRef image = [img CGImage];
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(image));
const unsigned char * buffer =  CFDataGetBytePtr(data);

UIImage é um wrapper os bytes são CGImage ou CIImage

De acordo com a a Apple Referência em UIImage o objeto é imutável e você não tem acesso aos bytes de apoio. Embora seja verdade que você pode acessar os dados CGImage se povoaram a UIImage com um CGImage (explícita ou implicitamente), ele retornará NULL se o UIImage é apoiado por uma CIImage e vice-versa.

Imagem objetos não fornecem acesso direto à sua imagem subjacente dados. No entanto, você pode recuperar os dados de imagem em outros formatos para usar em seu aplicativo. Especificamente, você pode usar o CGImage e ciImage Propriedades para recuperar versões da imagem que são compatíveis com Core Graphics e Core Imagem, respectivamente. Você também pode usar o UIImagePNGRepresentation ( :) e UIImageJPEGRepresentation (: _ :) funções para gerar um objecto NSData contendo os dados de imagem em o formato PNG ou JPEG.

truques comuns para se locomover esta questão

Como afirmado suas opções são

  • UIImagePNGRepresentation ou JPEG
  • Determinar se a imagem tem dados de apoio CGImage ou CIImage e obtê-lo lá

Nenhum destes são particularmente bons truques se você quiser saída que não é ARGB, PNG ou JPEG de dados e os dados não estiver apoiada por CIImage.

A minha recomendação, tente CIImage

Ao desenvolver o seu projecto poderia fazer mais sentido para você evitar UIImage completamente e escolher outra coisa. UIImage, como um invólucro imagem Obj-C, é muitas vezes apoiado por CGImage ao ponto onde nós é um dado adquirido. CIImage tende a ser um melhor formato de embalagem em que você pode usar um CIContext para sair o formato que desejar sem precisar saber como ele foi criado. No seu caso, recebendo o bitmap seria uma questão de chamar

- render: toBitmap: rowBytes: limites: formato: Espaço de cor:

Como um bônus adicionado você pode começar a fazer agradáveis ??manipulações para a imagem encadeando filtros na imagem. Isto resolve um monte de problemas, onde a imagem é para baixo ou necessidades de cabeça a ser girada / escalado etc.

Com base Olie e resposta de algas, aqui é uma resposta atualizados para Swift 3

public func getRGBAs(fromImage image: UIImage, x: Int, y: Int, count: Int) -> [UIColor] {

var result = [UIColor]()

// First get the image into your data buffer
guard let cgImage = image.cgImage else {
    print("CGContext creation failed")
    return []
}

let width = cgImage.width
let height = cgImage.height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let rawdata = calloc(height*width*4, MemoryLayout<CUnsignedChar>.size)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bitsPerComponent = 8
let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

guard let context = CGContext(data: rawdata, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
    print("CGContext creation failed")
    return result
}

context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))

// Now your rawData contains the image data in the RGBA8888 pixel format.
var byteIndex = bytesPerRow * y + bytesPerPixel * x

for _ in 0..<count {
    let alpha = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 3, as: UInt8.self)) / 255.0
    let red = CGFloat(rawdata!.load(fromByteOffset: byteIndex, as: UInt8.self)) / alpha
    let green = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 1, as: UInt8.self)) / alpha
    let blue = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 2, as: UInt8.self)) / alpha
    byteIndex += bytesPerPixel

    let aColor = UIColor(red: red, green: green, blue: blue, alpha: alpha)
    result.append(aColor)
}

free(rawdata)

return result
}

Com base em diferentes respostas, mas principalmente na este , isso funciona para o que eu preciso:

UIImage *image1 = ...; // The image from where you want a pixel data
int pixelX = ...; // The X coordinate of the pixel you want to retrieve
int pixelY = ...; // The Y coordinate of the pixel you want to retrieve

uint32_t pixel1; // Where the pixel data is to be stored
CGContextRef context1 = CGBitmapContextCreate(&pixel1, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst);
CGContextDrawImage(context1, CGRectMake(-pixelX, -pixelY, CGImageGetWidth(image1.CGImage), CGImageGetHeight(image1.CGImage)), image1.CGImage);
CGContextRelease(context1);

Como resultado desta linhas, você terá um pixel em formato AARRGGBB com alfa sempre definido como FF no 4 byte pixel1 inteiro sem sinal.

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