CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iImageConvolverStationaryGaussian.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
22 */
23 
30 #include "vImageConvolver.hh"
32 #include "oImageSpace.hh"
34 
35 // =====================================================================
36 // ---------------------------------------------------------------------
37 // ---------------------------------------------------------------------
38 // =====================================================================
39 
41 {
42  m_nbSigmas = -1.;
43  m_transFWHM = -1.;
44  m_axialFWHM = -1.;
45  m_dimKernelXY = -1;
46  m_dimKernelXYZ = -1;
47 }
48 
49 // =====================================================================
50 // ---------------------------------------------------------------------
51 // ---------------------------------------------------------------------
52 // =====================================================================
53 
55 {
56 }
57 
58 // =====================================================================
59 // ---------------------------------------------------------------------
60 // ---------------------------------------------------------------------
61 // =====================================================================
62 
64 {
65  cout << "This convolver is based on a classic stationary Gaussian kernel." << endl;
66  cout << "It can be anisotropic so that the transaxial and axial FWHM can be different." << endl;
67  cout << "One also have to choose the number of sigmas of the Gaussian distribution that will be included in the kernel." << endl;
68  cout << "The following options can be used (in this particular order when provided as a list, separated by commas):" << endl;
69  cout << " trans FWHM: to set the transaxial FWHM (in mm)" << endl;
70  cout << " axial FWHM: to set the axial FWHM (in mm)" << endl;
71  cout << " number of sigmas: to set the number of sigmas included in the kernel (recommendations: at least 3. and maximum 5.)" << endl;
72 }
73 
74 // =====================================================================
75 // ---------------------------------------------------------------------
76 // ---------------------------------------------------------------------
77 // =====================================================================
78 
79 int iImageConvolverStationaryGaussian::ReadConfigurationFile(const string& a_configurationFile)
80 {
81  string key_word = "";
82  // Read the transaxial FWHM option
83  key_word = "trans FWHM";
84  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_transFWHM, 1, KEYWORD_MANDATORY))
85  {
86  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
87  return 1;
88  }
89  // Read the axial FWHM option
90  key_word = "axial FWHM";
91  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_axialFWHM, 1, KEYWORD_MANDATORY))
92  {
93  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
94  return 1;
95  }
96  // Read the number of sigma option
97  key_word = "number of sigmas";
98  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_nbSigmas, 1, KEYWORD_MANDATORY))
99  {
100  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
101  return 1;
102  }
103  // Normal end
104  return 0;
105 }
106 
107 // =====================================================================
108 // ---------------------------------------------------------------------
109 // ---------------------------------------------------------------------
110 // =====================================================================
111 
113 {
114  // There are 3 floating point variables as options
115  FLTNB options[3];
116  // Read them
117  if (ReadStringOption(a_optionsList, options, 3, ",", "Stationary Gaussian convolver configuration"))
118  {
119  Cerr("***** iImageConvolverStationaryGaussian::ReadConfigurationFile() -> Failed to correctly read the list of options !" << endl);
120  return 1;
121  }
122  // Affect options
123  m_transFWHM = options[0];
124  m_axialFWHM = options[1];
125  m_nbSigmas = options[2];
126  // Normal end
127  return 0;
128 }
129 
130 // =====================================================================
131 // ---------------------------------------------------------------------
132 // ---------------------------------------------------------------------
133 // =====================================================================
134 
136 {
137  // Check number of sigmas
138  if (m_nbSigmas<=0.)
139  {
140  Cerr("***** iImageConvolverStationaryGaussian::CheckSpecificParameters() -> Number of sigmas included in the kernel must be strictly positive !" << endl);
141  return 1;
142  }
143  // Check the transaxial FWHM
144  if (m_transFWHM<0.)
145  {
146  Cerr("***** iImageConvolverStationaryGaussian::CheckSpecificParameters() -> Transaxial FWHM is negative !" << endl);
147  return 1;
148  }
149  // Check the axial FWHM
150  if (m_axialFWHM<0.)
151  {
152  Cerr("***** iImageConvolverStationaryGaussian::CheckSpecificParameters() -> Axial FWHM is negative !" << endl);
153  return 1;
154  }
155  // Normal end
156  return 0;
157 }
158 
159 // =====================================================================
160 // ---------------------------------------------------------------------
161 // ---------------------------------------------------------------------
162 // =====================================================================
163 
165 {
166  // Verbose
167  if (m_verbose>=2)
168  {
169  Cout("iImageConvolverStationaryGaussian::BuildConvolutionKernel() -> Compute convolution kernel" << endl);
170  Cout(" --> Transaxial FWHM: " << m_transFWHM << " mm" << endl);
171  Cout(" --> Axial FWHM: " << m_axialFWHM << " mm" << endl);
172  Cout(" --> Included sigmas: " << m_nbSigmas << endl);
173  }
174 
175  // --------------------------------
176  // Stationary kernel
177  // --------------------------------
178 
179  // Set the number of kernels to 1
180  m_nbKernels = 1;
181  // Specify that it is stationary (we do not overload the Convolve() function here)
182  m_stationary = true;
183  // Compute kernel half size based on the number of sigmas included in the kernel
184  FLTNB two_sqrt_two_ln_two = 2.*sqrt(2.*log(2.));
185  FLTNB half_kern_floatX = m_transFWHM * m_nbSigmas / (two_sqrt_two_ln_two*mp_ImageDimensionsAndQuantification->GetVoxSizeX());
186  FLTNB half_kern_floatY = m_transFWHM * m_nbSigmas / (two_sqrt_two_ln_two*mp_ImageDimensionsAndQuantification->GetVoxSizeY());
187  FLTNB half_kern_floatZ = m_axialFWHM * m_nbSigmas / (two_sqrt_two_ln_two*mp_ImageDimensionsAndQuantification->GetVoxSizeZ());
188  // 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,
189  // otherwise, we had a voxel to the half size
190  INTNB half_kern_dimX = ((INTNB)(half_kern_floatX));
191  if (half_kern_floatX-((FLTNB)half_kern_dimX)>0.5) half_kern_dimX++;
192  INTNB half_kern_dimY = ((INTNB)(half_kern_floatY));
193  if (half_kern_floatY-((FLTNB)half_kern_dimY)>0.5) half_kern_dimY++;
194  INTNB half_kern_dimZ = ((INTNB)(half_kern_floatZ));
195  if (half_kern_floatZ-((FLTNB)half_kern_dimZ)>0.5) half_kern_dimZ++;
196  // Allocate kernel dimensions
197  mp_dimKernelX = (INTNB*)malloc(1*sizeof(INTNB));
198  mp_dimKernelY = (INTNB*)malloc(1*sizeof(INTNB));
199  mp_dimKernelZ = (INTNB*)malloc(1*sizeof(INTNB));
200  // Compute kernel dimensions
201  mp_dimKernelX[0] = half_kern_dimX * 2 + 1;
202  mp_dimKernelY[0] = half_kern_dimY * 2 + 1;
203  mp_dimKernelZ[0] = half_kern_dimZ * 2 + 1;
206  // Verbose
207  if (m_verbose>=2) Cout (" --> Kernel dimensions: [" << mp_dimKernelX[0] << ";" << mp_dimKernelY[0] << ";" << mp_dimKernelZ[0] << "]" << endl);
208  // Allocate kernel
209  m2p_kernel = (FLTNB**)malloc(1*sizeof(FLTNB*));
210  m2p_kernel[0] = (FLTNB*)malloc(m_dimKernelXYZ*sizeof(FLTNB));
211 
212  // --------------------------------
213  // Compute the kernel
214  // --------------------------------
215 
216  // Variables
217  FLTNB sigma_x = m_transFWHM / two_sqrt_two_ln_two;
218  FLTNB sigma_y = m_transFWHM / two_sqrt_two_ln_two;
219  FLTNB sigma_z = m_axialFWHM / two_sqrt_two_ln_two;
220  FLTNB mu_x = (FLTNB)(half_kern_dimX);
221  FLTNB mu_y = (FLTNB)(half_kern_dimY);
222  FLTNB mu_z = (FLTNB)(half_kern_dimZ);
223  // Compute kernel by integration of the Gaussian distribution (this method is not exactly exact... but is closer to it compared to single Gaussian points)
224  FLTNB sum_kernel = 0.;
225  for (int z=0, index=0; z<mp_dimKernelZ[0]; z++)
226  {
227  FLTNB kern_Z = 0.5 * ( erf( ( (((FLTNB)z)-mu_z+0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeZ()) / (sqrt(2.)*sigma_z) )
228  - erf( ( (((FLTNB)z)-mu_z-0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeZ()) / (sqrt(2.)*sigma_z) ) );
229  for (int y=0; y<mp_dimKernelY[0]; y++)
230  {
231  FLTNB kern_ZY = 0.5 * ( erf( ( (((FLTNB)y)-mu_y+0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeY()) / (sqrt(2.)*sigma_y) )
232  - erf( ( (((FLTNB)y)-mu_y-0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeY()) / (sqrt(2.)*sigma_y) ) )
233  * kern_Z;
234  for (int x=0; x<mp_dimKernelX[0]; x++, index++)
235  {
236  m2p_kernel[0][index] = 0.5 * ( erf( ( (((FLTNB)x)-mu_x+0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeX()) / (sqrt(2.)*sigma_x) )
237  - erf( ( (((FLTNB)x)-mu_x-0.5)*mp_ImageDimensionsAndQuantification->GetVoxSizeX()) / (sqrt(2.)*sigma_x) ) )
238  * kern_ZY;
239  sum_kernel += m2p_kernel[0][index];
240  }
241  }
242  }
243  // Normalize kernel
244  for (int v=0; v<m_dimKernelXYZ; v++) m2p_kernel[0][v] /= sum_kernel;
245  // Normal end
246  return 0;
247 }
248 
249 // =====================================================================
250 // ---------------------------------------------------------------------
251 // ---------------------------------------------------------------------
252 // =====================================================================
Declaration of class oImageDimensionsAndQuantification.
#define FLTNB
Definition: gVariables.hh:81
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
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.
int ReadConfigurationFile(const string &a_fileOptions)
A function used to read options from a configuration file.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child module.
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
FLTNB GetVoxSizeY()
Get the voxel&#39;s size along the Y axis, in mm.
#define Cerr(MESSAGE)
Declaration of class 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...
Definition: gOptions.cc:129
iImageConvolverStationaryGaussian()
The constructor of iImageConvolverStationaryGaussian.
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
int ReadOptionsList(const string &a_listOptions)
A function used to read options from a list of options.
#define INTNB
Definition: gVariables.hh:92
Declaration of class oImageSpace.
~iImageConvolverStationaryGaussian()
The destructor of iImageConvolverStationaryGaussian.
#define Cout(MESSAGE)
Declaration of class vImageConvolver.
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.
Definition: gOptions.cc:50
void ShowHelp()
A function used to show help about the child module.
This abstract class is the generic image convolver class used by the oImageConvolverManager.