Tutorial on linear filters

from timagetk.algorithms.linearfilter import linearfilter
from timagetk.third_party.vt_parser import FILTERING_METHODS
from timagetk.io import imread
from timagetk.io.util import shared_data
from timagetk.visu.mplt import grayscale_imshow
/builds/J-gEBwyb/0/mosaic/timagetk/src/timagetk/components/labelled_image.py:31: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from tqdm.autonotebook import tqdm

Linear filters are applied on intensity images (grayscale).

image_path = shared_data('sphere_membrane_0.0.inr.gz', 'sphere')
img = imread(image_path)
middle_z_slice = img.get_shape('z') // 2

List of filtering methods

print(FILTERING_METHODS)
['smoothing', 'gradient', 'hessian', 'laplacian', 'zero-crossings-hessian', 'zero-crossings-laplacian', 'gradient-hessian', 'gradient-laplacian', 'gradient-extrema']

Gaussian smoothing

Gaussian smoothing is often used to reduce noise and sharp local variations in intensity images.

fimg = linearfilter(img, method='smoothing', sigma=1.0, real=True)

We advise to use real=True to avoid applying anisotropic filtering on anisotropic images.

The ‘intensity’ of the smoothing is controlled by the parameter sigma.

fimg2 = linearfilter(img, method='smoothing', sigma=2.0, real=True)
fimg4 = linearfilter(img, method='smoothing', sigma=4.0, real=True)
img_titles = ['Original', f'Sigma=1.0', f'Sigma=2.0', f'Sigma=4.0']
fig, axes = grayscale_imshow([img, fimg, fimg2, fimg4], slice_id=middle_z_slice, title=img_titles, max_per_line=2, figsize=(8, 8), dpi=96)
  0%|          | 0/4 [00:00<?, ?image/s]
 25%|██▌       | 1/4 [00:00<00:01,  2.14image/s]
 50%|█████     | 2/4 [00:00<00:00,  2.01image/s]
 75%|███████▌  | 3/4 [00:01<00:00,  2.08image/s]
100%|██████████| 4/4 [00:01<00:00,  2.08image/s]
100%|██████████| 4/4 [00:01<00:00,  2.07image/s]

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[6], line 2
      1 img_titles = ['Original', f'Sigma=1.0', f'Sigma=2.0', f'Sigma=4.0']
----> 2 fig, axes = grayscale_imshow([img, fimg, fimg2, fimg4], slice_id=middle_z_slice, title=img_titles, max_per_line=2, figsize=(8, 8), dpi=96)

File /builds/J-gEBwyb/0/mosaic/timagetk/src/timagetk/visu/mplt.py:447, in grayscale_imshow(image, slice_id, axis, val_range, cmap, **kwargs)
    445     # Update kwargs to pass them to ``_multiple_plots``
    446     kwargs.update({'val_range': val_range, 'cmap': cmap, 'title': titles})
--> 447     return _multiple_plots(grayscale_imshow, image, **kwargs)
    448 else:
    449     image, title_suffix = _to_2d(image, slice_id)

File /builds/J-gEBwyb/0/mosaic/timagetk/src/timagetk/visu/mplt.py:1732, in _multiple_plots(plotting_func, image, **kwargs)
   1729 else:
   1730     thumb_height = thumb_size
-> 1732 fig, axes = plt.subplots(n_rows, n_columns, gridspec_kw={'width_ratios': width_ratios})
   1733 if n_rows == 1 or n_columns == 1:
   1734     axes = axes.reshape(n_rows, n_columns)

File /opt/conda/envs/timagetk/lib/python3.9/site-packages/matplotlib/pyplot.py:1614, in subplots(nrows, ncols, sharex, sharey, squeeze, width_ratios, height_ratios, subplot_kw, gridspec_kw, **fig_kw)
   1470 """
   1471 Create a figure and a set of subplots.
   1472 
   (...)
   1611 
   1612 """
   1613 fig = figure(**fig_kw)
-> 1614 axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey,
   1615                    squeeze=squeeze, subplot_kw=subplot_kw,
   1616                    gridspec_kw=gridspec_kw, height_ratios=height_ratios,
   1617                    width_ratios=width_ratios)
   1618 return fig, axs

