satijalab / seurat

R toolkit for single cell genomics
http://www.satijalab.org/seurat
Other
2.31k stars 919 forks source link

Rotate images for spatial analysis (previous workaround no longer runs) #9344

Open jgaunt264 opened 2 months ago

jgaunt264 commented 2 months ago

Hi, I want to rotate some spatial images and the corresponding data by 90 degrees. I was previously using the workaround in issue 2702 by @longmanz (based on code by @AmhedVargas and @JPingLin), reproduced below. However, this code no longer works in Seurat v5.1.0 with error message: Error in rotateSeuratImage(seu.obj, rotation = "L90", slide = "Sample_1") : no slot of name "coordinates" for this object of class "VisiumV2"

I've checked each of the attributes in the new object and can't find where the coordinates are now stored. I also note that in a Seurat object generated with a previous version (when the rotate script worked), GetTissueCoordinates() produces an entirely different output than seu.obj@images)[["Sample_1"]]@coordinates.

Can anyone help to update the rotation script?

Many thanks

library(Seurat)
library(ggplot2)
library(patchwork)
library(dplyr)

rotimat=function(foo,rotation){
    if(!is.matrix(foo)){
        cat("Input is not a matrix")
        return(foo)
    }
    if(!(rotation %in% c("180","Hf","Vf", "R90", "L90"))){
        cat("Rotation should be either L90, R90, 180, Hf or Vf\n")
        return(foo)
    }
    if(rotation == "180"){
        foo <- foo %>% 
            .[, dim(.)[2]:1] %>%
            .[dim(.)[1]:1, ]
    }
    if(rotation == "Hf"){
        foo <- foo %>%
            .[, dim(.)[2]:1]
    }

    if(rotation == "Vf"){
        foo <- foo %>%
            .[dim(.)[1]:1, ]
    }
    if(rotation == "L90"){
        foo = t(foo)
        foo <- foo %>%
            .[dim(.)[1]:1, ]
    }
    if(rotation == "R90"){
        foo = t(foo)
        foo <- foo %>%
            .[, dim(.)[2]:1]
    }
    return(foo)
}

rotateSeuratImage = function(seuratVisumObject, slide = "slice1", rotation="Vf"){
    if(!(rotation %in% c("180","Hf","Vf", "L90", "R90"))){
        cat("Rotation should be either 180, L90, R90, Hf or Vf\n")
        return(NULL)
    }else{
        seurat.visium = seuratVisumObject
        ori.array = (seurat.visium@images)[[slide]]@image
        img.dim = dim(ori.array)[1:2]/(seurat.visium@images)[[slide]]@scale.factors$lowres
        new.mx <- c()  
        # transform the image array
        for (rgb_idx in 1:3){
            each.mx <- ori.array[,,rgb_idx]
            each.mx.trans <- rotimat(each.mx, rotation)
            new.mx <- c(new.mx, list(each.mx.trans))
        }

        # construct new rgb image array
        new.X.dim <- dim(each.mx.trans)[1]
        new.Y.dim <- dim(each.mx.trans)[2]
        new.array <- array(c(new.mx[[1]],
                             new.mx[[2]],
                             new.mx[[3]]), 
                           dim = c(new.X.dim, new.Y.dim, 3))

        #swap old image with new image
        seurat.visium@images[[slide]]@image <- new.array

        ## step4: change the tissue pixel-spot index
        img.index <- (seurat.visium@images)[[slide]]@coordinates

        #swap index
        if(rotation == "Hf"){
            seurat.visium@images[[slide]]@coordinates$imagecol <- img.dim[2]-img.index$imagecol
        }

        if(rotation == "Vf"){
            seurat.visium@images[[slide]]@coordinates$imagerow <- img.dim[1]-img.index$imagerow
        }

        if(rotation == "180"){
            seurat.visium@images[[slide]]@coordinates$imagerow <- img.dim[1]-img.index$imagerow
            seurat.visium@images[[slide]]@coordinates$imagecol <- img.dim[2]-img.index$imagecol
        }

        if(rotation == "L90"){
            seurat.visium@images[[slide]]@coordinates$imagerow <- img.dim[2]-img.index$imagecol
            seurat.visium@images[[slide]]@coordinates$imagecol <- img.index$imagerow
        }

        if(rotation == "R90"){
            seurat.visium@images[[slide]]@coordinates$imagerow <- img.index$imagecol
            seurat.visium@images[[slide]]@coordinates$imagecol <- img.dim[1]-img.index$imagerow
        }

        return(seurat.visium)
    }  
}

