├── LICENSE
├── README.md
├── TESLA_package
├── LICENSE
├── README.md
├── TESLA.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ ├── requires.txt
│ └── top_level.txt
├── TESLA
│ ├── ConnectedComponents.py
│ ├── TESLA.py
│ ├── TLS_detection.py
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── TESLA.cpython-37.pyc
│ │ ├── __init__.cpython-37.pyc
│ │ ├── annotation.cpython-37.pyc
│ │ ├── calculate_dis.cpython-37.pyc
│ │ ├── contour_util.cpython-37.pyc
│ │ ├── imputation.cpython-37.pyc
│ │ ├── models.cpython-37.pyc
│ │ └── util.cpython-37.pyc
│ ├── annotation.py
│ ├── calculate_dis.py
│ ├── contour_util.py
│ ├── imputation.py
│ ├── models.py
│ ├── tumor_edge_core.py
│ └── util.py
├── TESLAforST.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ ├── requires.txt
│ └── top_level.txt
├── build
│ └── lib
│ │ └── TESLA
│ │ ├── ConnectedComponents.py
│ │ ├── TESLA.py
│ │ ├── TLS_detection.py
│ │ ├── __init__.py
│ │ ├── annotation.py
│ │ ├── calculate_dis.py
│ │ ├── contour_util.py
│ │ ├── imputation.py
│ │ ├── models.py
│ │ ├── tumor_edge_core.py
│ │ └── util.py
├── dist
│ ├── TESLA-1.0.0-py3.7.egg
│ ├── TESLAforST-1.2.0-py3-none-any.whl
│ ├── TESLAforST-1.2.0.tar.gz
│ ├── TESLAforST-1.2.1-py3-none-any.whl
│ ├── TESLAforST-1.2.1.tar.gz
│ ├── TESLAforST-1.2.2-py3-none-any.whl
│ └── TESLAforST-1.2.2.tar.gz
└── setup.py
├── docs
└── asserts
│ └── images
│ ├── applications.jpg
│ ├── applications_new.jpg
│ └── workflow.jpg
└── tutorial
├── output_19_0.jpg
├── output_23_0.png
├── output_28_1.jpg
├── output_35_0.jpg
├── output_37_2.jpg
├── output_43_0.jpg
├── output_44_0.jpg
├── output_51_0.jpg
├── tutorial.ipynb
└── tutorial.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 JianHu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TESLA v1.2.4
2 |
3 | ## TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics
4 |
5 | ### Jian Hu*, Kyle Coleman, Edward B. Lee, Humam Kadara, Linghua Wang*, Mingyao Li*
6 |
7 | TESLA (Tumor Edge Structure and Lymphocyte multi-level Annotation) is a machine learning framework for multi-level tissue annotation on the histology image with pixel-level resolution in Spatial Transcriptomics (ST). By integrating information from high-resolution histology image, TESLA can impute gene expression at superpixels and fill in missing gene expression in tissue gaps. The increased gene expression resolution makes it possible to treat gene expression data as images, which enabled the integration with histological features for joint tissue segmentation and annotation of different cell types directly on the histology image with pixel-level resolution. Additionally, TESLA can detect unique structures of tumor immune microenvironment such as Tertiary Lymphoid Structures (TLSs), , separate a tumor into core and edge to examine their cellular compositions, expression features, and molecular processes. TESLA has been evaluated on five cancer datasets. Our results consistently showed that TESLA can generate high-quality super-resolution gene expression images, which facilitated the downstream multi-level tissue annotation.
8 |
9 |
10 |
11 | 
12 |
13 |
14 |
15 | ## Applications
16 | TESLA has been used for cancer genomics data analysis in numerous top-tier journals.
17 |
18 |
19 |
20 | 
21 |
22 |
23 |
24 | ## Usage
25 |
26 | The [**TESLA**](https://github.com/jianhuupenn/TESLA) package is an implementation of multi-level tissue annotation on the histology image with pixel-level resolution in spatial transcriptomics. With TESLA, you can:
27 |
28 | - Enhance the gene expression resolution to pixel resolution the same as the histology image;
29 | - Annotate tumor region at pixel resolution;
30 | - Annotate user-defined cell types at pixel resolution;
31 | - Characterize the intra-tumor heterogeneity, e.g, the tumor leading edge, tumor core and edge, tumor subtypes.
32 |
33 | For tutorial, please refer to: https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md
34 |
35 | A Jupyter Notebook of the tutorial is accessible from : https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.ipynb
36 |
37 | Please install jupyter in order to open this notebook.
38 |
39 | Toy data and results can be downloaded at: https://drive.google.com/drive/folders/1hC6ldkxmZX0yiCWZR57iMXjWIIm9qUJU?usp=sharing
40 |
41 | ## System Requirements
42 | Python support packages: torch, pandas, numpy, scipy, scanpy > 1.5, anndata, sklearn, cv2.
43 |
44 | ## Versions the software has been tested on
45 | Environment 1:
46 | - System: Mac OS 10.13.6
47 | - Python: 3.7.0
48 | - Python packages: pandas = 1.1.3, numpy = 1.18.1, torch=1.5.1,louvain=0.6.1,scipy = 1.4.1, scanpy = 1.5.1, anndata = 0.6.22.post1, sklearn = 0.22.1, cv2=4.5.1.
49 |
50 |
51 | Environment 2:
52 | - System: Anaconda
53 | - Python: 3.7.9
54 | - Python packages: pandas = 1.1.3, numpy = 1.20.2, python-igraph=0.8.3, torch=1.6.0,louvain=0.7.0, scipy = 1.5.2, scanpy = 1.6.0, anndata = 0.7.4, sklearn = 0.23.3, cv2=4.5.1
55 |
56 |
57 | ## Contributing
58 |
59 | Souce code: [Github](https://github.com/jianhuupenn/TESLA)
60 |
61 | We are continuing adding new features. Bug reports or feature requests are welcome.
62 |
63 | Last update: 11/20/2021, version 1.0.0
64 |
65 |
66 |
67 | ## References
68 |
69 | Please consider citing the following reference:
70 |
71 | - https://doi.org/10.1016/j.cels.2023.03.008
72 |
73 |
--------------------------------------------------------------------------------
/TESLA_package/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 JianHu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/TESLA_package/README.md:
--------------------------------------------------------------------------------
1 | # TESLA
2 |
3 | ## TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics
4 |
5 | ### Jian Hu,*, Kyle Coleman, Edward B. Lee, Humam Kadara, Linghua Wang,*, Mingyao Li,*
6 |
7 | TESLA is a machine learning framework for multi-level tissue annotation with pixel-level resolution in ST. TESLA integrates histological information with gene expression to annotate heterogeneous immune and tumor cells directly on the histology image. TESLA further detects unique Tumor Microenvironment (TME) features such as tertiary lymphoid structures and differential transcriptome programs in the core or edge of a tumor, which represent a promising avenue for understanding the spatial architecture of the TME. Although we illustrated the applications in cancer, TESLA can also be applied to other diseases.
8 | For more info, please go to:
9 | https://github.com/jianhuupenn/TESLA
--------------------------------------------------------------------------------
/TESLA_package/TESLA.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 2.1
2 | Name: TESLA
3 | Version: 1.0.0
4 | Summary: TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics
5 | Home-page: https://github.com/jianhuupenn/TESLA
6 | Author: Jian Hu
7 | Author-email: jianhu@pennmedicine.upenn.edu
8 | License: UNKNOWN
9 | Description: # TESLA
10 |
11 | ## TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics
12 |
13 | ### Jian Hu,*, Kyle Coleman, Edward B. Lee, Humam Kadara, Linghua Wang,*, Mingyao Li,*
14 |
15 | TESLA is a machine learning framework for multi-level tissue annotation with pixel-level resolution in ST. TESLA integrates histological information with gene expression to annotate heterogeneous immune and tumor cells directly on the histology image. TESLA further detects unique Tumor Microenvironment (TME) features such as tertiary lymphoid structures and differential transcriptome programs in the core or edge of a tumor, which represent a promising avenue for understanding the spatial architecture of the TME. Although we illustrated the applications in cancer, TESLA can also be applied to other diseases.
16 | For more info, please go to:
17 | https://github.com/jianhuupenn/TESLA
18 | Platform: UNKNOWN
19 | Classifier: Programming Language :: Python :: 3
20 | Classifier: License :: OSI Approved :: MIT License
21 | Classifier: Operating System :: OS Independent
22 | Requires-Python: >=3.6
23 | Description-Content-Type: text/markdown
24 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | README.md
2 | setup.py
3 | TESLA/ConnectedComponents.py
4 | TESLA/TESLA.py
5 | TESLA/__init__.py
6 | TESLA/annotation.py
7 | TESLA/calculate_dis.py
8 | TESLA/contour_util.py
9 | TESLA/imputation.py
10 | TESLA/models.py
11 | TESLA/util.py
12 | TESLA.egg-info/PKG-INFO
13 | TESLA.egg-info/SOURCES.txt
14 | TESLA.egg-info/dependency_links.txt
15 | TESLA.egg-info/requires.txt
16 | TESLA.egg-info/top_level.txt
--------------------------------------------------------------------------------
/TESLA_package/TESLA.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA.egg-info/requires.txt:
--------------------------------------------------------------------------------
1 | torch
2 | pandas
3 | numpy
4 | scipy
5 | scanpy
6 | anndata
7 | sklearn
8 | numba
9 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | TESLA
2 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/ConnectedComponents.py:
--------------------------------------------------------------------------------
1 | #Get connected components in an undirected graph
2 | import pandas as pd
3 | import numpy as np
4 | from collections import deque
5 | class Graph:
6 | # init function to declare class variables
7 | def __init__(self, V, adj):
8 | self.V = V
9 | self.adj = adj
10 | def DFSUtil(self, v, tmp, visited):
11 | # Mark the current vertex as visited
12 | visited[v] = 1
13 | # Store the vertex to list
14 | tmp.append(v)
15 | # Repeat for all vertices adjacent to this vertex v
16 | nbr=self.adj[v][self.adj[v]==1].index.tolist()
17 | for i in nbr:
18 | if visited[i] == 0:
19 | tmp = self.DFSUtil(i, tmp, visited)
20 | return tmp
21 | def ConnectedComponentsDFS(self):
22 | visited = pd.Series([0]* len(self.V), index=self.V)
23 | cc = []
24 | for v in self.V:
25 | if visited[v] == 0:
26 | tmp = []
27 | cc.append(self.DFSUtil(v, tmp, visited))
28 | return cc
29 |
30 | def ConnectedComponents(self):
31 | visited = pd.Series([0] * len(self.V), index=self.V)
32 | cc = []
33 | for v in self.V:
34 | if visited[v] == 1:
35 | continue
36 |
37 | queue = deque([v])
38 | visited[v] = 1
39 | tmp = [v]
40 | while len(queue) > 0:
41 | elem = queue.pop()
42 | nbrs = self.adj[elem][self.adj[elem] == 1].index.tolist()
43 |
44 | for nbr in nbrs:
45 | if visited[nbr] == 0:
46 | queue.append(nbr)
47 | visited[nbr] = 1
48 | tmp.append(nbr)
49 |
50 | if len(tmp) > 0:
51 | cc.append(tmp)
52 |
53 | return cc
54 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/TESLA.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | import time
4 | import cv2
5 | import argparse
6 | import torch
7 | import torch.nn as nn
8 | import torch.nn.functional as F
9 | import torch.optim as optim
10 | import torch.nn.init
11 | from torch.autograd import Variable
12 | from . models import Spatial_CNN
13 |
14 | class TESLA(object):
15 | def __init__(self):
16 | super(TESLA, self).__init__()
17 |
18 | def train(self, input,
19 | use_cuda=False,
20 | train_refine=True,
21 | radius=3,
22 | nChannel=100,
23 | lr=0.1,
24 | minLabels=20,
25 | maxIter=30,
26 | stepsize_sim=1,
27 | stepsize_con=10,
28 | threshold=1000,
29 | plot_intermedium=False,
30 | plot_dir="./"):
31 | self.use_cuda=use_cuda
32 | self.train_refine=train_refine
33 | self.radius=radius
34 | self.nChannel=nChannel
35 | self.lr=lr
36 | self.minLabels=minLabels
37 | self.maxIter=maxIter
38 | self.stepsize_sim=stepsize_sim
39 | self.stepsize_con=stepsize_con
40 | self.threshold=threshold
41 | self.plot_intermedium=plot_intermedium
42 | self.resize_height=input.shape[0]
43 | self.resize_width=input.shape[1]
44 | input = torch.from_numpy( np.array([input.transpose( (2, 0, 1) ).astype('float32')]) )
45 | if use_cuda:
46 | input = input.cuda()
47 | input = Variable(input)
48 | #--------------------------------------- Train ---------------------------------------
49 | self.model = Spatial_CNN(input.size(1), nConv=2, nChannel=self.nChannel, kernel_size_list=[5, 5],stride_list=[1, 1], padding_list=[2, 2])
50 | if use_cuda:
51 | self.model.cuda()
52 | # Similarity loss definition
53 | loss_fn = torch.nn.CrossEntropyLoss()
54 | # Continuity loss definition
55 | loss_hpy = torch.nn.L1Loss(size_average = True)
56 | loss_hpz = torch.nn.L1Loss(size_average = True)
57 | HPy_target = torch.zeros(self.resize_height-1, self.resize_width, nChannel)
58 | HPz_target = torch.zeros(self.resize_height, self.resize_width-1, nChannel)
59 | if use_cuda:
60 | HPy_target = HPy_target.cuda()
61 | HPz_target = HPz_target.cuda()
62 | optimizer = optim.SGD(self.model.parameters(), lr=lr, momentum=0.9)
63 | label_colours = np.random.randint(255,size=(100,3))
64 | start_time = time.time()
65 | self.model.train()
66 | for batch_idx in range(maxIter):
67 | # forwarding
68 | optimizer.zero_grad()
69 | output = self.model( input )[ 0 ]
70 | output = output.permute( 1, 2, 0 ).contiguous().view( -1, nChannel )
71 | outputHP = output.reshape( (self.resize_height, self.resize_width, nChannel) )
72 | HPy = outputHP[1:, :, :] - outputHP[0:-1, :, :]
73 | HPz = outputHP[:, 1:, :] - outputHP[:, 0:-1, :]
74 | lhpy = loss_hpy(HPy,HPy_target)
75 | lhpz = loss_hpz(HPz,HPz_target)
76 | _, target = torch.max( output, 1 )
77 | img_target = target.data.cpu().numpy()
78 | # Total number of clusters
79 | nLabels = len(np.unique(img_target))
80 | # Number of main clusters
81 | mainLabels=(pd.Series(img_target).value_counts()>=threshold).sum()
82 | #--------------------------Refine during training----------------------------------------
83 | if train_refine:
84 | pixel_num=pd.Series(img_target).value_counts()
85 | main_clusters=pixel_num.index[pixel_num>=threshold].tolist()
86 | minor_clusters=pixel_num.index[pixel_num0:
98 | replace_map[i]=nbs_num.index[nbs_num.index.isin(main_clusters)][0]
99 | target=torch.from_numpy(pd.Series(img_target).replace(replace_map).to_numpy())
100 | #------------------------------------------------------------------
101 | loss1 = stepsize_sim * loss_fn(output, target)
102 | loss2 = stepsize_con * (lhpy + lhpz)
103 | loss=loss1+loss2
104 | loss.backward()
105 | optimizer.step()
106 | print (batch_idx, '/', maxIter, '|', ' label num :', nLabels, '|',' main clusters :',mainLabels,' | feature loss :', loss1.item(),' | spatial loss :', loss2.item())
107 | print("--- %s seconds ---" % (time.time() - start_time))
108 | if batch_idx%2==0:
109 | output = self.model(input)[0]
110 | output = output.permute(1, 2, 0).contiguous().view(-1, nChannel)
111 | _, target = torch.max(output, 1)
112 | img_target = target.data.cpu().numpy()
113 | img_target_rgb = np.array([label_colours[ c % label_colours.shape[0]] for c in img_target])
114 | img_target_rgb = img_target_rgb.reshape(self.resize_height, self.resize_width, 3).astype( np.uint8 )
115 | if self.plot_intermedium:
116 | cv2.imwrite(plot_dir+str(batch_idx)+"_nL"+str(nLabels)+"_mL"+str(mainLabels)+".png", img_target_rgb)
117 | if mainLabels <= minLabels:
118 | print ("mainLabels", mainLabels, "reached minLabels", minLabels, ".")
119 | break
120 |
121 | def predict(self, input):
122 | input = torch.from_numpy( np.array([input.transpose( (2, 0, 1) ).astype('float32')]) )
123 | output = self.model( input )[ 0 ]
124 | output = output.permute( 1, 2, 0 ).contiguous().view( -1, self.nChannel )
125 | prob=output.data.cpu().numpy()
126 | prob=np.divide(np.exp(prob), np.sum(np.exp(prob), 1).reshape(-1, 1))
127 | _, pred = torch.max( output, 1 )
128 | pred = pred.data.cpu().numpy()
129 | return prob, pred
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/TLS_detection.py:
--------------------------------------------------------------------------------
1 | import os, sys, csv,re, time, random
2 | import cv2
3 | import numpy as np
4 | import pandas as pd
5 | import scanpy as sc
6 | from scipy.sparse import issparse
7 | from . util import *
8 | from . contour_util import *
9 |
10 | def TLS_detection( pred_refined_list, cluster_density_list, num_required, cnt_color, pooling="min"):
11 | pred_TLS=np.zeros([len(pred_refined_list[0]), len(pred_refined_list)])
12 | for i in range(len(pred_refined_list)):
13 | tmp=np.zeros(pred_refined_list[i].shape)
14 | for k, v in cluster_density_list[i].items():
15 | tmp[pred_refined_list[i]==k]=v/np.max(list(cluster_density_list[i].values()))
16 | pred_TLS[:,i]=tmp
17 | target = np.partition(pred_TLS, -num_required, axis=1)[:,-num_required:] #Select top num_required
18 | if pooling=="mean":
19 | target=np.mean(target, axis=1)
20 | elif pooling=="min":
21 | target=np.min(target, axis=1)
22 | else:
23 | print("Error! Pooling logic not understood.")
24 | target=(target-np.min(target))/(np.max(target)-np.min(target))
25 | target[target<0.5]=0
26 | return target
27 |
28 |
29 | def plot_TLS_score(img, resize_factor, binary,target, cnt_color):
30 | resize_width=int(img.shape[1]*resize_factor)
31 | resize_height=int(img.shape[0]*resize_factor)
32 | binary_resized=cv2.resize(binary, (resize_width, resize_height))
33 | img_resized =cv2.resize(img, (resize_width, resize_height))
34 | target_img=target.reshape(resize_height, resize_width)
35 | target_img_rgb=(cnt_color((target*255).astype("int"))[:, 0:3]*255).reshape(resize_height, resize_width,3).astype( np.uint8 )
36 | target_img_rgb=(cnt_color((target*255).astype("int"))[:, 0:3]*255).reshape(resize_height, resize_width,3).astype( np.uint8 )
37 | target_img_rgb=cv2.cvtColor(target_img_rgb, cv2.COLOR_RGB2BGR)
38 | ret_img=img_resized.copy()
39 | #Whiten
40 | white_ratio=0.5
41 | ret_img[binary_resized!=0]=ret_img[binary_resized!=0]*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
42 | ret_img[target_img!=0]=target_img_rgb[target_img!=0]
43 | ret_img[binary_resized==0]=255
44 | return ret_img
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '1.2.4'
2 | from . TESLA import *
3 | from . util import *
4 | from . contour_util import *
5 | from . calculate_dis import *
6 | from . imputation import *
7 | from . annotation import *
8 | from . contour_util import *
9 | from . TLS_detection import *
10 | from . tumor_edge_core import *
11 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/TESLA.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/TESLA.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/annotation.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/annotation.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/calculate_dis.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/calculate_dis.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/contour_util.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/contour_util.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/imputation.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/imputation.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/models.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/models.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/__pycache__/util.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/TESLA/__pycache__/util.cpython-37.pyc
--------------------------------------------------------------------------------
/TESLA_package/TESLA/annotation.py:
--------------------------------------------------------------------------------
1 | import os,csv,re, time, random
2 | import cv2
3 | import numpy as np
4 | import pandas as pd
5 | import torch
6 | import scanpy as sc
7 | from . util import *
8 | from . contour_util import *
9 | from . calculate_dis import *
10 | from . TESLA import TESLA
11 |
12 |
13 | def annotation(img,
14 | sudo_adata,
15 | genes,
16 | resize_factor,
17 | binary,
18 | res=50,
19 | num_required=1,
20 | pooling="min",
21 | rseed=100,
22 | tseed=100,
23 | nseed=100,
24 | nChannel=100,
25 | threshold=1000,
26 | radius=3,
27 | minLabels=30,
28 | train_refine=True,
29 | plot_intermedium=False,
30 | target_size="small",
31 | min_UMI=1):
32 | #-------------------------------Image band-------------------------------------------------#
33 | if target_size=="small":
34 | target_ratio=1/2
35 | elif target_size=="large":
36 | target_ratio=1/3
37 | else:
38 | print("target_size not valid, Please specify (small / large).Use default small")
39 | target_ratio=1/2
40 | print("Computing image band...")
41 | resize_width=int(img.shape[1]*resize_factor)
42 | resize_height=int(img.shape[0]*resize_factor)
43 | img_resized=(img * np.dstack([(binary!=0)]*3)).astype(np.uint8)
44 | img_resized = cv2.resize(img_resized, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
45 | gray_resized = cv2.cvtColor(img_resized,cv2.COLOR_BGR2GRAY)
46 | gray_resized=gray_resized.reshape(list(gray_resized.shape)+[1])
47 | binary_resized=cv2.resize(binary, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
48 | img_band1=(gray_resized-np.min(gray_resized))/(np.max(gray_resized)-np.min(gray_resized))
49 | #-------------------------------Gene band-------------------------------------------------#
50 | print("Computing gene band...")
51 | genes=list(set([i for i in genes if i in sudo_adata.var.index ]))
52 | assert num_required<=len(genes)
53 | tmp=sudo_adata.X[:,sudo_adata.var.index.isin(genes)]
54 | tmp=(tmp-np.min(tmp, 0))/(np.max(tmp, 0)-np.min(tmp, 0))
55 | tmp = np.partition(tmp, -num_required, axis=1)[:,-num_required:] #Select top num_required
56 | if pooling=="mean":
57 | sudo_adata.obs["meta"]=np.mean(tmp, axis=1)
58 | elif pooling=="min":
59 | sudo_adata.obs["meta"]=np.min(tmp, axis=1)
60 | else:
61 | print("Error! Pooling logic not understood.")
62 | gene_img=np.zeros(list(img.shape[0:2])+[1])
63 | for _, row in sudo_adata.obs.iterrows():
64 | x=row["x"]
65 | y=row["y"]
66 | exp=row["meta"]
67 | gene_img[int(x-res/2):int(x+res/2), int(y-res/2):int(y+res/2),0]=exp
68 | gene_img[binary==0]=0
69 | gene_band1=gene_img[:,:,0]
70 | #Filter on min UMI
71 | gene_band1[gene_band1<=np.log(min_UMI+1)]=0
72 | gene_band1=cv2.resize(gene_band1, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
73 | gene_band1=(gene_band1-np.min(gene_band1))/(np.max(gene_band1)-np.min(gene_band1))
74 | gene_band1=gene_band1.reshape(list(gene_band1.shape)+[1])
75 | #-------------------------------TESLA-------------------------------------------------#
76 | print("Running TESLA...")
77 | assert np.max(img_band1)==1 and np.min(img_band1)==0 and np.max(gene_band1)==1 and np.min(gene_band1)==0
78 | data=np.concatenate((img_band1, gene_band1), axis=2)
79 | random.seed(rseed)
80 | torch.manual_seed(tseed)
81 | np.random.seed(nseed)
82 | tesla=TESLA()
83 | tesla.train(input=data, use_cuda=False, train_refine=train_refine, radius=radius, nChannel=nChannel, lr=0.1,
84 | minLabels=minLabels, maxIter=30, stepsize_sim=1, stepsize_con=5, threshold=threshold, plot_intermedium=plot_intermedium, plot_dir=None)
85 | prob, pred = tesla.predict(data)
86 | pred_refined=refine_clusters(pred=pred, resize_height=resize_height, resize_width=resize_width, threshold=threshold, radius=radius)
87 | nLabels=len(np.unique(pred))
88 | mainLabels=len(np.unique(pred_refined))
89 | #-----------------------------------Find target cluster---------------------------#
90 | print("Finding target clusters...")
91 | marker=gene_band1.flatten()
92 | clusters=pred_refined.flatten()
93 | c_m={} #cluster_marker expression
94 | for i in np.unique(clusters):
95 | c_m[i]=np.mean(marker[clusters==i])
96 | c_m = sorted(c_m.items(), key=lambda x: x[1], reverse=True)
97 | target_clusters=list(filter(lambda x: (x[1] > c_m[0][1]*target_ratio), c_m))
98 | target_clusters=[x[0] for x in target_clusters]
99 | print("c_m:\n", c_m, "\n", "Target clusters:\n", target_clusters, sep = '')
100 | return pred_refined, target_clusters, c_m
101 |
102 | def visualize_annotation(pred_refined,
103 | target_clusters,
104 | c_m,
105 | img,
106 | binary,
107 | resize_factor,
108 | cnt_color=clr.LinearSegmentedColormap.from_list('red', ["#EAE7CC", '#BA0000'], N=256)):
109 | resize_width=int(img.shape[1]*resize_factor)
110 | resize_height=int(img.shape[0]*resize_factor)
111 | binary_resized=cv2.resize(binary, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
112 | background = cv2.resize(img, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
113 | ret_img=(background.copy()).astype(np.uint8)
114 | alpha=0.8
115 | #Whiten
116 | white_ratio=0.5
117 | ret_img=ret_img*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
118 | for i in range(len(target_clusters)):
119 | color=((np.array(cnt_color(int(c_m[i][1]/c_m[0][1]*255)))[0:3])*255).astype(int)[::-1]
120 | target_img=(1*(pred_refined==target_clusters[i])).reshape(resize_height, resize_width)
121 | target_img[binary_resized==0]=0
122 | ret_img[target_img!=0]=ret_img[target_img!=0]*(1-alpha)+np.array(color)*(alpha)
123 | return ret_img
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/calculate_dis.py:
--------------------------------------------------------------------------------
1 | import os,csv,re
2 | import pandas as pd
3 | import numpy as np
4 | import scanpy as sc
5 | import math
6 | import matplotlib.colors as clr
7 | import matplotlib.pyplot as plt
8 |
9 | def distance(t1,t2):
10 | sum=((t1-t2)**2).sum()
11 | return math.sqrt(sum)
12 |
13 | def calculate_dis_matrix(x, y, x_pixel=None, y_pixel=None, image=None, beta=49, alpha=1, histology=True):
14 | #x,y,x_pixel, y_pixel are lists
15 | dis=np.zeros((len(x),len(x)))
16 | if histology:
17 | assert (x_pixel is not None) & (x_pixel is not None) & (image is not None)
18 | assert (len(x)==len(x_pixel)) & (len(y)==len(y_pixel))
19 | print("Calculateing distance matrix using histology image...")
20 | #beta to control the range of neighbourhood when calculate grey vale for one spot
21 | #alpha to control the color scale
22 | beta_half=round(beta/2)
23 | g=[]
24 | for i in range(len(x_pixel)):
25 | max_x=image.shape[0]
26 | max_y=image.shape[1]
27 | nbs=image[max(0,x_pixel[i]-beta_half):min(max_x,x_pixel[i]+beta_half+1),max(0,y_pixel[i]-beta_half):min(max_y,y_pixel[i]+beta_half+1)]
28 | g.append(np.mean(np.mean(nbs,axis=0),axis=0))
29 | c0, c1, c2=[], [], []
30 | for i in g:
31 | c0.append(i[0])
32 | c1.append(i[1])
33 | c2.append(i[2])
34 | c0=np.array(c0)
35 | c1=np.array(c1)
36 | c2=np.array(c2)
37 | print("Var of c0,c1,c2 = ", np.var(c0),np.var(c1),np.var(c2))
38 | c3=(c0*np.var(c0)+c1*np.var(c1)+c2*np.var(c2))/(np.var(c0)+np.var(c1)+np.var(c2))
39 | c4=(c3-np.mean(c3))/np.std(c3)
40 | z_scale=np.max([np.std(x), np.std(y)])*alpha
41 | z=c4*z_scale
42 | z=z.tolist()
43 | print("Var of x,y,z = ", np.var(x),np.var(y),np.var(z))
44 | for i in range(len(x)):
45 | if i%500==0:
46 | print("Calculating spot ", i, "/", len(x))
47 | for j in range(i, len(x)):
48 | cord1=np.array([x[i],y[i],z[i]])
49 | cord2=np.array([x[j],y[j],z[j]])
50 | dis[i][j]=dis[j][i]=distance(cord1, cord2)
51 | return dis, z
52 | else:
53 | print("Calculateing distance matrix using xy only...")
54 | for i in range(len(x)):
55 | if i%50==0:
56 | print("Calculating spot ", i, "/", len(x))
57 | for j in range(i, len(x)):
58 | cord1=np.array([x[i],y[i]])
59 | cord2=np.array([x[j],y[j]])
60 | dis[i][j]=dis[j][i]=distance(cord1, cord2)
61 | return dis
62 |
63 | def extract_color(x_pixel=None, y_pixel=None, image=None, beta=49, RGB=True):
64 | if RGB:
65 | #beta to control the range of neighbourhood when calculate grey vale for one spot
66 | beta_half=round(beta/2)
67 | g=[]
68 | for i in range(len(x_pixel)):
69 | max_x=image.shape[0]
70 | max_y=image.shape[1]
71 | nbs=image[max(0,x_pixel[i]-beta_half):min(max_x,x_pixel[i]+beta_half+1),max(0,y_pixel[i]-beta_half):min(max_y,y_pixel[i]+beta_half+1)]
72 | g.append(np.mean(np.mean(nbs,axis=0),axis=0))
73 | c0, c1, c2=[], [], []
74 | for i in g:
75 | c0.append(i[0])
76 | c1.append(i[1])
77 | c2.append(i[2])
78 | c0=np.array(c0)
79 | c1=np.array(c1)
80 | c2=np.array(c2)
81 | c3=(c0*np.var(c0)+c1*np.var(c1)+c2*np.var(c2))/(np.var(c0)+np.var(c1)+np.var(c2))
82 | else:
83 | beta_half=round(beta/2)
84 | g=[]
85 | for i in range(len(x_pixel)):
86 | max_x=image.shape[0]
87 | max_y=image.shape[1]
88 | nbs=image[max(0,x_pixel[i]-beta_half):min(max_x,x_pixel[i]+beta_half+1),max(0,y_pixel[i]-beta_half):min(max_y,y_pixel[i]+beta_half+1)]
89 | g.append(np.mean(nbs))
90 | c3=np.array(g)
91 | return c3
92 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/contour_util.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import cv2
4 |
5 | def scan_contour(spots, scan_x=True, shape="hexagon"):
6 | #shape="hexagon" # For 10X Vsium, shape="square" for ST data
7 | if scan_x:
8 | array_a="array_x"
9 | array_b="array_y"
10 | pixel_a="pixel_x"
11 | pixel_b="pixel_y"
12 | else:
13 | array_a="array_y"
14 | array_b="array_x"
15 | pixel_a="pixel_y"
16 | pixel_b="pixel_x"
17 | upper, lower={}, {}
18 | uniq_array_a=sorted(set(spots[array_a]))
19 | if shape=="hexagon":
20 | for i in range(len(uniq_array_a)-1):
21 | a1=uniq_array_a[i]
22 | a2=uniq_array_a[i+1]
23 | group=spots.loc[spots[array_a].isin([a1, a2]),:]
24 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
25 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
26 | #print(a1, lower[np.min(group[pixel_a])], upper[np.min(group[pixel_a])])
27 | a1=uniq_array_a[-1]
28 | a2=uniq_array_a[-2]
29 | group=spots.loc[spots[array_a].isin([a1, a2]),:]
30 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
31 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
32 | #print(a1, lower[np.min(group[pixel_a])], upper[np.min(group[pixel_a])])
33 | elif shape=="square":
34 | for i in range(len(uniq_array_a)-1):
35 | a1=uniq_array_a[i]
36 | group=spots.loc[spots[array_a]==a1,:]
37 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
38 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
39 | a1=uniq_array_a[-1]
40 | group=spots.loc[spots[array_a]==a1,:]
41 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
42 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
43 | else:
44 | print("Error, unknown shape, pls specify 'square' or 'hexagon'.")
45 | lower=np.array(list(lower.items())[::-1]).astype("int32")
46 | upper=np.array(list(upper.items())).astype("int32")
47 | cnt=np.concatenate((upper, lower), axis=0)
48 | cnt=cnt.reshape(cnt.shape[0], 1, 2)
49 | if scan_x:
50 | cnt=cnt[:, : , [1, 0]]
51 | return cnt
52 |
53 | def cv2_detect_contour(img,
54 | CANNY_THRESH_1 = 100,
55 | CANNY_THRESH_2 = 200,
56 | apertureSize=5,
57 | L2gradient = True,
58 | all_cnt_info=False):
59 | if len(img.shape)==3:
60 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
61 | elif len(img.shape)==2:
62 | gray=(img*((1, 255)[np.max(img)<=1])).astype(np.uint8)
63 | else:
64 | print("Image format error!")
65 | edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2,apertureSize = apertureSize, L2gradient = L2gradient)
66 | edges = cv2.dilate(edges, None)
67 | edges = cv2.erode(edges, None)
68 | cnt_info = []
69 | cnts, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
70 | for c in cnts:
71 | cnt_info.append((c,cv2.isContourConvex(c),cv2.contourArea(c),))
72 | cnt_info = sorted(cnt_info, key=lambda c: c[2], reverse=True)
73 | cnt=cnt_info[0][0]
74 | if all_cnt_info:
75 | return cnt_info
76 | else:
77 | return cnt
78 |
79 |
80 | def cut_contour_boundary(cnt, x_min, x_max, y_min, y_max, enlarge):
81 | ret=cnt.copy()
82 | ret[:, : , 0][ret[:, : , 0]>y_max]=y_max
83 | ret[:, : , 0][ret[:, : , 0]x_max]=x_max
85 | ret[:, : , 1][ret[:, : , 1]0:
62 | dis_tmp=(nbs.to_numpy()+0.1)/np.min(nbs.to_numpy()+0.1) #avoid 0 distance
63 | if isinstance(k, int):
64 | weights=((1/(dis_tmp**k))/((1/(dis_tmp**k)).sum()))
65 | else:
66 | weights=np.exp(-dis_tmp)/np.sum(np.exp(-dis_tmp))
67 | #For superpixel have super far nbs, weights=0
68 | if np.min(nbs.values)>dis_threshold:
69 | weights=weights*0
70 | row_index=[known_adata.obs.index.get_loc(i) for i in nbs.index]
71 | sudo_adata.X[i, :]=np.dot(weights, known_adata.X[row_index,:])
72 | else:
73 | sudo_adata.X[i, :]=0
74 | return sudo_adata
75 |
76 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/models.py:
--------------------------------------------------------------------------------
1 | #from __future__ import print_function
2 | import torch
3 | import torch.nn as nn
4 | import torch.nn.functional as F
5 | import torch.optim as optim
6 | import numpy as np
7 | import torch.nn.init
8 |
9 | class Spatial_CNN(nn.Module):
10 | def __init__(self,input_dim, nChannel=100, nConv=2, kernel_size_list=[3,3],stride_list=[1,1], padding_list=[1,1]):
11 | super(Spatial_CNN, self).__init__()
12 | assert nConv==len(kernel_size_list)==len(stride_list)==len(padding_list)
13 | self.nChannel=nChannel
14 | self.nConv=nConv
15 | self.conv1 = nn.Conv2d(input_dim, self.nChannel, kernel_size=kernel_size_list[0], stride=stride_list[0], padding=padding_list[0] )
16 | self.bn1 = nn.BatchNorm2d(self.nChannel)
17 | self.conv2 = nn.ModuleList()
18 | self.bn2 = nn.ModuleList()
19 | for i in range(self.nConv-1):
20 | self.conv2.append( nn.Conv2d(self.nChannel, self.nChannel, kernel_size=kernel_size_list[i+1], stride=stride_list[i+1], padding=padding_list[i+1] ) )
21 | self.bn2.append( nn.BatchNorm2d(self.nChannel) )
22 | self.conv3 = nn.Conv2d(nChannel, nChannel, kernel_size=1, stride=1, padding=0 )
23 | self.bn3 = nn.BatchNorm2d(self.nChannel)
24 | def forward(self, x):
25 | x = self.conv1(x)
26 | x = F.relu( x )
27 | x = self.bn1(x)
28 | for i in range(self.nConv-1):
29 | x = self.conv2[i](x)
30 | x = F.relu( x )
31 | x = self.bn2[i](x)
32 | x = self.conv3(x)
33 | x = self.bn3(x)
34 | return x
35 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/tumor_edge_core.py:
--------------------------------------------------------------------------------
1 | import os, sys, csv,re, time, random
2 | import cv2
3 | import numpy as np
4 | import pandas as pd
5 | import scanpy as sc
6 | from scipy.sparse import issparse
7 | from . util import *
8 | from . contour_util import *
9 | from . ConnectedComponents import Graph
10 |
11 |
12 | def leading_edge_detection(img, pred_refined, resize_factor, target_clusters, binary):
13 | resize_width=int(img.shape[1]*resize_factor)
14 | resize_height=int(img.shape[0]*resize_factor)
15 | img_new = img.copy()
16 | img_new_resized = cv2.resize(img_new, (resize_width, resize_height))
17 | for t in target_clusters:
18 | target_img=(1*(pred_refined==t)).reshape(resize_height, resize_width)
19 | cnt_info=cv2_detect_contour((target_img==0).astype(np.uint8), apertureSize=5,L2gradient = True, all_cnt_info=True)
20 | cnt_info=list(filter(lambda x: (x[2] > 2000), cnt_info))
21 | for i in range(len(cnt_info)):
22 | cv2.drawContours(img_new_resized, [cnt_info[i][0]], -1, ([0, 0, 0]), thickness=2) #BGR
23 | return img_new_resized
24 |
25 |
26 | def tumor_edge_core_separation(img, binary, resize_factor, pred_refined, target_clusters, sudo_adata, res, shrink_rate=0.8):
27 | resize_width=int(img.shape[1]*resize_factor)
28 | resize_height=int(img.shape[0]*resize_factor)
29 | #Extract target pixels
30 | target_img=(1*(np.isin(pred_refined, target_clusters))).reshape(resize_height, resize_width)
31 | #Filter using binary filter
32 | binary_resized=cv2.resize(binary, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
33 | target_img=target_img*(binary_resized!=0)
34 | tumor_binary=cv2.resize(target_img.astype(np.uint8), ((img.shape[1], img.shape[0])))
35 | tumor_index=[]
36 | for index, row in sudo_adata.obs.iterrows():
37 | x=int(row["x"])
38 | y=int(row["y"])
39 | if tumor_binary[x, y]!=0: tumor_index.append(index)
40 | sudo_tumor=sudo_adata[sudo_adata.obs.index.isin(tumor_index)]
41 | print("Running Connected Components ...")
42 | adj=pd.DataFrame(np.zeros([sudo_tumor.shape[0], sudo_tumor.shape[0]]), columns=sudo_tumor.obs.index.tolist(), index=sudo_tumor.obs.index.tolist())
43 | for index, row in sudo_tumor.obs.iterrows():
44 | x, y=row["x"], row["y"]
45 | nbr=sudo_tumor.obs[((sudo_tumor.obs["x"]==x) & (np.abs(sudo_tumor.obs["y"]-y)<=res))| ((sudo_tumor.obs["y"]==y) & (np.abs(sudo_tumor.obs["x"]-x)<=res))]
46 | adj.loc[index, nbr.index.tolist()]=1
47 | sys.setrecursionlimit(adj.shape[0])
48 | g = Graph(V=adj.index.tolist(), adj=adj)
49 | c_c = g.ConnectedComponents()
50 | cc_info=[]
51 | for c in c_c:
52 | cc_info.append((c,len(c)))
53 | cc_info = sorted(cc_info, key=lambda c: c[1], reverse=True)
54 | print("Running Select biggest Tumor region ...")
55 | #cc_info[0][0] contains the index of all superpiexls in the biggest tumor region
56 | sudo_tumor_sub=sudo_tumor[sudo_tumor.obs.index.isin(cc_info[0][0])]
57 | adj_sub=adj.loc[cc_info[0][0], cc_info[0][0]]
58 | adj_sub=adj_sub[np.sum(adj_sub, 1)>2]
59 | sudo_tumor_sub=sudo_tumor_sub[sudo_tumor_sub.obs.index.isin(adj_sub.index.tolist())]
60 | binary_tumor = np.zeros(img.shape[0:2]).astype(np.uint8)
61 | for index, row in sudo_tumor_sub.obs.iterrows():
62 | x=int(row["x"])
63 | y=int(row["y"])
64 | binary_tumor[int(x-res/2):int(x+res/2), int(y-res/2):int(y+res/2)]=1
65 | cnt_tumor=cv2_detect_contour((binary_tumor==0).astype(np.uint8), apertureSize=5,L2gradient = True)
66 | print("Running Core and edge separation ...")
67 | cnt_tumor_reduced=scale_contour(cnt_tumor, shrink_rate)
68 | binary_core = np.zeros(img.shape[0:2]).astype(np.uint8)
69 | cv2.drawContours(binary_core, [cnt_tumor_reduced], -1, (1), thickness=-1)
70 | binary_core=binary_core*binary_tumor
71 | cnt_core=cv2_detect_contour((binary_core==0).astype(np.uint8), apertureSize=5,L2gradient = True)
72 | print("Running Create Gene Expression adata for tumor edge vs. core ...")
73 | region=[]
74 | for index, row in sudo_tumor_sub.obs.iterrows():
75 | x=int(row["x"])
76 | y=int(row["y"])
77 | region.append(("edge","core",)[binary_core[x, y]])
78 | sudo_tumor_sub.obs["region"]=region
79 | return binary_tumor, binary_core, sudo_tumor_sub
80 |
81 |
82 | def plot_tumor_edge_core(img, resize_factor, binary, binary_tumor, binary_core, color_edge=[66, 50, 225], color_core=[62, 25, 53]):
83 | resize_width=int(img.shape[1]*resize_factor)
84 | resize_height=int(img.shape[0]*resize_factor)
85 | white_ratio=0.5
86 | alpha=0.8
87 | ret_img=img.copy()
88 | cnt_tumor=cv2_detect_contour((binary_tumor==0).astype(np.uint8), apertureSize=5,L2gradient = True)
89 | cnt_core=cv2_detect_contour((binary_core==0).astype(np.uint8), apertureSize=5,L2gradient = True)
90 | ret_img[binary!=0]=ret_img[binary!=0]*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
91 | #Core and edge region
92 | target_img = np.zeros(img.shape, dtype=np.uint8)
93 | cv2.drawContours(target_img, [cnt_tumor], -1, (color_edge), thickness=-1) #BGR
94 | cv2.drawContours(target_img, [cnt_core], -1, (color_core), thickness=-1) #BGR
95 | #Overlay
96 | ret_img[binary_tumor!=0]=ret_img[binary_tumor!=0]*(1-alpha)+target_img[binary_tumor!=0]*alpha
97 | ret_img=cv2.resize(ret_img, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
98 | return ret_img
99 |
100 |
101 | def tumor_edge_core_DE(sudo_core_edge):
102 | sc.tl.rank_genes_groups(sudo_core_edge, groupby="region",reference="rest",n_genes=sudo_core_edge.shape[1], method='wilcoxon')
103 | pvals_adj1=[i[1] for i in sudo_core_edge.uns['rank_genes_groups']["pvals_adj"]]
104 | pvals_adj0=[i[0] for i in sudo_core_edge.uns['rank_genes_groups']["pvals_adj"]]
105 | genes1=[i[1] for i in sudo_core_edge.uns['rank_genes_groups']["names"]]
106 | genes0=[i[0] for i in sudo_core_edge.uns['rank_genes_groups']["names"]]
107 | if issparse(sudo_core_edge.X):
108 | obs_tidy=pd.DataFrame(sudo_core_edge.X.A)
109 | else:
110 | obs_tidy=pd.DataFrame(sudo_core_edge.X)
111 | obs_tidy.index=sudo_core_edge.obs["region"].tolist()
112 | obs_tidy.columns=sudo_core_edge.var.index.tolist()
113 | #--------Edge enriched genes
114 | obs_tidy1=obs_tidy.loc[:,genes1]
115 | # 1. compute mean value
116 | mean_obs1 = obs_tidy1.groupby(level=0).mean()
117 | mean_all1=obs_tidy1.mean()
118 | # 2. compute fraction of cells having value >0
119 | obs_bool1 = obs_tidy1.astype(bool)
120 | fraction_obs1 = obs_bool1.groupby(level=0).sum() / obs_bool1.groupby(level=0).count()
121 | # compute fold change.
122 | fold_change1 = (mean_obs1.loc["edge"] / (mean_obs1.loc["core"]+ 1e-9)).values
123 | df1 = {'edge_genes': genes1,
124 | 'in_group_fraction': fraction_obs1.loc["edge"].tolist(),
125 | "out_group_fraction":fraction_obs1.loc["core"].tolist(),
126 | "in_out_group_ratio":(fraction_obs1.loc["edge"]/fraction_obs1.loc["core"]).tolist(),
127 | "in_group_mean_exp": mean_obs1.loc["edge"].tolist(),
128 | "out_group_mean_exp": mean_obs1.loc["core"].tolist(),
129 | "fold_change":fold_change1.tolist(),
130 | "pvals_adj":pvals_adj1,
131 | "all_mean_exp": mean_all1}
132 | df1 = pd.DataFrame(data=df1)
133 | df1=df1[(df1["pvals_adj"]<0.05)]
134 | #--------Core enriched genes
135 | obs_tidy0=obs_tidy.loc[:,genes0]
136 | # 1. compute mean value
137 | mean_obs0 = obs_tidy0.groupby(level=0).mean()
138 | mean_all0=obs_tidy0.mean()
139 | # 2. compute fraction of cells having value >0
140 | obs_bool0 = obs_tidy0.astype(bool)
141 | fraction_obs0 = obs_bool0.groupby(level=0).sum() / obs_bool0.groupby(level=0).count()
142 | # compute fold change.
143 | fold_change0 = (mean_obs0.loc["core"] / (mean_obs0.loc["edge"]+ 1e-9)).values
144 | df0 = {'edge_genes': genes0,
145 | 'in_group_fraction': fraction_obs0.loc["core"].tolist(),
146 | "out_group_fraction":fraction_obs0.loc["edge"].tolist(),
147 | "in_out_group_ratio":(fraction_obs0.loc["core"]/fraction_obs0.loc["edge"]).tolist(),
148 | "in_group_mean_exp": mean_obs0.loc["core"].tolist(),
149 | "out_group_mean_exp": mean_obs0.loc["edge"].tolist(),
150 | "fold_change":fold_change0.tolist(),
151 | "pvals_adj":pvals_adj0,
152 | "all_mean_exp": mean_all0}
153 | df0 = pd.DataFrame(data=df0)
154 | df0=df0[(df0["pvals_adj"]<0.05)]
155 | return df0, df1
156 |
157 | def filter_DE_genes(df, min_all_mean_exp=0.1, min_in_out_group_ratio=1, min_in_group_fraction=0.5, min_fold_change=1.2):
158 | df_filtered=df.copy()
159 | df_filtered=df_filtered[(df_filtered["all_mean_exp"]>min_all_mean_exp) &
160 | (df_filtered["in_out_group_ratio"]>=min_in_out_group_ratio) &
161 | (df_filtered["in_group_fraction"]>min_in_group_fraction) &
162 | (df_filtered["fold_change"]>min_fold_change)]
163 | return df_filtered
164 |
165 | def plot_edge_core_enrichd_genes(img, resize_factor, binary, binary_tumor, sudo_core_edge, genes, cnt_color, plot_dir, res):
166 | resize_width=int(img.shape[1]*resize_factor)
167 | resize_height=int(img.shape[0]*resize_factor)
168 | if issparse(sudo_core_edge.X):sudo_core_edge.X=sudo_core_edge.X.A
169 | res=res*1.5
170 | white_ratio=0.5
171 | for g in genes:
172 | sudo_core_edge.obs["exp"]=sudo_core_edge.X[:,sudo_core_edge.var.index==g]
173 | sudo_core_edge.obs["relexp"]=(sudo_core_edge.obs["exp"]-np.min(sudo_core_edge.obs["exp"]))/(np.max(sudo_core_edge.obs["exp"])-np.min(sudo_core_edge.obs["exp"]))
174 | img_new=np.zeros(img.shape, dtype=np.uint8)
175 | for _, row in sudo_core_edge.obs.iterrows():
176 | x=row["x"]
177 | y=row["y"]
178 | c=row["relexp"]
179 | img_new[int(x-res/2):int(x+res/2), int(y-res/2):int(y+res/2),:]=((np.array(cnt_color(int(c*255)))[0:3])*255).astype(int)
180 | img_new=cv2.cvtColor(img_new, cv2.COLOR_RGB2BGR)
181 | #Background
182 | ret_img=img.copy()
183 | ret_img[binary!=0]=ret_img[binary!=0]*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
184 | #Overlay
185 | ret_img[binary_tumor!=0]=img_new[binary_tumor!=0]
186 | ret_img=cv2.resize(ret_img, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
187 | cv2.imwrite(plot_dir+g+'.jpg', ret_img)
188 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/TESLA_package/TESLA/util.py:
--------------------------------------------------------------------------------
1 | import scanpy as sc
2 | import pandas as pd
3 | import numpy as np
4 | import scipy
5 | import os
6 | from anndata import AnnData,read_csv,read_text,read_mtx
7 | from scipy.sparse import issparse
8 |
9 | def prefilter_cells(adata,min_counts=None,max_counts=None,min_genes=200,max_genes=None):
10 | if min_genes is None and min_counts is None and max_genes is None and max_counts is None:
11 | raise ValueError('Provide one of min_counts, min_genes, max_counts or max_genes.')
12 | id_tmp=np.asarray([True]*adata.shape[0],dtype=bool)
13 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,min_genes=min_genes)[0]) if min_genes is not None else id_tmp
14 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,max_genes=max_genes)[0]) if max_genes is not None else id_tmp
15 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,min_counts=min_counts)[0]) if min_counts is not None else id_tmp
16 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,max_counts=max_counts)[0]) if max_counts is not None else id_tmp
17 | adata._inplace_subset_obs(id_tmp)
18 | adata.raw=sc.pp.log1p(adata,copy=True) #check the rowname
19 | print("the var_names of adata.raw: adata.raw.var_names.is_unique=:",adata.raw.var_names.is_unique)
20 |
21 |
22 | def prefilter_genes(adata,min_counts=None,max_counts=None,min_cells=10,max_cells=None):
23 | if min_cells is None and min_counts is None and max_cells is None and max_counts is None:
24 | raise ValueError('Provide one of min_counts, min_genes, max_counts or max_genes.')
25 | id_tmp=np.asarray([True]*adata.shape[1],dtype=bool)
26 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,min_cells=min_cells)[0]) if min_cells is not None else id_tmp
27 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,max_cells=max_cells)[0]) if max_cells is not None else id_tmp
28 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,min_counts=min_counts)[0]) if min_counts is not None else id_tmp
29 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,max_counts=max_counts)[0]) if max_counts is not None else id_tmp
30 | adata._inplace_subset_var(id_tmp)
31 |
32 |
33 | def prefilter_specialgenes(adata,Gene1Pattern="ERCC",Gene2Pattern="MT-"):
34 | id_tmp1=np.asarray([not str(name).startswith(Gene1Pattern) for name in adata.var_names],dtype=bool)
35 | id_tmp2=np.asarray([not str(name).startswith(Gene2Pattern) for name in adata.var_names],dtype=bool)
36 | id_tmp=np.logical_and(id_tmp1,id_tmp2)
37 | adata._inplace_subset_var(id_tmp)
38 |
39 | def relative_func(expres):
40 | #expres: an array counts expression for a gene
41 | maxd = np.max(expres) - np.min(expres)
42 | min_exp=np.min(expres)
43 | rexpr = (expres - min_exp)/maxd
44 | return rexpr
45 |
46 | def plot_relative_exp(input_adata, gene, x_name, y_name,color,use_raw=False, spot_size=200000):
47 | adata=input_adata.copy()
48 | if use_raw:
49 | X=adata.raw.X
50 | else:
51 | X=adata.X
52 | if issparse(X):
53 | X=pd.DataFrame(X.A)
54 | else:
55 | X=pd.DataFrame(X)
56 | X.index=adata.obs.index
57 | X.columns=adata.var.index
58 | rexpr=relative_func(X.loc[:,gene])
59 | adata.obs["rexpr"]=rexpr
60 | fig=sc.pl.scatter(adata,x=x_name,y=y_name,color="rexpr",title=gene+"_rexpr",color_map=color,show=False,size=spot_size/adata.shape[0])
61 | return fig
62 |
63 | def plot_log_exp(input_adata, gene, x_name, y_name,color,use_raw=False):
64 | adata=input_adata.copy()
65 | if use_raw:
66 | X=adata.X
67 | else:
68 | X=adata.raw.X
69 | if issparse(X):
70 | X=pd.DataFrame(X.A)
71 | else:
72 | X=pd.DataFrame(X)
73 | X.index=adata.obs.index
74 | X.columns=adata.var.index
75 | adata.obs["log"]=np.log((X.loc[:,gene]+1).tolist())
76 | fig=sc.pl.scatter(adata,x=x_name,y=y_name,color="log",title=gene+"_log",color_map=color,show=False,size=200000/adata.shape[0])
77 | return fig
78 |
79 | def refine_clusters(pred, resize_height, resize_width, threshold, radius):
80 | pixel_num=pd.Series(pred).value_counts()
81 | clusters=pixel_num.index.tolist()
82 | reorder_map={}
83 | for i in range(pixel_num.shape[0]):
84 | reorder_map[clusters[i]]=i
85 | pred_reordered=pd.Series(pred).replace(reorder_map).to_numpy()
86 | pixel_num=pd.Series(pred_reordered).value_counts()
87 | # Number of clusters
88 | nLabels = len(np.unique(pred_reordered))
89 | # Number of main clusters
90 | mainLabels=(pd.Series(pred_reordered).value_counts()>=threshold).sum()
91 | #------------- Refine clusters ---------------------
92 | main_clusters=pixel_num.index[pixel_num>=threshold].tolist()
93 | minor_clusters=pixel_num.index[pixel_num0:
105 | replace_map[i]=nbs_num.index[ nbs_num.index.isin(main_clusters) ][ 0 ]
106 | pred_refined=pd.Series(pred_reordered).replace(replace_map).to_numpy()
107 | return pred_refined
108 |
--------------------------------------------------------------------------------
/TESLA_package/TESLAforST.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 2.1
2 | Name: TESLAforST
3 | Version: 1.2.2
4 | Summary: TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics
5 | Home-page: https://github.com/jianhuupenn/TESLA
6 | Author: Jian Hu
7 | Author-email: jianhu@pennmedicine.upenn.edu
8 | License: UNKNOWN
9 | Description: # TESLA
10 |
11 | ## TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics
12 |
13 | ### Jian Hu,*, Kyle Coleman, Edward B. Lee, Humam Kadara, Linghua Wang,*, Mingyao Li,*
14 |
15 | TESLA is a machine learning framework for multi-level tissue annotation with pixel-level resolution in ST. TESLA integrates histological information with gene expression to annotate heterogeneous immune and tumor cells directly on the histology image. TESLA further detects unique Tumor Microenvironment (TME) features such as tertiary lymphoid structures and differential transcriptome programs in the core or edge of a tumor, which represent a promising avenue for understanding the spatial architecture of the TME. Although we illustrated the applications in cancer, TESLA can also be applied to other diseases.
16 | For more info, please go to:
17 | https://github.com/jianhuupenn/TESLA
18 | Platform: UNKNOWN
19 | Classifier: Programming Language :: Python :: 3
20 | Classifier: License :: OSI Approved :: MIT License
21 | Classifier: Operating System :: OS Independent
22 | Requires-Python: >=3.6
23 | Description-Content-Type: text/markdown
24 |
--------------------------------------------------------------------------------
/TESLA_package/TESLAforST.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | README.md
2 | setup.py
3 | TESLA/ConnectedComponents.py
4 | TESLA/TESLA.py
5 | TESLA/TLS_detection.py
6 | TESLA/__init__.py
7 | TESLA/annotation.py
8 | TESLA/calculate_dis.py
9 | TESLA/contour_util.py
10 | TESLA/imputation.py
11 | TESLA/models.py
12 | TESLA/tumor_edge_core.py
13 | TESLA/util.py
14 | TESLAforST.egg-info/PKG-INFO
15 | TESLAforST.egg-info/SOURCES.txt
16 | TESLAforST.egg-info/dependency_links.txt
17 | TESLAforST.egg-info/requires.txt
18 | TESLAforST.egg-info/top_level.txt
--------------------------------------------------------------------------------
/TESLA_package/TESLAforST.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/TESLA_package/TESLAforST.egg-info/requires.txt:
--------------------------------------------------------------------------------
1 | torch
2 | pandas
3 | numpy
4 | scipy
5 | scanpy
6 | anndata
7 | sklearn
8 | numba
9 |
--------------------------------------------------------------------------------
/TESLA_package/TESLAforST.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | TESLA
2 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/ConnectedComponents.py:
--------------------------------------------------------------------------------
1 | #Get connected components in an undirected graph
2 | import pandas as pd
3 | import numpy as np
4 | class Graph:
5 | # init function to declare class variables
6 | def __init__(self, V, adj):
7 | self.V = V
8 | self.adj = adj
9 | def DFSUtil(self, v, tmp, visited):
10 | # Mark the current vertex as visited
11 | visited[v] = 1
12 | # Store the vertex to list
13 | tmp.append(v)
14 | # Repeat for all vertices adjacent to this vertex v
15 | nbr=self.adj[v][self.adj[v]==1].index.tolist()
16 | for i in nbr:
17 | if visited[i] == 0:
18 | tmp = self.DFSUtil(i, tmp, visited)
19 | return tmp
20 | def ConnectedComponents(self):
21 | visited = pd.Series([0]* len(self.V), index=self.V)
22 | cc = []
23 | for v in self.V:
24 | if visited[v] == 0:
25 | tmp = []
26 | cc.append(self.DFSUtil(v, tmp, visited))
27 | return cc
28 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/TESLA.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | import time
4 | import cv2
5 | import argparse
6 | import torch
7 | import torch.nn as nn
8 | import torch.nn.functional as F
9 | import torch.optim as optim
10 | import torch.nn.init
11 | from torch.autograd import Variable
12 | from . models import Spatial_CNN
13 |
14 | class TESLA(object):
15 | def __init__(self):
16 | super(TESLA, self).__init__()
17 |
18 | def train(self, input,
19 | use_cuda=False,
20 | train_refine=True,
21 | radius=3,
22 | nChannel=100,
23 | lr=0.1,
24 | minLabels=20,
25 | maxIter=30,
26 | stepsize_sim=1,
27 | stepsize_con=10,
28 | threshold=1000,
29 | plot_intermedium=False,
30 | plot_dir="./"):
31 | self.use_cuda=use_cuda
32 | self.train_refine=train_refine
33 | self.radius=radius
34 | self.nChannel=nChannel
35 | self.lr=lr
36 | self.minLabels=minLabels
37 | self.maxIter=maxIter
38 | self.stepsize_sim=stepsize_sim
39 | self.stepsize_con=stepsize_con
40 | self.threshold=threshold
41 | self.plot_intermedium=plot_intermedium
42 | self.resize_height=input.shape[0]
43 | self.resize_width=input.shape[1]
44 | input = torch.from_numpy( np.array([input.transpose( (2, 0, 1) ).astype('float32')]) )
45 | if use_cuda:
46 | input = input.cuda()
47 | input = Variable(input)
48 | #--------------------------------------- Train ---------------------------------------
49 | self.model = Spatial_CNN(input.size(1), nConv=2, nChannel=self.nChannel, kernel_size_list=[5, 5],stride_list=[1, 1], padding_list=[2, 2])
50 | if use_cuda:
51 | self.model.cuda()
52 | # Similarity loss definition
53 | loss_fn = torch.nn.CrossEntropyLoss()
54 | # Continuity loss definition
55 | loss_hpy = torch.nn.L1Loss(size_average = True)
56 | loss_hpz = torch.nn.L1Loss(size_average = True)
57 | HPy_target = torch.zeros(self.resize_height-1, self.resize_width, nChannel)
58 | HPz_target = torch.zeros(self.resize_height, self.resize_width-1, nChannel)
59 | if use_cuda:
60 | HPy_target = HPy_target.cuda()
61 | HPz_target = HPz_target.cuda()
62 | optimizer = optim.SGD(self.model.parameters(), lr=lr, momentum=0.9)
63 | label_colours = np.random.randint(255,size=(100,3))
64 | start_time = time.time()
65 | self.model.train()
66 | for batch_idx in range(maxIter):
67 | # forwarding
68 | optimizer.zero_grad()
69 | output = self.model( input )[ 0 ]
70 | output = output.permute( 1, 2, 0 ).contiguous().view( -1, nChannel )
71 | outputHP = output.reshape( (self.resize_height, self.resize_width, nChannel) )
72 | HPy = outputHP[1:, :, :] - outputHP[0:-1, :, :]
73 | HPz = outputHP[:, 1:, :] - outputHP[:, 0:-1, :]
74 | lhpy = loss_hpy(HPy,HPy_target)
75 | lhpz = loss_hpz(HPz,HPz_target)
76 | _, target = torch.max( output, 1 )
77 | img_target = target.data.cpu().numpy()
78 | # Total number of clusters
79 | nLabels = len(np.unique(img_target))
80 | # Number of main clusters
81 | mainLabels=(pd.Series(img_target).value_counts()>=threshold).sum()
82 | #--------------------------Refine during training----------------------------------------
83 | if train_refine:
84 | pixel_num=pd.Series(img_target).value_counts()
85 | main_clusters=pixel_num.index[pixel_num>=threshold].tolist()
86 | minor_clusters=pixel_num.index[pixel_num0:
98 | replace_map[i]=nbs_num.index[nbs_num.index.isin(main_clusters)][0]
99 | target=torch.from_numpy(pd.Series(img_target).replace(replace_map).to_numpy())
100 | #------------------------------------------------------------------
101 | loss1 = stepsize_sim * loss_fn(output, target)
102 | loss2 = stepsize_con * (lhpy + lhpz)
103 | loss=loss1+loss2
104 | loss.backward()
105 | optimizer.step()
106 | print (batch_idx, '/', maxIter, '|', ' label num :', nLabels, '|',' main clusters :',mainLabels,' | feature loss :', loss1.item(),' | spatial loss :', loss2.item())
107 | print("--- %s seconds ---" % (time.time() - start_time))
108 | if batch_idx%2==0:
109 | output = self.model(input)[0]
110 | output = output.permute(1, 2, 0).contiguous().view(-1, nChannel)
111 | _, target = torch.max(output, 1)
112 | img_target = target.data.cpu().numpy()
113 | img_target_rgb = np.array([label_colours[ c % label_colours.shape[0]] for c in img_target])
114 | img_target_rgb = img_target_rgb.reshape(self.resize_height, self.resize_width, 3).astype( np.uint8 )
115 | if self.plot_intermedium:
116 | cv2.imwrite(plot_dir+str(batch_idx)+"_nL"+str(nLabels)+"_mL"+str(mainLabels)+".png", img_target_rgb)
117 | if mainLabels <= minLabels:
118 | print ("mainLabels", mainLabels, "reached minLabels", minLabels, ".")
119 | break
120 |
121 | def predict(self, input):
122 | input = torch.from_numpy( np.array([input.transpose( (2, 0, 1) ).astype('float32')]) )
123 | output = self.model( input )[ 0 ]
124 | output = output.permute( 1, 2, 0 ).contiguous().view( -1, self.nChannel )
125 | prob=output.data.cpu().numpy()
126 | prob=np.divide(np.exp(prob), np.sum(np.exp(prob), 1).reshape(-1, 1))
127 | _, pred = torch.max( output, 1 )
128 | pred = pred.data.cpu().numpy()
129 | return prob, pred
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/TLS_detection.py:
--------------------------------------------------------------------------------
1 | import os, sys, csv,re, time, random
2 | import cv2
3 | import numpy as np
4 | import pandas as pd
5 | import scanpy as sc
6 | from scipy.sparse import issparse
7 | from . util import *
8 | from . contour_util import *
9 |
10 | def TLS_detection( pred_refined_list, cluster_density_list, num_required, cnt_color, pooling="min"):
11 | pred_TLS=np.zeros([len(pred_refined_list[0]), len(pred_refined_list)])
12 | for i in range(len(pred_refined_list)):
13 | tmp=np.zeros(pred_refined_list[i].shape)
14 | for k, v in cluster_density_list[i].items():
15 | tmp[pred_refined_list[i]==k]=v/np.max(list(cluster_density_list[i].values()))
16 | pred_TLS[:,i]=tmp
17 | target = np.partition(pred_TLS, -num_required, axis=1)[:,-num_required:] #Select top num_required
18 | if pooling=="mean":
19 | target=np.mean(target, axis=1)
20 | elif pooling=="min":
21 | target=np.min(target, axis=1)
22 | else:
23 | print("Error! Pooling logic not understood.")
24 | target=(target-np.min(target))/(np.max(target)-np.min(target))
25 | target[target<0.5]=0
26 | return target
27 |
28 |
29 | def plot_TLS_score(img, resize_factor, binary,target, cnt_color):
30 | resize_width=int(img.shape[1]*resize_factor)
31 | resize_height=int(img.shape[0]*resize_factor)
32 | binary_resized=cv2.resize(binary, (resize_width, resize_height))
33 | img_resized =cv2.resize(img, (resize_width, resize_height))
34 | target_img=target.reshape(resize_height, resize_width)
35 | target_img_rgb=(cnt_color((target*255).astype("int"))[:, 0:3]*255).reshape(resize_height, resize_width,3).astype( np.uint8 )
36 | target_img_rgb=(cnt_color((target*255).astype("int"))[:, 0:3]*255).reshape(resize_height, resize_width,3).astype( np.uint8 )
37 | target_img_rgb=cv2.cvtColor(target_img_rgb, cv2.COLOR_RGB2BGR)
38 | ret_img=img_resized.copy()
39 | #Whiten
40 | white_ratio=0.5
41 | ret_img[binary_resized!=0]=ret_img[binary_resized!=0]*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
42 | ret_img[target_img!=0]=target_img_rgb[target_img!=0]
43 | ret_img[binary_resized==0]=255
44 | return ret_img
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '1.2.2'
2 | from . TESLA import *
3 | from . util import *
4 | from . contour_util import *
5 | from . calculate_dis import *
6 | from . imputation import *
7 | from . annotation import *
8 | from . contour_util import *
9 | from . TLS_detection import *
10 | from . tumor_edge_core import *
11 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/annotation.py:
--------------------------------------------------------------------------------
1 | import os,csv,re, time, random
2 | import cv2
3 | import numpy as np
4 | import pandas as pd
5 | import torch
6 | import scanpy as sc
7 | from . util import *
8 | from . contour_util import *
9 | from . calculate_dis import *
10 | from . TESLA import TESLA
11 |
12 |
13 | def annotation(img,
14 | sudo_adata,
15 | genes,
16 | resize_factor,
17 | binary,
18 | res=50,
19 | num_required=1,
20 | pooling="min",
21 | rseed=100,
22 | tseed=100,
23 | nseed=100,
24 | nChannel=100,
25 | threshold=1000,
26 | radius=3,
27 | minLabels=30,
28 | train_refine=True,
29 | plot_intermedium=False,
30 | target_size="small",
31 | min_UMI=0):
32 | #-------------------------------Image band-------------------------------------------------#
33 | if target_size=="small":
34 | target_ratio=1/2
35 | elif target_size=="large":
36 | target_ratio=1/3
37 | else:
38 | print("target_size not valid, Please specify (small / large).Use default small")
39 | target_ratio=1/2
40 | print("Computing image band...")
41 | resize_width=int(img.shape[1]*resize_factor)
42 | resize_height=int(img.shape[0]*resize_factor)
43 | img_resized=(img * np.dstack([(binary!=0)]*3)).astype(np.uint8)
44 | img_resized = cv2.resize(img_resized, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
45 | gray_resized = cv2.cvtColor(img_resized,cv2.COLOR_BGR2GRAY)
46 | gray_resized=gray_resized.reshape(list(gray_resized.shape)+[1])
47 | binary_resized=cv2.resize(binary, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
48 | img_band1=(gray_resized-np.min(gray_resized))/(np.max(gray_resized)-np.min(gray_resized))
49 | #-------------------------------Gene band-------------------------------------------------#
50 | print("Computing gene band...")
51 | genes=list(set([i for i in genes if i in sudo_adata.var.index ]))
52 | assert num_required<=len(genes)
53 | tmp=sudo_adata.X[:,sudo_adata.var.index.isin(genes)]
54 | tmp=(tmp-np.min(tmp, 0))/(np.max(tmp, 0)-np.min(tmp, 0))
55 | tmp = np.partition(tmp, -num_required, axis=1)[:,-num_required:] #Select top num_required
56 | if pooling=="mean":
57 | sudo_adata.obs["meta"]=np.mean(tmp, axis=1)
58 | elif pooling=="min":
59 | sudo_adata.obs["meta"]=np.min(tmp, axis=1)
60 | else:
61 | print("Error! Pooling logic not understood.")
62 | gene_img=np.zeros(list(img.shape[0:2])+[1])
63 | for _, row in sudo_adata.obs.iterrows():
64 | x=row["x"]
65 | y=row["y"]
66 | exp=row["meta"]
67 | gene_img[int(x-res/2):int(x+res/2), int(y-res/2):int(y+res/2),0]=exp
68 | gene_img[binary==0]=0
69 | gene_band1=gene_img[:,:,0]
70 | #Filter on min UMI
71 | gene_band1[gene_band1<=np.log(min_UMI+1)]=0
72 | gene_band1=cv2.resize(gene_band1, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
73 | gene_band1=(gene_band1-np.min(gene_band1))/(np.max(gene_band1)-np.min(gene_band1))
74 | gene_band1=gene_band1.reshape(list(gene_band1.shape)+[1])
75 | #-------------------------------TESLA-------------------------------------------------#
76 | print("Running TESLA...")
77 | assert np.max(img_band1)==1 and np.min(img_band1)==0 and np.max(gene_band1)==1 and np.min(gene_band1)==0
78 | data=np.concatenate((img_band1, gene_band1), axis=2)
79 | random.seed(rseed)
80 | torch.manual_seed(tseed)
81 | np.random.seed(nseed)
82 | tesla=TESLA()
83 | tesla.train(input=data, use_cuda=False, train_refine=train_refine, radius=radius, nChannel=nChannel, lr=0.1,
84 | minLabels=minLabels, maxIter=30, stepsize_sim=1, stepsize_con=5, threshold=threshold, plot_intermedium=plot_intermedium, plot_dir=None)
85 | prob, pred = tesla.predict(data)
86 | pred_refined=refine_clusters(pred=pred, resize_height=resize_height, resize_width=resize_width, threshold=threshold, radius=radius)
87 | nLabels=len(np.unique(pred))
88 | mainLabels=len(np.unique(pred_refined))
89 | #-----------------------------------Find target cluster---------------------------#
90 | print("Finding target clusters...")
91 | marker=gene_band1.flatten()
92 | clusters=pred_refined.flatten()
93 | c_m={} #cluster_marker expression
94 | for i in np.unique(clusters):
95 | c_m[i]=np.mean(marker[clusters==i])
96 | c_m = sorted(c_m.items(), key=lambda x: x[1], reverse=True)
97 | target_clusters=list(filter(lambda x: (x[1] > c_m[0][1]*target_ratio), c_m))
98 | target_clusters=[x[0] for x in target_clusters]
99 | print("c_m:\n", c_m, "\n", "Target clusters:\n", target_clusters, sep = '')
100 | return pred_refined, target_clusters, c_m
101 |
102 | def visualize_annotation(pred_refined,
103 | target_clusters,
104 | c_m,
105 | img,
106 | binary,
107 | resize_factor,
108 | cnt_color=clr.LinearSegmentedColormap.from_list('red', ["#EAE7CC", '#BA0000'], N=256)):
109 | resize_width=int(img.shape[1]*resize_factor)
110 | resize_height=int(img.shape[0]*resize_factor)
111 | binary_resized=cv2.resize(binary, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
112 | background = cv2.resize(img, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
113 | ret_img=(background.copy()).astype(np.uint8)
114 | alpha=0.8
115 | #Whiten
116 | white_ratio=0.5
117 | ret_img=ret_img*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
118 | for i in range(len(target_clusters)):
119 | color=((np.array(cnt_color(int(c_m[i][1]/c_m[0][1]*255)))[0:3])*255).astype(int)[::-1]
120 | target_img=(1*(pred_refined==target_clusters[i])).reshape(resize_height, resize_width)
121 | target_img[binary_resized==0]=0
122 | ret_img[target_img!=0]=ret_img[target_img!=0]*(1-alpha)+np.array(color)*(alpha)
123 | return ret_img
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/calculate_dis.py:
--------------------------------------------------------------------------------
1 | import os,csv,re
2 | import pandas as pd
3 | import numpy as np
4 | import scanpy as sc
5 | import math
6 | import matplotlib.colors as clr
7 | import matplotlib.pyplot as plt
8 |
9 | def distance(t1,t2):
10 | sum=((t1-t2)**2).sum()
11 | return math.sqrt(sum)
12 |
13 | def calculate_dis_matrix(x, y, x_pixel=None, y_pixel=None, image=None, beta=49, alpha=1, histology=True):
14 | #x,y,x_pixel, y_pixel are lists
15 | dis=np.zeros((len(x),len(x)))
16 | if histology:
17 | assert (x_pixel is not None) & (x_pixel is not None) & (image is not None)
18 | assert (len(x)==len(x_pixel)) & (len(y)==len(y_pixel))
19 | print("Calculateing distance matrix using histology image...")
20 | #beta to control the range of neighbourhood when calculate grey vale for one spot
21 | #alpha to control the color scale
22 | beta_half=round(beta/2)
23 | g=[]
24 | for i in range(len(x_pixel)):
25 | max_x=image.shape[0]
26 | max_y=image.shape[1]
27 | nbs=image[max(0,x_pixel[i]-beta_half):min(max_x,x_pixel[i]+beta_half+1),max(0,y_pixel[i]-beta_half):min(max_y,y_pixel[i]+beta_half+1)]
28 | g.append(np.mean(np.mean(nbs,axis=0),axis=0))
29 | c0, c1, c2=[], [], []
30 | for i in g:
31 | c0.append(i[0])
32 | c1.append(i[1])
33 | c2.append(i[2])
34 | c0=np.array(c0)
35 | c1=np.array(c1)
36 | c2=np.array(c2)
37 | print("Var of c0,c1,c2 = ", np.var(c0),np.var(c1),np.var(c2))
38 | c3=(c0*np.var(c0)+c1*np.var(c1)+c2*np.var(c2))/(np.var(c0)+np.var(c1)+np.var(c2))
39 | c4=(c3-np.mean(c3))/np.std(c3)
40 | z_scale=np.max([np.std(x), np.std(y)])*alpha
41 | z=c4*z_scale
42 | z=z.tolist()
43 | print("Var of x,y,z = ", np.var(x),np.var(y),np.var(z))
44 | for i in range(len(x)):
45 | if i%500==0:
46 | print("Calculating spot ", i, "/", len(x))
47 | for j in range(i, len(x)):
48 | cord1=np.array([x[i],y[i],z[i]])
49 | cord2=np.array([x[j],y[j],z[j]])
50 | dis[i][j]=dis[j][i]=distance(cord1, cord2)
51 | return dis, z
52 | else:
53 | print("Calculateing distance matrix using xy only...")
54 | for i in range(len(x)):
55 | if i%50==0:
56 | print("Calculating spot ", i, "/", len(x))
57 | for j in range(i, len(x)):
58 | cord1=np.array([x[i],y[i]])
59 | cord2=np.array([x[j],y[j]])
60 | dis[i][j]=dis[j][i]=distance(cord1, cord2)
61 | return dis
62 |
63 | def extract_color(x_pixel=None, y_pixel=None, image=None, beta=49, RGB=True):
64 | if RGB:
65 | #beta to control the range of neighbourhood when calculate grey vale for one spot
66 | beta_half=round(beta/2)
67 | g=[]
68 | for i in range(len(x_pixel)):
69 | max_x=image.shape[0]
70 | max_y=image.shape[1]
71 | nbs=image[max(0,x_pixel[i]-beta_half):min(max_x,x_pixel[i]+beta_half+1),max(0,y_pixel[i]-beta_half):min(max_y,y_pixel[i]+beta_half+1)]
72 | g.append(np.mean(np.mean(nbs,axis=0),axis=0))
73 | c0, c1, c2=[], [], []
74 | for i in g:
75 | c0.append(i[0])
76 | c1.append(i[1])
77 | c2.append(i[2])
78 | c0=np.array(c0)
79 | c1=np.array(c1)
80 | c2=np.array(c2)
81 | c3=(c0*np.var(c0)+c1*np.var(c1)+c2*np.var(c2))/(np.var(c0)+np.var(c1)+np.var(c2))
82 | else:
83 | beta_half=round(beta/2)
84 | g=[]
85 | for i in range(len(x_pixel)):
86 | max_x=image.shape[0]
87 | max_y=image.shape[1]
88 | nbs=image[max(0,x_pixel[i]-beta_half):min(max_x,x_pixel[i]+beta_half+1),max(0,y_pixel[i]-beta_half):min(max_y,y_pixel[i]+beta_half+1)]
89 | g.append(np.mean(nbs))
90 | c3=np.array(g)
91 | return c3
92 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/contour_util.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import cv2
4 |
5 | def scan_contour(spots, scan_x=True, shape="hexagon"):
6 | #shape="hexagon" # For 10X Vsium, shape="square" for ST data
7 | if scan_x:
8 | array_a="array_x"
9 | array_b="array_y"
10 | pixel_a="pixel_x"
11 | pixel_b="pixel_y"
12 | else:
13 | array_a="array_y"
14 | array_b="array_x"
15 | pixel_a="pixel_y"
16 | pixel_b="pixel_x"
17 | upper, lower={}, {}
18 | uniq_array_a=sorted(set(spots[array_a]))
19 | if shape=="hexagon":
20 | for i in range(len(uniq_array_a)-1):
21 | a1=uniq_array_a[i]
22 | a2=uniq_array_a[i+1]
23 | group=spots.loc[spots[array_a].isin([a1, a2]),:]
24 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
25 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
26 | #print(a1, lower[np.min(group[pixel_a])], upper[np.min(group[pixel_a])])
27 | a1=uniq_array_a[-1]
28 | a2=uniq_array_a[-2]
29 | group=spots.loc[spots[array_a].isin([a1, a2]),:]
30 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
31 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
32 | #print(a1, lower[np.min(group[pixel_a])], upper[np.min(group[pixel_a])])
33 | elif shape=="square":
34 | for i in range(len(uniq_array_a)-1):
35 | a1=uniq_array_a[i]
36 | group=spots.loc[spots[array_a]==a1,:]
37 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
38 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
39 | a1=uniq_array_a[-1]
40 | group=spots.loc[spots[array_a]==a1,:]
41 | lower[np.min(group[pixel_a])]=np.min(group[pixel_b])
42 | upper[np.min(group[pixel_a])]=np.max(group[pixel_b])
43 | else:
44 | print("Error, unknown shape, pls specify 'square' or 'hexagon'.")
45 | lower=np.array(list(lower.items())[::-1]).astype("int32")
46 | upper=np.array(list(upper.items())).astype("int32")
47 | cnt=np.concatenate((upper, lower), axis=0)
48 | cnt=cnt.reshape(cnt.shape[0], 1, 2)
49 | if scan_x:
50 | cnt=cnt[:, : , [1, 0]]
51 | return cnt
52 |
53 | def cv2_detect_contour(img,
54 | CANNY_THRESH_1 = 100,
55 | CANNY_THRESH_2 = 200,
56 | apertureSize=5,
57 | L2gradient = True,
58 | all_cnt_info=False):
59 | if len(img.shape)==3:
60 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
61 | elif len(img.shape)==2:
62 | gray=(img*((1, 255)[np.max(img)<=1])).astype(np.uint8)
63 | else:
64 | print("Image format error!")
65 | edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2,apertureSize = apertureSize, L2gradient = L2gradient)
66 | edges = cv2.dilate(edges, None)
67 | edges = cv2.erode(edges, None)
68 | cnt_info = []
69 | cnts, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
70 | for c in cnts:
71 | cnt_info.append((c,cv2.isContourConvex(c),cv2.contourArea(c),))
72 | cnt_info = sorted(cnt_info, key=lambda c: c[2], reverse=True)
73 | cnt=cnt_info[0][0]
74 | if all_cnt_info:
75 | return cnt_info
76 | else:
77 | return cnt
78 |
79 |
80 | def cut_contour_boundary(cnt, x_min, x_max, y_min, y_max, enlarge):
81 | ret=cnt.copy()
82 | ret[:, : , 0][ret[:, : , 0]>y_max]=y_max
83 | ret[:, : , 0][ret[:, : , 0]x_max]=x_max
85 | ret[:, : , 1][ret[:, : , 1] 2000), cnt_info))
21 | for i in range(len(cnt_info)):
22 | cv2.drawContours(img_new_resized, [cnt_info[i][0]], -1, ([0, 0, 0]), thickness=2) #BGR
23 | return img_new_resized
24 |
25 |
26 | def tumor_edge_core_separation(img, binary, resize_factor, pred_refined, target_clusters, sudo_adata, res, shrink_rate=0.8):
27 | resize_width=int(img.shape[1]*resize_factor)
28 | resize_height=int(img.shape[0]*resize_factor)
29 | #Extract target pixels
30 | target_img=(1*(np.isin(pred_refined, target_clusters))).reshape(resize_height, resize_width)
31 | #Filter using binary filter
32 | binary_resized=cv2.resize(binary, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
33 | target_img=target_img*(binary_resized!=0)
34 | tumor_binary=cv2.resize(target_img.astype(np.uint8), ((img.shape[1], img.shape[0])))
35 | tumor_index=[]
36 | for index, row in sudo_adata.obs.iterrows():
37 | x=int(row["x"])
38 | y=int(row["y"])
39 | if tumor_binary[x, y]!=0: tumor_index.append(index)
40 | sudo_tumor=sudo_adata[sudo_adata.obs.index.isin(tumor_index)]
41 | print("Running Connected Components ...")
42 | adj=pd.DataFrame(np.zeros([sudo_tumor.shape[0], sudo_tumor.shape[0]]), columns=sudo_tumor.obs.index.tolist(), index=sudo_tumor.obs.index.tolist())
43 | for index, row in sudo_tumor.obs.iterrows():
44 | x, y=row["x"], row["y"]
45 | nbr=sudo_tumor.obs[((sudo_tumor.obs["x"]==x) & (np.abs(sudo_tumor.obs["y"]-y)<=res))| ((sudo_tumor.obs["y"]==y) & (np.abs(sudo_tumor.obs["x"]-x)<=res))]
46 | adj.loc[index, nbr.index.tolist()]=1
47 | sys.setrecursionlimit(adj.shape[0])
48 | g = Graph(V=adj.index.tolist(), adj=adj)
49 | c_c = g.ConnectedComponents()
50 | cc_info=[]
51 | for c in c_c:
52 | cc_info.append((c,len(c)))
53 | cc_info = sorted(cc_info, key=lambda c: c[1], reverse=True)
54 | print("Running Select biggest Tumor region ...")
55 | #cc_info[0][0] contains the index of all superpiexls in the biggest tumor region
56 | sudo_tumor_sub=sudo_tumor[sudo_tumor.obs.index.isin(cc_info[0][0])]
57 | adj_sub=adj.loc[cc_info[0][0], cc_info[0][0]]
58 | adj_sub=adj_sub[np.sum(adj_sub, 1)>2]
59 | sudo_tumor_sub=sudo_tumor_sub[sudo_tumor_sub.obs.index.isin(adj_sub.index.tolist())]
60 | binary_tumor = np.zeros(img.shape[0:2]).astype(np.uint8)
61 | for index, row in sudo_tumor_sub.obs.iterrows():
62 | x=int(row["x"])
63 | y=int(row["y"])
64 | binary_tumor[int(x-res/2):int(x+res/2), int(y-res/2):int(y+res/2)]=1
65 | cnt_tumor=cv2_detect_contour((binary_tumor==0).astype(np.uint8), apertureSize=5,L2gradient = True)
66 | print("Running Core and edge separation ...")
67 | cnt_tumor_reduced=scale_contour(cnt_tumor, shrink_rate)
68 | binary_core = np.zeros(img.shape[0:2]).astype(np.uint8)
69 | cv2.drawContours(binary_core, [cnt_tumor_reduced], -1, (1), thickness=-1)
70 | binary_core=binary_core*binary_tumor
71 | cnt_core=cv2_detect_contour((binary_core==0).astype(np.uint8), apertureSize=5,L2gradient = True)
72 | print("Running Create Gene Expression adata for tumor edge vs. core ...")
73 | region=[]
74 | for index, row in sudo_tumor_sub.obs.iterrows():
75 | x=int(row["x"])
76 | y=int(row["y"])
77 | region.append(("edge","core",)[binary_core[x, y]])
78 | sudo_tumor_sub.obs["region"]=region
79 | return binary_tumor, binary_core, sudo_tumor_sub
80 |
81 |
82 | def plot_tumor_edge_core(img, resize_factor, binary, binary_tumor, binary_core, color_edge=[66, 50, 225], color_core=[62, 25, 53]):
83 | resize_width=int(img.shape[1]*resize_factor)
84 | resize_height=int(img.shape[0]*resize_factor)
85 | white_ratio=0.5
86 | alpha=0.8
87 | ret_img=img.copy()
88 | cnt_tumor=cv2_detect_contour((binary_tumor==0).astype(np.uint8), apertureSize=5,L2gradient = True)
89 | cnt_core=cv2_detect_contour((binary_core==0).astype(np.uint8), apertureSize=5,L2gradient = True)
90 | ret_img[binary!=0]=ret_img[binary!=0]*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
91 | #Core and edge region
92 | target_img = np.zeros(img.shape, dtype=np.uint8)
93 | cv2.drawContours(target_img, [cnt_tumor], -1, (color_edge), thickness=-1) #BGR
94 | cv2.drawContours(target_img, [cnt_core], -1, (color_core), thickness=-1) #BGR
95 | #Overlay
96 | ret_img[binary_tumor!=0]=ret_img[binary_tumor!=0]*(1-alpha)+target_img[binary_tumor!=0]*alpha
97 | ret_img=cv2.resize(ret_img, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
98 | return ret_img
99 |
100 |
101 | def tumor_edge_core_DE(sudo_core_edge):
102 | sc.tl.rank_genes_groups(sudo_core_edge, groupby="region",reference="rest",n_genes=sudo_core_edge.shape[1], method='wilcoxon')
103 | pvals_adj1=[i[1] for i in sudo_core_edge.uns['rank_genes_groups']["pvals_adj"]]
104 | pvals_adj0=[i[0] for i in sudo_core_edge.uns['rank_genes_groups']["pvals_adj"]]
105 | genes1=[i[1] for i in sudo_core_edge.uns['rank_genes_groups']["names"]]
106 | genes0=[i[0] for i in sudo_core_edge.uns['rank_genes_groups']["names"]]
107 | if issparse(sudo_core_edge.X):
108 | obs_tidy=pd.DataFrame(sudo_core_edge.X.A)
109 | else:
110 | obs_tidy=pd.DataFrame(sudo_core_edge.X)
111 | obs_tidy.index=sudo_core_edge.obs["region"].tolist()
112 | obs_tidy.columns=sudo_core_edge.var.index.tolist()
113 | #--------Edge enriched genes
114 | obs_tidy1=obs_tidy.loc[:,genes1]
115 | # 1. compute mean value
116 | mean_obs1 = obs_tidy1.groupby(level=0).mean()
117 | mean_all1=obs_tidy1.mean()
118 | # 2. compute fraction of cells having value >0
119 | obs_bool1 = obs_tidy1.astype(bool)
120 | fraction_obs1 = obs_bool1.groupby(level=0).sum() / obs_bool1.groupby(level=0).count()
121 | # compute fold change.
122 | fold_change1 = (mean_obs1.loc["edge"] / (mean_obs1.loc["core"]+ 1e-9)).values
123 | df1 = {'edge_genes': genes1,
124 | 'in_group_fraction': fraction_obs1.loc["edge"].tolist(),
125 | "out_group_fraction":fraction_obs1.loc["core"].tolist(),
126 | "in_out_group_ratio":(fraction_obs1.loc["edge"]/fraction_obs1.loc["core"]).tolist(),
127 | "in_group_mean_exp": mean_obs1.loc["edge"].tolist(),
128 | "out_group_mean_exp": mean_obs1.loc["core"].tolist(),
129 | "fold_change":fold_change1.tolist(),
130 | "pvals_adj":pvals_adj1,
131 | "all_mean_exp": mean_all1}
132 | df1 = pd.DataFrame(data=df1)
133 | df1=df1[(df1["pvals_adj"]<0.05)]
134 | #--------Core enriched genes
135 | obs_tidy0=obs_tidy.loc[:,genes0]
136 | # 1. compute mean value
137 | mean_obs0 = obs_tidy0.groupby(level=0).mean()
138 | mean_all0=obs_tidy0.mean()
139 | # 2. compute fraction of cells having value >0
140 | obs_bool0 = obs_tidy0.astype(bool)
141 | fraction_obs0 = obs_bool0.groupby(level=0).sum() / obs_bool0.groupby(level=0).count()
142 | # compute fold change.
143 | fold_change0 = (mean_obs0.loc["core"] / (mean_obs0.loc["edge"]+ 1e-9)).values
144 | df0 = {'edge_genes': genes0,
145 | 'in_group_fraction': fraction_obs0.loc["core"].tolist(),
146 | "out_group_fraction":fraction_obs0.loc["edge"].tolist(),
147 | "in_out_group_ratio":(fraction_obs0.loc["core"]/fraction_obs0.loc["edge"]).tolist(),
148 | "in_group_mean_exp": mean_obs0.loc["core"].tolist(),
149 | "out_group_mean_exp": mean_obs0.loc["edge"].tolist(),
150 | "fold_change":fold_change0.tolist(),
151 | "pvals_adj":pvals_adj0,
152 | "all_mean_exp": mean_all0}
153 | df0 = pd.DataFrame(data=df0)
154 | df0=df0[(df0["pvals_adj"]<0.05)]
155 | return df0, df1
156 |
157 | def filter_DE_genes(df, min_all_mean_exp=0.1, min_in_out_group_ratio=1, min_in_group_fraction=0.5, min_fold_change=1.2):
158 | df_filtered=df.copy()
159 | df_filtered=df_filtered[(df_filtered["all_mean_exp"]>min_all_mean_exp) &
160 | (df_filtered["in_out_group_ratio"]>=min_in_out_group_ratio) &
161 | (df_filtered["in_group_fraction"]>min_in_group_fraction) &
162 | (df_filtered["fold_change"]>min_fold_change)]
163 | return df_filtered
164 |
165 | def plot_edge_core_enrichd_genes(img, resize_factor, binary, binary_tumor, sudo_core_edge, genes, cnt_color, plot_dir, res):
166 | resize_width=int(img.shape[1]*resize_factor)
167 | resize_height=int(img.shape[0]*resize_factor)
168 | if issparse(sudo_core_edge.X):sudo_core_edge.X=sudo_core_edge.X.A
169 | res=res*1.5
170 | white_ratio=0.5
171 | for g in genes:
172 | sudo_core_edge.obs["exp"]=sudo_core_edge.X[:,sudo_core_edge.var.index==g]
173 | sudo_core_edge.obs["relexp"]=(sudo_core_edge.obs["exp"]-np.min(sudo_core_edge.obs["exp"]))/(np.max(sudo_core_edge.obs["exp"])-np.min(sudo_core_edge.obs["exp"]))
174 | img_new=np.zeros(img.shape, dtype=np.uint8)
175 | for _, row in sudo_core_edge.obs.iterrows():
176 | x=row["x"]
177 | y=row["y"]
178 | c=row["relexp"]
179 | img_new[int(x-res/2):int(x+res/2), int(y-res/2):int(y+res/2),:]=((np.array(cnt_color(int(c*255)))[0:3])*255).astype(int)
180 | img_new=cv2.cvtColor(img_new, cv2.COLOR_RGB2BGR)
181 | #Background
182 | ret_img=img.copy()
183 | ret_img[binary!=0]=ret_img[binary!=0]*(1-white_ratio)+np.array([255, 255, 255])*(white_ratio)
184 | #Overlay
185 | ret_img[binary_tumor!=0]=img_new[binary_tumor!=0]
186 | ret_img=cv2.resize(ret_img, (resize_width, resize_height), interpolation = cv2.INTER_AREA)
187 | cv2.imwrite(plot_dir+g+'.jpg', ret_img)
188 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/TESLA_package/build/lib/TESLA/util.py:
--------------------------------------------------------------------------------
1 | import scanpy as sc
2 | import pandas as pd
3 | import numpy as np
4 | import scipy
5 | import os
6 | from anndata import AnnData,read_csv,read_text,read_mtx
7 | from scipy.sparse import issparse
8 |
9 | def prefilter_cells(adata,min_counts=None,max_counts=None,min_genes=200,max_genes=None):
10 | if min_genes is None and min_counts is None and max_genes is None and max_counts is None:
11 | raise ValueError('Provide one of min_counts, min_genes, max_counts or max_genes.')
12 | id_tmp=np.asarray([True]*adata.shape[0],dtype=bool)
13 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,min_genes=min_genes)[0]) if min_genes is not None else id_tmp
14 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,max_genes=max_genes)[0]) if max_genes is not None else id_tmp
15 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,min_counts=min_counts)[0]) if min_counts is not None else id_tmp
16 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_cells(adata.X,max_counts=max_counts)[0]) if max_counts is not None else id_tmp
17 | adata._inplace_subset_obs(id_tmp)
18 | adata.raw=sc.pp.log1p(adata,copy=True) #check the rowname
19 | print("the var_names of adata.raw: adata.raw.var_names.is_unique=:",adata.raw.var_names.is_unique)
20 |
21 |
22 | def prefilter_genes(adata,min_counts=None,max_counts=None,min_cells=10,max_cells=None):
23 | if min_cells is None and min_counts is None and max_cells is None and max_counts is None:
24 | raise ValueError('Provide one of min_counts, min_genes, max_counts or max_genes.')
25 | id_tmp=np.asarray([True]*adata.shape[1],dtype=bool)
26 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,min_cells=min_cells)[0]) if min_cells is not None else id_tmp
27 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,max_cells=max_cells)[0]) if max_cells is not None else id_tmp
28 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,min_counts=min_counts)[0]) if min_counts is not None else id_tmp
29 | id_tmp=np.logical_and(id_tmp,sc.pp.filter_genes(adata.X,max_counts=max_counts)[0]) if max_counts is not None else id_tmp
30 | adata._inplace_subset_var(id_tmp)
31 |
32 |
33 | def prefilter_specialgenes(adata,Gene1Pattern="ERCC",Gene2Pattern="MT-"):
34 | id_tmp1=np.asarray([not str(name).startswith(Gene1Pattern) for name in adata.var_names],dtype=bool)
35 | id_tmp2=np.asarray([not str(name).startswith(Gene2Pattern) for name in adata.var_names],dtype=bool)
36 | id_tmp=np.logical_and(id_tmp1,id_tmp2)
37 | adata._inplace_subset_var(id_tmp)
38 |
39 | def relative_func(expres):
40 | #expres: an array counts expression for a gene
41 | maxd = np.max(expres) - np.min(expres)
42 | min_exp=np.min(expres)
43 | rexpr = (expres - min_exp)/maxd
44 | return rexpr
45 |
46 | def plot_relative_exp(input_adata, gene, x_name, y_name,color,use_raw=False, spot_size=200000):
47 | adata=input_adata.copy()
48 | if use_raw:
49 | X=adata.raw.X
50 | else:
51 | X=adata.X
52 | if issparse(X):
53 | X=pd.DataFrame(X.A)
54 | else:
55 | X=pd.DataFrame(X)
56 | X.index=adata.obs.index
57 | X.columns=adata.var.index
58 | rexpr=relative_func(X.loc[:,gene])
59 | adata.obs["rexpr"]=rexpr
60 | fig=sc.pl.scatter(adata,x=x_name,y=y_name,color="rexpr",title=gene+"_rexpr",color_map=color,show=False,size=spot_size/adata.shape[0])
61 | return fig
62 |
63 | def plot_log_exp(input_adata, gene, x_name, y_name,color,use_raw=False):
64 | adata=input_adata.copy()
65 | if use_raw:
66 | X=adata.X
67 | else:
68 | X=adata.raw.X
69 | if issparse(X):
70 | X=pd.DataFrame(X.A)
71 | else:
72 | X=pd.DataFrame(X)
73 | X.index=adata.obs.index
74 | X.columns=adata.var.index
75 | adata.obs["log"]=np.log((X.loc[:,gene]+1).tolist())
76 | fig=sc.pl.scatter(adata,x=x_name,y=y_name,color="log",title=gene+"_log",color_map=color,show=False,size=200000/adata.shape[0])
77 | return fig
78 |
79 | def refine_clusters(pred, resize_height, resize_width, threshold, radius):
80 | pixel_num=pd.Series(pred).value_counts()
81 | clusters=pixel_num.index.tolist()
82 | reorder_map={}
83 | for i in range(pixel_num.shape[0]):
84 | reorder_map[clusters[i]]=i
85 | pred_reordered=pd.Series(pred).replace(reorder_map).to_numpy()
86 | pixel_num=pd.Series(pred_reordered).value_counts()
87 | # Number of clusters
88 | nLabels = len(np.unique(pred_reordered))
89 | # Number of main clusters
90 | mainLabels=(pd.Series(pred_reordered).value_counts()>=threshold).sum()
91 | #------------- Refine clusters ---------------------
92 | main_clusters=pixel_num.index[pixel_num>=threshold].tolist()
93 | minor_clusters=pixel_num.index[pixel_num0:
105 | replace_map[i]=nbs_num.index[ nbs_num.index.isin(main_clusters) ][ 0 ]
106 | pred_refined=pd.Series(pred_reordered).replace(replace_map).to_numpy()
107 | return pred_refined
108 |
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLA-1.0.0-py3.7.egg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLA-1.0.0-py3.7.egg
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLAforST-1.2.0-py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLAforST-1.2.0-py3-none-any.whl
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLAforST-1.2.0.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLAforST-1.2.0.tar.gz
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLAforST-1.2.1-py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLAforST-1.2.1-py3-none-any.whl
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLAforST-1.2.1.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLAforST-1.2.1.tar.gz
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLAforST-1.2.2-py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLAforST-1.2.2-py3-none-any.whl
--------------------------------------------------------------------------------
/TESLA_package/dist/TESLAforST-1.2.2.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/TESLA_package/dist/TESLAforST-1.2.2.tar.gz
--------------------------------------------------------------------------------
/TESLA_package/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | setuptools.setup(
7 | name="TESLAforST",
8 | version="1.2.4",
9 | author="Jian Hu",
10 | author_email="jianhu@pennmedicine.upenn.edu",
11 | description="TESLA: Deciphering tumor ecosystems at super-resolution from spatial transcriptomics",
12 | long_description=long_description,
13 | long_description_content_type="text/markdown",
14 | url="https://github.com/jianhuupenn/TESLA",
15 | packages=setuptools.find_packages(),
16 | install_requires=["torch","pandas","numpy","scipy","scanpy","anndata","scikit-learn", "numba"],
17 | #install_requires=[],
18 | classifiers=[
19 | "Programming Language :: Python :: 3",
20 | "License :: OSI Approved :: MIT License",
21 | "Operating System :: OS Independent",
22 | ],
23 | python_requires='>=3.6',
24 | )
25 |
--------------------------------------------------------------------------------
/docs/asserts/images/applications.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/docs/asserts/images/applications.jpg
--------------------------------------------------------------------------------
/docs/asserts/images/applications_new.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/docs/asserts/images/applications_new.jpg
--------------------------------------------------------------------------------
/docs/asserts/images/workflow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/docs/asserts/images/workflow.jpg
--------------------------------------------------------------------------------
/tutorial/output_19_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_19_0.jpg
--------------------------------------------------------------------------------
/tutorial/output_23_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_23_0.png
--------------------------------------------------------------------------------
/tutorial/output_28_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_28_1.jpg
--------------------------------------------------------------------------------
/tutorial/output_35_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_35_0.jpg
--------------------------------------------------------------------------------
/tutorial/output_37_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_37_2.jpg
--------------------------------------------------------------------------------
/tutorial/output_43_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_43_0.jpg
--------------------------------------------------------------------------------
/tutorial/output_44_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_44_0.jpg
--------------------------------------------------------------------------------
/tutorial/output_51_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jianhuupenn/TESLA/8c4dcf896497dd4a34a6e720f03ec68c1502d571/tutorial/output_51_0.jpg
--------------------------------------------------------------------------------
/tutorial/tutorial.md:
--------------------------------------------------------------------------------
1 | TESLA Tutorial
2 |
3 |
4 | Author: Jian Hu*, Kyle Coleman, Edward B. Lee, Humam Kadara, Linghua Wang*, Mingyao Li*
5 |
6 | ## Outline
7 | #### 1. [Installation](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#1-installation-1)
8 | #### 2. [Import modules](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#2-import-python-modules)
9 | #### 3. [Read in data](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#3-read-in-data-1)
10 | #### 4. [Gene expression enhancement](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#4-gene-expression-enhancement-1)
11 | #### 5. [Region annotation](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#5-region-annotation-1)
12 | #### 6. [Characterize the intra-tumor heterogeneity](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#6-characterize-the-intra-tumor-heterogeneity-1)
13 | #### 7. [TLS detection](https://github.com/jianhuupenn/TESLA/blob/main/tutorial/tutorial.md#7-tls-detection-1)
14 |
15 | ### 1. Installation
16 | To install TESLA package you must make sure that your python version is over 3.5.=. If you don’t know the version of python you can check it by:
17 |
18 |
19 | ```python
20 | import platform
21 | platform.python_version()
22 | ```
23 |
24 |
25 |
26 |
27 | '3.8.8'
28 |
29 |
30 |
31 | Note: Because TESLA pends on pytorch, you should make sure torch is correctly installed.
32 |
33 | Now you can install the current release of TESLA by the following three ways:
34 | #### 1.1 PyPI: Directly install the package from PyPI.
35 |
36 |
37 | ```python
38 | pip3 install TESLAforST
39 | #If you do not have permission (when you get a permission denied error), you should install TESLA by
40 | pip3 install --user TESLAforST
41 | ```
42 |
43 | #### 1.2 Github
44 | Download the package from Github and install it locally:
45 |
46 |
47 | ```python
48 | git clone https://github.com/jianhuupenn/TESLA
49 | cd ./TESLA/TESLA_package/
50 | python3 setup.py install --user
51 | ```
52 |
53 | #### 1.3 Anaconda ()
54 | If you do not have Python3.5 or Python3.6 installed, consider installing Anaconda (see Installing Anaconda). After installing Anaconda, you can create a new environment, for example, TESLA (you can change to any name you like).
55 |
56 |
57 | ```python
58 | #create an environment called TESLA
59 | conda create -n TESLA python=3.7.9
60 | #activate your environment
61 | conda activate TESLA
62 | git clone https://github.com/jianhuupenn/TESLA
63 | cd TESLA/TESLA_package/
64 | python3 setup.py build
65 | python3 setup.py install
66 | conda deactivate
67 | ```
68 |
69 | ### 2. Import python modules
70 |
71 |
72 | ```python
73 | import os,csv,re, time
74 | import pickle
75 | import random
76 | import warnings
77 | warnings.filterwarnings('ignore')
78 | import pandas as pd
79 | import numpy as np
80 | from scipy import stats
81 | from scipy.sparse import issparse
82 | import scanpy as sc
83 | import matplotlib.colors as clr
84 | import matplotlib.pyplot as plt
85 | import cv2
86 | import TESLA as tesla
87 | from IPython.display import Image
88 | ```
89 |
90 |
91 | ```python
92 | tesla.__version__
93 | ```
94 |
95 |
96 |
97 |
98 | '1.2.2'
99 |
100 |
101 |
102 | ### 3. Read in data
103 | The current version of TESLA requres three input data:
104 |
105 |
106 | 1. The gene expression matrix(n by k): expression_matrix.h5;
107 |
108 | 2. Spatial coordinateds of samplespositions.txt;
109 |
110 | 3. Histology image(optional): histology.tif, can be tif or png or jepg.
111 |
112 | The gene expreesion data can be stored as an AnnData object. AnnData stores a data matrix .X together with annotations of observations .obs, variables .var and unstructured annotations .uns.
113 |
114 |
115 | ```python
116 | """
117 | #Read original 10x_h5 data and save it to h5ad
118 | from scanpy import read_10x_h5
119 | adata = read_10x_h5("../tutorial/data/151673/expression_matrix.h5")
120 | spatial=pd.read_csv("../tutorial/data/151673/positions.txt",sep=",",header=None,na_filter=False,index_col=0)
121 | adata.obs["x1"]=spatial[1]
122 | adata.obs["x2"]=spatial[2]
123 | adata.obs["x3"]=spatial[3]
124 | adata.obs["x4"]=spatial[4]
125 | adata.obs["x5"]=spatial[5]
126 | #Select captured samples
127 | adata=adata[adata.obs["x1"]==1]
128 | adata.var_names=[i.upper() for i in list(adata.var_names)]
129 | adata.var["genename"]=adata.var.index.astype("str")
130 | adata.write_h5ad("../tutorial/data/151673/sample_data.h5ad")
131 | """
132 | #Read in gene expression and spatial location
133 | counts=sc.read("./data/sample_data.h5ad")
134 | #Read in hitology image
135 | img=cv2.imread("./data/sample_H&E.jpg")
136 | ```
137 |
138 | ### 4. Gene expression enhancement
139 |
140 | #### 4.1 Preprocessing
141 |
142 |
143 | ```python
144 | resize_factor=1000/np.min(img.shape[0:2])
145 | resize_width=int(img.shape[1]*resize_factor)
146 | resize_height=int(img.shape[0]*resize_factor)
147 | counts.var.index=[i.upper() for i in counts.var.index]
148 | counts.var_names_make_unique()
149 | counts.raw=counts
150 | sc.pp.log1p(counts) # impute on log scale
151 | if issparse(counts.X):counts.X=counts.X.A.copy()
152 | ```
153 |
154 | #### 4.2 Contour detection
155 |
156 |
157 | ```python
158 | #Three different algorithms to detect contour, select the best one.Here we use cv2.
159 | #Important note: If you get incorrect contour for all of the 3 three methods, please double check your array_x, array_y, pixel_x, pixel_y are matched correctly.
160 |
161 | #-----------------1. Detect contour using cv2-----------------
162 | cnt=tesla.cv2_detect_contour(img, apertureSize=5,L2gradient = True)
163 |
164 | #-----------------2. Scan contour by x-----------------
165 | spots=counts.obs.loc[:, ['pixel_x', 'pixel_y', "array_x", "array_y"]]
166 | #shape="hexagon" for 10X Vsium, shape="square" for ST
167 | cnt=tesla.scan_contour(spots, scan_x=True, shape="hexagon")
168 |
169 | #-----------------3. Scan contour by y-----------------
170 | spots=counts.obs.loc[:, ['pixel_x', 'pixel_y', "array_x", "array_y"]]
171 | #shape="hexagon" for 10X Vsium, shape="square" for ST
172 | cnt=tesla.scan_contour(spots, scan_x=False, shape="hexagon")
173 |
174 | binary=np.zeros((img.shape[0:2]), dtype=np.uint8)
175 | cv2.drawContours(binary, [cnt], -1, (1), thickness=-1)
176 | #Enlarged filter
177 | cnt_enlarged = tesla.scale_contour(cnt, 1.05)
178 | binary_enlarged = np.zeros(img.shape[0:2])
179 | cv2.drawContours(binary_enlarged, [cnt_enlarged], -1, (1), thickness=-1)
180 | img_new = img.copy()
181 | cv2.drawContours(img_new, [cnt], -1, (255), thickness=50)
182 | img_new=cv2.resize(img_new, ((resize_width, resize_height)))
183 | cv2.imwrite('./results/cnt.jpg', img_new)
184 | Image(filename='./results/cnt.jpg')
185 | ```
186 |
187 |
188 |
189 |
190 |
191 | 
192 |
193 |
194 |
195 |
196 | #### 4.3 Gene expression enhancement
197 |
198 |
199 | ```python
200 | #Set size of superpixel
201 | res=50
202 | # Note, if the numer of superpixels is too large and take too long, you can increase the res to 100
203 | enhanced_exp_adata=tesla.imputation(img=img, raw=counts, cnt=cnt, genes=counts.var.index.tolist(), shape="None", res=res, s=1, k=2, num_nbs=10)
204 | ```
205 |
206 | Trying to set attribute `.obs` of view, copying.
207 |
208 |
209 | Total number of sudo points: 19988
210 | Calculating spot 0
211 | Calculating spot 1000
212 | Calculating spot 2000
213 | Calculating spot 3000
214 | Calculating spot 4000
215 | Calculating spot 5000
216 | Calculating spot 6000
217 | Calculating spot 7000
218 | Calculating spot 8000
219 | Calculating spot 9000
220 | Calculating spot 10000
221 | Calculating spot 11000
222 | Calculating spot 12000
223 | Calculating spot 13000
224 | Calculating spot 14000
225 | Calculating spot 15000
226 | Calculating spot 16000
227 | Calculating spot 17000
228 | Calculating spot 18000
229 | Calculating spot 19000
230 | --- 76.97813606262207 seconds ---
231 | Imputing spot 0
232 | Imputing spot 1000
233 | Imputing spot 2000
234 | Imputing spot 3000
235 | Imputing spot 4000
236 | Imputing spot 5000
237 | Imputing spot 6000
238 | Imputing spot 7000
239 | Imputing spot 8000
240 | Imputing spot 9000
241 | Imputing spot 10000
242 | Imputing spot 11000
243 | Imputing spot 12000
244 | Imputing spot 13000
245 | Imputing spot 14000
246 | Imputing spot 15000
247 | Imputing spot 16000
248 | Imputing spot 17000
249 | Imputing spot 18000
250 | Imputing spot 19000
251 |
252 |
253 | #### 4.4 Plot gene expression image
254 |
255 |
256 | ```python
257 | cnt_color = clr.LinearSegmentedColormap.from_list('magma', ["#000003", "#3b0f6f", "#8c2980", "#f66e5b", "#fd9f6c", "#fbfcbf"], N=256)
258 | g="CD151"
259 | enhanced_exp_adata.obs[g]=enhanced_exp_adata.X[:,enhanced_exp_adata.var.index==g]
260 | fig=sc.pl.scatter(enhanced_exp_adata,alpha=1,x="y",y="x",color=g,color_map=cnt_color,show=False,size=10)
261 | fig.set_aspect('equal', 'box')
262 | fig.invert_yaxis()
263 | plt.gcf().set_dpi(600)
264 | fig.figure.show()
265 | ```
266 |
267 |
268 |
269 | 
270 |
271 |
272 |
273 | #### 4.5 Save results
274 |
275 |
276 | ```python
277 | enhanced_exp_adata.write_h5ad("./results/enhanced_exp.h5ad")
278 | ```
279 |
280 | ### 5. Region annotation
281 |
282 | #### 5.1 Target region annotation
283 | Prefer choosing 5 to 10 marker genes.
284 |
285 | The default num_required=1. If you include one gene that is not that specific (also highly expressed in other cell types), num_required+=1. For example, if you include 2 non-specific markers, please set num_required=3.
286 | Please drop genes with all 0 expression from the list since they are not infomrative.
287 |
288 | ```python
289 | #Select your gene list
290 | #For example, if we want to annotate tumor region, use tumor markers
291 | genes=['BUB1B', 'KIF1C','TOP2A', 'CD151', 'MMP10', 'PTHLH','FEZ1','IL24','KCNMA','INHBA','MAGEA4','NT5E','LAMC2','SLITRK6']
292 | genes=list(set([i for i in genes if i in enhanced_exp_adata.var.index ]))
293 | #target_size can be set to "small" or "large".
294 | pred_refined, target_clusters, c_m=tesla.annotation(img=img,
295 | binary=binary,
296 | sudo_adata=enhanced_exp_adata,
297 | genes=genes,
298 | resize_factor=resize_factor,
299 | num_required=1,
300 | target_size="small")
301 | #Plot
302 | ret_img=tesla.visualize_annotation(img=img,
303 | binary=binary,
304 | resize_factor=resize_factor,
305 | pred_refined=pred_refined,
306 | target_clusters=target_clusters,
307 | c_m=c_m)
308 |
309 | cv2.imwrite('./results/tumor.jpg', ret_img)
310 | Image(filename='./results/tumor.jpg')
311 | ```
312 |
313 | Computing image band...
314 | Computing gene band...
315 | Running TESLA...
316 | 0 / 30 | label num : 100 | main clusters : 65 | feature loss : 6.286277770996094 | spatial loss : 2.477134943008423
317 | --- 79.8510057926178 seconds ---
318 | 1 / 30 | label num : 75 | main clusters : 30 | feature loss : 4.300119400024414 | spatial loss : 0.44662031531333923
319 | --- 136.36640787124634 seconds ---
320 | mainLabels 30 reached minLabels 30 .
321 | Finding target clusters...
322 | c_m:
323 | [(6, 0.660496668592895), (2, 0.5849543163588522), (15, 0.5158497030203698), (19, 0.48606369815923994), (12, 0.4802130752317957), (10, 0.4580134991936973), (4, 0.45732190334046835), (14, 0.43235966925018005), (17, 0.3388222879213612), (13, 0.3331709370762233), (16, 0.3160079580858375), (9, 0.2478758789018975), (8, 0.21917223597411475), (7, 0.20651287130232146), (3, 0.19796864337328288), (11, 0.19742332922793465), (20, 0.15788060926103378), (1, 0.14535831267938887), (5, 0.1265518524797657), (18, 0.024549401709053432), (0, 0.0)]
324 | Target clusters:
325 | [6, 2, 15, 19, 12, 10, 4, 14, 17, 13]
326 |
327 |
328 |
329 |
330 |
331 |
332 | 
333 |
334 |
335 |
336 |
337 | #### 5.2 Save results
338 |
339 |
340 | ```python
341 | #Save
342 | np.save("./results/tumor_annotation.npy", pred_refined)
343 | print("Target_clusters: ", target_clusters, "\n")
344 | #Save the cluster density information
345 | c_d={i[0]:i[1] for i in c_m[0:len(target_clusters)]}
346 | print("Cluster_density : ", c_d)
347 | with open('./results/tumor_annotation_c_d.pkl', 'wb') as f: pickle.dump(c_d, f)
348 |
349 | ```
350 |
351 | Target_clusters: [6, 2, 15, 19, 12, 10, 4, 14, 17, 13]
352 |
353 | Cluster_density : {6: 0.660496668592895, 2: 0.5849543163588522, 15: 0.5158497030203698, 19: 0.48606369815923994, 12: 0.4802130752317957, 10: 0.4580134991936973, 4: 0.45732190334046835, 14: 0.43235966925018005, 17: 0.3388222879213612, 13: 0.3331709370762233}
354 |
355 |
356 | ### 6. Characterize the intra-tumor heterogeneity
357 |
358 | #### 6.1 Read in saved results
359 |
360 |
361 | ```python
362 | enhanced_exp_adata=sc.read("./results/enhanced_exp.h5ad")
363 | pred_refined=np.load("./results/tumor_annotation.npy")
364 | target_clusters=[6, 2, 15, 19, 12, 10, 4, 14, 17, 13]
365 | c_m= [(6, 0.660496668592895), (2, 0.5849543163588522), (15, 0.5158497030203698), (19, 0.48606369815923994), (12, 0.4802130752317957), (10, 0.4580134991936973), (4, 0.45732190334046835), (14, 0.43235966925018005), (17, 0.3388222879213612), (13, 0.3331709370762233), (16, 0.3160079580858375), (9, 0.2478758789018975), (8, 0.21917223597411475), (7, 0.20651287130232146), (3, 0.19796864337328288), (11, 0.19742332922793465), (20, 0.15788060926103378), (1, 0.14535831267938887), (5, 0.1265518524797657), (18, 0.024549401709053432), (0, 0.0)]
366 | ```
367 |
368 | #### 6.2 Leading edge detection
369 |
370 |
371 | ```python
372 | ret_img=tesla.leading_edge_detection(img=img,
373 | pred_refined=pred_refined,
374 | resize_factor=resize_factor,
375 | target_clusters=target_clusters,
376 | binary=binary)
377 |
378 | cv2.imwrite('./results/leading_edge.jpg', ret_img)
379 | Image(filename='./results/leading_edge.jpg')
380 | ```
381 |
382 |
383 |
384 |
385 |
386 | 
387 |
388 |
389 |
390 |
391 | #### 6.3 Tumor edge and core separation
392 |
393 |
394 | ```python
395 | shrink_rate=0.8
396 | res=50
397 | enhanced_exp_adata.obs.index = enhanced_exp_adata.obs.index.astype(str)
398 | binary_tumor, binary_core, core_edge_exp=tesla.tumor_edge_core_separation(img=img,
399 | binary=binary,
400 | resize_factor=resize_factor,
401 | pred_refined=pred_refined,
402 | target_clusters=target_clusters,
403 | sudo_adata=enhanced_exp_adata,
404 | res=res,
405 | shrink_rate=shrink_rate)
406 |
407 | ret_img=tesla.plot_tumor_edge_core(img=img,
408 | resize_factor=resize_factor,
409 | binary=binary,
410 | binary_tumor=binary_tumor,
411 | binary_core=binary_core,
412 | color_edge=[66, 50, 225],
413 | color_core=[62, 25, 53])
414 |
415 | cv2.imwrite('./results/core_edge.jpg', ret_img)
416 | Image(filename='./results/core_edge.jpg')
417 | ```
418 |
419 | Running Connected Components ...
420 | Running Select biggest Tumor region ...
421 | Running Core and edge separation ...
422 | Running Create Gene Expression adata for tumor edge vs. core ...
423 |
424 |
425 | Trying to set attribute `.obs` of view, copying.
426 |
427 |
428 |
429 |
430 |
431 |
432 | 
433 |
434 |
435 |
436 |
437 | #### 6.4. Core vs core DE analysis
438 |
439 |
440 | ```python
441 | df_core, df_edge=tesla.tumor_edge_core_DE(core_edge_exp)
442 | df_edge_filtered=tesla.filter_DE_genes(df=df_edge, min_all_mean_exp=0.1, min_in_out_group_ratio=1, min_in_group_fraction=0.5, min_fold_change=1.2)
443 | df_core_filtered=tesla.filter_DE_genes(df=df_core, min_all_mean_exp=0.1, min_in_out_group_ratio=1, min_in_group_fraction=0.5, min_fold_change=1.2)
444 | df_edge_filtered
445 | ```
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 | |
455 | edge_genes |
456 | in_group_fraction |
457 | out_group_fraction |
458 | in_out_group_ratio |
459 | in_group_mean_exp |
460 | out_group_mean_exp |
461 | fold_change |
462 | pvals_adj |
463 | all_mean_exp |
464 |
465 |
466 |
467 |
468 | B3GALT4 |
469 | B3GALT4 |
470 | 0.887826 |
471 | 0.730257 |
472 | 1.215772 |
473 | 0.159630 |
474 | 0.099114 |
475 | 1.610570 |
476 | 1.289481e-90 |
477 | 0.121456 |
478 |
479 |
480 | RHOF |
481 | RHOF |
482 | 0.825470 |
483 | 0.637768 |
484 | 1.294311 |
485 | 0.168203 |
486 | 0.112965 |
487 | 1.488977 |
488 | 9.696451e-66 |
489 | 0.133358 |
490 |
491 |
492 | DMXL2 |
493 | DMXL2 |
494 | 0.924777 |
495 | 0.792817 |
496 | 1.166445 |
497 | 0.187402 |
498 | 0.127881 |
499 | 1.465440 |
500 | 2.114505e-65 |
501 | 0.149855 |
502 |
503 |
504 | PTGDS |
505 | PTGDS |
506 | 0.774992 |
507 | 0.621162 |
508 | 1.247648 |
509 | 0.194328 |
510 | 0.110119 |
511 | 1.764701 |
512 | 2.475420e-64 |
513 | 0.141208 |
514 |
515 |
516 | FYB |
517 | FYB |
518 | 0.933355 |
519 | 0.858274 |
520 | 1.087480 |
521 | 0.325664 |
522 | 0.229077 |
523 | 1.421633 |
524 | 4.381763e-64 |
525 | 0.264736 |
526 |
527 |
528 | ... |
529 | ... |
530 | ... |
531 | ... |
532 | ... |
533 | ... |
534 | ... |
535 | ... |
536 | ... |
537 | ... |
538 |
539 |
540 | ZBTB18 |
541 | ZBTB18 |
542 | 0.837017 |
543 | 0.834331 |
544 | 1.003220 |
545 | 0.136065 |
546 | 0.108772 |
547 | 1.250920 |
548 | 1.998338e-07 |
549 | 0.118848 |
550 |
551 |
552 | TMEM176A |
553 | TMEM176A |
554 | 0.798086 |
555 | 0.795713 |
556 | 1.002982 |
557 | 0.175890 |
558 | 0.139969 |
559 | 1.256630 |
560 | 1.528395e-06 |
561 | 0.153230 |
562 |
563 |
564 | ARMC7 |
565 | ARMC7 |
566 | 0.863411 |
567 | 0.839930 |
568 | 1.027956 |
569 | 0.144560 |
570 | 0.119669 |
571 | 1.207997 |
572 | 3.360836e-06 |
573 | 0.128859 |
574 |
575 |
576 | PTAFR |
577 | PTAFR |
578 | 0.780271 |
579 | 0.770805 |
580 | 1.012280 |
581 | 0.203142 |
582 | 0.168537 |
583 | 1.205324 |
584 | 4.606770e-04 |
585 | 0.181313 |
586 |
587 |
588 | CXCL13 |
589 | CXCL13 |
590 | 0.606401 |
591 | 0.579455 |
592 | 1.046501 |
593 | 0.154883 |
594 | 0.117137 |
595 | 1.322246 |
596 | 6.435324e-04 |
597 | 0.131072 |
598 |
599 |
600 |
601 |
104 rows × 9 columns
602 |
603 |
604 |
605 |
606 | #### 6.5. Plot core/edge enriched genes
607 |
608 |
609 | ```python
610 | cnt_color = clr.LinearSegmentedColormap.from_list('magma', ["#000003", "#3b0f6f", "#8c2980", "#f66e5b", "#fd9f6c", "#fbfcbf"], N=256)
611 | genes=["IGFBP2", "CXCL12"]
612 | plot_dir="./results/"
613 | tesla.plot_edge_core_enrichd_genes(img=img,
614 | resize_factor=resize_factor,
615 | binary=binary,
616 | binary_tumor=binary_tumor,
617 | sudo_core_edge=core_edge_exp,
618 | genes=genes,
619 | cnt_color=cnt_color,
620 | plot_dir=plot_dir,
621 | res=res)
622 |
623 | ```
624 |
625 | ### 6.6 Plot ome examples
626 |
627 |
628 | ```python
629 | # Core enriched gene
630 | Image(filename='./results/IGFBP2.jpg')
631 | ```
632 |
633 |
634 |
635 |
636 |
637 | 
638 |
639 |
640 |
641 |
642 |
643 | ```python
644 | # Edge enriched gene
645 | Image(filename='./results/CXCL12.jpg')
646 | ```
647 |
648 |
649 |
650 |
651 |
652 | 
653 |
654 |
655 |
656 |
657 | ### 7. TLS detection
658 |
659 | #### 7.1 Read in cell type annotations
660 | ##### Before runing TLS detection, please detect B, CD4+T, DC and CXCL13.
661 |
662 |
663 | ```python
664 | cnt_color = clr.LinearSegmentedColormap.from_list('red', ["#EAE7CC", '#BA0000'], N=256)
665 | pred_refined1=np.load("./results/B_annotation.npy")
666 | pred_refined2=np.load("./results/CD4+T_annotation.npy")
667 | pred_refined3=np.load("./results/DC_annotation.npy")
668 | pred_refined4=np.load("./results/CXCL13_annotation.npy")
669 | pred_refined_list=[pred_refined1, pred_refined2, pred_refined3, pred_refined4]
670 | #Read in cluster_density information
671 | with open('./results/B_annotation_c_d.pkl', 'rb') as f: c_d1 = pickle.load(f)
672 |
673 | with open('./results/CD4+T_annotation_c_d.pkl', 'rb') as f: c_d2 = pickle.load(f)
674 |
675 | with open('./results/DC_annotation_c_d.pkl', 'rb') as f: c_d3 = pickle.load(f)
676 |
677 | with open('./results/CXCL13_annotation_c_d.pkl', 'rb') as f: c_d4 = pickle.load(f)
678 |
679 | cluster_density_list=[c_d1, c_d2, c_d3, c_d4]
680 | ```
681 |
682 | #### 7.2 Calculate TLS score
683 |
684 |
685 | ```python
686 | cnt_color = clr.LinearSegmentedColormap.from_list('red', ["#EAE7CC", '#BA0000'], N=256)
687 | num_required=3
688 | tls_score=tesla.TLS_detection(pred_refined_list, cluster_density_list, num_required, cnt_color)
689 | img_tls=tesla.plot_TLS_score(img, resize_factor, binary,tls_score, cnt_color)
690 | ```
691 |
692 | #### 7.3 Plot TLS score
693 |
694 |
695 | ```python
696 | cv2.imwrite('./results/TLS_score.jpg', img_tls)
697 | Image(filename='./results/TLS_score.jpg')
698 | ```
699 |
700 |
701 |
702 |
703 |
704 | 
705 |
706 |
707 |
708 |
709 |
710 | ```python
711 |
712 | ```
713 |
--------------------------------------------------------------------------------