views:

12459

answers:

5

I have a UIImage (Cocoa Touch). From that, I'm happy to get a CGImage or anything else you'd like that's available. I'd like to write this function:

- (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;
}

Thanks!

+9  A: 

One way of doing it is to draw the image to a bitmap context that is backed by a given buffer for a given colorspace (in this case it is RGB): (note that this will copy the image data to that buffer, so you do want to cache it instead of doing this operation every time you need to get pixel values)

See below as a sample:

// First get the image into your data buffer
CGImageRef image = [UIImage 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];
keremk
I did something similar in my app. To extract an arbitrary pixel, you just draw into a 1x1 bitmap with a known bitmap format, adjusting the origin of the CGBitmapContext appropriately.
Mark Bessey
That sounds interestign. Could you post an example as an answer to this question?
Ian1971
+1  A: 

i understand the above code but i don't know what does xx and yy means. can anybody tell me about that two variables. i don't know where are these variables defined. in the above code i havn't seen those variables defined.

They are the x (horizontal) and y (vertical) co-ordinates of the pixel to retrieve the colors of.
Peter Hosey
See the original question. They are passed in to the function; this answer's code goes between the { and }.
Olie
+6  A: 

Apple's Technical Q&A QA1509 shows the following simple approach:

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

Use CFDataGetBytePtr to get to the actual bytes (and various CGImageGet* methods to understand how to interpret them).

yakovlev
This is not a great approach because the pixel format tends to vary a lot per image. There are several things that can change, including 1. the orientation of the image 2. the format of the alpha component and 3. the byte order of RGB. I've personally spent some time trying to decode Apple's docs on how to do this, but I'm not sure it's worth it. If you want it done fast, just keremk's solution.
Tyler
+7  A: 

FYI, I combined Keremk's answer with my original outline, cleaned-up the typos, generalized it to return an array of colors and got the whole thing to compile. Here is the result:

+ (NSArray*)getRGBAsFromImage:(UIImage*)image atX:(int)xx andY:(int)yy 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 = 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), imageRef);
    CGContextRelease(context);

    // Now your rawData contains the image data in the RGBA8888 pixel format.
    int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
    for (int ii = 0 ; ii < count ; ++ii)
    {
        CGFloat red   = (rawData[byteIndex]     * 1.0) / 255.0;
        CGFloat green = (rawData[byteIndex + 1] * 1.0) / 255.0;
        CGFloat blue  = (rawData[byteIndex + 2] * 1.0) / 255.0;
        CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.0;
        byteIndex += 4;

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

  free(rawData);

  return result;
}
Olie
(Stripping out the array thing and getting it to return just 1 color should be easy enough. I wanted to grab a bunch of them, as long as I was creating the buffer.)
Olie
thanks! beauty...
John Ballinger
Don't forget to unpremultiply the colors. Also, those multiplications by 1 don't do anything.
Peter Hosey
What would you need to set 'count' to if you wanted to scan the whole image?
Tom Irving
@Peter: Correct, they're left-over from when I was doing color-fiddling.@Tom: to scan the entire image, it'd be count = width*height, right? For me, I added count because, in my special images, the first 6 pixels contained special color values, and that's what I wanted back in my array.
Olie
A: 

how do i make this find the average pixel color from the image and use that for the color?

jake
You should post this as a new question, not as an answer (since it isn't an answer to this question).
sth
Olie