Simulating Depth-of-Field with Variable Bokeh in Core Image

DZone 's Guide to

Simulating Depth-of-Field with Variable Bokeh in Core Image

Depth of field focuses our attention in a photograph. Read this and focus on how to fake it digitally.

· Mobile Zone ·
Free Resource

Circular Masked Variable Bokeh

Hexagonal Masked Variable Bokeh

Continuing on from my recent post on simulating bokeh with Metal Performance Shaders, I thought it would be interesting to create a filter similar to Core Image's built-in Masked Variable Blur but applying bokeh intensity rather than blur based on the luminance of a second image.

I've created two filters, MaskedVariableHexagonalBokeh and MaskedVariableCircularBokeh to do just this. Both have an inputBokehMask attribute which is of type CIImage. Where the inputBokehMask is black no effect is applied, and where it is white, the maximum effect is applied:

If you're wondering about the photograph: yes, we do have a shop in London that sells sterling silver lids for your mustard, Marmite, and other condiment jars. 

To produce these filters, I had to write my own dilate operator in Core Image Kernel Language. The approach is very similar to a masked variable box blur I wrote for my book, Core Image for Swift.

In a nutshell, my kernel iterates over a pixel's neighbours. While a box blur averages out the values of those neighbouring pixels, a dilate simply returns the brightest pixel. The great thing about writing my own dilate is that I can code in a formula for the probe rather than passing in an array. That formula is based on the luminance of the bokeh mask image - allowing for a variable bokeh intensity at each pixel. 

The only practical difference between the circular and hexagonal versions of my masked variable bokeh is that formula. My base class is the circular version and the hexagonal version simply overrides withinProbe() which is used in the construction of the CIKL:

// MaskedVariableHexagonalBokeh

override func withinProbe() -> String
    {         return "float withinProbe = ((xx > h || yy > v * 2.0) ? 1.0 : ((2.0 * v * h - v * xx - h * yy) >= 0.0) ? 0.0 : 1.0);"     }

 // MaskedVariableCircularBokeh

func withinProbe() -> String
    {         return "float withinProbe = length(vec2(xx, yy)) < float(radius) ? 0.0 : 1.0; "     }


With the clever use of a mask, these filters are excellent for simulating depth-of-field or tilt shift with a more lens-like look that a simple Gaussian blur.

Both of these filters have been added to Filterpedia. I've also added a radial gradient image  to Filterpedia which is a great way to illustrate the effect of these filters.

Core Image for Swift 

If you'd like to learn more about Core Image, may I recommend my book, Core Image for Swift.

Core Image for Swift is available from both Apple's iBooks Store or, as a PDF, from Gumroad. IMHO, the iBooks version is better, especially as it contains video assets which the PDF version doesn't.

core image filters, image manipulation, swift

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}