2

My app has several methods partly based on this article.

Images, where I need to find the color at a location could come from the camera, the library, the app bundle, or generated within the app. I've just run into an issue where the order of the channels is different (it happens that the ones that are different are the ones generated by the app and saved in jpeg format for cache purposes).

So my question is, how, preferably using the link as a starting point, can I determine the color order of a CGImage? I'm sure this is documented somewhere, but where?

Using a method I've called +imageDump:, inspired by this article I extract the following information from one image:

CGImageGetHeight: 120
CGImageGetWidth:  120
CGImageGetColorSpace: <CGColorSpace 0x1c562050> (kCGColorSpaceDeviceRGB)
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      480
CGImageGetBitmapInfo: 0x00002002
  kCGBitmapAlphaInfoMask     = YES
  kCGBitmapFloatComponents   = NO
  kCGBitmapByteOrderMask     = YES
  kCGBitmapByteOrderDefault  = NO
  kCGBitmapByteOrder16Little = NO
  kCGBitmapByteOrder32Little = YES
  kCGBitmapByteOrder16Big    = YES
  kCGBitmapByteOrder32Big    = NO

and from another image, with the channels in a different order:

CGImageGetHeight: 120
CGImageGetWidth:  120
CGImageGetColorSpace: <CGColorSpace 0x1c562050> (kCGColorSpaceDeviceRGB)
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      480
CGImageGetBitmapInfo: 0x00000001
  kCGBitmapAlphaInfoMask     = YES
  kCGBitmapFloatComponents   = NO
  kCGBitmapByteOrderMask     = NO
  kCGBitmapByteOrderDefault  = NO
  kCGBitmapByteOrder16Little = NO
  kCGBitmapByteOrder32Little = NO
  kCGBitmapByteOrder16Big    = NO
  kCGBitmapByteOrder32Big    = NO

It seems like this information somehow specifies the channel order, but I don't know how to interpret these constants. Or am I looking at the wrong thing?

Community
  • 1
  • 1
Victor Engel
  • 2,037
  • 2
  • 25
  • 46
  • Did you figure this out? I cannot figure out if my pixels are ARGB, RGBA, RGB, BGR, BGRA, etc. – bmauter Apr 21 '15 at 17:16
  • Sorry, it's been so long since I've worked on this project that I don't recall what I did. A quick glance at the code referenced this stack overflow article, though. Perhaps it will help you out. http://stackoverflow.com/questions/144250/how-to-get-the-rgb-values-for-a-pixel-on-an-image-on-the-iphone FYI, I added my own answer mainly to provide an answer to your question. – Victor Engel Apr 27 '15 at 00:25

2 Answers2

2

I'm posting this answer mainly to answer a comment to my question. Here is the solution I used:

- (UIColor*)colorFromImage:(UIImage*)image sampledAtPoint:(CGPoint)p {
   // from http://stackoverflow.com/questions/144250/how-to-get-the-rgb-values-for-a-pixel-on-an-image-on-the-iphone
   //DLog(@"Image dump call from colorFromImage:sampledAtPoint:");
   //[Utilities imageDump:image];
   int radius = 3;
   CGImageRef cgImage = [image CGImage];
   CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
   CFDataRef bitmapData = CGDataProviderCopyData(provider);
   const UInt8* data = CFDataGetBytePtr(bitmapData);
   size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
   size_t width = CGImageGetWidth(cgImage);
   size_t height = CGImageGetHeight(cgImage);
   CGBitmapInfo info = CGImageGetBitmapInfo(cgImage);

   long col = p.x*(width-1);
   long row = p.y*(height-1);
   long cnt = 0;long redval=0,greenval=0,blueval=0;
   for (int rrow = row - radius; rrow < row + radius; rrow++) {
      for (int ccol = col - radius; ccol < col + radius; ccol++) {
         if ((rrow >= 0) && (ccol >= 0)) {
            if ((rrow < height) && (ccol < width)) {
               const UInt8* pixel = data + rrow*bytesPerRow+ccol*4;
               int tempred,tempgreen,tempblue,tempalpha;
               if (info & kCGBitmapByteOrder32Little) {
                  tempred = pixel[2];
                  tempgreen = pixel[1];
                  tempblue = pixel[0];
                  tempalpha = pixel[3];
               } else {
                  tempred = pixel[0];
                  tempgreen = pixel[1];
                  tempblue = pixel[2];
                  tempalpha = pixel[3];
               }
               //DLog(@"%d %d %d %d",tempred,tempgreen,tempblue,tempalpha);
               if (tempalpha==255) {
                  redval += tempred;
                  greenval += tempgreen;
                  blueval += tempblue;
                  cnt++;
               }
            }
         }
      }
   }
   UIColor *returnColor = [UIColor colorWithRed:1.0f*redval/(cnt*255) green:1.0f*greenval/(cnt*255) blue:1.0f*blueval/(cnt*255) alpha:1.0];
   CFRelease(bitmapData);//take care of memory leak
   return returnColor;
}

I seem to recall that it wasn't a complete solution but was sufficient for my own purposes.

Victor Engel
  • 2,037
  • 2
  • 25
  • 46
  • In your if condition to determine if it's BGRA vs RGBA, you have `info & kCGBitmapByteOrder32Little` -- did you actually mean `(bitmapInfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little` ? – chinabuffet Aug 25 '15 at 11:56
  • There is no variable name bitmapInfo. There is, however, a variable name info, which is type CGBitmapInfo. – Victor Engel Aug 27 '15 at 14:23
  • Sorry, I have it named correctly in the first snippet in my comment, but misspelled it in the second snippet. Should have read: `(info & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little)` -- So my question don't you need to use the bitwise operator on the mask, and then compare it to the constant to check the condition? – chinabuffet Aug 27 '15 at 15:00
  • Hmmm - I don't think so. The kCGBitmapByteOrder32Little constant I think resolves to only a single bit. So anding it with anything is just one bit. We just check what the value of the bit is. Your check extracts the mask from the info first and then checks if it matches the constant. It arrives at the same conclusion in two steps instead of one. – Victor Engel Aug 28 '15 at 00:15
  • Ahh... I think my problem was that I was storing off the result of the bitwise operation into a BOOL so that I could give it a meaningful variable name to describe the intention behind its use. The conversion to BOOL was boofin' it. Thanks! – chinabuffet Aug 28 '15 at 11:35
0

You need to use the CGImageGetAlphaInfo, CGImageGetBitmapInfo, CGImageGetBitsPerComponent, and CGImageGetBitsPerPixel functions to determine the layout of the data. The combination of the values will allow you to determine what components there are, their size, and their order.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Right. I'm looking at that information, and I'm not sure how to interpret it. I'll update my question with details. – Victor Engel Oct 01 '13 at 17:50