You can apply Core Image filters to images in any format supported by Mac OS X which, in Mac OS X v10.5 and later, includes RAW image data (see “RAW Image Options” in CIFilter Class Reference).
The steps to process an image with a Core Image filter are:
Create a CIContext object.
Get the image to process.
Create a CIFilter object for the filter to apply to the image.
Set the default values for the filter.
Set the filter parameters.
Apply one or more filters.
Draw the processed image.
The details for performing each step are in the sections that follow. You’ll see how to apply three filters to the image shown in Figure 2-1.
Create a Core Image Context
Get the Image to Process
Create, Set Up, and Apply Filters
Draw the Result
In Core Image, images are evaluated to a Core Image context which represents a drawing destination. You can create a Core Image context:
By calling the CIContext method of the NSGraphicsContext class
From a Quartz 2D graphics context
From an OpenGL graphics context
You create one Core Image context per window rather than one per view.
The CIContext method of the NSGraphicsContext class returns a CIContext object that you can use to render into the NSGraphicsContext object. The CIContext object is created on demand and remains in existence for the lifetime of its owning NSGraphicsContext object. You create the Core Image context using a line of code similar to the following:
[[NSGraphicsContext currentContext] CIContext]
For more information on this method, see NSGraphicsContext Class Reference.
You can create a Core Image context from a Quartz 2D graphics context using code similar to that shown in Listing 2-3, which is an excerpt from the drawRect: method in a Cocoa application. You get the current NSGraphicsContext, convert that to a Quartz 2D graphics context (CGContextRef), and then provide the Quartz 2D graphics context as an argument to the contextWithCGContext:options: method of the CIContext class. For information on Quartz 2D graphics contexts, see Quartz 2D Programming Guide.
Listing 2-3 Creating a Core Image context from a Quartz 2D graphics context
if(context == nil) |
{ |
context = [CIContext contextWithCGContext: |
[[NSGraphicsContext currentContext] graphicsPort] |
options: nil] |
[context retain];} |
The code in Listing 2-4 shows how to set up a Core Image context from the current OpenGL graphics context. It’s important that the pixel format for the context includes the NSOpenGLPFANoRecovery constant as an attribute. Otherwise Core Image may not be able to create another context that share textures with this one. You must also make sure that you pass a pixel format whose data type is CGLPixelFormatObj, as shown in the listing. For more information on pixel formats and OpenGL, see OpenGL Programming Guide for Mac OS X.
Listing 2-4 Creating a Core Image context from an OpenGL graphics context
CIContext *myCIContext; |
const NSOpenGLPixelFormatAttribute attr[] = { |
NSOpenGLPFAAccelerated, |
NSOpenGLPFANoRecovery, |
NSOpenGLPFAColorSize, 32, |
0 |
}; |
pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:(void *)&attr]; |
myCIContext = [CIContext contextWithCGLContext: CGLGetCurrentContext() |
pixelFormat: [pf CGLPixelFormatObj] |
options: nil]; |
Core Image filters process Core Image images (CIImage objects). Table 2-5 lists the methods that create a CIImage object. The method you use depends on the source of the image. Keep in mind that a CIImage object is really an image recipe; Core Image doesn’t actually produce any pixels until it’s called on to render results to a destination.
Image source | Methods |
|---|---|
URL | |
Quartz 2D image ( | |
Quartz 2D layer ( | |
OpenGL texture | |
Bitmap data |
|
NSCIImageRep |
|
Encoded data (an image in memory) | |
CVImageBuffer |
Listing 2-5 shows how to create, set up, and apply a hue filter. You use the filterWithName: method to create a filter whose type is specified by the name argument. The hue adjust filter is named CIHueAdjust. You can obtain a list of filter names by following the instructions in “Getting a List of Filters and Attributes” or you can look up a filter name in Core Image Filter Reference. The input values for a filter are undefined when you first create it, which is why you either need to call the setDefaults method to set the default values or supply values for all input parameters at the time you create the filter by calling the method filterWithName:keysAndValues:.
If you don’t know the input parameters for a filter, you can get an array of them using the method inputKeys. (Or, you can look up the input parameters for most of the built-in filters in Core Image Filter Reference.) Set a value for each input parameter whose default value you want to change by calling the method setValue:forKey:.
Listing 2-5 sets two input parameters—the input image and the input angle. Filters, except for generator filters, require an input image. Some require two or more images or textures. The input angle for the hue adjustment filter refers to the location of the hue in the HSV and HLS color spaces. This is an angular measurement that can vary from 0.0 to 2 pi. A value of 0 indicates the color red; the color green corresponds to 2/3 pi radians, and the color blue is 4/3 pi radians.
The last line in Listing 2-5 requests the value that corresponds to the outputImage key. When you request the output image, Core Image evaluates the input parameters and stores the calculations necessary to produce the resulting image. The image is not actually rendered. You can apply another filter and continue the process of applying filters until you want to render the result.
Listing 2-5 Creating, setting up, and applying a hue filter
hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"]; |
[hueAdjust setDefaults]; |
[hueAdjust setValue: myCIImage forKey: @"inputImage"]; |
[hueAdjust setValue: [NSNumber numberWithFloat: 2.094] |
forKey: @"inputAngle"]; |
result = [hueAdjust valueForKey: @"outputImage"]; |
If you use one of the Core Image draw methods to render the output image from Listing 2-5, you’ll see what’s shown in Figure 2-2. Next you’ll see how to apply two more filters to the image—gloom (CIGloom) and bump distortion (CIBumpDistortion).
The gloom filter does just that—makes an image gloomy by dulling its highlights. Notice that the code in Listing 2-6 is very similar to that shown in Listing 2-5. It creates a filter and sets default values for the gloom filter. This time, the input image is the output image from the hue adjustment filter. It’s that easy to chain filters together!
The gloom filter has two input parameters. Rather than use the default values, which you could do, the code sets the input radius to 25 and the input intensity to 0.75. The input radius specifies the extent of the effect, and can vary from 0 to 100 with a default value of 10. Recall that you can find the minimum, maximum, and default values for a filter programmatically by retrieving the attribute dictionary for the filter.
The input intensity is a scalar value that specifies a linear blend between the filter output and the original image. The minimum is 0.0, the maximum is 1.0, and the default value is 1.0.
Listing 2-6 Creating, setting up, and applying a gloom filter
gloom = [CIFilter filterWithName:@"CIGloom"]; |
[gloom setDefaults]; |
[gloom setValue: result forKey: @"inputImage"]; |
[gloom setValue: [NSNumber numberWithFloat: 25] |
forKey: @"inputRadius"]; |
[gloom setValue: [NSNumber numberWithFloat: 0.75] |
forKey: @"inputIntensity"]; |
result = [gloom valueForKey: @"outputImage"]; |
The code requests the output image but does not draw the image. You’ll see how to draw the image in the next section. Figure 2-2 shows what the image would look like if you drew it at this point after processing it with both the hue adjustment and gloom filters.
The bump distortion filter (CIBumpDistortion) creates a bulge in an image that originates at a specified point. Listing 2-7 shows how to create, set up, and apply this filter to the output image from the previous filter, the gloom filter. By now you should be an expert. First, create the filter by providing its name. Then, set the defaults and set the input image to the previous result. The bump distortion takes three parameters: a location that specifies the center of the effect, the radius of the effect, and the input scale. The input scale specifies the direction and the amount of the effect. The default value is –0.5. The range is –10.0 through 10.0. A value of 0 specifies no effect. A negative value creates an outward bump; a positive value creates an inward bump.
Listing 2-7 Creating, setting up, and applying the bump distortion filter
bumpDistortion = [CIFilter filterWithName:@"CIBumpDistortion"]; |
[bumpDistortion setDefaults]; |
[bumpDistortion setValue: result forKey: @"inputImage"]; |
[bumpDistortion setValue: [CIVector vectorWithX:200 Y:150 ] |
forKey: @"inputCenter"]; |
[bumpDistortion setValue: [NSNumber numberWithFloat: 100] |
forKey: @"inputRadius"]; |
[bumpDistortion setValue: [NSNumber numberWithFloat: 3.0] |
forKey: @"inputScale"]; |
result = [bumpDistortion valueForKey: @"outputImage"]; |
Drawing the result triggers the processor-intensive operations (GPU or CPU). Core Image provides two methods for drawing:
drawImage:atPoint:fromRect:, which renders a region of an image to a point in the context destination.
drawImage:inRect:fromRect:, which renders a region of an image to a rectangle in the context destination.
The following code renders the hue-adjusted, gloom-filtered, bump-distorted image from the previous section:
[myCIContext drawImage: result |
atPoint: CGPointZero |
fromRect: contextRect]; |
Figure 2-4 shows the rendered image. In this case, Core Image draws the image at (0,0), which is CGPointZero, and draws into the entire context destination.
Last updated: 2008-06-09