Table of Contents

DeconvTest: simulation framework for quantifying errors and selecting optimal parameters of image deconvolution

DOI

Author: Anna Medyukhina

Affiliation: Research Group Applied Systems Biology - Head: Prof. Dr. Marc Thilo Figge
https://www.leibniz-hki.de/en/applied-systems-biology.html
HKI-Center for Systems Biology of Infection
Leibniz Institute for Natural Product Research and Infection Biology -
Hans Knöll Insitute (HKI)
Adolf-Reichwein-Straße 23, 07745 Jena, Germany


DeconvTest is a Python-based simulation framework that allows the user to quantify and compare the performance of different deconvolution methods. The framework integrates all components needed for such quantitative comparison and consists of three main modules: (1) in silico microscopy, (2) deconvolution, and (3) performance quantification.

Source code

The source code of the package can be found at https://github.com/applied-systems-biology/DeconvTest

Installation

  1. Download Fiji, make sure that the Fiji installation path does not contain spaces; note the Fiji installation path: you will have to provide it later when installing the DeconvTest packge
  2. Download the DeconvolutionLab_2.jar and Iterative_Deconvolve_3D.class and copy them to the plugins folder of Fiji
  3. Install python or python Anaconda
  4. Download the DeconvTest package, enter the package directory and install the package by running python setup.py install
  5. Provide the path to Fiji when prompted

Requirements

The software was tested with the following versions of the required packakges:

  • scikit-image 0.15.0
  • pandas 0.25.1
  • numpy 1.16.5
  • seaborn 0.9.0
  • scipy 1.3.1
  • ddt 1.2.1

License

The source code of this framework is released under the 3-clause BSD license

Simulation of microscopy experiments

Microscopic imaging of a sample involves illuminating the sample with a light source, collecting the emitted light, and discretizing the signal by recoding it in a detector, such as a CCD camera. This process can be represented as a continuous convolution of the sample with a point spread function (PSF) - the image of a point source - and subsequent discretization. Convolution of complex continuous signals is non-trivial to compute. Hence, when simulating this process on a computer, we represent the continuous convolution process by a convolution of discrete signals of high resolution (very small voxel size), while the process of discretization is simulated by the subsequent downsampling (decrease of the resolution).

To simulate the whole microscopy process, we need to model the following steps and components:

  1. Images of cells
  2. Point spread function (PSF) of the imaging system
  3. Convolution and downsampling
  4. Noise

Generating synthetic cells

Synthetic cells can be generated with the class Cell, which is initialized with the following parameters:

In [1]:
from DeconvTest import Cell
print(Cell.__init__.__doc__)
        Initializes the cell image from a given list of indices or by reading from file.

        Parameters
        ----------
        filename: str, optional
            Path used to load the cell image.
            If None or non-existent, no image will be loaded.
            Default is None.
        ind : ndarray or list, optional
            3D coordinates of all pixels of the binary mask of the cell. 
            The size of the first axis of the ndarray or the length of the list should be equal to 
             the number of dimensions in the cell image (3 for 3D image). 
            The size of the second axis of the ndarray or the length of each sublist corresponds to 
             the number of pixels belonging to the cell mask.
        position : sequence of floats, optional
            Coordinates of the cell center in a multicellular stack in pixels. 
            The length of the sequence should correspond to the number of dimensions in the cell image
             (3 for 3D image).
        input_voxel_size : scalar or sequence of scalars, optional
            Voxel size in z, y and x used to generate the cell image.
            If not None, a cell image will be generated with the give voxel size and cell parameters specified 
             in `generate_kwargs`.
            Default is None.
        generate_kwargs : key, value pairings
            Keyword arguments passed to the `self.generate` function.
        

Cell of ellipsoidal, as well as realistic shapes can be generated:

In [2]:
print(Cell.generate.__doc__)
        Generates a synthetic object image from given parameters and stores the output in the `self.image` variable.

        Parameters
        ----------
        input_voxel_size : scalar or sequence of scalars
            Voxel size in z, y and x used to generate the object image.
            If one value is provided, the voxel size is assumed to be equal along all axes.
        input_cell_kind : string, optional
            Name of the shape of the ground truth object from set of
            {ellipsoid, spiky_cell}.
            Default is 'ellipsoid'
        kwargs : key, value pairings
            Keyword arguments passed to corresponding methods to generate synthetic objects.
        

Usage examples

Example 1

Generate a spherical cell with the diameter 10 µm and voxel size 0.3 µm per px and visualize its xy, xz, and yz maximum projections:

In [3]:
cell = Cell(input_voxel_size=0.3, 
            input_cell_kind='ellipsoid', 
            size=10)  
cell.show_2d_projections()

The input_cell_kind argument can be ommited in this case, because its default value is ellipsoid:

In [4]:
cell = Cell(input_voxel_size=0.3, 
            size=10)  
cell.show_2d_projections()
Example 2

Generate a cell with ellipsoidal shape with axes 10, 7, and 7 µm and voxel size 0.3 µm per px:

In [5]:
cell = Cell(input_voxel_size=0.3, 
            size=[10, 7, 7])  
cell.show_2d_projections()

Visualize the cell in 3D using ipyvolume:

In [6]:
import ipyvolume.pylab as p3
from IPython.core.display import HTML
p3.clear()
p3.plot_isosurface(cell.image)
p3.show()
Out[6]:
IPyVolume Widget
Example 3

Generate a cell with ellipsoidal shape with axes 10, 7, and 7 µm and rotation of π/4 along the polar axis and π/2 along the azimuthal axis. Voxel size is 0.3 µm per px:

In [7]:
import numpy as np
cell = Cell(input_voxel_size=0.3, 
            size=[10, 7, 7], 
            phi=np.pi/4, 
            theta=np.pi/2)  
cell.show_2d_projections()

The cell sizes can be alternatively specified by the arguments size_x, size_y and size_z:

In [8]:
cell = Cell(input_voxel_size=0.3, 
            size_x=7, 
            size_y=7, 
            size_z=10, 
            phi=np.pi/4, 
            theta=np.pi/2)  
cell.show_2d_projections()
Example 4

Generate a spiky cell with spikiness 0.5 (i.e., 50% of cell surface covered by spikes), spike size 1 cell radius, and spike smoothness 0.05 (i.e., the size of the smoothing filter equals to 5% of cell radius); the cell has ellipsoidal shape with axes 10, 7, and 7 µm and rotation of π/4 along the polar axis and π/2 along the azimuthal axis; voxel size is 0.3 µm per px:

In [9]:
cell = Cell(input_cell_kind='spiky_cell', 
            input_voxel_size=0.3, 
            size_x=7, 
            size_y=7, 
            size_z=10, 
            phi=np.pi/4, 
            theta=np.pi/2, 
            spikiness=0.5, 
            spike_size=1, 
            spike_smoothness=0.05) 
cell.show_2d_projections()
p3.clear()
p3.plot_isosurface(cell.image)
p3.show()
Out[9]:
IPyVolume Widget

Generating multicellular samples

To generate a multicellular sample one has to specify cell parameters (e.g. size and rotation) for each cell in the sample. This is done in two steps:

  1. Cell parameters for each sample are specified as pandas.DataFrame, or as an instance of the class CellParams, where the cell parameters are generated randomly with specified mean and standard deviation.
  2. Sample images are generated with a given voxel size.

Generating parameters for a multicellular sample

In [10]:
from DeconvTest import CellParams
print(CellParams.__init__.__doc__)
        Initializes the class for CellParams and generate random cell parameters with given properties.

        Keyword arguments:
        -----------
        input_cell_kind : string, optional
            Name of the shape of the ground truth object from set of
            {ellipsoid, spiky_cell}.
            Default is 'ellipsoid'
        number_of_stacks : int, optional
            Number of stacks to generate.
            If None, parameters for single cells will be generated
            Default is None.
        number_of_cells: int, optional
            Number of cells to generate.
            Default is 1.
        coordinates : bool, optional
            If True, relative cell coordinates will be generated.
            Default is True.
        kwargs : key, value pairings
            Further keyword arguments passed to corresponding methods to generate cell parameters.
        
        

Usage examples

Example 1

Generate random cell parameters for 5 spherical cells with diameters 10 $\pm$ 2 µm:

In [11]:
params = CellParams(number_of_cells=5, 
                    size_mean_and_std=(10, 2), 
                    equal_dimensions=True)
params
Out[11]:
size_x size_y size_z phi theta z y x input_cell_kind
0 10.622235 10.622235 10.622235 0 0 0.388699 0.632136 0.835086 ellipsoid
1 12.107066 12.107066 12.107066 0 0 0.041857 0.326144 0.739110 ellipsoid
2 6.425231 6.425231 6.425231 0 0 0.649720 0.077322 0.088054 ellipsoid
3 10.372747 10.372747 10.372747 0 0 0.400604 0.004007 0.168240 ellipsoid
4 8.690967 8.690967 8.690967 0 0 0.293970 0.204721 0.982759 ellipsoid
Example 2

Generate random cell parameters for 5 spiky ellipsoidal cells with axes sizes 10 $\pm$ 2 µm, spikiness from 0.1 to 1, spike size from 0.1 to 1 and spike smoothness from 0.05 to 0.07:

In [12]:
params = CellParams(number_of_cells=5, 
                    input_cell_kind='spiky_cell',
                    size_mean_and_std=(10, 2), 
                    equal_dimensions=False, 
                    spikiness_range=(0.1, 1), 
                    spike_size_range=(0.1, 1), 
                    spike_smoothness_range=(0.05, 0.07))
params
Out[12]:
size_x size_y size_z phi theta spikiness spike_size spike_smoothness z y x input_cell_kind
0 9.559672 7.257061 8.199977 0.772193 2.343945 0.694482 0.535540 0.064830 0.315120 0.774142 0.128627 spiky_cell
1 10.642330 7.593122 13.448859 6.146339 1.786036 0.708266 0.839482 0.055717 0.850541 0.084370 0.697277 spiky_cell
2 5.333567 13.829871 9.653306 2.054071 1.876519 0.839661 0.883497 0.050711 0.258778 0.220791 0.967620 spiky_cell
3 7.237454 8.715542 13.131329 3.043581 0.247296 0.493759 0.539404 0.059861 0.143567 0.557877 0.034623 spiky_cell
4 10.629596 7.663825 11.718086 3.931058 1.331277 0.673595 0.336582 0.061777 0.060485 0.138157 0.165865 spiky_cell

Save the cell parameters into a csv file:

In [13]:
params.save('code_examples/cell_parameters.csv')

Initialize a new class for cell parameters and read in the saved cell parameter:

