Visium HD数据分析之Bin2Cell |生信开发实战

企业   2024-12-13 14:43   浙江  


文献名:Bin2cell reconstructs cells from high resolution Visium HD data


期刊:Bioinformatics


IF:5.874


发表日期:5 September 2024


10X Genomics 的 Visium HD 是首个能够以亚细胞分辨率从FFPE 样本切片中捕获全尺寸转录组数据与参考形态图像的商用平台。然而,在数据中进行单个细胞的研究具有一定的挑战。如果基于官方指定的8 um bin进行下游分析,可能会存在一些问题,诸如:大细胞被分割 ;下游分析受bin的内容限制;多个小细胞被包含在一个bin中 ;纳入非细胞组织成分等问题。这将大大影响细胞类型推断和细胞间通讯研究的能力。为了解决上述存在的问题,今天来介绍一款可以对HD数据进行细胞分割的软件Bin2Cell。    

Bin2Cell 是Krzysztof Polański等人开发的利用形态学、图像分割和基因表达信息(2μm bins)对Visium HD数据进行单细胞层面上的研究的工具,可兼容已建立的 Python 单细胞和空间转录组学软件,无需 GPU,即可在几分钟内高效运行。

本文使用了10x官方的小鼠大脑demo数据(https://www.10xgenomics.com/datasets/visium-hd-cytassist-gene-expression-libraries-of-mouse-brain-he)进行细胞分割、细胞类型注释。下面让我们来看一下Bin2Cell的具体操作方法。 

          

 

操作方法







一、联合HE图片和转录本定量进行细胞分割






https://nbviewer.org/github/Teichlab/bin2cell/blob/main/notebooks/demo.ipynb

Bin2Cell以0.5 微米每像素(mpp)的分辨率渲染图像,使用StarDist进行H&E 染色图像中细胞核的分割( prob_thresh=0.01),使用StarDist荧光染色图像细胞分割模型进行基因表达高斯模糊图像的细胞分割(prob_thresh=0。05; nms_thresh=0.5)。

来自小鼠大脑的 8 μm bin 结果中包含了 393,543 个 bins。相比之下,以下Bin2Cell 流程进行细胞分割找到了 69,902 个细胞,这个数字更接近于预期的小鼠大脑冠状半球在 5 μm 切片上的细胞数量。    

       Bin2cell segmentation, label expansion and harmonisation          

1. 导入模块和地址设置

Python                  # In[1]: 导入模块                                   import matplotlib.pyplot as plt                  import scanpy as sc                  import numpy as np                  import os                  import bin2cell as b2c                                   # In[2]:地址设置                                   os.chdir("../2_bin2cell/Demo2/Output")                  os.makedirs("stardist", exist_ok=True)                  path = "../Input/binned_outputs/square_002um/"                  source_image_path = "../Input/Visium_HD_Mouse_Brain_tissue_image.tif"                  spaceranger_image_path = "../Input/spatial"      


2. 导入数据

Python# In[3]:导入HD表达数据(2um)和高清HE图片
adata = b2c.read_visium(path, source_image_path = source_image_path, spaceranger_image_path = spaceranger_image_path )adata.var_names_make_unique()adata
# In[4]:过滤低质量bin
sc.pp.filter_genes(adata, min_cells=3)sc.pp.filter_cells(adata, min_counts=1)adata
# In[5]:基于高清图像保存低分辨率图像并存入adata.obsm['spatial_cropped']
mpp = 0.5 # 每像素微米数
b2c.scaled_he_image(adata, mpp=mpp, save_path="stardist/he.tiff") adata      


3. 校正bin的行列偏移    

Visium HD的bin的X或Y位置其实是存在一定变异性的,当我们检查其表达空间图时,可以发现一种特征性的条纹状外观,比如某几行的转录本明显少于其它行。使用每行的bin的总count数的99%分位数进行行标准化,同样的方法进行列标准化,之后使用校正因子进行全局校正,使用缩放因子进行重缩放得到新的计数矩阵。这样之后可以校正Visium HD数据的不均匀性。

Python                  # In[6]: 校正bin的行列偏移                                   b2c.destripe(adata)                  adata                                   # 取子图像进行可视化                  mask = ((adata.obs['array_row'] >= 1450) &                          (adata.obs['array_row'] <= 1550) &                          (adata.obs['array_col'] >= 250) &                          (adata.obs['array_col'] <= 450)                         )                                           # In[7]: Picture1 使用adata.obsm['spatial_cropped']进行可视化                                   bdata = adata[mask]                  sc.pl.spatial(bdata, color=[None, "n_counts", "n_counts_adjusted"], img_key="0.5_mpp", basis="spatial_cropped")                  plt.savefig('destripe_mask.png', dpi=300, bbox_inches='tight')                  plt.close()                                   # In[8]: Picture2 使用默认图片进行可视化                                   sc.pl.spatial(bdata, color=[None, "n_counts", "n_counts_adjusted"])                  plt.savefig('destripe_mask_default.png', dpi=300, bbox_inches='tight')                  plt.close()




   destripe_mask.png          


 destripe_mask_default.png          

4. 基于HE图像进行细胞核分割和转录本分配

Python                  # In[9]: 细胞核分割(约13min)                                   # block_size StarDist 在 predict_instances_big() 函数中使用的输入参数,指定了作为单个瓦片处理的图像的正方形边长(4096)                  # min_overlap StarDist 在 predict_instances_big() 函数中使用的输入参数,指定了相邻瓦片之间的最小重叠区域(128)                  # context StarDist 在 predict_instances_big() 函数中使用的输入参数,指定了在每个瓦片的边界上要丢弃的图像上下文量                  # prob_thresh控制调用对象的严格性,确定像素被分类为前景(细胞)的概率,值越小分割的细胞越多                  # nms_thresh 非最大抑制中定义了两个或多个预测对象(putative objects)被视为重叠并因此被合并成一个单一标签的阈值                  # labels_npz_path代表输出的npz文件名(稀疏矩阵文件)                  # stardist_model选择StarDist 的 H&E 模型                  # nms_thresh                  b2c.stardist(image_path="stardist/he.tiff",                               labels_npz_path="stardist/he.npz",                               stardist_model="2D_versatile_he",                               prob_thresh=0.01                              )                                   # In[10]:转录本分配                                   b2c.insert_labels(adata,                                    labels_npz_path="stardist/he.npz",                                    basis="spatial",                                    spatial_key="spatial_cropped",                                    mpp=mpp,                                    labels_key="labels_he" # .obs key to store the labels under.                                   )                  adata                  # adata.uns多了'bin2cell'                  # adata.obs 多了'labels_he'                                   # In[11]: Picture3 使用adata.obsm['spatial_cropped']进行可视化nuclei                                   bdata = adata[mask]                                   #0 means unassigned                  bdata = bdata[bdata.obs['labels_he']>0]                  bdata.obs['labels_he'] = bdata.obs['labels_he'].astype(str)                                   sc.pl.spatial(bdata, color=[None, "labels_he"], img_key="0.5_mpp", basis="spatial_cropped")                  # sc.pl.spatial(bdata, color=[None, "labels_he"], img_key="0.5_mpp", basis="spatial_cropped",save='_he_labels.pdf')                  plt.savefig('he_nuclei_mask_test.png', dpi=300, bbox_inches='tight')                  plt.close()                                   # In[12]: Picture4 使用HE图片进行可视化nuclei                                   #the label viewer wants a crop of the processed image                  #get the corresponding coordinates spanning the subset object                  # 获取spatial_cropped的coordinate                  crop = b2c.get_crop(bdata,                                      basis="spatial",                                      spatial_key="spatial_cropped",                                      mpp=mpp)                                   #if this errors about missing get_cmap(), downgrade matplotlib below 3.9.0                  rendered = b2c.view_stardist_labels(image_path="stardist/he.tiff",                                                      labels_npz_path="stardist/he.npz",                                                      crop=crop                                                     )                  plt.imshow(rendered)                  plt.savefig('he_nuclei_mask2.png', dpi=300, bbox_inches='tight')                  # plt.savefig('he_labels_image.pdf')                  plt.close()                  

         he_nuclei_mask.png                  

he_nuclei_mask2.png                  

5. 基于HE细胞核分割结果进行外扩

Python                  # In[13]: 扩展nuclei为cell(4 min)                                   # 如果一个 bin 与多个细胞核的距离相等,则根据其基因表达谱的相似性将其分配给一个 bin                  # max_bin_distance指定扩展核标签时最大的 bin 数量 (2)                  # volume_ratio如果 max_bin_distance 设置为 None,则根据ceil((volume_ratio**(1/3)-1) * sqrt(n_bins/pi))进行扩展(4)                  # k为每个未分配的 bin 寻找潜在最近邻的已分配空间坐标 bin 的数量 (4)                  # subset_pca 如果设置为 True,则只对于那些与多个细胞核的bins距离相等的未分配bins进行PCA表示和决断(True)                                   b2c.expand_labels(adata,                                    labels_key='labels_he',                                    expanded_labels_key="labels_he_expanded"                                   )                  # adata.obs 多了'labels_he_expanded'                                   # In[14]:Picture4 使用adata.obsm['spatial_cropped']进行可视化cell                                   bdata = adata[mask]                  bdata = bdata[bdata.obs['labels_he_expanded']>0]                  bdata.obs['labels_he_expanded'] = bdata.obs['labels_he_expanded'].astype(str)                                   sc.pl.spatial(bdata, color=[None, "labels_he_expanded"], img_key="0.5_mpp", basis="spatial_cropped")                  # sc.pl.spatial(bdata, color=[None, "labels_he_expanded"], img_key="0.5_mpp", basis="spatial_cropped")                  plt.savefig('he_cell_mask.png', dpi=300, bbox_inches='tight')                  plt.close()


he_cell_mask.png  

       

6. 基于表达量进行细胞分割

Python                  # In[15]: 根据表达矩阵进行细胞分割                                   # 基于adata.obs['n_counts_adjusted']生成高斯模糊图像                  # log1p是否做log1p-transform(False)                  # sigma使用 skimage.filters.gaussian() 对最终图像进行高斯模糊处理,sigma 值决定模糊的程度(None)                  b2c.grid_image(adata, "n_counts_adjusted", mpp=mpp, sigma=5, save_path="stardist/gex.tiff")                                   # In[16]: 使用StarDist's fluorescence model进行细胞分割(20min)                                   # 由于网络原因会下载模型失败,多次尝试即可                  b2c.stardist(image_path="stardist/gex.tiff",                               labels_npz_path="stardist/gex.npz",                               stardist_model='2D_versatile_fluo',                               prob_thresh=0.05,                               nms_thresh=0.5                              )                                   # In[17]: 转录本分配                                   b2c.insert_labels(adata,                                    labels_npz_path="stardist/gex.npz",                                    basis="array",                                    mpp=mpp,                                    labels_key="labels_gex"                                   )                  adata                  # adata.obs 多了'labels_gex'                                   # In[18]: 使用adata.obsm['spatial_cropped']进行可视化cell                                   bdata = adata[mask]                  bdata = bdata[bdata.obs['labels_gex']>0]                  bdata.obs['labels_gex'] = bdata.obs['labels_gex'].astype(str)                                   sc.pl.spatial(bdata, color=[None, "labels_gex"], img_key="0.5_mpp", basis="spatial_cropped")                  plt.savefig('gex_cell_mask.png', dpi=300, bbox_inches='tight')                  plt.close()                                   # In[19]:使用gex.tiff进行可视化cell                                   #the label viewer wants a crop of the processed image                  #get the corresponding coordinates spanning the subset object                  crop = b2c.get_crop(bdata, basis="array", mpp=mpp)                                   #if this errors about missing get_cmap(), downgrade matplotlib below 3.9.0                  rendered = b2c.view_stardist_labels(image_path="stardist/gex.tiff",                                                      labels_npz_path="stardist/gex.npz",                                                      crop=crop                                                     )                  plt.imshow(rendered)                  plt.savefig('gex_cell_mask2.png', dpi=300, bbox_inches='tight')                  plt.close()    

  


7. 用基于表达量进行细胞分割的结果填补HE细胞核分割结果中未识别的细胞

Python                  # In[20]: 用GEX的结果填补HE结果中未识别的细胞                                   b2c.salvage_secondary_labels(adata,                                               primary_label="labels_he_expanded",                                               secondary_label="labels_gex",                                               labels_key="labels_joint"                                              )                  adata                  # adata.obs 多了'labels_joint', 'labels_joint_source'                                   # In[21]:使用adata.obsm['spatial_cropped']进行可视化cell                                   bdata = adata[mask]                  bdata = bdata[bdata.obs['labels_joint']>0]                  bdata.obs['labels_joint'] = bdata.obs['labels_joint'].astype(str)                                   sc.pl.spatial(bdata, color=[None, "labels_joint_source", "labels_joint"], img_key="0.5_mpp", basis="spatial_cropped")                  # sc.pl.spatial(bdata, color=[None, "labels_joint_source", "labels_joint"], img_key="0.5_mpp", basis="spatial_cropped",save='_salvage_labels.svg')                  plt.savefig('he_gex_mask_bin.png', dpi=300, bbox_inches='tight')                  plt.close()                                   # In[22]:将同一个cell中的bin进行合并                                   cdata = b2c.bin_to_cell(adata, labels_key="labels_joint", spatial_keys=["spatial", "spatial_cropped"])                  cdata                  # AnnData object with n_obs × n_vars = 69902 × 18823                  #     obs: 'bin_count', 'array_row', 'array_col', 'labels_joint_source'                  #     var: 'gene_ids', 'feature_types', 'genome', 'n_cells'                  #     uns: 'spatial'                  #     obsm: 'spatial', 'spatial_cropped'                                   # In[23]:使用adata.obsm['spatial_cropped']进行可视化cell                                   cell_mask = ((cdata.obs['array_row'] >= 1450) &                               (cdata.obs['array_row'] <= 1550) &                               (cdata.obs['array_col'] >= 250) &                               (cdata.obs['array_col'] <= 450)                              )                                   ddata = cdata[cell_mask]                  sc.pl.spatial(ddata, color=["bin_count", "labels_joint_source"], img_key="0.5_mpp", basis="spatial_cropped")                  # sc.pl.spatial(ddata, color=["bin_count", "labels_joint_source"], img_key="0.5_mpp", basis="spatial_cropped",s=4)                  plt.savefig('he_gex_mask_cell.png', dpi=300, bbox_inches='tight')                  plt.close()                                   # In[24]:保存数据                  cdata.write_h5ad('m_brain_b2c.h5ad')                  adata.write_h5ad('m_brain_2um.h5ad')

he_gex_mask_bin.png          

he_gex_mask_cell.png          

通过修改prob_thresh参数可以产出不同的细胞分割结果,细胞分割的HE染色图建议是分辨率为0.25MPP。

 






二、细胞类型注释和下游分析






在完成细胞分割后,我们导入8um bin数据和细胞分割后的数据进行下游分析,来对比下细胞分割的效果如何。

https://nbviewer.org/github/Teichlab/bin2cell/blob/main/notebooks/N2_demo_analysis_mouse_brain_submission.ipynb

1. 导入模块和地址设置

Python                  # In[1]: 导入模块                  

import scanpy as sc import os import bin2cell as b2c import celltypist from celltypist import models import numpy as np from matplotlib import rcParams from matplotlib import font_manager import matplotlib.pyplot as plt

# In[2]:地址设置

os.chdir("../2_bin2cell/Demo2/Output") os.makedirs("scanpy", exist_ok=True) os.makedirs("spatial", exist_ok=True)

path008 = "../Input/binned_outputs/square_008um/" path002 = "../Input/binned_outputs/square_002um/" source_image_path = "../Input/Visium_HD_Mouse_Brain_tissue_image.tif" prediction_model = "../CellTypist/models/Mouse_Whole_Brain.pkl"


2. 导入8um bin 数据进行常规降维聚类

Python                  # In[3]:导入HD表达数据(8um)                                    bdata = b2c.read_visium(path008, source_image_path = source_image_path)                  bdata.var_names_make_unique()                  bdata                                    # In[4]:                                   bdata.raw = bdata                                    # In[5]:过滤低质量bin                                   sc.pp.filter_genes(bdata, min_cells=3)                  sc.pp.filter_cells(bdata, min_genes=100)                  sc.pp.calculate_qc_metrics(bdata,inplace=True)                  bdata                                    # In[6]:寻找高变基因、标准化                  
                 sc.pp.highly_variable_genes(bdata,n_top_genes=5000,flavor="seurat_v3") sc.pp.normalize_total(bdata,target_sum=1e4) sc.pp.log1p(bdata)                   # In[7]:基于celltypist模型进行细胞类型鉴定
                predictions_8bin = celltypist.annotate(bdata, model = prediction_model, majority_voting = False) bdata = predictions_8bin.to_adata() bdata                   # In[8]:使用高变基因进行降维和可视化                  bdata = bdata[:, bdata.var["highly_variable"]].copy() sc.pp.scale(bdata, max_value=10) sc.pp.pca(bdata, use_highly_variable=True) sc.pp.neighbors(bdata) sc.tl.umap(bdata)                   # In[9]:表达量小提琴图 # 每细胞基因数、log1p(x) = log(1 + x)、每细胞UMI数、log1p(x) = log(1 + x)、细胞中表达量最高的前50个基因、细胞中表达量最高的前100个基因 sc.set_figure_params(dpi=50,fontsize=10,) sc.pl.violin(bdata, ['n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes'], jitter=0.1, multi_panel=True) plt.savefig('scanpy/8um_violin.png', dpi=300, bbox_inches='tight') plt.close() # In[10]: UMAP                   sc.set_figure_params(dpi=100,fontsize=10,) sc.tl.leiden(bdata,resolution=6,key_added='leiden') del bdata.uns["leiden_colors"] sc.pl.umap(bdata,color=['leiden'],size=2,wspace=0.25,frameon=False) plt.savefig('scanpy/8um_umap.png', dpi=300, bbox_inches='tight') plt.close()                  # In[11]: save while recovering raw                                    bdata.raw.to_adata().write('mb_8um.h5ad')


8um_violin.png         

8um_umap.png         

3. 导入细胞分割数据进行常规降维聚类

Python                  # In[12]:导入HD细胞分割结果(2um)                  
cdata = sc.read_h5ad('../2_bin2cell/Demo2/Output/m_brain_b2c.h5ad') cdata # AnnData object with n_obs × n_vars = 69902 × 18823 # obs: 'bin_count', 'array_row', 'array_col', 'labels_joint_source' # var: 'gene_ids', 'feature_types', 'genome', 'n_cells' # uns: 'spatial' # obsm: 'spatial', 'spatial_cropped' # In[13]:过滤过小的细胞 cdata = cdata[cdata.obs['bin_count']>7] # min 8 bins cdata # need integers for seuratv3 hvgs cdata.X.data = np.round(cdata.X.data) cdata.raw = cdata.copy() # In[14]:过滤低质量基因和细胞 sc.pp.filter_genes(cdata, min_cells=3) sc.pp.filter_cells(cdata,min_genes=100) sc.pp.calculate_qc_metrics(cdata,inplace=True) cdata # AnnData object with n_obs × n_vars = 60274 × 18552 # obs: 'bin_count', 'array_row', 'array_col', 'labels_joint_source', 'n_genes', 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes', 'pct_counts_in_top_200_genes', 'pct_counts_in_top_500_genes' # var: 'gene_ids', 'feature_types', 'genome', 'n_cells', 'n_cells_by_counts', 'mean_counts', 'log1p_mean_counts', 'pct_dropout_by_counts', 'total_counts', 'log1p_total_counts' # uns: 'spatial' # obsm: 'spatial', 'spatial_cropped' # In[15]:寻找高变基因、标准化 sc.pp.highly_variable_genes(cdata,n_top_genes=5000,flavor="seurat_v3") sc.pp.normalize_total(cdata,target_sum=1e4) sc.pp.log1p(cdata) # In[16]:基于celltypist模型进行细胞类型鉴定 b2c_predictions = celltypist.annotate(cdata, model = prediction_model, majority_voting = False) cdata = b2c_predictions.to_adata() cdata # In[17]:使用高变基因进行降维和可视化 cdata = cdata[:, cdata.var["highly_variable"]] sc.pp.scale(cdata, max_value=10) sc.pp.pca(cdata, use_highly_variable=True) sc.pp.neighbors(cdata) sc.tl.umap(cdata) # In[18]:表达量小提琴图 sc.set_figure_params(dpi=50,fontsize=10,) sc.pl.violin(cdata, ['n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes','bin_count'], jitter=0.05, multi_panel=True) plt.savefig('scanpy/cell_violin.png', dpi=300, bbox_inches='tight') plt.close() # In[19]:UMAP sc.set_figure_params(dpi=100,fontsize=10,) sc.tl.leiden(cdata,resolution=6,key_added='leiden') sc.pl.umap(cdata,color=['leiden'],size=2,wspace=0.25,frameon=False) plt.savefig('scanpy/cell_umap.png', dpi=300, bbox_inches='tight') plt.close() # In[20]:save while recovering raw cdata.raw.to_adata().write('mb_b2c.h5ad')


cell_violin.png         

cell_umap.png         

         

 






补充说明






Bin2Cell主要输出结果为h5ad文件,可在Python中接入下游流程进行单细胞转录组数据类似的分析,也可以转换成RDS文件导入R中使用Seurat进行下游分析。需要注意的是,尽管Bin2Cell依靠高清HE图像细胞分割后生成掩膜,然后将Visium HD数据的最小研究单位(2μm bin)中的转录本分配到所属的掩膜当中去,以解决Visium HD数据的高分辨率带来的bin覆盖问题,随之也带来了细胞分割的适配性和可能舍弃的细胞质部分等问题。

        



参考资料


  • Krzysztof Polański, Raquel Bartolomé-Casado, Ioannis Sarropoulos, Chuan Xu, Nick England, Frode L Jahnsen, Sarah A Teichmann, Nadav Yayon, Bin2cell reconstructs cells from high resolution visium HD data, Bioinformatics, 2024;, btae546, https://doi.org/10.1093/bioinformatics/btae546

  • bin2cell使用方法:https://bin2cell.readthedocs.io/en/latest/index.html

  • github网址:https://github.com/Teichlab/bin2cell

         

 

相关阅读

ROC分析介绍|生信开发实战

如果使用find_circ来鉴定circRNA|生信开发实战

国自然2025:单细胞+bulk转录组如何开展研究?|生信开发实战

解析细胞功能基因集变异——遇见GSVA|生信开发实战


       

 

    
本文系联川生物公众号原创文章,未经授权禁止转载,侵权必究!

扫描下方二维码





点分享


点点赞


点在看

         

 

联川生物
一个提供科研入门学习资源、经验的平台。 分享前沿测序技术资讯、实用生信绘图技巧及工具。 发布高质量的科研论文精度、精炼科研思路。 我们的目标是持续提供“干货”,滋润您的科研生涯。
 最新文章