Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files- app.py +191 -0
- params.npz +3 -0
- requirements.txt +6 -0
- settings.yaml +83 -0
app.py
ADDED
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# %%
|
2 |
+
import gradio as gr
|
3 |
+
from sklearn import preprocessing
|
4 |
+
import collections
|
5 |
+
import SimpleITK as sitk
|
6 |
+
from radiomics import featureextractor
|
7 |
+
import os
|
8 |
+
import numpy as np
|
9 |
+
from sklearn.preprocessing import StandardScaler
|
10 |
+
|
11 |
+
# %%
|
12 |
+
|
13 |
+
|
14 |
+
def resize_image_itk(itkimage, newSize, resamplemethod=sitk.sitkLinear):
|
15 |
+
|
16 |
+
resampler = sitk.ResampleImageFilter()
|
17 |
+
originSize = itkimage.GetSize() # 原来的体素块尺寸
|
18 |
+
# print(originSize)
|
19 |
+
originSpacing = itkimage.GetSpacing()
|
20 |
+
newSize = np.array(newSize, float)
|
21 |
+
factor = originSize / newSize
|
22 |
+
newSpacing = originSpacing * factor
|
23 |
+
newSize = newSize.astype(np.int0) # spacing肯定不能是整数
|
24 |
+
resampler.SetReferenceImage(itkimage) # 需要重新采样的目标图像
|
25 |
+
resampler.SetSize(newSize.tolist())
|
26 |
+
resampler.SetOutputSpacing(newSpacing.tolist())
|
27 |
+
resampler.SetTransform(sitk.Transform(3, sitk.sitkIdentity))
|
28 |
+
resampler.SetInterpolator(resamplemethod)
|
29 |
+
itkimgResampled = resampler.Execute(itkimage) # 得到重新采样后的图像
|
30 |
+
# print(itkimgResampled.GetSize())
|
31 |
+
|
32 |
+
return itkimgResampled
|
33 |
+
|
34 |
+
|
35 |
+
def load_image(img):
|
36 |
+
|
37 |
+
# imgfilePath = os.path.join(img_path, img_name)
|
38 |
+
imgfilePath = img
|
39 |
+
if not os.path.isfile(imgfilePath):
|
40 |
+
print('Error: IMAGE DIR READ FAILED!')
|
41 |
+
|
42 |
+
# mskfilePath = imgfilePath + '.img'
|
43 |
+
'''
|
44 |
+
# 读取.dcm Series
|
45 |
+
reader = sitk.ImageSeriesReader()
|
46 |
+
dcm_series = reader.GetGDCMSeriesFileNames(imgfilePath)
|
47 |
+
reader.SetFileNames(dcm_series)
|
48 |
+
image = reader.Execute()
|
49 |
+
mask = sitk.ReadImage(mskfilePath)
|
50 |
+
'''
|
51 |
+
# 读取.png或.jpeg图像——可在此处更换读取IO适应不同格式图像
|
52 |
+
if imgfilePath[-4:] == '.png':
|
53 |
+
image = sitk.ReadImage(
|
54 |
+
imgfilePath, imageIO="PNGImageIO", outputPixelType=sitk.sitkInt16)
|
55 |
+
else:
|
56 |
+
image = sitk.ReadImage(
|
57 |
+
imgfilePath, imageIO="JPEGImageIO", outputPixelType=sitk.sitkInt16)
|
58 |
+
|
59 |
+
# resize_img = resize_image_itk(image, newSize=(512, 512))
|
60 |
+
|
61 |
+
# img_array = sitk.GetArrayFromImage(resize_img)
|
62 |
+
|
63 |
+
return image # np.asarray(image)
|
64 |
+
|
65 |
+
|
66 |
+
def feature_extractor(img):
|
67 |
+
|
68 |
+
image = load_image(img)
|
69 |
+
params = 'settings.yaml'
|
70 |
+
if os.path.isfile(params):
|
71 |
+
extractor = featureextractor.RadiomicsFeatureExtractor(
|
72 |
+
params)
|
73 |
+
|
74 |
+
else:
|
75 |
+
settings = {}
|
76 |
+
settings['binWidth'] = 25
|
77 |
+
settings['resampledPixelSpacing'] = [3, 3, 3]
|
78 |
+
settings['interpolator'] = sitk.sitkBSpline
|
79 |
+
settings['enableCExtensions'] = True
|
80 |
+
|
81 |
+
extractor = featureextractor.RadiomicsFeatureExtractor(**settings)
|
82 |
+
|
83 |
+
headers = None
|
84 |
+
featureVector = collections.OrderedDict()
|
85 |
+
|
86 |
+
# image = sitk.GetImageFromArray(img_array)
|
87 |
+
img_array = sitk.GetArrayFromImage(image)
|
88 |
+
mask = sitk.GetImageFromArray(np.ones_like(img_array), isVector=True)
|
89 |
+
|
90 |
+
mask.CopyInformation(image)
|
91 |
+
|
92 |
+
row = []
|
93 |
+
# try:
|
94 |
+
featureVector.update(extractor.execute(
|
95 |
+
image, mask, label=1)) # 提取特征ROI区域 label=1,2,3,4
|
96 |
+
if headers is None:
|
97 |
+
headers = list(featureVector.keys()) # [22:]
|
98 |
+
|
99 |
+
for h in headers:
|
100 |
+
row.append(featureVector.get(h, "N/A"))
|
101 |
+
# except Exception:
|
102 |
+
# print('Error: FEATURE EXTRACTION FAILED!')
|
103 |
+
|
104 |
+
return np.array(row[22:])
|
105 |
+
|
106 |
+
|
107 |
+
def tansig(x):
|
108 |
+
return (2/(1+np.exp(-2*x)))-1
|
109 |
+
|
110 |
+
|
111 |
+
def to_proba(output):
|
112 |
+
output = output-np.min(output)
|
113 |
+
ret = (output-np.min(output))/(np.max(output)-np.min(output))
|
114 |
+
return ret
|
115 |
+
|
116 |
+
|
117 |
+
def softmax(x):
|
118 |
+
x = x-np.min(x)
|
119 |
+
ex = np.exp(x)
|
120 |
+
return ex/ex.sum()
|
121 |
+
|
122 |
+
|
123 |
+
labels = ['covid', 'nofindings', 'pneumonia']
|
124 |
+
|
125 |
+
|
126 |
+
def predict(test_img):
|
127 |
+
|
128 |
+
params = np.load('params.npz')
|
129 |
+
N1 = params['N1']
|
130 |
+
N2 = params['N2']
|
131 |
+
Beta1OfEachWindow = params['Beta1OfEachWindow']
|
132 |
+
ymax = params['ymax']
|
133 |
+
ymin = params['ymin']
|
134 |
+
minOfEachWindow = params['minOfEachWindow']
|
135 |
+
distOfMaxAndMin = params['distOfMaxAndMin']
|
136 |
+
weightOfEnhanceLayer = params['weightOfEnhanceLayer']
|
137 |
+
parameterOfShrink = params['parameterOfShrink']
|
138 |
+
OutputWeight = params['OutputWeight']
|
139 |
+
|
140 |
+
test_x = feature_extractor(test_img).reshape(1, -1)
|
141 |
+
scaler = StandardScaler()
|
142 |
+
test_x = scaler.fit_transform(test_x)
|
143 |
+
print(test_x.shape)
|
144 |
+
# test_x = np.expand_dims(test_x, axis=0)
|
145 |
+
# print(test_x.shape)
|
146 |
+
test_x = preprocessing.scale(test_x, axis=1)
|
147 |
+
|
148 |
+
FeatureOfInputDataWithBiasTest = np.hstack(
|
149 |
+
[test_x, 0.1 * np.ones((test_x.shape[0], 1))])
|
150 |
+
OutputOfFeatureMappingLayerTest = np.zeros(
|
151 |
+
[test_x.shape[0], N2 * N1])
|
152 |
+
|
153 |
+
for i in range(N2):
|
154 |
+
outputOfEachWindowTest = np.dot(
|
155 |
+
FeatureOfInputDataWithBiasTest, Beta1OfEachWindow[i])
|
156 |
+
OutputOfFeatureMappingLayerTest[:, N1*i: N1*(i+1)] = (ymax - ymin)*(
|
157 |
+
outputOfEachWindowTest - minOfEachWindow[i]) / distOfMaxAndMin[i] - ymin
|
158 |
+
|
159 |
+
InputOfEnhanceLayerWithBiasTest = np.hstack(
|
160 |
+
[OutputOfFeatureMappingLayerTest, 0.1 * np.ones((OutputOfFeatureMappingLayerTest.shape[0], 1))])
|
161 |
+
tempOfOutputOfEnhanceLayerTest = np.dot(
|
162 |
+
InputOfEnhanceLayerWithBiasTest, weightOfEnhanceLayer)
|
163 |
+
|
164 |
+
OutputOfEnhanceLayerTest = tansig(
|
165 |
+
tempOfOutputOfEnhanceLayerTest * parameterOfShrink)
|
166 |
+
|
167 |
+
InputOfOutputLayerTest = np.hstack(
|
168 |
+
[OutputOfFeatureMappingLayerTest, OutputOfEnhanceLayerTest])
|
169 |
+
|
170 |
+
OutputOfTest = np.dot(InputOfOutputLayerTest, OutputWeight)
|
171 |
+
# print(OutputOfTest.shape)
|
172 |
+
OutputOfTest = np.squeeze(OutputOfTest)
|
173 |
+
proba = OutputOfTest # softmax(OutputOfTest)
|
174 |
+
confidences = {labels[i]: float(proba[i]) for i in range(3)}
|
175 |
+
# np.argmax(OutputOfTest, axis=1) # np.array(OutputOfTest)
|
176 |
+
return confidences
|
177 |
+
|
178 |
+
|
179 |
+
# %%
|
180 |
+
# pth = '01E392EE-69F9-4E33-BFCE-E5C968654078.jpeg'
|
181 |
+
# predict(pth)
|
182 |
+
|
183 |
+
# %%
|
184 |
+
|
185 |
+
demo = gr.Interface(fn=predict,
|
186 |
+
inputs=gr.Image(type="filepath"),
|
187 |
+
outputs=gr.Label()
|
188 |
+
)
|
189 |
+
demo.launch(share=False)
|
190 |
+
|
191 |
+
# %%
|
params.npz
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1eccc3eea4d4ee28bdb68bcb253f2ebf5f8545ce97685dc26a043d731cda911a
|
3 |
+
size 135798
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio==3.22.1
|
2 |
+
numpy==1.21.6
|
3 |
+
pyradiomics==3.0.1
|
4 |
+
radiomics==0.1
|
5 |
+
scikit_learn==1.2.2
|
6 |
+
SimpleITK==2.2.1
|
settings.yaml
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This is an example of settings that can be used as a starting point for analyzing CT data. This is only intended as a
|
2 |
+
# starting point and is not likely to be the optimal settings for your dataset. Some points in determining better values
|
3 |
+
# are added as comments where appropriate
|
4 |
+
|
5 |
+
# When adapting and using these settings for an analysis, be sure to add the PyRadiomics version used to allow you to
|
6 |
+
# easily recreate your extraction at a later timepoint:
|
7 |
+
|
8 |
+
# ############################# Extracted using PyRadiomics version: <version> ######################################
|
9 |
+
|
10 |
+
imageType:
|
11 |
+
Original: {}
|
12 |
+
#LoG:
|
13 |
+
# sigma: [1.0, 2.0] #, 3.0, 4.0, 5.0] # If you include sigma values >5, remember to also increase the padDistance.
|
14 |
+
Wavelet: {}
|
15 |
+
|
16 |
+
featureClass:
|
17 |
+
# redundant Compactness 1, Compactness 2 an Spherical Disproportion features are disabled by default, they can be
|
18 |
+
# enabled by specifying individual feature names (as is done for glcm) and including them in the list.
|
19 |
+
#shape:
|
20 |
+
firstorder:
|
21 |
+
glcm: # Disable SumAverage by specifying all other GLCM features available
|
22 |
+
- "Autocorrelation"
|
23 |
+
- "JointAverage"
|
24 |
+
- "ClusterProminence"
|
25 |
+
- "ClusterShade"
|
26 |
+
- "ClusterTendency"
|
27 |
+
- "Contrast"
|
28 |
+
- "Correlation"
|
29 |
+
- "DifferenceAverage"
|
30 |
+
- "DifferenceEntropy"
|
31 |
+
- "DifferenceVariance"
|
32 |
+
- "JointEnergy"
|
33 |
+
- "JointEntropy"
|
34 |
+
- "Imc1"
|
35 |
+
- "Imc2"
|
36 |
+
- "Idm"
|
37 |
+
- "Idmn"
|
38 |
+
- "Id"
|
39 |
+
- "Idn"
|
40 |
+
- "InverseVariance"
|
41 |
+
- "MaximumProbability"
|
42 |
+
- "SumEntropy"
|
43 |
+
- "SumSquares"
|
44 |
+
#glrlm:
|
45 |
+
#glszm:
|
46 |
+
gldm:
|
47 |
+
|
48 |
+
setting:
|
49 |
+
# Normalization:
|
50 |
+
# most likely not needed, CT gray values reflect absolute world values (HU) and should be comparable between scanners.
|
51 |
+
# If analyzing using different scanners / vendors, check if the extracted features are correlated to the scanner used.
|
52 |
+
# If so, consider enabling normalization by uncommenting settings below:
|
53 |
+
#normalize: true
|
54 |
+
#normalizeScale: 500 # This allows you to use more or less the same bin width.
|
55 |
+
|
56 |
+
# Resampling:
|
57 |
+
# Usual spacing for CT is often close to 1 or 2 mm, if very large slice thickness is used,
|
58 |
+
# increase the resampled spacing.
|
59 |
+
# On a side note: increasing the resampled spacing forces PyRadiomics to look at more coarse textures, which may or
|
60 |
+
# may not increase accuracy and stability of your extracted features.
|
61 |
+
#interpolator: "sitkBSpline"
|
62 |
+
#resampledPixelSpacing: [1, 1]
|
63 |
+
# padDistance: 10 # Extra padding for large sigma valued LoG filtered images
|
64 |
+
|
65 |
+
# Mask validation:
|
66 |
+
# correctMask and geometryTolerance are not needed, as both image and mask are resampled, if you expect very small
|
67 |
+
# masks, consider to enable a size constraint by uncommenting settings below:
|
68 |
+
#minimumROIDimensions: 2
|
69 |
+
#minimumROISize: 50
|
70 |
+
|
71 |
+
# Image discretization:
|
72 |
+
# The ideal number of bins is somewhere in the order of 16-128 bins. A possible way to define a good binwidt is to
|
73 |
+
# extract firstorder:Range from the dataset to analyze, and choose a binwidth so, that range/binwidth remains approximately
|
74 |
+
# in this range of bins.
|
75 |
+
binWidth: 25
|
76 |
+
|
77 |
+
# first order specific settings:
|
78 |
+
#voxelArrayShift: 1000 # Minimum value in HU is -1000, shift +1000 to prevent negative values from being squared.
|
79 |
+
|
80 |
+
# Misc:
|
81 |
+
# default label value. Labels can also be defined in the call to featureextractor.execute, as a commandline argument,
|
82 |
+
# or in a column "Label" in the input csv (batchprocessing)
|
83 |
+
label: 1
|