In [14]:
params2 = CellParams()
params2.read_from_csv('code_examples/cell_parameters.csv')
params2
Out[14]:
size_x size_y size_z phi theta spikiness spike_size spike_smoothness z y x input_cell_kind
0 9.559672 7.257061 8.199977 0.772193 2.343945 0.694482 0.535540 0.064830 0.315120 0.774142 0.128627 spiky_cell
1 10.642330 7.593122 13.448859 6.146339 1.786036 0.708266 0.839482 0.055717 0.850541 0.084370 0.697277 spiky_cell
2 5.333567 13.829871 9.653306 2.054071 1.876519 0.839661 0.883497 0.050711 0.258778 0.220791 0.967620 spiky_cell
3 7.237454 8.715542 13.131329 3.043581 0.247296 0.493759 0.539404 0.059861 0.143567 0.557877 0.034623 spiky_cell
4 10.629596 7.663825 11.718086 3.931058 1.331277 0.673595 0.336582 0.061777 0.060485 0.138157 0.165865 spiky_cell
Example 3

Specify cell parameters as a pandas.DataFrame:

In [15]:
import pandas as pd
params = pd.DataFrame({'size_x': [10, 10, 10],
                      'size_y': [9, 10, 8], 
                      'size_z': [10, 11, 10],
                      'phi': [0, np.pi/4, np.pi/2],
                      'theta': [0, 0, 0],
                      'x': [0.1, 0.8, 0.2],
                      'y': [0.5, 0.5, 0.5],
                      'z': [0.2, 0.5, 0.8],
                       'input_cell_kind': 'spiky_cell',
                      'spikiness': [0, 0, 1],
                      'spike_size': [0, 0, 1],
                      'spike_smoothness': [0.05, 0.05, 0.05]})
params
Out[15]:
size_x size_y size_z phi theta x y z input_cell_kind spikiness spike_size spike_smoothness
0 10 9 10 0.000000 0 0.1 0.5 0.2 spiky_cell 0 0 0.05
1 10 10 11 0.785398 0 0.8 0.5 0.5 spiky_cell 0 0 0.05
2 10 8 10 1.570796 0 0.2 0.5 0.8 spiky_cell 1 1 0.05

Generating a multicellular sample from cell parameters

An image with multiple cells can be generated using the class Stack, which accepts the following arguments:

In [16]:
from DeconvTest import Stack
print(Stack.__init__.__doc__)
        Initializes the Stack class.

        Parameters
        ----------
        filename: str, optional
            Path used to load the cell image.
            If None or non-existent, no image will be loaded.
            Default is None.
        input_voxel_size : scalar or sequence of scalars, optional
            Voxel size used to generate the stack.
            If None, no stack will be generated.
            Default is None.
        stack_size : sequence of scalars, optional
            Dimensions of the image stack in micrometers.
            If None, no stack will be generated.
            Default is None.
        cell_params : pandas.DataFrame or CellParams
            Dictionary of cell parameters.
            The columns should include the keyword arguments passed though `Cell.generate`.
            If None, no stack will be generated.
            Default is None.
        

Usage examples

Example 1

Generate a multicellular sample with voxel size 0.5 µm and dimensions 50 x 50 x 50 µm using the cell parameters defined in Example 3 of section 2.2.1.1:

In [17]:
stack = Stack(cell_params=params, 
              input_voxel_size=0.5, 
              stack_size=[50, 50, 50])
stack.show_2d_projections()
p3.clear()
p3.plot_isosurface(stack.image)
p3.show()
Out[17]:
IPyVolume Widget

Generating synthetic PSFs

PSF for multiphoton microscopy can be estimated to have a Gaussian shape, therefore we here generate PSFs as discretized 3D Gaussian functions. A PSF can be generated using the class PSF, which accept the following arguments:

In [18]:
from DeconvTest import PSF
print(PSF.__init__.__doc__)
        Initializes the PSF image by reading from file or generating from given sigma and elongation
        
        Parameters
        ----------
        filename : str, optional
            Path used to load the PSF image.
            If None or non-existent, no image will be loaded.
            Default is None.
        kwargs : key, value pairings
            Keyword arguments passed to corresponding methods to generate synthetic PSFs.

        

Usage examples

Example 1

Generate a PSF image with standard deviation 15 pixels in xy and aspect ratio 3 (which corresponds to standard deviation in z equal to 45 pixels):

In [19]:
psf = PSF(sigma=10, 
          aspect_ratio=3)
psf.show_2d_projections()

Convolving cells with PSFs

The microscopic imaging process is represented by convolution of a cell image with a PSF image.

Usage examples

Example 1

Generate an image of an ellipsoidal cell with axes 10, 6, and 6 µm, rotation of π/4 along the polar axis and π/2 along the azimuthal axis, and the voxel size of 0.3 µm per px:

In [20]:
cell = Cell(input_voxel_size=0.3, 
            size=[10, 6, 6], 
            phi=np.pi/4, 
            theta=np.pi/2)
cell.show_2d_projections()

Generate a PSF image with standard deviation 0.5 µm in xy and 1.5 µm in z and voxel size 0.3 µm:

In [21]:
psf = PSF(sigma=0.5/0.3, 
          aspect_ratio=1.5/0.5)
psf.show_2d_projections()

Convolve the cell image with the PSF image:

In [22]:
cell.convolve(psf)
cell.show_2d_projections()
Example 2

Generate a multicellular sample image with 6 ellipsoidal cells with axes sizes 7 $\pm$ 3 µm; sample (stack) size is 50 x 50 x 50 µm and voxel size is 0.5 µm per px:

In [23]:
params = CellParams(number_of_cells=6, 
                    size_mean_and_std=(7, 2), 
                    equal_dimensions=False)
stack = Stack(cell_params=params, 
              input_voxel_size=0.5, 
              stack_size=[50, 50, 50])
stack.show_2d_projections()

Convolve the sample image with the PSF image from Example 1:

In [24]:
stack.convolve(psf)
stack.show_2d_projections()

Downsizing convolved images

The process of image discretization is simulated by downsizing the convolved cell images.

Usage examples

Example 1

Downsize the convolved cell image from Example 1 of section 2.4.1 from voxel size 0.3 µm per px to voxel size 0.8 µm per px:

In [25]:
cell.resize(zoom=0.3/0.8)
cell.show_2d_projections()
Example 2

Downsize the convolved stack image from Example 2 of section 2.4.1 from voxel size 0.5 µm per px to voxel size 0.8 µm per px in xy and 3 µm per px in z.

In [26]:
stack.resize(zoom=[0.5/3, 0.5/0.8, 0.5/0.8])
stack.show_2d_projections()

Adding synthetic noise

To investigate the influence of noise, Poisson or Gaussian noise of different signal-to-noise ratios (SNRs) can be added to the convolved and down-sampled images.

The SNR for the Gaussian noise is evaluated as follows:

$$\mathrm{SNR} = 20 \log_{10} \frac{I_{max}}{\sigma}$$

Here, $I_{max}$ is the maximum image intensity, which represents the signal amplitude; $\sigma$ is the standard deviation of the Gaussian noise, which represents the noise amplitude.

Hence, to add Gaussian noise of a given SNR, the noise amplitude $\sigma$ can be computed as follows: $$\sigma = \frac{I_{max}}{10^{\mathrm{SNR}/20}}$$

Then, for each pixel, a random number is drawn from the normal distribution with $\mu = 0$ and $\sigma = \sigma$ and is added to the value of this pixel.

The SNR of the Poisson distribution is evaluated as follows:

$$\mathrm{SNR} = \sqrt{I},$$

To generate the Poisson noise with a given SNR, the image is normalized such that its maximum value equals to $\mathrm{SNR}^2$. Then, each pixel's intensity is replaced by a random value from the Poisson distribution that has the expectation value equal to corresponding pixel intensity.

Usage examples

Example 1

Add Poisson noise with SNR = 2 to the downsampled cells from Example 1 of section 2.5.1:

In [27]:
cell.add_noise(snr=2, 
               kind='poisson')
cell.show_2d_projections()
Example 2

Generate a spherical cell of size 10 µm and voxel size 0.3 µm per px and add Gaussian noise with SNR = 5:

In [28]:
cell = Cell(input_voxel_size=0.3, 
            size=10)
cell.add_noise(snr=5, 
               kind='gaussian')
cell.show_2d_projections()
Example 3

Generate a multicellular sample with 6 ellipoidal cells with axes sizes 7 $\pm$ 2 µm; sample dimensions 50 x 50 x 50 µm and voxel size 0.3 µm per px, and add Gaussian noise with SNR = 10 and then Poisson noise with SNR = 2:

In [29]:
params = CellParams(number_of_cells=6, 
                    size_mean_and_std=(7, 2), 
                    equal_dimensions=False)
stack = Stack(cell_params=params, 
              input_voxel_size=0.3, 
              stack_size=[50, 50, 50])
stack.add_noise(snr=[10, 2], 
                kind=['gaussian', 'poisson'])
stack.show_2d_projections()

Reconstructing synthetic data with different deconvolution approaches

The following Fiji plugins have so far been implemented in the DeconvTest framework.

The plugins are run from python and thus can be easily incorporated in the analysis pipeline. The images of cells and PSF are loaded into the plugins from a corresponding tiff file.

Preparing the images for deconvolution

Generate an image of an ellipsoidal cell with axes 10, 6, and 6 µm, rotation of π/4 along the polar axis and π/2 along the azimuthal axis, and the voxel size of 0.3 µm per px; save the image to a tif file and the image metadata to a csv file:

In [30]:
cell = Cell(input_voxel_size=0.3, 
            size=[10, 6, 6], 
            phi=np.pi/4, 
            theta=np.pi/2)
cell.save('code_examples/input_cell.tif')
cell.show_2d_projections()
pd.read_csv('code_examples/input_cell.csv', sep='\t', index_col=0, header=None).squeeze()
Out[30]:
0
Voxel size             [0.3 0.3 0.3]
Voxel size z                     0.3
Voxel size y                     0.3
Voxel size x                     0.3
Voxel size arr         [0.3 0.3 0.3]
size                      [10, 6, 6]
phi               0.7853981633974483
theta             1.5707963267948966
kind                       ellipsoid
Convolved                      False
Name: 1, dtype: object

Generate a PSF image with standard deviation 0.5 µm in xy and 1.5 µm in z (elongation 3), voxel size 0.3 µm per px; save the PSF image to a tif file:

In [31]:
psf = PSF(sigma=0.5 / 0.3, 
          aspect_ratio=3)
psf.save('code_examples/psf.tif')
psf.show_2d_projections()

Convolve the cell image with the PSF image and save the result to a tif file:

In [32]:
cell.convolve(psf)
cell.save('code_examples/convolved_cell.tif')
cell.show_2d_projections()

Get the Fiji path and store it in a config file (this step is done automatically when installing the DeconvTest package):

In [33]:
from DeconvTest.modules.deconvolution import get_fiji_path
imagej_path = get_fiji_path()

DeconvolutionLab2: Regularized Inverse Filter (RIF)

RIF a simple deconvolution approach, which involves coefficient-wise division in the Fourier domain (see Sage et al. 2017 for detail). To penalize noise amplification, RIF involves a regularization parameter $\lambda$:

