30 Registration
30.1 Introduction
30.2 Spatial vs spatial (registration)
Spatial omic technologies provide capabilities to capture omic profiles of cells and spots over tissue sections as well as the morphological features of these tissues. Multiple data modalities and microscopy images could be generated with numerous spatial omic instruments where the data generated from these modalities may capture observations (cells, spots, images etc.) in different units and perspectives (i.e. coordinate systems). These could be datasets generated from the same section, adjacent sections or tissue sections with similar morphology and structure.
Image registration and alignment methods provide transformation functions to map spatial localization of observations from one coordinate system to the other. In this section, we cover R packages and Python modules that store, manipulate and transform the coordinate of such spatial data modalities.
30.3 Single-cell vs spatial (reconstruction)
Spatial registration methods use gene expression profiles for cell populations of interest from single-cell RNA sequencing data as a reference, and use these single-cell expression profiles to ‘spatially register’ the spatial coordinates of observations corresponding to these cell populations in spatial transcriptomics data.
30.3.1 Dependencies
We first start by loading Bioconductor/CRAN dependencies that will allow us to read and visualize microscopy images that are generated with or alongside imaging-based platforms.
We will employ the VoltRon package (Manukyan et al. 2023) on GitHub to align SpatialExperiment
object of the Xenium data with a post-Xenium H&E of the same tissue section. We will also need the RBioFormats package for reading multi-resolution ome.tiff files of both DAPI and H&E images. Both ome.tiff
files can be found at GSM7780153.
Code
if(!requireNamespace("VoltRon"))
BiocManager::install("BIMSBbinfo/VoltRon")
library(VoltRon)
library(RBioFormats)
30.3.2 Xenium vs Post-Xenium H&E Alignment
For this use case, we use the Janesick Breast Cancer replicate 1 sample generated by Xenium in situ platform, which is available from STexampleData.
Code
xen <- Janesick_breastCancer_Xenium_rep1()
xen$sample_id <- "JanesickBC_Xenium1"
xen
## class: SpatialExperiment
## dim: 313 167780
## metadata(0):
## assays(1): counts
## rownames(313): ABCC11 ACTA2 ... ZEB2 ZNF562
## rowData names(3): ID Symbol Type
## colnames(167780): 1 2 ... 167779 167780
## colData names(8): cell_id transcript_counts ... nucleus_area
## sample_id
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):
## spatialCoords names(2) : x_centroid y_centroid
## imgData names(1): sample_id
The SpatialExperiment
of the Xenium data does not include any images, to perform the spatial alignment via image registration. We add the DAPI stained image channel to the object. We use the morphology_mip.ome.tif
file provided in the standard Xenium output and use RBioFormats package to read a specific resolution from the image pyramid. We choose the lowest resolution image and define the parameter for scaling spatial coordinates of cells according to the selected resolution. See here for more information on image scale factors.
Code
resolution_level <- 7
scaleparam <- 0.2125*(2^(resolution_level-1))
ome.tiff.dapi <- read.image("morphology_mip.ome.tif", resolution=resolution_level)
Before adding the selected image to the SpatialExperiment
object, we normalize the contrast to 1 and then we save the image to a temporary .png
file.
Code
ome.tiff.dapi <- (ome.tiff.dapi/max(ome.tiff.dapi))
Dapi.image <- "Xenium_DAPI_res7.png"
EBImage::writeImage(ome.tiff.dapi, files=Dapi.image, type="png")
We can now add the DAPI channel to the SpatialExperiment
.
Code
## DataFrame with 1 row and 4 columns
## sample_id image_id data scaleFactor
## <character> <character> <list> <numeric>
## 1 JanesickBC_Xenium1 DAPI #### 0.0735294
We visualize the new DAPI image and the associated cells of the Xenium experiment using ggspavis package.
Code
# plot DAPI image
p <- plotVisium(xen, spots=FALSE, image_id="DAPI")
# overlay cells with image
img <- imgRaster(xen)
sf <- scaleFactors(xen)
xy <- spatialCoords(xen)*sf
xy[, 2] <- nrow(img)-xy[, 2]
p + geom_point(
aes(x_centroid, y_centroid), data.frame(xy),
shape=16, stroke=0, size=0.2, alpha=0.4,
col="red", inherit.aes=FALSE)
We will employ VoltRon to align the Xenium data with the post Xenium H&E images captured from the same tissue section following the Xenium in situ experiment. We define a VoltRon object from an H&E image using the importImageData
function but first select a resolution from the ome.tiff
file of the H&E.
Code
ome.tiff.he <- read.image("GSM7780153_Post-Xenium_HE_Rep1.ome.tif", resolution=7)
He.image <- "Xenium_H&E_res7.png"
EBImage::writeImage(ome.tiff.he, files=He.image, type="png")
Code
HE_image_vr <- importImageData(image=He.image)
Before executing the automated registration, we have to convert the SpatialExperiment
object of the Xenium data into a VoltRon object. The assay_type
argument requires you to define the spatial entity type of the SpatialExperiment
which is cell
for spatial datasets in single cell resolution.
Code
xen_vr <- as.VoltRon(xen, assay_type="cell", assay_name="Xenium")
The registerSpatialData
function performs the registration using a mini Shiny application which allows the user to manipulate images of both modalities. We first select the Automated
option for non-manual (hence automated) registration method. We choose Homography
method for registration which incorporates the SIFT method for detecting landmark points across two images which are paired by the FLANN method later (Lowe 2004).
Before executing alignment, we can use the interface to negate the DAPI image (this step is needed to transform the DAPI image to make it similar to the H&E image). Then we horizontally flip the DAPI image to slightly synchronize the perspective of two images before automated alignment with SIFT and FLANN. These prior transformations help finding better landmarks.
Code
xen_reg <- registerSpatialData(
reference_spatdata=HE_image_vr,
query_spatdata=xen_vr)
The returning value of the function will have a list that includes the query spatial data that is registered to the reference. We can also convert the registered Xenium data using as.SpatialExperiment
function available in VoltRon
.
Code
xen_reg <- as.SpatialExperiment(xen_reg$registered_spat[[2]])
Now the registered Xenium data and the post Xenium H&E image have identical coordinate system, hence we add the H&E image directly to the registered SpatialExperiment
object.
Code
## DataFrame with 1 row and 4 columns
## sample_id image_id data scaleFactor
## <character> <character> <list> <numeric>
## 1 JanesickBC_Xenium1 H&E #### 1
Again by using ggspavis, we visualize the registered Xenium cells on the H&E image.
Code
# plot H&E image
p <- plotVisium(xen_reg, spots=FALSE, image_ids="H&E")
# overlay cells with image
img <- imgRaster(xen_reg)
sf <- scaleFactors(xen_reg)
xy <- spatialCoords(xen_reg)*sf
xy[, 2] <- nrow(img)-xy[, 2]
p + geom_point(
aes(x_centroid, y_centroid), data.frame(xy),
shape=16, stroke=0, size=0.2, alpha=0.4,
col="red", inherit.aes=FALSE)
30.3.3 Spatial Alignment Algorithms
Alternatively, we can use other R/Python frameworks to automatically or manually align spatial omic datasets across tissue sections. Some of these methods could be used through R using packages such as reticulate and basilisk.
-
NiftyReg is available from the R wrapper package RNiftyReg (Clayden 2015), and can be combined with mmand for automated image registration using both rigid and non-rigid approaches. Users can extract and apply the resulting transformation matrix on spatial coordinates of a
SpatialExperiment
object. However, the automation does not work well on images with very different orientation, scale, and intensity (e.g., Xenium and Visium; see Chapter 32). Here is a demonstration with external data, before and after registration of the two slices:


STAlign is available as a Python module from GitHub (Clifton et al. 2023). The tool performs optimal transport across two sets of spatial coordinates with or without associated microscopy images.
SpatialData is maintained by the scverse consortium and available as a Python module from GitHub (Marconato et al. 2025). Combined with the napari platform, SpatialData allows users to manually select landmark points before performing rigid alignment between two objects. Here we show the registration of Visium onto Xenium in napari:
