时空云平台(STOmics Cloud)是以时空为特色的多组学数据分析平台,可以管理和分析多组学数据。 STOmics Cloud以项目为核心,用户可快速将数据和工具整合到项目,实现分析过程可追溯,结果可复现,知识可分享,项目可协作,形成项目分析体系。并通过一个用户友好的门户,提供灵活易于使用的无代码标准和高级分析,高分辨率可视化分析,以及个性化的分析服务,任何注册登录的用户可以轻松在平台上访问,分析,使用和共享数据和工具。

总体来说,STOmics 平台是一个很出色的多组学的数据分析平台,但是仍然有一些不足,STOmics 在做数据分析的时候无法连接外部网络,如果说我们需要用到一些外部的模型或是其他资源的时候,则是会遇到十分大的阻碍,尤其是当我们调用 keras 的某些模型的时候,是需要进行联网下载的,那我们就需要对源码进行一些修改。

Spateo 利用 Stereo-seq 的超高空间分辨率、大视野和高 RNA 捕获灵敏度,通过核染色和基于 RNA 信号的细胞分割实现单细胞分辨率空间转录组学。Spateo 还提供了空间约束聚类的新方法,以识别连续的组织域,空间感知差异分析以揭示空间基因表达热点和模块,以及复杂的配体-受体相互作用。

首先我们需要在 STOmics 上构建镜像,通过测试,以下镜像构建代码是有较为不错的效果,基础镜像选择为Python 3 and R image,也可以采用已经构建好的 spateo_zt 镜像。镜像使用需要在自己的工作目录创建/data/work/.keras/models/data/work/.cellpose/models文件夹。

stardist需要下载 python_2D_versatile_fluo.zip 并且改名为 2D_versatile_fluo.zip,放置于 /data/work/.keras/models/StarDist2D/2D_versatile_fluo/2D_versatile_fluo.zip。cellpose需要下载 nucleitorch_0size_nucleitorch_0.npy 放置于 /data/work/.cellpose/models/nucleitorch_0/data/work/.cellpose/models/size_nucleitorch_0.npy。deepcell需要下载 NuclearSegmentation-75.tar.gz 并且改名为 NuclearSegmentation.tgz,放置于 /data/work/.keras/models/NuclearSegmentation.tgz 之后便可进行正常的圈细胞。

/opt/software/python/bin/python3.8 -m pip install --upgrade pip
pip install scanpy
pip install spateo-release
pip install tensorflow==2.12.0
pip install stardist
pip install cellpose
pip install DeepCell
sed -i '214a \ \ \ \ cache_dir="/data/work/.keras"' /opt/software/python/lib/python3.8/site-packages/keras/utils/data_utils.py
sed -i '221a \ \ \ \ datadir_base="/data/work/.keras"' /opt/software/python/lib/python3.8/site-packages/keras/utils/data_utils.py
sed -i '17a MODEL_DIR = pathlib.Path("/data/work/.cellpose/models")' /opt/software/python/lib/python3.8/site-packages/cellpose/models.py

构建镜像完成之后便是可以使用空间组芯片数据gem和空间组拍照数据tif进行圈细胞。我写的代码如下所示