File /opt/conda/envs/timagetk/lib/python3.9/site-packages/matplotlib/figure.py:930, in FigureBase.subplots(self, nrows, ncols, sharex, sharey, squeeze, width_ratios, height_ratios, subplot_kw, gridspec_kw)
    926         raise ValueError("'width_ratios' must not be defined both as "
    927                          "parameter and as key in 'gridspec_kw'")
    928     gridspec_kw['width_ratios'] = width_ratios
--> 930 gs = self.add_gridspec(nrows, ncols, figure=self, **gridspec_kw)
    931 axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze,
    932                   subplot_kw=subplot_kw)
    933 return axs

File /opt/conda/envs/timagetk/lib/python3.9/site-packages/matplotlib/figure.py:1542, in FigureBase.add_gridspec(self, nrows, ncols, **kwargs)
   1503 """
   1504 Return a `.GridSpec` that has this figure as a parent.  This allows
   1505 complex layout of Axes in the figure.
   (...)
   1538 
   1539 """
   1541 _ = kwargs.pop('figure', None)  # pop in case user has added this...
-> 1542 gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, **kwargs)
   1543 return gs

File /opt/conda/envs/timagetk/lib/python3.9/site-packages/matplotlib/gridspec.py:378, in GridSpec.__init__(self, nrows, ncols, figure, left, bottom, right, top, wspace, hspace, width_ratios, height_ratios)
    375 self.hspace = hspace
    376 self.figure = figure
--> 378 super().__init__(nrows, ncols,
    379                  width_ratios=width_ratios,
    380                  height_ratios=height_ratios)

File /opt/conda/envs/timagetk/lib/python3.9/site-packages/matplotlib/gridspec.py:55, in GridSpecBase.__init__(self, nrows, ncols, height_ratios, width_ratios)
     53 self._nrows, self._ncols = nrows, ncols
     54 self.set_height_ratios(height_ratios)
---> 55 self.set_width_ratios(width_ratios)

File /opt/conda/envs/timagetk/lib/python3.9/site-packages/matplotlib/gridspec.py:110, in GridSpecBase.set_width_ratios(self, width_ratios)
    108     width_ratios = [1] * self._ncols
    109 elif len(width_ratios) != self._ncols:
--> 110     raise ValueError('Expected the given number of width ratios to '
    111                      'match the number of columns of the grid')
    112 self._col_width_ratios = width_ratios

ValueError: Expected the given number of width ratios to match the number of columns of the grid
<Figure size 640x480 with 0 Axes>

Note that it can decrease the contrast!

Gradient filtering

An image gradient is a directional change in the intensity or color in an image. https://en.wikipedia.org/wiki/Image_gradient

fimg = linearfilter(img, method='gradient')
img_titles = ['Original', f'Gradient']
fig, axes = grayscale_imshow([img, fimg], slice_id=middle_z_slice, title=img_titles, figsize=(8, 8), dpi=96)

Hessian filtering

In mathematics, the Hessian matrix or Hessian is a square matrix of second-order partial derivatives of a scalar-valued function, or scalar field. https://en.wikipedia.org/wiki/Hessian_matrix

fimg = linearfilter(img, method='hessian')
img_titles = ['Original', f'Hessian']
fig, axes = grayscale_imshow([img, fimg], slice_id=middle_z_slice, title=img_titles, figsize=(8, 8), dpi=96)

Laplacian filtering

Discrete Laplace operator is often used in image processing e.g. in edge detection and motion estimation applications. https://en.wikipedia.org/wiki/Discrete_Laplace_operator#Image_Processing

fimg = linearfilter(img, method='laplacian')
img_titles = ['Original', f'Laplacian']
fig, axes = grayscale_imshow([img, fimg], slice_id=middle_z_slice, title=img_titles, figsize=(8, 8), dpi=96)

Other gradient filters

gfilters = ['gradient-hessian', 'gradient-laplacian', 'gradient-extrema']
fimg = [linearfilter(img, method=fmeth) for fmeth in gfilters]
img_titles = ['Original'] + gfilters
fig, axes = grayscale_imshow([img]+fimg, slice_id=middle_z_slice, title=img_titles, max_per_line=2, figsize=(8, 8), dpi=96)

Zero-crossing filters

zfilters = ['zero-crossings-hessian', 'zero-crossings-laplacian']
fimg = [linearfilter(img, method=fmeth) for fmeth in zfilters]
img_titles = ['Original'] + zfilters
fig, axes = grayscale_imshow([img]+fimg, slice_id=middle_z_slice, title=img_titles, max_per_line=3, figsize=(8, 8), dpi=96)