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 much faster search using Brent’s 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 consisting of the ROI and a margin around it. It is also possible to pre-compute a look up table of propagators to provide a further speed-up.
If using the Holo class then the focus can be found and the image refocused using the pyholoscope.Holo.auto_focus() method, or the pyholoscope.Holo.find_focus() method can be used
to obtain the focus depth without actually refocusing. Alternatively, the lower level pyholoscope.find_focus() function can be used directly.
Getting Started Using Holo class
Begin by importing the package:
import pyholoscope as pyh
For refocusing we need to define the image pixel size, and wavelength, for example:
wavelength = 520e-9
pixel_size = 4.8e-6
We also define the refocus depth range to search over, and the focus metric (method) to apply:
depth_range = (.08, .16)
method = 'sum'
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.
We choose a region of interest (ROI) around the interesting part of the image:
roi = pyh.Roi(450,110,500,400)
We then create the Holo object, optionally specifying a background:
holo = pyh.Holo(mode = pyh.INLINE, wavelength = wavelength, pixel_size = pixel_size, background = background)
We set the focus parameters, optionally specifying the region of interest:
holo.set_find_focus_parameters(depth_range = depth_range, roi = roi, method = method)
We then use pyholoscope.Holo.auto_focus() to obtain a refocused image at the best focal depth:
refocused = holo.auto_focus(hologram)
This also sets holo.depth to the best focal depth. If we just wanted to find
the best focal depth without producing a refocused image, we can call pyholoscope.Holo.find_focus():
focus_depth = holo.find_focus(hologram)
If we only want to refocus a margin around the region of interest when searching for the best focus,
for improved speed, we can specify the margin, for example:
holo.set_find_focus_parameters(depth_range = depth_range, roi = roi, method = method, margin = 20)
For further improved speed we can pre-compute a propagator LUT by deciding how many propagator depths we would like to pre-calculate, and then calling:
num_depths = 100
holo.make_auto_focus_propagator_LUT(hologram, depth_range, num_depths, roi=roi, margin=margin)
where we must provide the correct roi and margin if a margin is to be used, to ensure the
propagator LUT has propagators of the correct size. We then indicate we would like to use the LUT:
holo.set_find_focus_parameters(depth_range = depth_range, roi = roi, method = method, margin = margin, use_prop_lut = True)
Now, we call auto_focus or find_focus as before. num_depths must be chosen to
give the required focus precision, which will depend on the imaging system and application.
Focus Metrics
The available options for the focus metric are below. A list of strings of the available methods can be obtained from pyholoscope.get_focus_score_methods().
sum- Takes the sum of all pixel values, for amplitude images this will be lowest when the image is in focus.peak- The largest single pixel value, this is usually highest for in focus images.brenner- Applies the Brenner edge detection filter across the image and takes the mean.sobel- Applies a Sobel edge detection filter and takes the meansobel_variance- Applies a Sobel edge detection filter and takes the standard deviationdark_focus- Takes the standard deviation of the quadrature sum of the vertical and horizontal image gradientsnorm_var- Normalised variance.
Where appropriate the output from the metrics are negated to ensure the best focus also has the lowest score.
Custom metrics can be defined as functions, with the function passed to the auto/find focus functions instead of a string. Custom metrics must accept a single argument, the hologram, and return a single focus score, with lower values meaning better focus.
Getting Started Using Lower Level Functions
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 pixel size, and wavelength, for example:
pixel_size = 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 ‘sum’ metric:
depth_range = (0, 0.001)
method = 'sum'
We then call pyholoscope.find_focus():
focusDepth = pyh.find_focus(hologram, wavelength, pixel_size, depth_range, method)
This returns the best refocus depth, found using Brent’s method.
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
focus_roi = pyh.Roi(x,y,w,h)
and pass this as an optional argument:
focus_depth = pyh.find_focus(hologram, wavelength, pixel_size, depth_range, method, roi = focus_roi)
While the focus metric will be applied only to the ROI, the whole hologram is refocused to each trial depth, so there is usually no significant 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:
focus_depth = pyh.find_focus(hologram, wavelength, pixel_size, depth_range, method, roi = focus_roi, 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)
focus_depth = pyh.find_focus(hologram, wavelength, pixel_size, depth_range, method, roi = focus_roi, margin = 20, background = background_img, window = window)
To use a propagator LUT, this is first created using pyholoscope.PropLUT:
nDepths = 100
lut = pyh.PropLUT(imgSize, wavelength, pixel_size, depth_range, nDepths)
and then passed to the find_focus function:
focusDepth = pyh.find_focus(hologram, wavelength, pixel_size, depth_range, method, prop_lut = 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. There is a helper function for this: pyholoscope.propagator_size_for_roi():
prop_size = pyh.propagator_size_for_roi(hologram, roi = roi, margin = margin)
prop_lut = pyh.PropLUT(prop_size, wavelength, pixel_size, depth_range, num_depths)