Rules99 commited on
Commit
92a3aed
1 Parent(s): 33ee672

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +295 -0
app.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from numpy import load
3
+ from numpy import expand_dims
4
+ from matplotlib import pyplot
5
+ from PIL import Image, ImageDraw, ImageFont
6
+ import numpy as np
7
+ import os
8
+ import os,sys
9
+ sys.path.insert(0,"..")
10
+ from glob import glob
11
+ import torch
12
+ import torchvision
13
+ import sys
14
+ import torch.nn.functional as F
15
+ import torchxrayvision as xrv
16
+ import pydicom as dicom
17
+ import PIL # optional
18
+ import pandas as pd
19
+ import matplotlib.pyplot as plt
20
+ import os
21
+ import cv2
22
+ import skimage
23
+ from skimage.transform import rescale, resize, downscale_local_mean
24
+ import operator
25
+ import mols2grid
26
+ import streamlit.components.v1 as components
27
+ from rdkit import Chem
28
+ from rdkit.Chem.Descriptors import ExactMolWt
29
+ from chembl_webresource_client.new_client import new_client
30
+
31
+ ### Title
32
+ st.markdown("<h1 style='text-align: center;'>Chest Anomaly Identifier</h1>",unsafe_allow_html=True)
33
+ ### Description
34
+ st.markdown("""<p style='text-align: center;'>The goal of this application is mainly to help doctors to interpret
35
+ Chest X-Ray Images, being able to find medical compounds in a quick way to deal with Chest's anomalies found</p>""",unsafe_allow_html=True)
36
+
37
+ ### Image
38
+ st.image("doctors.jpg")
39
+
40
+ ### Uploder
41
+ # st.markdown("""<p style='text-align: center;'>The goal of this application is mainly to help doctors to interpret
42
+ # Chest X-Ray Images, being able to find medical compounds in a quick way to deal with Chest's anomalies found</p>""",unsafe_allow_html=True)
43
+ uploaded_file = st.file_uploader("Choose an X-Ray image to detect anomalies of the chest (the file must be a dicom extension or jpg)")
44
+
45
+
46
+
47
+
48
+ #### Get Compounds found
49
+ @st.cache(allow_output_mutation=True)
50
+ def getdrugs(name,phase):
51
+ drug_indication = new_client.drug_indication
52
+ molecules = new_client.molecule
53
+ obj = drug_indication.filter(efo_term__icontains=name)
54
+ appdrugs = molecules.filter(molecule_chembl_id__in=[x['molecule_chembl_id'] for x in obj])
55
+
56
+
57
+ if phase!=[]:
58
+ temp = None
59
+ for ph in phase:
60
+ dftemp = pd.DataFrame.from_dict(appdrugs.filter(max_phase=int(ph)))
61
+ dftemp["phase"] = int(ph)
62
+ if isinstance(temp,pd.DataFrame):
63
+ temp= pd.concat([temp,dftemp],axis=0)
64
+ else:
65
+ temp = dftemp
66
+
67
+ df = temp
68
+ else:
69
+ df = pd.DataFrame.from_dict(appdrugs)
70
+
71
+ try:
72
+ df.dropna(subset=["molecule_properties","molecule_structures"],inplace=True)
73
+
74
+ df["smiles"] = df.molecule_structures.apply(lambda x:x["canonical_smiles"])
75
+ df["Acceptors"] = df.molecule_properties.apply(lambda x :x["hba"])
76
+ df["Donnors"] = df.molecule_properties.apply(lambda x :x["hbd"])
77
+ df["mol_weight"] = df.molecule_properties.apply(lambda x :x["mw_freebase"])
78
+ df["Logp"] = df.molecule_properties.apply(lambda x :x["cx_logp"])
79
+
80
+ subs = ["pref_name","smiles","Acceptors","Donnors","mol_weight","Logp"]
81
+ df.dropna(subset=subs,inplace=True)
82
+ df["Acceptors"] = df["Acceptors"].astype(int)
83
+ df["Donnors"] = df["Donnors"].astype(int)
84
+ df["mol_weight"] = df["mol_weight"].astype(float)
85
+ df["Logp"] = df["Logp"] .astype(float)
86
+
87
+ return df.loc[:,subs]
88
+ except:
89
+ return None
90
+
91
+ ### Read Chest X Ray Image
92
+ def read_image(imgpath):
93
+
94
+ if (str(imgpath).find("jpg")!=-1) or (str(imgpath).find("png")!=-1):
95
+
96
+ # sample = Image.open("JPG_test/0c4eb1e1-b801903c-bcebe8a4-3da9cd3c-3b94a27c.jpg")
97
+ sample = Image.open(imgpath)
98
+ return np.array(sample)
99
+ if str(imgpath).find("dcm")!=-1:
100
+ img = dicom.dcmread(imgpath).pixel_array
101
+ return img
102
+
103
+ ### Generate torchxrayvision model to find output probabilities
104
+ def generatemodel(xrvmodel,wts):
105
+ return xrvmodel(weights=wts)
106
+ ### Transform the image to ouput some illness
107
+ def transform2(img):
108
+ input_tensor = torch.from_numpy(img).unsqueeze(0)
109
+ img = input_tensor.numpy()[0, 0, :]
110
+ img = (img / 1024.0 / 2.0) + 0.5
111
+ img = np.clip(img, 0, 1)
112
+ img = Image.fromarray(np.uint8(img * 255) , 'L')
113
+ return img
114
+ ### Transform the image to test an output image
115
+ def transform(img):
116
+
117
+ img = ((img-img.min())/(img.max()-img.min())*255)
118
+
119
+
120
+ # img = (img / 1024.0 / 2.0) + 0.5
121
+ # img = np.clip(img, 0, 1)
122
+ # img = Image.fromarray(np.uint8(img * 255) , 'L')
123
+ # print(img.shape)
124
+ # img = skimage.io.imread("JPG_test/0c4eb1e1-b801903c-bcebe8a4-3da9cd3c-3b94a27c.jpg")
125
+ # print(img.max())
126
+ img = xrv.datasets.normalize(np.array(img), 255)
127
+
128
+ # Check that images are 2D arrays
129
+ if len(img.shape) > 2:
130
+ img = img[:, :, 0]
131
+ if len(img.shape) < 2:
132
+ print("error, dimension lower than 2 for image")
133
+
134
+ # Add color channel
135
+ img = img[None, :, :]
136
+
137
+ transform = torchvision.transforms.Compose([xrv.datasets.XRayCenterCrop(),
138
+ xrv.datasets.XRayResizer(224,engine="cv2")])
139
+
140
+ img = transform(img)
141
+ return img
142
+ ### Returns the output probabilities of having certain illnesses anomalies
143
+ def testimage(model,img):
144
+ # with torch.no_grad():
145
+ model.eval()
146
+ out = model(torch.from_numpy(img).unsqueeze(0)).cpu()
147
+ # out = model(img).cpu()
148
+ # out = torch.sigmoid(out)
149
+
150
+ return {key:value for (key,value) in zip(model.pathologies, out.detach().numpy()[0]) if len(key)>2}
151
+
152
+ ### Resize the model
153
+ def outputprob2(img,pr_model,visimage=True):
154
+ ### Read an image
155
+ img = resize(img, (img.shape[0] // 2, img.shape[1] // 2),
156
+ anti_aliasing=True)
157
+
158
+ ### Preprocessmodel
159
+ img_t = transform(img)
160
+ ### Test an image
161
+ return testimage(pr_model,img_t)
162
+
163
+ ### Pipeline since we read an image until the ouput it is generated
164
+ def outputprob(imgpath,pr_model,visimage=True):
165
+ ### Read an image
166
+ img = read_image(imgpath)
167
+ if visimage:
168
+ plt.imshow(img,cmap="gray")
169
+ plt.show()
170
+ ### Preprocessmodel
171
+ img_t = transform(img)
172
+ ### Test an image
173
+ return testimage(pr_model,img_t)
174
+
175
+
176
+ ### Error in case we do not find compounds
177
+ def error(option):
178
+ option = str(option).replace(" ","%20")
179
+ st.markdown(f"""
180
+ We have not found compounds for this illness; for more information visit this link:
181
+ [Chemble](https://www.ebi.ac.uk/chembl/g/#search_results/all/query={option})
182
+ """, unsafe_allow_html=True)
183
+
184
+ ### If you insert an image
185
+ if uploaded_file is not None:
186
+ ## Controller header
187
+
188
+ st.sidebar.markdown("<h1 style='text-align: center;'>Compound's filter</h1>",unsafe_allow_html=True)
189
+ ## Write the compound
190
+ st.sidebar.markdown('''
191
+ <h4 style='text-align: center;'>This controller sidebar is used to filter the compounds by the following features</h4>
192
+
193
+ - Molecular weight : is the weight of a compound in grame per mol
194
+ - LogP : it measures how hydrophilic or hydrophobic a compound is
195
+ - NumDonnors : number of chemical components that are able to deliver electrons to other chemical components
196
+ - NumAcceptors : number of chemical components that are able to accept electrons to other chemical components
197
+ ''',unsafe_allow_html=True)
198
+ weight_cutoff = st.sidebar.slider(
199
+ label="Molecular weight",
200
+ min_value=0,
201
+ max_value=1000,
202
+ value=500,
203
+ step=10,
204
+ help="Look for compounds that have less or equal molecular weight than the value selected"
205
+ )
206
+ logp_cutoff = st.sidebar.slider(
207
+ label="LogP",
208
+ min_value=-10,
209
+ max_value=10,
210
+ value=5,
211
+ step=1,
212
+ help="Look for compounds that have less or equal logp than the value selected"
213
+ )
214
+ NumHDonors_cutoff = st.sidebar.slider(
215
+ label="NumHDonors",
216
+ min_value=0,
217
+ max_value=15,
218
+ value=5,
219
+ step=1,
220
+ help="Look for compounds that have less or equal donors weight than the value selected"
221
+ )
222
+ NumHAcceptors_cutoff = st.sidebar.slider(
223
+ label="NumHAcceptors",
224
+ min_value=0,
225
+ max_value=20,
226
+ value=10,
227
+ step=1,
228
+ help="Look for compounds that have less or equal acceptors weight than the value selected"
229
+ )
230
+ max_phase = st.sidebar.multiselect("Phase of the compound",
231
+ ['1','2', '3', '4'],
232
+ help="""
233
+ - Phase 1 : Phase I of the compound in progress
234
+ - Phase 2 : Phase II of the compound in progress
235
+ - Phase 3 : Phase III of the compound in progress
236
+ - Phase 4 : Approved compound
237
+ """
238
+ )
239
+
240
+ #### Read an image
241
+
242
+
243
+ imgdef = read_image(uploaded_file)
244
+
245
+ ### Plot the input image
246
+ fig, ax = plt.subplots()
247
+ ax.imshow(imgdef,cmap="gray")
248
+ st.pyplot(fig=fig)
249
+ # Printing the possibility of having anomalies
250
+ st.markdown("<h3 style='text-align: center;'>Possibility of anomalies</h3>",unsafe_allow_html=True)
251
+ model = generatemodel(xrv.models.DenseNet,"densenet121-res224-mimic_ch") ### MIMIC MODEL+
252
+ model.eval()
253
+ pr = outputprob2(imgdef,model)
254
+
255
+ # Sort results by the descending probability order
256
+ pr = dict( sorted(pr.items(), key=operator.itemgetter(1),reverse=True))
257
+ # Select the treatment
258
+ option = st.sidebar.selectbox('Select the treatment you believe for these illness',list(pr.keys()))
259
+ col1,col2,col3 = st.columns((1,1,1))
260
+ cnt = 1
261
+ for (key,value) in pr.items():
262
+ if cnt%3==1:
263
+ col1.metric(label=key, value=str(cnt), delta=str(value))
264
+ if cnt%3==2:
265
+ col2.metric(label=key, value=str(cnt), delta=str(value))
266
+ if cnt%3==0:
267
+ col3.metric(label=key, value=str(cnt), delta=str(value))
268
+ cnt+=1
269
+ # temp = st.expander("Compunds to take care of {}".format(key))
270
+ #### Get the compounds for the anomaly selected
271
+ df = getdrugs(option,max_phase)
272
+ st.markdown("<h3 style='text-align: center;'>Compounds for {}</h3>".format(option),unsafe_allow_html=True)
273
+ ### If exists the compounds
274
+ if df is not None:
275
+
276
+ #### Filter dataframe by controllers
277
+ df_result = df[df["mol_weight"] < weight_cutoff]
278
+ df_result2 = df_result[df_result["Logp"] < logp_cutoff]
279
+ df_result3 = df_result2[df_result2["Donnors"] < NumHDonors_cutoff]
280
+ df_result4 = df_result3[df_result3["Acceptors"] < NumHAcceptors_cutoff]
281
+
282
+
283
+
284
+ if len(df_result4)==0:
285
+
286
+ error(option)
287
+ else:
288
+ raw_html = mols2grid.display(df_result, mapping={"smiles": "SMILES","pref_name":"Name","Acceptors":"Acceptors","Donnors":"Donnors","Logp":"Logp","mol_weight":"mol_weight"},
289
+ subset=["img","Name"],tooltip=["Name","Acceptors","Donnors","Logp","mol_weight"],tooltip_placement="top",tooltip_trigger="click hover")._repr_html_()
290
+
291
+ components.html(raw_html, width=900, height=900, scrolling=True)
292
+ #### We do not find compounds for the anomaly
293
+ else:
294
+ error(option)
295
+