seu.obj <- rotateSeuratImage(seu.obj, rotation = "L90", slide = "Sample_1")

sessionInfo()
R version 4.4.1 (2024-06-14)
Platform: aarch64-apple-darwin20
Running under: macOS Monterey 12.5.1

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.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] dplyr_1.1.4        patchwork_1.2.0    ggplot2_3.5.1      Seurat_5.1.0       SeuratObject_5.0.2
[6] sp_2.1-4          

loaded via a namespace (and not attached):
  [1] deldir_2.0-4           pbapply_1.7-2          gridExtra_2.3          rlang_1.1.4           
  [5] magrittr_2.0.3         RcppAnnoy_0.0.22       matrixStats_1.3.0      ggridges_0.5.6        
  [9] compiler_4.4.1         spatstat.geom_3.3-2    png_0.1-8              vctrs_0.6.5           
 [13] reshape2_1.4.4         stringr_1.5.1          pkgconfig_2.0.3        fastmap_1.2.0         
 [17] utf8_1.2.4             promises_1.3.0         purrr_1.0.2            jsonlite_1.8.8        
 [21] goftest_1.2-3          later_1.3.2            spatstat.utils_3.0-5   irlba_2.3.5.1         
 [25] parallel_4.4.1         cluster_2.1.6          R6_2.5.1               ica_1.0-3             
 [29] stringi_1.8.4          RColorBrewer_1.1-3     spatstat.data_3.1-2    reticulate_1.38.0     
 [33] parallelly_1.37.1      spatstat.univar_3.0-0  lmtest_0.9-40          scattermore_1.2       
 [37] Rcpp_1.0.13            tensor_1.5             future.apply_1.11.2    zoo_1.8-12            
 [41] sctransform_0.4.1      httpuv_1.6.15          Matrix_1.7-0           splines_4.4.1         
 [45] igraph_2.0.3           tidyselect_1.2.1       rstudioapi_0.16.0      abind_1.4-5           
 [49] spatstat.random_3.3-1  codetools_0.2-20       miniUI_0.1.1.1         spatstat.explore_3.3-1
 [53] listenv_0.9.1          lattice_0.22-6         tibble_3.2.1           plyr_1.8.9            
 [57] withr_3.0.0            shiny_1.8.1.1          ROCR_1.0-11            Rtsne_0.17            
 [61] future_1.33.2          fastDummies_1.7.3      survival_3.6-4         polyclip_1.10-7       
 [65] fitdistrplus_1.2-1     pillar_1.9.0           KernSmooth_2.23-24     plotly_4.10.4         
 [69] generics_0.1.3         RcppHNSW_0.6.0         munsell_0.5.1          scales_1.3.0          
 [73] globals_0.16.3         xtable_1.8-4           glue_1.7.0             lazyeval_0.2.2        
 [77] tools_4.4.1            data.table_1.15.4      RSpectra_0.16-2        RANN_2.6.1            
 [81] leiden_0.4.3.1         dotCall64_1.1-1        cowplot_1.1.3          grid_4.4.1            
 [85] tidyr_1.3.1            colorspace_2.1-0       nlme_3.1-164           cli_3.6.3             
 [89] spatstat.sparse_3.1-0  spam_2.10-0            fansi_1.0.6            viridisLite_0.4.2     
 [93] uwot_0.2.2             gtable_0.3.5           digest_0.6.36          progressr_0.14.0      
 [97] ggrepel_0.9.5          htmlwidgets_1.6.4      htmltools_0.5.8.1      lifecycle_1.0.4       
[101] httr_1.4.7             mime_0.12              MASS_7.3-60.2         
nina-hahn commented 1 month ago

Unfortunately I am facing the same problem.

Best