1

I have two UIViews and would like to do a pixel by pixel comparison. Based on this answer here How to get the color of a pixel in an UIView? I have the following method.

- (UIColor *)colorOfPoint:(CGPoint)point view:(UIView *)view {
  unsigned char pixel[4] = {0};
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace,   kCGImageAlphaPremultipliedLast);

  CGContextTranslateCTM(context, -point.x, -point.y);

  [view.layer renderInContext:context];

  CGContextRelease(context);
  CGColorSpaceRelease(colorSpace);

  //NSLog(@"pixel: %d %d %d %d", pixel[0], pixel[1], pixel[2], pixel[3]);

  UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];

  return color;
}

And then to do the pixel comparisons I loop through

- (void)comparePixels {
  for (int height = 0; height <= view1.frame.size.height; height++) {
    for (int width = 0; width <= view1.frame.size.width; width++) {
      UIColor *view1Color = [self colorOfPoint:CGPointMake(height, width) view:view1];
      UIColor *view2Color = [self colorOfPoint:CGPointMake(height, width) view:view2];
      if (![view1Color isEqual:view2Color]) {
        NSLog(@"%d %d", height, width);
      }
    }
  }  
}

So I have two questions: 1) This approach is incredibly slow. Is there a faster way? 2) After several iterations, I sometimes get an exec bad access on the line [view.layer renderInContext:context]. It doesn't always happen but only when the number of pixels to compare is large.

Community
  • 1
  • 1
user1802143
  • 14,662
  • 17
  • 46
  • 55
  • Instead of rendering each time, you could try rendering the entire view once and then just iterating over the pixels. – borrrden Sep 04 '13 at 03:48

1 Answers1

1

A much easier way would be to look at the underlying data and compare it.

Write each UIView into an image. (from this answer)

#import <QuartzCore/QuartzCore.h>

@implementation UIView (Ext)
- (UIImage*) renderToImage
{
  // IMPORTANT: using weak link on UIKit
  if(UIGraphicsBeginImageContextWithOptions != NULL)
  {
    UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0);
  } else {
    UIGraphicsBeginImageContext(self.frame.size);
  }

  [self.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();  
  return image;
}

Compare the bitmaps. You can go word by word, or can make a hash of each

#import <CommonCrypto/CommonDigest.h>:

unsigned char result[16];
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(inImage)];
CC_MD5([imageData bytes], [imageData length], result);
NSString *imageHash = [NSString stringWithFormat:
                       @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
                       result[0], result[1], result[2], result[3], 
                       result[4], result[5], result[6], result[7],
                       result[8], result[9], result[10], result[11],
                       result[12], result[13], result[14], result[15]
                       ];

or compare in innumerable ways that are faster than what you have.

Community
  • 1
  • 1
HalR
  • 11,411
  • 5
  • 48
  • 80
  • Good idea!! I would skip the string part and just compare the two byte arrays though (`result` in this case). Pixel by pixel would be the fastest since you can terminate early. – borrrden Sep 04 '13 at 03:57
  • Yeh, that would be faster. I was just trying to give a good existing solution that he could plug in. You never know what time or experience constraints the op has. (or my time constraints ;) – HalR Sep 04 '13 at 04:00
  • Thanks a lot for the detailed code. How would you retrieve a point for the pixels that are different? – user1802143 Sep 04 '13 at 18:21