Spatial calibration

Prerequisites

Before starting this lesson, you should be familiar with:

Learning Objectives

After completing this lesson, learners should be able to:
  • Understand that a pixel index is related to a physical coordinate.

  • Understand that a spatial calibration allows for physical size measurements.

Motivation

We would like to relate the image dimensions to a physical size. The relation between pixels and physical size is referred to as spatial calibration. Image calibration is dictated by acquisition and detection parameters of a microscope, such as magnification, camera detector size, sampling, etc, and is usually stored within the so-called image metadata. Before performing quantitative measurements, e.g. volume, area, …, you should make sure that the spatial calibration has been set appropriately.

Concept map

graph TD Im("Image") -->|has many| P("Pixel/Voxel") Im --> |has| C("Calibration") P -->|has| Va("Value") P -->|has| I("Indices") P --> |has| RWC("Real world coordinate") C --> RWC I --> RWC



Figure


Spatial calibration and size measurements



Activities

Explore the spatial calibration of a 3D image


Show activity for:  

ImageJ GUI

  • Open image: xyz_8bit__mitotic_plate_calibrated.tif
  • Check the pixel sizes (calibration) of this image
  • Where to see the calibration?
    • Next to the pixel indices in the ImageJ menu bar
    • Properties of the image [Image > Properties] or [Shift-Ctrl-P]
  • How to check whether the calibration makes sense?
  • Appreciate that image calibration might be necessary, e.g.
    • 2D distance measurement between two pixels
      • One can use the Line tool
    • 3D distance measurement between two voxels
      • One cannot use the Line tool but needs to measure manually: sqrt( (x0-x1)^2 + (y0-y1)^2 + (z0-z1)^2 )
        • Note: It is critical to use the calibrated voxel positions and not the voxel indices in above formula!
  • Appreciate that image calibration can be confusing, e.g.
    • It is not consistently used in image filter parameter specification

skimage napari

#######################################################
## To follow along you need to complete the tool
## installation activity for skimage napari.
#######################################################

#%%
# Import python packages.
from OpenIJTIFF import open_ij_tiff, save_ij_tiff
import numpy as np
from napari.viewer import Viewer

#%% 
# download and read tif file
# load image from url
fpath = "https://github.com/NEUBIAS/training-resources/raw/master/image_data/xyz_8bit__mitotic_plate_calibrated.tif"
image, axes, voxel_size_input, units = open_ij_tiff(fpath)

# visualize metadata
print(axes)
print(voxel_size_input)
print(units)

# %%
# Create a napari_viewer and visualize image
napari_viewer = Viewer()
napari_viewer.add_image(image, scale=voxel_size_input)

# %% [markdown]
# **Napari GUI** using the orthogonal views button (change order of visible axis) \
# **Napari GUI** inspect the volume and observe that the voxel size makes sense.\
# **Napari GUI** use 3D viewer button to inspect data in 3D.

# %%
# update voxel size to some other values
# change voxel size and resave
voxel_size_output = voxel_size_input
voxel_size_output[0] = voxel_size_output[0]*2
print('Output voxel size:', voxel_size_output)

save_ij_tiff(
    'resaved_image.tif',
     image,
     axes,
     voxel_size_output,
     units
)

# %%
# Add an image with changed scale. 
# Visualize images side by side in Napari (Orthogonal views)
napari_viewer.add_image(image, scale=voxel_size_output, name = "rescaled")

# %% [markdown]
# **Napari GUI** use the `New points layer button` to create a new point layer and name it `points2D` (double click on the name to rename a layer) \
# **Napari GUI** use `Add points` to create 2 points in the 2D slice \
# **Napari GUI** do the same for points in 3D in a separate layer called `points3D`

#%%
# extract point coordinates
layer_names = [l.name for l in napari_viewer.layers]
points2d = napari_viewer.layers[layer_names.index('points2D')].data
points3d = napari_viewer.layers[layer_names.index('points3D')].data

# %%
# compute distance between 2D points in voxel indices
dist_2d_pxl = np.sqrt(((points2d[1]-points2d[0])**2).sum())
print('Distance in pixels:',dist_2d_pxl)

# %% 
# calibrate point position and compute distance in um 
points2d_cal = np.stack([p*voxel_size_input for p in points2d])
# compute distance between points in um using calibrated point positions, appreciate that these are different values!
dist_2d_cal = np.sqrt(((points2d_cal[1]-points2d_cal[0])**2).sum())
print('Distance in um:',dist_2d_cal)

# appreciate that, in this special case, one can use voxel-distance multiplied by XY pixel size:
print('Distance in um:',dist_2d_pxl * voxel_size_input[1])

# %%
# compute distance between 3D points in voxel indices
dist_3d_pxl = np.sqrt(((points3d[1]-points3d[0])**2).sum())
print('Distance in pixels:',dist_3d_pxl)
# Appreciate that this distance doesn't make any physical sense due to the anisotropy of the image!

# %%
# Instead, one should always measure distances between points using calibration!
points3d_cal = np.stack([p*voxel_size_input for p in points3d])

# %%
# compute distance between points in um using calibrated point positions, appreciate that in this case it is important to do the calibration!
dist_3d_cal = np.sqrt(((points3d_cal[1]-points3d_cal[0])**2).sum())
print('Distance in um:',dist_3d_cal)



Modify spatial calibration


Show activity for:  

ImageJ GUI

  1. [File > Open ] the image and then [Image > Properties …] or [Ctrl-Shift-P]. Pixel-height = pixel-width = 0.13 um.
  2. Open the 3D image and change the properties from the [Image > Properties …] gui and the unit.
  3. Maximal extension is ~ 19.2 um. Move to the middle of the nucleus (~ z-slice 3) and draw a line using the line-tool.






Assessment

Answer these questions

Solution

  • sqrt( (x0*dxy-x1*dxy)^2 + (y0*dxy-y1*dxy)^2 ) = sqrt( (x0-x1)^2 + (y0-y1)^2 ) * dxy = sqrt( (10-9)^2 + (10-21)^2 ) * 0.13 = 11.04536 * 0.13 micrometer = 1.435897 micrometer. The fact that one can separate out the isotropic calibration dxy in the formula allows one to perform measurements in pixel units and convert the results to calibrated units later, by means of multiplication with dxy.
  • sqrt( (x0*dx-x1*dx)^2 + (y0*dy-y1*dy)^2 + (z0*dz-z1*dz)^2 ) = sqrt( (10*0.13-9*0.13)^2 + (10*0.13-21*0.13)^2 + (0*1.0-3*1.0)^2 ) micrometer = 3.325928 micrometer. Unfortunately, in an anisotropic 3D image one cannot separate out a calibration factor from the formula, making life more difficult.
  • 10 * 0.13 micrometer * 0.13 micrometer = 10 * 0.0169 micrometer square = 0.169 micrometer square
  • 10 * 0.13 micrometer * 0.13 micrometer * 1.0 micrometer = 10 * 0.0169 micrometer cube = 0.169 micrometer cube. This shows that measuring volumes in 3D can be done first in voxel units, as the calibration factor can easily taken into account later (in contrast to the distance measurements). Thus, somewhat surprisingly, is in practice easier to measure volumes than distances in 3D.

Explanations

Isotropy

One speaks of isotropic sampling if the pixels have the same extent in all dimensions (2D or 3D).

While microscopy images typically are isotropic in 2D they are typically anisotropic in 3D with coarser sampling in the z-direction.

It is very convenient for image analysis if pixels are isotropic, thus one sometimes resamples the image during image analysis such that they become isotropic.




Follow-up material

Recommended follow-up modules:

Learn more: