Automatic Focusing

The optimal refocus depth for a hologram can be determined using trial refocuses, assessed using a focus metric. The package supports an exhaustive search or a faster golden section based optimisation method. The focus can be optimised for the entire image or for a region of interest (ROI). Faster determination for a ROI can be achieved by only refocusing a subset of the image surrounding the ROI. It is also possible to create a look up table of propagators to provide a further speed-up.

Getting Started

Begin by importing the package:

import pyholoscope as pyh

We will assume we have a hologram hologram stored as a 2D numpy array. This can either be a raw inline hologram, in which case hologram will be real, or a demodulated off-axis hologram, in which case hologram will be complex.

For refocusing we need to define the image size, in pixels, pixel size, and wavelength, for example:

imageSize = 512
pixelSize = 2e-6
wavelength = 0.5e-6

We also need to define the range of depths to search over as a tuple of (minDepth, maxDepth), and select the focus metric to use, for this example we will choose the ‘Sobel’ metric:

depthRange = (0, 0.001)
method = 'Sobel'

We then call find_focus:

focusDepth = pyh.find_focus(hologram, wavelength, pixelSize, depthRange, method)

This returns the best refocus depth.

If we would like to find the best focus for a specific region of the image only, we define a ROI such as:

x = 20
y = 30
w = 40
h = 40
focusRoi = pyh.Roi(x,y,w,h)

and pass this as an optional argument:

focusDepth = pyh.find_focus(hologram, wavelength, pixelSize, depthRange, method, roi = focusRoi )

The entire hologram will still be refocused, but only the focus metric will only be applied to the ROI, so there is no speed-up from doing this.

A speed-up can be obtained by refocusing only the ROI plus a small margin around it. This is activated by specifying the margin in pixels:

focusDepth = pyh.find_focus(hologram, wavelength, pixelSize, depthRange, method, roi = focusRoi, margin = 20)

If the hologram has not had a background subtracted or a window previously applied, we can also request these are applied prior to refocusing by passing them as optional arguments:

window = pyh.circ_cosine_window(imageSize, imageSize / 2, 20)
focusDepth = pyh.find_focus(hologram, wavelength, pixelSize, depthRange, method, roi = focusRoi, margin = 20, background = backgroundImg, window = window)

Focus Metrics

The available options for the focus metric are:

  • Peak - Takes the largest pixel value

  • Var - Takes the standard deviation

  • Brenner - Applies the Brenner edge detection filter and takes the mean

  • Sobel - Applies a Sobel edge detection filter and takes the mean

  • SobelVariance - Applies a Sobel edge detection filter and takes the standard deviation

  • DarkFocus - Takes the standard deviation of the quadrature sum of the vertical and horizontal image gradients

The result from each of these metric is negated so that the best focus depth has the smallest score.

Using a Propagator Look Up Table

When determining the focal depth, the rate limiting step can sometimes be the generation of angular spectrum method propagators for each trial refocus depth. When repeated autofocusing will be performed over a similar depth range, and with the same size ROI (or whole image), a speed-up can be achieved by pre-generating the required propagators.

To do this, create an instance of the PropLUT class. We need to specify the range of refocus depths and the number of depths within this range that propagators will be generated for. When searching for focus, we can search within a smaller range than this, but not larger. Increasing the number of depths within the range will increase the precision of the autofocusing.:

imgSize = 256
wavelength = 0.5e-6
pixelSize = 2e-6
depthRange = (0, 0.1)
nDepths = 100
lut = pyh.PropLUT(imgSize, wavelength, pixelSize, depthRange, nDepths)

The lut is then passed to the find_focus function:

focusDepth = pyh.find_focus(hologram, wavelength, pixelSize, depthRange, method, propagatorLUT = lut)

If also attempting to speed-up by refocusing only a ROI (i.e. the margin is specified) then it is necessary to create a propagator LUT of the correct size for this ROI + margin. This must currently be done manually by adjusing the gridSize input to be the ROI.