Article

Selectively Focusing on an Image

Focus on a part of an image by applying Gaussian blur and gradient masks.

Overview

The tilt-shift effect simulates sharpening or focusing a portion of an image while blurring the rest. Using this technique, you can draw attention to the focused region.

Figure 1

Applying a masked variable blur effect to selectively focus on a portion of the scene

Flowchart showing the combination of image and mask in focusing on the scoop in a photo of walnuts

You specify the region to blur by applying a mask image; the shape and values of the mask image determine the location and strength of the blurring. Creating this effect involves the following steps:

  1. To focus on a strip across the image, create two linear gradients representing the portions of the image to blur.

  2. To focus on a circular region in the image, create a radial gradient centered on the region to keep sharp.

  3. Composite the gradients into a mask.

  4. Apply Core Image's CIMaskedVariableBlur filter to the original image, inputting the created mask.

Focusing on a Strip of the Image

You can build your mask in any shape, but the general strategy remains the same: leave the mask transparent where you want the original image to stay sharp and focused. This section covers focusing on a horizontal strip.

To build a mask that leaves out a stripe, create linear gradients from a single color, such as green or gray. Our goal is to build the mask shown in Figure 2.

Figure 2

CILinearGradient-generated linear mask

Linear gradient mask for blurring all regions of an image except a strip

The linear gradients cause the blur to taper smoothly as it approaches the focused stripe of the image. The Core Image CIFilter named CILinearGradient generates filters of the desired color. The linear gradient has four parameters:

inputPoint0

The starting point of the gradient ramp, expressed as a CIVector

inputPoint1

The endpoint of the gradient ramp, expressed as a CIVector

inputColor0

The starting point gradient color, expressed as a CIColor

inputColor1

The endpoint gradient color, expressed as a CIColor

Compute the start and stop points of the gradient as fractions of the image height, as obtained through extent. For this particular mask and example image, focus on the area near the middle, in the second quarter of the image. Set the linear gradient's inputPoint0 and inputPoint1 to reflect the region through which the gradient tapers.

let h = inputImage.extent.size.height
guard let topGradient = CIFilter(name:"CILinearGradient") else {
    return nil
}
topGradient.setValue(CIVector(x:0, y:0.85 * h), 
                     forKey:"inputPoint0")
topGradient.setValue(CIColor.green, 
                     forKey:"inputColor0")
topGradient.setValue(CIVector(x:0, y:0.6 * h), 
                     forKey:"inputPoint1")
topGradient.setValue(CIColor(red:0, green:1, blue:0, alpha:0), 
                     forKey:"inputColor1")

The lower gradient should complement the upper gradient, so that their combined coverage delineates the entire area to blur. Express the starting inputPoint0 y-value at 0.35 of the image height and taper to 0.6, where the top gradient began. This leaves a gap in the combined mask through which the sharp image will show.

guard let bottomGradient = CIFilter(name:"CILinearGradient") else {
    return nil
}
bottomGradient.setValue(CIVector(x:0, y:0.35 * h), 
                        forKey:"inputPoint0")
bottomGradient.setValue(CIColor.green, 
                        forKey:"inputColor0")
bottomGradient.setValue(CIVector(x:0, y:0.6 * h), 
                        forKey:"inputPoint1")
bottomGradient.setValue(CIColor(red:0, green:1, blue:0, alpha:0), 
                        forKey:"inputColor1")

Creating a Mask by Compositing Linear Gradients

To create a mask that dilineates where and how strong a blur to apply, combine the two linear gradients.

Figure 3

Add two mutually exclusive linear gradients to create a mask

 Graphic depicting the additive compositing of two linear gradients to form a single mask

Since the gradients themselves are CIFilter objects, compositing them is as simple as concatenating their filter outputs to a compositing filter. Use the built-in CIFilter named CIAdditionCompositing to composite two images additively.