D. Sage, L. Donati, F. Soulez, D. Fortun, G. Schmit, A. Seitz, R. Guiet, C. Vonesch, and M. Unser, “Decon- volutionLab2: An open-source software for deconvolution microscopy,” Methods 115, 28–41 (2017).

Usage examples

Example 1

Deconvolve the previously stored cell image (Section 3.1) with the regularized inverse filter using $\lambda$ = 0.001:

In [34]:
import os
from DeconvTest.modules.deconvolution import deconvolution_lab_rif
deconvolution_lab_rif(imagej_path=imagej_path,
                      inputfile=os.getcwd() + '/code_examples/convolved_cell.tif',
                      psffile=os.getcwd() + '/code_examples/psf.tif',
                      regularization_lambda=0.001, 
                      outputfile=os.getcwd() + '/code_examples/deconvolved_rif.tif')

cell = Cell(filename='code_examples/deconvolved_rif.tif')
cell.show_2d_projections()
/home/anna/anaconda3/lib/python3.7/site-packages/deconvtest-2.4-py3.7.egg/DeconvTest/classes/image.py:66: Warning: Metadata file is not found!

DeconvolutionLab2: Richardson-Lucy with Total Variance (RLTV)

RLTV is an iterative minimization approach, which -- similarly to RIF -- also involves regularization (see Sage et al. 2017 for detail). Thus, RLTV uses two parameters: the regularization $\lambda$ and the number of iterations:

D. Sage, L. Donati, F. Soulez, D. Fortun, G. Schmit, A. Seitz, R. Guiet, C. Vonesch, and M. Unser, “Decon- volutionLab2: An open-source software for deconvolution microscopy,” Methods 115, 28–41 (2017).

Usage examples

Example 1

Deconvolve the previously stored cell image (Section 3.1) with the Richardson-Lucy Total Variance algorithm using $\lambda$ = 0.001 and 5 iterations:

In [35]:
from DeconvTest.modules.deconvolution import deconvolution_lab_rltv
deconvolution_lab_rltv(imagej_path=imagej_path, 
                       inputfile=os.getcwd() + '/code_examples/convolved_cell.tif',
                       psffile=os.getcwd() + '/code_examples/psf.tif',
                       rltv_lambda=0.001, 
                       iterations=5, 
                       outputfile=os.getcwd() + '/code_examples/deconvolved_rltv.tif')

cell = Cell(filename='code_examples/deconvolved_rltv.tif')
cell.show_2d_projections()
/home/anna/anaconda3/lib/python3.7/site-packages/deconvtest-2.4-py3.7.egg/DeconvTest/classes/image.py:66: Warning: Metadata file is not found!

Iterative Deconvolve 3D (DAMAS)

DAMAS is an iterative deconvolution algorithm that includes multiple regularization techniques (see Dougherty et al. 2005 and the plugin documentation at http://www.optinav.info/Iterative-Deconvolve-3D.htm for details).

Six of the plugin's parameters can be adjusted from the DeconvTest framework:

  • Normalize PSF (normalizes the PSF before deconvoltution)
  • Detect divergence (stops the iteration if the changes in the image increase)
  • Perform antiringing step (reduces artifacts from features close to the image border)
  • Wiener filter $\gamma$ (regualization parameter)
  • Low pass filter (regualization parameter)
  • Terminate iteration if mean delta less than x% (stops the iteration if the changes in the image are less than the value of x)

R. Dougherty, “Extensions of DAMAS and Benefits and Limitations of Deconvolution in Beamforming,” (American Institute of Aeronautics and Astronautics, 2005).

Usage examples

Example 1

Deconvolve the previously stored cell image (Section 3.1) with the DAMAS algorithm using 2 iterations and the following parameters:

  • Normalize PSF: False
  • Detect divergence: False
  • Perform antiringing step: True
  • Wiener filter $\gamma$: 0.0001
  • Low pass filter: 3
  • Terminate iteration if mean $\delta$ <: 0.01
In [36]:
from DeconvTest.modules.deconvolution import iterative_deconvolve_3d
iterative_deconvolve_3d(imagej_path=imagej_path, 
                        inputfile=os.getcwd() + '/code_examples/convolved_cell.tif',
                        psffile=os.getcwd() + '/code_examples/psf.tif', 
                        outputfile=os.getcwd() + '/code_examples/deconvolved_iterative.tif',
                        normalize=False, 
                        perform=False, 
                        detect=True, 
                        wiener=0.0001, 
                        low=3, 
                        terminate=0.01, 
                        iterations=2)

cell = Cell(filename='code_examples/deconvolved_iterative.tif')
cell.show_2d_projections()
/home/anna/anaconda3/lib/python3.7/site-packages/deconvtest-2.4-py3.7.egg/DeconvTest/classes/image.py:66: Warning: Metadata file is not found!

Quantification of deconvolution performance

After deconvolving synthetic images of cells, the performance of deconvolution can be quantified by comparing the images of reconstructed cells to the ground truth (initially generated cells) and computing the reconstruction errors. To quantify the reconstuction errors, the following two measures are computed:

  1. Root-mean-square error (RMSE):
$$RMSE = \sqrt{\frac{\sum_{i=1}^{N} (y_i - x_i)^2}{N}},$$

where $x_i$ is the $i$-th pixel's value in the ground truth image, $y_i$ is the corresponding pixel value in the reconstructed image, and $N$ is the total number of pixels.

  1. Normalized root-mean-square error (NRMSE):
$$NRMSE = \frac{RMSE}{x_{max} - x_{min}},$$

where $x_{max}$ and $x_{min}$ are the maximum and the minimum intensity values of the ground truth image, respectively.

Usage examples

Example 1

Generate a cell, save it to a tiff file, and convolve it with a PSF:

In [37]:
cell = Cell(input_voxel_size=0.5, 
            size=[10, 6, 6], 
            phi=np.pi/4, 
            theta=np.pi/2)
cell.show_2d_projections()
cell.save('code_examples/ground_truth_cell.tif')
psf = PSF(sigma=2, 
          aspect_ratio=1.5)
cell.convolve(psf)
cell.show_2d_projections()

Compare the images of the cell before and after convolution:

In [38]:
gt_cell = Cell(filename='code_examples/ground_truth_cell.tif')
cell.compute_accuracy_measures(gt_cell)
Out[38]:
RMSE NRMSE
0 22.300309 0.087452

Running DeconvTest in a batch mode

Simulation of microscopy experiments can be done in a batch mode, i.e. synthetic cells can be generated, convolved, deconvolved and quantified in a parallel manner. The number of processes that should be run in parallel is specified by the parameters max_threads. If the parameter print_progress is set to True, the progress of the computation and the remaining time will be printed out.

Usage examples

Example 1: Generate single cells

Generate cell parameters for 3 ellipsoidal cells with axes 10 $\pm$ 2 µm:

In [39]:
from DeconvTest import batch
batch.generate_cell_parameters(outputfile='code_examples/cell_parameters.csv', 
                               number_of_cells=3, 
                               size_mean_and_std=(10, 2), 
                               equal_dimensions=False)
pd.read_csv('code_examples/cell_parameters.csv', sep='\t', index_col=0)
Out[39]:
size_x size_y size_z phi theta z y x input_cell_kind
0 10.342048 7.540784 10.621244 5.222262 1.848282 0.212806 0.075437 0.648796 ellipsoid
1 8.530039 12.086726 7.791857 5.223985 2.325712 0.278137 0.339334 0.912768 ellipsoid
2 11.472264 9.833669 8.702283 1.231429 1.897113 0.338912 0.717907 0.915383 ellipsoid

Generate the cells with voxel size 0.2 µm per px running 4 processes in parallel:

In [40]:
batch.generate_cells_batch(params_file='code_examples/cell_parameters.csv', 
                           outputfolder='code_examples/input_cells', 
                           input_voxel_size=0.2, 
                           max_threads=4, 
                           print_progress=True)
Run Generation of cells
Started at  Sat Nov 23 20:22:09 2019
done 1 of 3 ( 33.333333333333336 % ), approx. time left:  0.5394186973571777 sec
done 2 of 3 ( 66.66666666666667 % ), approx. time left:  0.1605837345123291 sec
done 3 of 3 ( 100.0 % ), approx. time left:  0.0 sec
Generation of cells done

Cell images are saved in the specified directory together with their metadata, which is stored in a csv file:

In [41]:
sorted(os.listdir('code_examples/input_cells'))
Out[41]:
['cell_000.csv',
 'cell_000.tif',
 'cell_001.csv',
 'cell_001.tif',
 'cell_002.csv',
 'cell_002.tif']
In [42]:
pd.read_csv('code_examples/input_cells/cell_002.csv', sep='\t', 
            index_col=0, header=None).transpose()
Out[42]:
Voxel size Voxel size z Voxel size y Voxel size x Voxel size arr size_x size_y size_z phi theta z y x kind Convolved CellID
1 [0.2 0.2 0.2] 0.2 0.2 0.2 [0.2 0.2 0.2] 11.472263615296075 9.833669089581225 8.702282724036799 1.2314294955994782 1.8971128153188703 0.3389115175482139 0.7179073754119432 0.9153831848159096 ellipsoid False 002

Generate 4 PSF images with resolution 0.2 µm per px and all combinations of given xy standard deviations and elongation:

In [43]:
batch.generate_psfs_batch(outputfolder='code_examples/psfs', 
                          psf_sigmas=[0.3, 1], 
                          psf_aspect_ratios=[1.5, 4], 
                          input_voxel_size=[0.2], 
                          max_threads=4, 
                          print_progress=False)

PSF images are saved in the specified directory together with their metadata:

In [44]:
sorted(os.listdir('code_examples/psfs'))
Out[44]:
['psf_sigma_0.3_aspect_ratio_1.5.csv',
 'psf_sigma_0.3_aspect_ratio_1.5.tif',
 'psf_sigma_0.3_aspect_ratio_4.csv',
 'psf_sigma_0.3_aspect_ratio_4.tif',
 'psf_sigma_1_aspect_ratio_1.5.csv',
 'psf_sigma_1_aspect_ratio_1.5.tif',
 'psf_sigma_1_aspect_ratio_4.csv',
 'psf_sigma_1_aspect_ratio_4.tif']

Convolve all input cells with all PSFs:

In [45]:
batch.convolve_batch(inputfolder='code_examples/input_cells', 
                     psffolder='code_examples/psfs',
                     outputfolder='code_examples/convolved', 
                     max_threads=4, 
                     print_progress=False)

For each PSF, a directory is generated where the convolved cells are stored. The PSF itself is also stored together with its metadata.

The output file structure is as follows:

  • PSF 1 image
  • PSF 1 metadata
  • folder with convolution results for PSF 1
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • PSF 2 image
  • PSF 2 metadata
  • folder with convolution results for PSF 2
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • ...
In [46]:
sorted(os.listdir('code_examples/convolved'))
Out[46]:
['psf_sigma_0.3_aspect_ratio_1.5',
 'psf_sigma_0.3_aspect_ratio_1.5.csv',
 'psf_sigma_0.3_aspect_ratio_1.5.tif',
 'psf_sigma_0.3_aspect_ratio_4',
 'psf_sigma_0.3_aspect_ratio_4.csv',
 'psf_sigma_0.3_aspect_ratio_4.tif',
 'psf_sigma_1_aspect_ratio_1.5',
 'psf_sigma_1_aspect_ratio_1.5.csv',
 'psf_sigma_1_aspect_ratio_1.5.tif',
 'psf_sigma_1_aspect_ratio_4',
 'psf_sigma_1_aspect_ratio_4.csv',
 'psf_sigma_1_aspect_ratio_4.tif']

Each PSF directory contains convolution results for all input cells together with the metadata:

In [47]:
sorted(os.listdir('code_examples/convolved/psf_sigma_1_aspect_ratio_1.5'))
Out[47]:
['cell_000.csv',
 'cell_000.tif',
 'cell_001.csv',
 'cell_001.tif',
 'cell_002.csv',
 'cell_002.tif']

Resize the convolved cells to voxel sizes 0.5 µm in all axes, as well as 0.5 µm in xy and 1 µm in z:

In [48]:
batch.resize_batch(inputfolder='code_examples/convolved', 
                   outputfolder='code_examples/resized', 
                   voxel_sizes_for_resizing=[0.5, [1, 0.5, 0.5]], 
                   max_threads=4, 
                   print_progress=False)

For each combination of voxel size and PSF, a new directory was created. PSF images are also resized. The output file structure is as follows:

  • PSF 1, resolution 1: image
  • PSF 1, resolution 1: metadata
  • folder with resizing results for PSF 1, resolution 1
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • PSF 1, resolution 2: image
  • PSF 1, resolution 2: metadata
  • folder with resizing results for PSF 1, resolution 2
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • PSF 2, resolution 1: image
  • PSF 2, resolution 1: metadata
  • folder with resizing results for PSF 2, resolution 1
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • PSF 2, resolution 2: image
  • PSF 2, resolution 2: metadata
  • folder with resizing results for PSF 2, resolution 2
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • ...
In [49]:
sorted(os.listdir('code_examples/resized'))
Out[49]:
['psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5]',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5].tif',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5]',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5].tif',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[0.5_0.5_0.5]',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[0.5_0.5_0.5].tif',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[1._0.5_0.5]',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[1._0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[1._0.5_0.5].tif',
 'psf_sigma_1_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5]',
 'psf_sigma_1_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_1_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5].tif',
 'psf_sigma_1_aspect_ratio_1.5_voxel_size_[1._0.5_0.5]',
 'psf_sigma_1_aspect_ratio_1.5_voxel_size_[1._0.5_0.5].csv',
 'psf_sigma_1_aspect_ratio_1.5_voxel_size_[1._0.5_0.5].tif',
 'psf_sigma_1_aspect_ratio_4_voxel_size_[0.5_0.5_0.5]',
 'psf_sigma_1_aspect_ratio_4_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_1_aspect_ratio_4_voxel_size_[0.5_0.5_0.5].tif',
 'psf_sigma_1_aspect_ratio_4_voxel_size_[1._0.5_0.5]',
 'psf_sigma_1_aspect_ratio_4_voxel_size_[1._0.5_0.5].csv',
 'psf_sigma_1_aspect_ratio_4_voxel_size_[1._0.5_0.5].tif']

