iOS

Better iOS code: Wield the power of Core Image

Learn to wield the power of Core Image with this tutorial from Marcio Valenzuela.

Our new tutorial in the TechRepublic iOS App Builder blog is for an app called TRWow. Through it, we will learn to wield the power of Core Image. Since we have a few apps under our belt, I will quickly run through the most basic steps. So let's start by resuming some of the initial steps.

Building the app

Our application will be:

  • Single View Application
  • iPhone
  • ARC & Storyboards

Now let's move to add the CoreImage framework in Target - Build Phases - Link Library.

Move over to the Storyboard and drag a UIImageView to the center of the canvas. Make its size 180(w) x 180(h) at the location 70(x) x 70(y). This can be done with precision using the Dimensions Inspector.

Now open the Assistant Editor and using Ctrl-drag, create an outlet for the UIImageView you dragged onto the storyboard.

We will now import a sample image to our project. Choose the image of your liking but since this will be for the iPhone, try and keep it at around 250 x 250 pixels.

Core Image

Our project now has an image you imported, and an UIImageView we created and connected in order to contain the image. Now we will create a method to add some basic Core Image modifications and call it from vDL. The steps for doing so are as follows:

  1. Create an CIImageObject
  2. Create a Context (basically where you will edit the picture, like CoreData)
  3. Create CIFilter (the mods you will apply)
  4. Get the filter results

The method is quite simple. Let's take a look at it:

-(void)imageTransformation{
//1. Create imagepath
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"png"];
NSURL *fileNameAndPath = [NSURL fileURLWithPath:filePath];
//2. Create image and context objects
CIImage *beginImage = [CIImage imageWithContentsOfURL:fileNameAndPath];
CIContext *context = [CIContext contextWithOptions:nil];
//3. Create filter
CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"
keysAndValues: kCIInputImageKey, beginImage,
@"inputIntensity", @0.7, nil];
CIImage *outputImage = [filter outputImage];
//4. Get filter results
CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]];
//5. Put results into image
UIImage *newImage = [UIImage imageWithCGImage:cgimg];
self.imageView.image = newImage;
//6. Oldschool memory management for CGImageRef object
CGImageRelease(cgimg);
}

Don't forget to call this method from your viewDidLoad in ViewController. Compile and Run to see the result.

Segmented control

Now we want to make our app choose between a few options. So let's add a UISegmented control in order to switch between one option and the other.

Back in storyboard drag a UISegmentedControl below the UIImageView.

Modify segmented control segments by entering 3 into the Attributes Inspector Segments field. Then in the Segment field select each segment in order to give a unique title to each segment. I gave mine the titles 1, 2 and 3.

Add 3 ivars to the ViewController @implementation and modify them in the imageTransformation method so that they use the declared ivars instead of the declaring new local variables:

@implementation ViewController {
CIContext *context;
CIFilter *filter;
CIImage *beginImage;
}

The last step, get the filter results, you do by removing CIFilter and the * symbol before filter, CIContext * before context and CIImage * before beginImage.

Now we are ready to create the methods for each segment and connect those methods to the control.

Methods

First connect the outlet you dragged in. In storyboard select the Assistant Editor and make sure that the ViewController.h is showing in the file pane. (If it isn't, sometimes XCode messes up, using the Bar Navigation Menu at the top, select from ViewController.m to ViewController.h). Ctrl-drag from the segmented control in storyboard to the @interface of ViewController.h and name the outlet: imageTransform. This creates the outlet and connects it for you.

Second, create the IBAction method that will be called when the segmented control is engaged. What we want is to take the value of the control and use it somehow to modify the filter value. So I came up with this:

//Adding UISegmentedControl
-(IBAction)effectChanged {
NSLog(@"effectChanged");
float newValue = _imageTransform.selectedSegmentIndex + 0.5;
//
[filter setValue:@(newValue) forKey:@"inputIntensity"];
CIImage *outputImage = [filter outputImage];
CGImageRef cgimg = [context createCGImage:outputImage
fromRect:[outputImage extent]];
UIImage *newImage = [UIImage imageWithCGImage:cgimg];
self.imageView.image = newImage;
CGImageRelease(cgimg);
}

What we do here is take the control's selected value and add 0.5 to it. We then set the filter value to that of the newValue. Then we use that new filter to get a new image and set it to the UIImageView's image and finally release it again.

Okay, now we will give the user the ability to select the photo from his device in order to apply the filter.

First drag a UIButton to the scene in storyboard. Then open the Assistant Editor and Ctrl-drag from the button to ViewController.h. Now set the Connection to Action and name it choosePhoto with the Event set to TouchUpInside and click Connect.

First, add the protocols you need to use the UIImagePickerController (Apple's native image picking controller). So in the @interface declaration of your ViewController.m file add this:

<UIImagePickerControllerDelegate, UINavigationBarDelegate>

Now add the method you just declared:

- (IBAction)choosePhoto:(id)sender {
UIImagePickerController *pickerC = [[UIImagePickerController alloc] init];
pickerC.delegate = self;
[self presentViewController:pickerC animated:YES completion:nil];
}

In order to actually select the photo from your media library, implement these two methods:

Method 1

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[self dismissViewControllerAnimated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
UIImage *gotImage = [info objectForKey:UIImagePickerControllerOriginalImage];
beginImage = [CIImage imageWithCGImage:gotImage.CGImage];
[filter setValue:beginImage forKey:kCIInputImageKey];
[self effectChanged:self.imageTransform];
}

Method 2

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}

Change the id sender from effectChanged method to (UISegmentedControl*)sender. This way we are able to properly call our method with the right type.

NOTE: Be careful because when changing the method signature of an already connected IBAction method, this connection may break. Make sure the circle next to the IBAction name is filled in. If it isn't, even if you method might actually exist in Storyboard's Outlet Connection Inspector, go ahead and break it from the Connection's Inspector and re-attach it.

Ok so we gave the user the ability to pick a photo, not let's give him the ability to save the modified photo.

First add the AssetsLibrary framework. Then import this:

#import <AssetsLibrary/AssetsLibrary.h>

Now add a new button, the saveButton and connect both the outlet and the action. Then add the savePhoto method like so:

- (IBAction)savePhoto:(id)sender {
// We get the CIImage from the filter
CIImage *saveToSave = [filter outputImage];
// We must create a new context
CIContext *softwareContext = [CIContext
contextWithOptions:@{kCIContextUseSoftwareRenderer : @(YES)} ];
// Create CGImageRef
CGImageRef cgImg = [softwareContext createCGImage:saveToSave
fromRect:[saveToSave extent]];
// Save CGImageRef to library
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:cgImg
metadata:[saveToSave properties]
completionBlock:^(NSURL *assetURL, NSError *error) {
// Release GCImageRef
CGImageRelease(cgImg);
}];
}

Now your app is finished. This is only one very small example of what you can do with Core Image. Look around for more effects we can use to modify our images. You can make your own instagram in no time!

Also read:

About

My name is Marcio Valenzuela and I am from Honduras. I have been coding in iOS for 5 years and I previously worked on web applications in ASP and PHP/mySQL. I have a few apps up in the app store and am currently working on a couple of Cocos2d games...

0 comments

Editor's Picks