├── LICENSE
├── Mistic_code
├── app.py
├── code
│ ├── apply_theme.py
│ ├── image_tSNE_GUI
│ │ ├── current_package_versions.txt
│ │ ├── desc.html
│ │ ├── descMontage.html
│ │ ├── descSM1.html
│ │ ├── main.py
│ │ ├── main_old.py
│ │ ├── static
│ │ │ └── image_tsne_tils_all_3_rot.png
│ │ └── templates
│ │ │ └── index.html
│ ├── mistic.sh
│ ├── output_tiles
│ │ └── test.txt
│ └── user_inputs
│ │ ├── figures
│ │ └── .gitkeep
│ │ └── metadata
│ │ ├── CODEX
│ │ ├── Marker_ids.csv
│ │ └── markers.csv
│ │ ├── CyCIF
│ │ ├── Marker_ids.csv
│ │ └── markers.csv
│ │ ├── Vectra
│ │ ├── Cluster_categories.csv
│ │ ├── Marker_ids.csv
│ │ ├── Patient_ids.csv
│ │ ├── Response_categories.csv
│ │ ├── Treatment_categories.csv
│ │ ├── X_imagetSNE.csv
│ │ └── markers.csv
│ │ └── t-CyCIF
│ │ ├── Marker_ids.csv
│ │ └── markers.csv
├── color_scatter.html
├── image.html
├── theme.yaml
└── title.html
├── Readme.md
└── fig_readme
├── Figure_2.jpg
└── GUI_Mistic.png
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 IMO
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 |
--------------------------------------------------------------------------------
/Mistic_code/app.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Run this app with `python app.py` and
4 | # visit http://127.0.0.1:8050/ in your web browser.
5 |
6 | import dash
7 | import dash_core_components as dcc
8 | import dash_html_components as html
9 | import plotly.express as px
10 | import pandas as pd
11 |
12 | external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
13 |
14 | app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
15 |
16 | # assume you have a "long-form" data frame
17 | # see https://plotly.com/python/px-arguments/ for more options
18 | df = pd.DataFrame({
19 | "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
20 | "Amount": [4, 1, 2, 2, 4, 5],
21 | "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
22 | })
23 |
24 | fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
25 |
26 | app.layout = html.Div(children=[
27 | html.H1(children='Hello Dash'),
28 |
29 | html.Div(children='''
30 | Dash: A web application framework for Python.
31 | '''),
32 |
33 | dcc.Graph(
34 | id='example-graph',
35 | figure=fig
36 | )
37 | ])
38 |
39 | if __name__ == '__main__':
40 | app.run_server(debug=True)
41 |
--------------------------------------------------------------------------------
/Mistic_code/code/apply_theme.py:
--------------------------------------------------------------------------------
1 | """ Example demonstrating how to apply a theme
2 | """
3 |
4 | # External imports
5 | import numpy as np
6 |
7 | # Bokeh imports
8 | from bokeh.io import curdoc
9 | from bokeh.plotting import figure
10 |
11 | p = figure(width=800)
12 | props = dict(line_width=4, line_alpha=0.7)
13 |
14 | x = np.arange(-np.pi, np.pi, np.pi / 16)
15 | l0 = p.line(x, np.sin(x), color='yellow', legend_label='sin', **props)
16 | l2 = p.line(x, np.cos(x), color='red', legend_label='cos', **props)
17 |
18 | curdoc().add_root(p)
19 | curdoc().theme = 'dark_minimal'
20 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/current_package_versions.txt:
--------------------------------------------------------------------------------
1 | Package Version Latest Type
2 | ---------------------------- --------- ------------ -----
3 | antlr4-python3-runtime 4.9.3 4.13.1 wheel
4 | anyio 4.2.0 4.4.0 wheel
5 | appnope 0.1.2 0.1.4 wheel
6 | archspec 0.2.3 0.2.4 wheel
7 | argon2-cffi 21.3.0 23.1.0 wheel
8 | asttokens 2.0.5 2.4.1 wheel
9 | attrs 23.1.0 23.2.0 wheel
10 | Babel 2.11.0 2.15.0 wheel
11 | beautifulsoup4 4.12.2 4.12.3 wheel
12 | bleach 4.1.0 6.1.0 wheel
13 | bokeh 3.4.1 3.4.2 wheel
14 | Bottleneck 1.3.7 1.4.0 wheel
15 | certifi 2024.2.2 2024.6.2 wheel
16 | comm 0.2.1 0.2.2 wheel
17 | conda-package-handling 2.2.0 2.3.0 wheel
18 | conda_package_streaming 0.9.0 0.10.0 wheel
19 | contourpy 1.2.0 1.2.1 wheel
20 | cycler 0.11.0 0.12.1 wheel
21 | debugpy 1.6.7 1.8.2 wheel
22 | dulwich 0.21.7 0.22.1 sdist
23 | exceptiongroup 1.2.0 1.2.1 wheel
24 | executing 0.8.3 2.0.1 wheel
25 | filelock 3.15.3 3.15.4 wheel
26 | fonttools 4.51.0 4.53.0 wheel
27 | fsspec 2024.6.0 2024.6.1 wheel
28 | gast 0.5.4 0.6.0 sdist
29 | grpcio 1.64.0 1.64.1 wheel
30 | idna 3.6 3.7 wheel
31 | imageio 2.34.1 2.34.2 wheel
32 | importlib_metadata 7.2.0 8.0.0 wheel
33 | ipykernel 6.28.0 6.29.5 wheel
34 | ipython 8.20.0 8.26.0 wheel
35 | ipywidgets 8.1.2 8.1.3 wheel
36 | jedi 0.18.1 0.19.1 wheel
37 | joblib 1.4.0 1.4.2 wheel
38 | json5 0.9.6 0.9.25 wheel
39 | jsonpointer 2.4 3.0.0 wheel
40 | jsonschema 4.19.2 4.22.0 wheel
41 | jsonschema-specifications 2023.7.1 2023.12.1 wheel
42 | jupyter_client 8.6.0 8.6.2 wheel
43 | jupyter_core 5.5.0 5.7.2 wheel
44 | jupyter-events 0.8.0 0.10.0 wheel
45 | jupyter-lsp 2.2.0 2.2.5 wheel
46 | jupyter_server 2.10.0 2.14.1 wheel
47 | jupyter_server_terminals 0.4.4 0.5.3 wheel
48 | jupyterlab 4.0.11 4.2.3 wheel
49 | jupyterlab-pygments 0.1.2 0.3.0 wheel
50 | jupyterlab_server 2.25.1 2.27.2 wheel
51 | jupyterlab-widgets 3.0.10 3.0.11 wheel
52 | keras 3.3.3 3.4.1 wheel
53 | keyring 24.3.1 25.2.1 wheel
54 | kiwisolver 1.4.4 1.4.5 wheel
55 | lightning 2.3.0 2.3.1 wheel
56 | lightning-utilities 0.11.2 0.11.3.post0 wheel
57 | MarkupSafe 2.1.3 2.1.5 wheel
58 | matplotlib 3.8.4 3.9.0 wheel
59 | matplotlib-inline 0.1.6 0.1.7 wheel
60 | mistune 2.0.4 3.0.2 wheel
61 | ml-dtypes 0.3.2 0.4.0 wheel
62 | nbclient 0.8.0 0.10.0 wheel
63 | nbconvert 7.10.0 7.16.4 wheel
64 | nbformat 5.9.2 5.10.4 wheel
65 | notebook 7.0.8 7.2.1 wheel
66 | notebook_shim 0.2.3 0.2.4 wheel
67 | numexpr 2.8.7 2.10.1 wheel
68 | numpy 1.26.4 2.0.0 wheel
69 | overrides 7.4.0 7.7.0 wheel
70 | packaging 24.0 24.1 wheel
71 | pandas 2.2.1 2.2.2 wheel
72 | pandocfilters 1.5.0 1.5.1 wheel
73 | parso 0.8.3 0.8.4 wheel
74 | pexpect 4.8.0 4.9.0 wheel
75 | pillow 10.3.0 10.4.0 wheel
76 | pip 24.0 24.1.1 wheel
77 | platformdirs 4.2.0 4.2.2 wheel
78 | pluggy 1.4.0 1.5.0 wheel
79 | prometheus-client 0.14.1 0.20.0 wheel
80 | prompt-toolkit 3.0.43 3.0.47 wheel
81 | protobuf 4.25.3 5.27.2 wheel
82 | psutil 5.9.0 6.0.0 wheel
83 | pydantic 2.7.4 2.8.0 wheel
84 | pydantic_core 2.18.4 2.20.1 wheel
85 | Pygments 2.15.1 2.18.0 wheel
86 | pyparsing 3.0.9 3.1.2 wheel
87 | pytorch-lightning 2.3.0 2.3.1 wheel
88 | pyzmq 25.1.2 26.0.3 wheel
89 | qtconsole 5.5.1 5.5.2 wheel
90 | rapidfuzz 3.9.3 3.9.4 wheel
91 | referencing 0.30.2 0.35.1 wheel
92 | requests 2.31.0 2.32.3 wheel
93 | rpds-py 0.10.6 0.18.1 wheel
94 | scikit-image 0.23.2 0.24.0 wheel
95 | scikit-learn 1.4.2 1.5.1 wheel
96 | scipy 1.13.0 1.14.0 wheel
97 | Send2Trash 1.8.2 1.8.3 wheel
98 | setuptools 69.5.1 70.2.0 wheel
99 | sip 6.7.12 6.8.5 wheel
100 | sniffio 1.3.0 1.3.1 wheel
101 | stack-data 0.2.0 0.6.3 wheel
102 | style 1.1.0 1.1.6 wheel
103 | tensorboard 2.16.2 2.17.0 wheel
104 | tensorflow 2.16.1 2.16.2 wheel
105 | tensorflow-io-gcs-filesystem 0.37.0 0.37.1 wheel
106 | terminado 0.17.1 0.18.1 wheel
107 | threadpoolctl 2.2.0 3.5.0 wheel
108 | tifffile 2024.5.22 2024.7.2 wheel
109 | tinycss2 1.2.1 1.3.0 wheel
110 | tornado 6.3.3 6.4.1 wheel
111 | tqdm 4.66.2 4.66.4 wheel
112 | traitlets 5.7.1 5.14.3 wheel
113 | trove-classifiers 2024.5.22 2024.7.2 wheel
114 | truststore 0.8.0 0.9.1 wheel
115 | typing_extensions 4.11.0 4.12.2 wheel
116 | tzdata 2023.3 2024.1 wheel
117 | urllib3 2.2.1 2.2.2 wheel
118 | virtualenv 20.26.2 20.26.3 wheel
119 | wcwidth 0.2.5 0.2.13 wheel
120 | widgetsnbextension 4.0.10 4.0.11 wheel
121 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/desc.html:
--------------------------------------------------------------------------------
1 |
43 |
44 |
Mistic: Image t-SNE viewer for multiplexed images
45 |
46 |
47 | Interact with the widgets below to plot either a stack montage for all markers of a single multiplxed image, or multiple multiplexed images based on a subset of markers.
48 | There are two canvases: the image tSNE canvas to the right with the multiplexed images and the live canvases showing the tSNE scatter plots based on image metadata.
49 | Hover over the tSNE dots to get more information about each image.
50 |
51 |
52 |
53 | Imaging Technique
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/descMontage.html:
--------------------------------------------------------------------------------
1 |
44 |
45 |
46 |
47 |
48 | Or
49 |
50 |
51 |
52 | Multiple multiplexed images
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/descSM1.html:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
44 | Single multiplexed image
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/main.py:
--------------------------------------------------------------------------------
1 | ### Code for Mistic
2 | ### Image t-SNE viewer for multiplexed images
3 | ###
4 | ### code author Sandhya Prabhakaran
5 | ###
6 | ### Revision history
7 | ### FoV code ###
8 | ### Jan 29th 2021
9 | ### 25th June 2021
10 | ### github version
11 | ### 19th Jan 2022
12 | ### added Patient id live canvas
13 | ### 26th Jan 2022 ###
14 | ### merging the stack montage code
15 | ### prior version is main_rollback.
16 | ### 14th Feb 2022
17 | ### adding codex, tcycif, vectra option
18 | ### prior version is main_rollback_1
19 | ### 14th mar 2022
20 | ### added the user tsne as well 'arrange in rows' (seeall) tsne generation code
21 |
22 | ## 20th Feb 2022
23 | ## this is the latest main.py, with dates and comments removed
24 | ## for github upload
25 |
26 | ## 8th April 2022
27 | ## 2nd rebuttal updates
28 | ## tsne and dpmm code added
29 |
30 | #### 21st April 2022
31 | #### CyCIF files
32 | ## code cleanup for github
33 |
34 |
35 | ### 27th June 2024 ###
36 | ### Package compatibility updates
37 |
38 |
39 |
40 | import os
41 | import sys
42 | import random
43 | import warnings
44 |
45 | import numpy as np
46 | import pandas as pd
47 |
48 | import matplotlib
49 | import matplotlib.pyplot as plt
50 | import matplotlib.patches as mpatches
51 |
52 | from matplotlib.figure import Figure
53 |
54 | from matplotlib import cm
55 |
56 | from scipy import ndimage
57 |
58 | from sklearn.manifold import TSNE
59 |
60 | import phenograph as pg
61 | from scipy.stats import zscore
62 |
63 | from skimage.color import rgb2gray
64 |
65 | from matplotlib.patches import Polygon
66 |
67 | from skimage import io
68 |
69 | from skimage import data
70 | from skimage.filters import threshold_otsu
71 | from skimage.segmentation import clear_border
72 | from skimage.measure import label, regionprops
73 | from skimage.morphology import closing, square
74 | from skimage.color import label2rgb
75 | from skimage.io import imread, imshow, imread_collection, concatenate_images
76 | from skimage.transform import resize
77 | from skimage.morphology import label
78 |
79 | import seaborn as sns
80 |
81 | import scipy.spatial
82 |
83 | from scipy.spatial import distance
84 |
85 |
86 | import seaborn as sns
87 |
88 |
89 | import matplotlib.image as mpimg
90 |
91 |
92 | import matplotlib.colors as colors
93 |
94 | from matplotlib import colors as mcolors
95 |
96 | colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
97 |
98 | from PIL import Image, ImageOps
99 |
100 | from bokeh.layouts import column, row
101 | from bokeh.models import (Select, Button)
102 | from bokeh.palettes import Spectral5
103 | from bokeh.plotting import curdoc, figure
104 | from bokeh.models.widgets import Div
105 | from bokeh.layouts import column, layout
106 |
107 | from bokeh.models import (HoverTool, ColumnDataSource)
108 | from bokeh.models.widgets import RadioButtonGroup
109 | from bokeh.models import CheckboxGroup
110 | from bokeh.models import CustomJS
111 | from bokeh.models import BoxSelectTool
112 | from bokeh.themes import Theme
113 |
114 | from bokeh.models.layouts import TabPanel, Tabs #2024
115 | from bokeh.io import output_file, show
116 |
117 |
118 | ##
119 | import skimage.io as io
120 | import tifffile
121 | from PIL import TiffImagePlugin
122 |
123 | from sklearn.mixture import BayesianGaussianMixture
124 |
125 |
126 |
127 | ### image-tSNE
128 |
129 | width = 5000
130 | height = 5000
131 | max_dim = 500
132 | tw =1200
133 | th = 900
134 |
135 | full_image_1 = Image.new('RGBA', (width, height))
136 |
137 |
138 |
139 | ##############################################################
140 |
141 | ################## Function definitions ######################
142 |
143 | ##############################################################
144 |
145 |
146 | #######################################################################
147 | ##### get_cell_coords(), get_neighbours(), point_valid(), ####
148 | ##### get_point() are functions to generate random points ####
149 | ##### functions modified from: ####
150 | ##### https://scipython.com/blog/poisson-disc-sampling-in-python/ ####
151 | #######################################################################
152 |
153 |
154 |
155 | def get_cell_coords(pt,a):
156 | """Get the coordinates of the cell that pt = (x,y) falls in."""
157 |
158 | return int(pt[0] // a), int(pt[1] // a)
159 |
160 | def get_neighbours(coords,nx,ny,cells):
161 | """Return the indexes of points in cells neighbouring cell at coords.
162 | For the cell at coords = (x,y), return the indexes of points in the cells
163 | with neighbouring coordinates illustrated below: ie those cells that could
164 | contain points closer than r.
165 | ooo
166 | ooooo
167 | ooXoo
168 | ooooo
169 | ooo
170 | """
171 |
172 | dxdy = [(-1,-2),(0,-2),(1,-2),(-2,-1),(-1,-1),(0,-1),(1,-1),(2,-1),
173 | (-2,0),(-1,0),(1,0),(2,0),(-2,1),(-1,1),(0,1),(1,1),(2,1),
174 | (-1,2),(0,2),(1,2),(0,0)]
175 | neighbours = []
176 | for dx, dy in dxdy:
177 | neighbour_coords = coords[0] + dx, coords[1] + dy
178 | if not (0 <= neighbour_coords[0] < nx and
179 | 0 <= neighbour_coords[1] < ny):
180 | # We're off the grid: no neighbours here.
181 | continue
182 | neighbour_cell = cells[neighbour_coords]
183 | if neighbour_cell is not None:
184 | # This cell is occupied: store this index of the contained point.
185 | neighbours.append(neighbour_cell)
186 | return neighbours
187 |
188 | def point_valid(pt,a,nx,ny,cells,samples,r):
189 | """Is pt a valid point to emit as a sample?
190 | It must be no closer than r from any other point: check the cells in its
191 | immediate neighbourhood.
192 | """
193 |
194 | cell_coords = get_cell_coords(pt,a)
195 | for idx in get_neighbours(cell_coords,nx,ny,cells):
196 | nearby_pt = samples[idx]
197 | # Squared distance between or candidate point, pt, and this nearby_pt.
198 | distance2 = (nearby_pt[0]-pt[0])**2 + (nearby_pt[1]-pt[1])**2
199 | if distance2 < r**2:
200 | # The points are too close, so pt is not a candidate.
201 | return False
202 | # All points tested: if we're here, pt is valid
203 | return True
204 |
205 | def get_point(k, refpt,r,a,nx,ny,cells,samples):
206 | """Try to find a candidate point relative to refpt to emit in the sample.
207 | We draw up to k points from the annulus of inner radius r, outer radius 2r
208 | around the reference point, refpt. If none of them are suitable (because
209 | they're too close to existing points in the sample), return False.
210 | Otherwise, return the pt.
211 | """
212 | i = 0
213 | while i < k:
214 | rho, theta = np.random.uniform(r, 2*r), np.random.uniform(0, 2*np.pi)
215 | pt = refpt[0] + rho*np.cos(theta), refpt[1] + rho*np.sin(theta)
216 | if not (0 <= pt[0] < width and 0 <= pt[1] < height):
217 | # This point falls outside the domain, so try again.
218 | continue
219 | if point_valid(pt,a,nx,ny,cells,samples,r):
220 | return pt
221 | i += 1
222 | # We failed to find a suitable point in the vicinity of refpt.
223 | return False
224 |
225 | ###############################################################
226 | ### draw_tSNE_scatter() ####
227 | ### a) creates the metadata-based tSNE scatter plots ####
228 | ### b) populates the hover functionality for each tSNE dot ####
229 | ###############################################################
230 |
231 | def draw_tSNE_scatter(tsne1, file_name_hover,cluster_ms_list ):
232 | tsne=np.asarray(tsne1)
233 |
234 | source = ColumnDataSource(data=dict(
235 | x=tsne[:,0],
236 | y=tsne[:,1],
237 | pat_list = pat_ind_list,
238 | res_list = resp_list,
239 | fov_list = pat_fov_list,
240 | color_vec_list = color_vec,
241 | tx_list = tx_list,
242 | color_vec_tx_list = color_vec_tx,
243 | clust_asgn_list = clust_asgn_list,
244 | color_vec_clasgn_list = color_vec_clasgn,
245 | cluster_anno_list = cluster_anno_list,
246 | cluster_ms_list = cluster_ms_list,
247 | cluster_pat_list = cluster_pat_list,
248 | color_vec_patid_list = color_vec_patid,
249 | file_name_hover_list = file_name_hover
250 | #legend_p11 = legend_p1
251 | ))
252 | TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
253 |
254 | TOOLTIPS = [
255 | ("index", "$index"),
256 | ("(x,y)", "($x, $y)"),
257 | ("Pat_id", "@pat_list"),
258 | ("Response", "@res_list"),
259 | ("Treatment", "@tx_list"),
260 | ("Cluster id", "@clust_asgn_list"),
261 | ("Channel", "@cluster_ms_list"),
262 | ("Thumbnail", "@file_name_hover_list"),
263 | ("FoV","@fov_list")
264 | ]
265 |
266 |
267 |
268 | #2024: legend to legend_label; plot_width to width, plot_height to height
269 | p1 = figure(width=400, height=400, tooltips=TOOLTIPS,tools = TOOLS,
270 | title="Patient response")
271 |
272 | p1.scatter('x', 'y', size=10, source=source, legend_label= 'res_list', color = 'color_vec_list',fill_alpha=0.6)
273 | p1.legend.location = "bottom_left"
274 |
275 |
276 | p2 = figure(width=400, height=400, tooltips=TOOLTIPS,tools = TOOLS,
277 | title="Treatment category")
278 | p2.scatter('x', 'y', size=10, source=source, legend_label= 'tx_list', color = 'color_vec_tx_list',fill_alpha=0.6)
279 | p2.legend.location = "bottom_left"
280 |
281 |
282 | p3 = figure(width=400, height=400, tooltips=TOOLTIPS,tools = TOOLS,
283 | title="Cluster annotations")
284 | p3.scatter('x', 'y', size=10, source=source, legend_label= 'cluster_anno_list', color = 'color_vec_clasgn_list',fill_alpha=0.6)
285 | p3.legend.location = "bottom_left"
286 |
287 |
288 |
289 | p4 = figure(width=400, height=400, tooltips=TOOLTIPS,tools = TOOLS,
290 | title="Patient id")
291 | p4.scatter('x', 'y', size=10, source=source, legend_label= 'cluster_pat_list', color = 'color_vec_patid_list',fill_alpha=0.6)
292 | p4.legend.location = "bottom_left"
293 |
294 |
295 | return ([p1,p2,p3,p4, source])
296 |
297 |
298 |
299 |
300 |
301 | ##############################################################################################
302 | ### generate_stack_montage() ####
303 | ### a) reads in and processes the image channels ####
304 | ### b) generates evenly-spaced points on the static canvas to arrange the images in rows ####
305 | ### c) generates thumbnails, and pastes these onto the static canvas ####
306 | ### d) stores the thumbnails in the output folder ####
307 | ### e) updates the hover tool with thumbnail paths, marker names and metadata ####
308 | ##############################################################################################
309 |
310 |
311 | ##################
312 | def generate_stack_montage(chk_box_marker_sm, rb_imtech_val, LABELS_MARKERS):
313 |
314 |
315 |
316 | full_image = Image.new('RGBA', (width, height))
317 | size = [256,256]
318 |
319 | file_name_hover = []
320 | file_name_hover_list = []
321 |
322 | # Choose up to k points around each reference point as candidates for a new
323 | # sample point
324 | k = 10
325 |
326 | # Minimum distance between samples
327 | r = 2
328 |
329 | width_1, height_1 = 20, 22
330 |
331 | print('Arrange images side-by-side')
332 |
333 | # Cell side length
334 | a = r/np.sqrt(2)
335 | # Number of cells in the x- and y-directions of the grid
336 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
337 |
338 | # A list of coordinates in the grid of cells
339 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
340 |
341 |
342 | #logic to get grid points
343 | m = np.int64(np.floor(nx*ny/num_images)) #2024 np.int to int64
344 |
345 |
346 | row_needed = []
347 | def multiples(m, num_images):
348 | for i in range(num_images):
349 | row_needed.append(i*m)
350 |
351 | multiples(m,num_images)
352 |
353 |
354 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
355 |
356 | print(type(select_coords))
357 | ################
358 |
359 | #tsne = np.asarray(coords_list)
360 | tsne = select_coords
361 | df_Xtsne = pd.DataFrame(tsne)
362 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_sm.csv'), header=None, index=None)
363 |
364 |
365 |
366 | # save the tSNE points to bve read later on irrespective of whoch option was chosen
367 | df_Xtsne_touse = pd.DataFrame(tsne)
368 | df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse_sm.csv'), header=None, index=None)
369 |
370 | tx, ty = tsne[:,0], tsne[:,1]
371 |
372 | tx[tx ==0] = 0.2
373 | ty[ty ==0] = 0.1
374 |
375 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
376 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
377 | print('###tx')
378 | print(tx)
379 | print(ty)
380 | inds = range(len(marker_image_list))
381 |
382 | N = len(inds)
383 |
384 |
385 | for k in range(len(inds)):
386 |
387 | image_all_2 = []
388 | image_all_1 = []
389 |
390 | im = Image.open(marker_image_list[inds[k]])
391 |
392 |
393 | for m in range(1):
394 | image = im
395 |
396 | median_filtered = scipy.ndimage.median_filter(image, size=1)
397 | image_all_2.append(median_filtered)
398 |
399 |
400 | # apply threshold
401 |
402 | thresh = threshold_otsu(image_all_2[m])
403 |
404 |
405 |
406 |
407 | print('thresh is: ', str(thresh))
408 |
409 | bw = closing(image > thresh)# 2024, square(1))
410 |
411 | # remove artifacts connected to image border
412 | cleared_imtsne = clear_border(bw)
413 |
414 |
415 | image_all_1.append(cleared_imtsne*100)
416 |
417 |
418 | tl = sum(image_all_2) #2024
419 |
420 | tl=tl[0,0:tl.shape[1],0:tl.shape[2]] #2024
421 |
422 |
423 |
424 | if (rb_imtech_val ==0): #vectra
425 | tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))
426 |
427 | elif (rb_imtech_val ==1): #t-CyCIF
428 | tile_1 = Image.fromarray(np.uint8(cm.hsv(tl)*500))
429 | elif (rb_imtech_val ==2): # CODEX
430 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
431 | elif (rb_imtech_val ==3): # CyCIF
432 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
433 |
434 | old_size = tile_1.size
435 |
436 |
437 |
438 |
439 |
440 | new_size = (old_size[0]+1, old_size[1]+1)
441 | new_im = Image.new("RGB", new_size,color='black')
442 |
443 | new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))
444 |
445 |
446 |
447 | file_name = os.path.join(path_wd+'/output_tiles/sm_image_tsne_tils'+str(k)+'.png')
448 |
449 | file_name_hover.append('sm_image_tsne_tils'+str(k)+'.png')
450 |
451 | new_im.save(file_name)
452 |
453 | # Read Images
454 |
455 |
456 | tile_2 = Image.open(file_name)
457 | rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
458 | tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias
459 |
460 | full_image.paste(tile_2, (int((width-max_dim)*ty[k]), int((height-max_dim)*tx[k])),mask=tile_2.convert('RGBA'))
461 |
462 | matplotlib.pyplot.figure(figsize = (25,20))
463 | plt.imshow(full_image)
464 | plt.axis('off')
465 |
466 |
467 |
468 |
469 |
470 | k = random.randint(2,500)
471 | file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/sm_image_tsne_tils_all_'+str(k)+'.png')
472 | full_image.save(file_name)
473 |
474 |
475 |
476 | rotated_img = full_image.rotate(90)
477 | file_name_rot = os.path.join(path_wd+'/image_tSNE_GUI/static/sm_image_tsne_tils_all_'+str(k)+'_rot.png')
478 |
479 |
480 | rotated_img.save(file_name_rot)
481 |
482 | return([file_name_rot,tsne, file_name_hover])
483 |
484 |
485 |
486 | ###########################################################################################################
487 | ### generate_image_tSNE() ########
488 | ### ########
489 | ### a) reads in and pre-processes the images ########
490 | ### b) generates random points or evenly-spaced points on the static canvas to arrange the images ########
491 | ### in rows or reads in the user-provided tSNE ########
492 | ### c) generates thumbnails based on border choices, pastes the thumbnails onto the static canvas ########
493 | ### d) stores the thumbnails in the output folder ########
494 | ### e) updates the hover tool with thumbnail paths ########
495 | ### f) shuffle or no shuffle option is handled in this function where images are randomly shuffled ########
496 | ###########################################################################################################
497 |
498 | def generate_image_tSNE(chk_box_marker,rb_val,rb_rs_val,rb_shf_val, rb_imtech_val, mc, wc, LABELS_MARKERS):
499 | full_image = Image.new('RGBA', (width, height))
500 | size = [256,256]
501 |
502 | file_name_hover = []
503 | file_name_hover_list = []
504 |
505 | if (rb_shf_val == 0): # no shuffle option
506 |
507 |
508 | if(rb_rs_val==1):
509 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), index_col=None, header= None)
510 | print('usertsne')
511 | elif(rb_rs_val==0):
512 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
513 | print('origtsne')
514 |
515 | elif(rb_rs_val==2):
516 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), index_col=None, header= None)
517 | print('seealltsne')
518 |
519 | tsne = np.array(df_Xtsne)
520 |
521 | '''
522 | #create the random tsne projections
523 | if (rb_rs_val==1):
524 | # Choose up to k points around each reference point as candidates for a new
525 | # sample point
526 | k = 10
527 |
528 | # Minimum distance between samples
529 | r = 1.7
530 |
531 | width_1, height_1 = 20, 22
532 |
533 | print('Generating random co-ordinates')
534 |
535 | # Cell side length
536 | a = r/np.sqrt(2)
537 | # Number of cells in the x- and y-directions of the grid
538 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
539 |
540 | # A list of coordinates in the grid of cells
541 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
542 | # Initilalize the dictionary of cells: each key is a cell's coordinates, the
543 | # corresponding value is the index of that cell's point's coordinates in the
544 | # samples list (or None if the cell is empty).
545 | cells = {coords: None for coords in coords_list}
546 |
547 |
548 |
549 | # Pick a random point to start with.
550 | pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
551 | samples = [pt]
552 | # Our first sample is indexed at 0 in the samples list...
553 | cells[get_cell_coords(pt,a)] = 0
554 | # ... and it is active, in the sense that we're going to look for more points
555 | # in its neighbourhood.
556 | active = [0]
557 |
558 | nsamples = 1
559 | # As long as there are points in the active list, keep trying to find samples.
560 | while (nsamples < num_images): #active:
561 | # choose a random "reference" point from the active list.
562 | idx = np.random.choice(active)
563 | refpt = samples[idx]
564 | # Try to pick a new point relative to the reference point.
565 | pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
566 | if pt:
567 | # Point pt is valid: add it to the samples list and mark it as active
568 | samples.append(pt)
569 | nsamples += 1
570 | active.append(len(samples)-1)
571 | cells[get_cell_coords(pt,a)] = len(samples) - 1
572 | print('nsamples is: ',str(nsamples))
573 | else:
574 | # We had to give up looking for valid points near refpt, so remove it
575 | # from the list of "active" points.
576 | active.remove(idx)
577 |
578 | tsne = np.asarray(samples)
579 | df_Xtsne = pd.DataFrame(tsne)
580 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None)
581 |
582 |
583 | elif(rb_rs_val==0):
584 |
585 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
586 | df_Xtsne.shape
587 | tsne = np.array(df_Xtsne )
588 |
589 | elif(rb_rs_val==2):
590 |
591 | # Choose up to k points around each reference point as candidates for a new
592 | # sample point
593 | k = 10
594 |
595 | # Minimum distance between samples
596 | r = 2
597 |
598 | width_1, height_1 = 20, 22
599 |
600 | print('Arrange images side-by-side')
601 |
602 | # Cell side length
603 | a = r/np.sqrt(2)
604 | # Number of cells in the x- and y-directions of the grid
605 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
606 |
607 | # A list of coordinates in the grid of cells
608 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
609 |
610 |
611 | #logic to get grid points
612 | m = np.int(np.floor(nx*ny/num_images))
613 |
614 |
615 | row_needed = []
616 | def multiples(m, num_images):
617 | for i in range(num_images):
618 | row_needed.append(i*m)
619 |
620 | multiples(m,num_images)
621 |
622 |
623 |
624 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
625 |
626 | print(type(select_coords))
627 | ################
628 |
629 | #tsne = np.asarray(coords_list)
630 | tsne = select_coords
631 | df_Xtsne = pd.DataFrame(tsne)
632 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)
633 |
634 |
635 | '''
636 | # save the tSNE points to bve read later on irrespective of whoch option was chosen
637 | df_Xtsne_touse = pd.DataFrame(tsne)
638 | df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse.csv'), header=None, index=None)
639 |
640 | tx, ty = tsne[:,0], tsne[:,1]
641 |
642 | tx[tx ==0] = 0.2
643 | ty[ty ==0] = 0.1
644 |
645 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
646 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
647 |
648 |
649 |
650 | ##identify the markers
651 | mm = np.asarray(LABELS_MARKERS)[chk_box_marker]
652 |
653 | tiles = []
654 |
655 |
656 | marker_choice = np.array(mc)[chk_box_marker]
657 | weight_choice = np.array(wc)[chk_box_marker]
658 |
659 |
660 |
661 | inds = range(len(marker_image_list))
662 |
663 | N = len(inds)
664 |
665 |
666 | for k in range(len(inds)):
667 |
668 | image_all_2 = []
669 | image_all_1 = []
670 |
671 |
672 |
673 |
674 | im = tifffile.imread(marker_image_list[inds[k]])
675 | if (rb_imtech_val ==2): #for codex
676 | im = im.reshape(64,5040,9408)
677 |
678 |
679 |
680 | for m in range(len(marker_choice)):
681 |
682 | print('############marker_choice:',str (marker_choice)) #2024
683 | image = im[marker_choice[m]]
684 |
685 | median_filtered = scipy.ndimage.median_filter(image, size=1)
686 | image_all_2.append(median_filtered)
687 |
688 |
689 | # apply threshold
690 |
691 | thresh = threshold_otsu(image_all_2[m])
692 |
693 |
694 |
695 |
696 | print('thresh is: ', str(thresh))
697 |
698 | bw = closing(image > thresh)#, square(1)) #2024
699 |
700 | # remove artifacts connected to image border
701 | cleared_imtsne = clear_border(bw)
702 |
703 |
704 | image_all_1.append(cleared_imtsne *weight_choice[m]) #2024
705 |
706 | tl = sum(image_all_2) #2024
707 | ##2024
708 | print('$$$$$$$$')
709 | print(len(tl))
710 | print(tl.shape)
711 | print(np.squeeze(tl).shape)
712 | #tl=np.squeeze(tl)
713 | tl=tl[0,0:tl.shape[1],0:tl.shape[2]] #2024
714 | ##
715 |
716 |
717 | if (rb_imtech_val ==0): #vectra
718 | tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255)) #2024 commented this added below
719 | #tile_1 = Image.fromarray(np.uint8((tl)*255)) #2024 commented this added below
720 | #tile_1 = Image.fromarray((tl).astype(np.uint8),'RGB')
721 | #im = Image.fromarray((x * 255).astype(np.uint8))
722 |
723 | elif (rb_imtech_val ==1): #t-CyCIF
724 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
725 | elif (rb_imtech_val ==2): # CODEX
726 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
727 | elif (rb_imtech_val ==3): # CyCIF
728 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
729 |
730 | old_size = tile_1.size
731 |
732 |
733 |
734 |
735 | if(rb_val==0):
736 | new_size = (old_size[0]+1, old_size[1]+1)
737 | new_im = Image.new("RGB", new_size,color='black')
738 |
739 | elif(rb_val==1): # for the border #response based
740 | new_size = (old_size[0]+50, old_size[1]+50)
741 |
742 |
743 |
744 | new_im = Image.new("RGB", new_size,color_vec[inds[k]])## color='yellow')
745 |
746 | elif(rb_val==2): # for the border #treatment based
747 | new_size = (old_size[0]+50, old_size[1]+50)
748 |
749 |
750 | new_im = Image.new("RGB", new_size,color_vec_tx[inds[k]])# color='yellow')
751 |
752 | elif(rb_val==3): # for the border #cluster based
753 | new_size = (old_size[0]+50, old_size[1]+50)
754 |
755 |
756 | new_im = Image.new("RGB", new_size,color_vec_clasgn[inds[k]])# colours_58[cx])# color='yellow')
757 |
758 |
759 | elif(rb_val==4): # for the border #patient id based
760 | new_size = (old_size[0]+50, old_size[1]+50)
761 |
762 |
763 | new_im = Image.new("RGB", new_size, color_vec_patid[inds[k]])# color='yellow')
764 |
765 | new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))
766 |
767 |
768 |
769 | file_name = os.path.join(path_wd+'/output_tiles/image_tsne_tils'+str(k)+'.png')
770 |
771 | file_name_hover.append('image_tsne_tils'+str(k)+'.png')
772 |
773 | new_im.save(file_name)
774 |
775 | # Read Images
776 |
777 |
778 | tile_2 = Image.open(file_name)
779 | rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
780 | #tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias
781 | tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.Resampling.LANCZOS) #2024 Resampling.LANCZOS for ANTIALIAS #commented antialias on 9th Feb 2021
782 |
783 | full_image.paste(tile_2, (int((width-max_dim)*ty[k]), int((height-max_dim)*tx[k])),mask=tile_2.convert('RGBA'))
784 |
785 |
786 |
787 | matplotlib.pyplot.figure(figsize = (25,20))
788 | plt.imshow(full_image)
789 | plt.axis('off')
790 |
791 |
792 |
793 |
794 | k = random.randint(2,500)
795 | file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k)+'.png')
796 | full_image.save(file_name)
797 |
798 |
799 |
800 | rotated_img = full_image.rotate(90)
801 | file_name_rot = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k)+'_rot.png')
802 |
803 |
804 | rotated_img.save(file_name_rot)
805 |
806 |
807 |
808 |
809 | ##code for reshuffling
810 |
811 | elif (rb_shf_val==1): # shuffle images
812 |
813 | #pick the tsne file to start with
814 | if(rb_rs_val==1):
815 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), index_col=None, header= None)
816 | print('usertsne')
817 | elif(rb_rs_val==0):
818 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
819 | print('origtsne')
820 |
821 | elif(rb_rs_val==2):
822 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), index_col=None, header= None)
823 | print('seealltsne')
824 |
825 | tsne = np.array(df_Xtsne)
826 |
827 |
828 |
829 | # save the tSNE points to be read later on, irrespective of which option was chosen
830 | df_Xtsne_touse = pd.DataFrame(tsne)
831 | df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse.csv'), header=None, index=None)
832 |
833 |
834 | tx, ty = tsne[:,0], tsne[:,1]
835 |
836 | tx[tx ==0] = 0.2
837 | ty[ty ==0] = 0.1
838 |
839 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
840 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
841 |
842 | full_image = Image.new('RGBA', (width, height))
843 |
844 | size = [256,256]
845 |
846 |
847 | ##identify the markers
848 | mm = np.asarray(LABELS_MARKERS)[chk_box_marker]
849 |
850 | tiles = []
851 |
852 |
853 | marker_choice = np.array(mc)[chk_box_marker]
854 | weight_choice = np.array(wc)[chk_box_marker]
855 |
856 | inds = range(len(marker_image_list))
857 |
858 | N = len(inds)
859 | shf_N = np.array(range(N))
860 | random.shuffle(shf_N)
861 | print("#############shuffle file order###")
862 | print(shf_N)
863 | count_file = 0
864 |
865 | for k in range(N):
866 |
867 | image_all_2 = []
868 | image_all_1 = []
869 |
870 |
871 |
872 |
873 | im = tifffile.imread(marker_image_list[shf_N[k]])
874 | if (rb_imtech_val ==2): #for codex
875 | im = im.reshape(64,5040,9408) ##
876 |
877 | for m in range(len(marker_choice)):
878 | image = im[marker_choice[m]]
879 |
880 | median_filtered = scipy.ndimage.median_filter(image, size=1)
881 | image_all_2.append(median_filtered)
882 |
883 |
884 |
885 | # apply threshold
886 |
887 | thresh = threshold_otsu(image_all_2[m])
888 |
889 |
890 | print(thresh)
891 |
892 | bw = closing(image > thresh)# 2024, square(1))
893 |
894 | # remove artifacts connected to image border
895 | cleared_imtsne = clear_border(bw)
896 |
897 |
898 | image_all_1.append(cleared_imtsne*weight_choice[m])
899 |
900 | tl = sum(image_all_2) #2024
901 |
902 | tl=tl[0,0:tl.shape[1],0:tl.shape[2]] #2024
903 |
904 |
905 | if (rb_imtech_val ==0): #vectra
906 | tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))
907 |
908 | elif (rb_imtech_val ==1): #t-CyCIF
909 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
910 | elif (rb_imtech_val ==2): # CODEX
911 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
912 | elif (rb_imtech_val ==3): # CyCIF
913 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
914 |
915 | old_size = tile_1.size
916 |
917 | print(pat_ind_list[shf_N[k]])
918 |
919 |
920 | if(rb_val==0):
921 | new_size = (old_size[0]+1, old_size[1]+1)
922 | new_im = Image.new("RGB", new_size,color='black')
923 |
924 | elif(rb_val==1): # for the border #response based
925 | new_size = (old_size[0]+50, old_size[1]+50)
926 |
927 |
928 |
929 |
930 | new_im = Image.new("RGB", new_size,color_vec[shf_N[k]])# color='yellow')
931 |
932 | elif(rb_val==2): # for the border #treatment based
933 | new_size = (old_size[0]+50, old_size[1]+50)
934 |
935 | new_im = Image.new("RGB", new_size,color_vec_tx[shf_N[k]])# color='yellow')
936 |
937 | elif(rb_val==3): # for the border #cluster based
938 | new_size = (old_size[0]+50, old_size[1]+50)
939 |
940 |
941 | new_im = Image.new("RGB", new_size,color_vec_clasgn[shf_N[k]])# color='yellow')
942 |
943 |
944 | elif(rb_val==4): # for the border #patient id based
945 | new_size = (old_size[0]+50, old_size[1]+50)
946 |
947 |
948 | new_im = Image.new("RGB", new_size,color_vec_patid[shf_N[k]])# color='yellow')
949 |
950 |
951 | new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))
952 |
953 |
954 | count_file = np.int64(np.array(np.where(shf_N[k]==shf_N)).flatten()) #2024 np.int to int64
955 |
956 | file_name = os.path.join(path_wd+'/output_tiles/image_tsne_tils'+str(shf_N[k])+'.png')
957 |
958 | file_name_hover.append('image_tsne_tils'+str(shf_N[k])+'.png')
959 |
960 |
961 | new_im.save(file_name)
962 |
963 |
964 |
965 | # Read Images
966 |
967 |
968 | tile_2 = Image.open(file_name)
969 | rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
970 | #tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias
971 | tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.Resampling.LANCZOS) # 2024 Resampling.LANCZOS for ANTIALIAS #commented antialias on 9th Feb 2021
972 |
973 |
974 | full_image.paste(tile_2, (int((width-max_dim)*ty[shf_N[k]]), int((height-max_dim)*tx[shf_N[k]])),mask=tile_2.convert('RGBA'))
975 |
976 |
977 | matplotlib.pyplot.figure(figsize = (25,20))
978 | plt.imshow(full_image)
979 | plt.axis('off')
980 |
981 |
982 |
983 | ## order filenamehover list before sending it out to the live panels
984 | so = np.argsort(shf_N)
985 | file_name_hover_np = np.array(file_name_hover)
986 | sorted_file_name_hover = file_name_hover_np[so]
987 | file_name_hover = sorted_file_name_hover.tolist()
988 |
989 |
990 | k1 = random.randint(2,500)
991 | file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k1)+'.png')
992 |
993 | full_image.save(file_name)
994 |
995 |
996 | rotated_img = full_image.rotate(90)
997 | file_name_rot = file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k1)+'_rot.png')
998 |
999 |
1000 |
1001 | rotated_img.save(file_name_rot)
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 | return([file_name_rot,tsne, file_name_hover])
1011 |
1012 |
1013 | ###############################################################################
1014 | ### button_callback() ####
1015 | ### a) Calls the create_figure() that collects user inputs from the GUI ####
1016 | ### b) draw_tSNE_scatter() generates the tSNE plots for the live canvases ####
1017 | ### b) reads in the user-provided tSNE ####
1018 | ### c) generates thumbnails, and pastes these onto the static canvas ####
1019 | ### d) stores the thumbnails in the output folder ####
1020 | ### e) updates the hover tool with thumbnail paths ####
1021 | ###############################################################################
1022 |
1023 |
1024 | def button_callback():
1025 |
1026 | theme_t = theme_select.value
1027 | if(theme_t == 'black'):
1028 | curdoc().theme= theme_black
1029 | elif(theme_t == 'gray'):
1030 | curdoc().theme= theme_gray
1031 | elif(theme_t == 'dark blue'):
1032 | curdoc().theme= theme_blue
1033 |
1034 | jk = create_figure(stack_montage_flag)
1035 | layout.children[1] = jk[0]
1036 |
1037 | print('############')
1038 | print('In Button callback')
1039 | tsne3 = jk[1]
1040 | print(type(tsne3))
1041 | print(tsne3)
1042 |
1043 |
1044 | file_name_hover = jk[2]
1045 | print('############fnh in button callback')
1046 | print('file_name_hover')
1047 | print(file_name_hover)
1048 |
1049 |
1050 |
1051 | markers_single = jk[3]
1052 | print('############')
1053 | print(type(markers_single))
1054 | print(markers_single)
1055 |
1056 | cluster_ms_list = jk[4]
1057 | print('############')
1058 | print(type(cluster_ms_list ))
1059 | print(cluster_ms_list )
1060 |
1061 | p1_out = draw_tSNE_scatter(tsne3, file_name_hover, cluster_ms_list)
1062 | p1 = p1_out[0]
1063 | p2 = p1_out[1]
1064 | p3 = p1_out[2]
1065 | p4 = p1_out[3]
1066 |
1067 | source = p1_out[4]
1068 |
1069 | #panel to tabpanel 2024
1070 | tab1 = TabPanel(child=p1, title="Response")
1071 | tab2 = TabPanel(child=p2, title="Treatment")
1072 | tab3 = TabPanel(child=p3, title="Cluster Annotations")
1073 | tab4 = TabPanel(child=p4, title="Patient id")
1074 |
1075 | tabs = Tabs(tabs=[ tab1, tab2, tab3, tab4 ])
1076 | layout.children[2] = tabs
1077 |
1078 | return([p,p1,p2,p3,p4,source,tabs])
1079 |
1080 |
1081 | ############################################################################
1082 | ### create_figure() collects the user choices from the GUI and ####
1083 | ### calls either the generate_stack_montage() for reading in ####
1084 | ### a single image or the generate_image_tSNE() for multiplexed images ####
1085 | ############################################################################
1086 |
1087 |
1088 |
1089 | def create_figure(stack_montage_flag):
1090 |
1091 | p = figure(tools=TOOLS,x_range=x_range, y_range=y_range,width=1000,height=1000)
1092 |
1093 | rb_imtech = radio_button_group_imtech.value
1094 |
1095 | rb = radio_button_group.value
1096 |
1097 | rb_rs = radio_button_group_RS.value
1098 |
1099 | rb_shf = radio_button_group_Shf.value
1100 |
1101 | chk_box_marker = checkbox_group.active
1102 |
1103 | print(chk_box_marker)
1104 |
1105 | chk_box_marker_sm = checkbox_group_sm.active
1106 | print('**chk_box_marker_sm**')
1107 | print(chk_box_marker_sm)
1108 |
1109 |
1110 | if(rb=='No'):
1111 | rb_val = 0
1112 | elif(rb=='Based on Response'):
1113 | rb_val = 1
1114 | elif(rb=='Based on Treatment'):
1115 | rb_val = 2
1116 | elif(rb=='Based on Clusters'):
1117 | rb_val = 3
1118 | elif(rb=='Based on Patient id'):
1119 | rb_val = 4
1120 |
1121 |
1122 |
1123 | if(rb_rs=='Generate random co-ordinates'):
1124 | rb_rs_val = 1
1125 | elif(rb_rs=='Use t-SNE co-ordinates'):
1126 | rb_rs_val = 0
1127 | elif(rb_rs == 'Arrange in rows'):
1128 | rb_rs_val = 2
1129 |
1130 |
1131 | if(rb_shf=='Yes'):
1132 | rb_shf_val = 1
1133 | else:
1134 | rb_shf_val = 0
1135 |
1136 |
1137 |
1138 | if (rb_imtech == 'Vectra'):
1139 | rb_imtech_val = 0
1140 | elif (rb_imtech=='t-CyCIF'):
1141 | rb_imtech_val = 1
1142 | elif (rb_imtech=='CODEX'):
1143 | rb_imtech_val = 2
1144 | elif (rb_imtech=='CyCIF'):
1145 | rb_imtech_val = 3
1146 |
1147 | ## adding in the codex, vectra, tcycif, cycif options
1148 | # collecting the marker names for tcycif/codex stack montage
1149 |
1150 | cluster_ms_list = ['nil'] * len(LABELS_MARKERS)#[]
1151 | clust_ms_list = []
1152 | clust_ms_list_1 = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/markers.csv'),
1153 | header= 0,index_col=None)
1154 | markers_single = np.array(clust_ms_list_1.iloc[:,2]).flatten()
1155 |
1156 |
1157 | ## need to update the markers_single array based on the order the files are read in
1158 | if (stack_montage_flag==True):
1159 | if (rb_imtech_val ==1): #SM for t-CyCIF data
1160 | print('in SM for t-CyCIF')
1161 | ms_list=[]
1162 | cluster_ms_list = []
1163 | for i in range(markers_single.shape[0]):
1164 | print(i)
1165 | print(pat_fov_list[i])
1166 | print(markers_single)
1167 | channel_num = np.int64(pat_fov_list[i].split("_40X_")[1].split(".tiff")[0]) #2024 np.int64
1168 | ms_list.append(markers_single[channel_num-1])
1169 |
1170 |
1171 | markers_single = np.copy(np.array(ms_list))
1172 |
1173 |
1174 |
1175 | # use this updated markers_single name order going forward
1176 | for i in range(markers_single.shape[0]):
1177 |
1178 | cluster_ms_list.append('Channel '+ markers_single[i])
1179 |
1180 | elif (rb_imtech_val ==2): #SM for CODEX
1181 | ## for codex sm for tonsils
1182 | print('in SM for CODEX')
1183 | ## need to update the markers_single array based on the order the files are read in
1184 | ms_list=[]
1185 | cluster_ms_list = []
1186 | for i in range(markers_single.shape[0]):
1187 | print(i)
1188 | print(pat_fov_list[i])
1189 |
1190 | channel_name = pat_fov_list[i].split('.tif')[0].split('_')[3]
1191 | channel_num = np.int64(np.array(np.where(channel_name==markers_single)).flatten()) #2024 np.int64
1192 | ms_list.append(markers_single[channel_num])
1193 |
1194 | markers_single = np.copy(np.array(ms_list))
1195 |
1196 |
1197 | # use this updated markers_single name order going forward
1198 | for i in range(markers_single.shape[0]):
1199 |
1200 | cluster_ms_list.append('Channel '+ markers_single[i])
1201 |
1202 |
1203 | elif (stack_montage_flag == False):
1204 |
1205 |
1206 | # to handle all markers in t-CyCIF/CODEX/Vectra
1207 |
1208 | mc = []
1209 | for m in range(len(LABELS_MARKERS)):
1210 | in_mc = np.int64(np.array(np.where(LABELS_MARKERS[m] == np.array(markers_single))).flatten()) #2024 np.int64
1211 | mc.append(in_mc)
1212 |
1213 | if (rb_imtech_val ==0):#Vectra
1214 | wc = [100] * len(LABELS_MARKERS)
1215 | wc[0] = 800
1216 | wc[1]=wc[2] = 400
1217 | elif (rb_imtech_val ==1):#t-CyCIF
1218 | wc = [100] * len(LABELS_MARKERS)
1219 | wc[0] = 50
1220 | elif (rb_imtech_val ==2):
1221 | wc = [150] * len(LABELS_MARKERS)
1222 | wc[0] = 300 #
1223 | wc[6] = 100
1224 |
1225 | elif (rb_imtech_val ==3):
1226 | wc = [150] * len(LABELS_MARKERS)
1227 | wc[1] = 100
1228 | wc[2] = 300
1229 |
1230 | cluster_ms_list = ['nil'] * num_images #[]
1231 |
1232 |
1233 |
1234 |
1235 | ###########
1236 |
1237 |
1238 | if(len(chk_box_marker_sm)==0): #so we are using the multiple multiplexed option
1239 | if(len(chk_box_marker)==0): #if no markers are chosen, display defaults
1240 |
1241 | file_name_1 = "image_tSNE_GUI/static/image_tsne_tils_all.png"
1242 | p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
1243 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
1244 | df_Xtsne.shape
1245 | tsne_points = np.array(df_Xtsne )
1246 | file_name_hover = list(range(num_images))
1247 |
1248 | else:
1249 |
1250 |
1251 | out1 = generate_image_tSNE(chk_box_marker, rb_val,rb_rs_val,rb_shf_val,rb_imtech_val, mc, wc, LABELS_MARKERS)
1252 | file_name_all = out1[0]
1253 | tsne_points = out1[1]
1254 | file_name_hover = out1[2]
1255 |
1256 | print('file_name_hover after gen_image_tsne')
1257 | print(file_name_hover)
1258 |
1259 | print('#########file_name_all########')
1260 | print('#################')
1261 |
1262 | print(file_name_all)
1263 |
1264 | file_name_1 = file_name_all.split('/code')[1]
1265 | print('#########file_name_1########')
1266 | print(file_name_1)
1267 |
1268 |
1269 | p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
1270 |
1271 | elif(len(chk_box_marker_sm)==1): #stack montage option
1272 | print('chosen stack montage')
1273 | out1sm = generate_stack_montage(chk_box_marker, rb_imtech_val, LABELS_MARKERS)
1274 | file_name_all = out1sm[0]
1275 | tsne_points = out1sm[1]
1276 | file_name_hover = out1sm[2]
1277 |
1278 | print('############fnh post sm')
1279 | print('file_name_hover')
1280 | print(file_name_hover)
1281 |
1282 | print('#########file_name_all########')
1283 | print('#################')
1284 |
1285 | print(file_name_all)
1286 |
1287 | file_name_1 = file_name_all.split('/code')[1]
1288 | print('#########file_name_1########')
1289 | print(file_name_1)
1290 |
1291 |
1292 | p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
1293 |
1294 |
1295 | return ([p,tsne_points, file_name_hover,markers_single, cluster_ms_list])
1296 |
1297 |
1298 | ##########
1299 | # define the colour vectors
1300 | colours_58 = ["firebrick","gold","royalblue","green","dimgray","orchid","darkviolet",
1301 | "red", "orange", "limegreen", "blue", "purple", "seagreen","gold","darkolivegreen",
1302 | "lightpink","thistle","mistyrose","saddlebrown","slategrey","powderblue",
1303 | "palevioletred","mediumvioletred","yellowgreen","lemonchiffon","chocolate",
1304 | "lightsalmon","lightcyan","lightblue", "darkorange","black","darkblue","darkgreen","paleturquoise","yellow","rosybrown",
1305 | "steelblue","dodgerblue","darkkhaki","lime","coral","aquamarine","mediumpurple","violet","plum",
1306 | "deeppink","navy","seagreen","teal","mediumspringgreen","cadetblue",
1307 | "maroon","silver","sienna","crimson","slateblue","magenta","darkmagenta"]
1308 |
1309 | colours_resp = ["yellow","red","green"]
1310 | colours_tx = ["orange","limegreen","violet"]
1311 |
1312 |
1313 | #####################################
1314 | #### Section that gets populated ####
1315 | ####based on User uploads ###########
1316 | #####################################
1317 | #####################################
1318 | ## This section reads in images, ####
1319 | ## metadata, markers in the data,####
1320 | ## and user choices ####
1321 | #####################################
1322 | #####################################
1323 |
1324 |
1325 | path_wd = os.getcwd()
1326 | print('Current working directory: ')
1327 | print(path_wd)
1328 |
1329 |
1330 |
1331 |
1332 | ### to generate tsne points from the onset itself
1333 |
1334 | fname = os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv')
1335 | if os.path.isfile(fname):
1336 | df_Xtsne = pd.read_csv(fname, index_col=None, header= None)
1337 | tsne = np.array(df_Xtsne )
1338 | tx, ty = tsne[:,0], tsne[:,1]
1339 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
1340 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
1341 | num_images = tsne.shape[0]
1342 |
1343 |
1344 |
1345 | ## to have the arrange_in_rows and random points already available
1346 | # Choose up to k points around each reference point as candidates for a new
1347 | # sample point
1348 | k = 10
1349 |
1350 | # Minimum distance between samples
1351 | r = 1.7
1352 |
1353 | width_1, height_1 = 20, 22
1354 |
1355 | print('Generating random co-ordinates')
1356 |
1357 | # Cell side length
1358 | a = r/np.sqrt(2)
1359 | # Number of cells in the x- and y-directions of the grid
1360 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
1361 |
1362 | # A list of coordinates in the grid of cells
1363 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
1364 | # Initilalize the dictionary of cells: each key is a cell's coordinates, the
1365 | # corresponding value is the index of that cell's point's coordinates in the
1366 | # samples list (or None if the cell is empty).
1367 | cells = {coords: None for coords in coords_list}
1368 |
1369 |
1370 |
1371 | # Pick a random point to start with.
1372 | pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
1373 | samples = [pt]
1374 | # Our first sample is indexed at 0 in the samples list...
1375 | cells[get_cell_coords(pt,a)] = 0
1376 | # ... and it is active, in the sense that we're going to look for more points
1377 | # in its neighbourhood.
1378 | active = [0]
1379 |
1380 | nsamples = 1
1381 | # As long as there are points in the active list, keep trying to find samples.
1382 | while (nsamples < num_images): #active:
1383 | # choose a random "reference" point from the active list.
1384 | idx = np.random.choice(active)
1385 | refpt = samples[idx]
1386 | # Try to pick a new point relative to the reference point.
1387 | pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
1388 | if pt:
1389 | # Point pt is valid: add it to the samples list and mark it as active
1390 | samples.append(pt)
1391 | nsamples += 1
1392 | active.append(len(samples)-1)
1393 | cells[get_cell_coords(pt,a)] = len(samples) - 1
1394 | print('nsamples is: ',str(nsamples))
1395 | else:
1396 | # We had to give up looking for valid points near refpt, so remove it
1397 | # from the list of "active" points.
1398 | active.remove(idx)
1399 |
1400 | tsne = np.asarray(samples)
1401 | tx, ty = tsne[:,0], tsne[:,1]
1402 |
1403 | tx[tx ==0] = 0.2
1404 | ty[ty ==0] = 0.1
1405 |
1406 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
1407 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
1408 |
1409 | df_Xtsne_m = pd.DataFrame(tsne)
1410 | #df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)
1411 | df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None) #14th march 2022
1412 |
1413 |
1414 | ##### for 'arrange in rows' option at the onset itself
1415 |
1416 |
1417 | # Minimum distance between samples
1418 | r = 2
1419 |
1420 | width_1, height_1 = 20, 22
1421 |
1422 | print('Arrange images side-by-side')
1423 |
1424 | # Cell side length
1425 | a = r/np.sqrt(2)
1426 | # Number of cells in the x- and y-directions of the grid
1427 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
1428 |
1429 | # A list of coordinates in the grid of cells
1430 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
1431 |
1432 |
1433 | #logic to get grid points - 29th March 2021
1434 | m = np.int64(np.floor(nx*ny/num_images)) #2024 np.int to int64
1435 |
1436 |
1437 | row_needed = []
1438 | def multiples(m, num_images):
1439 | for i in range(num_images):
1440 | row_needed.append(i*m)
1441 |
1442 | multiples(m,num_images)
1443 |
1444 |
1445 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
1446 |
1447 | print(type(select_coords))
1448 |
1449 | tsne1 = select_coords
1450 |
1451 | tsne1[tsne1 ==0] = 0.2
1452 |
1453 | df_Xtsne = pd.DataFrame(tsne1)
1454 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)
1455 |
1456 |
1457 | else:
1458 | #read in images
1459 | FoV_path = os.path.join(path_wd + '/user_inputs/figures/')
1460 | count_images = 0
1461 | for fname in os.listdir(FoV_path):
1462 | print(fname)
1463 | count_images = count_images + 1
1464 | num_images = count_images
1465 | #generate random 2D coords for these images
1466 |
1467 | ####
1468 | #### tsne and clustering within mistic, if not provided by user
1469 | ####
1470 |
1471 | im1 = tifffile.imread(os.path.join(FoV_path+fname))
1472 |
1473 | if (im1.shape[0] < 6):
1474 | num_markers = im1.shape[0]
1475 | else:
1476 | num_markers = 6
1477 |
1478 | data_CM = np.zeros((1,num_markers)) # one row = one image with mean of markers in the columns, defaults to 6 markers
1479 |
1480 | for fname in os.listdir(FoV_path):
1481 | print(fname)
1482 | im1 = tifffile.imread(os.path.join(FoV_path+fname))
1483 | if ((im1.shape[0]==16) and (im1.shape[1]==4) and (im1.shape[2]==5040) and (im1.shape[3]==9408)): #for codex intake
1484 | im1 = im1.reshape(64,5040,9408)
1485 |
1486 | #if (rb_imtech_val ==2): #for codex
1487 | # im = im.reshape(64,5040,9408)
1488 |
1489 |
1490 |
1491 | image_summary_vec = np.zeros((1,num_markers))
1492 | for m in range(num_markers):
1493 | image1 = im1[m]
1494 |
1495 | median_filtered1 = scipy.ndimage.median_filter(image1, size=1)
1496 |
1497 |
1498 | thresh = threshold_otsu(median_filtered1)
1499 |
1500 |
1501 | bw = closing(image1 > thresh, square(1))
1502 |
1503 | # remove artifacts connected to image border
1504 | cleared_imtsne = clear_border(bw)
1505 |
1506 |
1507 | image_summary_vec[0,m] = np.mean(cleared_imtsne) #mean of each channel m per image fname
1508 |
1509 |
1510 |
1511 | data_CM = np.concatenate((data_CM,image_summary_vec),axis=0)
1512 |
1513 | data_CM = np.delete(data_CM, (0), axis=0)
1514 |
1515 | # perform tSNE
1516 | tsne = TSNE(n_components=2, verbose=1, perplexity=5)#, n_iter=900) #2024, setting perplexity value to be thresh)#2024, square(1))
1802 |
1803 | # remove artifacts connected to image border
1804 | cleared_imtsne = clear_border(bw)
1805 |
1806 |
1807 | image_summary_vec[0,m] = np.mean(cleared_imtsne) #mean of each channel m per image fname
1808 |
1809 |
1810 |
1811 | data_CM = np.concatenate((data_CM,image_summary_vec),axis=0)
1812 |
1813 | data_CM = np.delete(data_CM, (0), axis=0)
1814 |
1815 |
1816 |
1817 | # Fit a Dirichlet process mixture of Gaussians using n components
1818 | bgm = BayesianGaussianMixture(n_components=3, random_state=42).fit(data_CM)
1819 | cluster_asgn = bgm.predict(data_CM)
1820 |
1821 | df_dpgmm_u = pd.DataFrame(cluster_asgn)
1822 | df_dpgmm_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv'), header=None, index=None)
1823 |
1824 |
1825 | fname2 = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')
1826 |
1827 |
1828 | clust_asgn_list_2 = np.array(pd.read_csv(fname2, header= None,index_col=None)).flatten()
1829 | for i in range(num_images):
1830 |
1831 | color_vec_clasgn.append(colours_58[clust_asgn_list_2[i]])
1832 | clust_asgn_list.append(clust_asgn_list_2[i])
1833 | cluster_anno_list.append('Cluster '+ str(clust_asgn_list_2[i]))
1834 |
1835 | ## bayes clustering end
1836 |
1837 |
1838 |
1839 | # collecting the patient id metadata
1840 |
1841 | fname = os.path.join(path_wd + '/user_inputs/metadata/Patient_ids.csv')
1842 | color_vec_patid = []
1843 | cluster_pat_list = []
1844 | clust_patid_list = []
1845 |
1846 | if os.path.isfile(fname):
1847 |
1848 |
1849 | # collecting the Patient id metadata
1850 |
1851 | clust_patid_list_1 = np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
1852 | pat_ind_list = np.copy(clust_patid_list_1)
1853 |
1854 | for i in range(clust_patid_list_1.shape[0]):
1855 |
1856 | color_vec_patid.append(colours_58[clust_patid_list_1[i]])
1857 | clust_patid_list.append(clust_patid_list_1[i])
1858 | cluster_pat_list.append('Patient '+ str(clust_patid_list_1[i]))
1859 |
1860 | else:
1861 | pat_ind_list = []
1862 | for i in range(num_images):
1863 | color_vec_patid.append('gray')
1864 | clust_patid_list.append('nil')
1865 | cluster_pat_list.append('Patient nil')
1866 | pat_ind_list.append('nil')
1867 |
1868 | # generate dummy thumbnail names for hover panel
1869 |
1870 | file_name_hover = []
1871 | file_name_hover_list = []
1872 |
1873 | for i in range(num_images): #clust_patid_list_1.shape[0]):
1874 |
1875 | file_name_hover.append(str(i))
1876 | file_name_hover_list.append('Thumbnail '+ str(i))
1877 |
1878 |
1879 |
1880 |
1881 |
1882 | #################
1883 | # set up widgets
1884 | #################
1885 |
1886 | RB_1 = ['Based on Response', 'Based on Treatment','Based on Clusters','Based on Patient id','No']
1887 |
1888 | RB_2 = ['Generate random co-ordinates', 'Use t-SNE co-ordinates', 'Arrange in rows']
1889 |
1890 | RB_3 = ['Yes', 'No']
1891 |
1892 | RB_4 = ['Vectra', 't-CyCIF', 'CODEX', 'CyCIF']
1893 |
1894 | TS_1 = ['black','gray', 'dark blue']
1895 |
1896 | TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,save,box_select,"
1897 |
1898 | x_range=(0,1)
1899 | y_range=(0,1)
1900 |
1901 |
1902 | #main image tSNE canvas
1903 | p = figure(tools=TOOLS,x_range=x_range, y_range=y_range,width=1000,height=1000)
1904 | tsne_points = np.zeros([1,2])
1905 |
1906 | #additional tSNE scatter plot canvas
1907 |
1908 |
1909 | #2024 plot_width to width, same for height
1910 | point_tSNE = figure(width=350, height=350,
1911 | tools='hover,pan,wheel_zoom,box_select,reset')
1912 | point_tSNE.title.text = 'tSNE point cloud for Patient response'
1913 |
1914 |
1915 | point_tSNE.scatter(tsne[:,0],tsne[:,1],fill_alpha=0.6, color ='red',size=8,legend_label='Response') #2024 legend to legend_label
1916 |
1917 | point_tSNE.legend.location = "bottom_left"
1918 |
1919 | #2024 Figure to figure
1920 | theme_black = Theme(json={
1921 | 'attrs': {
1922 | 'figure': {
1923 | 'background_fill_color': '#2F2F2F',
1924 | 'border_fill_color': '#2F2F2F',
1925 | 'outline_line_color': '#444444'
1926 | },
1927 | 'Axis': {
1928 | 'axis_line_color': "white",
1929 | 'axis_label_text_color': "white",
1930 | 'major_label_text_color': "white",
1931 | 'major_tick_line_color': "white",
1932 | 'minor_tick_line_color': "white",
1933 | 'minor_tick_line_color': "white"
1934 | },
1935 | 'Grid': {
1936 | 'grid_line_dash': [6, 4],
1937 | 'grid_line_alpha': .3
1938 | },
1939 | 'Circle': {
1940 | 'fill_color': 'lightblue',
1941 | 'size': 10,
1942 | },
1943 | 'Title': {
1944 | 'text_color': "white"
1945 | }
1946 | }
1947 | })
1948 |
1949 | theme_gray = Theme(json={
1950 | 'attrs': {
1951 | 'figure': {
1952 | 'background_fill_color': '#555555',
1953 | 'border_fill_color': '#2F2F2F',
1954 | 'outline_line_color': '#444444'
1955 | },
1956 | 'Axis': {
1957 | 'axis_line_color': "white",
1958 | 'axis_label_text_color': "white",
1959 | 'major_label_text_color': "white",
1960 | 'major_tick_line_color': "white",
1961 | 'minor_tick_line_color': "white",
1962 | 'minor_tick_line_color': "white"
1963 | },
1964 | 'Grid': {
1965 | 'grid_line_dash': [6, 4],
1966 | 'grid_line_alpha': .3
1967 | },
1968 | 'Circle': {
1969 | 'fill_color': 'lightblue',
1970 | 'size': 10,
1971 | },
1972 | 'Title': {
1973 | 'text_color': "white"
1974 | }
1975 | }
1976 | })
1977 |
1978 | theme_blue = Theme(json={
1979 | 'attrs': {
1980 | 'figure': {
1981 | 'background_fill_color': '#25256d',
1982 | 'border_fill_color': '#2F2F2F',
1983 | 'outline_line_color': '#444444'
1984 | },
1985 | 'Axis': {
1986 | 'axis_line_color': "white",
1987 | 'axis_label_text_color': "white",
1988 | 'major_label_text_color': "white",
1989 | 'major_tick_line_color': "white",
1990 | 'minor_tick_line_color': "white",
1991 | 'minor_tick_line_color': "white"
1992 | },
1993 | 'Grid': {
1994 | 'grid_line_dash': [6, 4],
1995 | 'grid_line_alpha': .3
1996 | },
1997 | 'Circle': {
1998 | 'fill_color': 'lightblue',
1999 | 'size': 10,
2000 | },
2001 | 'Title': {
2002 | 'text_color': "white"
2003 | }
2004 | }
2005 | })
2006 |
2007 | #########
2008 | #########
2009 |
2010 |
2011 | TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
2012 |
2013 |
2014 | TOOLTIPS = [
2015 | ("index", "$index"),
2016 | ("(x,y)", "($x, $y)"),
2017 | ("Pat_id", "@pat_list"),
2018 | ("Response", "@res_list"),
2019 | ("Treatment", "@tx_list"),
2020 | ("Cluster id", "@clust_asgn_list"),
2021 | ("Channel", "@cluster_ms_list"),
2022 | ("Thumbnail", "@file_name_hover_list"),
2023 | ("FoV","@fov_list")
2024 | ]
2025 |
2026 | p1 = figure(width=400, height=400, tooltips=TOOLTIPS,tools = TOOLS,
2027 | title="Patient response") #2024 width and height
2028 |
2029 |
2030 |
2031 |
2032 | #####################################################################################
2033 | ## This block prepares and sets up the GUI layout, and collects user-input choices ##
2034 | #####################################################################################
2035 |
2036 |
2037 | desc = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/desc.html')).read(), sizing_mode="stretch_width")
2038 |
2039 |
2040 | desc_SM1 = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/descSM1.html')).read(), sizing_mode="stretch_width")
2041 |
2042 | desc_SM = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/descMontage.html')).read(), sizing_mode="stretch_width")
2043 |
2044 |
2045 |
2046 | radio_button_group_imtech = Select(value='Vectra',
2047 | title='',
2048 | width=200,
2049 | options=RB_4)
2050 |
2051 |
2052 | radio_button_group = Select(value='No',
2053 | title='Image border',
2054 | width=200,
2055 | options=RB_1)
2056 |
2057 | radio_button_group_RS = Select(value='Generate new co-ordinates',
2058 | title='tSNE co-ordinates',
2059 | width=220,
2060 | options=RB_2)
2061 |
2062 | radio_button_group_Shf = Select(value='Yes',
2063 | title='Shuffle images',
2064 | width=200,
2065 | options=RB_3)
2066 |
2067 | theme_select = Select(value = 'black',
2068 | title='Theme',
2069 | width = 200,
2070 | options = TS_1)
2071 |
2072 |
2073 | checkbox_group = CheckboxGroup(labels=LABELS_MARKERS)#, active=[0, 1])
2074 |
2075 |
2076 | button = Button(label='Run', width=100, button_type="success")
2077 |
2078 | ## added to activate Run button
2079 | button.on_click(button_callback)
2080 |
2081 | ## for stack montage
2082 | SM = ['Stack montage']
2083 | checkbox_group_sm = CheckboxGroup(labels=SM)
2084 |
2085 |
2086 | print('updating new p1#')
2087 |
2088 |
2089 |
2090 | # set up layout and GUI refresh after every 'Run' click
2091 |
2092 | out2 = create_figure(stack_montage_flag)
2093 | print(out2)
2094 | p = out2[0]
2095 | tsne2 = out2[1]
2096 | print('############')
2097 | print(type(tsne2))
2098 | print(tsne2)
2099 |
2100 | file_name_hover = out2[2]
2101 | print('############fnh after create figure')
2102 | print(type(file_name_hover))
2103 | print(file_name_hover)
2104 |
2105 |
2106 | markers_single = out2[3]
2107 | print('############')
2108 | print(type(markers_single))
2109 | print(markers_single)
2110 |
2111 | cluster_ms_list = out2[4]
2112 | print('############')
2113 | print(type(cluster_ms_list ))
2114 | print(cluster_ms_list )
2115 |
2116 |
2117 | p11_out = draw_tSNE_scatter(tsne2, file_name_hover,cluster_ms_list )
2118 |
2119 | p1 = p11_out[0]
2120 | p2 = p11_out[1]
2121 | p3 = p11_out[2]
2122 | p4 = p11_out[3]
2123 |
2124 | #panel to tabpanel 2024
2125 | tab1 = TabPanel(child=p1, title="Response")
2126 | tab2 = TabPanel(child=p2, title="Treatment")
2127 | tab3 = TabPanel(child=p3, title="Cluster Annotations")
2128 | tab4 = TabPanel(child=p4, title="Patient id")
2129 |
2130 | tabs = Tabs(tabs=[ tab1, tab2, tab3, tab4 ]) # (added tab4)
2131 |
2132 |
2133 |
2134 | selects = column(desc, radio_button_group_imtech, desc_SM1, checkbox_group_sm, desc_SM, checkbox_group, radio_button_group, radio_button_group_RS, radio_button_group_Shf, theme_select, button, width=520) #
2135 |
2136 | layout=row(selects,p, tabs)#,selected_points)#create_figure())
2137 |
2138 | #doc = curdoc()
2139 | curdoc().theme= theme_black
2140 |
2141 |
2142 | # add to document
2143 | curdoc().add_root(layout)
2144 |
2145 | curdoc().title = "Mistic: Image tSNE viewer"
2146 |
2147 |
2148 |
2149 | # cd image_tSNE_code/bokeh_GUI/bokeh-branch-2.3/examples/app
2150 | # bokeh serve --port 5098 --show image_tSNE_GUI
2151 |
2152 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/main_old.py:
--------------------------------------------------------------------------------
1 | ### Code for Mistic
2 | ### Image t-SNE viewer for multiplexed images
3 | ###
4 | ### code author Sandhya Prabhakaran
5 | ###
6 | ### Revision history
7 | ### FoV code ###
8 | ### Jan 29th 2021
9 | ### 25th June 2021
10 | ### github version
11 | ### 19th Jan 2022
12 | ### added Patient id live canvas
13 | ### 26th Jan 2022 ###
14 | ### merging the stack montage code
15 | ### prior version is main_rollback.
16 | ### 14th Feb 2022
17 | ### adding codex, tcycif, vectra option
18 | ### prior version is main_rollback_1
19 | ### 14th mar 2022
20 | ### added the user tsne as well 'arrange in rows' (seeall) tsne generation code
21 |
22 | ## 20th Feb 2022
23 | ## this is the latest main.py, with dates and comments removed
24 | ## for github upload
25 |
26 | ## 8th April 2022
27 | ## 2nd rebuttal updates
28 | ## tsne and dpmm code added
29 |
30 | #### 21st April 2022
31 | #### CyCIF files
32 | ## code cleanup for github
33 |
34 |
35 | import os
36 | import sys
37 | import random
38 | import warnings
39 |
40 | import numpy as np
41 | import pandas as pd
42 |
43 | import matplotlib
44 | import matplotlib.pyplot as plt
45 | import matplotlib.patches as mpatches
46 |
47 | from matplotlib.figure import Figure
48 |
49 | from matplotlib import cm
50 |
51 | from scipy import ndimage
52 |
53 | from sklearn.manifold import TSNE
54 |
55 | import phenograph as pg
56 | from scipy.stats import zscore
57 |
58 | from skimage.color import rgb2gray
59 |
60 | from matplotlib.patches import Polygon
61 |
62 | from skimage import io
63 |
64 | from skimage import data
65 | from skimage.filters import threshold_otsu
66 | from skimage.segmentation import clear_border
67 | from skimage.measure import label, regionprops
68 | from skimage.morphology import closing, square
69 | from skimage.color import label2rgb
70 | from skimage.io import imread, imshow, imread_collection, concatenate_images
71 | from skimage.transform import resize
72 | from skimage.morphology import label
73 |
74 | import seaborn as sns
75 |
76 | import scipy.spatial
77 |
78 | from scipy.spatial import distance
79 |
80 |
81 | import seaborn as sns
82 |
83 |
84 | import matplotlib.image as mpimg
85 |
86 |
87 | import matplotlib.colors as colors
88 |
89 | from matplotlib import colors as mcolors
90 |
91 | colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
92 |
93 | from PIL import Image, ImageOps
94 |
95 | from bokeh.layouts import column, row
96 | from bokeh.models import (Select, Button)
97 | from bokeh.palettes import Spectral5
98 | from bokeh.plotting import curdoc, figure
99 | from bokeh.models.widgets import Div
100 | from bokeh.layouts import column, layout
101 |
102 | from bokeh.models import (HoverTool, ColumnDataSource)
103 | from bokeh.models.widgets import RadioButtonGroup
104 | from bokeh.models import CheckboxGroup
105 | from bokeh.models import CustomJS
106 | from bokeh.models import BoxSelectTool
107 | from bokeh.themes import Theme
108 |
109 | from bokeh.models.widgets import Panel, Tabs
110 | from bokeh.io import output_file, show
111 |
112 |
113 | ##
114 | import skimage.io as io
115 | import tifffile
116 | from PIL import TiffImagePlugin
117 |
118 | from sklearn.mixture import BayesianGaussianMixture
119 |
120 |
121 |
122 | ### image-tSNE
123 |
124 | width = 5000
125 | height = 5000
126 | max_dim = 500
127 | tw =1200
128 | th = 900
129 |
130 | full_image_1 = Image.new('RGBA', (width, height))
131 |
132 |
133 |
134 | ##############################################################
135 |
136 | ################## Function definitions ######################
137 |
138 | ##############################################################
139 |
140 |
141 | #######################################################################
142 | ##### get_cell_coords(), get_neighbours(), point_valid(), ####
143 | ##### get_point() are functions to generate random points ####
144 | ##### functions modified from: ####
145 | ##### https://scipython.com/blog/poisson-disc-sampling-in-python/ ####
146 | #######################################################################
147 |
148 |
149 |
150 | def get_cell_coords(pt,a):
151 | """Get the coordinates of the cell that pt = (x,y) falls in."""
152 |
153 | return int(pt[0] // a), int(pt[1] // a)
154 |
155 | def get_neighbours(coords,nx,ny,cells):
156 | """Return the indexes of points in cells neighbouring cell at coords.
157 | For the cell at coords = (x,y), return the indexes of points in the cells
158 | with neighbouring coordinates illustrated below: ie those cells that could
159 | contain points closer than r.
160 | ooo
161 | ooooo
162 | ooXoo
163 | ooooo
164 | ooo
165 | """
166 |
167 | dxdy = [(-1,-2),(0,-2),(1,-2),(-2,-1),(-1,-1),(0,-1),(1,-1),(2,-1),
168 | (-2,0),(-1,0),(1,0),(2,0),(-2,1),(-1,1),(0,1),(1,1),(2,1),
169 | (-1,2),(0,2),(1,2),(0,0)]
170 | neighbours = []
171 | for dx, dy in dxdy:
172 | neighbour_coords = coords[0] + dx, coords[1] + dy
173 | if not (0 <= neighbour_coords[0] < nx and
174 | 0 <= neighbour_coords[1] < ny):
175 | # We're off the grid: no neighbours here.
176 | continue
177 | neighbour_cell = cells[neighbour_coords]
178 | if neighbour_cell is not None:
179 | # This cell is occupied: store this index of the contained point.
180 | neighbours.append(neighbour_cell)
181 | return neighbours
182 |
183 | def point_valid(pt,a,nx,ny,cells,samples,r):
184 | """Is pt a valid point to emit as a sample?
185 | It must be no closer than r from any other point: check the cells in its
186 | immediate neighbourhood.
187 | """
188 |
189 | cell_coords = get_cell_coords(pt,a)
190 | for idx in get_neighbours(cell_coords,nx,ny,cells):
191 | nearby_pt = samples[idx]
192 | # Squared distance between or candidate point, pt, and this nearby_pt.
193 | distance2 = (nearby_pt[0]-pt[0])**2 + (nearby_pt[1]-pt[1])**2
194 | if distance2 < r**2:
195 | # The points are too close, so pt is not a candidate.
196 | return False
197 | # All points tested: if we're here, pt is valid
198 | return True
199 |
200 | def get_point(k, refpt,r,a,nx,ny,cells,samples):
201 | """Try to find a candidate point relative to refpt to emit in the sample.
202 | We draw up to k points from the annulus of inner radius r, outer radius 2r
203 | around the reference point, refpt. If none of them are suitable (because
204 | they're too close to existing points in the sample), return False.
205 | Otherwise, return the pt.
206 | """
207 | i = 0
208 | while i < k:
209 | rho, theta = np.random.uniform(r, 2*r), np.random.uniform(0, 2*np.pi)
210 | pt = refpt[0] + rho*np.cos(theta), refpt[1] + rho*np.sin(theta)
211 | if not (0 <= pt[0] < width and 0 <= pt[1] < height):
212 | # This point falls outside the domain, so try again.
213 | continue
214 | if point_valid(pt,a,nx,ny,cells,samples,r):
215 | return pt
216 | i += 1
217 | # We failed to find a suitable point in the vicinity of refpt.
218 | return False
219 |
220 | ###############################################################
221 | ### draw_tSNE_scatter() ####
222 | ### a) creates the metadata-based tSNE scatter plots ####
223 | ### b) populates the hover functionality for each tSNE dot ####
224 | ###############################################################
225 |
226 | def draw_tSNE_scatter(tsne1, file_name_hover,cluster_ms_list ):
227 | tsne=np.asarray(tsne1)
228 |
229 | source = ColumnDataSource(data=dict(
230 | x=tsne[:,0],
231 | y=tsne[:,1],
232 | pat_list = pat_ind_list,
233 | res_list = resp_list,
234 | fov_list = pat_fov_list,
235 | color_vec_list = color_vec,
236 | tx_list = tx_list,
237 | color_vec_tx_list = color_vec_tx,
238 | clust_asgn_list = clust_asgn_list,
239 | color_vec_clasgn_list = color_vec_clasgn,
240 | cluster_anno_list = cluster_anno_list,
241 | cluster_ms_list = cluster_ms_list,
242 | cluster_pat_list = cluster_pat_list,
243 | color_vec_patid_list = color_vec_patid,
244 | file_name_hover_list = file_name_hover
245 | #legend_p11 = legend_p1
246 | ))
247 | TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
248 |
249 | TOOLTIPS = [
250 | ("index", "$index"),
251 | ("(x,y)", "($x, $y)"),
252 | ("Pat_id", "@pat_list"),
253 | ("Response", "@res_list"),
254 | ("Treatment", "@tx_list"),
255 | ("Cluster id", "@clust_asgn_list"),
256 | ("Channel", "@cluster_ms_list"),
257 | ("Thumbnail", "@file_name_hover_list"),
258 | ("FoV","@fov_list")
259 | ]
260 |
261 |
262 |
263 |
264 | p1 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
265 | title="Patient response")
266 |
267 | p1.scatter('x', 'y', size=10, source=source, legend= 'res_list', color = 'color_vec_list',fill_alpha=0.6)
268 | p1.legend.location = "bottom_left"
269 |
270 |
271 | p2 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
272 | title="Treatment category")
273 | p2.scatter('x', 'y', size=10, source=source, legend= 'tx_list', color = 'color_vec_tx_list',fill_alpha=0.6)
274 | p2.legend.location = "bottom_left"
275 |
276 |
277 | p3 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
278 | title="Cluster annotations")
279 | p3.scatter('x', 'y', size=10, source=source, legend= 'cluster_anno_list', color = 'color_vec_clasgn_list',fill_alpha=0.6)
280 | p3.legend.location = "bottom_left"
281 |
282 |
283 |
284 | p4 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
285 | title="Patient id")
286 | p4.scatter('x', 'y', size=10, source=source, legend= 'cluster_pat_list', color = 'color_vec_patid_list',fill_alpha=0.6)
287 | p4.legend.location = "bottom_left"
288 |
289 |
290 | return ([p1,p2,p3,p4, source])
291 |
292 |
293 |
294 |
295 |
296 | ##############################################################################################
297 | ### generate_stack_montage() ####
298 | ### a) reads in and processes the image channels ####
299 | ### b) generates evenly-spaced points on the static canvas to arrange the images in rows ####
300 | ### c) generates thumbnails, and pastes these onto the static canvas ####
301 | ### d) stores the thumbnails in the output folder ####
302 | ### e) updates the hover tool with thumbnail paths, marker names and metadata ####
303 | ##############################################################################################
304 |
305 |
306 | ##################
307 | def generate_stack_montage(chk_box_marker_sm, rb_imtech_val, LABELS_MARKERS):
308 |
309 |
310 |
311 | full_image = Image.new('RGBA', (width, height))
312 | size = [256,256]
313 |
314 | file_name_hover = []
315 | file_name_hover_list = []
316 |
317 | # Choose up to k points around each reference point as candidates for a new
318 | # sample point
319 | k = 10
320 |
321 | # Minimum distance between samples
322 | r = 2
323 |
324 | width_1, height_1 = 20, 22
325 |
326 | print('Arrange images side-by-side')
327 |
328 | # Cell side length
329 | a = r/np.sqrt(2)
330 | # Number of cells in the x- and y-directions of the grid
331 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
332 |
333 | # A list of coordinates in the grid of cells
334 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
335 |
336 |
337 | #logic to get grid points
338 | m = np.int(np.floor(nx*ny/num_images))
339 |
340 |
341 | row_needed = []
342 | def multiples(m, num_images):
343 | for i in range(num_images):
344 | row_needed.append(i*m)
345 |
346 | multiples(m,num_images)
347 |
348 |
349 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
350 |
351 | print(type(select_coords))
352 | ################
353 |
354 | #tsne = np.asarray(coords_list)
355 | tsne = select_coords
356 | df_Xtsne = pd.DataFrame(tsne)
357 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_sm.csv'), header=None, index=None)
358 |
359 |
360 |
361 | # save the tSNE points to bve read later on irrespective of whoch option was chosen
362 | df_Xtsne_touse = pd.DataFrame(tsne)
363 | df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse_sm.csv'), header=None, index=None)
364 |
365 | tx, ty = tsne[:,0], tsne[:,1]
366 |
367 | tx[tx ==0] = 0.2
368 | ty[ty ==0] = 0.1
369 |
370 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
371 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
372 | print('###tx')
373 | print(tx)
374 | print(ty)
375 | inds = range(len(marker_image_list))
376 |
377 | N = len(inds)
378 |
379 |
380 | for k in range(len(inds)):
381 |
382 | image_all_2 = []
383 | image_all_1 = []
384 |
385 | im = Image.open(marker_image_list[inds[k]])
386 |
387 |
388 | for m in range(1):
389 | image = im
390 |
391 | median_filtered = scipy.ndimage.median_filter(image, size=1)
392 | image_all_2.append(median_filtered)
393 |
394 |
395 | # apply threshold
396 |
397 | thresh = threshold_otsu(image_all_2[m])
398 |
399 |
400 |
401 |
402 | print('thresh is: ', str(thresh))
403 |
404 | bw = closing(image > thresh, square(1))
405 |
406 | # remove artifacts connected to image border
407 | cleared_imtsne = clear_border(bw)
408 |
409 |
410 | image_all_1.append(cleared_imtsne*100)
411 |
412 | tl = sum(image_all_1)
413 |
414 | if (rb_imtech_val ==0): #vectra
415 | tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))
416 |
417 | elif (rb_imtech_val ==1): #t-CyCIF
418 | tile_1 = Image.fromarray(np.uint8(cm.hsv(tl)*500))
419 | elif (rb_imtech_val ==2): # CODEX
420 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
421 | elif (rb_imtech_val ==3): # CyCIF
422 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
423 |
424 | old_size = tile_1.size
425 |
426 |
427 |
428 |
429 |
430 | new_size = (old_size[0]+1, old_size[1]+1)
431 | new_im = Image.new("RGB", new_size,color='black')
432 |
433 | new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))
434 |
435 |
436 |
437 | file_name = os.path.join(path_wd+'/output_tiles/sm_image_tsne_tils'+str(k)+'.png')
438 |
439 | file_name_hover.append('sm_image_tsne_tils'+str(k)+'.png')
440 |
441 | new_im.save(file_name)
442 |
443 | # Read Images
444 |
445 |
446 | tile_2 = Image.open(file_name)
447 | rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
448 | tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias
449 |
450 | full_image.paste(tile_2, (int((width-max_dim)*ty[k]), int((height-max_dim)*tx[k])),mask=tile_2.convert('RGBA'))
451 |
452 | matplotlib.pyplot.figure(figsize = (25,20))
453 | plt.imshow(full_image)
454 | plt.axis('off')
455 |
456 |
457 |
458 |
459 |
460 | k = random.randint(2,500)
461 | file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/sm_image_tsne_tils_all_'+str(k)+'.png')
462 | full_image.save(file_name)
463 |
464 |
465 |
466 | rotated_img = full_image.rotate(90)
467 | file_name_rot = os.path.join(path_wd+'/image_tSNE_GUI/static/sm_image_tsne_tils_all_'+str(k)+'_rot.png')
468 |
469 |
470 | rotated_img.save(file_name_rot)
471 |
472 | return([file_name_rot,tsne, file_name_hover])
473 |
474 |
475 |
476 | ###########################################################################################################
477 | ### generate_image_tSNE() ########
478 | ### ########
479 | ### a) reads in and pre-processes the images ########
480 | ### b) generates random points or evenly-spaced points on the static canvas to arrange the images ########
481 | ### in rows or reads in the user-provided tSNE ########
482 | ### c) generates thumbnails based on border choices, pastes the thumbnails onto the static canvas ########
483 | ### d) stores the thumbnails in the output folder ########
484 | ### e) updates the hover tool with thumbnail paths ########
485 | ### f) shuffle or no shuffle option is handled in this function where images are randomly shuffled ########
486 | ###########################################################################################################
487 |
488 | def generate_image_tSNE(chk_box_marker,rb_val,rb_rs_val,rb_shf_val, rb_imtech_val, mc, wc, LABELS_MARKERS):
489 | full_image = Image.new('RGBA', (width, height))
490 | size = [256,256]
491 |
492 | file_name_hover = []
493 | file_name_hover_list = []
494 |
495 | if (rb_shf_val == 0): # no shuffle option
496 |
497 |
498 | if(rb_rs_val==1):
499 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), index_col=None, header= None)
500 | print('usertsne')
501 | elif(rb_rs_val==0):
502 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
503 | print('origtsne')
504 |
505 | elif(rb_rs_val==2):
506 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), index_col=None, header= None)
507 | print('seealltsne')
508 |
509 | tsne = np.array(df_Xtsne)
510 |
511 | '''
512 | #create the random tsne projections
513 | if (rb_rs_val==1):
514 | # Choose up to k points around each reference point as candidates for a new
515 | # sample point
516 | k = 10
517 |
518 | # Minimum distance between samples
519 | r = 1.7
520 |
521 | width_1, height_1 = 20, 22
522 |
523 | print('Generating random co-ordinates')
524 |
525 | # Cell side length
526 | a = r/np.sqrt(2)
527 | # Number of cells in the x- and y-directions of the grid
528 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
529 |
530 | # A list of coordinates in the grid of cells
531 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
532 | # Initilalize the dictionary of cells: each key is a cell's coordinates, the
533 | # corresponding value is the index of that cell's point's coordinates in the
534 | # samples list (or None if the cell is empty).
535 | cells = {coords: None for coords in coords_list}
536 |
537 |
538 |
539 | # Pick a random point to start with.
540 | pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
541 | samples = [pt]
542 | # Our first sample is indexed at 0 in the samples list...
543 | cells[get_cell_coords(pt,a)] = 0
544 | # ... and it is active, in the sense that we're going to look for more points
545 | # in its neighbourhood.
546 | active = [0]
547 |
548 | nsamples = 1
549 | # As long as there are points in the active list, keep trying to find samples.
550 | while (nsamples < num_images): #active:
551 | # choose a random "reference" point from the active list.
552 | idx = np.random.choice(active)
553 | refpt = samples[idx]
554 | # Try to pick a new point relative to the reference point.
555 | pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
556 | if pt:
557 | # Point pt is valid: add it to the samples list and mark it as active
558 | samples.append(pt)
559 | nsamples += 1
560 | active.append(len(samples)-1)
561 | cells[get_cell_coords(pt,a)] = len(samples) - 1
562 | print('nsamples is: ',str(nsamples))
563 | else:
564 | # We had to give up looking for valid points near refpt, so remove it
565 | # from the list of "active" points.
566 | active.remove(idx)
567 |
568 | tsne = np.asarray(samples)
569 | df_Xtsne = pd.DataFrame(tsne)
570 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None)
571 |
572 |
573 | elif(rb_rs_val==0):
574 |
575 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
576 | df_Xtsne.shape
577 | tsne = np.array(df_Xtsne )
578 |
579 | elif(rb_rs_val==2):
580 |
581 | # Choose up to k points around each reference point as candidates for a new
582 | # sample point
583 | k = 10
584 |
585 | # Minimum distance between samples
586 | r = 2
587 |
588 | width_1, height_1 = 20, 22
589 |
590 | print('Arrange images side-by-side')
591 |
592 | # Cell side length
593 | a = r/np.sqrt(2)
594 | # Number of cells in the x- and y-directions of the grid
595 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
596 |
597 | # A list of coordinates in the grid of cells
598 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
599 |
600 |
601 | #logic to get grid points
602 | m = np.int(np.floor(nx*ny/num_images))
603 |
604 |
605 | row_needed = []
606 | def multiples(m, num_images):
607 | for i in range(num_images):
608 | row_needed.append(i*m)
609 |
610 | multiples(m,num_images)
611 |
612 |
613 |
614 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
615 |
616 | print(type(select_coords))
617 | ################
618 |
619 | #tsne = np.asarray(coords_list)
620 | tsne = select_coords
621 | df_Xtsne = pd.DataFrame(tsne)
622 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)
623 |
624 |
625 | '''
626 | # save the tSNE points to bve read later on irrespective of whoch option was chosen
627 | df_Xtsne_touse = pd.DataFrame(tsne)
628 | df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse.csv'), header=None, index=None)
629 |
630 | tx, ty = tsne[:,0], tsne[:,1]
631 |
632 | tx[tx ==0] = 0.2
633 | ty[ty ==0] = 0.1
634 |
635 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
636 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
637 |
638 |
639 |
640 | ##identify the markers
641 | mm = np.asarray(LABELS_MARKERS)[chk_box_marker]
642 |
643 | tiles = []
644 |
645 |
646 | marker_choice = np.array(mc)[chk_box_marker]
647 | weight_choice = np.array(wc)[chk_box_marker]
648 |
649 |
650 |
651 | inds = range(len(marker_image_list))
652 |
653 | N = len(inds)
654 |
655 |
656 | for k in range(len(inds)):
657 |
658 | image_all_2 = []
659 | image_all_1 = []
660 |
661 |
662 |
663 |
664 | im = tifffile.imread(marker_image_list[inds[k]])
665 | if (rb_imtech_val ==2): #for codex
666 | im = im.reshape(64,5040,9408)
667 |
668 |
669 |
670 | for m in range(len(marker_choice)):
671 | image = im[marker_choice[m]]
672 |
673 | median_filtered = scipy.ndimage.median_filter(image, size=1)
674 | image_all_2.append(median_filtered)
675 |
676 |
677 | # apply threshold
678 |
679 | thresh = threshold_otsu(image_all_2[m])
680 |
681 |
682 |
683 |
684 | print('thresh is: ', str(thresh))
685 |
686 | bw = closing(image > thresh, square(1))
687 |
688 | # remove artifacts connected to image border
689 | cleared_imtsne = clear_border(bw)
690 |
691 |
692 | image_all_1.append(cleared_imtsne*weight_choice[m])
693 |
694 | tl = sum(image_all_1)
695 |
696 |
697 |
698 | if (rb_imtech_val ==0): #vectra
699 | tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))
700 |
701 | elif (rb_imtech_val ==1): #t-CyCIF
702 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
703 | elif (rb_imtech_val ==2): # CODEX
704 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
705 | elif (rb_imtech_val ==3): # CyCIF
706 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
707 |
708 | old_size = tile_1.size
709 |
710 |
711 |
712 |
713 | if(rb_val==0):
714 | new_size = (old_size[0]+1, old_size[1]+1)
715 | new_im = Image.new("RGB", new_size,color='black')
716 |
717 | elif(rb_val==1): # for the border #response based
718 | new_size = (old_size[0]+50, old_size[1]+50)
719 |
720 |
721 |
722 | new_im = Image.new("RGB", new_size,color_vec[inds[k]])## color='yellow')
723 |
724 | elif(rb_val==2): # for the border #treatment based
725 | new_size = (old_size[0]+50, old_size[1]+50)
726 |
727 |
728 | new_im = Image.new("RGB", new_size,color_vec_tx[inds[k]])# color='yellow')
729 |
730 | elif(rb_val==3): # for the border #cluster based
731 | new_size = (old_size[0]+50, old_size[1]+50)
732 |
733 |
734 | new_im = Image.new("RGB", new_size,color_vec_clasgn[inds[k]])# colours_58[cx])# color='yellow')
735 |
736 |
737 | elif(rb_val==4): # for the border #patient id based
738 | new_size = (old_size[0]+50, old_size[1]+50)
739 |
740 |
741 | new_im = Image.new("RGB", new_size, color_vec_patid[inds[k]])# color='yellow')
742 |
743 | new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))
744 |
745 |
746 |
747 | file_name = os.path.join(path_wd+'/output_tiles/image_tsne_tils'+str(k)+'.png')
748 |
749 | file_name_hover.append('image_tsne_tils'+str(k)+'.png')
750 |
751 | new_im.save(file_name)
752 |
753 | # Read Images
754 |
755 |
756 | tile_2 = Image.open(file_name)
757 | rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
758 | tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias
759 |
760 | full_image.paste(tile_2, (int((width-max_dim)*ty[k]), int((height-max_dim)*tx[k])),mask=tile_2.convert('RGBA'))
761 |
762 |
763 |
764 | matplotlib.pyplot.figure(figsize = (25,20))
765 | plt.imshow(full_image)
766 | plt.axis('off')
767 |
768 |
769 |
770 |
771 | k = random.randint(2,500)
772 | file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k)+'.png')
773 | full_image.save(file_name)
774 |
775 |
776 |
777 | rotated_img = full_image.rotate(90)
778 | file_name_rot = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k)+'_rot.png')
779 |
780 |
781 | rotated_img.save(file_name_rot)
782 |
783 |
784 |
785 |
786 | ##code for reshuffling
787 |
788 | elif (rb_shf_val==1): # shuffle images
789 |
790 | #pick the tsne file to start with
791 | if(rb_rs_val==1):
792 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), index_col=None, header= None)
793 | print('usertsne')
794 | elif(rb_rs_val==0):
795 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
796 | print('origtsne')
797 |
798 | elif(rb_rs_val==2):
799 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), index_col=None, header= None)
800 | print('seealltsne')
801 |
802 | tsne = np.array(df_Xtsne)
803 |
804 |
805 |
806 | # save the tSNE points to be read later on, irrespective of which option was chosen
807 | df_Xtsne_touse = pd.DataFrame(tsne)
808 | df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse.csv'), header=None, index=None)
809 |
810 |
811 | tx, ty = tsne[:,0], tsne[:,1]
812 |
813 | tx[tx ==0] = 0.2
814 | ty[ty ==0] = 0.1
815 |
816 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
817 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
818 |
819 | full_image = Image.new('RGBA', (width, height))
820 |
821 | size = [256,256]
822 |
823 |
824 | ##identify the markers
825 | mm = np.asarray(LABELS_MARKERS)[chk_box_marker]
826 |
827 | tiles = []
828 |
829 |
830 | marker_choice = np.array(mc)[chk_box_marker]
831 | weight_choice = np.array(wc)[chk_box_marker]
832 |
833 | inds = range(len(marker_image_list))
834 |
835 | N = len(inds)
836 | shf_N = np.array(range(N))
837 | random.shuffle(shf_N)
838 | print("#############shuffle file order###")
839 | print(shf_N)
840 | count_file = 0
841 |
842 | for k in range(N):
843 |
844 | image_all_2 = []
845 | image_all_1 = []
846 |
847 |
848 |
849 |
850 | im = tifffile.imread(marker_image_list[shf_N[k]])
851 | if (rb_imtech_val ==2): #for codex
852 | im = im.reshape(64,5040,9408) ##
853 |
854 | for m in range(len(marker_choice)):
855 | image = im[marker_choice[m]]
856 |
857 | median_filtered = scipy.ndimage.median_filter(image, size=1)
858 | image_all_2.append(median_filtered)
859 |
860 |
861 |
862 | # apply threshold
863 |
864 | thresh = threshold_otsu(image_all_2[m])
865 |
866 |
867 | print(thresh)
868 |
869 | bw = closing(image > thresh, square(1))
870 |
871 | # remove artifacts connected to image border
872 | cleared_imtsne = clear_border(bw)
873 |
874 |
875 | image_all_1.append(cleared_imtsne*weight_choice[m])
876 |
877 | tl = sum(image_all_1)
878 |
879 |
880 | if (rb_imtech_val ==0): #vectra
881 | tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))
882 |
883 | elif (rb_imtech_val ==1): #t-CyCIF
884 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
885 | elif (rb_imtech_val ==2): # CODEX
886 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
887 | elif (rb_imtech_val ==3): # CyCIF
888 | tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
889 |
890 | old_size = tile_1.size
891 |
892 | print(pat_ind_list[shf_N[k]])
893 |
894 |
895 | if(rb_val==0):
896 | new_size = (old_size[0]+1, old_size[1]+1)
897 | new_im = Image.new("RGB", new_size,color='black')
898 |
899 | elif(rb_val==1): # for the border #response based
900 | new_size = (old_size[0]+50, old_size[1]+50)
901 |
902 |
903 |
904 |
905 | new_im = Image.new("RGB", new_size,color_vec[shf_N[k]])# color='yellow')
906 |
907 | elif(rb_val==2): # for the border #treatment based
908 | new_size = (old_size[0]+50, old_size[1]+50)
909 |
910 | new_im = Image.new("RGB", new_size,color_vec_tx[shf_N[k]])# color='yellow')
911 |
912 | elif(rb_val==3): # for the border #cluster based
913 | new_size = (old_size[0]+50, old_size[1]+50)
914 |
915 |
916 | new_im = Image.new("RGB", new_size,color_vec_clasgn[shf_N[k]])# color='yellow')
917 |
918 |
919 | elif(rb_val==4): # for the border #patient id based
920 | new_size = (old_size[0]+50, old_size[1]+50)
921 |
922 |
923 | new_im = Image.new("RGB", new_size,color_vec_patid[shf_N[k]])# color='yellow')
924 |
925 |
926 | new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))
927 |
928 |
929 | count_file = np.int(np.array(np.where(shf_N[k]==shf_N)).flatten())
930 |
931 | file_name = os.path.join(path_wd+'/output_tiles/image_tsne_tils'+str(shf_N[k])+'.png')
932 |
933 | file_name_hover.append('image_tsne_tils'+str(shf_N[k])+'.png')
934 |
935 |
936 | new_im.save(file_name)
937 |
938 |
939 |
940 | # Read Images
941 |
942 |
943 | tile_2 = Image.open(file_name)
944 | rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
945 | tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias
946 |
947 |
948 | full_image.paste(tile_2, (int((width-max_dim)*ty[shf_N[k]]), int((height-max_dim)*tx[shf_N[k]])),mask=tile_2.convert('RGBA'))
949 |
950 |
951 | matplotlib.pyplot.figure(figsize = (25,20))
952 | plt.imshow(full_image)
953 | plt.axis('off')
954 |
955 |
956 |
957 | ## order filenamehover list before sending it out to the live panels
958 | so = np.argsort(shf_N)
959 | file_name_hover_np = np.array(file_name_hover)
960 | sorted_file_name_hover = file_name_hover_np[so]
961 | file_name_hover = sorted_file_name_hover.tolist()
962 |
963 |
964 | k1 = random.randint(2,500)
965 | file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k1)+'.png')
966 |
967 | full_image.save(file_name)
968 |
969 |
970 | rotated_img = full_image.rotate(90)
971 | file_name_rot = file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k1)+'_rot.png')
972 |
973 |
974 |
975 | rotated_img.save(file_name_rot)
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 | return([file_name_rot,tsne, file_name_hover])
985 |
986 |
987 | ###############################################################################
988 | ### button_callback() ####
989 | ### a) Calls the create_figure() that collects user inputs from the GUI ####
990 | ### b) draw_tSNE_scatter() generates the tSNE plots for the live canvases ####
991 | ### b) reads in the user-provided tSNE ####
992 | ### c) generates thumbnails, and pastes these onto the static canvas ####
993 | ### d) stores the thumbnails in the output folder ####
994 | ### e) updates the hover tool with thumbnail paths ####
995 | ###############################################################################
996 |
997 |
998 | def button_callback():
999 |
1000 | theme_t = theme_select.value
1001 | if(theme_t == 'black'):
1002 | curdoc().theme= theme_black
1003 | elif(theme_t == 'gray'):
1004 | curdoc().theme= theme_gray
1005 | elif(theme_t == 'dark blue'):
1006 | curdoc().theme= theme_blue
1007 |
1008 | jk = create_figure(stack_montage_flag)
1009 | layout.children[1] = jk[0]
1010 |
1011 | print('############')
1012 | print('In Button callback')
1013 | tsne3 = jk[1]
1014 | print(type(tsne3))
1015 | print(tsne3)
1016 |
1017 |
1018 | file_name_hover = jk[2]
1019 | print('############fnh in button callback')
1020 | print('file_name_hover')
1021 | print(file_name_hover)
1022 |
1023 |
1024 |
1025 | markers_single = jk[3]
1026 | print('############')
1027 | print(type(markers_single))
1028 | print(markers_single)
1029 |
1030 | cluster_ms_list = jk[4]
1031 | print('############')
1032 | print(type(cluster_ms_list ))
1033 | print(cluster_ms_list )
1034 |
1035 | p1_out = draw_tSNE_scatter(tsne3, file_name_hover, cluster_ms_list)
1036 | p1 = p1_out[0]
1037 | p2 = p1_out[1]
1038 | p3 = p1_out[2]
1039 | p4 = p1_out[3]
1040 |
1041 | source = p1_out[4]
1042 |
1043 | tab1 = Panel(child=p1, title="Response")
1044 | tab2 = Panel(child=p2, title="Treatment")
1045 | tab3 = Panel(child=p3, title="Cluster Annotations")
1046 | tab4 = Panel(child=p4, title="Patient id")
1047 |
1048 | tabs = Tabs(tabs=[ tab1, tab2, tab3, tab4 ])
1049 | layout.children[2] = tabs
1050 |
1051 | return([p,p1,p2,p3,p4,source,tabs])
1052 |
1053 |
1054 | ############################################################################
1055 | ### create_figure() collects the user choices from the GUI and ####
1056 | ### calls either the generate_stack_montage() for reading in ####
1057 | ### a single image or the generate_image_tSNE() for multiplexed images ####
1058 | ############################################################################
1059 |
1060 |
1061 |
1062 | def create_figure(stack_montage_flag):
1063 |
1064 | p = figure(tools=TOOLS,x_range=x_range, y_range=y_range,width=1000,height=1000)
1065 |
1066 | rb_imtech = radio_button_group_imtech.value
1067 |
1068 | rb = radio_button_group.value
1069 |
1070 | rb_rs = radio_button_group_RS.value
1071 |
1072 | rb_shf = radio_button_group_Shf.value
1073 |
1074 | chk_box_marker = checkbox_group.active
1075 |
1076 | print(chk_box_marker)
1077 |
1078 | chk_box_marker_sm = checkbox_group_sm.active
1079 | print('**chk_box_marker_sm**')
1080 | print(chk_box_marker_sm)
1081 |
1082 |
1083 | if(rb=='No'):
1084 | rb_val = 0
1085 | elif(rb=='Based on Response'):
1086 | rb_val = 1
1087 | elif(rb=='Based on Treatment'):
1088 | rb_val = 2
1089 | elif(rb=='Based on Clusters'):
1090 | rb_val = 3
1091 | elif(rb=='Based on Patient id'):
1092 | rb_val = 4
1093 |
1094 |
1095 |
1096 | if(rb_rs=='Generate random co-ordinates'):
1097 | rb_rs_val = 1
1098 | elif(rb_rs=='Use t-SNE co-ordinates'):
1099 | rb_rs_val = 0
1100 | elif(rb_rs == 'Arrange in rows'):
1101 | rb_rs_val = 2
1102 |
1103 |
1104 | if(rb_shf=='Yes'):
1105 | rb_shf_val = 1
1106 | else:
1107 | rb_shf_val = 0
1108 |
1109 |
1110 |
1111 | if (rb_imtech == 'Vectra'):
1112 | rb_imtech_val = 0
1113 | elif (rb_imtech=='t-CyCIF'):
1114 | rb_imtech_val = 1
1115 | elif (rb_imtech=='CODEX'):
1116 | rb_imtech_val = 2
1117 | elif (rb_imtech=='CyCIF'):
1118 | rb_imtech_val = 3
1119 |
1120 | ## adding in the codex, vectra, tcycif, cycif options
1121 | # collecting the marker names for tcycif/codex stack montage
1122 |
1123 | cluster_ms_list = ['nil'] * len(LABELS_MARKERS)#[]
1124 | clust_ms_list = []
1125 | clust_ms_list_1 = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/markers.csv'),
1126 | header= 0,index_col=None)
1127 | markers_single = np.array(clust_ms_list_1.iloc[:,2]).flatten()
1128 |
1129 |
1130 | ## need to update the markers_single array based on the order the files are read in
1131 | if (stack_montage_flag==True):
1132 | if (rb_imtech_val ==1): #SM for t-CyCIF data
1133 | print('in SM for t-CyCIF')
1134 | ms_list=[]
1135 | cluster_ms_list = []
1136 | for i in range(markers_single.shape[0]):
1137 | print(i)
1138 | print(pat_fov_list[i])
1139 | print(markers_single)
1140 | channel_num = np.int(pat_fov_list[i].split("_40X_")[1].split(".tiff")[0])
1141 | ms_list.append(markers_single[channel_num-1])
1142 |
1143 |
1144 | markers_single = np.copy(np.array(ms_list))
1145 |
1146 |
1147 |
1148 | # use this updated markers_single name order going forward
1149 | for i in range(markers_single.shape[0]):
1150 |
1151 | cluster_ms_list.append('Channel '+ markers_single[i])
1152 |
1153 | elif (rb_imtech_val ==2): #SM for CODEX
1154 | ## for codex sm for tonsils
1155 | print('in SM for CODEX')
1156 | ## need to update the markers_single array based on the order the files are read in
1157 | ms_list=[]
1158 | cluster_ms_list = []
1159 | for i in range(markers_single.shape[0]):
1160 | print(i)
1161 | print(pat_fov_list[i])
1162 |
1163 | channel_name = pat_fov_list[i].split('.tif')[0].split('_')[3]
1164 | channel_num = np.int(np.array(np.where(channel_name==markers_single)).flatten())
1165 | ms_list.append(markers_single[channel_num])
1166 |
1167 | markers_single = np.copy(np.array(ms_list))
1168 |
1169 |
1170 | # use this updated markers_single name order going forward
1171 | for i in range(markers_single.shape[0]):
1172 |
1173 | cluster_ms_list.append('Channel '+ markers_single[i])
1174 |
1175 |
1176 | elif (stack_montage_flag == False):
1177 |
1178 |
1179 | # to handle all markers in t-CyCIF/CODEX/Vectra
1180 |
1181 | mc = []
1182 | for m in range(len(LABELS_MARKERS)):
1183 | in_mc = np.int(np.array(np.where(LABELS_MARKERS[m] == np.array(markers_single))).flatten())
1184 | mc.append(in_mc)
1185 |
1186 | if (rb_imtech_val ==0):#Vectra
1187 | wc = [100] * len(LABELS_MARKERS)
1188 | wc[0] = 800
1189 | wc[1]=wc[2] = 400
1190 | elif (rb_imtech_val ==1):#t-CyCIF
1191 | wc = [100] * len(LABELS_MARKERS)
1192 | wc[0] = 50
1193 | elif (rb_imtech_val ==2):
1194 | wc = [150] * len(LABELS_MARKERS)
1195 | wc[0] = 300 #
1196 | wc[6] = 100
1197 |
1198 | elif (rb_imtech_val ==3):
1199 | wc = [150] * len(LABELS_MARKERS)
1200 | wc[1] = 100
1201 | wc[2] = 300
1202 |
1203 | cluster_ms_list = ['nil'] * num_images #[]
1204 |
1205 |
1206 |
1207 |
1208 | ###########
1209 |
1210 |
1211 | if(len(chk_box_marker_sm)==0): #so we are using the multiple multiplexed option
1212 | if(len(chk_box_marker)==0): #if no markers are chosen, display defaults
1213 |
1214 | file_name_1 = "image_tSNE_GUI/static/image_tsne_tils_all.png"
1215 | p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
1216 | df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
1217 | df_Xtsne.shape
1218 | tsne_points = np.array(df_Xtsne )
1219 | file_name_hover = list(range(num_images))
1220 |
1221 | else:
1222 |
1223 |
1224 | out1 = generate_image_tSNE(chk_box_marker, rb_val,rb_rs_val,rb_shf_val,rb_imtech_val, mc, wc, LABELS_MARKERS)
1225 | file_name_all = out1[0]
1226 | tsne_points = out1[1]
1227 | file_name_hover = out1[2]
1228 |
1229 | print('file_name_hover after gen_image_tsne')
1230 | print(file_name_hover)
1231 |
1232 | print('#########file_name_all########')
1233 | print('#################')
1234 |
1235 | print(file_name_all)
1236 |
1237 | file_name_1 = file_name_all.split('/code')[1]
1238 | print('#########file_name_1########')
1239 | print(file_name_1)
1240 |
1241 |
1242 | p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
1243 |
1244 | elif(len(chk_box_marker_sm)==1): #stack montage option
1245 | print('chosen stack montage')
1246 | out1sm = generate_stack_montage(chk_box_marker, rb_imtech_val, LABELS_MARKERS)
1247 | file_name_all = out1sm[0]
1248 | tsne_points = out1sm[1]
1249 | file_name_hover = out1sm[2]
1250 |
1251 | print('############fnh post sm')
1252 | print('file_name_hover')
1253 | print(file_name_hover)
1254 |
1255 | print('#########file_name_all########')
1256 | print('#################')
1257 |
1258 | print(file_name_all)
1259 |
1260 | file_name_1 = file_name_all.split('/code')[1]
1261 | print('#########file_name_1########')
1262 | print(file_name_1)
1263 |
1264 |
1265 | p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
1266 |
1267 |
1268 | return ([p,tsne_points, file_name_hover,markers_single, cluster_ms_list])
1269 |
1270 |
1271 | ##########
1272 | # define the colour vectors
1273 | colours_58 = ["firebrick","gold","royalblue","green","dimgray","orchid","darkviolet",
1274 | "red", "orange", "limegreen", "blue", "purple", "seagreen","gold","darkolivegreen",
1275 | "lightpink","thistle","mistyrose","saddlebrown","slategrey","powderblue",
1276 | "palevioletred","mediumvioletred","yellowgreen","lemonchiffon","chocolate",
1277 | "lightsalmon","lightcyan","lightblue", "darkorange","black","darkblue","darkgreen","paleturquoise","yellow","rosybrown",
1278 | "steelblue","dodgerblue","darkkhaki","lime","coral","aquamarine","mediumpurple","violet","plum",
1279 | "deeppink","navy","seagreen","teal","mediumspringgreen","cadetblue",
1280 | "maroon","silver","sienna","crimson","slateblue","magenta","darkmagenta"]
1281 |
1282 | colours_resp = ["yellow","red","green"]
1283 | colours_tx = ["orange","limegreen","violet"]
1284 |
1285 |
1286 | #####################################
1287 | #### Section that gets populated ####
1288 | ####based on User uploads ###########
1289 | #####################################
1290 | #####################################
1291 | ## This section reads in images, ####
1292 | ## metadata, markers in the data,####
1293 | ## and user choices ####
1294 | #####################################
1295 | #####################################
1296 |
1297 |
1298 | path_wd = os.getcwd()
1299 | print('Current working directory: ')
1300 | print(path_wd)
1301 |
1302 |
1303 |
1304 |
1305 | ### to generate tsne points from the onset itself
1306 |
1307 | fname = os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv')
1308 | if os.path.isfile(fname):
1309 | df_Xtsne = pd.read_csv(fname, index_col=None, header= None)
1310 | tsne = np.array(df_Xtsne )
1311 | tx, ty = tsne[:,0], tsne[:,1]
1312 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
1313 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
1314 | num_images = tsne.shape[0]
1315 |
1316 |
1317 |
1318 | ## to have the arrange_in_rows and random points already available
1319 | # Choose up to k points around each reference point as candidates for a new
1320 | # sample point
1321 | k = 10
1322 |
1323 | # Minimum distance between samples
1324 | r = 1.7
1325 |
1326 | width_1, height_1 = 20, 22
1327 |
1328 | print('Generating random co-ordinates')
1329 |
1330 | # Cell side length
1331 | a = r/np.sqrt(2)
1332 | # Number of cells in the x- and y-directions of the grid
1333 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
1334 |
1335 | # A list of coordinates in the grid of cells
1336 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
1337 | # Initilalize the dictionary of cells: each key is a cell's coordinates, the
1338 | # corresponding value is the index of that cell's point's coordinates in the
1339 | # samples list (or None if the cell is empty).
1340 | cells = {coords: None for coords in coords_list}
1341 |
1342 |
1343 |
1344 | # Pick a random point to start with.
1345 | pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
1346 | samples = [pt]
1347 | # Our first sample is indexed at 0 in the samples list...
1348 | cells[get_cell_coords(pt,a)] = 0
1349 | # ... and it is active, in the sense that we're going to look for more points
1350 | # in its neighbourhood.
1351 | active = [0]
1352 |
1353 | nsamples = 1
1354 | # As long as there are points in the active list, keep trying to find samples.
1355 | while (nsamples < num_images): #active:
1356 | # choose a random "reference" point from the active list.
1357 | idx = np.random.choice(active)
1358 | refpt = samples[idx]
1359 | # Try to pick a new point relative to the reference point.
1360 | pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
1361 | if pt:
1362 | # Point pt is valid: add it to the samples list and mark it as active
1363 | samples.append(pt)
1364 | nsamples += 1
1365 | active.append(len(samples)-1)
1366 | cells[get_cell_coords(pt,a)] = len(samples) - 1
1367 | print('nsamples is: ',str(nsamples))
1368 | else:
1369 | # We had to give up looking for valid points near refpt, so remove it
1370 | # from the list of "active" points.
1371 | active.remove(idx)
1372 |
1373 | tsne = np.asarray(samples)
1374 | tx, ty = tsne[:,0], tsne[:,1]
1375 |
1376 | tx[tx ==0] = 0.2
1377 | ty[ty ==0] = 0.1
1378 |
1379 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
1380 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
1381 |
1382 | df_Xtsne_m = pd.DataFrame(tsne)
1383 | #df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)
1384 | df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None) #14th march 2022
1385 |
1386 |
1387 | ##### for 'arrange in rows' option at the onset itself
1388 |
1389 |
1390 | # Minimum distance between samples
1391 | r = 2
1392 |
1393 | width_1, height_1 = 20, 22
1394 |
1395 | print('Arrange images side-by-side')
1396 |
1397 | # Cell side length
1398 | a = r/np.sqrt(2)
1399 | # Number of cells in the x- and y-directions of the grid
1400 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
1401 |
1402 | # A list of coordinates in the grid of cells
1403 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
1404 |
1405 |
1406 | #logic to get grid points - 29th March 2021
1407 | m = np.int(np.floor(nx*ny/num_images))
1408 |
1409 |
1410 | row_needed = []
1411 | def multiples(m, num_images):
1412 | for i in range(num_images):
1413 | row_needed.append(i*m)
1414 |
1415 | multiples(m,num_images)
1416 |
1417 |
1418 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
1419 |
1420 | print(type(select_coords))
1421 |
1422 | tsne1 = select_coords
1423 |
1424 | tsne1[tsne1 ==0] = 0.2
1425 |
1426 | df_Xtsne = pd.DataFrame(tsne1)
1427 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)
1428 |
1429 |
1430 | else:
1431 | #read in images
1432 | FoV_path = os.path.join(path_wd + '/user_inputs/figures/')
1433 | count_images = 0
1434 | for fname in os.listdir(FoV_path):
1435 | print(fname)
1436 | count_images = count_images + 1
1437 | num_images = count_images
1438 | #generate random 2D coords for these images
1439 |
1440 | ####
1441 | #### tsne and clustering within mistic, if not provided by user
1442 | ####
1443 |
1444 | im1 = tifffile.imread(os.path.join(FoV_path+fname))
1445 |
1446 | if (im1.shape[0] < 6):
1447 | num_markers = im1.shape[0]
1448 | else:
1449 | num_markers = 6
1450 |
1451 | data_CM = np.zeros((1,num_markers)) # one row = one image with mean of markers in the columns, defaults to 6 markers
1452 |
1453 | for fname in os.listdir(FoV_path):
1454 | print(fname)
1455 | im1 = tifffile.imread(os.path.join(FoV_path+fname))
1456 | if ((im1.shape[0]==16) and (im1.shape[1]==4) and (im1.shape[2]==5040) and (im1.shape[3]==9408)): #for codex intake
1457 | im1 = im1.reshape(64,5040,9408)
1458 |
1459 | #if (rb_imtech_val ==2): #for codex
1460 | # im = im.reshape(64,5040,9408)
1461 |
1462 |
1463 |
1464 | image_summary_vec = np.zeros((1,num_markers))
1465 | for m in range(num_markers):
1466 | image1 = im1[m]
1467 |
1468 | median_filtered1 = scipy.ndimage.median_filter(image1, size=1)
1469 |
1470 |
1471 | thresh = threshold_otsu(median_filtered1)
1472 |
1473 |
1474 | bw = closing(image1 > thresh, square(1))
1475 |
1476 | # remove artifacts connected to image border
1477 | cleared_imtsne = clear_border(bw)
1478 |
1479 |
1480 | image_summary_vec[0,m] = np.mean(cleared_imtsne) #mean of each channel m per image fname
1481 |
1482 |
1483 |
1484 | data_CM = np.concatenate((data_CM,image_summary_vec),axis=0)
1485 |
1486 | data_CM = np.delete(data_CM, (0), axis=0)
1487 |
1488 | # perform tSNE
1489 | tsne = TSNE(n_components=2, verbose=1)#, perplexity=30, n_iter=900)
1490 | X_2d = tsne.fit_transform(data_CM)
1491 |
1492 | # Fit a Dirichlet process mixture of Gaussians using n components
1493 | bgm = BayesianGaussianMixture(n_components=3, random_state=42).fit(data_CM)
1494 | cluster_asgn = bgm.predict(data_CM)
1495 |
1496 | #save tSNE and cluster assignments
1497 |
1498 | df_Xtsne_u = pd.DataFrame(X_2d)
1499 | df_Xtsne_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)
1500 |
1501 |
1502 |
1503 | fname = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')
1504 | if (os.path.isfile(fname)==False):
1505 | df_dpgmm_u = pd.DataFrame(cluster_asgn)
1506 | df_dpgmm_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv'), header=None, index=None)
1507 |
1508 | ### end of tsne/clustering by mistic
1509 |
1510 | # Choose up to k points around each reference point as candidates for a new
1511 | # sample point
1512 | k = 10
1513 |
1514 | # Minimum distance between samples
1515 | r = 1.7
1516 |
1517 | width_1, height_1 = 20, 22
1518 |
1519 | print('Generating random co-ordinates')
1520 |
1521 | # Cell side length
1522 | a = r/np.sqrt(2)
1523 | # Number of cells in the x- and y-directions of the grid
1524 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
1525 |
1526 | # A list of coordinates in the grid of cells
1527 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
1528 | # Initilalize the dictionary of cells: each key is a cell's coordinates, the
1529 | # corresponding value is the index of that cell's point's coordinates in the
1530 | # samples list (or None if the cell is empty).
1531 | cells = {coords: None for coords in coords_list}
1532 |
1533 |
1534 |
1535 | # Pick a random point to start with.
1536 | pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
1537 | samples = [pt]
1538 | # Our first sample is indexed at 0 in the samples list...
1539 | cells[get_cell_coords(pt,a)] = 0
1540 | # ... and it is active, in the sense that we're going to look for more points
1541 | # in its neighbourhood.
1542 | active = [0]
1543 |
1544 | nsamples = 1
1545 | # As long as there are points in the active list, keep trying to find samples.
1546 | while (nsamples < num_images): #active:
1547 | # choose a random "reference" point from the active list.
1548 | idx = np.random.choice(active)
1549 | refpt = samples[idx]
1550 | # Try to pick a new point relative to the reference point.
1551 | pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
1552 | if pt:
1553 | # Point pt is valid: add it to the samples list and mark it as active
1554 | samples.append(pt)
1555 | nsamples += 1
1556 | active.append(len(samples)-1)
1557 | cells[get_cell_coords(pt,a)] = len(samples) - 1
1558 | print('nsamples is: ',str(nsamples))
1559 | else:
1560 | # We had to give up looking for valid points near refpt, so remove it
1561 | # from the list of "active" points.
1562 | active.remove(idx)
1563 |
1564 | tsne = np.asarray(samples)
1565 | tx, ty = tsne[:,0], tsne[:,1]
1566 |
1567 | tx[tx ==0] = 0.2
1568 | ty[ty ==0] = 0.1
1569 |
1570 | tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
1571 | ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
1572 |
1573 | df_Xtsne_m = pd.DataFrame(tsne)
1574 | #df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)
1575 | df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None)
1576 |
1577 |
1578 | ##### for 'arrange in rows' option at the onset itself
1579 |
1580 |
1581 | # Minimum distance between samples
1582 | r = 2
1583 |
1584 | width_1, height_1 = 20, 22
1585 |
1586 | print('Arrange images side-by-side')
1587 |
1588 | # Cell side length
1589 | a = r/np.sqrt(2)
1590 | # Number of cells in the x- and y-directions of the grid
1591 | nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1
1592 |
1593 | # A list of coordinates in the grid of cells
1594 | coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
1595 |
1596 |
1597 | #logic to get grid points - 29th March 2021
1598 | m = np.int(np.floor(nx*ny/num_images))
1599 |
1600 |
1601 | row_needed = []
1602 | def multiples(m, num_images):
1603 | for i in range(num_images):
1604 | row_needed.append(i*m)
1605 |
1606 | multiples(m,num_images)
1607 |
1608 |
1609 | select_coords = np.array(coords_list)[np.array(row_needed).flatten()]
1610 |
1611 | print(type(select_coords))
1612 |
1613 | tsne1 = select_coords
1614 |
1615 | tsne1[tsne1 ==0] = 0.2
1616 |
1617 | df_Xtsne = pd.DataFrame(tsne1)
1618 | df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)
1619 |
1620 | ########################
1621 |
1622 |
1623 |
1624 | # adding this for reading in data if no marker info is present
1625 | fname = os.path.join(path_wd + '/user_inputs/metadata/Marker_ids.csv')
1626 |
1627 | if os.path.isfile(fname):
1628 | df_markers = pd.read_csv(fname, index_col=None, header= None)
1629 | markers_list = np.array(df_markers ).flatten()
1630 | LABELS_MARKERS = []
1631 | for j in range(markers_list.shape[0]):
1632 | LABELS_MARKERS.append(markers_list[j])
1633 | stack_montage_flag = False
1634 |
1635 |
1636 | else:
1637 | LABELS_MARKERS = ['nil'] * 6
1638 | stack_montage_flag = True
1639 |
1640 | ## read in images
1641 | marker_image_list = []
1642 | pat_fov_list = []
1643 | FoV_path = os.path.join(path_wd + '/user_inputs/figures/')
1644 | file_order = []
1645 |
1646 | #sorted input
1647 | for fname in sorted(os.listdir(FoV_path)):
1648 | print(fname)
1649 | marker_image_list.append(FoV_path+fname)
1650 | pat_fov_list.append(fname)
1651 |
1652 |
1653 |
1654 | print("print image list")
1655 | print(marker_image_list)
1656 | print("file order")
1657 | print(file_order)
1658 |
1659 |
1660 |
1661 |
1662 | #################################
1663 | #################################
1664 | # checks for user metadata inputs
1665 | # if these files are not present, gray out the tSNE or points.
1666 |
1667 | # collecting the response metadata
1668 |
1669 |
1670 | color_vec = []
1671 | resp_list= []
1672 | fname = os.path.join(path_wd + '/user_inputs/metadata/Response_categories.csv')
1673 |
1674 | if os.path.isfile(fname):
1675 | resp_list_1 = np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
1676 | uni_resp,counts_resp = np.unique(resp_list_1,return_counts=True)
1677 |
1678 | for i in range(resp_list_1.shape[0]):
1679 | row_t = np.int(np.array(np.where(resp_list_1[i]==uni_resp)).flatten())
1680 |
1681 | color_vec.append(colours_resp[row_t])
1682 |
1683 | resp_list.append(resp_list_1[i])
1684 | else:
1685 | for i in range(num_images):
1686 | color_vec.append('gray')
1687 | resp_list.append('Response nil')
1688 |
1689 |
1690 | # collecting the treatment metadata
1691 |
1692 | color_vec_tx = []
1693 | tx_list = []
1694 | fname = os.path.join(path_wd + '/user_inputs/metadata/Treatment_categories.csv')
1695 |
1696 | if os.path.isfile(fname):
1697 | tx_list_1= np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
1698 | uni_tx,counts_tx = np.unique(tx_list_1,return_counts=True)
1699 | for i in range(tx_list_1.shape[0]):
1700 | row_t = np.int(np.array(np.where(tx_list_1[i]==uni_tx)).flatten())
1701 |
1702 | color_vec_tx.append(colours_tx[row_t])
1703 |
1704 | tx_list.append(tx_list_1[i])
1705 | else:
1706 | for i in range(num_images):
1707 | color_vec_tx.append('gray')
1708 | tx_list.append('Treatment nil')
1709 |
1710 |
1711 | # collecting the cluster assignments metadata
1712 |
1713 | fname = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')
1714 | color_vec_clasgn = []
1715 | cluster_anno_list = []
1716 | clust_asgn_list = []
1717 |
1718 |
1719 | if os.path.isfile(fname):
1720 | clust_asgn_list_1 = np.array(pd.read_csv(fname, header= None,index_col=None)).flatten()
1721 | for i in range(clust_asgn_list_1.shape[0]):
1722 |
1723 | color_vec_clasgn.append(colours_58[clust_asgn_list_1[i]])
1724 | clust_asgn_list.append(clust_asgn_list_1[i])
1725 | cluster_anno_list.append('Cluster '+ str(clust_asgn_list_1[i]))
1726 |
1727 | else:
1728 | #for bayesian clustering, if not present
1729 |
1730 |
1731 |
1732 | FoV_path = os.path.join(path_wd + '/user_inputs/figures/')
1733 |
1734 | #sorted input data add on 17th Feb 2022
1735 | for fname in sorted(os.listdir(FoV_path)):
1736 | print(fname)
1737 |
1738 |
1739 | im1 = tifffile.imread(os.path.join(FoV_path+fname))
1740 |
1741 | if (im1.shape[0] < 6):
1742 | num_markers = im1.shape[0]
1743 | else:
1744 | num_markers = 6
1745 |
1746 | data_CM = np.zeros((1,num_markers)) # one row = one image with mean of markers in the columns, defaults to 6 markers
1747 |
1748 | for fname1 in os.listdir(FoV_path):
1749 | print(fname1)
1750 | im1 = tifffile.imread(os.path.join(FoV_path+fname1))
1751 |
1752 | if ((im1.shape[0]==16) and (im1.shape[1]==4) and (im1.shape[2]==5040) and (im1.shape[3]==9408)): #for codex intake
1753 | im1 = im1.reshape(64,5040,9408)
1754 |
1755 | image_summary_vec = np.zeros((1,num_markers))
1756 | for m in range(num_markers):
1757 | image1 = im1[m]
1758 |
1759 | median_filtered1 = scipy.ndimage.median_filter(image1, size=1)
1760 |
1761 |
1762 | thresh = threshold_otsu(median_filtered1)
1763 |
1764 |
1765 | bw = closing(image1 > thresh, square(1))
1766 |
1767 | # remove artifacts connected to image border
1768 | cleared_imtsne = clear_border(bw)
1769 |
1770 |
1771 | image_summary_vec[0,m] = np.mean(cleared_imtsne) #mean of each channel m per image fname
1772 |
1773 |
1774 |
1775 | data_CM = np.concatenate((data_CM,image_summary_vec),axis=0)
1776 |
1777 | data_CM = np.delete(data_CM, (0), axis=0)
1778 |
1779 |
1780 |
1781 | # Fit a Dirichlet process mixture of Gaussians using n components
1782 | bgm = BayesianGaussianMixture(n_components=3, random_state=42).fit(data_CM)
1783 | cluster_asgn = bgm.predict(data_CM)
1784 |
1785 | df_dpgmm_u = pd.DataFrame(cluster_asgn)
1786 | df_dpgmm_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv'), header=None, index=None)
1787 |
1788 |
1789 | fname2 = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')
1790 |
1791 |
1792 | clust_asgn_list_2 = np.array(pd.read_csv(fname2, header= None,index_col=None)).flatten()
1793 | for i in range(num_images):
1794 |
1795 | color_vec_clasgn.append(colours_58[clust_asgn_list_2[i]])
1796 | clust_asgn_list.append(clust_asgn_list_2[i])
1797 | cluster_anno_list.append('Cluster '+ str(clust_asgn_list_2[i]))
1798 |
1799 | ## bayes clustering end
1800 |
1801 |
1802 |
1803 | # collecting the patient id metadata
1804 |
1805 | fname = os.path.join(path_wd + '/user_inputs/metadata/Patient_ids.csv')
1806 | color_vec_patid = []
1807 | cluster_pat_list = []
1808 | clust_patid_list = []
1809 |
1810 | if os.path.isfile(fname):
1811 |
1812 |
1813 | # collecting the Patient id metadata
1814 |
1815 | clust_patid_list_1 = np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
1816 | pat_ind_list = np.copy(clust_patid_list_1)
1817 |
1818 | for i in range(clust_patid_list_1.shape[0]):
1819 |
1820 | color_vec_patid.append(colours_58[clust_patid_list_1[i]])
1821 | clust_patid_list.append(clust_patid_list_1[i])
1822 | cluster_pat_list.append('Patient '+ str(clust_patid_list_1[i]))
1823 |
1824 | else:
1825 | pat_ind_list = []
1826 | for i in range(num_images):
1827 | color_vec_patid.append('gray')
1828 | clust_patid_list.append('nil')
1829 | cluster_pat_list.append('Patient nil')
1830 | pat_ind_list.append('nil')
1831 |
1832 | # generate dummy thumbnail names for hover panel
1833 |
1834 | file_name_hover = []
1835 | file_name_hover_list = []
1836 |
1837 | for i in range(num_images): #clust_patid_list_1.shape[0]):
1838 |
1839 | file_name_hover.append(str(i))
1840 | file_name_hover_list.append('Thumbnail '+ str(i))
1841 |
1842 |
1843 |
1844 |
1845 |
1846 | #################
1847 | # set up widgets
1848 | #################
1849 |
1850 | RB_1 = ['Based on Response', 'Based on Treatment','Based on Clusters','Based on Patient id','No']
1851 |
1852 | RB_2 = ['Generate random co-ordinates', 'Use t-SNE co-ordinates', 'Arrange in rows']
1853 |
1854 | RB_3 = ['Yes', 'No']
1855 |
1856 | RB_4 = ['Vectra', 't-CyCIF', 'CODEX', 'CyCIF']
1857 |
1858 | TS_1 = ['black','gray', 'dark blue']
1859 |
1860 | TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,save,box_select,"
1861 |
1862 | x_range=(0,1)
1863 | y_range=(0,1)
1864 |
1865 |
1866 | #main image tSNE canvas
1867 | p = figure(tools=TOOLS,x_range=x_range, y_range=y_range,width=1000,height=1000)
1868 | tsne_points = np.zeros([1,2])
1869 |
1870 | #additional tSNE scatter plot canvas
1871 |
1872 |
1873 |
1874 | point_tSNE = figure(plot_width=350, plot_height=350,
1875 | tools='hover,pan,wheel_zoom,box_select,reset')
1876 | point_tSNE.title.text = 'tSNE point cloud for Patient response'
1877 |
1878 |
1879 | point_tSNE.scatter(tsne[:,0],tsne[:,1],fill_alpha=0.6, color ='red',size=8,legend='Response')
1880 |
1881 | point_tSNE.legend.location = "bottom_left"
1882 |
1883 | theme_black = Theme(json={
1884 | 'attrs': {
1885 | 'Figure': {
1886 | 'background_fill_color': '#2F2F2F',
1887 | 'border_fill_color': '#2F2F2F',
1888 | 'outline_line_color': '#444444'
1889 | },
1890 | 'Axis': {
1891 | 'axis_line_color': "white",
1892 | 'axis_label_text_color': "white",
1893 | 'major_label_text_color': "white",
1894 | 'major_tick_line_color': "white",
1895 | 'minor_tick_line_color': "white",
1896 | 'minor_tick_line_color': "white"
1897 | },
1898 | 'Grid': {
1899 | 'grid_line_dash': [6, 4],
1900 | 'grid_line_alpha': .3
1901 | },
1902 | 'Circle': {
1903 | 'fill_color': 'lightblue',
1904 | 'size': 10,
1905 | },
1906 | 'Title': {
1907 | 'text_color': "white"
1908 | }
1909 | }
1910 | })
1911 |
1912 | theme_gray = Theme(json={
1913 | 'attrs': {
1914 | 'Figure': {
1915 | 'background_fill_color': '#555555',
1916 | 'border_fill_color': '#2F2F2F',
1917 | 'outline_line_color': '#444444'
1918 | },
1919 | 'Axis': {
1920 | 'axis_line_color': "white",
1921 | 'axis_label_text_color': "white",
1922 | 'major_label_text_color': "white",
1923 | 'major_tick_line_color': "white",
1924 | 'minor_tick_line_color': "white",
1925 | 'minor_tick_line_color': "white"
1926 | },
1927 | 'Grid': {
1928 | 'grid_line_dash': [6, 4],
1929 | 'grid_line_alpha': .3
1930 | },
1931 | 'Circle': {
1932 | 'fill_color': 'lightblue',
1933 | 'size': 10,
1934 | },
1935 | 'Title': {
1936 | 'text_color': "white"
1937 | }
1938 | }
1939 | })
1940 |
1941 | theme_blue = Theme(json={
1942 | 'attrs': {
1943 | 'Figure': {
1944 | 'background_fill_color': '#25256d',
1945 | 'border_fill_color': '#2F2F2F',
1946 | 'outline_line_color': '#444444'
1947 | },
1948 | 'Axis': {
1949 | 'axis_line_color': "white",
1950 | 'axis_label_text_color': "white",
1951 | 'major_label_text_color': "white",
1952 | 'major_tick_line_color': "white",
1953 | 'minor_tick_line_color': "white",
1954 | 'minor_tick_line_color': "white"
1955 | },
1956 | 'Grid': {
1957 | 'grid_line_dash': [6, 4],
1958 | 'grid_line_alpha': .3
1959 | },
1960 | 'Circle': {
1961 | 'fill_color': 'lightblue',
1962 | 'size': 10,
1963 | },
1964 | 'Title': {
1965 | 'text_color': "white"
1966 | }
1967 | }
1968 | })
1969 |
1970 | #########
1971 | #########
1972 |
1973 |
1974 | TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
1975 |
1976 |
1977 | TOOLTIPS = [
1978 | ("index", "$index"),
1979 | ("(x,y)", "($x, $y)"),
1980 | ("Pat_id", "@pat_list"),
1981 | ("Response", "@res_list"),
1982 | ("Treatment", "@tx_list"),
1983 | ("Cluster id", "@clust_asgn_list"),
1984 | ("Channel", "@cluster_ms_list"),
1985 | ("Thumbnail", "@file_name_hover_list"),
1986 | ("FoV","@fov_list")
1987 | ]
1988 |
1989 | p1 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
1990 | title="Patient response")
1991 |
1992 |
1993 |
1994 |
1995 | #####################################################################################
1996 | ## This block prepares and sets up the GUI layout, and collects user-input choices ##
1997 | #####################################################################################
1998 |
1999 |
2000 | desc = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/desc.html')).read(), sizing_mode="stretch_width")
2001 |
2002 |
2003 | desc_SM1 = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/descSM1.html')).read(), sizing_mode="stretch_width")
2004 |
2005 | desc_SM = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/descMontage.html')).read(), sizing_mode="stretch_width")
2006 |
2007 |
2008 |
2009 | radio_button_group_imtech = Select(value='Vectra',
2010 | title='',
2011 | width=200,
2012 | options=RB_4)
2013 |
2014 |
2015 | radio_button_group = Select(value='No',
2016 | title='Image border',
2017 | width=200,
2018 | options=RB_1)
2019 |
2020 | radio_button_group_RS = Select(value='Generate new co-ordinates',
2021 | title='tSNE co-ordinates',
2022 | width=220,
2023 | options=RB_2)
2024 |
2025 | radio_button_group_Shf = Select(value='Yes',
2026 | title='Shuffle images',
2027 | width=200,
2028 | options=RB_3)
2029 |
2030 | theme_select = Select(value = 'black',
2031 | title='Theme',
2032 | width = 200,
2033 | options = TS_1)
2034 |
2035 |
2036 | checkbox_group = CheckboxGroup(labels=LABELS_MARKERS)#, active=[0, 1])
2037 |
2038 |
2039 | button = Button(label='Run', width=100, button_type="success")
2040 |
2041 | ## added to activate Run button
2042 | button.on_click(button_callback)
2043 |
2044 | ## for stack montage
2045 | SM = ['Stack montage']
2046 | checkbox_group_sm = CheckboxGroup(labels=SM)
2047 |
2048 |
2049 | print('updating new p1#')
2050 |
2051 |
2052 |
2053 | # set up layout and GUI refresh after every 'Run' click
2054 |
2055 | out2 = create_figure(stack_montage_flag)
2056 | print(out2)
2057 | p = out2[0]
2058 | tsne2 = out2[1]
2059 | print('############')
2060 | print(type(tsne2))
2061 | print(tsne2)
2062 |
2063 | file_name_hover = out2[2]
2064 | print('############fnh after create figure')
2065 | print(type(file_name_hover))
2066 | print(file_name_hover)
2067 |
2068 |
2069 | markers_single = out2[3]
2070 | print('############')
2071 | print(type(markers_single))
2072 | print(markers_single)
2073 |
2074 | cluster_ms_list = out2[4]
2075 | print('############')
2076 | print(type(cluster_ms_list ))
2077 | print(cluster_ms_list )
2078 |
2079 |
2080 | p11_out = draw_tSNE_scatter(tsne2, file_name_hover,cluster_ms_list )
2081 |
2082 | p1 = p11_out[0]
2083 | p2 = p11_out[1]
2084 | p3 = p11_out[2]
2085 | p4 = p11_out[3]
2086 |
2087 | tab1 = Panel(child=p1, title="Response")
2088 | tab2 = Panel(child=p2, title="Treatment")
2089 | tab3 = Panel(child=p3, title="Cluster Annotations")
2090 | tab4 = Panel(child=p4, title="Patient id")
2091 |
2092 | tabs = Tabs(tabs=[ tab1, tab2, tab3, tab4 ]) # (added tab4)
2093 |
2094 |
2095 |
2096 | selects = column(desc, radio_button_group_imtech, desc_SM1, checkbox_group_sm, desc_SM, checkbox_group, radio_button_group, radio_button_group_RS, radio_button_group_Shf, theme_select, button, width=520) #
2097 |
2098 | layout=row(selects,p, tabs)#,selected_points)#create_figure())
2099 |
2100 | #doc = curdoc()
2101 | curdoc().theme= theme_black
2102 |
2103 |
2104 | # add to document
2105 | curdoc().add_root(layout)
2106 |
2107 | curdoc().title = "Mistic: Image tSNE viewer"
2108 |
2109 |
2110 |
2111 | # cd image_tSNE_code/bokeh_GUI/bokeh-branch-2.3/examples/app
2112 | # bokeh serve --port 5098 --show image_tSNE_GUI
2113 |
2114 |
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/static/image_tsne_tils_all_3_rot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MathOnco/Mistic/ebbce7bd66b26bc368f64e74555bd131e6d6cd5f/Mistic_code/code/image_tSNE_GUI/static/image_tsne_tils_all_3_rot.png
--------------------------------------------------------------------------------
/Mistic_code/code/image_tSNE_GUI/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends base %}
2 |
3 | {% block title %}Bokeh Image t-SNE{% endblock %}
4 |
5 | {% block preamble %}
6 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/Mistic_code/code/mistic.sh:
--------------------------------------------------------------------------------
1 | find . | grep .git | xargs rm -rf
2 | find . -name ".DS_Store" -delete
3 | bokeh serve --port 5098 --show image_tSNE_GUI
4 |
--------------------------------------------------------------------------------
/Mistic_code/code/output_tiles/test.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/figures/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/CODEX/Marker_ids.csv:
--------------------------------------------------------------------------------
1 | Keratin14
2 | FoxP3
3 | CD34
4 | CD8
5 | CD3e
6 | CD68
7 | Perlecan
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/CODEX/markers.csv:
--------------------------------------------------------------------------------
1 | cycle_number,channel_number,marker_name
2 | 1,1,DAPI-1
3 | 1,2,Blank-1
4 | 1,3,Blank-2
5 | 1,4,Blank-3
6 | 2,5,DAPI-2
7 | 2,6,Keratin19
8 | 2,7,Myosin
9 | 2,8,PCNA
10 | 3,9,DAPI-3
11 | 3,10,Vimentin
12 | 3,11,CD34
13 | 3,12,FoxA1
14 | 4,13,DAPI-4
15 | 4,14,Keratin17
16 | 4,15,S100A4
17 | 4,16,CollagenIV
18 | 5,17,DAPI-5
19 | 5,18,E-cadherin
20 | 5,19,CD31
21 | 5,20,FoxP3
22 | 6,21,DAPI-6
23 | 6,22,b-catenin1
24 | 6,23,Ki67
25 | 6,24,CD68
26 | 7,25,DAPI-7
27 | 7,26,Keratin8
28 | 7,27,Perlecan
29 | 7,28,PR
30 | 8,29,DAPI-8
31 | 8,30,b-actin
32 | 8,31,TFAM
33 | 8,32,CD3e
34 | 9,33,DAPI-9
35 | 9,34,Keratin18
36 | 9,35,E2F1
37 | 9,36,SMA
38 | 10,37,DAPI-10
39 | 10,38,MHCI
40 | 10,39,CD8
41 | 10,40,Runx3
42 | 11,41,DAPI-11
43 | 11,42,CD227
44 | 11,43,LaminB1
45 | 11,44,CD66e
46 | 12,45,DAPI-12
47 | 12,46,Ch2Cy12
48 | 12,47,MHCII
49 | 12,48,TP63
50 | 13,49,DAPI-13
51 | 13,50,Ch2Cy13
52 | 13,51,Ch3Cy13
53 | 13,52,H2A.X
54 | 14,53,DAPI-14
55 | 14,54,Ch2Cy14
56 | 14,55,Ch3Cy14
57 | 14,56,Keratin5
58 | 15,57,DAPI-15
59 | 15,58,Ch2Cy15
60 | 15,59,Keratin14
61 | 15,60,p53
62 | 16,61,DAPI-16
63 | 16,62,Blank-4
64 | 16,63,Blank-5
65 | 16,64,Blank-6
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/CyCIF/Marker_ids.csv:
--------------------------------------------------------------------------------
1 | Marker_1
2 | Marker_2
3 | Marker_3
4 | Marker_4
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/CyCIF/markers.csv:
--------------------------------------------------------------------------------
1 | cycle_number,channel_number,marker_name
2 | 1,1,Marker_1
3 | 1,2,Marker_2
4 | 1,3,Marker_3
5 | 1,4,Marker_4
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/Cluster_categories.csv:
--------------------------------------------------------------------------------
1 | 0
2 | 0
3 | 0
4 | 0
5 | 1
6 | 1
7 | 2
8 | 2
9 | 2
10 | 1
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/Marker_ids.csv:
--------------------------------------------------------------------------------
1 | Marker_1
2 | Marker_2
3 | Marker_3
4 | Marker_4
5 | Marker_5
6 | Marker_6
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/Patient_ids.csv:
--------------------------------------------------------------------------------
1 | 4
2 | 4
3 | 4
4 | 5
5 | 3
6 | 2
7 | 0
8 | 0
9 | 1
10 | 3
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/Response_categories.csv:
--------------------------------------------------------------------------------
1 | Response 1
2 | Response 1
3 | Response 1
4 | Response 1
5 | Response 2
6 | Response 2
7 | Response 2
8 | Response 2
9 | Response 2
10 | Response 2
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/Treatment_categories.csv:
--------------------------------------------------------------------------------
1 | Treatment 1
2 | Treatment 2
3 | Treatment 2
4 | Treatment 2
5 | Treatment 1
6 | Treatment 2
7 | Treatment 1
8 | Treatment 1
9 | Treatment 1
10 | Treatment 1
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/X_imagetSNE.csv:
--------------------------------------------------------------------------------
1 | 1.3549204,-4.9493012
2 | 2.9607117,-7.3730865
3 | 3.5662005,-3.4312763
4 | 2.268297,-2.2885783
5 | 5.6273146,0.73978335
6 | 3.9662726,2.4958441
7 | -2.5741525,0.28250438
8 | -4.058194,3.2042348
9 | -2.2520936,1.395093
10 | 2.312218,0.39663085
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/Vectra/markers.csv:
--------------------------------------------------------------------------------
1 | cycle_number,channel_number,marker_name
2 | 1,1,Marker_1
3 | 1,2,Marker_2
4 | 1,3,Marker_3
5 | 1,4,Marker_4
6 | 2,5,Marker_5
7 | 2,6,Marker_6
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/t-CyCIF/Marker_ids.csv:
--------------------------------------------------------------------------------
1 | KERATIN
2 | FOXP3
3 | CD45
4 | PD-1
5 | PD-L1
6 | KI67
7 | CD8A
--------------------------------------------------------------------------------
/Mistic_code/code/user_inputs/metadata/t-CyCIF/markers.csv:
--------------------------------------------------------------------------------
1 | cycle_number,channel_number,marker_name
2 | 1,1,DNA1
3 | 1,2,488_BG_1
4 | 1,3,555_BG_1
5 | 1,4,647_BG_1
6 | 2,5,DNA2
7 | 2,6,488_BG_2
8 | 2,7,555_BG_2
9 | 2,8,647_BG_2
10 | 3,9,DNA3
11 | 3,10,488_BG_3
12 | 3,11,LAG3
13 | 3,12,ARL13B
14 | 4,13,DNA4
15 | 4,14,KI67
16 | 4,15,KERATIN
17 | 4,16,PD-1
18 | 5,17,DNA5
19 | 5,18,CD45RB
20 | 5,19,CD3D
21 | 5,20,PD-L1
22 | 6,21,DNA6
23 | 6,22,CD4
24 | 6,23,CD45
25 | 6,24,CD8A
26 | 7,25,DNA7
27 | 7,26,CD163
28 | 7,27,CD68
29 | 7,28,CD14
30 | 8,29,DNA8
31 | 8,30,CD11B
32 | 8,31,FOXP3
33 | 8,32,CD21
34 | 9,33,DNA9
35 | 9,34,IBA1
36 | 9,35,ASMA
37 | 9,36,CD20
38 | 10,37,DNA10
39 | 10,38,CD19
40 | 10,39,GFAP
41 | 10,40,G-TUBULIN
42 | 11,41,DNA11
43 | 11,42,LAMIN_A/C
44 | 11,43,BANF1
45 | 11,44,LAMIN_B
46 |
--------------------------------------------------------------------------------
/Mistic_code/color_scatter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | color_scatter.py example
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
44 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Mistic_code/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | image.py example
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Mistic_code/theme.yaml:
--------------------------------------------------------------------------------
1 | attrs:
2 | Figure:
3 | background_fill_color: '#938dba'
4 | border_fill_color: '#2F2F2F'
5 | outline_line_color: '#444444'
6 | Axis:
7 | axis_line_color: "white"
8 | axis_label_text_color: "white"
9 | major_label_text_color: "white"
10 | major_tick_line_color: "white"
11 | minor_tick_line_color: "white"
12 | minor_tick_line_color: "white"
13 | Grid:
14 | grid_line_dash: [6, 4]
15 | grid_line_alpha: .3
16 | Title:
17 | text_color: "white"
18 |
--------------------------------------------------------------------------------
/Mistic_code/title.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Bokeh Plot
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Mistic: image tSNE visualizer
2 |
3 | This is a Python tool using the Bokeh library to view multiple multiplex images simultaneously. The code has been tested on 7-panel Vectra TIFF, 32- & 64-panel CODEX TIFF, 16-panel CODEX QPTIFF, 4-panel CyCIF TIFF, and 44-panel t-CyCIF TIFF images.
4 |
5 | Mistic is published at [Patterns (2022)](https://www.cell.com/patterns/fulltext/S2666-3899(22)00120-9).
6 |
7 | Mistic's GUI with user inputs is shown below:
8 |
9 |
10 |
11 | Figure description: A sample Mistic GUI with user inputs is shown. **A.** User-input panel where imaging technique choice, stack montage option or markers can be selected, images borders can be added, new or pre-defined image display coordinates can be chosen, and a theme for the canvases can be selected. **B.** Static canvas showing the image t-SNE colored and arranged as per user inputs. **C.** Live canvas showing the corresponding t-SNE scatter plot where each image is represented as a dot. The live canvas has tabs for displaying additional information per image. Metadata for each image can be obtained by hovering over each dot.
12 |
13 |
14 | ## Features of Mistic
15 | * Two canvases:
16 | * still canvas with the image tSNE rendering
17 | * live canvases with tSNE scatter plots for image metadata rendering
18 | * Dropdown option to select the imaging technique: Vectra, CyCIF, t-CyCIF, or CODEX
19 | * Option to choose between Stack montage view or multiple multiplexed images by selecting the markers to be visualised at once
20 | * Option to place a border around each image based on image metadata
21 | * Option to use a pre-defined tSNE or generate a new set of tSNE co-ordinates
22 | * Option to shuffle images with the tSNE co-ordinates
23 | * Option to render multiple tSNE scatter plots based on image metadata
24 | * Hover functionality available on the tSNE scatter plot to get more information of each image
25 | * Save, zoom, etc each of the Bokeh canvases
26 |
27 | ## Requirements
28 |
29 | * Python >= 3.6
30 | * Install Python from here: https://www.python.org/downloads/
31 |
32 |
33 |
34 |
35 | * Open a command prompt (or the Terminal application):
36 | * Download ``` pip ```. Type:
37 | * ``` curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py ```
38 | * ``` python3 get-pip.py ``` and wait for the installation to complete
39 | * Verify the ``` pip ``` installation by typing ``` pip --version ```
40 | * ```pip install mistic ```
41 |
42 | ## Setting up Mistic
43 |
44 | * Download this code repository or Open Terminal and use `git clone`
45 |
46 | `$ git clone https://github.com/MathOnco/Mistic.git`
47 |
48 | * In the Mistic folder, navigate to /user_inputs folder to upload input files:
49 | * ```Mistic_code/code/user_inputs/```
50 | * Use the /figures folder to upload the multiplexed images
51 | * Example NSCLC Vectra dataset is available at: https://doi.org/10.5281/zenodo.6131933
52 | * Use the /metadata folder to
53 | * Upload the imaging markers of interest as Markers_ids.csv and markers.csv.
54 | * Example files are provided in the subfolders: Vectra, CyCIF, t-CyCIF and CODEX
55 | * Move the files from the relevant subfolder into the /metadata folder
56 | * Note: For the Stack Montage option, only the markers.csv file is required
57 | * Optional uploads:
58 | * Upload image tSNE co-ordinates as X_imagetSNE.csv
59 | * If no user-generated tSNE co-ordinates are provided, Mistic will generate a set of t-SNE coordinates to render the images
60 | * Upload image metadata such as
61 | * Cluster labels as Cluster_categories.csv
62 | * If cluster labels are not provided, Mistic will cluster the images using a Bayesian mixture model.
63 | * Patient_ids as Patient_ids.csv
64 | * Treatments as Treatment_catgories.csv
65 | * Patient response as Response_categories.csv
66 | * If any of these are unavailable, Mistic will use either the randomly-generated or user-provided tSNE points without any color coding i.e. dots are colored in gray.
67 | * Sample metadata files are provided for reference in separate subfolders for each imaging technique (Vectra, CyCIF, t-CyCIF and CODEX) in the /metadata folder
68 | * If using the sample metadata, move the files from the relevant subfolder into the /metadata folder
69 |
70 | ## Run Mistic
71 |
72 | * Open a command prompt (or the Terminal application), change to the directory containing /code and type:
73 | * ```bash mistic.sh```
74 | * This runs a bokeh server locally and will automatically open the interactive dashboard in your browser at http://localhost:5098/image_tSNE_GUI
75 | * Enter the imaging format, montage or multiplexed views and other user options on the GUI and click ```Run```.
76 |
77 | * Examples for running Mistic:
78 | * For instructions on how to run Mistic on the t-CyCIF data, please check: https://mistic-rtd.readthedocs.io/en/latest/vignette_example_tcycif.html
79 |
80 | * For instructions on how to run Mistic on the toy data from our NSCLC Vectra FoVs, please check:https://mistic-rtd.readthedocs.io/en/latest/vignette_example_vectra.html
81 |
82 |
83 | * If you get an error: ```Cannot start Bokeh server, port 5098 is already in use```, then at the Terminal, issue:
84 | * ```ps -ef | grep 5098```
85 | * You should see a line similar to the one below on the Terminal:
86 | ```55525 12519 11678 0 1:22AM ttys004 0:57.81 /opt/anaconda3/bin/python /opt/anaconda3/bin/bokeh serve --port 5098 --show image_tSNE_GUI```
87 | where the 2nd term is the _process id_. Here this is '12519'.
88 | * Use this _process id_ to kill the process: ```kill -9 12519```
89 |
90 |
91 | ## Additional information
92 |
93 | * Paper on bioRxiv: https://www.biorxiv.org/content/10.1101/2021.10.08.463728v1
94 | * Documentation: https://mistic-rtd.readthedocs.io
95 | * Code has been published at Zenodo: https://doi.org/10.5281/zenodo.5912169
96 | * Example NSCLC Vectra images are published here: https://doi.org/10.5281/zenodo.6131933
97 | * Mistic is highlighted on Bokeh's user showcase: http://bokeh.org/
98 |
99 |
100 |
--------------------------------------------------------------------------------
/fig_readme/Figure_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MathOnco/Mistic/ebbce7bd66b26bc368f64e74555bd131e6d6cd5f/fig_readme/Figure_2.jpg
--------------------------------------------------------------------------------
/fig_readme/GUI_Mistic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MathOnco/Mistic/ebbce7bd66b26bc368f64e74555bd131e6d6cd5f/fig_readme/GUI_Mistic.png
--------------------------------------------------------------------------------