Each directory contains corresponding cells with the metadata:

In [50]:
sorted(os.listdir('code_examples/resized/psf_sigma_0.3_aspect_ratio_4_voxel_size_[1._0.5_0.5]'))
Out[50]:
['cell_000.csv',
 'cell_000.tif',
 'cell_001.csv',
 'cell_001.tif',
 'cell_002.csv',
 'cell_002.tif']

Add Gaussian noise with SNRs 5 and 10 to all resized cells:

In [51]:
batch.add_noise_batch(inputfolder='code_examples/resized', 
                      outputfolder='code_examples/noise', 
                      snr=[5, 10], 
                      noise_kind='gaussian', 
                      max_threads=4, 
                      print_progress=False)

The output file structure is as follows:

  • PSF 1, resoluton 1: image
  • PSF 1, resoluton 1: metadata
  • folder with convolution results for PSF 1, resoluton 1, SNR 1
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • folder with convolution results for PSF 1, resoluton 1, SNR 2
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • ...
  • PSF 2, resoluton 2: image
  • PSF 2, resoluton 2: metadata
  • folder with convolution results for PSF 2, resoluton 2, SNR 1
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • folder with convolution results for PSF 2, resoluton 2, SNR 2
    • cell 1 image
    • cell 1 metadata
    • cell 2 image
    • cell 2 metadata
    • ...
  • ...
In [52]:
sorted(os.listdir('code_examples/noise'))[0:10]
Out[52]:
['psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5].tif',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5]_noise_gaussian_snr=10',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5]_noise_gaussian_snr=5',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5].tif',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5]_noise_gaussian_snr=10',
 'psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[1._0.5_0.5]_noise_gaussian_snr=5',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_0.3_aspect_ratio_4_voxel_size_[0.5_0.5_0.5].tif']
Example 2: generate multicellular samples

Generate parameters for samples with varying number of cells:

In [53]:
batch.generate_cell_parameters(outputfile='code_examples/stack_parameters.csv', 
                               number_of_stacks=3, 
                               number_of_cells=[3, 5], 
                               size_mean_and_std=(10, 2), 
                               equal_dimensions=False)
pd.read_csv('code_examples/stack_parameters.csv', sep='\t', index_col=0)
Out[53]:
size_x size_y size_z phi theta z y x input_cell_kind stack
0 8.434726 10.234011 7.660893 2.705187 1.367274 0.746544 0.198747 0.528852 ellipsoid 0
1 8.404471 8.079947 8.106322 3.340192 1.188578 0.206613 0.773662 0.645588 ellipsoid 0
2 16.066037 13.163236 7.372376 0.168824 2.767139 0.435277 0.409944 0.425916 ellipsoid 0
3 9.939774 10.901759 9.030953 4.639031 1.840216 0.767115 0.780256 0.655598 ellipsoid 0
4 8.586936 12.351030 10.890392 3.954106 0.572943 0.900957 0.859113 0.244108 ellipsoid 1
5 6.730910 12.148388 6.977474 5.519703 2.208111 0.683956 0.038275 0.347131 ellipsoid 1
6 13.859217 9.491067 11.429917 5.495994 1.029616 0.801981 0.125064 0.779321 ellipsoid 1
7 9.686063 10.714937 10.580438 6.227956 0.495953 0.809791 0.790994 0.922074 ellipsoid 1
8 8.403695 10.019205 12.752367 1.586155 0.508800 0.129551 0.621037 0.725749 ellipsoid 2
9 13.096508 6.722746 10.851751 5.352123 2.692592 0.838122 0.673539 0.394024 ellipsoid 2
10 9.325140 11.779813 10.229388 2.135610 2.340001 0.812708 0.672131 0.633409 ellipsoid 2
11 8.862764 13.684674 7.520243 4.468480 1.807637 0.861867 0.243643 0.438672 ellipsoid 2

Generate multicellular samples with voxel size 2 µm per px in z and 0.5 µm per px in xy:

In [54]:
batch.generate_cells_batch(params_file='code_examples/stack_parameters.csv', 
                           stack_size_microns=[60, 100, 100],
                           outputfolder='code_examples/input_stacks', 
                           input_voxel_size=[2, 0.5, 0.5], 
                           max_threads=4, 
                           print_progress=False)
sorted(os.listdir('code_examples/input_stacks'))
Out[54]:
['stack_000.csv',
 'stack_000.tif',
 'stack_001.csv',
 'stack_001.tif',
 'stack_002.csv',
 'stack_002.tif']

Generate PSFs with voxel size 2 µm per px in z and 0.5 µm per px in xy, standard deviation 0.3 µm in xy and elongation 1.5:

In [55]:
batch.generate_psfs_batch(outputfolder='code_examples/psfs2', 
                          psf_sigmas=[0.3], 
                          psf_aspect_ratios=[1.5], 
                          input_voxel_size=[2, 0.5, 0.5], 
                          max_threads=4, 
                          print_progress=False)

Convolve all input images with all PSFs:

In [56]:
batch.convolve_batch(inputfolder='code_examples/input_stacks', 
                     psffolder='code_examples/psfs2', 
                     outputfolder='code_examples/stacks_convolved', 
                     max_threads=4, 
                     print_progress=False)

Resize the convolved images:

In [57]:
batch.resize_batch(inputfolder='code_examples/stacks_convolved', 
                   outputfolder='code_examples/stacks_resized', 
                   voxel_sizes_for_resizing=[[3, 0.5, 0.5]], 
                   max_threads=4, 
                   print_progress=False)

Add Poisson noise to the resized images:

In [58]:
batch.add_noise_batch(inputfolder='code_examples/stacks_resized', 
                      outputfolder='code_examples/stacks_noise', 
                      snr=[2], 
                      noise_kind='poisson', 
                      max_threads=4, 
                      print_progress=False)
Example 3: run the full simulation on single cell images

Generate cell parameters, cells, PSFs, convolve the cells with the PSFs and add noise:

In [59]:
batch.generate_cell_parameters(outputfile='code_examples/cell_parameters.csv', 
                               number_of_cells=1, 
                               size_mean_and_std=(8, 2), 
                               equal_dimensions=False)
batch.generate_cells_batch(params_file='code_examples/cell_parameters.csv', 
                           outputfolder='code_examples/input_cells_deconv', 
                           input_voxel_size=0.3, 
                           max_threads=4, 
                           print_progress=False)
batch.generate_psfs_batch(outputfolder='code_examples/psfs_deconv', 
                          psf_sigmas=[0.3], psf_aspect_ratios=[1.5], 
                          input_voxel_size=[0.3], 
                          max_threads=4, 
                          print_progress=False)
batch.convolve_batch(inputfolder='code_examples/input_cells_deconv', 
                     psffolder='code_examples/psfs_deconv', 
                     outputfolder='code_examples/convolved_deconv', 
                     max_threads=4, 
                     print_progress=False)
batch.resize_batch(inputfolder='code_examples/convolved_deconv',
                  outputfolder='code_examples/resized_deconv',
                  voxel_sizes_for_resizing=[0.5],
                  max_threads=4, 
                  print_progress=False)
batch.add_noise_batch(inputfolder='code_examples/resized_deconv', 
                      outputfolder='code_examples/noise_deconv', 
                      snr=[10], 
                      noise_kind='poisson',
                      max_threads=4, 
                      print_progress=False)

Deconvolve the generated cells with Regularize Inverse Filter and with Richardson-Lucy Total Variance algorithm, using two sets of parameters for each algorithm:

