Facial obfuscation, once the domain of criminals and confidential informants, is now available for the whole family. Today I’m going to show you how to use the new Core Image framework in iOS 5 to auto-detect and hide faces in your photos.

Awkward family photos?  Not anymore!

Protect you and your family from awkward photos.

Auto-hiding faces can be accomplished in 3 short steps:

  1. Prep a photo for Core Image.
  2. Find faces in a given photo.
  3. Pixelate each detected face.

Prepping a photo for Core Image
The first thing we need to do is prep our photo for Core Image by flipping it on its Y-axis. This will allow us to match coordinate space Core Image uses.

inputImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"my_image_name.jpg"]];
[self.view addSubview:inputImage];
[photo setTransform:CGAffineTransformMakeScale(1, -1)];
[self.view setTransform:CGAffineTransformMakeScale(1, -1)];

Detecting faces in a photo
Next, using our photo, we create a new instance of CIImage to work with.

CIImage *coreImage = [CIImage imageWithCGImage:photo.image.CGImage];

We also create an instance of CIDetector that detects faces with a high level of accuracy.

detector = [CIDetector detectorOfType:CIDetectorTypeFace
                                  context:nil
                                  options:[NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh
                                                                      forKey:CIDetectorAccuracy]];

To find all the face features of our image, we can call the detector’s featuresInImage: method, passing in the core image we just created.

NSArray *faces = [detector featuresInImage:coreImage];

We can now loop through our faces array to ‘pixelate’ each of them.

Rendering a pixelated face
Before rendering a pixelated face we need to flip the bounds of the face feature we are working with to match Core Image’s coordinate space.

face.bounds.origin.y = photo.bounds.size.height - face.bounds.origin.y - face.bounds.size.height;

We next create a UIView overlay our pixelated image overtop of our photo.

UIView *faceView = [[UIView alloc] initWithFrame:face.bounds];

Since we only want to pixelate a face, and not the entire photo, we create a new CGIMageRef and then use CGImageCreateWithImageInRect() and face.bounds to clip out out the current face.

CGImageRef imageRef = CGImageCreateWithImageInRect([photo.image CGImage], rect);
UIImage *faceImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);

To get a ‘pixelated’ effect we first shrink the image (in this example by a factor of 10), and then scale it to it’s original size.

faceImage = [self resizeImage:faceImage newSize:CGSizeMake(rect.size.width * .1, rect.size.height * .1)];
faceImage = [self resizeImage:faceImage newSize:CGSizeMake(rect.size.width, rect.size.height)];

....

/*
 Resize the image using the given size.
 */
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {

    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
    CGImageRef imageRef = image.CGImage;

    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Set the quality level
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);

    CGContextConcatCTM(context, flipVertical); 

    // Draw into the context, scaling the image
    CGContextDrawImage(context, newRect, imageRef);

    // Get the resized image from the context and make a new UIImage
    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

    CGImageRelease(newImageRef);
    UIGraphicsEndImageContext();    

    return newImage;
}

Finally, we create a texture from our newly pixelated faceImage and draw it onto the faceView UIView we created earlier.

UIColor *faceTexture = [UIColor colorWithPatternImage:faceImage];
faceView.layer.backgroundColor = faceTexture.CGColor;
[self.view addSubview:faceView];

And that’s it! Auto-anonymity! Below is the source in it’s entirety. Feel free to download the project’s source to play around with it.

The final result.

The final result.

//
//  ViewController.m
//  Instavert
//
//  Created by Nathanael De Jager on 11-12-30.
//  Copyright (c) 2011 Nathanael de Jager. All rights reserved.
//

#import "ViewController.h"

@implementation ViewController

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
	[self setup];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

#pragma mark - Initialization
/*
 Load and initialize a picture for face detection / pixelation.
 */
- (void)setup
{
    inputImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"my_image_name.jpg"]];
    [self.view addSubview:inputImage];
    [self performSelectorInBackground:@selector(detectFaces:) withObject:inputImage];
    [self prepareImage:inputImage];
}

/*
 Prep the image for Core Image by flipping it on the Y-axis.
 */
- (void)prepareImage:(UIImageView *)photo
{
    [photo setTransform:CGAffineTransformMakeScale(1, -1)];
    [self.view setTransform:CGAffineTransformMakeScale(1, -1)];
}

#pragma mark - Detection and drawing

/*
 Detects and processes faces found in the provided photo
 */
- (void)detectFaces:(UIImageView *)photo
{
    
    CIImage *coreImage = [CIImage imageWithCGImage:photo.image.CGImage];
    
    detector = [CIDetector detectorOfType:CIDetectorTypeFace 
                                  context:nil 
                                  options:[NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh 
                                                                      forKey:CIDetectorAccuracy]];
    NSArray *faces = [detector featuresInImage:coreImage];
    
    for (CIFaceFeature *face in faces)
    {
        [self renderFaceForFaceFeature:face fromImage:photo];
    }
    
}

/*
 Renders the pixelated face based on the provided feature.
 */
- (void)renderFaceForFaceFeature:(CIFaceFeature *)faceFeature fromImage:(UIImageView *)photo
{
    UIView *faceView = [[UIView alloc] initWithFrame:faceFeature.bounds];
    UIImage *faceImage = [self faceImageFromImage:photo forRect:faceFeature.bounds];
    
    UIColor *faceTexture = [UIColor colorWithPatternImage:faceImage];
    faceView.layer.backgroundColor = faceTexture.CGColor;

    [self.view addSubview:faceView];
}


- (UIImage *)faceImageFromImage:(UIImageView *)photo forRect:(CGRect)rect
{   
    // flip the rect to match the current coordinate space
    rect.origin.y = photo.bounds.size.height - rect.origin.y - rect.size.height;
    
    CGImageRef imageRef = CGImageCreateWithImageInRect([photo.image CGImage], rect);
    UIImage *faceImage = [UIImage imageWithCGImage:imageRef]; 
    CGImageRelease(imageRef);

    faceImage = [self resizeImage:faceImage newSize:CGSizeMake(rect.size.width * .1, rect.size.height * .1)];
    faceImage = [self resizeImage:faceImage newSize:CGSizeMake(rect.size.width, rect.size.height)];
    
    return faceImage;
    
}

/*
 Resize the image using the given size.
 */
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
    
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
    CGImageRef imageRef = image.CGImage;
    
    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // Set the quality level
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
    
    CGContextConcatCTM(context, flipVertical); 
    
    // Draw into the context, scaling the image
    CGContextDrawImage(context, newRect, imageRef);
    
    // Get the resized image from the context and make a new UIImage
    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
    
    CGImageRelease(newImageRef);
    UIGraphicsEndImageContext();    
    
    return newImage;
}
@end

Comments are closed.