4. FIB-SEM reconstruction#
In this notebook we’ll use elastix to
reconstruct a FIB-SEM dataset
perform drift correction of a timelapse
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from skimage import io
import itk
from tqdm import tqdm
FIB-SEM reconstruction#
FIB-SEM is a volumetric imaging technique in which
the surface of a block is imaged using Scanning Electron Microscopy (SEM)
the top layer of the block is ablated using a Focused Ion Beam (FIB)
this is repeated n times to obtain a volume of n slices
Typically the slices exhibit deformations with respect to each other and a high quality 3D stack can be reconstructed by registering slices sequentially. !
(from Briggman and Bock, Curr. Op. in Neurobiol. 2012)
Load and visualize the data#
The image used is a subset of the dataset available here: https://www.ebi.ac.uk/bioimage-archive/galleries/EMPIAR-10479/IM1.html
im = io.imread('../example_data/fibsem.tif')
# visualise
import napari
viewer = napari.Viewer()
viewer.add_image(im)
<Image layer 'im' at 0x179fb0950>
n_slices = im.shape[0]
print('Image shape is %s in z, y, x' %list(im.shape))
Image shape is [41, 168, 251] in z, y, x
Defining the registration parameters#
For FIB-SEM acquisitions, registration typically requires nonlinear registration. Here, we perform first a linear and then a nonlinear transformation.
registration_parameter_object = itk.ParameterObject.New()
registration_parameter_object.AddParameterMap(itk.ParameterObject.New().GetDefaultParameterMap('translation'))
pmap_bspline = itk.ParameterObject.New().GetDefaultParameterMap('bspline')
pmap_bspline['GridSpacingSchedule'] = [str(v) for v in [10] * 4]
registration_parameter_object.AddParameterMap(pmap_bspline)
Reconstruction#
Our strategy will be to
choose the first slice as a reference slice
register neighboring slices to each other
transform each slice by chaining together all pairwise transformations between the slice and the reference slice
# initialise the corrected stack as a list, containing the unmodified reference slice
corrected_slices = [im[0]]
# initialise a parameter object to which the transforms will be appended that result from the pairwise slice registrations
curr_transform_object = itk.ParameterObject.New()
# the first fixed image will be the reference slice
fixed_image_itk = itk.GetImageFromArray(im[0])
for z in tqdm(range(1, n_slices)):
# for z in tqdm(range(1, 6)):
# the moving image is the current slice
moving_image_itk = itk.GetImageFromArray(im[z])
# perform the pairwise registration between two slices
transformed_moving_image, result_transform_parameters = itk.elastix_registration_method(
fixed_image_itk,
moving_image_itk,
parameter_object=registration_parameter_object,
log_to_console=False
)
# set the current moving image as the fixed image for the registration in the next iteration
fixed_image_itk = moving_image_itk
# append the obtained transform to the transform parameter object
curr_transform_object.AddParameterMap(result_transform_parameters.GetParameterMap(0))
# transform the current slice and append it to the reconstructed stack
curr_slice = itk.transformix_filter(moving_image_itk, curr_transform_object)
corrected_slices.append(np.array(curr_slice))
# convert the list of 2D slices into a 3D numpy array
corrected_slices = np.array(corrected_slices)
100%|██████████| 40/40 [00:51<00:00, 1.28s/it]
import napari
viewer = napari.Viewer()
viewer.add_image(im, name='Before correction')
viewer.add_image(corrected_slices, name='After correction')
<Image layer 'After correction' at 0x17f2ab400>
Exercise: Drift correction#
Can you use a similar approach to perform drift correction of a timelapse?
There’s an example dataset available for this at ‘../example_data/2d_movie.tif’.