CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/image/oImageConvolverManager.cc
Go to the documentation of this file.
1 
8 #include "oImageConvolverManager.hh"
9 #include "sAddonManager.hh"
10 
11 // =====================================================================
12 // ---------------------------------------------------------------------
13 // ---------------------------------------------------------------------
14 // =====================================================================
15 
17 {
18  // Image dimensions
20  // Options
21  m_options = {};
22  // Image convolver objects and associated bool
24  m2p_ImageConvolvers = NULL;
25  mp_applyForward = NULL;
26  mp_applyBackward = NULL;
27  mp_applyIntra = NULL;
28  mp_applyPost = NULL;
29  // Booleans
30  m_checked = false;
31  m_initialized = false;
32  // Verbosity
33  m_verbose = -1;
34 }
35 
36 // =====================================================================
37 // ---------------------------------------------------------------------
38 // ---------------------------------------------------------------------
39 // =====================================================================
40 
42 {
43  // Delete object
45  {
46  for (int c=0; c<m_nbImageConvolvers; c++) if (m2p_ImageConvolvers[c]) delete m2p_ImageConvolvers[c];
47  free(m2p_ImageConvolvers);
48  }
49 }
50 
51 // =====================================================================
52 // ---------------------------------------------------------------------
53 // ---------------------------------------------------------------------
54 // =====================================================================
55 
57 {
58  // Check image dimensions
60  {
61  Cerr("***** oImageConvolverManager::CheckParameters() -> No image dimensions provided !" << endl);
62  return 1;
63  }
64  // Check verbosity
65  if (m_verbose<0)
66  {
67  Cerr("***** oImageConvolverManager::CheckParameters() -> Wrong verbosity level provided !" << endl);
68  return 1;
69  }
70  // All set
71  m_checked = true;
72  // Normal end
73  return 0;
74 }
75 
76 // =====================================================================
77 // ---------------------------------------------------------------------
78 // ---------------------------------------------------------------------
79 // =====================================================================
80 
82 {
83  // Return when using MPI and mpi_rank is not 0
84  #ifdef CASTOR_MPI
85  int mpi_rank = 0;
86  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
87  if (mpi_rank!=0) return;
88  #endif
89  // Show help
90  cout << "------------------------------------------------------------------" << endl;
91  cout << "----- How to use an image convolver" << endl;
92  cout << "------------------------------------------------------------------" << endl;
93  cout << endl;
94  cout << "An image convolver is called through the -conv option. The provided argument describes the convolver to be used, its options," << endl;
95  cout << "and when to include it within the algorithm. The syntax of the argument must obey one of the three following options:" << endl;
96  cout << " conv::when (in this case, the default configuration file of the convolver is used to set the options values)" << endl;
97  cout << " conv:file.conf::when (in this case, the provided configuration is used)" << endl;
98  cout << " conv,param1,param2,...::when (in this case, the options values are directly provided in the argument)" << endl;
99  cout << "In any case, the description of the options specific to each convolver, their order in the list and their configuration" << endl;
100  cout << "files syntax are provided in the specific help of each convolver." << endl;
101  cout << "The 'when' parameter is an argument describing when to include the convolver within the algorithm. It is a list of keywords" << endl;
102  cout << "separating by commas. The following keywords can be used:" << endl;
103  cout << " forward (include convolver into forward model; a convolution of the current estimate is forward-projected)" << endl;
104  cout << " backward (include convolver into backward model; a convolution of the correction terms is used for the update)" << endl;
105  cout << " post (apply convolver before saving the image; the convolved image is not put back as the estimate for the next update)" << endl;
106  cout << " psf (include both 'forward' and 'backward'; the standard image-based PSF modelling)" << endl;
107  cout << " sieve (include both 'psf' and 'post'; the standard method of sieve)" << endl;
108  cout << " intra (apply convolver to the updated image and use it as the current estimate for the next update)" << endl;
109  cout << endl;
110 }
111 
112 // =====================================================================
113 // ---------------------------------------------------------------------
114 // ---------------------------------------------------------------------
115 // =====================================================================
116 
118 {
119  // Check if parameters have been checked
120  if (!m_checked)
121  {
122  Cerr("***** oImageConvolverManager::Initialize() -> Parameters have not been checked ! Please call CheckParameters() before." << endl);
123  return 1;
124  }
125  // Case with no options (no convolver)
126  if (m_options.size()==0)
127  {
128  m_initialized = true;
129  return 0;
130  }
131  // Verbose
132  if (m_verbose>=1) Cout("oImageConvolverManager::Initialize() -> Initialize image convolvers" << endl);
133  // Parse image convolver options and initialize them
135  {
136  Cerr("***** oImageConvolverManager::Initialize() -> A problem occurred while parsing image convolvers options and initializing them !" << endl);
137  return 1;
138  }
139  // All set
140  m_initialized = true;
141  // Normal end
142  return 0;
143 }
144 
145 // =====================================================================
146 // ---------------------------------------------------------------------
147 // ---------------------------------------------------------------------
148 // =====================================================================
149 
151 {
152  // ==============================================================
153  // First get the number of convolvers from the list of options
154  // ==============================================================
155 
157 
158  // Allocate the tables
160  mp_applyForward = (bool*)malloc(m_nbImageConvolvers*sizeof(bool));
161  mp_applyBackward = (bool*)malloc(m_nbImageConvolvers*sizeof(bool));
162  mp_applyIntra = (bool*)malloc(m_nbImageConvolvers*sizeof(bool));
163  mp_applyPost = (bool*)malloc(m_nbImageConvolvers*sizeof(bool));
164 
165  // ==============================================================
166  // Then we loop over all convolvers, read options and initialize
167  // ==============================================================
168 
169  // This is for the automatic initialization of the convolvers
170  typedef vImageConvolver *(*maker_image_convolver) ();
171  // Get image convolver's list from addon manager
172  std::map <string,maker_image_convolver> list = sAddonManager::GetInstance()->mp_listOfImageConvolvers;
173 
174  // Start the loop
175  for (int c=0; c<m_nbImageConvolvers; c++)
176  {
177  // Default initializations
178  m2p_ImageConvolvers[c] = NULL;
179  mp_applyForward[c] = false;
180  mp_applyBackward[c] = false;
181  mp_applyIntra[c] = false;
182  mp_applyPost[c] = false;
183 
184  // ___________________________________________________________________________________
185  // Search for a double-colon and isolate the convolver's options from the 'when' actions
186 
187 // size_t double_colon = m_options[c].find_first_of("::");
188  size_t double_colon = m_options[c].find("::");
189 
190  // Send an error if no double-colon
191  if (double_colon==string::npos)
192  {
193  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> Wrong syntax in the " << c+1 << "th image convolver !" << endl);
194  Cerr(" No double-colon \"::\" found." << endl);
195  ShowCommonHelp();
196  return 1;
197  }
198 
199  // Separate the two arguments
200  string conv_part_options = m_options[c].substr(0,double_colon);
201  string when_part_options = m_options[c].substr(double_colon+2);
202 
203  // ___________________________________________________________________________________
204  // Get the image convolver name in the options and isolate the actual image convolver's options
205 
206  // Useful strings
207  string convolver = "";
208  string list_options = "";
209  string file_options = "";
210 
211  // Search for a colon ":", this indicates that a configuration file is provided after the image convolver name
212  size_t colon = conv_part_options.find_first_of(":");
213  size_t comma = conv_part_options.find_first_of(",");
214 
215  // Case 1: we have a colon
216  if (colon!=string::npos)
217  {
218  // Get the image convolver name before the colon
219  convolver = conv_part_options.substr(0,colon);
220  // Get the configuration file after the colon
221  file_options = conv_part_options.substr(colon+1);
222  // List of options is empty
223  list_options = "";
224  }
225  // Case 2: we have a comma
226  else if (comma!=string::npos)
227  {
228  // Get the image convolver name before the first comma
229  convolver = conv_part_options.substr(0,comma);
230  // Get the list of options after the first comma
231  list_options = conv_part_options.substr(comma+1);
232  // Configuration file is empty
233  file_options = "";
234  }
235  // Case 3: no colon and no comma (a single image convolver name)
236  else
237  {
238  // Get the image convolver name
239  convolver = conv_part_options;
240  // List of options is empty
241  list_options = "";
242  // Build the default configuration file
243  file_options = sOutputManager::GetInstance()->GetPathToConfigDir() + "/convolver/" + convolver + ".conf";
244  }
245 
246  // ___________________________________________________________________________________
247  // Read the 'when' actions
248 
249  // Loop while commas are found
250  while ((comma=when_part_options.find_first_of(",")) != string::npos)
251  {
252  // Extract the first option
253  string option = when_part_options.substr(0,comma);
254  // Extract the rest
255  when_part_options = when_part_options.substr(comma+1);
256  // Check the meaning of the option
257  if (option=="forward") {mp_applyForward[c] = true;}
258  else if (option=="backward") {mp_applyBackward[c] = true;}
259  else if (option=="post") {mp_applyPost[c] = true;}
260  else if (option=="psf") {mp_applyForward[c] = true; mp_applyBackward[c] = true;}
261  else if (option=="sieve") {mp_applyForward[c] = true; mp_applyBackward[c] = true; mp_applyPost[c] = true;}
262  else if (option=="intra") {mp_applyIntra[c] = true;}
263  else
264  {
265  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> Unknown keyword '" << option << "' provided in options list !" << endl);
266  ShowCommonHelp();
267  return 1;
268  }
269  }
270  // Last option
271  if (when_part_options=="forward") {mp_applyForward[c] = true;}
272  else if (when_part_options=="backward") {mp_applyBackward[c] = true;}
273  else if (when_part_options=="post") {mp_applyPost[c] = true;}
274  else if (when_part_options=="psf") {mp_applyForward[c] = true; mp_applyBackward[c] = true;}
275  else if (when_part_options=="sieve") {mp_applyForward[c] = true; mp_applyBackward[c] = true; mp_applyPost[c] = true;}
276  else if (when_part_options=="intra") {mp_applyIntra[c] = true;}
277  else
278  {
279  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> Unknown keyword '" << when_part_options << "' provided in options list !" << endl);
280  ShowCommonHelp();
281  return 1;
282  }
283 
284  // ______________________________________________________________________________
285  // Create convolver and call associated functions
286 
287  // Create the image convolver
288  if (list[convolver]) m2p_ImageConvolvers[c] = list[convolver]();
289  else
290  {
291  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> Image convolver '" << convolver << "' does not exist !" << endl);
293  return 1;
294  }
295  // Set parameters
298  // Provide configuration file if any
299  if (file_options!="" && m2p_ImageConvolvers[c]->ReadConfigurationFile(file_options))
300  {
301  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> A problem occurred while reading and checking configuration file for image convolver '" << convolver << "' !" << endl);
302  return 1;
303  }
304  // Provide options if any
305  if (list_options!="" && m2p_ImageConvolvers[c]->ReadOptionsList(list_options))
306  {
307  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> A problem occurred while parsing and reading options list for image convolver '" << convolver << "' !" << endl);
308  return 1;
309  }
310  // Check parameters
312  {
313  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> A problem occurred while checking parameters for image convolver '" << convolver << "' !" << endl);
314  return 1;
315  }
316  // Initialize the image convolver
318  {
319  Cerr("***** oImageConvolverManager::ParseOptionsAndInitializeImageConvolvers() -> A problem occurred while initializing image convolver '" << convolver << "' !" << endl);
320  return 1;
321  }
322  }
323 
324  // Normal end
325  return 0;
326 }
327 
328 // =====================================================================
329 // ---------------------------------------------------------------------
330 // ---------------------------------------------------------------------
331 // =====================================================================
332 
334 {
335  #ifdef CASTOR_DEBUG
336  // Check if initialized
337  if (!m_initialized)
338  {
339  Cerr("***** oImageConvolverManager::ConvolveForward() -> Called while not initialized !" << endl);
340  return 1;
341  }
342  #endif
343  // Loop on convolvers
344  for (int c=0; c<m_nbImageConvolvers; c++)
345  {
346  // Apply it only if asked for
347  if (mp_applyForward[c])
348  {
349  // Verbose
350  if (m_verbose>=2)
351  {
352  if (m_nbImageConvolvers>1) Cout("oImageConvolverManager::ConvolveForward() -> Apply convolution " << c+1 << " to forward image" << endl);
353  else Cout("oImageConvolverManager::ConvolveForward() -> Apply convolution to forward image" << endl);
354  }
355  // Loop on basis functions
357  {
359  {
361  {
362  // Get the pointer to the image
363  FLTNB* image = ap_ImageSpace->m4p_forwardImage[tb][rb][cb];
364  // Apply convolution
366  }
367  }
368  }
369  }
370  }
371  // Normal end
372  return 0;
373 }
374 
375 // =====================================================================
376 // ---------------------------------------------------------------------
377 // ---------------------------------------------------------------------
378 // =====================================================================
379 
381 {
382  #ifdef CASTOR_DEBUG
383  // Check if initialized
384  if (!m_initialized)
385  {
386  Cerr("***** oImageConvolverManager::ConvolveBackward() -> Called while not initialized !" << endl);
387  return 1;
388  }
389  #endif
390  // Loop on convolvers
391  for (int c=0; c<m_nbImageConvolvers; c++)
392  {
393  // Apply it only if asked for
394  if (mp_applyBackward[c])
395  {
396  // Verbose
397  if (m_verbose>=2)
398  {
399  if (m_nbImageConvolvers>1)
400  {
401  if (ap_ImageSpace->IsLoadedSensitivity()) Cout("oImageConvolverManager::ConvolveBackward() -> Apply convolution " << c+1 << " to backward image" << endl);
402  else Cout("oImageConvolverManager::ConvolveBackward() -> Apply convolution " << c+1 << " to backward and sensitivity images" << endl);
403  }
404  else
405  {
406  if (ap_ImageSpace->IsLoadedSensitivity()) Cout("oImageConvolverManager::ConvolveBackward() -> Apply convolution to backward image" << endl);
407  else Cout("oImageConvolverManager::ConvolveBackward() -> Apply convolution to backward and sensitivity images" << endl);
408  }
409  }
410  // Deal with backward images, loop on number of images
411  for (int img=0; img<ap_ImageSpace->GetNbBackwardImages(); img++)
412  {
413  // Loop on basis functions
415  {
417  {
419  {
420  // Get the pointer to the image
421  int thread_0 = 0;
422  FLTNB* image = ap_ImageSpace->m6p_backwardImage[img][thread_0][tb][rb][cb];
423  // Apply convolution
425  }
426  }
427  }
428  }
429  // Deal with sensitivity images for histogram reconstructions
430  if (!ap_ImageSpace->IsLoadedSensitivity())
431  {
432  // Loop on frames and gates
433  for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++)
434  {
435  for (int rg=0; rg<mp_ImageDimensionsAndQuantification->GetNb1stMotImgsForLMS(fr); rg++)
436  {
438  {
439  // Get the pointer to the image
440  int thread_0 = 0;
441  FLTNB* image = ap_ImageSpace->m5p_sensitivity[thread_0][fr][rg][cg];
442  // Apply convolution
444  }
445  }
446  }
447  }
448  }
449  }
450  // Normal end
451  return 0;
452 }
453 
454 // =====================================================================
455 // ---------------------------------------------------------------------
456 // ---------------------------------------------------------------------
457 // =====================================================================
458 
460 {
461  #ifdef CASTOR_DEBUG
462  // Check if initialized
463  if (!m_initialized)
464  {
465  Cerr("***** oImageConvolverManager::ConvolveIntra() -> Called while not initialized !" << endl);
466  return 1;
467  }
468  #endif
469  // Loop on convolvers
470  for (int c=0; c<m_nbImageConvolvers; c++)
471  {
472  // Apply it only if asked for
473  if (mp_applyIntra[c])
474  {
475  // Verbose
476  if (m_verbose>=2)
477  {
478  if (m_nbImageConvolvers>1) Cout("oImageConvolverManager::ConvolveIntra() -> Apply convolution " << c+1 << " to current image" << endl);
479  else Cout("oImageConvolverManager::ConvolveIntra() -> Apply convolution to current image" << endl);
480  }
481  // Loop on basis functions
483  {
485  {
487  {
488  // Get the pointer to the image
489  FLTNB* image = ap_ImageSpace->m4p_image[tb][rb][cb];
490  // Apply convolution
492  }
493  }
494  }
495  }
496  }
497  // Normal end
498  return 0;
499 }
500 
501 // =====================================================================
502 // ---------------------------------------------------------------------
503 // ---------------------------------------------------------------------
504 // =====================================================================
505 
507 {
508  #ifdef CASTOR_DEBUG
509  // Check if initialized
510  if (!m_initialized)
511  {
512  Cerr("***** oImageConvolverManager::ConvolveSensitivity() -> Called while not initialized !" << endl);
513  return 1;
514  }
515  #endif
516  // Loop on convolvers
517  for (int c=0; c<m_nbImageConvolvers; c++)
518  {
519  // Apply it only if asked for
520  if (mp_applyBackward[c])
521  {
522  // Verbose
523  if (m_verbose>=2)
524  {
525  if (m_nbImageConvolvers>1) Cout("oImageConvolverManager::ConvolveSensitivity() -> Apply convolution " << c+1 << " to sensitivity image" << endl);
526  else Cout("oImageConvolverManager::ConvolveSensitivity() -> Apply convolution to sensitivity image" << endl);
527  }
528  // Loop on frames and gates
529  for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++)
530  {
531  for (int rg=0; rg<mp_ImageDimensionsAndQuantification->GetNb1stMotImgsForLMS(fr); rg++)
532  {
534  {
535  // Get the pointer to the image
536  int thread_0 = 0;
537  FLTNB* image = ap_ImageSpace->m5p_sensitivity[thread_0][fr][rg][cg];
538  // Apply convolution
540  }
541  }
542  }
543  }
544  }
545  // Normal end
546  return 0;
547 }
548 
549 // =====================================================================
550 // ---------------------------------------------------------------------
551 // ---------------------------------------------------------------------
552 // =====================================================================
553 
555 {
556  #ifdef CASTOR_DEBUG
557  // Check if initialized
558  if (!m_initialized)
559  {
560  Cerr("***** oImageConvolverManager::ConvolvePost() -> Called while not initialized !" << endl);
561  return 1;
562  }
563  #endif
564  // Loop on convolvers
565  for (int c=0; c<m_nbImageConvolvers; c++)
566  {
567  // Apply it only if asked for
568  if (mp_applyPost[c])
569  {
570  // Verbose
571  if (m_verbose>=2)
572  {
573  if (m_nbImageConvolvers>1) Cout("oImageConvolverManager::ConvolvePost() -> Apply convolution " << c+1 << " to output image" << endl);
574  else Cout("oImageConvolverManager::ConvolvePost() -> Apply convolution to output image" << endl);
575  }
576  // At this step, the output image is still as basis functions and copied into the forward image.
577  // So we loop on basis functions
578  for (int tbf=0; tbf<mp_ImageDimensionsAndQuantification->GetNbTimeBasisFunctions(); tbf++)
579  {
580  for (int rbf=0; rbf<mp_ImageDimensionsAndQuantification->GetNbRespBasisFunctions(); rbf++)
581  {
582  for (int cbf=0; cbf<mp_ImageDimensionsAndQuantification->GetNbCardBasisFunctions(); cbf++)
583  {
584  // Get the pointer to the output image
585  FLTNB* image = ap_ImageSpace->m4p_forwardImage[tbf][rbf][cbf];
586  // Apply convolution
588  }
589  }
590  }
591 /*
592  // Loop on frames and respiratory/cardiac gates
593  for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++)
594  {
595  for (int rg=0; rg<mp_ImageDimensionsAndQuantification->GetNbRespGates(); rg++)
596  {
597  for (int cg=0; cg<mp_ImageDimensionsAndQuantification->GetNbCardGates(); cg++)
598  {
599  // Get the pointer to the output image
600  FLTNB* image = ap_ImageSpace->m4p_outputImage[fr][rg][cg];
601  // Apply convolution
602  m2p_ImageConvolvers[c]->ApplyConvolution(image);
603  }
604  }
605  }
606 */
607  }
608  }
609  // Normal end
610  return 0;
611 }
612 
613 // =====================================================================
614 // ---------------------------------------------------------------------
615 // ---------------------------------------------------------------------
616 // =====================================================================
int ParseOptionsAndInitializeImageConvolvers()
A function used to parse options and initialize image convolvers.
int GetNbCardBasisFunctions()
Get the number of cardiac basis functions.
#define Cerr(MESSAGE)
int ConvolveForward(oImageSpace *ap_ImageSpace)
int CheckParameters()
A function used to check the parameters settings.
oImageConvolverManager()
The constructor of oImageConvolverManager.
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
int GetNbTimeBasisFunctions()
Get the number of time basis functions.
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
int ConvolvePost(oImageSpace *ap_ImageSpace)
int ConvolveIntra(oImageSpace *ap_ImageSpace)
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
static sAddonManager * GetInstance()
const string & GetPathToConfigDir()
Return the path to the CASTOR config directory.
std::map< string, maker_image_convolver > mp_listOfImageConvolvers
int ConvolveBackward(oImageSpace *ap_ImageSpace)
int ApplyConvolutionTranspose(FLTNB *ap_image)
static void ShowCommonHelp()
This function does not take any parameter and is used to display some help about the syntax of the op...
~oImageConvolverManager()
The destructor of oImageConvolverManager.
int ApplyConvolution(FLTNB *ap_image)
This class holds all the matrices in the image domain that can be used in the algorithm: image...
int GetNb2ndMotImgsForLMS()
call the eponym function from the oDynamicDataManager object
int ConvolveSensitivity(oImageSpace *ap_ImageSpace)
#define Cout(MESSAGE)
int GetNbRespBasisFunctions()
Get the number of respiratory basis functions.
This abstract class is the generic image convolver class used by the oImageConvolverManager.
int Initialize()
A function used to initialize the manager and all image convolvers it manages.
void ShowHelpImageConvolver()
Show help about all implemented image convolvers.