CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
oImageProcessingManager.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 
31 #include "sAddonManager.hh"
32 
33 // =====================================================================
34 // ---------------------------------------------------------------------
35 // ---------------------------------------------------------------------
36 // =====================================================================
37 
39 {
40  // Image dimensions
42  // Options
43  m_options = {};
44  // Image processing objects and associated bool
47  mp_applyForward = NULL;
48  mp_applyIntra = NULL;
49  mp_applyPost = NULL;
50  // Booleans
51  m_checked = false;
52  m_initialized = false;
53  // Verbosity
54  m_verbose = -1;
55 }
56 
57 // =====================================================================
58 // ---------------------------------------------------------------------
59 // ---------------------------------------------------------------------
60 // =====================================================================
61 
63 {
64  // Delete object
66  {
69  }
70 }
71 
72 // =====================================================================
73 // ---------------------------------------------------------------------
74 // ---------------------------------------------------------------------
75 // =====================================================================
76 
78 {
79  // Check image dimensions
81  {
82  Cerr("***** oImageProcessingManager::CheckParameters() -> No image dimensions provided !" << endl);
83  return 1;
84  }
85  // Check verbosity
86  if (m_verbose<0)
87  {
88  Cerr("***** oImageProcessingManager::CheckParameters() -> Wrong verbosity level provided !" << endl);
89  return 1;
90  }
91  // All set
92  m_checked = true;
93  // Normal end
94  return 0;
95 }
96 
97 // =====================================================================
98 // ---------------------------------------------------------------------
99 // ---------------------------------------------------------------------
100 // =====================================================================
101 
103 {
104  // Return when using MPI and mpi_rank is not 0
105  #ifdef CASTOR_MPI
106  int mpi_rank = 0;
107  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
108  if (mpi_rank!=0) return;
109  #endif
110  // Show help
111  cout << "------------------------------------------------------------------" << endl;
112  cout << "----- How to use an image processing module" << endl;
113  cout << "------------------------------------------------------------------" << endl;
114  cout << endl;
115  cout << "An image processing module is called through the -proc option. The provided argument describes the processing module to be used," << endl;
116  cout << "its options, and when to include it within the algorithm. The syntax of the argument must obey one of the three following options:" << endl;
117  cout << " proc::when (in this case, the default configuration file of the processing module is used to set the options values)" << endl;
118  cout << " proc:file.conf::when (in this case, the provided configuration is used)" << endl;
119  cout << " proc,param1,param2,...::when (in this case, the options values are directly provided in the argument)" << endl;
120  cout << "In any case, the description of the options specific to each processing module, their order in the list and their configuration" << endl;
121  cout << "files syntax are provided in the specific help of each module." << endl;
122  cout << "The 'when' parameter is an argument describing when to include the processing module within the algorithm. It is a list of keywords" << endl;
123  cout << "separating by commas. The following keywords can be used:" << endl;
124  cout << " forward (include module into forward model; the processed current estimate is forward-projected)" << endl;
125  cout << " post (apply module before saving the image; the processed image is not put back as the estimate for the next update)" << endl;
126  cout << " intra (apply module to the updated image use it as the current estimate for the next update)" << endl;
127  cout << endl;
128 }
129 
130 // =====================================================================
131 // ---------------------------------------------------------------------
132 // ---------------------------------------------------------------------
133 // =====================================================================
134 
136 {
137  // Check if parameters have been checked
138  if (!m_checked)
139  {
140  Cerr("***** oImageProcessingManager::Initialize() -> Parameters have not been checked ! Please call CheckParameters() before." << endl);
141  return 1;
142  }
143  // Case with no options (no image processing module)
144  if (m_options.size()==0)
145  {
146  m_initialized = true;
147  return 0;
148  }
149  // Verbose
150  if (m_verbose>=1) Cout("oImageProcessingManager::Initialize() -> Initialize image processing modules" << endl);
151  // Parse image processing modules options and initialize them
153  {
154  Cerr("***** oImageProcessingManager::Initialize() -> A problem occurred while parsing image processing modules options and initializing them !" << endl);
155  return 1;
156  }
157  // All set
158  m_initialized = true;
159  // Normal end
160  return 0;
161 }
162 
163 // =====================================================================
164 // ---------------------------------------------------------------------
165 // ---------------------------------------------------------------------
166 // =====================================================================
167 
169 {
170  // ===================================================================
171  // First get the number of processing modules from the list of options
172  // ===================================================================
173 
175 
176  // Allocate the tables
178  mp_applyForward = (bool*)malloc(m_nbImageProcessingModules*sizeof(bool));
179  mp_applyIntra = (bool*)malloc(m_nbImageProcessingModules*sizeof(bool));
180  mp_applyPost = (bool*)malloc(m_nbImageProcessingModules*sizeof(bool));
181 
182  // ===================================================================
183  // Then we loop over all modules, read options and initialize them
184  // ===================================================================
185 
186  // This is for the automatic initialization of the processing modules
187  typedef vImageProcessingModule *(*maker_image_processing_module) ();
188  // Get image processing modules list from addon manager
189  std::map <string,maker_image_processing_module> list = sAddonManager::GetInstance()->mp_listOfImageProcessingModules;
190 
191  // Start the loop
192  for (int c=0; c<m_nbImageProcessingModules; c++)
193  {
194  // Default initializations
195  m2p_ImageProcessingModules[c] = NULL;
196  mp_applyForward[c] = false;
197  mp_applyIntra[c] = false;
198  mp_applyPost[c] = false;
199 
200  // ___________________________________________________________________________________
201  // Search for a double-colon and isolate the module's options from the 'when' actions
202 
203  size_t double_colon = m_options[c].find("::");
204 
205  // Send an error if no double-colon
206  if (double_colon==string::npos)
207  {
208  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> Wrong syntax in the " << c+1 << "th image processing module !" << endl);
209  Cerr(" No double-colon \"::\" found." << endl);
210  ShowCommonHelp();
211  return 1;
212  }
213 
214  // Separate the two arguments
215  string proc_part_options = m_options[c].substr(0,double_colon);
216  string when_part_options = m_options[c].substr(double_colon+2);
217 
218  // ___________________________________________________________________________________
219  // Get the module name in the options and isolate the actual module's options
220 
221  // Useful strings
222  string module = "";
223  string list_options = "";
224  string file_options = "";
225 
226  // Search for a colon ":", this indicates that a configuration file is provided after the module's name
227  size_t colon = proc_part_options.find_first_of(":");
228  size_t comma = proc_part_options.find_first_of(",");
229 
230  // Case 1: we have a colon
231  if (colon!=string::npos)
232  {
233  // Get the image processing module name before the colon
234  module = proc_part_options.substr(0,colon);
235  // Get the configuration file after the colon
236  file_options = proc_part_options.substr(colon+1);
237  // List of options is empty
238  list_options = "";
239  }
240  // Case 2: we have a comma
241  else if (comma!=string::npos)
242  {
243  // Get the image processing module name before the first comma
244  module = proc_part_options.substr(0,comma);
245  // Get the list of options after the first comma
246  list_options = proc_part_options.substr(comma+1);
247  // Configuration file is empty
248  file_options = "";
249  }
250  // Case 3: no colon and no comma (a single image processing module name)
251  else
252  {
253  // Get the image processing module name
254  module = proc_part_options;
255  // List of options is empty
256  list_options = "";
257  // Build the default configuration file
258  file_options = sOutputManager::GetInstance()->GetPathToConfigDir() + "/processing/" + module + ".conf";
259  }
260 
261  // ___________________________________________________________________________________
262  // Read the 'when' actions
263 
264  // Loop while commas are found
265  while ((comma=when_part_options.find_first_of(",")) != string::npos)
266  {
267  // Extract the first option
268  string option = when_part_options.substr(0,comma);
269  // Extract the rest
270  when_part_options = when_part_options.substr(comma+1);
271  // Check the meaning of the option
272  if (option=="forward") {mp_applyForward[c] = true;}
273  else if (option=="post") {mp_applyPost[c] = true;}
274  else if (option=="intra") {mp_applyIntra[c] = true;}
275  else
276  {
277  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> Unknown keyword '" << option << "' provided in options list !" << endl);
278  ShowCommonHelp();
279  return 1;
280  }
281  }
282  // Last option
283  if (when_part_options=="forward") {mp_applyForward[c] = true;}
284  else if (when_part_options=="post") {mp_applyPost[c] = true;}
285  else if (when_part_options=="intra") {mp_applyIntra[c] = true;}
286  else
287  {
288  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> Unknown keyword '" << when_part_options << "' provided in options list !" << endl);
289  ShowCommonHelp();
290  return 1;
291  }
292 
293  // ______________________________________________________________________________
294  // Create processing module and call associated functions
295 
296  // Create the image processing module
297  if (list[module]) m2p_ImageProcessingModules[c] = list[module]();
298  else
299  {
300  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> Image processing module '" << module << "' does not exist !" << endl);
302  return 1;
303  }
304  // Set parameters
307  // Provide configuration file if any
308  if (file_options!="" && m2p_ImageProcessingModules[c]->ReadConfigurationFile(file_options))
309  {
310  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> A problem occurred while reading and checking configuration file for image processing module '" << module << "' !" << endl);
311  return 1;
312  }
313  // Provide options if any
314  if (list_options!="" && m2p_ImageProcessingModules[c]->ReadOptionsList(list_options))
315  {
316  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> A problem occurred while parsing and reading options list for image processing module '" << module << "' !" << endl);
317  return 1;
318  }
319  // Check parameters
321  {
322  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> A problem occurred while checking parameters for image processing module '" << module << "' !" << endl);
323  return 1;
324  }
325  // Initialize the image processing module
327  {
328  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> A problem occurred while initializing image processing module '" << module << "' !" << endl);
329  return 1;
330  }
331  // Check if processing module is dynamic and if dynamic basis functions are used, then it is not compatible if used inside the reconstruction (all but 'post')
332  bool intra_reconstruction = mp_applyForward[c] || mp_applyIntra[c];
336  if (intra_reconstruction && (condition1 || condition2 || condition3))
337  {
338  Cerr("***** oImageProcessingManager::ParseOptionsAndInitializeImageProcessingModules() -> Cannot use dynamic image processing module '" << module << "' along with dynamic basis functions inside the reconstruction !" << endl);
339  return 1;
340  }
341  }
342 
343  // Normal end
344  return 0;
345 }
346 
347 // =====================================================================
348 // ---------------------------------------------------------------------
349 // ---------------------------------------------------------------------
350 // =====================================================================
351 
353 {
354  #ifdef CASTOR_DEBUG
355  // Check if initialized
356  if (!m_initialized)
357  {
358  Cerr("***** oImageProcessingManager::ApplyProcessingForward() -> Called while not initialized !" << endl);
359  return 1;
360  }
361  #endif
362  // Loop on processing modules
363  for (int c=0; c<m_nbImageProcessingModules; c++)
364  {
365  // Apply it only if asked for
366  if (mp_applyForward[c])
367  {
368  // Verbose
369  if (m_verbose>=2) Cout("oImageProcessingrManager::ApplyProcessingForward() -> Apply image processing module " << c+1 << " to forward image" << endl);
370  // Get the pointer to the image
371  FLTNB**** image = ap_ImageSpace->m4p_forwardImage;
372  // Apply convolution
374  }
375  }
376  // Normal end
377  return 0;
378 }
379 
380 // =====================================================================
381 // ---------------------------------------------------------------------
382 // ---------------------------------------------------------------------
383 // =====================================================================
384 
386 {
387  #ifdef CASTOR_DEBUG
388  // Check if initialized
389  if (!m_initialized)
390  {
391  Cerr("***** oImageProcessingManager::ApplyProcessingIntra() -> Called while not initialized !" << endl);
392  return 1;
393  }
394  #endif
395  // Loop on processing modules
396  for (int c=0; c<m_nbImageProcessingModules; c++)
397  {
398  // Apply it only if asked for
399  if (mp_applyIntra[c])
400  {
401  // Verbose
402  if (m_verbose>=2) Cout("oImageProcessingrManager::ApplyProcessingIntra() -> Apply image processing module " << c+1 << " to current image" << endl);
403  // Get the pointer to the image
404  FLTNB**** image = ap_ImageSpace->m4p_image;
405  // Apply processing module
407  }
408  }
409  // Normal end
410  return 0;
411 }
412 
413 // =====================================================================
414 // ---------------------------------------------------------------------
415 // ---------------------------------------------------------------------
416 // =====================================================================
417 
419 {
420  #ifdef CASTOR_DEBUG
421  // Check if initialized
422  if (!m_initialized)
423  {
424  Cerr("***** oImageProcessingManager::ApplyProcessingPost() -> Called while not initialized !" << endl);
425  return 1;
426  }
427  #endif
428  // Loop on processing modules
429  for (int c=0; c<m_nbImageProcessingModules; c++)
430  {
431  // Apply it only if asked for
432  if (mp_applyPost[c])
433  {
434  // Verbose
435  if (m_verbose>=2) Cout("oImageProcessingManager::ApplyProcessingPost() -> Apply image processing module " << c+1 << " to output image" << endl);
436  // At this step, the output image is still as basis functions and copied into the forward image.
437  // Get the pointer to the output image
438 // FLTNB**** image = ap_ImageSpace->m4p_outputImage;
439  FLTNB**** image = ap_ImageSpace->m4p_forwardImage;
440  // Apply processing module
442  }
443  }
444  // Normal end
445  return 0;
446 }
447 
448 // =====================================================================
449 // ---------------------------------------------------------------------
450 // ---------------------------------------------------------------------
451 // =====================================================================
int ApplyProcessingForward(oImageSpace *ap_ImageSpace)
A function used to apply image processing modules onto the forward image of the oImageSpace.
FLTNB **** m4p_forwardImage
Definition: oImageSpace.hh:87
#define FLTNB
Definition: gVariables.hh:81
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
Set the member mp_ImageDimensionsAndQuantification to the provided value.
static void ShowCommonHelp()
This function does not take any parameter and is used to display some help about the syntax of the op...
bool GetAffectCardDimensionFlag()
Return the boolean value of m_affectCardDimensionFlag member.
bool GetAffectTimeDimensionFlag()
Return the boolean value of m_affectTimeDimensionFlag member.
void ShowHelpImageProcessingModule()
Show help about all implemented image processing modules.
int Initialize()
A function used to initialize the manager and all image processing modules it manages.
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
~oImageProcessingManager()
The destructor of oImageProcessingManager.
int ApplyProcessingPost(oImageSpace *ap_ImageSpace)
A function used to apply convolvers onto the output image of the oImageSpace.
bool GetRespStaticFlag()
Get the respiratory static flag that says if the reconstruction has only one respiratory gate or not...
static sAddonManager * GetInstance()
#define Cerr(MESSAGE)
bool GetCardStaticFlag()
Get the cardiac static flag that says if the reconstruction has only one cardiac gate or not...
FLTNB **** m4p_image
Definition: oImageSpace.hh:80
const string & GetPathToConfigDir()
Return the path to the CASTOR config directory.
This abstract class is the generic image processing module class used by the oImageProcessingManager...
int CheckParameters()
A function used to check the parameters settings.
oImageProcessingManager()
The constructor of oImageProcessingManager.
bool GetTimeStaticFlag()
Get the time static flag that says if the reconstruction has only one frame or not.
bool GetAffectRespDimensionFlag()
Return the boolean value of m_affectRespDimensionFlag member.
int ApplyProcessingIntra(oImageSpace *ap_ImageSpace)
A function used to apply image processing modules onto the current image of the oImageSpace.
std::map< string, maker_image_processing_module > mp_listOfImageProcessingModules
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Declaration of class oImageProcessingManager.
This class holds all the matrices in the image domain that can be used in the algorithm: image...
Definition: oImageSpace.hh:60
void SetVerbose(int a_verbose)
Set the member m_verboseLevel to the provided value.
vImageProcessingModule ** m2p_ImageProcessingModules
#define Cout(MESSAGE)
virtual int Process(FLTNB ****a4p_image)=0
A function used to actually perform the processing.
Declaration of class sAddonManager.
int ParseOptionsAndInitializeImageProcessingModules()
A function used to parse options and initialize image processing modules.