1+ import numpy as np
2+
3+ class GenerateData :
4+ """
5+ Generate exponential and linear data
6+ """
7+ def __init__ (self , operator = None ):
8+ """
9+ Parameters
10+ ----------
11+ operator : numpy or torch
12+ Must provide mathematical operators
13+ """
14+ if operator is None :
15+ self ._op = np
16+ else :
17+ self ._op = operator
18+
19+ def ivim_signal (self , D , Dp , f , S0 , bvalues , snr = None ):
20+ """
21+ Generates IVIM (biexponential) signal
22+
23+ Parameters
24+ ----------
25+ D : float
26+ The tissue diffusion value
27+ Dp : float
28+ The pseudo perfusion value
29+ f : float
30+ The fraction of the signal from perfusion
31+ S0 : float
32+ The baseline signal (magnitude at no diffusion)
33+ bvalues : list or array of float
34+ The diffusion (b-values)
35+ """
36+ signal = self .multiexponential_signal ([D , Dp ], [1 - f , f ], S0 , self ._op .asarray (bvalues , dtype = 'float64' ))
37+ return self .add_rician_noise (signal , snr )
38+
39+ def exponential_signal (self , D , bvalues ):
40+ """
41+ Generates exponential signal
42+
43+ Parameters
44+ ----------
45+ D : float
46+ The tissue diffusion value
47+ bvalues : list or array of float
48+ The diffusion (b-values)
49+ """
50+ assert D >= 0 , 'D must be >= 0'
51+ return self ._op .exp (- self ._op .asarray (bvalues , dtype = 'float64' ) * D )
52+
53+ def multiexponential_signal (self , D , F , S0 , bvalues ):
54+ """
55+ Generates multiexponential signal
56+ The combination of exponential signals
57+
58+ Parameters
59+ ----------
60+ D : list or arrray of float
61+ The tissue diffusion value
62+ F : list or array of float
63+ The fraction of the signal from perfusion
64+ S0 : list or array of float
65+ The baseline signal (magnitude at no diffusion)
66+ bvalues : list or array of float
67+ The diffusion (b-values)
68+ """
69+ assert len (D ) == len (F ), 'D and F must be the same length'
70+ signal = self ._op .zeros_like (bvalues )
71+ for [d , f ] in zip (D , F ):
72+ signal += f * self .exponential_signal (d , bvalues )
73+ signal *= S0
74+ return signal
75+
76+ def add_rician_noise (self , real_signal , snr = None , imag_signal = None ):
77+ """
78+ Adds Rician noise to a real or complex signal
79+
80+ Parameters
81+ ----------
82+ real_signal : list or array of float
83+ The real channel float
84+ snr : float
85+ The signal to noise ratio
86+ imag_signal : list or array of float
87+ The imaginary channel float
88+ """
89+ if imag_signal is None :
90+ imag_signal = self ._op .zeros_like (real_signal )
91+ if snr is None :
92+ noisy_data = self ._op .sqrt (self ._op .power (real_signal , 2 ) + self ._op .power (imag_signal , 2 ))
93+ else :
94+ real_noise = self ._op .random .normal (0 , 1 / snr , real_signal .shape )
95+ imag_noise = self ._op .random .normal (0 , 1 / snr , imag_signal .shape )
96+ noisy_data = self ._op .sqrt (self ._op .power (real_signal + real_noise , 2 ) + self ._op .power (imag_signal + imag_noise , 2 ))
97+ return noisy_data
98+
99+ def linear_signal (self , D , bvalues , offset = 0 ):
100+ """
101+ Generates linear signal
102+
103+ Parameters
104+ ----------
105+ D : float
106+ The tissue diffusion value
107+ bvalues : list or array of float
108+ The diffusion (b-values)
109+ offset : float
110+ The signal offset
111+ """
112+ assert D >= 0 , 'D must be >= 0'
113+ data = - D * np .asarray (bvalues )
114+ return data + offset
115+
116+ def multilinear_signal (self , D , F , S0 , bvalues , offset = 0 ):
117+ """
118+ Generates multilinear signal
119+ The combination of multiple linear signals
120+
121+ Parameters
122+ ----------
123+ D : list or arrray of float
124+ The tissue diffusion value
125+ F : list or array of float
126+ The fraction of the signal from perfusion
127+ S0 : list or array of float
128+ The baseline signal (magnitude at no diffusion)
129+ bvalues : list or array of float
130+ The diffusion (b-values)
131+ offset : float
132+ The signal offset
133+ """
134+ assert len (D ) == len (F ), 'D and F must be the same length'
135+ signal = self ._op .zeros_like (bvalues )
136+ for [d , f ] in zip (D , F ):
137+ signal += f * self .linear_signal (d , bvalues )
138+ signal *= S0
139+ signal += offset
140+ return signal
0 commit comments