In [60]:
batch.deconvolve_batch(inputfolder='code_examples/noise_deconv/',
                      outputfolder='code_examples/deconvolved/', 
                      deconvolution_algorithm=['deconvolution_lab_rif', 'deconvolution_lab_rltv'],
                      deconvolution_lab_rif_regularization_labmda=[0.001, 0.002], 
                      deconvolution_lab_rltv_regularization_labmda=[0.0001], 
                      deconvolution_lab_rltv_iterations=[10, 15], 
                      print_progress=False, 
                      max_threads=4)
os.listdir('code_examples/deconvolved/')
Out[60]:
['deconvolution_lab_rif_regularization_labmda=0.002',
 'deconvolution_lab_rltv_regularization_labmda=0.0001_iterations=10',
 'deconvolution_lab_rltv_regularization_labmda=0.0001_iterations=15',
 'deconvolution_lab_rif_regularization_labmda=0.001']
In [61]:
os.listdir('code_examples/deconvolved/deconvolution_lab_rif_regularization_labmda=0.001')
Out[61]:
['psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=10']
In [62]:
os.listdir('code_examples/deconvolved/deconvolution_lab_rif_regularization_labmda=0.001/psf_sigma_0.3_aspect_ratio_1.5_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=10')
Out[62]:
['cell_000.csv', 'cell_000.tif']

Compare the deconvolved images to the ground truth:

In [63]:
batch.accuracy_batch(inputfolder='code_examples/deconvolved', 
                     reffolder='code_examples/input_cells_deconv', 
                     outputfolder='code_examples/accuracy', 
                     max_threads=4, 
                     print_progress=False)

All accuracy values are combined into a single csv file:

In [64]:
pd.read_csv('code_examples/accuracy.csv', sep='\t', index_col=0)
Out[64]:
CellID Convolved Deconvolution algorithm NRMSE Name PSF aspect ratio PSF sigma xy um RMSE SNR Voxel size ... noise type phi regularization_labmda size_x size_y size_z theta x y z
0 0.0 True deconvolution_lab_rif 0.260275 deconvolution_lab_rif_regularization_labmda=0.... 1.5 0.3 66.370061 10.0 [0.5 0.5 0.5] ... poisson 2.371714 0.0020 6.770303 9.922441 8.722771 2.045996 0.913367 0.057857 0.094649
1 0.0 True deconvolution_lab_rltv 0.123203 deconvolution_lab_rltv_regularization_labmda=0... 1.5 0.3 31.416859 10.0 [0.5 0.5 0.5] ... poisson 2.371714 0.0001 6.770303 9.922441 8.722771 2.045996 0.913367 0.057857 0.094649
2 0.0 True deconvolution_lab_rltv 0.133348 deconvolution_lab_rltv_regularization_labmda=0... 1.5 0.3 34.003770 10.0 [0.5 0.5 0.5] ... poisson 2.371714 0.0001 6.770303 9.922441 8.722771 2.045996 0.913367 0.057857 0.094649
3 0.0 True deconvolution_lab_rif 0.260275 deconvolution_lab_rif_regularization_labmda=0.... 1.5 0.3 66.370061 10.0 [0.5 0.5 0.5] ... poisson 2.371714 0.0010 6.770303 9.922441 8.722771 2.045996 0.913367 0.057857 0.094649

4 rows × 26 columns

Running in silico deconvolution experiments with the "run_simulation" script

Specifying parameters of the experiment

An in silico microscopy and deconvolution experiment can be run via executing a specifically developed script, which accepts the parameters of the experiment specified in a csv config file.

The following are the parameters and their default values (if any) used by the script:

Parameter Description Default value
simulation_folder directory to save the simulation output ./test_simulation
simulation_steps list of simulation steps to run; valid values are: generate_cells, generate_psfs, convolve, resize, add_noise, deconvolve, accuracy ['generate_cells', 'generate_psfs', 'convolve', 'resize', 'add_noise','deconvolve', 'accuracy']
cell_parameter_filename csv file name in the simulation_folder to save cell parameters cell_parameters.csv
inputfolder directory in the simulation_folder to save generated synthetic cells input
psffolder directory in the simulation_folder to save generated PSF images psf
convolve_results_folder directory in the simulation_folder to save the results of convolution of input cells with PSFs convolved
resize_results_folder directory in the simulation_folder to save the downsized images resized
add_noise_results_folder directory in the simulation_folder to save images after adding noise noise
deconvolve_results_folder directory in the simulation_folder to save deconvolved images deconvolved
accuracy_results_folder directory in the simulation_folder to save the deconvolution performance results accuracy_measures
log_folder directory in the simulation_folder to store computing time timelog
max_threads the maximal number of processes to run in parallel 4
print_progress if True, the progress of the computation will be printed True
number_of_stacks number of multicellular samples to generate; if None, no multicellular samples will be generated, but only single cells None
number_of_cells number of single cells to generate or number of cells per each multicellular stack; if a range is indicated, and the number_of_stacks parameter is not None, a random number will be chosen from this range to set the number of cells in each individual multicellular sample 2
input_cell_kind type the input cell to generate; valid values are: ellipsoid, spiky_cell ellipsoid
size_mean_and_std mean value and standard deviation for the cell size in micrometers; the cell size is drawn randomly from a Gaussian distribution with the given mean and standard deviation. (10, 2)
equal_dimensions if True, input cells will be generated as spheres; if False, input cells will be generated as ellipsoids with sizes for all three axes chosen independently False
spikiness_range range for the fraction of cell surface area covered by spikes (if input_cell_kind is spiky_cell)
spike_size_range range for the standard deviation for the spike amplitude relative to the cell radius (if input_cell_kind is spiky_cell)
spike_smoothness_range range for the width of the Gaussian filter that is used to smooth the spikes (if input_cell_kind is spiky_cell)
input_voxel_size voxel size in z, y and x used to generate the cell image; if one value is provided, the voxel size is assume to be equal along all axes. 0.3
stack_size_microns dimensions of the multicellular image stack in micrometers [10, 100, 100]
psf_sigmas standard deviation(s) of the PSF in xy in micrometers; a PSF image will be generated for each combination of the provided values of psf_sigmas and psf_aspect_ratios [0.1, 0.5]
psf_aspect_ratios PSF aspect ratios; a PSF image will be generated for each combination of the provided values of psf_sigmas and psf_aspect_ratios [3]
voxel_sizes_for_resizing list of new voxel sizes to which the input images should be resized; each item of the list is a scalar (for the same voxels size along all axes) or sequence of scalars (voxel size in z, y and x) [[1, 0.5, 0.5]]
noise_kind type of noise to add; valid values are: poisson, gaussian; if several values are provided, all noise types will be added in the given order; if the same number of values are provided for noise_kind and snr and the test_snr_combinations parameter is False, only one corresponding SNR will be used for each noise kind; for test_snr_combinations equal True see description of the test_snr_combinations parameter [poisson]
snr target signal-to-noise ratio (SNR) value(s); for each provided value, a new image will be generated; if several values for noise_kind are provided, and the same nuber of values are provided for snr, one corresponding SNR will be used for each noise kind (only if test_snr_combinations parameter is False, for test_snr_combinations equal True see description of the test_snr_combinations parameter) [None, 5]
test_snr_combinations if True, and several values are provided for the noise_kind parameter and snr parameter, all combinations of given SNRs will be used for all provided noise types (e.g. if noise_kind is [poisson, gaussian] and snr is [5, 10], the following four noise features will be applied: Poisson noise with SNR 5 + Gaussian noise with SNR 5, Poisson noise with SNR 5 + Gaussian noise with SNR 10, Poisson noise with SNR 10 + Gaussian noise with SNR 5, Poisson noise with SNR 10 + Gaussian noise with SNR 10) False
deconvolution_algorithm deconvolution algorithm(s) that will be applied to deconvolve the input images; if a sequence of algorithms is provided, all algorithms from the sequence will be applied [deconvolution_lab_rif, deconvolution_lab_rltv]
log_computing_time if True, computing time spent on deconvolution will be recorded and stored in the log_folder True
deconvolution_lab_rif_regularization_lambda regularization parameter for the RIF algorithm; if a sequence is provided, all values from the sequence will be tested [0.001, 1]
deconvolution_lab_rltv_regularization_lambda regularization parameter for the RLTV algorithm; if a sequence is provided, all values from the sequence will be tested in combination with the iterations parameter 0.001
deconvolution_lab_rltv_iterations number of iterations in the RLTV algorithm; if a sequence is provided, all values from the sequence will be tested in combination with the rltv_lambda parameter [2, 3]
iterative_deconvolve_3d_normalize Normalize PSF parameter for the Iterative Deconvolve 3D plugin; if a sequence is provided, all values from the sequence will be tested in combination with other parameters
iterative_deconvolve_3d_perform Perform anti-ringig step parameter for the Iterative Deconvolve 3D plugin; if a sequence is provided, all values from the sequence will be tested in combination with other parameters
iterative_deconvolve_3d_detect Detect divergence parameter for the Iterative Deconvolve 3D plugin; if a sequence is provided, all values from the sequence will be tested in combination with other parameters
iterative_deconvolve_3d_wiener Wiener filter gamma parameter for the Iterative Deconvolve 3D plugin; <0.0001 to turn off, 0.0001 - 0.1 as tests; if a sequence is provided, all values from the sequence will be tested in combination with other parameters
iterative_deconvolve_3d_low Low pass filter parameter for the Iterative Deconvolve 3D plugin, pixels; the same value is used for low pass filter in xy and z; if a sequence is provided, all values from the sequence will be tested in combination with other parameters
iterative_deconvolve_3d_terminate Terminate iteration if mean delta < x% parameter for the Iterative Deconvolve 3D plugin; 0 to turn off; if a sequence is provided, all values from the sequence will be tested in combination with other parameters

The parameter file does not have to specify all parameter values, but only those that should be different from the default values. This is how an example parameter file may look like:

