CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/image/iImageConvolverStationaryGaussian.cc
Go to the documentation of this file.
1 
8 #include "vImageConvolver.hh"
9 #include "oImageDimensionsAndQuantification.hh"
10 #include "oImageSpace.hh"
11 #include "iImageConvolverStationaryGaussian.hh"
12 
13 // =====================================================================
14 // ---------------------------------------------------------------------
15 // ---------------------------------------------------------------------
16 // =====================================================================
17 
19 {
20  m_nbSigmas = -1.;
21  m_transFWHM = -1.;
22  m_axialFWHM = -1.;
23  m_dimKernelXY = -1;
24  m_dimKernelXYZ = -1;
25 }
26 
27 // =====================================================================
28 // ---------------------------------------------------------------------
29 // ---------------------------------------------------------------------
30 // =====================================================================
31 
33 {
34 }
35 
36 // =====================================================================
37 // ---------------------------------------------------------------------
38 // ---------------------------------------------------------------------
39 // =====================================================================
40 
42 {
43  cout << "This convolver is based on a classic stationary Gaussian kernel." << endl;
44  cout << "It can be anisotropic so that the transaxial and axial FWHM can be different." << endl;
45  cout << "One also have to choose the number of sigmas of the Gaussian distribution that will be included in the kernel." << endl;
46  cout << "The following options can be used (in this particular order when provided as a list, separated by commas):" << endl;
47  cout << " trans FWHM: to set the transaxial FWHM (in mm)" << endl;
48  cout << " axial FWHM: to set the axial FWHM (in mm)" << endl;
49  cout << " number of sigmas: to set the number of sigmas included in the kernel (recommendations: at least 3. and maximum 5.)" << endl;
50 }
51 
52 // =====================================================================
53 // ---------------------------------------------------------------------
54 // ---------------------------------------------------------------------
55 // =====================================================================
56 
57 int iImageConvolverStationaryGaussian::ReadConfigurationFile(const string& a_configurationFile)
58 {
59  string key_word = "";
60  // Read the transaxial FWHM option
61  key_word = "trans FWHM";
62  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_transFWHM, 1, KEYWORD_MANDATORY))
63  {
64  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
65  return 1;
66  }
67  // Read the axial FWHM option
68  key_word = "axial FWHM";
69  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_axialFWHM, 1, KEYWORD_MANDATORY))
70  {
71  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
72  return 1;
73  }
74  // Read the number of sigma option
75  key_word = "number of sigmas";
76  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_nbSigmas, 1, KEYWORD_MANDATORY))
77  {
78  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
79  return 1;
80  }
81  // Normal end
82  return 0;
83 }
84 
85 // =====================================================================
86 // ---------------------------------------------------------------------
87 // ---------------------------------------------------------------------
88 // =====================================================================
89 
90 int iImageConvolverStationaryGaussian::ReadOptionsList(const string& a_optionsList)
91 {
92  // There are 3 floating point variables as options
93  FLTNB options[3];
94  // Read them
95  if (ReadStringOption(a_optionsList, options, 3, ",", "Stationary Gaussian convolver configuration"))
96  {
97  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to correctly read the list of options !" << endl);
98  return 1;
99  }
100  // Affect options
101  m_transFWHM = options[0];
102  m_axialFWHM = options[1];
103  m_nbSigmas = options[2];
104  // Normal end
105  return 0;
106 }
107 
108 // =====================================================================
109 // ---------------------------------------------------------------------
110 // ---------------------------------------------------------------------
111 // =====================================================================
112 
114 {
115  // Check number of sigmas
116  if (m_nbSigmas<=0.)
117  {
118  Cerr("***** iImageConvolverStationaryGaussian::CheckSpecificParameters() -> Number of sigmas included in the kernel must be strictly positive !" << endl);
119  return 1;
120  }
121  // Check the transaxial FWHM
122  if (m_transFWHM<0.)
123  {
124  Cerr("***** iImageConvolverStationaryGaussian::CheckSpecificParameters() -> Transaxial FWHM is negative !" << endl);
125  return 1;
126  }
127  // Check the axial FWHM
128  if (m_axialFWHM<0.)
129  {
130  Cerr("***** iImageConvolverStationaryGaussian::CheckSpecificParameters() -> Axial FWHM is negative !" << endl);
131  return 1;
132  }
133  // Normal end
134  return 0;
135 }
136 
137 // =====================================================================
138 // ---------------------------------------------------------------------
139 // ---------------------------------------------------------------------
140 // =====================================================================
141 
143 {
144  // Verbose
145  if (m_verbose>=2)
146  {
147  Cout("iImageConvolverStationaryGaussian::BuildConvolutionKernel() -> Compute convolution kernel" << endl);
148  Cout(" --> Transaxial FWHM: " << m_transFWHM << " mm" << endl);
149  Cout(" --> Axial FWHM: " << m_axialFWHM << " mm" << endl);
150  Cout(" --> Included sigmas: " << m_nbSigmas << endl);
151  }
152 
153  // --------------------------------
154  // Stationary kernel
155  // --------------------------------
156 
157  // Set the number of kernels to 1
158  m_nbKernels = 1;
159  // Specify that it is stationary (we do not overload the Convolve() function here)
160  m_stationary = true;
161  // Compute kernel half size based on the number of sigmas included in the kernel
162  FLTNB two_sqrt_two_ln_two = 2.*sqrt(2.*log(2.));
163  FLTNB half_kern_floatX = m_transFWHM * m_nbSigmas / (two_sqrt_two_ln_two*mp_ImageDimensionsAndQuantification->GetVoxSizeX());
164  FLTNB half_kern_floatY = m_transFWHM * m_nbSigmas / (two_sqrt_two_ln_two*mp_ImageDimensionsAndQuantification->GetVoxSizeY());
165  FLTNB half_kern_floatZ = m_axialFWHM * m_nbSigmas / (two_sqrt_two_ln_two*mp_ImageDimensionsAndQuantification->GetVoxSizeZ());
166  // For each half size, if the decimal part is below 0.5, then it will be taken into account into the central kernel's voxel,
167  // otherwise, we had a voxel to the half size
168  INTNB half_kern_dimX = ((INTNB)(half_kern_floatX));
169  if (half_kern_floatX-((FLTNB)half_kern_dimX)>0.5) half_kern_dimX++;
170  INTNB half_kern_dimY = ((INTNB)(half_kern_floatY));
171  if (half_kern_floatY-((FLTNB)half_kern_dimY)>0.5) half_kern_dimY++;
172  INTNB half_kern_dimZ = ((INTNB)(half_kern_floatZ));
173  if (half_kern_floatZ-((FLTNB)half_kern_dimZ)>0.5) half_kern_dimZ++;
174  // Allocate kernel dimensions
175  mp_dimKernelX = (INTNB*)malloc(1*sizeof(INTNB));
176  mp_dimKernelY = (INTNB*)malloc(1*sizeof(INTNB));
177  mp_dimKernelZ = (INTNB*)malloc(1*sizeof(INTNB));
178  // Compute kernel dimensions
179  mp_dimKernelX[0] = half_kern_dimX * 2 + 1;
180  mp_dimKernelY[0] = half_kern_dimY * 2 + 1;
181  mp_dimKernelZ[0] = half_kern_dimZ * 2 + 1;
184  // Verbose
185  if (m_verbose>=2) Cout (" --> Kernel dimensions: [" << mp_dimKernelX[0] << ";" << mp_dimKernelY[0] << ";" << mp_dimKernelZ[0] << "]" << endl);
186  // Allocate kernel
187  m2p_kernel = (FLTNB**)malloc(1*sizeof(FLTNB*));
188  m2p_kernel[0] = (FLTNB*)malloc(m_dimKernelXYZ*sizeof(FLTNB));
189 
190  // --------------------------------
191  // Compute the kernel
192  // --------------------------------
193 
194  // Variables
195  FLTNB sigma_x = m_transFWHM / two_sqrt_two_ln_two;
196  FLTNB sigma_y = m_transFWHM / two_sqrt_two_ln_two;
197  FLTNB sigma_z = m_axialFWHM / two_sqrt_two_ln_two;
198  FLTNB mu_x = (FLTNB)(half_kern_dimX);
199  FLTNB mu_y = (FLTNB)(half_kern_dimY);
200  FLTNB mu_z = (FLTNB)(half_kern_dimZ);
201  // Compute kernel by integration of the Gaussian distribution (this method is not exactly exact... but much better than single Gaussian points usually used...)
202  FLTNB sum_kernel = 0.;
203  for (int z=0, index=0; z<mp_dimKernelZ[0]; z++)
204  {
205  FLTNB kern_Z = 0.5 * ( erf( ( (((FLTNB)z)-mu_z+0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeZ()) / (sqrt(2.)*sigma_z) )
206  - erf( ( (((FLTNB)z)-mu_z-0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeZ()) / (sqrt(2.)*sigma_z) ) );
207  for (int y=0; y<mp_dimKernelY[0]; y++)
208  {
209  FLTNB kern_ZY = 0.5 * ( erf( ( (((FLTNB)y)-mu_y+0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeY()) / (sqrt(2.)*sigma_y) )
210  - erf( ( (((FLTNB)y)-mu_y-0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeY()) / (sqrt(2.)*sigma_y) ) )
211  * kern_Z;
212  for (int x=0; x<mp_dimKernelX[0]; x++, index++)
213  {
214  m2p_kernel[0][index] = 0.5 * ( erf( ( (((FLTNB)x)-mu_x+0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeX()) / (sqrt(2.)*sigma_x) )
215  - erf( ( (((FLTNB)x)-mu_x-0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeX()) / (sqrt(2.)*sigma_x) ) )
216  * kern_ZY;
217  sum_kernel += m2p_kernel[0][index];
218  }
219  }
220  }
221  // Normalize kernel
222  for (int v=0; v<m_dimKernelXYZ; v++) m2p_kernel[0][v] /= sum_kernel;
223  // Normal end
224  return 0;
225 }
226 
227 // =====================================================================
228 // ---------------------------------------------------------------------
229 // ---------------------------------------------------------------------
230 // =====================================================================
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
#define Cerr(MESSAGE)
int BuildConvolutionKernel()
A private function used to build the convolution kernel specific to the child convolver.
FLTNB GetVoxSizeZ()
Get the voxel&#39;s size along the Z axis, in mm.
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child module.
FLTNB GetVoxSizeY()
Get the voxel&#39;s size along the Y axis, in mm.
iImageConvolverStationaryGaussian()
The constructor of iImageConvolverStationaryGaussian.
#define KEYWORD_MANDATORY
~iImageConvolverStationaryGaussian()
The destructor of iImageConvolverStationaryGaussian.
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
void ShowHelp()
A function used to show help about the child module.
#define Cout(MESSAGE)
This abstract class is the generic image convolver class used by the oImageConvolverManager.