guard let gradientMask = CIFilter(name:"CIAdditionCompositing") else {
    return nil
}
gradientMask.setValue(topGradient.outputImage, 
                      forKey: kCIInputImageKey)
gradientMask.setValue(bottomGradient.outputImage, 
                      forKey: kCIInputBackgroundImageKey)

The resulting mask is now ready to be applied as part of the CIMaskedVariableBlur filter.

Focusing on a Circular Region

In order to focus on a circular region of an image, you can create a Core Image CIFilter with the name CIRadialGradient.

The filter takes two parameters, inputImage and inputRadius:

  1. Set the inputCenter to a CIVector pointing to the center of the region you want to leave unblurred.

  2. Set the inputRadius0 to a fraction of the image's dimension, like 0.2*h in this example. You can tweak this parameter to determine the size of the sharp region.

  3. Set the inputRadius1 to a larger fraction of the image's dimension, like 0.3*h in this example. Tweaking this parameter changes the extent of the blur's tapering effect; a larger value makes the blur more gradual, whereas a smaller value makes the image transition more abruptly from sharp (at inputRadius0) to blur (at inputRadius1).

  4. As with the linear gradients, set inputColor0 to transparency, and inputColor1 to a solid opaque color, to indicate the blur's gradation from nonexistent to full.

guard let radialMask = CIFilter(name:"CIRadialGradient") else {
    return nil
}
let imageCenter = CIVector(x:0.55 * w, y:0.6 * h)
radialMask.setValue(imageCenter, forKey:kCIInputCenterKey)
radialMask.setValue(0.2 * h, forKey:"inputRadius0")
radialMask.setValue(0.3 * h, forKey:"inputRadius1")
radialMask.setValue(CIColor(red:0, green:1, blue:0, alpha:0), 
                    forKey:"inputColor0")
radialMask.setValue(CIColor(red:0, green:1, blue:0, alpha:1), 
                    forKey:"inputColor1")

This yields a circular mask to composite using the CIMaskedVariableBlur filter.

Figure 4

CIRadialGradient-generated circular transparency mask

Circular gradient mask for blurring out all pixels except those in a circular region

Without accessing the output image, keep this filter to chain with the other components.

Masking the Blurred Image to Apply Selective Focus

The final step is applying your choice of mask with the input image. The CIMaskedVariableBlur built-in CIFilter accomplishes this task with the following input parameters:

inputImage

Set to the original, unprocessed CIImage.

inputRadius

Set to 10, and experiment to find the right degree of blur desired. (The default is 5.)

inputMask

Set to the desired gradient mask, either the outputImage of the CIAdditionCompositing filter for linear focus, or the outputImage of the CIRadialGradient filter for circular focus.

guard let maskedVariableBlur = CIFilter(name:"CIMaskedVariableBlur") else {
    return nil
}
maskedVariableBlur.setValue(inputImage, forKey: kCIInputImageKey)
maskedVariableBlur.setValue(10, forKey: kCIInputRadiusKey)
maskedVariableBlur.setValue(radialMask.outputImage, forKey: "inputMask")
let selectivelyFocusedCIImage = maskedVariableBlur.outputImage

The resulting image shows the original image with portions blurred out according to the mask applied. The linear gradient mask results in an output image focused on a strip, and the radial gradient mask results in an output image focused on a circular region.

Figure 5

CIMaskedVariableBlur with linear gradient mask applied to a walnut image, resulting in focus on a strip

CIMaskedVariableBlur with a linear gradient mask applied to a photo of walnuts

Figure 6

CIMaskedVariableBlur with circular gradient mask applied to a walnut image, resulting in focus on a circle

CIMaskedVariableBlur with a radial gradient mask applied to a photo of walnuts

See Also

Filter Recipes

Applying a Chroma Key Effect

Replace a color in one image with the background from another.

Customizing Image Transitions

Transition between images in creative ways using Core Image filters.

Simulating Scratchy Analog Film

Degrade the quality of an image to make it look like dated, scratchy analog film.