In [65]:
pd.read_csv('example_config_file.csv', sep='\t', index_col=0, header=None).squeeze()
Out[65]:
0
simulation_folder                                                      ../../../Data/simulation1
max_threads                                                                                    3
simulation_steps                               ['generate_cells', 'generate_psfs', 'convolve'...
cell_parameter_filename                                                      cell_parameters.csv
number_of_cells                                                                                5
input_cell_kind                                                                       spiky_cell
input_voxel_size                                                                             0.3
spikiness_range                                                                       (0.5, 0.6)
psf_aspect_ratios                                                                          [4.5]
psf_sigmas                                                                                   [1]
voxel_sizes_for_resizing                                                    [0.3, [1, 0.3, 0.3]]
deconvolution_algorithm                                                    deconvolution_lab_rif
deconvolution_lab_rif_regularization_lambda                                         [0.001, 001]
Name: 1, dtype: object

Running the experiment with the "run_simulation" script

To run a deconvolution experiment with specified settings, the run_simulation.py scipt should be copied to the directory of choice, and the following command should be executed:

python run_simulation.py <settings_file>

After the simulation is completed, the computed accuracy will be summarized in a csv file named the same as the accuracy_results_folder specified in the config file.

Running DeconvTest with external synthetic data

DeconvTest can also be used with externally generated synthetic images.

Usage examples

Example 1

Generate synthetic images with the software of your choice. Here is an example how this can be done using the DeconvolutionLab2 plugin of Fiji:

Generate synthetic PSF images. For instance, with DeconvolutionLab2 you can generate an Airy-disk-shaped PSF:

We generated two images with random lines and one airy-disk PSF and saved them in the folders 'data/synthetic/input/' and 'data/synthetic/psf/', respectively:

In [66]:
os.listdir('data/synthetic/input')
Out[66]:
['RandomLines1.tif', 'RandomLines2.tif']
In [67]:
stack = Stack(filename='data/synthetic/input/RandomLines1.tif')
stack.show_2d_projections()
/home/anna/anaconda3/lib/python3.7/site-packages/deconvtest-2.4-py3.7.egg/DeconvTest/classes/image.py:66: Warning: Metadata file is not found!
In [68]:
os.listdir('data/synthetic/psf')
Out[68]:
['psf_airy.tif']
In [69]:
psf = PSF(filename='data/synthetic/psf/psf_airy.tif')
psf.show_2d_projections()
/home/anna/anaconda3/lib/python3.7/site-packages/deconvtest-2.4-py3.7.egg/DeconvTest/classes/image.py:66: Warning: Metadata file is not found!

As the next step, we need to create a metadata file with voxel size values for each image. Let's set voxel size to 1 µm in all dimensions:

In [70]:
from DeconvTest.classes.metadata import Metadata
for fn in os.listdir('data/synthetic/input/'):
    md = Metadata()
    md.set_voxel_size(1)
    md.to_csv('data/synthetic/input/' + fn[:-4] + '.csv', sep='\t', header=False)

For the PSF image, the size of the PSF needs to be stored in the metadata file. Since this value is only used to group the quantification results, any value can be provided. Moreover, isPSF parameter has to be set to True to indicate that we don't want to deconvolve this image during the deconvolution step.

In [71]:
for fn in os.listdir('data/synthetic/psf/'):
    md = Metadata()
    md.set_voxel_size(1)
    md['PSF sigma xy um'] = 1
    md['PSF aspect ratio'] = 1
    md['isPSF'] = 1
    md.to_csv('data/synthetic/psf/' + fn[:-4] + '.csv', sep='\t', header=False)

This is how the resulting metadata file looks like:

In [72]:
pd.read_csv('data/synthetic/psf/psf_airy.csv', sep='\t', index_col=0, header=None).squeeze()
Out[72]:
0
Voxel size          [1. 1. 1.]
Voxel size z               1.0
Voxel size y               1.0
Voxel size x               1.0
Voxel size arr      [1. 1. 1.]
PSF sigma xy um              1
PSF aspect ratio             1
isPSF                        1
Name: 1, dtype: object

Now, let's generate a configuration file to run DeconvTest workflow with these images. We will convolve the synthetic images and the PSF, add Poisson noise with SNR=5 and deconvolve the resulting data with two different values of the RIF $\lambda$:

In [73]:
config = pd.Series({'simulation_folder': 'data/synthetic/',
                    'simulation_steps': ['convolve', 'add_noise','deconvolve', 'accuracy'],
                    'max_threads': 1,
                    'noise_kind': ['poisson'],
                    'snr': [5],
                    'test_snr_combinations': False,
                    'deconvolution_algorithm': ['deconvolution_lab_rif'],
                    'deconvolution_lab_rif_regularization_lambda': [0.001, 1]
                    })
config.to_csv('config_external_data.csv', sep='\t', header=False)
config
Out[73]:
simulation_folder                                                          data/synthetic/
simulation_steps                               [convolve, add_noise, deconvolve, accuracy]
max_threads                                                                              1
noise_kind                                                                       [poisson]
snr                                                                                    [5]
test_snr_combinations                                                                False
deconvolution_algorithm                                            [deconvolution_lab_rif]
deconvolution_lab_rif_regularization_lambda                                     [0.001, 1]
dtype: object

Next, we run DeconvTest using this configuration file:

In [74]:
%run run_simulation.py config_external_data.csv
Run the step 'convolve'
Input folder: data/synthetic/input Output folder: data/synthetic/convolved
Run Convolution
Started at  Sat Nov 23 20:22:52 2019
done 1 of 2 ( 50.0 % ), approx. time left:  2.616804599761963 sec
done 2 of 2 ( 100.0 % ), approx. time left:  0.0 sec
Convolution done
Run the step 'add_noise'
Input folder: data/synthetic/convolved Output folder: data/synthetic/noise
Run Add noise
Started at  Sat Nov 23 20:22:58 2019
done 1 of 3 ( 33.333333333333336 % ), approx. time left:  2.3192248344421387 sec
done 2 of 3 ( 66.66666666666667 % ), approx. time left:  1.0852831602096558 sec
done 3 of 3 ( 100.0 % ), approx. time left:  0.0 sec
Add noise done
Run the step 'deconvolve'
Input folder: data/synthetic/noise Output folder: data/synthetic/deconvolved
Run Deconvolve
Started at  Sat Nov 23 20:23:00 2019
done 1 of 6 ( 16.666666666666668 % ), approx. time left:  1.9963355263074238 min
done 2 of 6 ( 33.333333333333336 % ), approx. time left:  1.5492371956507365 min
done 3 of 6 ( 50.0 % ), approx. time left:  1.1429373264312743 min
done 4 of 6 ( 66.66666666666667 % ), approx. time left:  45.04067528247833 sec
done 5 of 6 ( 83.33333333333333 % ), approx. time left:  18.0279598236084 sec
done 6 of 6 ( 100.0 % ), approx. time left:  0.0 sec
Deconvolve done
Run the step 'accuracy'
Input folder: data/synthetic/deconvolved Output folder: data/synthetic/accuracy_measures
Reference folder: data/synthetic/deconvolved
Run Compute accuracy measures
Started at  Sat Nov 23 20:24:30 2019
done 1 of 4 ( 25.0 % ), approx. time left:  19.2667293548584 sec
done 2 of 4 ( 50.0 % ), approx. time left:  12.755794525146484 sec
done 3 of 4 ( 75.0 % ), approx. time left:  6.276400248209637 sec
done 4 of 4 ( 100.0 % ), approx. time left:  0.0 sec
Compute accuracy measures done

Let's now look at the resulting accuracy measures and plot the NRMSE depending on the value of RIF $\lambda$:

In [75]:
accuracy = pd.read_csv('data/synthetic/accuracy_measures.csv', sep='\t')
accuracy
Out[75]:
Unnamed: 0 Convolved Deconvolution algorithm NRMSE Name PSF aspect ratio PSF sigma xy um RMSE SNR Voxel size Voxel size arr Voxel size x Voxel size y Voxel size z noise type regularization_lambda
0 0 True deconvolution_lab_rif 149.833538 deconvolution_lab_rif_regularization_lambda=1.... 1.0 1.0 53.829678 5.0 [1. 1. 1.] [1. 1. 1.] 1.0 1.0 1.0 poisson 1.000
1 1 True deconvolution_lab_rif 144.858149 deconvolution_lab_rif_regularization_lambda=1.... 1.0 1.0 55.850574 5.0 [1. 1. 1.] [1. 1. 1.] 1.0 1.0 1.0 poisson 1.000
2 2 True deconvolution_lab_rif 792.756149 deconvolution_lab_rif_regularization_lambda=0.... 1.0 1.0 106.241340 5.0 [1. 1. 1.] [1. 1. 1.] 1.0 1.0 1.0 poisson 0.001
3 3 True deconvolution_lab_rif 631.433266 deconvolution_lab_rif_regularization_lambda=0.... 1.0 1.0 99.508128 5.0 [1. 1. 1.] [1. 1. 1.] 1.0 1.0 1.0 poisson 0.001
In [76]:
import seaborn as sns
sns.swarmplot(data=accuracy, x='regularization_lambda', y='NRMSE')
Out[76]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fbc0cc89358>

It seems like the RIF $\lambda$ value of 1 worked much better for these data than the value of 0.001.

Running DeconvTest with external deconvolution results

Synthetic microscopy images generated with DeconvTest can be deconvolved using external software and evaluated for deconvolution errors using DeconvTest.

Usage examples

Example 1

First, we need to create a configuration file to generate synthetic images. Let's generate two ellipsoidal cells, convolve them with PSF, downsample, and add Poisson noise of SNR 5:

In [77]:
config = pd.Series({'simulation_folder': 'data/ext_deconvolution/',
                    'simulation_steps': ['generate_cells', 'generate_psfs', 'convolve', 'resize', 'add_noise'],
                    'max_threads': 1,
                    'number_of_cells': 2,
                    'input_cell_kind': 'ellipsoid',
                    'input_voxel_size': 0.3,
                    'psf_sigmas': [0.5],
                    'psf_aspect_ratios': [4],
                    'voxel_sizes_for_resizing': [0.5],
                    'noise_kind': ['poisson'],
                    'snr': [5]
                    })
config.to_csv('config_external_deconv.csv', sep='\t', header=False)
config
Out[77]:
simulation_folder                                     data/ext_deconvolution/
simulation_steps            [generate_cells, generate_psfs, convolve, resi...
max_threads                                                                 1
number_of_cells                                                             2
input_cell_kind                                                     ellipsoid
input_voxel_size                                                          0.3
psf_sigmas                                                              [0.5]
psf_aspect_ratios                                                         [4]
voxel_sizes_for_resizing                                                [0.5]
noise_kind                                                          [poisson]
snr                                                                       [5]
dtype: object
In [78]:
%run run_simulation.py config_external_deconv.csv
Run the step 'generate_cells'
Generating new cell parameters
Output filedata/ext_deconvolution/cell_parameters.csv
Generating cells
Input file: data/ext_deconvolution/cell_parameters.csv Output folder: data/ext_deconvolution/input
Run Generation of cells
Started at  Sat Nov 23 20:25:00 2019
done 1 of 2 ( 50.0 % ), approx. time left:  0.1070709228515625 sec
done 2 of 2 ( 100.0 % ), approx. time left:  0.0 sec
Generation of cells done
Run the step 'generate_psfs'
Output folder: data/ext_deconvolution/psf
Run Generation of PSFs
Started at  Sat Nov 23 20:25:00 2019
done 1 of 1 ( 100.0 % ), approx. time left:  0.0 sec
Generation of PSFs done
Run the step 'convolve'
Input folder: data/ext_deconvolution/input Output folder: data/ext_deconvolution/convolved
Run Convolution
Started at  Sat Nov 23 20:25:00 2019
done 1 of 2 ( 50.0 % ), approx. time left:  0.30812501907348633 sec
done 2 of 2 ( 100.0 % ), approx. time left:  0.0 sec
Convolution done
Run the step 'resize'
Input folder: data/ext_deconvolution/convolved Output folder: data/ext_deconvolution/resized
Run Resize
Started at  Sat Nov 23 20:25:01 2019
done 1 of 3 ( 33.333333333333336 % ), approx. time left:  0.31549596786499023 sec
done 2 of 3 ( 66.66666666666667 % ), approx. time left:  0.159232497215271 sec
done 3 of 3 ( 100.0 % ), approx. time left:  0.0 sec
Resize done
Run the step 'add_noise'
Input folder: data/ext_deconvolution/resized Output folder: data/ext_deconvolution/noise
Run Add noise
Started at  Sat Nov 23 20:25:01 2019
done 1 of 3 ( 33.333333333333336 % ), approx. time left:  0.2144641876220703 sec
done 2 of 3 ( 66.66666666666667 % ), approx. time left:  0.11081743240356445 sec
done 3 of 3 ( 100.0 % ), approx. time left:  0.0 sec
Add noise done

This resulted in generating two synthetic microscopy images and corresponding PSFs:

In [79]:
os.listdir('data/ext_deconvolution/noise')
Out[79]:
['psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5].tif',
 'psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5].csv',
 'psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=5.0']
In [80]:
os.listdir('data/ext_deconvolution/noise/psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=5.0')
Out[80]:
['cell_001.csv', 'cell_001.tif', 'cell_000.csv', 'cell_000.tif']
In [81]:
psf = Stack(filename='data/ext_deconvolution/noise/psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=5.0/cell_001.tif')
psf.show_2d_projections()
In [82]:
psf = PSF(filename='data/ext_deconvolution/noise/psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5].tif')
psf.show_2d_projections()

As the next step, the generated images can be deconvolved with external software. For instance, here we deconvolved them with the Landweber algorithm from DeconvolutionLab2, which is not yet integrated into DeconvTest:

The user should make sure that the structure of the deconvolution output corresponds to the structure generated by a DeconvTest workflow. Thus, deconvolution results from each parameter combination should be kept in a separate folder (here we applied two different values of the parameter $\gamma$):

In [83]:
os.listdir('data/ext_deconvolution/deconvolved')
Out[83]:
['Landweber_gamma=1', 'Landweber_gamma=0.1']

Each of these folders should contain a subfolder for each PSF/noise combination with the same naming as used before deconvolution:

In [84]:
os.listdir('data/ext_deconvolution/deconvolved/Landweber_gamma=1')
Out[84]:
['psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=5.0']

The subfolders contain the deconvolved images along with their metadata files:

In [85]:
os.listdir('data/ext_deconvolution/deconvolved/Landweber_gamma=1/psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=5.0')
Out[85]:
['cell_001.csv', 'cell_001.tif', 'cell_000.csv', 'cell_000.tif']

The metadata files can be generated by copying the corresponding files before deconvolution and adding the values of the deconvolution parameters (in this case, Landweber gamma):

In [86]:
pd.read_csv('data/ext_deconvolution/deconvolved/Landweber_gamma=1/psf_sigma_0.5_aspect_ratio_4.0_voxel_size_[0.5_0.5_0.5]_noise_poisson_snr=5.0/cell_000.csv', sep='\t', index_col=0, header=None).squeeze()
Out[86]:
0
Convolved                        True
Voxel size              [0.5 0.5 0.5]
Voxel size z                      0.5
Voxel size y                      0.5
Voxel size x                      0.5
Voxel size arr          [0.5 0.5 0.5]
size_x               12.2202971186863
size_y                11.302124005864
size_z               11.5728292920448
phi                  5.16113849690512
theta                1.15527948928779
z                   0.267924706436403
y                   0.015359300731685
x                   0.155969596604871
kind                        ellipsoid
CellID                              0
PSF sigma xy um                   0.5
PSF aspect ratio                    4
SNR                                 5
noise type                    poisson
LW gamma                            1
Name: 1, dtype: object

Once this is done, we can specify another configuration file for running the quantification step of DeconvTest:

In [87]:
config = pd.Series({'simulation_folder': 'data/ext_deconvolution/',
                    'simulation_steps': ['accuracy'],
                    'max_threads': 1,
                    'inputfolder': 'deconvolved',
                    'reffolder': 'input'
                    })
config.to_csv('config_external_deconv_accuracy.csv', sep='\t', header=False)
config
Out[87]:
simulation_folder    data/ext_deconvolution/
simulation_steps                  [accuracy]
max_threads                                1
inputfolder                      deconvolved
reffolder                              input
dtype: object
In [88]:
%run run_simulation.py config_external_deconv_accuracy.csv
Run the step 'accuracy'
Input folder: data/ext_deconvolution/deconvolved Output folder: data/ext_deconvolution/accuracy_measures
Reference folder: data/ext_deconvolution/input
Run Compute accuracy measures
Started at  Sat Nov 23 20:25:07 2019
done 1 of 4 ( 25.0 % ), approx. time left:  0.9212672710418701 sec
done 2 of 4 ( 50.0 % ), approx. time left:  1.0692863464355469 sec
done 3 of 4 ( 75.0 % ), approx. time left:  0.45969223976135254 sec
done 4 of 4 ( 100.0 % ), approx. time left:  0.0 sec
Compute accuracy measures done

Let's now look at the resulting accuracy measures and plot the NRMSE depending on the value of Landweber $\gamma$ :

In [89]:
accuracy = pd.read_csv('data/ext_deconvolution/accuracy_measures.csv', sep='\t')
accuracy
Out[89]:
Unnamed: 0 CellID Convolved LW gamma NRMSE Name PSF aspect ratio PSF sigma xy um RMSE SNR ... kind noise type phi size_x size_y size_z theta x y z
0 0 1.0 True 1.0 0.233872 Landweber_gamma=1/psf_sigma_0.5_aspect_ratio_4... 4.0 0.5 59.637282 5.0 ... ellipsoid poisson 4.609668 7.812153 5.721329 7.123903 2.972010 0.992599 0.640824 0.840501
1 1 0.0 True 1.0 0.281557 Landweber_gamma=1/psf_sigma_0.5_aspect_ratio_4... 4.0 0.5 71.797018 5.0 ... ellipsoid poisson 5.161138 12.220297 11.302124 11.572829 1.155279 0.155970 0.015359 0.267925
2 2 1.0 True 0.1 0.219763 Landweber_gamma=0.1/psf_sigma_0.5_aspect_ratio... 4.0 0.5 56.039561 5.0 ... ellipsoid poisson 4.609668 7.812153 5.721329 7.123903 2.972010 0.992599 0.640824 0.840501
3 3 0.0 True 0.1 0.180290 Landweber_gamma=0.1/psf_sigma_0.5_aspect_ratio... 4.0 0.5 45.974065 5.0 ... ellipsoid poisson 5.161138 12.220297 11.302124 11.572829 1.155279 0.155970 0.015359 0.267925

4 rows × 25 columns

In [90]:
import seaborn as sns
sns.swarmplot(data=accuracy, x='LW gamma', y='NRMSE')
Out[90]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fbc0cb9fc18>

Deconvolving microscopy images with the help of DeconvTest

Importantly, DeconvTest can help to deconvolve real microscopy images by identifying the optimal deconvolution parameters. For this, we first need to identify the properties of our microscopy images such as voxel size, PSF size, noise SNR, and the size of the features of interest. We then generate synthetic data with the same properties and deconvolve them with different settings using DeconvTest, which allows us to identify the deconvolution settings that result in the lowest errors. Finally, we can apply the identified algorithm and settings to reconstruct real microscopy images either within DeconvTest or using other software.

Identifying image properties

Thus, as the first step, we need to determine important image properties. Many of these can be identified using ImageJ.

Voxel size

To check the voxel size of the image, go to Image -> Properties:

Size of the features of interest

To measure the size of the features of interest, e.g. cells, select the line tool and measure the size of a few objects. The length of the line will be displayed on the main panel of ImageJ using the same units that are set in the Image Properties:

Signal-to-noise ratio

To measure the signal-to-noise ratio (SNR), run Analyze -> Set Measurements and select Standard deviation and Mean gray value to be computed.

After that, select a few regions (e.g. with the elliptical selection tool) that are relatively bright but not over-saturated and run Analyze -> Measure.

Divide the computed Mean gray value by the standard deviation to estimate SNR for each region. Take the smallest value for a conservative estimate.

Size of the point spread function

To estimate the size of the PSF, we need a PSF image, which can be either generated theoretically (e.g. using Huygens or ImageJ/Fiji) or measured by imaging fluorescent beads.

In [91]:
psf = PSF(filename='data/microscopy/real_data/psf_measured.tif')
psf.show_2d_projections()

DeconvTest provides tools to measure the size of the PSF by approximating is with a Gaussian function:

In [92]:
sigmas = psf.measure_psf_sigma()
sigmas
Out[92]:
array([1.85119242, 3.34716592, 3.35760041])

The measured values are in pixels and need to be multiplied by the corresponding voxel size to obtain the values in micrometers:

In [93]:
sigmas = sigmas * np.array([3, 0.415, 0.415])
sigmas
Out[93]:
array([5.55357725, 1.38907386, 1.39340417])

The aspect ratio of the PSF can be computed by dividing the standard deviation in z (the first value) by the standard deviation in x or y (the second or third value):

In [94]:
aspect = sigmas[0] / sigmas[1]
aspect
Out[94]:
3.9980431713697064

Running DeconvTest workflow with identified image properties

Now, when the microscopy image properties have been identified, we can adjust the DeconvTest configuration file to generate synthetic images with the same properties and deconvolve them with desired algorithms and parameters:

In [95]:
config = pd.Series({'simulation_folder': 'data/microscopy/',
                    'simulation_steps': ['generate_cells', 'generate_psfs', 
                                         'convolve', 'resize', 'add_noise', 'deconvolve', 'accuracy'],
                    'max_threads': 1,
                    'number_of_cells': 10,
                    'size_mean_and_std': (15, 3),
                    'input_voxel_size': 0.1,
                    'psf_sigmas': 1.4,
                    'psf_aspect_ratios': 4,
                    'voxel_sizes_for_resizing': [[3, 0.45, 0.45]],
                    'noise_kind': ['poisson'],
                    'snr': 2,
                    'deconvolution_algorithm': ['deconvolution_lab_rif', 
                                                'deconvolution_lab_rltv', 'iterative_deconvolve_3d'],
                    'deconvolution_lab_rif_regularization_lambda': [0.01, 0.1, 1, 10, 100, 1000],
                    'deconvolution_lab_rltv_regularization_lambda': [0.001, 0.01, 0.05, 0.1],
                    'deconvolution_lab_rltv_iterations': 5,
                    'iterative_deconvolve_3d_detect': 'FALSE',
                    'iterative_deconvolve_3d_low': [0, 2, 4, 6],
                    'iterative_deconvolve_3d_normalize': 'TRUE',
                    'iterative_deconvolve_3d_perform': 'TRUE',
                    'iterative_deconvolve_3d_terminate': 0.1,
                    'iterative_deconvolve_3d_wiener': [0, 0.1, 1.0, 10]
                    })
config.to_csv('config_microscopy_data.csv', sep='\t', header=False)
config
Out[95]:
simulation_folder                                                                data/microscopy/
simulation_steps                                [generate_cells, generate_psfs, convolve, resi...
max_threads                                                                                     1
number_of_cells                                                                                10
size_mean_and_std                                                                         (15, 3)
input_voxel_size                                                                              0.1
psf_sigmas                                                                                    1.4
psf_aspect_ratios                                                                               4
voxel_sizes_for_resizing                                                        [[3, 0.45, 0.45]]
noise_kind                                                                              [poisson]
snr                                                                                             2
deconvolution_algorithm                         [deconvolution_lab_rif, deconvolution_lab_rltv...
deconvolution_lab_rif_regularization_lambda                         [0.01, 0.1, 1, 10, 100, 1000]
deconvolution_lab_rltv_regularization_lambda                             [0.001, 0.01, 0.05, 0.1]
deconvolution_lab_rltv_iterations                                                               5
iterative_deconvolve_3d_detect                                                              FALSE
iterative_deconvolve_3d_low                                                          [0, 2, 4, 6]
iterative_deconvolve_3d_normalize                                                            TRUE
iterative_deconvolve_3d_perform                                                              TRUE
iterative_deconvolve_3d_terminate                                                             0.1
iterative_deconvolve_3d_wiener                                                  [0, 0.1, 1.0, 10]
dtype: object

After running python run_simulation.py config_microscopy_data.csv, DeconvTest will generate a csv table with NRMSE values for all applied algorithms and parameter combinations:

In [96]:
accuracy = pd.read_csv('data/microscopy/accuracy_measures.csv', sep='\t')
accuracy[:10]
Out[96]:
Unnamed: 0 CellID Convolved Deconvolution algorithm NRMSE Name PSF aspect ratio PSF sigma xy um RMSE SNR ... regularization_lambda size_x size_y size_z terminate theta wiener x y z
0 0 8.0 True iterative_deconvolve_3d 0.172070 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 43.877922 2.0 ... NaN 18.819422 10.701913 12.138512 0.1 1.831814 0.1 0.492445 0.307087 0.320266
1 1 9.0 True iterative_deconvolve_3d 0.162104 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 41.336641 2.0 ... NaN 17.459039 12.091641 9.532431 0.1 0.687875 0.1 0.569173 0.503548 0.939216
2 2 2.0 True iterative_deconvolve_3d 0.162479 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 41.432197 2.0 ... NaN 15.873985 8.576436 15.295708 0.1 1.923576 0.1 0.096738 0.022226 0.689975
3 3 3.0 True iterative_deconvolve_3d 0.207363 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 52.877645 2.0 ... NaN 17.480459 12.056731 14.249562 0.1 1.611465 0.1 0.846776 0.013182 0.924882
4 4 1.0 True iterative_deconvolve_3d 0.140929 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 35.936900 2.0 ... NaN 15.464502 20.934921 16.307666 0.1 1.781606 0.1 0.800311 0.191939 0.875046
5 5 0.0 True iterative_deconvolve_3d 0.118418 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 30.196591 2.0 ... NaN 16.609939 20.644649 13.984606 0.1 0.527417 0.1 0.109264 0.958234 0.521603
6 6 4.0 True iterative_deconvolve_3d 0.165482 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 42.197909 2.0 ... NaN 19.206903 16.233450 16.985679 0.1 2.347987 0.1 0.486737 0.628141 0.385925
7 7 5.0 True iterative_deconvolve_3d 0.133698 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 34.092970 2.0 ... NaN 13.345931 11.945929 15.904296 0.1 0.833224 0.1 0.529087 0.193788 0.642546
8 8 7.0 True iterative_deconvolve_3d 0.140780 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 35.899025 2.0 ... NaN 16.899596 11.008707 19.428881 0.1 0.154044 0.1 0.223912 0.258616 0.762918
9 9 6.0 True iterative_deconvolve_3d 0.101072 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.0 1.4 25.773247 2.0 ... NaN 14.284761 22.099231 14.454698 0.1 2.570631 0.1 0.388103 0.320851 0.824618

10 rows × 33 columns

Here, the NRMSE values are provided per cell. To obtain average performance, we need to average among all deconvolved cells.

First, let's extract the algorithm name and the settings from the file name:

In [97]:
for i in range(len(accuracy)):
        setting = accuracy.iloc[i]['Name'].split('/')[-3]
        accuracy.at[i, 'Settings'] = setting

Then, we group by the extracted settings, compute the mean value and sort by NRMSE:

In [98]:
accuracy_summary = accuracy.groupby(['Settings']).mean().reset_index().sort_values('NRMSE')
accuracy_summary[:10]
Out[98]:
Settings Unnamed: 0 CellID Convolved NRMSE PSF aspect ratio PSF sigma xy um RMSE SNR Voxel size x ... regularization_lambda size_x size_y size_z terminate theta wiener x y z
16 iterative_deconvolve_3d_detect=False_low=2.0_n... 114.5 4.5 True 0.136130 4.0 1.4 34.713027 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 1.0 0.454254 0.339761 0.6887
17 iterative_deconvolve_3d_detect=False_low=2.0_n... 204.5 4.5 True 0.136234 4.0 1.4 34.739742 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 10.0 0.454254 0.339761 0.6887
15 iterative_deconvolve_3d_detect=False_low=2.0_n... 184.5 4.5 True 0.141038 4.0 1.4 35.964780 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 0.1 0.454254 0.339761 0.6887
19 iterative_deconvolve_3d_detect=False_low=4.0_n... 154.5 4.5 True 0.141349 4.0 1.4 36.043997 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 0.1 0.454254 0.339761 0.6887
20 iterative_deconvolve_3d_detect=False_low=4.0_n... 84.5 4.5 True 0.143687 4.0 1.4 36.640206 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 1.0 0.454254 0.339761 0.6887
21 iterative_deconvolve_3d_detect=False_low=4.0_n... 144.5 4.5 True 0.146219 4.0 1.4 37.285888 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 10.0 0.454254 0.339761 0.6887
22 iterative_deconvolve_3d_detect=False_low=6.0_n... 24.5 4.5 True 0.147953 4.0 1.4 37.727964 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 0.0 0.454254 0.339761 0.6887
18 iterative_deconvolve_3d_detect=False_low=4.0_n... 164.5 4.5 True 0.149294 4.0 1.4 38.069977 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 0.0 0.454254 0.339761 0.6887
23 iterative_deconvolve_3d_detect=False_low=6.0_n... 4.5 4.5 True 0.150440 4.0 1.4 38.362105 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 0.1 0.454254 0.339761 0.6887
24 iterative_deconvolve_3d_detect=False_low=6.0_n... 224.5 4.5 True 0.155217 4.0 1.4 39.580272 2.0 0.45 ... NaN 16.544454 14.629361 14.828204 0.1 1.426964 1.0 0.454254 0.339761 0.6887

10 rows × 25 columns

The first line in the table now corresponds to the optimal deconvolution settings:

In [99]:
accuracy_summary.iloc[0]
Out[99]:
Settings                 iterative_deconvolve_3d_detect=False_low=2.0_n...
Unnamed: 0                                                           114.5
CellID                                                                 4.5
Convolved                                                             True
NRMSE                                                              0.13613
PSF aspect ratio                                                         4
PSF sigma xy um                                                        1.4
RMSE                                                                34.713
SNR                                                                      2
Voxel size x                                                          0.45
Voxel size y                                                          0.45
Voxel size z                                                             3
iterations                                                             NaN
low                                                                      2
phi                                                                2.76451
regularization_lambda                                                  NaN
size_x                                                             16.5445
size_y                                                             14.6294
size_z                                                             14.8282
terminate                                                              0.1
theta                                                              1.42696
wiener                                                                   1
x                                                                 0.454254
y                                                                 0.339761
z                                                                   0.6887
Name: 16, dtype: object

Thus, DAMAS was identified as the optimal algorithm.

For the following parameters, only one value was tested:

  • Normalize PSF: True
  • Detect divergence: False
  • Perform antiringing step: True
  • Terminate iteration: 0.1

But for the other two parameters, the following values were identified as optimal:

  • Wiener filter $\gamma$: 1
  • Low pass filter: 2

Deconvolving microscopy images with identified optimal settings

Using external deconvolution software

The real microscopy image can now be deconvolved with identified optimal settings. This can be done directly with the corresponding Fiji plugin by choosing the optimal parameter values. Make sure that the specified input and psf image have the same voxel size:

Using DeconvTest

Alternatively, the user can deconvolve images from within DeconvTest. For this, the images have to be stored in a subfolder that has the same name as the corresponding PSF image:

In [100]:
os.listdir('data/microscopy/real_data')
Out[100]:
['psf_measured.tif', 'psf_measured', 'psf_measured.csv']
In [101]:
os.listdir('data/microscopy/real_data/psf_measured')
Out[101]:
['MC.tif', 'MC.csv']

Image metadata must contain the voxel size values:

In [102]:
pd.read_csv('data/microscopy/real_data/psf_measured/MC.csv', sep='\t', index_col=0, header=None).squeeze()
Out[102]:
0
Voxel size z    3.00
Voxel size y    0.45
Voxel size x    0.45
Name: 1, dtype: float64
In [103]:
pd.read_csv('data/microscopy/real_data/psf_measured.csv', sep='\t', index_col=0, header=None).squeeze()
Out[103]:
0
Voxel size z       3
Voxel size y    0.45
Voxel size x    0.45
isPSF           True
Name: 1, dtype: object

Next, we need to specify a configuration file to run deconvolution and run the DeconvTest workflow with it:

In [104]:
config = pd.Series({'simulation_folder': 'data/microscopy/',
                    'simulation_steps': ['deconvolve'],
                    'inputfolder': 'real_data',
                    'deconvolve_results_folder': 'real_deconvolved',
                    'max_threads': 1,
                    'deconvolution_algorithm': ['iterative_deconvolve_3d'],
                    'iterative_deconvolve_3d_detect': 'FALSE',
                    'iterative_deconvolve_3d_low': 2,
                    'iterative_deconvolve_3d_normalize': 'TRUE',
                    'iterative_deconvolve_3d_perform': 'TRUE',
                    'iterative_deconvolve_3d_terminate': 0.1,
                    'iterative_deconvolve_3d_wiener': 1
                    })
config.to_csv('config_microscopy_data_deconvolve.csv', sep='\t', header=False)
config
Out[104]:
simulation_folder                             data/microscopy/
simulation_steps                                  [deconvolve]
inputfolder                                          real_data
deconvolve_results_folder                     real_deconvolved
max_threads                                                  1
deconvolution_algorithm              [iterative_deconvolve_3d]
iterative_deconvolve_3d_detect                           FALSE
iterative_deconvolve_3d_low                                  2
iterative_deconvolve_3d_normalize                         TRUE
iterative_deconvolve_3d_perform                           TRUE
iterative_deconvolve_3d_terminate                          0.1
iterative_deconvolve_3d_wiener                               1
dtype: object
In [ ]: