import numpy as np
import matplotlib.pyplot as plt
import os
from tqdm import tqdm
import cv2 as cv
import matplotlib.patches as patches
[docs]
class Plot_Visium:
def __init__(self, segmentation, boundary_dict, type_list, colors=None):
"""
Args:
segmentation: Object of spotiphy.segmentation.Segmentation.
boundary_dict: Output of function spotiphy.segmentation.cell_boundary.
type_list: List of the cell types.
colors: Colors of each cell type.
"""
self.img = segmentation.img # Original image.
if np.max(self.img) <= 1:
self.img = (self.img*255).astype(np.int32)
self.img_seg = segmentation.label # Segmented image.
self.n_cell_df = segmentation.n_cell_df # Dataframe shown the cells in each spot.
self.nucleus_boundary = segmentation.nucleus_boundary.astype(np.int32) # Segmented nucleus boundaries.
self.nucleus_df = segmentation.nucleus_df
self.spot_radius = segmentation.spot_radius
self.spot_centers = segmentation.spot_center
self.nucleus_centers = self.nucleus_df[['x', 'y']].values.astype(np.int32) # Segmented nucleus centers.
self.img_cell = boundary_dict['img_cell'].astype(np.int32) # Inferred cell location.
self.type_mask = None
self.img_boundary = boundary_dict['cell_boundary']
self.individual_boundary = boundary_dict['individual_boundary']
self.img_size = self.img_seg.shape[:2]
self.type_list = type_list
if colors is None:
self.colors = np.array(list(plt.get_cmap("tab20b").colors) + list(plt.get_cmap("tab20c").colors))
self.colors = (self.colors*255).astype(np.int32)
else:
self.colors = colors
self.color_dict = {type_: list(self.colors[i]) for i, type_ in enumerate(type_list)}
[docs]
def plot(self, background=False, cell='both', shape='cell', circle_size=10, boundary=None,
save='Visium_plot.png', background_alpha=0.5, spot=True, spot_width=2, spot_color=(0, 0, 255),
cell_boundary_color=(100, 100, 100), dpi=300):
"""
Args:
background: If show the background.
cell: Which group of cell shapes to plot? [both, in, out]
shape: Which shape to plot? [cell, nucleus, circle]
circle_size: Size of the nuclei.
boundary: Which group of cell boundary to plot? [both, in, out]
save: If not none, save the figure to the path.
background_alpha: Opacity of the background figure.
spot: If plot the spot.
spot_width: Width of the spot.
spot_color: Color of the spot.
cell_boundary_color: Color of the cell boundaries.
dpi: DPI of the image plotted.
"""
img = self.img.copy()*background_alpha if background else np.zeros(self.img.shape)
img = img.astype(np.int32)
group_dict = {'both': [True, False], 'in': [True], 'out': [False], None:[], 'None':[]}
type_annotation = list(self.nucleus_df['cell_type'])
in_spot = list(self.nucleus_df['in_spot'])
print('Adding cells.')
if shape == 'cell':
d = {t: i for i, t in enumerate(self.type_list)}
type_mask = np.zeros((img.shape[0], img.shape[1], len(self.type_list)), dtype=bool)
for i in tqdm(range(len(self.img_cell))):
for j in range(len(self.img_cell[0])):
k = self.img_cell[i, j]
if (0 < k <= len(type_annotation) and type_annotation[k-1] in d.keys()
and in_spot[k-1] in group_dict[cell]):
type_mask[i, j, d[type_annotation[k-1]]] = True
for i, type_ in tqdm(enumerate(self.type_list)):
color = np.array(self.color_dict[type_]).astype(np.int32)
img[type_mask[:, :, i]] = color
# img.astype(np.int32)
for i, type_ in tqdm(enumerate(type_annotation)):
if in_spot[i] in group_dict[cell] and type_ in self.color_dict.keys():
color = [int(i) for i in self.color_dict[type_]]
if shape == 'circle':
img = cv.circle(img, self.nucleus_centers[i], circle_size, color, -1)
elif shape == 'nucleus':
boundary_temp = self.nucleus_boundary[i].reshape((-1, 1, 2))
img = cv.fillPoly(img, [boundary_temp], color=color)
boundary_mask = np.zeros((img.shape[0], img.shape[1]), dtype=bool)
for i in tqdm(range(len(type_annotation))):
if (in_spot[i] in group_dict[boundary] and len(self.individual_boundary[i+1]) > 0
and type_annotation[i]) in self.color_dict.keys():
index_temp = np.array(self.individual_boundary[i+1])
boundary_mask[index_temp[:, 0], index_temp[:, 1]] = True
img[boundary_mask] = cell_boundary_color
if spot:
print('Adding spots.')
for i in range(len(self.spot_centers)):
img = cv.circle(img, self.spot_centers[i], int(self.spot_radius), spot_color, spot_width)
if save is not None:
print('Saving the image.')
img1 = img[:, :, [2, 1, 0]]
cv.imwrite(save, img1)
plt.figure(dpi=dpi)
plt.imshow(img)
[docs]
def plot_legend(self, save=None, dpi=300, background=(0, 0, 0), ncol=1, word_color=(255, 255, 255), fontsize=6,
horizontal_distance=380):
nrow = len(self.type_list)//ncol
if nrow*ncol < len(self.type_list):
nrow += 1
img = np.ones((nrow*60+40, ncol*horizontal_distance-30, 3)) * np.array(background)
img = img.astype(np.int32)
fig, ax = plt.subplots(dpi=dpi)
plt.imshow(img)
i = 0 # col index
j = 0 # row index
word_color = (word_color[0]/255, word_color[1]/255, word_color[2]/255)
for k, v in self.color_dict.items():
ax.add_patch(patches.Rectangle((i*horizontal_distance+20, j*60+20), 100, 45,
facecolor=np.array(v)/255, edgecolor='none'))
ax.text(i*horizontal_distance+145, j*60+43, k, va="center", ha="left", fontsize=fontsize, color=word_color)
j += 1
if j == nrow:
j = 0
i += 1
ax.axis('off')
if save is not None:
plt.savefig(save, bbox_inches='tight', pad_inches=0)
plt.show()
[docs]
class Plot_Xenium:
def __init__(self, Xenium_img, cell_boundaries, nucleus_boundaries, type_list, cell_type, nucleus_centers):
"""
Args:
Xenium_img: Image of Xenium
cell_boundaries: Coordinates of the cell boundaries on the image.
nucleus_boundaries: Coordinates of the nucleus boundaries on the image.
type_list: List of the cell types.
cell_type: Annotation.
nucleus_centers: Centers of the nuclei.
"""
self.img = Xenium_img
self.type_list = type_list
self.cell_type = cell_type
self.n_cell = len(cell_type)
self.nucleus_centers = nucleus_centers.astype(np.int32)
cell_boundaries_temp = cell_boundaries.iloc[:, 1:3].values.astype(np.int32)
idx = [[] for _ in range(self.n_cell)]
for i in tqdm(range(len(cell_boundaries))):
cell_idx = int(cell_boundaries.iloc[i, 0]-1)
idx[cell_idx] += [i]
self.cell_boundaries = [cell_boundaries_temp[np.array(idx[i])].reshape((-1, 1, 2)) if len(idx[i])>0
else [] for i in tqdm(range(self.n_cell))]
nucleus_boundaries_temp = nucleus_boundaries.iloc[:, 1:3].values.astype(np.int32)
idx = [[] for _ in range(self.n_cell)]
for i in tqdm(range(len(nucleus_boundaries))):
cell_idx = int(nucleus_boundaries.iloc[i, 0]-1)
idx[cell_idx] += [i]
self.nucleus_boundaries = [nucleus_boundaries_temp[np.array(idx[i])].reshape((-1, 1, 2)) if len(idx[i])>0
else [] for i in tqdm(range(self.n_cell))]
self.colors = np.array(list(plt.get_cmap("tab20b").colors) + list(plt.get_cmap("tab20c").colors))
self.colors = (self.colors*255).astype(np.int32)
self.color_dict = {type_: list(self.colors[i]) for i, type_ in enumerate(type_list)}
[docs]
def plot(self, background=False, shape='cell', save='Xenium_plot.png', cell_boundaries=False, background_alpha=0.8,
circle_size=10, cell_boundary_color=(100, 100, 100), cell_boundary_thickness=2):
"""
Args:
background: If show the background.
shape: Which shape to plot? [cell, nucleus, circle]
save: If not none, save the figure to the path.
cell_boundaries: If plot the cell boundaries.
background_alpha: Opacity of the background figure.
circle_size: Size of the circle.
cell_boundary_color: Color of the cell boundaries.
cell_boundary_thickness: Thickness of the cell boundaries.
"""
img = self.img.copy()*background_alpha if background else np.zeros(self.img.shape)
img = img.astype(np.int32)
print('Adding cellls.')
for i, type_ in tqdm(enumerate(self.cell_type)):
if type_ in self.color_dict.keys():
color = [int(i) for i in self.color_dict[type_]]
if shape == 'cell':
if len(self.cell_boundaries[i]) > 0:
img = cv.fillPoly(img, [self.cell_boundaries[i]], color=color)
elif shape == 'nucleus':
if len(self.nucleus_boundaries[i]) > 0:
img = cv.fillPoly(img, [self.nucleus_boundaries[i]], color=color)
elif shape == 'circle':
img = cv.circle(img, self.nucleus_centers[i], circle_size, color, -1)
if cell_boundaries:
for i, type_ in tqdm(enumerate(self.cell_type)):
if len(self.cell_boundaries[i]) > 0:
img = cv.polylines(img, [self.cell_boundaries[i]], color=cell_boundary_color,
thickness=cell_boundary_thickness, isClosed=True)
if save is not None:
print('Saving the image.')
img1 = img[:, :, [2, 1, 0]]
cv.imwrite(save, img1)
plt.imshow(img)