def cellbin_spateo(gem_path, ssDNA_path, save_path):
    
    adata = st.io.read_bgi_agg(
        gem_path, ssDNA_path,
    )
    
    name = gem_path.split('/')[-1].split('.')[0]
    
    ## stain mask
    st.cs.mask_nuclei_from_stain(adata)
    st.pl.imshow(adata, 'stain_mask', save_show_or_return = 'save', save_kwargs={'path':save_path + '01.stain_mask_' + name})
    
    ## watershed_labels
    st.cs.find_peaks_from_mask(adata, 'stain', 7)
    st.cs.watershed(adata, 'stain', 5, out_layer='watershed_labels')
    fig, ax = st.pl.imshow(adata, 'stain', save_show_or_return='return')
    st.pl.imshow(adata, 'watershed_labels', labels=True, alpha=0.5, ax=ax, save_show_or_return='save', save_kwargs={'path':save_path + '02.watershed_labels_' + name})
    
    ## stardist_labels
    st.cs.stardist(adata, tilesize=-1, equalize=2.0, out_layer='stardist_labels')
    fig, ax = st.pl.imshow(adata, 'stain', save_show_or_return='return')
    st.pl.imshow(adata, 'stardist_labels', labels=True, alpha=0.5, ax=ax, save_show_or_return='save', save_kwargs={'path':save_path + '03.stardist_labels_' + name})
    
    ## augmented_labels
    st.cs.augment_labels(adata, 'watershed_labels', 'stardist_labels', out_layer='augmented_labels')
    fig, ax = st.pl.imshow(adata, 'stain', save_show_or_return='return')
    st.pl.imshow(adata, 'augmented_labels', labels=True, alpha=0.5, ax=ax, save_show_or_return='save', save_kwargs={'path':save_path + '04.augmented_labels_' + name})
    
    ##
    st.cs.mask_cells_from_stain(adata, out_layer='stain_cell_mask')
    st.cs.watershed(
        adata, 'stain',
        mask_layer='stain_cell_mask',
        markers_layer='augmented_labels',
        out_layer='cell_labels',
    )
    
    ## 
    fig, ax = st.pl.imshow(adata, 'stain', save_show_or_return='return')
    st.pl.imshow(adata, 'cell_labels', labels=True, alpha=0.5, ax=ax, save_show_or_return='save', save_kwargs={'path':save_path + '05.cell_labels_' + name})
    
    ## expand_labels
    st.cs.expand_labels(adata, 'augmented_labels', distance=20, max_area=1600)
    fig, ax = st.pl.imshow(adata, 'stain', save_show_or_return='return')
    st.pl.imshow(adata, 'augmented_labels_expanded', labels=True, alpha=0.5, ax=ax, save_show_or_return='save', save_kwargs={'path':save_path + '06.augmented_labels_expanded_' + name})
    
    ## label adata
    adata.write(save_path + '07.labeled_adata_' + name + '.h5ad')
    
    ## final adata
    cell_adata = st.io.read_bgi(
        gem_path,
        segmentation_adata=adata,
        labels_layer='augmented_labels_expanded',
    )
    cell_adata.write(save_path + '08.final_adata_' + name + '.h5ad')
    return 0

使用上述代码则可以正常在 STOmics 平台上进行圈细胞。获得单细胞分辨率的空间组的数据。需要注意的是我们的拍照的 tif 数据是需要和 gem 数据的空间位置对应上的。那么应该如何对应呢?这里使用师兄 liaokuo 的代码,生成由 gem 文件生成的灰度图,将灰度图放置于 PhotoShop 中作为底图,然后将 tif 图与灰度图手动配准(因为自动配准效果不好),导出以 灰度图大小画布为底的、调整过的 tif 图片,然后将此图片拿去与gem文件做圈细胞。

# author: liaokuo
# time: 2022/4/1

# this is to generate grey graphic from spatial RNA.
# create_greyplot.py input_file_path out_put_directory

import sys
import os
inpath=sys.argv[1]
outpath=sys.argv[2]
os.chdir(outpath) # change the working directory
file=inpath.rsplit("/",1)[1]
name=file.split(".")[0]

import pandas as pd
import numpy as np

def mRNA_image_load(path, gray_factor=25):
    cbs = pd.read_csv(path, sep='\t',comment='#') # therefore, the input file must be a clear txt or csv file.
    cbs['coor'] = cbs['x'].astype(str) + '_' + cbs['y'].astype(str)
    cbs2 = cbs[['coor', 'UMICount']] if 'UMICount' in cbs.columns else cbs[['coor', 'MIDCount']] # there must be careful.
    cbs2 = cbs2['coor'].value_counts().reset_index()
    cbs2['x'] = cbs2['index'].str.split('_', expand=True)[0].astype(int)
    cbs2['y'] = cbs2['index'].str.split('_', expand=True)[1].astype(int)
    #cbs2['x'] -= cbs2['x'].min()
    #cbs2['y'] -= cbs2['y'].min()
    h = cbs2['x'].max()
    w = cbs2['y'].max()
    mtx = cbs2.pivot_table(values='coor', index='x', columns='y', aggfunc='sum')
    for i in range(h):
        if i not in mtx.index:
            mtx.loc[i] = 0
    for j in range(w):
        if j not in mtx.columns:
            mtx.loc[:, j] = 0
    mtx = mtx.fillna(0).sort_index().sort_index(axis=1)
    mtx = mtx * gray_factor
    mtx[mtx>255] = 255
    mtx = mtx.astype(np.uint8).values
    return mtx




# get the grey scale matrix
mtx=mRNA_image_load(path=inpath)
data=pd.DataFrame(mtx)
data.to_csv(name+"_grey.csv")


# draw grep scale plot in format of png
from PIL import Image
im = Image.fromarray(np.uint8(data))# image object should be an array in 0-255
im = im.convert('L')# L means grey scale, while RGB means color scale
im.save(name+"_grey.png")