Spatial transforms

Spatial transformations of data will become more and more important in the near future due to the fact that performing spatial analyses across any two sections of tissue from the same block will require that data to be spatially aligned into a common coordinate space. Minute differences during the sectioning process from the cutting motion to how long an FFPE section was floated can result in even neighboring sections being distorted when compared side-by-side.

These differences make it difficult to assemble multislice and/or cross platform multimodal datasets into a cohesive 3D volume. The solution for this is to perform registration across either the dataset images or expression information. Based on the registration results, both the raster images and vector feature and polygon information can be aligned into a continuous whole.

Ideally this registration will be a free deformation based on sets of control points or a deformation matrix, however affine transforms already provide a good approximation. In either case, the transform or deformation applied must work in the same way across both raster and vector information.

Giotto provides spatial classes and methods for easy manipulation of data with 2D affine transformations. These functionalities are all available from GiottoClass.

Spatial transforms:

We support simple transformations and more complex affine transformations which can be used to combine and encode more than one simple transform.

  • spatShift() - translations
  • spin() - rotations (degrees)
  • rescale() - scaling
  • flip() - flip vertical or horizontal across arbitrary lines
  • t() - transpose
  • shear() - shear transform
  • affine() - affine transform

Spatial utilities:

Helpful functions for use alongside these spatial transforms are ext() for finding the spatial bounding box of where your data object is, crop() for cutting out a spatial region of the data, and plot() for terra/base plots of the data.

  • ext() - spatial extent or bounding box
  • crop() - cut out a spatial region of the data
  • plot() - plot a spatial object

Spatial classes:

Giotto’s spatial subobjects respond to the above functions. The Giotto object itself can also be affine transformed.

  • spatLocsObj - xy centroids
  • spatialNetworkObj - spatial networks between centroids
  • giottoPoints - xy feature point detections
  • giottoPolygon - spatial polygons
  • giottoImage (mostly deprecated) - magick-based images
  • giottoLargeImage/giottoAffineImage - terra-based images
  • affine2d - affine matrix container
  • giotto - giotto analysis object
# load in data
library(Giotto)
## Loading required package: GiottoClass
## Giotto Suite 4.1.0
g <- GiottoData::loadGiottoMini("vizgen")
## 1. read Giotto object
## 2. read Giotto feature information
## 3. read Giotto spatial information
## 3.1 read Giotto spatial shape information
## 3.2 read Giotto spatial centroid information
## 3.3 read Giotto spatial overlap information
## 4. read Giotto image information
## a giotto python environment was found
## Using python path:
##  "/Users/george/Library/r-miniconda-arm64/envs/giotto_env/bin/pythonw"
activeSpatUnit(g) <- "aggregate"
gpoly <- getPolygonInfo(g, return_giottoPolygon = TRUE)
gimg <- getGiottoImage(g)

# examples of the simple transforms using giottoPolygon
plot(gpoly)

gpoly |> spatShift(dx = 1000) |> plot()

gpoly |> spin(45) |> plot()

gpoly |> rescale(10) |> plot()

gpoly |> flip(direction = "vertical") |> plot()

gpoly |> t() |> plot()

gpoly |> shear(fx = 0.5) |> plot()

Giotto also provides a utility affine2d class that can be created from any affine matrix. The affine2d can then be used to accumulate simple transforms that can be applied to spatial objects in a single step using affine()

# create affine2d
aff <- affine(diag(c(1,1)))
aff <- aff |> 
    spatShift(dx = 1000) |>
    spin(45) |>
    rescale(10) |>
    flip(direction = "vertical") |>
    t() |>
    shear(fx = 0.5)

gpoly |> affine(aff) |> plot()

Image transforms

Giotto uses giottoLargeImages as the core image class which is based on terra SpatRaster. Images are not loaded into memory when the object is generated and instead an amount of regular sampling appropriate to the zoom level requested is performed at time of plotting.

spatShift() and rescale() operations are supported by terra SpatRaster, and we inherit those functionalities. spin(), flip(), t(), shear(), affine() operations will coerce giottoLargeImage to giottoAffineImage, which is much the same, except it contains an affine2d object that tracks spatial manipulations performed, so that they can be applied through magick::image_distort() processing after sampled values are pulled into memory. giottoAffineImage also has alternative ext() and crop() methods so that those operations respect both the expected post-affine space and untransformed source image.

# affine transform of image info matches with polygon info
gimg |> affine(aff) |> plot()
gpoly |> affine(aff) |> plot(add = TRUE, border = "cyan", lwd = 0.3)

# affine of the giotto object
g |> affine(aff) |> 
    spatInSituPlotPoints(
        show_image = TRUE, 
        feats = list(rna = c("Adgrl1", "Gfap", "Ntrk3", "Slc17a7")), 
        polygon_color = "cyan", 
        point_size = 0.1,
        use_overlap = FALSE
    )
## Warning: spatial locations have been modified.
##  Relevant spatial networks may need to be
##  regenerated
## [polygon_feat_type] 'cell' not discovered in polygon names.
##  Defaulting to spat_unit.
## plot image layer done
## --| Plotting 12935 feature points
## plot feature points layer done
## plot polygon layer done

Currently giotto image objects are not fully compatible with .ome.tif objects. terra which relies on gdal drivers for image loading will find that the Gtiff driver opens some .ome.tif images, but fails when certain compressions (notably JP2000 as used by 10x for their single-channel stains) are used.

sessionInfo()
## R version 4.4.0 (2024-04-24)
## Platform: aarch64-apple-darwin20
## Running under: macOS Sonoma 14.4
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: America/New_York
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] Giotto_4.1.0      GiottoClass_0.3.2
## 
## loaded via a namespace (and not attached):
##  [1] tidyselect_1.2.1            viridisLite_0.4.2          
##  [3] farver_2.1.2                dplyr_1.1.4                
##  [5] GiottoVisuals_0.2.4         lazyeval_0.2.2             
##  [7] fastmap_1.2.0               SingleCellExperiment_1.26.0
##  [9] digest_0.6.35               lifecycle_1.0.4            
## [11] terra_1.7-71                magrittr_2.0.3             
## [13] dbscan_1.1-12               compiler_4.4.0             
## [15] rlang_1.1.4                 sass_0.4.9                 
## [17] tools_4.4.0                 igraph_2.0.3               
## [19] utf8_1.2.4                  yaml_2.3.8                 
## [21] data.table_1.15.4           knitr_1.47                 
## [23] labeling_0.4.3              S4Arrays_1.4.0             
## [25] htmlwidgets_1.6.4           sp_2.1-4                   
## [27] reticulate_1.36.1           DelayedArray_0.30.0        
## [29] plyr_1.8.9                  RColorBrewer_1.1-3         
## [31] abind_1.4-5                 withr_3.0.0                
## [33] purrr_1.0.2                 BiocGenerics_0.50.0        
## [35] grid_4.4.0                  stats4_4.4.0               
## [37] fansi_1.0.6                 colorspace_2.1-0           
## [39] ggplot2_3.5.1               scales_1.3.0               
## [41] gtools_3.9.5                SummarizedExperiment_1.34.0
## [43] cli_3.6.2                   rmarkdown_2.27             
## [45] crayon_1.5.2                generics_0.1.3             
## [47] rstudioapi_0.16.0           reshape2_1.4.4             
## [49] httr_1.4.7                  rjson_0.2.21               
## [51] cachem_1.1.0                stringr_1.5.1              
## [53] zlibbioc_1.50.0             parallel_4.4.0             
## [55] XVector_0.44.0              matrixStats_1.3.0          
## [57] vctrs_0.6.5                 Matrix_1.7-0               
## [59] GiottoData_0.2.14           jsonlite_1.8.8             
## [61] IRanges_2.38.0              S4Vectors_0.42.0           
## [63] ggrepel_0.9.5               scattermore_1.2            
## [65] magick_2.8.3                GiottoUtils_0.1.10         
## [67] plotly_4.10.4               tidyr_1.3.1                
## [69] jquerylib_0.1.4             glue_1.7.0                 
## [71] codetools_0.2-20            cowplot_1.1.3              
## [73] stringi_1.8.4               gtable_0.3.5               
## [75] GenomeInfoDb_1.40.0         deldir_2.0-4               
## [77] GenomicRanges_1.56.0        UCSC.utils_1.0.0           
## [79] munsell_0.5.1               tibble_3.2.1               
## [81] pillar_1.9.0                htmltools_0.5.8.1          
## [83] GenomeInfoDbData_1.2.12     R6_2.5.1                   
## [85] evaluate_0.24.0             lattice_0.22-6             
## [87] Biobase_2.64.0              highr_0.11                 
## [89] png_0.1-8                   backports_1.4.1            
## [91] SpatialExperiment_1.14.0    bslib_0.7.0                
## [93] Rcpp_1.0.12                 SparseArray_1.4.1          
## [95] checkmate_2.3.1             colorRamp2_0.1.0           
## [97] xfun_0.44                   MatrixGenerics_1.16.0      
## [99] pkgconfig_2.0.3