1
1
import numpy as np
2
2
from scipy .io import loadmat
3
3
import nibabel as nib
4
+ import json
4
5
5
6
##########
6
7
# code written by Oliver J Gurney-Champion
@@ -21,8 +22,8 @@ def phantom(bvalue, noise, TR=3000, TE=40, motion=False, rician=False, interleav
21
22
XCAT = mat_data ['IMG' ]
22
23
XCAT = XCAT [0 :- 1 :2 ,0 :- 1 :2 ,10 :160 :4 ]
23
24
24
- tissue_included , D , f , Ds = contrast_curve_calc ()
25
- S , Dim , fim , Dpim = XCAT_to_MR_DCE (XCAT , TR , TE , bvalue , tissue_included , D , f , Ds )
25
+ D , f , Ds = contrast_curve_calc ()
26
+ S , Dim , fim , Dpim , legend = XCAT_to_MR_DCE (XCAT , TR , TE , bvalue , D , f , Ds )
26
27
if state == 1 :
27
28
Dim_out = Dim
28
29
fim_out = fim
@@ -72,16 +73,15 @@ def phantom(bvalue, noise, TR=3000, TE=40, motion=False, rician=False, interleav
72
73
state2 = state2 + 1
73
74
else :
74
75
S = np .squeeze (totsig )
75
- return S , XCAT , Dim_out , fim_out , Dpim_out
76
+ return S , XCAT , Dim_out , fim_out , Dpim_out , legend
76
77
77
78
78
79
def ivim (bvalues ,D ,f ,Ds ):
79
80
return (1 - f ) * np .exp (- D * bvalues ) + f * np .exp (- Ds * bvalues )
80
81
81
82
def contrast_curve_calc ():
82
- tissue_included = np .array ([1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 13 , 17 , 18 , 20 , 22 , 23 , 24 , 25 , 26 , 30 , 36 , 37 , 40 , 41 , 42 , 43 , 50 , 73 ])
83
83
84
- D = np .zeros (74 )
84
+ D = np .full (74 , np . nan )
85
85
D [1 ] = 2.4e-3 # 1 Myocardium LV : Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
86
86
D [2 ] = 2.4e-3 # 2 myocardium RV: Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
87
87
D [3 ] = 2e-3 # 3 myocardium la
@@ -109,7 +109,7 @@ def contrast_curve_calc():
109
109
D [50 ] = 3e-3 # 50 pericardium
110
110
D [73 ] = 1.8e-3 # 73 Pancreatic tumor (advanced state, from literature)
111
111
112
- f = np .zeros (74 )
112
+ f = np .full (74 , np . nan )
113
113
f [1 ] = 0.15 # 1 Myocardium LV : Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
114
114
f [2 ] = 0.15 # 2 myocardium RV : Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
115
115
f [3 ] = 0.07 # 3 myocardium la
@@ -137,7 +137,7 @@ def contrast_curve_calc():
137
137
f [50 ] = 0.07 # 50 pericardium
138
138
f [73 ] = 0.37 # 73 Pancreatic tumor (advanced state, from literature)
139
139
140
- Ds = np .zeros (74 )
140
+ Ds = np .full (74 , np . nan )
141
141
Ds [1 ] = 0.08 # 1 Myocardium LV: Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
142
142
Ds [2 ] = 0.08 # 2 myocardium RV: Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
143
143
Ds [3 ] = 0.07 # 3 myocardium la
@@ -166,10 +166,10 @@ def contrast_curve_calc():
166
166
Ds [73 ] = 0.01 # 73 Pancreatic tumor (advanced state, from literature)
167
167
# Return values
168
168
169
- return tissue_included , D , f , Ds
169
+ return D , f , Ds
170
170
171
171
172
- def XCAT_to_MR_DCE (XCAT , TR , TE , bvalue , tissue_included , D , f , Ds , b0 = 3 , ivim_cont = True ):
172
+ def XCAT_to_MR_DCE (XCAT , TR , TE , bvalue , D , f , Ds , b0 = 3 , ivim_cont = True ):
173
173
###########################################################################################
174
174
# This script converts XCAT tissue values to MR contrast based on the SSFP signal equation.
175
175
# Christopher W. Roy 2018-12-04 # fetal.xcmr@gmail.com
@@ -180,78 +180,81 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, tissue_included, D, f, Ds, b0=3, ivim_c
180
180
# Stanisz GJ, Odrobina EE, Pun J, Escaravage M, Graham SJ, Bronskill MJ, Henkelman RM. T1, T2 relaxation and magnetization transfer in tissue at 3T. Magnetic resonance in medicine. 2005;54:507�12.
181
181
# Portnoy S, Osmond M, Zhu MY, Seed M, Sled JG, Macgowan CK. Relaxation properties of human umbilical cord blood at 1.5 Tesla. Magnetic Resonance in Medicine. 2016;00:1�13.
182
182
# https://www.itis.ethz.ch/virtual-population/tissue-properties/XCATbase/relaxation-times/ #Tissue legend:
183
- # 1 Myocardium LV
184
- # 2 myocardium RV #
185
- # 3 myocardium la
186
- # 4 myocardium ra # 5 Blood LV
187
- #6 Blood RV
188
- #7 Blood LA
189
- #8 Blood RA
190
- #9 body
191
- #10 muscle
192
- #11 Brain
193
- #12 Sinus
194
- #13 Liver
195
- #14 gall bladder
196
- #15 Right Lung
197
- #16 Left Lung
198
- #17 esophagus
199
- #18 esophagus cont
200
- #19 laryngopharynx
201
- #20 st wall
202
- #21 Stomach Contents
203
- #22 pancreas
204
- #23 Right kydney cortex
205
- #24 right kidney medulla
206
- #25 Left kidney cortex
207
- #26 left kidney medulla
208
- #27 adrenal
209
- #28 Right Renal Pelvis
210
- #29 Left Renal Pelvis
211
- #30 spleen
212
- #31 Ribs
213
- #32 Cortical Bone
214
- #33 Spine
215
- #34 spinal cord
216
- #35 Bone Marrow
217
- #36 Artery
218
- #37 Vein
219
- #38 bladder
220
- #39 prostate
221
- #40 asc lower intestine
222
- #41 trans lower intestine
223
- #42 desc lower intestine
224
- #43 small intestine
225
- #44 rectum
226
- #45 seminal vescile
227
- #46 vas deference
228
- #47 testicles
229
- #48 epididymus
230
- #49 ejac duct
231
- #50 pericardium
232
- #51 Cartilage
233
- #52 Intestine Cavity
234
- #53 ureter
235
- #54 urethra
236
- #55 Lymph
237
- #56 lymph abnormal
238
- #57 trach bronch
239
- #58 Airway
240
- #59 uterus
241
- #60 vagina
242
- #61 right ovary
243
- #62 left ovary
244
- #63 FAllopian tubes
245
- #64 Parathyroid
246
- #65 Thyroid
247
- #66 Thymus
248
- #67 salivary
249
- #68 Pituitary
250
- #69 Eye
251
- #70 eye lens
252
- #71 lesion
253
- #72 Fat
254
- # 73 Pancreas tumor ##############################################################################
183
+ legend = {
184
+ 1 : 'Myocardium LV' ,
185
+ 2 : 'myocardium RV' , #
186
+ 3 : 'myocardium la' ,
187
+ 4 : 'myocardium ra' , # 5 Blood LV
188
+ 6 : 'Blood RV' ,
189
+ 7 : 'Blood LA' ,
190
+ 8 : 'Blood RA' ,
191
+ 9 : 'body' ,
192
+ 10 : 'muscle' ,
193
+ 11 : 'Brain' ,
194
+ 12 : 'Sinus' ,
195
+ 13 : 'Liver' ,
196
+ 14 : 'gall bladder' ,
197
+ 15 : 'Right Lung' ,
198
+ 16 : 'Left Lung' ,
199
+ 17 : 'esophagus' ,
200
+ 18 : 'esophagus cont' ,
201
+ 19 : 'laryngopharynx' ,
202
+ 20 : 'st wall' ,
203
+ 21 : 'Stomach Contents' ,
204
+ 22 : 'pancreas' ,
205
+ 23 : 'Right kydney cortex' ,
206
+ 24 : 'right kidney medulla' ,
207
+ 25 : 'Left kidney cortex' ,
208
+ 26 : 'left kidney medulla' ,
209
+ 27 : 'adrenal' ,
210
+ 28 : 'Right Renal Pelvis' ,
211
+ 29 : 'Left Renal Pelvis' ,
212
+ 30 : 'spleen' ,
213
+ 31 : 'Ribs' ,
214
+ 32 : 'Cortical Bone' ,
215
+ 33 : 'Spine' ,
216
+ 34 : 'spinal cord' ,
217
+ 35 : 'Bone Marrow' ,
218
+ 36 : 'Artery' ,
219
+ 37 : 'Vein' ,
220
+ 38 : 'bladder' ,
221
+ 39 : 'prostate' ,
222
+ 40 : 'asc lower intestine' ,
223
+ 41 : 'trans lower intestine' ,
224
+ 42 : 'desc lower intestine' ,
225
+ 43 : 'small intestine' ,
226
+ 44 : 'rectum' ,
227
+ 45 : 'seminal vescile' ,
228
+ 46 : 'vas deference' ,
229
+ 47 : 'testicles' ,
230
+ 48 : 'epididymus' ,
231
+ 49 : 'ejac duct' ,
232
+ 50 : 'pericardium' ,
233
+ 51 : 'Cartilage' ,
234
+ 52 : 'Intestine Cavity' ,
235
+ 53 : 'ureter' ,
236
+ 54 : 'urethra' ,
237
+ 55 : 'Lymph' ,
238
+ 56 : 'lymph abnormal' ,
239
+ 57 : 'trach bronch' ,
240
+ 58 : 'Airway' ,
241
+ 59 : 'uterus' ,
242
+ 60 : 'vagina' ,
243
+ 61 : 'right ovary' ,
244
+ 62 : 'left ovary' ,
245
+ 63 : 'FAllopian tubes' ,
246
+ 64 : 'Parathyroid' ,
247
+ 65 : 'Thyroid' ,
248
+ 66 : 'Thymus' ,
249
+ 67 : 'salivary' ,
250
+ 68 : 'Pituitary' ,
251
+ 69 : 'Eye' ,
252
+ 70 : 'eye lens' ,
253
+ 71 : 'lesion' ,
254
+ 72 : 'Fat' ,
255
+ 73 : 'Pancreas tumor' ,
256
+ }
257
+ ###############################################################################
255
258
np .random .seed (42 )
256
259
Tissue = np .zeros ((74 , 4 ))
257
260
Tissue [1 ] = [1030 , 40 , 1471 , 47 ]
@@ -343,7 +346,7 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, tissue_included, D, f, Ds, b0=3, ivim_c
343
346
T1 = Tissue [iTissue , 2 ]
344
347
T2 = Tissue [iTissue , 3 ]
345
348
346
- if ivim_cont and iTissue in tissue_included :
349
+ if ivim_cont and not np . isnan ([ D [ iTissue ], f [ iTissue ], Ds [ iTissue ]]). any () :
347
350
# note we are assuming blood fraction has the same T1 as tissue fraction here for simplicity. Can be changed in future.
348
351
Dtemp = D [iTissue ]
349
352
ftemp = f [iTissue ]
@@ -352,28 +355,53 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, tissue_included, D, f, Ds, b0=3, ivim_c
352
355
Dtemp = 5e-4 + np .random .rand (1 )* 3e-3
353
356
ftemp = np .random .rand (1 )* 0.5
354
357
Dstemp = 5e-3 + np .random .rand (1 )* 1e-1
355
- #S0 = np.zeros(len(bvalue))
356
358
S0 = ivim (bvalue ,Dtemp ,ftemp ,Dstemp )
357
359
if T1 > 0 or T2 > 0 :
358
- MR = MR + np .tile (np .expand_dims (XCAT == iTissue ,3 ),len (S0 )) * S0 * (1 - 2 * np .exp (- (TR - TE / 2 ) / T1 ) + np .exp (- TR / T1 )) * np .exp (
359
- - TE / T2 )
360
+ MR = MR + np .tile (np .expand_dims (XCAT == iTissue ,3 ),len (S0 )) * S0 * (1 - 2 * np .exp (- (TR - TE / 2 ) / T1 ) + np .exp (- TR / T1 )) * np .exp (- TE / T2 )
360
361
Dim = Dim + (XCAT == iTissue ) * Dtemp
361
362
fim = fim + (XCAT == iTissue ) * ftemp
362
363
Dpim = Dpim + (XCAT == iTissue ) * Dstemp
363
- return MR , Dim , fim , Dpim
364
+ return MR , Dim , fim , Dpim , legend
364
365
365
366
if __name__ == '__main__' :
366
367
bvalue = np .array ([0. , 1 , 2 , 5 , 10 , 20 , 30 , 50 , 75 , 100 , 150 , 250 , 350 , 400 , 550 , 700 , 850 , 1000 ])
367
- noise = 0.005
368
+ noise = 0.0005
368
369
motion = False
369
- sig , XCAT , Dim ,fim ,Dpim = phantom (bvalue , noise ,motion = motion ,interleaved = False )
370
- sig = sig * 50000
371
- sig = np .flip (sig ,axis = 0 )
372
- sig = np .flip (sig ,axis = 1 )
370
+ sig , XCAT , Dim , fim , Dpim , legend = phantom (bvalue , noise , motion = motion , interleaved = False )
371
+ # sig = np.flip(sig,axis=0)
372
+ # sig = np.flip(sig,axis=1)
373
373
res = np .eye (4 )
374
374
res [2 ]= 2
375
+
376
+ voxel_selector_fraction = 0.5
377
+ D , f , Ds = contrast_curve_calc ()
378
+ ignore = np .isnan (D )
379
+ generic_data = {}
380
+ for level , name in legend .items ():
381
+ if len (ignore ) > level and ignore [level ]:
382
+ continue
383
+ selector = XCAT == level
384
+ voxels = sig [selector ]
385
+ if len (voxels ) < 1 :
386
+ continue
387
+ signals = np .squeeze (voxels [int (voxels .shape [0 ] * voxel_selector_fraction )]).tolist ()
388
+ generic_data [name ] = {
389
+ 'noise' : noise ,
390
+ 'D' : np .mean (Dim [selector ], axis = 0 ),
391
+ 'f' : np .mean (fim [selector ], axis = 0 ),
392
+ 'Dp' : np .mean (Dpim [selector ], axis = 0 ),
393
+ 'data' : signals
394
+ }
395
+ generic_data ['config' ] = {
396
+ 'bvalues' : bvalue .tolist ()
397
+ }
398
+ with open ('generic.json' , 'w' ) as f :
399
+ json .dump (generic_data , f , indent = 4 )
400
+
401
+
375
402
nifti_img = nib .Nifti1Image (sig , affine = res ) # Replace affine if necessary
376
403
# Save the NIfTI image to a file
404
+ nifti_img .header .set_data_dtype (np .float64 )
377
405
if motion :
378
406
output_file = 'output_resp_int.nii.gz' # Replace with your desired output file name
379
407
else :
0 commit comments