CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iRCPGSAlgorithm.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 
24 #include "iRCPGSAlgorithm.hh"
25 // TODO replace asserts with a better error management, propagate errors for a clean termination
26 #include <cassert>
27 #ifdef _WIN32
28 // Avoid compilation errors due to mix up between std::min()/max() and
29 // min max macros
30 #undef min
31 #undef max
32 #endif
33 
35 {
36  m_ddCRP = -1;
37  m_backprojection = -1;
38  m_neighbourhood = -1;
39  m_gammaShape = -1.;
40  m_gammaRate = -1.;
41  m_ddcrpAlpha = -1.;
42  m_ddcrpLogAlpha = 0.;
43  m_multiModalLag = 0;
49  mp_multiModalParam = NULL;
51  mp_clusterValues = NULL;
52  mp_clusterN = NULL;
53  mp_clusterSensitivity = NULL;
54  mpp_clusterMultiModal = NULL;
55  mp_clusterCount = NULL;
56  mp_nextLink = NULL;
57  mp_listVoxelIndices = NULL;
58  mp_listEventsIndices = NULL;
61  temp_count_multimodal = NULL;
63 }
64 
65 // -----------------------------------------------------------------------------------------------------------------------------------------
66 
68 {
69  delete[] mp_clusterValues;
70  mp_clusterValues = NULL;
71  delete[] mp_voxelClusterMapping;
73  delete[] mp_nextLink;
74  mp_nextLink = NULL;
75  delete[] mp_listVoxelIndices;
76  mp_listVoxelIndices = NULL;
77  delete[] mp_clusterN;
78  mp_clusterN = NULL;
79  delete[] mp_clusterSensitivity;
80  mp_clusterSensitivity = NULL;
81  delete[] mpv_parentLinks;
82  mpv_parentLinks = NULL;
83 
84  // optional variables, not always initialized and used
85  if (mp_listEventsIndices!=NULL)
86  {
87  delete[] mp_listEventsIndices;
88  mp_listEventsIndices = NULL;
89  }
91  {
94  }
95 
96  if (mpp_clusterMultiModal!=NULL)
97  {
98  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
99  {
100  if (mpp_clusterMultiModal[mmnb]!=NULL) delete[] mpp_clusterMultiModal[mmnb];
101  }
102  delete[] mpp_clusterMultiModal;
103  mpp_clusterMultiModal = NULL;
104  }
105  if (mp_clusterCount!=NULL)
106  {
107  delete[] mp_clusterCount;
108  mp_clusterCount = NULL;
109  }
110  if (temp_count_multimodal!=NULL)
111  {
112  delete[] temp_count_multimodal;
113  temp_count_multimodal = NULL;
114  }
115  if (mp_multiModalNoiseSigma!=NULL)
116  {
117  delete[] mp_multiModalNoiseSigma;
119  }
120  if (mp_multiModalParam!=NULL)
121  {
122  delete[] mp_multiModalParam;
123  mp_multiModalParam = NULL;
124  }
125  // delete helper variables used in backprojection modes 1 and 2
126  if (m_backprojection==1 || m_backprojection==2)
127  {
128  for (int bed=0 ; bed<m_nbBeds ; bed++)
129  {
130  int64_t index;
131  #pragma omp parallel for private(index) schedule(static,1)
132  for (index=0 ; index<m2p_DataFile[bed]->GetSize() ; index++)
133  {
134  // Get the thread index
135  int th = 0;
136  #ifdef CASTOR_OMP
137  th = omp_get_thread_num();
138  #endif
139 
140  vEvent* event = m2p_DataFile[bed]->GetEvent(index, th);
142 
143  for (int b=0; b<line->GetNbTOFBins(); b++)
144  {
145  delete[] m4p_EventsBackprojectionTrace[bed][index][b];
146  }
147  delete[] m4p_EventsBackprojectionTrace[bed][index];
148  }
149  delete[] m4p_EventsBackprojectionTrace[bed];
150  }
153  }
154 }
155 
156 // -----------------------------------------------------------------------------------------------------------------------------------------
157 
158 int iRCPGSAlgorithm::InitSpecificOptions(string a_specificOptions)
159 {
160  if (m_verbose>=2) Cout("iRCPGSAlgorithm::InitSpecificOptions() "<<a_specificOptions<<endl);
161 
162  // not great to do this allocation here TODO
164  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) mp_multiModalNoiseSigma[mmnb] = -1.;
165 
166  // TODO code copied from elsewhere, kind of a duplicate, so should be moved to more generic functions, to vAlgorithm
167  // Search for a colon ":", this indicates that a configuration file is provided after the algorithm name
168  size_t colon = a_specificOptions.find_first_of(":");
169  size_t comma = a_specificOptions.find_first_of(",");
170 
171  string name = "";
172  string list_options = "";
173  string file_options = "";
174 
175  // Case 1: we have a colon : config file
176  if (colon!=string::npos)
177  {
178  // Get the name before the colon
179  name = a_specificOptions.substr(0,colon);
180  // Get the configuration file after the colon
181  file_options = a_specificOptions.substr(colon+1);
182  // List of options is empty
183  list_options = "";
184  }
185  // Case 2: we have a comma : options list
186  else if (comma!=string::npos)
187  {
188  // Get the name before the first comma
189  name = a_specificOptions.substr(0,comma);
190  // Get the list of options after the first comma
191  list_options = a_specificOptions.substr(comma+1);
192  // Configuration file is empty
193  file_options = "";
194  }
195  // Case 3: no colon and no comma : default config file
196  else
197  {
198  // Get the algorithm name
199  name = a_specificOptions;
200  // List of options is empty
201  list_options = "";
202  // Build the default configuration file
203  sOutputManager* p_output_manager = sOutputManager::GetInstance();
204  file_options = p_output_manager->GetPathToConfigDir() + "/algorithm/" + name + ".conf";
205  }
206 
207  // TODO move this check elsewhere
208  if (name!="RCPGS")
209  {
210  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> The given algorithm name is not RCPGS !" << endl);
211  return 1;
212  }
213 
214  // process options list, formatted as 'paramName=paramValue,paramName=paramValue,etc.'
215  if(!list_options.empty())
216  {
217  vector<string> option_name;
218  vector<string> option_value;
219 
220  // extract all parameter name-value pairs
221  size_t pos_comma;
222  while ((pos_comma=list_options.find_first_of(","))!=string::npos)
223  {
224  // Get the substring before the comma
225  string sub_buf = list_options.substr(0,pos_comma);
226 
227  size_t pos_equal = sub_buf.find_first_of("=");
228  if (pos_equal==string::npos || pos_equal==0)
229  {
230  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Syntax problem in algorithm parameters !" << endl);
231  return 1;
232  }
233  option_name.push_back(sub_buf.substr(0,pos_equal));
234  option_value.push_back(sub_buf.substr(pos_equal+1));
235  list_options = list_options.substr(pos_comma+1);
236  }
237 
238  // last or single name-value pair
239  if (list_options.length()>0)
240  {
241  size_t pos_equal = list_options.find_first_of("=");
242  if (pos_equal==string::npos || pos_equal==0)
243  {
244  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Syntax problem in algorithm parameters !" << endl);
245  return 1;
246  }
247  option_name.push_back(list_options.substr(0,pos_equal));
248  option_value.push_back(list_options.substr(pos_equal+1));
249  }
250 
251  // temporary variable for noise std deviations for multimodal images
252  vector<HPFLTNB> temp_multiModalNoiseSigma;
253 
254  // read extracted options
255  for(size_t v=0;v<option_name.size();v++)
256  {
257  if (option_name.at(v)=="ddCRP")
258  {
259  int option[1];
260  // Read them
261  if (ReadStringOption(option_value.at(v), option, 1, ",", "ddCRP"))
262  {
263  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
264  return 1;
265  }
266  m_ddCRP = option[0];
267  }
268  else if (option_name.at(v)=="alpha")
269  {
270  HPFLTNB option[1];
271  // Read them
272  if (ReadStringOption(option_value.at(v), option, 1, ",", "alpha"))
273  {
274  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
275  return 1;
276  }
277  m_ddcrpAlpha = option[0];
278  }
279  else if (option_name.at(v)=="gammaShape")
280  {
281  HPFLTNB option[1];
282  // Read them
283  if (ReadStringOption(option_value.at(v), option, 1, ",", "gammaShape"))
284  {
285  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
286  return 1;
287  }
288  m_gammaShape = option[0];
289  }
290  else if (option_name.at(v)=="gammaRate")
291  {
292  HPFLTNB option[1];
293  // Read them
294  if (ReadStringOption(option_value.at(v), option, 1, ",", "gammaRate"))
295  {
296  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
297  return 1;
298  }
299  m_gammaRate = option[0];
300  }
301  else if (option_name.at(v)=="backprojection")
302  {
303  int option[1];
304  // Read them
305  if (ReadStringOption(option_value.at(v), option, 1, ",", "backprojection"))
306  {
307  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
308  return 1;
309  }
310  m_backprojection = option[0];
311  }
312  else if (option_name.at(v)=="multiModalNoiseSigma")
313  {
314  HPFLTNB option[1];
315  // Read them
316  if (ReadStringOption(option_value.at(v), option, 1, ",", "multiModalNoiseSigma"))
317  {
318  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
319  return 1;
320  }
321  temp_multiModalNoiseSigma.push_back(option[0]);
322  }
323  else if (option_name.at(v)=="multiModalLag")
324  {
325  INTNB option[1];
326  // Read them
327  if (ReadStringOption(option_value.at(v), option, 1, ",", "multiModalLag"))
328  {
329  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
330  return 1;
331  }
332  m_multiModalLag = option[0];
333  }
334  else if (option_name.at(v)=="meanClusterVolumeMin")
335  {
336  HPFLTNB option[1];
337  // Read them
338  if (ReadStringOption(option_value.at(v), option, 1, ",", "meanClusterVolumeMin"))
339  {
340  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
341  return 1;
342  }
343  m_meanClusterVolumeMin = option[0];
344  }
345  else if (option_name.at(v)=="meanClusterVolumeMax")
346  {
347  HPFLTNB option[1];
348  // Read them
349  if (ReadStringOption(option_value.at(v), option, 1, ",", "meanClusterVolumeMax"))
350  {
351  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
352  return 1;
353  }
354  m_meanClusterVolumeMax = option[0];
355  }
356  else if (option_name.at(v)=="alphaIncrement")
357  {
358  HPFLTNB option[1];
359  // Read them
360  if (ReadStringOption(option_value.at(v), option, 1, ",", "alphaIncrement"))
361  {
362  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Failed to read the list of options correctly!" << endl);
363  return 1;
364  }
365  m_ddcrpAlphaIncrement = option[0];
366  }
367  }
368  if (temp_multiModalNoiseSigma.size()!=(size_t)mp_ID->GetNbMultiModalImages())
369  {
370  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> The number of provided noise standard deviations does not equal the number of provided multimodal images! " << endl);
371  return 1;
372  }
373  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
374  {
375  mp_multiModalNoiseSigma[mmnb] = temp_multiModalNoiseSigma.at(mmnb);
376  }
377 
378  if (m_verbose>=2)
379  {
380  Cout("iRCPGSAlgorithm::InitSpecificOptions() -> Algorithm options read from command line "<<endl);
381  }
382  }
383  // otherwise process config file
384  else if(!file_options.empty())
385  {
386  ReadConfigurationFile(file_options);
387  if (m_verbose>=2)
388  {
389  Cout("iRCPGSAlgorithm::InitSpecificOptions() -> Algorithm options read from the configuration file "<<endl);
390  }
391  }
392 
393  // check parameters
394  if (m_ddCRP>0 && m_ddcrpAlpha<0.)
395  {
396  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> The ddCRP alpha parameter must be non negative when ddCRP is used! " << endl);
397  return 1;
398  }
399  if (m_ddCRP<0 || m_ddCRP>2)
400  {
401  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> The ddCRP type is not properly specified! " << endl);
402  return 1;
403  }
404  if (m_backprojection<0 || m_backprojection>2)
405  {
406  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> The backprojection type is not properly specified! " << endl);
407  return 1;
408  }
409  if (m_gammaShape<=0. || m_gammaRate<=0.)
410  {
411  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Gamma prior parameters are not positive! " << endl);
412  return 1;
413  }
414  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
415  {
416  if (mp_multiModalNoiseSigma[mmnb]<=0.)
417  {
418  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Wrong noise standard deviations for multimodal images! " << endl);
419  return 1;
420  }
421  }
422 
424  {
426  {
427  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Min/Max criteria for average cluster volume inconsistent! " << endl);
428  return 1;
429  }
430  if (m_ddcrpAlphaIncrement<=1.)
431  {
432  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Multiplicative update for ddCRP alpha must be >1. if min/max criteria for average cluster volume specified ! " << endl);
433  return 1;
434  }
435  if (!(m_ddcrpAlpha>0.))
436  {
437  Cerr("***** iRCPGSAlgorithm::InitSpecificOptions() -> Options for adaptative alpha not compatible with alpha=0. ! " << endl);
438  return 1;
439  }
440  }
441 
442  if (m_verbose>=2)
443  {
444  if (m_ddCRP>0)
445  {
446  Cout("iRCPGSAlgorithm::InitSpecificOptions() -> ddCRP parameters : type "<< m_ddCRP <<", alpha = "<<m_ddcrpAlpha<< ", multimodal lag = "<<m_multiModalLag<<" iterations "<< endl);
447  if (!(m_ddcrpAlpha>0.))
448  {
449  Cout(" as alpha=0, entering special mode when sampling the posterior clustering"<<endl);
450  }
451  if (m_meanClusterVolumeMax>0.)
452  {
453  Cout(" alpha auto-tune with condition "<< m_meanClusterVolumeMin<<" < mean cluster volume < "<<m_meanClusterVolumeMax<< " mm3 and multiplicative increment = "<< m_ddcrpAlphaIncrement <<endl);
454  }
455  }
456  else Cout("iRCPGSAlgorithm::InitSpecificOptions() -> ddCRP not used ! "<<endl);
457  Cout("iRCPGSAlgorithm::InitSpecificOptions() -> Backprojection type = "<<m_backprojection<<endl);
458  }
459 
460  return 0;
461 }
462 
463 int iRCPGSAlgorithm::ReadConfigurationFile(const string& a_configurationFile)
464 {
465  string key_word = "";
466 
467  key_word = "ddCRP type";
468  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_ddCRP, 1, KEYWORD_MANDATORY))
469  {
470  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
471  return 1;
472  }
473 
474  key_word = "ddCRP alpha";
475  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_ddcrpAlpha, 1, KEYWORD_MANDATORY))
476  {
477  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
478  return 1;
479  }
480 
481  key_word = "gamma prior shape";
482  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_gammaShape, 1, KEYWORD_MANDATORY))
483  {
484  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
485  return 1;
486  }
487 
488  key_word = "gamma prior rate";
489  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_gammaRate, 1, KEYWORD_MANDATORY))
490  {
491  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
492  return 1;
493  }
494 
495  key_word = "backprojection type";
496  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_backprojection, 1, KEYWORD_MANDATORY))
497  {
498  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
499  return 1;
500  }
501 
502  key_word = "multimodal noise sigma";
504  {
505  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
506  return 1;
507  }
508 
509  key_word = "multimodal lag";
510  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_multiModalLag, 1, KEYWORD_OPTIONAL))
511  {
512  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
513  return 1;
514  }
515 
516  key_word = "mean cluster volume min";
517  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_meanClusterVolumeMin, 1, KEYWORD_OPTIONAL))
518  {
519  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
520  return 1;
521  }
522 
523  key_word = "mean cluster volume max";
524  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_meanClusterVolumeMax, 1, KEYWORD_OPTIONAL))
525  {
526  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
527  return 1;
528  }
529 
530  key_word = "ddCRP alpha increment";
531  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_ddcrpAlphaIncrement, 1, KEYWORD_OPTIONAL))
532  {
533  Cerr("***** iRCPGSAlgorithm::ReadAndCheckConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
534  return 1;
535  }
536 
537  // End
538  return 0;
539 }
540 
541 // -----------------------------------------------------------------------------------------------------------------------------------------
542 
544 {
545  if (m_verbose>=2) Cout("iRCPGSAlgorithm::InitializeHelperVar() "<<endl);
546 
547  int nbVoxels = mp_ID->GetNbVoxXYZ();
548  bool multimodal_info = mp_ImageSpace->IsLoadedMultiModal();
549  bool background_mask = mp_ImageSpace->IsLoadedMask();
550 
551  // number of neighbourhood voxels, currently fixed parameter, TODO implement different neighbourhoods
552  m_neighbourhood = (mp_ID->GetNbVoxZ()>1)?6:4;
554  assert (!std::isnan(m_ddcrpLogAlpha));
555 
556  mp_listRelevantVoxelIndices = new bool[nbVoxels];
557  mp_voxelClusterMapping = new INTNB[nbVoxels];
558  // Due to the current implementation, cluster indices <= nbVoxels, cluster array size thus being = nbVoxels+1
559  mp_clusterValues = new HPFLTNB[nbVoxels+1];
560 
561  // the marginalized backprojection requires updates of mp_clusterN during parallel backprojection, so need for multithreaded mp_clusterN
562  // approximative implementation, because modifications in cluster sums can't be shared between threads
563  INTNB th_clusterN = (m_backprojection==2)?mp_ID->GetNbThreadsForProjection():1;
564  mp_clusterN = new HPFLTNB*[th_clusterN];
565  for (INTNB th=0; th<th_clusterN; th++) mp_clusterN[th] = new HPFLTNB[nbVoxels+1];
566 
567  mp_clusterSensitivity = new HPFLTNB[nbVoxels+1];
568 
569  // initialized only if multimodal info
570  if (multimodal_info)
571  {
573  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
574  {
575  mpp_clusterMultiModal[mmnb] = new HPFLTNB[nbVoxels+1];
576  }
577  mp_clusterCount = new INTNB[nbVoxels+1];
578  }
579 
580  // description of ddCRP links
581  mp_nextLink = new INTNB[nbVoxels];
582  mpv_parentLinks = new vector<INTNB>[nbVoxels];
583  mv_newClusters.assign(1,nbVoxels);
584  mp_listVoxelIndices = new INTNB[nbVoxels];
585 
586  // permanent backward image for backprojection modes 1 and 2
588 
589  // helper variables for the backprojection modes 1 and 2
590  // trace of voxels into which events were backprojected in the previous iteration
591  // initialize voxel indices at -1
592  if (m_backprojection==1 || m_backprojection==2)
593  {
594  //mp_listEventsIndices = new INTNB*[m_nbBeds];
596  for (int bed=0 ; bed<m_nbBeds ; bed++)
597  {
599  //mp_listEventsIndices[bed] = new INTNB[m2p_DataFile[bed]->GetSize()];
600 
601  // Compute start and stop indices taking MPI into account (the vDataFile does that)
602  int64_t index_start = 0;
603  int64_t index_stop = 0;
604  m2p_DataFile[bed]->GetEventIndexStartAndStop(&index_start, &index_stop, 0, 1);
605 
606  int64_t index;
607  // Keep the static scheduling with a chunk size at 1, it is important
608  #pragma omp parallel for private(index) schedule(static, 1)
609  for (index=0 ; index<m2p_DataFile[bed]->GetSize() ; index++)
610  {
611  // Get the thread index
612  INTNB th = 0;
613  #ifdef CASTOR_OMP
614  th = omp_get_thread_num();
615  #endif
616 
617  vEvent* event = m2p_DataFile[bed]->GetEvent(index, th);
618 
620 
621  m4p_EventsBackprojectionTrace[bed][index] = new INTNB*[line->GetNbTOFBins()];
622 
623  for (int b=0; b<line->GetNbTOFBins(); b++)
624  {
625  m4p_EventsBackprojectionTrace[bed][index][b] = new INTNB[(INTNB)round(event->GetEventValue(b))];
626  for (int e=0; e<(INTNB)round(event->GetEventValue(b)); e++)
627  {
628  m4p_EventsBackprojectionTrace[bed][index][b][e] = -1;
629  }
630  }
631  }
632  }
633  }
634 
635  // voxel and clusters variables
636  INTNB v;
637  #pragma omp parallel for private(v) schedule(guided)
638  for (v=0; v<nbVoxels; v++)
639  {
640  mp_listVoxelIndices[v] = v;
641 
642  mp_listRelevantVoxelIndices[v] = true;
643  if (background_mask && (((INTNB)round(mp_ImageSpace->mp_maskImage[v])) == 0))
644  mp_listRelevantVoxelIndices[v] = false;
645 
646  mp_voxelClusterMapping[v] = v;
647  mp_clusterValues[v] = mp_ImageSpace->m4p_image[0][0][0][v];
648  for (INTNB th=0; th<th_clusterN; th++) mp_clusterN[th][v] = 0.;
650  if (multimodal_info)
651  {
652  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
653  {
655  }
656  mp_clusterCount[v] = 1;
657  }
658  mp_nextLink[v] = v;
659  mpv_parentLinks[v].push_back(v);
660 
662  }
663 
664  // parameter initialization for the free available cluster, will be recomputed anyway
665  mp_clusterValues[nbVoxels] = 0.;
666  for (INTNB th=0; th<th_clusterN; th++) mp_clusterN[th][nbVoxels] = 0.;
667  mp_clusterSensitivity[nbVoxels] = 0.;
668  if (multimodal_info)
669  {
670  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) mpp_clusterMultiModal[mmnb][nbVoxels] = 0.;
671  mp_clusterCount[nbVoxels] = 0;
672  }
673 
676  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
677  {
678  temp_count_multimodal[mmnb] = 0.;
679  mp_multiModalParam[mmnb] = 0.;
680  }
681 
682  // End
683  return 0;
684 }
685 
686 // -----------------------------------------------------------------------------------------------------------------------------------------
687 
689 {
690  if (m_verbose>=2) Cout("iRCPGSAlgorithm::ProcessMultiModalInfo() "<<endl);
691 
692  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
693  {
694  FLTNB maxt = mp_ImageSpace->m2p_multiModalImage[mmnb][0];
695  FLTNB mint = mp_ImageSpace->m2p_multiModalImage[mmnb][0];
696 
697  for (int v=1; v<mp_ID->GetNbVoxXYZ(); v++)
698  {
699  if (mp_ImageSpace->m2p_multiModalImage[mmnb][v]>maxt) maxt = mp_ImageSpace->m2p_multiModalImage[mmnb][v];
700  if (mp_ImageSpace->m2p_multiModalImage[mmnb][v]<mint) mint = mp_ImageSpace->m2p_multiModalImage[mmnb][v];
701  }
702 
703  // compute the prior parameter rho
704  mp_multiModalParam[mmnb] = mp_multiModalNoiseSigma[mmnb]/maxt;
705 
706  if (m_verbose>=2) Cout("iRCPGSAlgorithm::StepBeforeIterationLoop() --> Multimodal image "<<mmnb<<" : noise std dev = "<<mp_multiModalNoiseSigma[mmnb]<<" , prior param rho = "<<mp_multiModalParam[mmnb]<<endl);
707  }
708 
709  // End
710  return 0;
711 }
712 
713 // -----------------------------------------------------------------------------------------------------------------------------------------
714 
716 {
718  {
719  Cerr("***** iRCPGSAlgorithm::StepBeforeIterationLoop() -> A problem occurred while calling StepBeforeIterationLoop() function !" << endl);
720  return 1;
721  }
722 
723  // Main image initialization
725  {
726  Cerr("***** vAlgorithm::StepBeforeIterationLoop() -> An error occurred while reading the initialization image !" << endl);
727  return 1;
728  }
729 
730  // allocate backward images
732 
733  // specific to this algorithm
735 
736  // currently the sensitivity image must be provided
738 
739  // If subsets and OSEM-like backprojection (mode 0), then divide by the number of subsets because the acqusition duration is included in the sensitivity
740  // No need for other backprojection modes because permanently dealing with the entire backprojected data for all the subsets
741  if (mp_nbSubsets[0]>1 && m_backprojection==0)
742  {
743  if (m_verbose>=2) Cout("iRCPGSAlgorithm::StepBeforeIterationLoop() --> Dividing the provided sensitivity image by the number of subsets "<<endl);
744  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++) mp_ImageSpace->m5p_sensitivity[0][0][0][0][v] /= ((FLTNB)mp_nbSubsets[0]);
745  }
746 
747  // process additional (multimodal) images
749 
750  // End
751  return 0;
752 }
753 
754 
755 // =====================================================================
756 // ---------------------------------------------------------------------
757 // ---------------------------------------------------------------------
758 // =====================================================================
759 
760 int iRCPGSAlgorithm::StepPreProcessInsideSubsetLoop(int a_iteration, int a_subset)
761 {
762  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepPreProcessInsideSubsetLoop ... " << endl);
763 
764  // Initialize the correction backward image(s)
766 
767  // Copy current image in forward-image buffer
769 
770  // End
771  return 0;
772 }
773 
774 // -----------------------------------------------------------------------------------------------------------------------------------------
775 
776 int iRCPGSAlgorithm::StepInnerLoopInsideSubsetLoop(int a_iteration, int a_subset, int a_bed)
777 {
778  // Get the chrono manager singleton pointer
779  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
780 
781  // Sample posterior complete data, backproject the acquired data, Gibbs Sampler Step 1
782  p_ChronoManager->StartCustomStep(0,0);
783  SampleConditionalCompleteData(a_iteration, a_subset, a_bed);
784  p_ChronoManager->StopCustomStep(0,0);
785 
786  // End
787  return 0;
788 }
789 
790 // -----------------------------------------------------------------------------------------------------------------------------------------
791 
792 int iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop(int a_iteration, int a_subset)
793 {
794  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop ... " << endl);
795 
796  // Get the chrono manager singleton pointer
797  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
798 
799  // Merge parallel results
801 
802  // If mask provided, apply mask to sensitivity, the CleanNeverVisitedVoxels will take care of the images
804 
805  // Finalize processing of backward images if permanent backward image
806  if (mp_permanentBackwardImage!=NULL)
807  {
808  INTNB v;
809  #pragma omp parallel for private(v) schedule(guided)
810  for (v=0;v<mp_ID->GetNbVoxXYZ();v++)
811  {
813  }
814  }
815 
816  // update algorithm variables based on the new backprojection
817  ComputeSumsPerClusters(a_iteration);
818 
819  // Sample posterior ddCRP (clustering), Gibbs Sampler Step 2
820  if (m_ddCRP>0)
821  {
822  p_ChronoManager->StartCustomStep(0,1);
823 
824  SampleConditionalClustering(a_iteration);
825 
826  p_ChronoManager->StopCustomStep(0,1);
827  }
828 
829  // Marginalized backprojection: as mp_clusterN threaded, and sampling step 2 (conditional clustering) modifies mp_clusterN only for the first thread,
830  // copy the modifications to all the other threads for the next backprojection
831  if (m_backprojection==2)
832  {
833  INTNB s;
834  #pragma omp parallel for private(s) schedule(guided)
835  for (s=0;s<mp_ID->GetNbVoxXYZ()+1;s++)
836  for (INTNB th=1;th<mp_ID->GetNbThreadsForProjection();th++)
837  mp_clusterN[th][s] = mp_clusterN[0][s];
838  }
839 
840  // Sample posterior cluster intensities, Gibbs Sampler Step 3, once per iteration
841  p_ChronoManager->StartCustomStep(0,2);
843  p_ChronoManager->StopCustomStep(0,2);
844 
845  // display performance info at each iteration
846  p_ChronoManager->Display();
847 
848  // computed at each iteration because the variable is cleaned at each iteration after image clean-up
850 
851  // Generate current posterior image sample from clustering/partition and cluster intensities
853 
854  // if required, try to roughly update ddCRP alpha to obtain approximately the requested target average cluster volume
855  // every 5 iterations and once per iteration
856  if (m_meanClusterVolumeMax>0. && a_iteration>=5 && (a_iteration+1)%5==0 && a_subset==mp_nbSubsets[a_iteration]/2)
857  {
858  bool alpha_changed = false;
859  // modify alpha if current average cluster volume less than m_meanClusterVolumeMax
861  {
863  alpha_changed = true;
864  }
865  // modify alpha if current average cluster volume more than m_meanClusterVolumeMin
867  {
869  alpha_changed = true;
870  }
871  // Log the modification
872  if (alpha_changed)
873  {
875  Cout("iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop() -> Updated ddCRP alpha to "<<m_ddcrpAlpha<<endl);
876  }
877  }
878 
879  // Save the sensitivity image in histogram mode, if asked for
881  {
882  // Get output manager to build the file name
883  sOutputManager* p_output_manager = sOutputManager::GetInstance();
884  // Build the file name
885  string temp_sens = p_output_manager->GetPathName() + p_output_manager->GetBaseName();
886  stringstream temp_it; temp_it << a_iteration + 1;
887  stringstream temp_ss; temp_ss << a_subset + 1;
888  temp_sens.append("_it").append(temp_it.str()).append("_ss").append(temp_ss.str()).append("_sensitivity");
889  // Save sensitivity
891  }
892 
893  // Save the current image estimate if asked for
894  if (mp_ID->GetMPIRank()==0 && mp_outputIterations[a_iteration] && m_saveImageAfterSubsets)
895  {
896  // Verbose
897  if (m_verbose>=1) Cout("iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop() -> Save image at iteration " << a_iteration+1 << " for subset " << a_subset+1 << endl);
898  // Save image
899  if (StepImageOutput(a_iteration,a_subset))
900  {
901  Cerr("***** iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while saving images at iteration " << a_iteration+1 << " !" << endl);
902  return 1;
903  }
904  }
905 
906  // End
907  return 0;
908 }
909 
910 // -----------------------------------------------------------------------------------------------------------------------------------------
911 
912 int iRCPGSAlgorithm::SampleConditionalCompleteData(int a_iteration, int a_subset, int a_bed)
913 {
914 
915  // Verbose
916  if (m_verbose>=2)
917  {
918  if (m_nbBeds>1) Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Start loop over events for bed " << a_bed+1 << endl << flush);
919  else Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Start loop over events" << endl << flush);
920  }
921 
922  // Reinitialize 4D gate indexes
924 
925  // Apply the bed offset for this bed position
927 
928  // Progression (increments of 2%)
930  {
931  cout << "0 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%" << endl;
932  cout << "|----|----|----|----|----|----|----|----|----|----|" << endl;
933  cout << "|" << flush;
934  }
935 
936  int progression_percentage_old = 0;
937  int progression_nb_bars = 0;
938  uint64_t progression_printing_index = 0;
939 
940  // Compute start and stop indices taking MPI into account (the vDataFile does that)
941  int64_t index_start = 0;
942  int64_t index_stop = 0;
943 
944  m2p_DataFile[a_bed]->GetEventIndexStartAndStop(&index_start, &index_stop, a_subset, mp_nbSubsets[a_iteration]);
945 
946  // Synchronize MPI processes
947  #ifdef CASTOR_MPI
948  MPI_Barrier(MPI_COMM_WORLD);
949  #endif
950 
951  // Set the number of threads for projections (right after this loop, we set back the number of threads for image computations)
952  #ifdef CASTOR_OMP
953  omp_set_num_threads(mp_ID->GetNbThreadsForProjection());
954  #endif
955 
956  // This boolean is used to report any problem inside the parallel loop
957  bool problem = false;
958 
959  // Get the chrono manager singleton pointer
960  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
961 
962  // shuffle events
963  if (m_backprojection==1)
964  {
965  //random_shuffle(mp_listEventsIndices[a_bed], mp_listEventsIndices[a_bed]+(index_stop-index_start));
966  }
967 
968  // Launch the loop on events
969  int64_t index_temp;
970  // Keep the static scheduling with a chunk size at 1, it is important
971  #pragma omp parallel for private(index_temp) schedule(static, 1)
972  for ( index_temp = index_start ; index_temp < index_stop ; index_temp += mp_nbSubsets[a_iteration])
973  {
974  // Get the thread index
975  int th = 0;
976  #ifdef CASTOR_OMP
977  th = omp_get_thread_num();
978  #endif
979 
980  // get random event
981  INTNB index = index_temp;
982  //if (m_backprojection==1) index = mp_listEventsIndices[a_bed][index_temp];
983 
984  // Print progression (do not log out with Cout here)
985  if (m_verbose>=2 && th==0 && mp_ID->GetMPIRank()==0)
986  {
987  if (progression_printing_index%1000==0)
988  {
989  int progression_percentage_new = ((int)( (((float)(index-index_start+1))/((float)(index_stop-index_start)) ) * 100.));
990  if (progression_percentage_new>=progression_percentage_old+2) // Increments of 2%
991  {
992  int nb_steps = (progression_percentage_new-progression_percentage_old)/2;
993  for (int i=0; i<nb_steps; i++)
994  {
995  cout << "-" << flush;
996  progression_nb_bars++;
997  }
998  progression_percentage_old += nb_steps*2;
999  }
1000  }
1001  progression_printing_index++;
1002  }
1003 
1004  // Step 1: Get the current event for that thread index
1005  p_ChronoManager->StartIterativeDataUpdateStep1(th);
1006 
1007  vEvent* event = m2p_DataFile[a_bed]->GetEvent(index, th);
1008 
1009  if (event==NULL)
1010  {
1011  Cerr("***** iRCPGSAlgorithm::SampleConditionalCompleteData() -> An error occured while getting the event from index "
1012  << index << " (thread " << th << ") !" << endl);
1013  // Specify that there was a problem
1014  problem = true;
1015  // We must continue here because we are inside an OpenMP loop
1016  continue;
1017  }
1018  p_ChronoManager->StopIterativeDataUpdateStep1(th);
1019 
1020  // Step 2: Call the dynamic switch function that updates the current frame and gate numbers, and also detects involuntary patient motion
1021  p_ChronoManager->StartIterativeDataUpdateStep2(th);
1022  #ifdef CASTOR_DEBUG
1023  if (m_verbose>=4)
1024  {
1025  Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Step2: Check for Dynamic event (frame/gate switch, image-based deformation " << endl);
1026  }
1027  #endif
1028  int dynamic_switch_value = mp_ID->DynamicSwitch(index, event->GetTimeInMs(), a_bed, th);
1029  // If the DYNAMIC_SWITCH_CONTINUE is returned, then it means that we are not yet at the first frame
1030  if ( dynamic_switch_value == DYNAMIC_SWITCH_CONTINUE )
1031  {
1032  // Then we just skip this event
1033  continue;
1034  }
1035 
1036  p_ChronoManager->StopIterativeDataUpdateStep2(th);
1037 
1038  // Skip if empty event
1039  bool emptyEvent = true;
1040  for (int b=0; b<event->GetNbValueBins(); b++)
1041  if (event->GetEventValue(b)>0.)
1042  {
1043  emptyEvent = false;
1044  break;
1045  }
1046 
1047  if (emptyEvent) continue;
1048 
1049  // Step 3: Compute the projection line
1050  p_ChronoManager->StartIterativeDataUpdateStep3(th);
1051  #ifdef CASTOR_DEBUG
1052  if (m_verbose>=4) Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Step3: Compute the projection line " << endl);
1053  #endif
1055  if (line==NULL)
1056  {
1057  Cerr("***** iRCPGSAlgorithm::SampleConditionalCompleteData() -> A problem occured while computing the projection line !" << endl);
1058  // Specify that there was a problem
1059  problem = true;
1060  // We must continue here because we are inside an OpenMP loop
1061  continue;
1062  }
1063  p_ChronoManager->StopIterativeDataUpdateStep3(th);
1064 
1065  if(line->NotEmptyLine())
1066  {
1067  // get time respiratory and cardiac frame indices
1068  int timeFrame = mp_ID->GetCurrentTimeFrame(th);
1069  int respGate = mp_ID->GetCurrentRespImage(th);
1070  int cardGate = mp_ID->GetCurrentCardImage(th);
1071 
1072  assert(timeFrame==0);assert(respGate==0);assert(cardGate==0);
1073 
1074  // Loop over time basis functions
1075  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1076  {
1077  // Get frame/basis coefficient and continue if null
1078  FLTNB time_basis_coef = mp_ID->GetTimeBasisCoefficient(tbf,timeFrame);
1079  if (time_basis_coef==0.) continue;
1080  // Loop over respiratory basis functions
1081  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1082  {
1083  // Get resp_gate/basis coefficient and continue if null
1084  FLTNB resp_basis_coef = mp_ID->GetRespBasisCoefficient(rbf,respGate);
1085  if (resp_basis_coef==0.) continue;
1086  // Loop over cardiac basis functions
1087  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1088  {
1089  // Get card_gate_basis coefficient and continue if null
1090  FLTNB card_basis_coef = mp_ID->GetCardBasisCoefficient(cbf,cardGate);
1091  if (card_basis_coef==0.) continue;
1092  // Compute global coefficient
1093  FLTNB global_basis_coef = time_basis_coef * resp_basis_coef * card_basis_coef;
1094  assert(global_basis_coef==1.);
1095 
1096  // Compute multiplicative correction for this LOR
1097  FLTNB multiplicative_corr = 1./(event->GetMultiplicativeCorrections() * mp_ID->GetQuantificationFactor(a_bed, timeFrame, respGate, cardGate));
1098 
1099  // Loop over TOF bins
1100  for (int b=0; b<line->GetNbTOFBins(); b++)
1101  {
1102  FLTNB temp_eventValue = event->GetEventValue(b);
1103  if (temp_eventValue>0.)
1104  {
1105  // multinomial backprojection step with permanent backward image, backprojection variables only updated
1106  if (m_backprojection==1 || m_backprojection==2)
1107  {
1108  INTNB numCounts = (INTNB)round(temp_eventValue);
1109  // the number of counts must be integer for this backprojection,
1110  // otherwise events tracking gets complicated
1111  assert(round(temp_eventValue)==temp_eventValue);
1112 
1113  // process acquired counts one by one
1114  for (int e=0; e<numCounts; e++)
1115  {
1116  // remove the count backprojected at the previous iteration from all the variables
1117  // if backprojected into random/scatter there is nothing to remove
1118  if (a_iteration>0 && !(m4p_EventsBackprojectionTrace[a_bed][index][b][e]<0))
1119  {
1120  // voxel into which the count was backprojected at the previous iteration
1121  INTNB previous_voxel = m4p_EventsBackprojectionTrace[a_bed][index][b][e];
1122  INTNB previous_voxel_current_cluster = mp_voxelClusterMapping[previous_voxel];
1123 
1124  // remove this count from the backprojected complete data
1125  if (mp_permanentBackwardImage!=NULL)
1126  {
1127  mp_ImageSpace->m6p_backwardImage[1][th][tbf][rbf][cbf][previous_voxel] -= 1.;
1128  }
1129 
1130  if (m_backprojection==2)
1131  {
1132  // update cluster sums of backprojected counts, because this variable is used in marginalized backprojection,
1133  // only the current thread is updated, modifications in other threads can't be taken into account,
1134  // so approximative implementation when using more than one thread
1135  mp_clusterN[th][previous_voxel_current_cluster] -= 1.;
1136  assert(mp_clusterN[th][previous_voxel_current_cluster]>-0.001);
1137  }
1138  }
1139 
1140  p_ChronoManager->StartCustomStep(th,3);
1141  HPFLTNB temp_value = 0.;
1142 
1143  // compute probabilities for backprojection
1144  INTNB current_nb_voxels = line->GetCurrentNbVoxels(FORWARD,b);
1145  vector<HPFLTNB> voxelProbabilities(current_nb_voxels+1,0.);
1146  HPFLTNB probabilities_sum = 0.;
1147 
1148  INTNB temp_voxelIndex = -1;
1149  // voxels outcomes
1150  if (a_iteration==0)
1151  {
1152  // first iteration : if the initial image = 1, the probability becomes aij
1153  for (int vl=0; vl<current_nb_voxels; vl++)
1154  {
1155  temp_voxelIndex = line->GetVoxelIndex(FORWARD,b,vl);
1156  temp_value = (!mp_listRelevantVoxelIndices[temp_voxelIndex]) ? 0. : line->GetVoxelWeights(FORWARD,b,vl) * multiplicative_corr * global_basis_coef;
1157 
1158  probabilities_sum += temp_value;
1159  voxelProbabilities.at(vl+1) = temp_value;
1160  }
1161  }
1162  else
1163  {
1164  // voxels outcomes
1165  for (int vl=0; vl<current_nb_voxels; vl++)
1166  {
1167  temp_voxelIndex = line->GetVoxelIndex(FORWARD,b,vl);
1168  // Aij (system matrix element) * expectation of conditional cluster intensity
1169  temp_value = (!mp_listRelevantVoxelIndices[temp_voxelIndex]) ? 0. : line->GetVoxelWeights(FORWARD,b,vl) * multiplicative_corr * global_basis_coef;
1170  if (m_backprojection==2)
1171  { // multinomial backprojection marginalized over cluster intensity
1172  temp_value *= ((mp_clusterN[th][mp_voxelClusterMapping[temp_voxelIndex]] + m_gammaShape)
1174  }
1175  else
1176  { // multinomial backprojection
1177  temp_value *= mp_ImageSpace->m4p_forwardImage[tbf][rbf][cbf][temp_voxelIndex];
1178  }
1179 
1180  probabilities_sum += temp_value;
1181  voxelProbabilities.at(vl) = temp_value;
1182  }
1183  }
1184 
1185 
1186  // random and scatter counts as the last outcome
1187  temp_value = event->GetAdditiveCorrections(b) * mp_ID->GetFrameDurationInSec(a_bed, timeFrame);
1188  voxelProbabilities[current_nb_voxels] = temp_value;
1189  probabilities_sum += temp_value;
1190 
1191  p_ChronoManager->StopCustomStep(th,3);
1192  assert(!(probabilities_sum<0.));
1193  // if the probability distribution is not empty for this line
1194  if (probabilities_sum>0.)
1195  {
1196  // get a random number from uniform distribution and multiply it by the sum of probabilities,
1197  // faster than normalizing probabilities before sampling
1198  //HPFLTNB current_random = gsl_rng_uniform(mpp_threadRandomGenerators[th])*probabilities_sum;
1199  HPFLTNB current_random = sRandomNumberGenerator::GetInstance()->GenerateRdmNber()*probabilities_sum;
1200  HPFLTNB cumulative_sum = 0.;
1201  INTNB sampledIndex = 0;
1202  for (sampledIndex=current_nb_voxels; sampledIndex>=0; sampledIndex--)
1203  {
1204  cumulative_sum += voxelProbabilities[sampledIndex];
1205  if (cumulative_sum>current_random) break;
1206  }
1207 
1208  // write the realization into the corresponding voxel and update other variables
1209  if (sampledIndex>=0 && sampledIndex<current_nb_voxels)
1210  {
1211  INTNB drawn_voxel_index = line->GetVoxelIndex(FORWARD,b,sampledIndex);
1212  INTNB new_cluster = mp_voxelClusterMapping[drawn_voxel_index];
1213 
1214  // add the count into the backprojected complete data
1215  mp_ImageSpace->m6p_backwardImage[0][th][tbf][rbf][cbf][drawn_voxel_index] += 1.;
1216 
1217  if (m_backprojection==2)
1218  {
1219  // update cluster sums of backprojected counts, because this variable is used in the backprojection
1220  mp_clusterN[th][new_cluster] += 1.;
1221  }
1222 
1223  // update backprojection trace for the next iteration/subset
1224  m4p_EventsBackprojectionTrace[a_bed][index][b][e] = drawn_voxel_index;
1225  }
1226  else // random/scatter outcome
1227  {
1228  // flag random/scatter outcome, not really used at present
1229  m4p_EventsBackprojectionTrace[a_bed][index][b][e] = -2;
1230  }
1231  }
1232  }
1233  }
1234  else // ordinary multinomial backprojection step, overwrites all the backprojection variables
1235  {
1236  p_ChronoManager->StartCustomStep(th,3);
1237  HPFLTNB temp_value = 0.;
1238 
1239  // Compute categorical probability distribution over voxels contributing to the line
1240  // and the scatter/random source contributing to the line;
1241  // The probabilities are not normalized, the normalization factor is taken into
1242  // account during sampling
1243  INTNB current_nb_voxels = line->GetCurrentNbVoxels(FORWARD,b);
1244  vector<HPFLTNB> voxelProbabilities(current_nb_voxels+1,0.);
1245  HPFLTNB probabilities_sum = 0.;
1246 
1247  // voxels
1248  for (int vl=0; vl<current_nb_voxels; vl++)
1249  {
1250  INTNB temp_voxelIndex = line->GetVoxelIndex(FORWARD,b,vl);
1251  temp_value = (!mp_listRelevantVoxelIndices[temp_voxelIndex]) ? 0. :line->GetVoxelWeights(FORWARD,b,vl) * multiplicative_corr * global_basis_coef
1252  * mp_ImageSpace->m4p_forwardImage[tbf][rbf][cbf][temp_voxelIndex];
1253  //* mp_clusterValues[mp_voxelClusterMapping[temp_voxelIndex]];
1254 
1255  probabilities_sum += temp_value;
1256  voxelProbabilities.at(vl) = temp_value;
1257  }
1258 
1259  // random and scatter counts as the last outcome
1260  temp_value = event->GetAdditiveCorrections(b) * mp_ID->GetFrameDurationInSec(a_bed, timeFrame);
1261  voxelProbabilities[current_nb_voxels] = temp_value;
1262  probabilities_sum += temp_value;
1263 
1264  assert(!(probabilities_sum<0.));
1265  p_ChronoManager->StopCustomStep(th,3);
1266 
1267  // if the probability distribution is not empty for this line
1268  if (probabilities_sum>0.)
1269  {
1270  p_ChronoManager->StartCustomStep(th,4);
1271  INTNB numCounts = (INTNB)round(temp_eventValue);
1272  // if the number of counts is not integer (might happen for GE PET/MR sinogram data),
1273  // pick randomly floor or ceil rounding
1274  if (fabs(round(temp_eventValue)-temp_eventValue)>1.e-7)
1275  {
1277  {
1278  numCounts = (INTNB)ceil(temp_eventValue);
1279  }
1280  else
1281  {
1282  numCounts = (INTNB)floor(temp_eventValue);
1283  }
1284  }
1285  p_ChronoManager->StopCustomStep(th,4);
1286  p_ChronoManager->StartCustomStep(th,5);
1287  // the number of repetitions for the multinomial distribution = event value
1288  for (int e=0; e<numCounts; e++)
1289  {
1290  // get a random number from uniform distribution and multiply it by the sum of probabilities,
1291  // faster than normalizing probabilities before sampling
1292  //HPFLTNB current_random = gsl_rng_uniform(mpp_threadRandomGenerators[th])*probabilities_sum;
1293  HPFLTNB current_random = sRandomNumberGenerator::GetInstance()->GenerateRdmNber()*probabilities_sum;
1294  HPFLTNB cumulative_sum = 0.;
1295  INTNB sampledIndex = 0;
1296  // faster if start from the random/scatter outcome, which probably has higher probability
1297  for (sampledIndex=current_nb_voxels; sampledIndex>=0; sampledIndex--)
1298  {
1299  cumulative_sum += voxelProbabilities[sampledIndex];
1300  if (cumulative_sum>current_random) break;
1301  }
1302  // write the realization into the corresponding voxel, and don't do anything if the sampled index refers to
1303  // random and scatter source (the last probability in the list)
1304  if (sampledIndex>=0 && sampledIndex<current_nb_voxels)
1305  {
1306  mp_ImageSpace->m6p_backwardImage[0][th][tbf][rbf][cbf][line->GetVoxelIndex(FORWARD,b,sampledIndex)] += 1.;
1307  }
1308  else if (sampledIndex<0)
1309  {
1310  // check : if nothing sampled throw a warning, though it should not happen
1311  Cout("Warning: Nothing sampled, sampledIndex "<<sampledIndex<<", current_nb_voxels "<<current_nb_voxels<<endl);
1312  }
1313  }
1314  p_ChronoManager->StopCustomStep(th,5);
1315  }
1316  }
1317  }
1318  }
1319  }
1320  }
1321  }
1322  }
1323  } // End of events loop (OpenMP stops here)
1324 
1325  // Synchronize MPI processes
1326  #ifdef CASTOR_MPI
1327  MPI_Barrier(MPI_COMM_WORLD);
1328  #endif
1329 
1330  // Set back the number of threads for image computation
1331  #ifdef CASTOR_OMP
1332  omp_set_num_threads(mp_ID->GetNbThreadsForImageComputation());
1333  #endif
1334 
1335  // End of progression printing (do not log out with Cout here)
1336  if (m_verbose>=2 && mp_ID->GetMPIRank()==0)
1337  {
1338  int progression_total_bars = 49;
1339  for (int i=0; i<progression_total_bars-progression_nb_bars; i++) cout << "-";
1340  cout << "|" << endl;
1341  }
1342 
1343  // If a problem was encountered, then report it here
1344  if (problem)
1345  {
1346  Cerr("***** iRCPGSAlgorithm::SampleConditionalCompleteData() -> A problem occured inside the parallel loop over events !" << endl);
1347  return 1;
1348  }
1349 
1350  // End
1351  return 0;
1352 }
1353 
1354 // -----------------------------------------------------------------------------------------------------------------------------------------
1355 
1357 {
1358  if (m_verbose>=2) Cout("iRCPGSAlgorithm::ComputeSumsPerClusters()... " << endl);
1359 
1360  bool multimodal_info = mp_ImageSpace->IsLoadedMultiModal();
1361 
1362  // reinitialize and compute sums over clusters
1363  // sum of nij over clusters must be recomputed at each iteration, because nij have been resampled
1364  // recomputing sensitivity sum over clusters is not compulsory because the sensitivity is constant over iterations
1365  // and the sums are updated at each clustering sampling, but it is done nevertheless, in order to avoid accumulations of numerical errors
1366 
1367  // marginalized backprojection needs threaded cluster sums
1368  INTNB th_clusterN = (m_backprojection==2)?mp_ID->GetNbThreadsForProjection():1;
1369 
1370  // reinitialize
1371  INTNB vp;
1372  #pragma omp parallel for private(vp) schedule(guided)
1373  for (vp=0; vp<(mp_ID->GetNbVoxXYZ()+1); vp++)
1374  {
1375  mp_clusterN[0][vp] = 0.;
1376  for (INTNB th=1;th<th_clusterN;th++) mp_clusterN[th][vp] = 0.;
1377  mp_clusterSensitivity[vp] = 0.;
1378  if (multimodal_info)
1379  {
1380  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) mpp_clusterMultiModal[mmnb][vp] = 0.;
1381  mp_clusterCount[vp] = 0;
1382  }
1383  }
1384 
1385  // sum using voxel to cluster mapping
1386  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1387  {
1388  // skip if background voxel
1389  if (!mp_listRelevantVoxelIndices[v]) continue;
1390 
1391  if (mp_permanentBackwardImage!=NULL)
1392  {
1393  assert(mp_permanentBackwardImage[v]>=-0.0001);
1395  }
1396  else
1397  {
1398  assert(mp_ImageSpace->m6p_backwardImage[0][0][0][0][0][v]>=0.);
1400  }
1401 
1402  assert(mp_ImageSpace->m5p_sensitivity[0][0][0][0][v]>=0.);
1404  if (multimodal_info)
1405  {
1406  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1407  {
1408  mpp_clusterMultiModal[mmnb][mp_voxelClusterMapping[v]] += mp_ImageSpace->m2p_multiModalImage[mmnb][v] ;
1409  }
1410  mp_clusterCount[mp_voxelClusterMapping[v]] += 1;
1411  }
1412  }
1413 
1414  // End
1415  return 0;
1416 }
1417 
1418 // -----------------------------------------------------------------------------------------------------------------------------------------
1419 
1421 {
1422  // Verbose
1423  if (m_verbose>=3) Cout("iRCPGSAlgorithm::UpdateVisitedVoxels() -> Tick visited voxels based on sensitivity" << endl);
1424 
1425  // zeros in the sensitivity map can be used to detect visited voxels, though not needed in this algorithm?
1426  int count=0;
1427  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1428  {
1429  // Use the sensitivity image to find visited voxels (we just add 1 to discriminate between visited and not visited voxels)
1430  if (mp_ImageSpace->m5p_sensitivity[0][0][0][0][v]>0.) mp_ImageSpace->mp_visitedVoxelsImage[v] += 1.;
1431  else count++;
1432  }
1433  if (m_verbose>=2 && count>0) Cout(" --> Invisible voxels : "<<count<<" over "<<mp_ID->GetNbVoxXYZ()<<" !"<<endl);
1434 
1435  // End
1436  return 0;
1437 }
1438 
1439 // -----------------------------------------------------------------------------------------------------------------------------------------
1440 
1442 {
1443  if (m_verbose>=2) Cout("iRCPGSAlgorithm::SampleConditionalClustering()... " << endl);
1444  // voxels eligible for assignment to the same cluster (here simple voxel neighbourhood)
1445  // variable size with maximum at m_neighbourhood+1
1446 
1447  //HPFLTNB mean_proba = 0., mean_proba_prev = 0., nb_proba = 0., std_proba = 0.;
1448 
1449  bool multimodal_info = mp_ImageSpace->IsLoadedMultiModal();
1450 
1451  vector<INTNB> fellow_voxels;
1452  fellow_voxels.reserve(m_neighbourhood+1);
1453 
1454  // non-normalized probabilities for the sampling of the next link
1455  // variable size with maximum at m_neighbourhood+1
1456  vector<HPFLTNB> probabilities;
1457  probabilities.reserve(m_neighbourhood+1);
1458 
1459  // voxels belonging to the same cluster as the current voxel,
1460  // after the next link of the current voxel has been cut (all parent voxels)
1461  vector<INTNB> voxels_after_cut;
1462 
1463  // dimensions
1464  INTNB dim_3D = mp_ID->GetNbVoxXYZ();
1465 
1466  // randomize voxel indices before sampling the ddCRP
1467  //random_shuffle(mp_listVoxelIndices, mp_listVoxelIndices+dim_3D);
1468  shuffle(mp_listVoxelIndices, mp_listVoxelIndices+dim_3D, sRandomNumberGenerator::GetInstance()->GetExtraGenerator(1));
1469 
1470  // resample voxels links
1471  for(int v=0; v<dim_3D; v++)
1472  {
1473  const int current_voxel = mp_listVoxelIndices[v];
1474 
1475  // skip if background voxel
1476  if (!mp_listRelevantVoxelIndices[current_voxel]) continue;
1477 
1478  const int previous_cluster = mp_voxelClusterMapping[current_voxel];
1479 
1480  // temporary new cluster
1481  int temp_new_cluster = mv_newClusters.at(mv_newClusters.size()-1);
1482  mv_newClusters.pop_back();
1483  assert( temp_new_cluster<=dim_3D );
1484  assert(temp_new_cluster!=previous_cluster);
1485 
1486  // get voxel indices in the neighbourhood
1487  ComputeFellowVoxelsList(fellow_voxels, current_voxel);
1488 
1489  // explore the parent links of the current voxel, check whether these links make a loop,
1490  // and build the list of voxels connected to the current voxel by parent links
1491  bool loop = false;
1492  voxels_after_cut.resize(0);
1493 
1494  // lambda function for optimization purposes
1495  function<void (int)> explorePreviousLinks= [this,&explorePreviousLinks,current_voxel,&voxels_after_cut,&loop] (int index)
1496  {
1497  voxels_after_cut.push_back(index);
1498  for(int ip: mpv_parentLinks[index])
1499  {
1500  if (ip!=current_voxel) explorePreviousLinks(ip);
1501  else loop=true;
1502  }
1503  };
1504  explorePreviousLinks(current_voxel);
1505  assert( voxels_after_cut.size()>0 );
1506 
1507  // cut the next (child) link of the current voxel, and update parent links
1508  size_t previous_next_link = mp_nextLink[current_voxel];
1509  for(size_t p=0; p<mpv_parentLinks[previous_next_link].size(); p++)
1510  {
1511  if (mpv_parentLinks[previous_next_link][p]==current_voxel)
1512  {
1513  mpv_parentLinks[previous_next_link].erase(mpv_parentLinks[previous_next_link].begin()+p);
1514  break;
1515  }
1516  }
1517 
1518  // assign the temporary new cluster to the current voxel and to all its parent voxels
1519  // compute also modifications of sums over the cluster
1520  HPFLTNB temp_count_N = 0.;
1521  HPFLTNB temp_count_sens = 0.;
1522  INTNB temp_count = 0;
1523  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) temp_count_multimodal[mmnb] = 0.;
1524 
1525  for(int nv : voxels_after_cut)
1526  {
1527  if (mp_permanentBackwardImage!=NULL) temp_count_N += mp_permanentBackwardImage[nv];
1528  else temp_count_N += mp_ImageSpace->m6p_backwardImage[0][0][0][0][0][nv];
1529 
1530  temp_count_sens += mp_ImageSpace->m5p_sensitivity[0][0][0][0][nv];
1531  if (multimodal_info)
1532  {
1533  temp_count++;
1534  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1535  {
1537  assert (temp_count_multimodal[mmnb]>=0.);
1538  }
1539  }
1540  mp_voxelClusterMapping[nv] = temp_new_cluster;
1541  }
1542  assert (temp_count_N>=0. && temp_count_sens>=0.);
1543  assert (temp_count>=0);
1544 
1545  // if this cluster contains the same voxels as the previous cluster (looped links),
1546  // free the previous cluster, make it available for assignment
1547  // (all these voxels have already been assigned to the temporary new cluster)
1548  if (loop) mv_newClusters.push_back(previous_cluster);
1549 
1550  // update sums for the temporary new cluster and the remaining part of the previous_cluster (might be empty)
1551  mp_clusterN[0][previous_cluster] -= temp_count_N;
1552  mp_clusterSensitivity[previous_cluster] -= temp_count_sens;
1553  mp_clusterN[0][temp_new_cluster] = temp_count_N;
1554  mp_clusterSensitivity[temp_new_cluster] = temp_count_sens;
1555 
1556  assert(mp_clusterN[0][previous_cluster]>=0.);
1557  //if (mp_clusterSensitivity[previous_cluster]<-1.e-11) Cout("WARNING mp_clusterSensitivity[previous_cluster]<0! : "<<mp_clusterSensitivity[previous_cluster]<<" cluster idx "<<previous_cluster<<" temp_count_sens "<<temp_count_sens<<endl);
1558  assert(mp_clusterN[0][temp_new_cluster]>=0.);
1559  //if (mp_clusterSensitivity[temp_new_cluster]<-1.e-11) Cout("WARNING mp_clusterSensitivity[temp_new_cluster]<0! : "<<mp_clusterSensitivity[temp_new_cluster]<<" cluster idx "<<temp_new_cluster<<" temp_count_sens "<<temp_count_sens<<endl);
1560 
1561  if (multimodal_info)
1562  {
1563  mp_clusterCount[previous_cluster] -= temp_count;
1564  mp_clusterCount[temp_new_cluster] = temp_count;
1565  assert(mp_clusterCount[previous_cluster]>=0);
1566  assert(mp_clusterCount[temp_new_cluster]>=0);
1567  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1568  {
1569  mpp_clusterMultiModal[mmnb][previous_cluster] -= temp_count_multimodal[mmnb];
1570  mpp_clusterMultiModal[mmnb][temp_new_cluster] = temp_count_multimodal[mmnb];
1571  assert(mpp_clusterMultiModal[mmnb][previous_cluster]>=0.);
1572  assert(mpp_clusterMultiModal[mmnb][temp_new_cluster]>=0.);
1573  }
1574  }
1575 
1576  // compute categorical probabilities for sampling the next link for the current voxel
1577  probabilities.resize(0);
1578 
1579  // add self-link if alpha not zero
1580  if (m_ddcrpAlpha>0.) probabilities.push_back(m_ddcrpLogAlpha);
1581 
1582  for(size_t fv=1; fv<fellow_voxels.size(); fv++)
1583  {
1584  int fellow_cluster = mp_voxelClusterMapping[fellow_voxels.at(fv)];
1585  if(fellow_cluster!=temp_new_cluster)
1586  {
1587  // probability of a link that will cause the fusion of two clusters
1588  HPFLTNB n0 = mp_clusterN[0][temp_new_cluster];
1589  HPFLTNB n1 = mp_clusterN[0][fellow_cluster];
1590  HPFLTNB s0 = mp_clusterSensitivity[temp_new_cluster];
1591  HPFLTNB s1 = mp_clusterSensitivity[fellow_cluster];
1592 
1593  assert (!(n0<0. || n1<0. || s0<0. || s1<0.));
1594 
1595  // ln (probability) to avoid numerical issues
1596  HPFLTNB temp = (n0+m_gammaShape)*log(s0+m_gammaRate)+(n1+m_gammaShape)*log(s1+m_gammaRate)-(n0+n1+m_gammaShape)*log(s0+s1+m_gammaRate)
1597  -lgamma(n0+m_gammaShape)-lgamma(n1+m_gammaShape)+lgamma(n0+n1+m_gammaShape)
1598  +lgamma(m_gammaShape)-m_gammaShape*log(m_gammaRate);
1599 
1600  // compute and add the multimodal influence only if beyond the specified number of iterations
1601  if (multimodal_info && a_iteration>=m_multiModalLag)
1602  {
1603  HPFLTNB c0 = mp_clusterCount[temp_new_cluster];
1604  HPFLTNB c1 = mp_clusterCount[fellow_cluster];
1605  assert (c0>0. && c1>0.);
1606  HPFLTNB help1 = c0*c1/(c0+c1);
1607 
1608  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1609  {
1610  HPFLTNB a0 = mpp_clusterMultiModal[mmnb][temp_new_cluster];
1611  HPFLTNB a1 = mpp_clusterMultiModal[mmnb][fellow_cluster];
1612  assert (a0>=0. && a1>=0.);
1613  HPFLTNB help2 = a0/c0-a1/c1;
1614 
1615  HPFLTNB temp_mm = (-log(mp_multiModalParam[mmnb]) - 0.5*( (help2*help2*help1)/(mp_multiModalNoiseSigma[mmnb]*mp_multiModalNoiseSigma[mmnb]) - log(help1) ));
1616 
1617  // if subsets, divide the MRI log influence by the number of subsets only if ordinary OSEM-like backprojection
1618  if (mp_nbSubsets[a_iteration]>1 && m_backprojection==0) temp_mm /= (HPFLTNB)mp_nbSubsets[a_iteration];
1619 
1620  // add the multimodal influence
1621  temp += temp_mm;
1622 
1623  }
1624  }
1625  assert (!std::isnan(temp));
1626  probabilities.push_back(temp);
1627  /*
1628  mean_proba += temp;
1629  nb_proba += 1.;
1630  std_proba += (temp-mean_proba_prev)*(temp-mean_proba_prev);
1631  */
1632  }
1633  else
1634  {
1635  // probability of a link which does not alter clusters, a link which loops into the same cluster (log(1))
1636  if (m_ddCRP==1)
1637  probabilities.push_back (0.);
1638  else if (m_ddCRP==2)
1639  probabilities.push_back(m_ddcrpLogAlpha);
1640  }
1641  }
1642 
1643  // apply exponential to log probabilities (tip to avoid numerical issues)
1644  HPFLTNB probabilities_sum=0.;
1645  for(size_t p=0; p<probabilities.size(); p++)
1646  {
1647  probabilities[p] = exp(probabilities[p]);
1648  assert (!std::isnan(probabilities[p]));
1649  assert (!std::isinf(probabilities[p]));
1650  probabilities_sum += probabilities[p];
1651  assert (!std::isinf(probabilities_sum));
1652  }
1653 
1654 
1655  INTNB p=0;
1656  // sample the categorical distribution (multinomial distribution with a single repetition)
1657  // if at least one probability greater than the HPFLTNB minimum
1658  if (probabilities_sum>std::numeric_limits<HPFLTNB>::min())
1659  {
1660  HPFLTNB current_random = sRandomNumberGenerator::GetInstance()->GenerateExtraRdmNber(0)*probabilities_sum;
1661  HPFLTNB cumulative_sum = 0.;
1662  for(p=0; p<(INTNB)probabilities.size(); p++)
1663  {
1664  cumulative_sum += probabilities[p];
1665  if(cumulative_sum>current_random) break;
1666  }
1667  }
1668  // choose a self link in the (rare) cases when the probabilities are too low to be represented by HPFLTNB
1669  // or there are no or few fellow voxels (might occur with a mask)
1670  // this can happen only if alpha is zero and hasn't been added to the list of probabilities
1671  else
1672  {
1673  assert(!(m_ddcrpAlpha>0.));
1674  p = -1;
1675  }
1676 
1677  assert(p<(INTNB)probabilities.size());
1678 
1679  // if alpha is zero, the self link probability wasn't added in the list of probabilities,
1680  // so there is an index shift of 1 with respect to the list of fellow voxels
1681  if (!(m_ddcrpAlpha>0.)) p+=1;
1682 
1683  // take into account the new next link : update parent and next links
1684  int new_next_link = fellow_voxels.at(p);
1685 
1686  mpv_parentLinks[new_next_link].push_back(current_voxel);
1687  mp_nextLink[current_voxel] = new_next_link;
1688 
1689  // take into account the new next link : update clusters
1690  int new_next_link_cluster = mp_voxelClusterMapping[new_next_link];
1691  // if the new link does not cause clusters fusion, there is nothing to do, the temporary new cluster
1692  // becomes the actual new cluster
1693  // if the new link causes clusters fusion, assign all the voxels to the cluster of the next link voxel
1694  // and release the temp_new_cluster as empty and free for assignment, and update sums per clusters
1695  if (new_next_link_cluster!=temp_new_cluster)
1696  {
1697  // update sums
1698  mp_clusterN[0][new_next_link_cluster] += mp_clusterN[0][temp_new_cluster];
1699  mp_clusterSensitivity[new_next_link_cluster] += mp_clusterSensitivity[temp_new_cluster];
1700 
1701  assert(mp_clusterN[0][new_next_link_cluster]>=0.);
1702  assert(mp_clusterSensitivity[new_next_link_cluster]>=0.);
1703 
1704  if (multimodal_info)
1705  {
1706  mp_clusterCount[new_next_link_cluster] += mp_clusterCount[temp_new_cluster];
1707  assert(mp_clusterCount[new_next_link_cluster]>=0);
1708  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1709  {
1710  mpp_clusterMultiModal[mmnb][new_next_link_cluster] += mpp_clusterMultiModal[mmnb][temp_new_cluster];
1711  assert(mpp_clusterMultiModal[mmnb][new_next_link_cluster]>=0.);
1712  }
1713  }
1714 
1715  // release the temporary new cluster
1716  mv_newClusters.push_back(temp_new_cluster);
1717  // assign all the fusioned voxels to the cluster of the child voxel
1718  for(size_t fv=0; fv<voxels_after_cut.size(); fv++)
1719  {
1720  mp_voxelClusterMapping[voxels_after_cut.at(fv)] = new_next_link_cluster;
1721  }
1722  }
1723  }
1724 
1725  /*
1726  mean_proba/=nb_proba;
1727  mean_proba_prev = mean_proba;
1728  std_proba = sqrt(std_proba/nb_proba);
1729  Cout("iRCPGSAlgorithm::SampleConditionalClustering() -> fusion probabilities mean "<< mean_proba<<" std "<<std_proba<<endl);
1730  */
1731 
1732  // End
1733  return 0;
1734 }
1735 
1736 // -----------------------------------------------------------------------------------------------------------------------------------------
1737 
1739 {
1740  if (m_verbose>=2) Cout("iRCPGSAlgorithm::SampleConditionalClusterIntensity()... " << endl);
1741 
1742  // reinitialize clusters values to -1,
1743  // trick for ensuring that the value of each cluster is sampled only once
1744  int vp;
1745  #pragma omp parallel for private(vp) schedule(guided)
1746  for (vp=0; vp<(mp_ID->GetNbVoxXYZ()+1); vp++) mp_clusterValues[vp] = -1.;
1747 
1748  INTNB nb_clusters = 0; INTNB count_empty = 0; HPFLTNB maxN = 0.; INTNB nb_relevant_voxels = 0;
1749  // sample voxel intensity for each cluster, using the updated Gamma distribution
1750  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1751  {
1752  // skip if background voxel
1753  if (!mp_listRelevantVoxelIndices[v]) continue;
1754 
1755  nb_relevant_voxels ++;
1756 
1757  int index = mp_voxelClusterMapping[v];
1758 
1759  if (m_ddCRP==0) assert(index==v);
1760 
1761  if (mp_clusterValues[index]<0.)
1762  {
1763  gamma_distribution<HPFLTNB> updated_gamma_distrib(mp_clusterN[0][index] + m_gammaShape, 1./(mp_clusterSensitivity[index] + m_gammaRate));
1764  mp_clusterValues[index] = updated_gamma_distrib(sRandomNumberGenerator::GetInstance()->GetExtraGenerator(1));
1765 
1766  assert(mp_clusterN[0][index]>=0. && mp_clusterSensitivity[index]>=0.);
1767  assert(!std::isnan(mp_clusterValues[index]) && !std::isinf(mp_clusterValues[index]) && mp_clusterValues[index]>=0.);
1768 
1769  nb_clusters++;
1770  if (mp_clusterValues[index]>maxN) maxN = mp_clusterValues[index];
1771  if (!(mp_clusterN[0][index]>0.)) count_empty++;
1772  }
1773  }
1774 
1775  // compute the current average cluster volume
1776  m_currentMeanClusterVolume = mp_ID->GetVoxSizeX() * mp_ID->GetVoxSizeY() * mp_ID->GetVoxSizeZ()* (FLTNB)nb_relevant_voxels / (FLTNB)nb_clusters;
1777 
1778  if (m_verbose>=2)
1779  {
1780  Cout(" --> Mean number of voxels per cluster = "<<((FLTNB)nb_relevant_voxels/(FLTNB)nb_clusters)<<endl);
1781  Cout(" --> Average cluster volume = "<<m_currentMeanClusterVolume<<" mm3"<<endl);
1782  }
1783 
1784  // End
1785  return 0;
1786 }
1787 
1788 // -----------------------------------------------------------------------------------------------------------------------------------------
1789 
1790 int iRCPGSAlgorithm::ComputeFellowVoxelsList(vector<INTNB>& a_fellow_voxels, int a_current_voxel)
1791 {
1792  // X,Y,Z dimensions, ordered according to the array storage :
1793  // X is the innermost and Z is the outermost dimension
1794  INTNB dim_3D = mp_ID->GetNbVoxXYZ();
1795  INTNB dim_X = mp_ID->GetNbVoxX();
1796  INTNB dim_Z = mp_ID->GetNbVoxZ();
1797  INTNB dim_XY = mp_ID->GetNbVoxXY();
1798 
1799  // build the list of fellow voxels for the current voxel
1800  a_fellow_voxels.resize(0);
1801  // add first the current voxel itself
1802  a_fellow_voxels.push_back(a_current_voxel);
1803 
1804  if (dim_Z==1)
1805  {
1806  // build the list of 2D neighbours excluding diagonal voxels
1807  const int ix = a_current_voxel%dim_X;
1808  bool y_low = (a_current_voxel>=dim_X) && mp_listRelevantVoxelIndices[a_current_voxel-dim_X];
1809  bool y_high = (a_current_voxel<dim_XY-dim_X) && mp_listRelevantVoxelIndices[a_current_voxel+dim_X];
1810  bool x_low = (ix>0) && mp_listRelevantVoxelIndices[a_current_voxel-1];
1811  bool x_high = (ix<dim_X-1) && mp_listRelevantVoxelIndices[a_current_voxel+1];
1812 
1813  if (y_low) a_fellow_voxels.push_back(a_current_voxel-dim_X);
1814  if (y_high) a_fellow_voxels.push_back(a_current_voxel+dim_X);
1815  if (x_low) a_fellow_voxels.push_back(a_current_voxel-1);
1816  if (x_high) a_fellow_voxels.push_back(a_current_voxel+1);
1817 
1818  for(size_t i=0;i<a_fellow_voxels.size();i++) assert(a_fellow_voxels.at(i)>=0 && a_fellow_voxels.at(i)<dim_3D );
1819  assert(a_fellow_voxels.size()>0);
1820  }
1821  else
1822  {
1823  // build the list of 3D neighbours excluding diagonal voxels
1824  const int current_voxel_xy = a_current_voxel%(dim_XY);
1825  // perform the same processing as for 2D on current_voxel_xy
1826  const int ix = current_voxel_xy%dim_X;
1827  bool y_low = (current_voxel_xy>=dim_X) && mp_listRelevantVoxelIndices[a_current_voxel-dim_X];
1828  bool y_high = (current_voxel_xy<dim_XY-dim_X) && mp_listRelevantVoxelIndices[a_current_voxel+dim_X];
1829  bool x_low = (ix>0) && mp_listRelevantVoxelIndices[a_current_voxel-1];
1830  bool x_high = (ix<dim_X-1) && mp_listRelevantVoxelIndices[a_current_voxel+1];
1831  // check z dimension
1832  bool z_low = (a_current_voxel >= dim_XY) && mp_listRelevantVoxelIndices[a_current_voxel-dim_XY];
1833  bool z_high = (a_current_voxel < dim_3D-dim_XY) && mp_listRelevantVoxelIndices[a_current_voxel+dim_XY];
1834 
1835  if (y_low) a_fellow_voxels.push_back(a_current_voxel-dim_X);
1836  if (y_high) a_fellow_voxels.push_back(a_current_voxel+dim_X);
1837  if (x_low) a_fellow_voxels.push_back(a_current_voxel-1);
1838  if (x_high) a_fellow_voxels.push_back(a_current_voxel+1);
1839  if (z_low) a_fellow_voxels.push_back(a_current_voxel-dim_XY);
1840  if (z_high) a_fellow_voxels.push_back(a_current_voxel+dim_XY);
1841 
1842  for(size_t i=0;i<a_fellow_voxels.size();i++) assert(a_fellow_voxels.at(i)>=0 && a_fellow_voxels.at(i)<dim_3D );
1843  assert(a_fellow_voxels.size()>0);
1844  }
1845 
1846  // End
1847  return 0;
1848 }
1849 
1850 // -----------------------------------------------------------------------------------------------------------------------------------------
1851 
1853 {
1854  // Loop on voxels with OpenMP
1855  int vp;
1856  #pragma omp parallel for private(vp) schedule(guided)
1857  for (vp=0; vp<mp_ID->GetNbVoxXYZ(); vp++)
1858  {
1859  if (!mp_listRelevantVoxelIndices[vp])
1860  {
1861  mp_ImageSpace->m4p_image[0][0][0][vp] = 0.;
1862  }
1863  else
1864  {
1865  // Compute output image value
1867  }
1868  }
1869 
1870  // End
1871  return 0;
1872 }
1873 
1874 // =====================================================================
1875 // ---------------------------------------------------------------------
1876 // ---------------------------------------------------------------------
1877 // =====================================================================
1878 
1880 {
1882  {
1883  Cerr("***** iRCPGSAlgorithm::StepAfterIterationLoop() -> A problem occurred while calling StepAfterIterationLoop() function !" << endl);
1884  return 1;
1885  }
1886  if (m_verbose>=2) Cout("iRCPGSAlgorithm::StepAfterIterationLoop ... " << endl);
1887 
1889 
1890  // Normal end
1891  return 0;
1892 }
1893 
1894 // =====================================================================
1895 // ---------------------------------------------------------------------
1896 // ---------------------------------------------------------------------
1897 // =====================================================================
1898 
1900 {
1901  (void)a_iteration; // avoid 'unused parameter' compil. warnings
1902  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepBeforeSubsetLoop ... " << endl);
1903 
1904  // End
1905  return 0;
1906 }
1907 
1908 // -----------------------------------------------------------------------------------------------------------------------------------------
1909 
1911 {
1912  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepAfterSubsetLoop() -> Clean never visited voxels and save images if needed" << endl);
1913  // Clean never visited voxels
1915  // Save the main image
1916  if (mp_ID->GetMPIRank()==0 && mp_outputIterations[a_iteration])
1917  {
1918  // Verbose
1919  if (m_verbose>=1) Cout("iRCPGSAlgorithm::StepAfterSubsetLoop() -> Save image at iteration " << a_iteration+1 << endl);
1920  // Save image
1921  if (StepImageOutput(a_iteration))
1922  {
1923  Cerr("***** iRCPGSAlgorithm::StepAfterSubsetLoop() -> A problem occurred while saving images at iteration " << a_iteration+1 << " !" << endl);
1924  return 1;
1925  }
1926  }
1927 
1928  // End
1929  return 0;
1930 }
1931 
1932 
1933 // =====================================================================
1934 // ---------------------------------------------------------------------
1935 // ---------------------------------------------------------------------
1936 // =====================================================================
1937 
1938 int iRCPGSAlgorithm::StepImageOutput(int a_iteration, int a_subset)
1939 {
1940  // =================================================================================================
1941  // Apply pre-processing steps
1942  // =================================================================================================
1943  // Copy the current image into the forward image
1945 
1946  // Apply output transaxial FOV masking
1948  {
1949  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> A problem occurred while applying output FOV masking !" << endl);
1950  return 1;
1951  }
1952  // Apply output flip
1954  {
1955  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> A problem occurred while applying output flip !" << endl);
1956  return 1;
1957  }
1958 
1959  // =================================================================================================
1960  // Save frames/gates
1961  // =================================================================================================
1962 
1963  // Save output image (note that if no basis functions at all are in use, the the output image already points to the forward image)
1964  if (mp_ImageSpace->SaveOutputImage(a_iteration, a_subset))
1965  {
1966  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> A problem occurred while saving output image !" << endl);
1967  return 1;
1968  }
1969 
1970  if (((a_iteration+1)%50)==0)
1971  {
1972  // Get the output manager
1973  sOutputManager* p_output_manager = sOutputManager::GetInstance();
1974 
1975  // ----------------------
1976  // Build the file name
1977  // ----------------------
1978 
1979  string data_file = p_output_manager->GetPathName() + p_output_manager->GetBaseName()+"_intermediary";
1980  // Add a suffix for iteration
1981  if (a_iteration >= 0)
1982  {
1983  stringstream ss; ss << a_iteration + 1;
1984  data_file.append("_it").append(ss.str());
1985  }
1986  // Add extension
1987  data_file.append(".img");
1988 
1989  // ----------------------
1990  // Write image in file
1991  // ----------------------
1992 
1993  // Open file
1994  FILE* fout = fopen(data_file.c_str(),"wb");
1995  if (fout==NULL)
1996  {
1997  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> Failed to create output file '" << data_file << "' !" << endl);
1998  return 1;
1999  }
2000 
2001  // Write the file by converting the data to the output type, currently no specific output type,
2002  // so the current data type is used
2003  INTNB nb_data = 0;
2004  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2005  {
2006  INTNB buffer = -1;
2007  if (mp_listRelevantVoxelIndices[v]) buffer = ((INTNB)(mp_voxelClusterMapping[v]));
2008  nb_data += fwrite(&buffer,sizeof(INTNB),1,fout);
2009  }
2010 
2011  // Close file
2012  fclose(fout);
2013 
2014  // Check writing
2015  if (nb_data!=mp_ID->GetNbVoxXYZ())
2016  {
2017  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> Failed to write all data into the output file '" << data_file << "' !" << endl);
2018  return 1;
2019  }
2020  }
2021 
2022  // Normal end
2023  return 0;
2024 }
2025 
2026 // =====================================================================
2027 // ---------------------------------------------------------------------
2028 // ---------------------------------------------------------------------
2029 // =====================================================================
2030 
2032 {
2033  cout << "" << endl;
2034  cout << "Random Clustering Prior - Gibbs Sampler : probabilistic Bayesian inference algorithm (no optimization)" << endl;
2035  cout << "M. Filipovic et al : PET reconstruction of the posterior image probability, including multimodal images." << endl;
2036  cout << "" << endl;
2037  cout << "The algorithm is launched using -prob, either with command line options (-prob RCPGS:option1name=option1value,option2name=option2value,...)" << endl;
2038  cout << "or with a config file (-prob RCPGS:config_file.conf), see also the default config file for examples of option values." << endl;
2039  cout << "" << endl;
2040  cout << "Required options :" << endl;
2041  cout << " ddCRP = type of ddCRP prior (0 = no ddCRP, 1 = original ddCRP (recommended), 2 = modified ddCRP)" << endl;
2042  cout << " alpha = ddCRP hyperparameter, the unnormalized probability of drawing a self link" << endl;
2043  cout << " gammaShape = Gamma prior distribution shape parameter (0.5 recommended)" << endl;
2044  cout << " gammaRate = Gamma prior distribution rate parameter (1.e-18 recommended)" << endl;
2045  cout << " backprojection = type of multinomial backprojection of the current iteration/subset data (0 = the previous backprojection state is cleared, as for ML-EM (recommended), 1 = update of the previous backprojection state, 2 = backprojection marginalized over cluster intensity, implies update of the previous backprojection state )" << endl;
2046  cout << " multiModalNoiseSigma = standard deviation of Gaussian noise in the provided additional image from another modality, must be repeated as many times as the -multimodal option" << endl;
2047  cout << " multiModalLag = number of iterations after which the multimodal images start affecting voxels clustering" << endl;
2048  cout << "" << endl;
2049  cout << "Optional options (if one of these is specified, all the others must be specified as well):" << endl;
2050  cout << " meanClusterVolumeMin = minimum threshold for average cluster volume (in mm3), used for tuning ddCRP alpha automatically through iterations" << endl;
2051  cout << " meanClusterVolumeMax = maximum threshold for average cluster volume (in mm3), used for tuning ddCRP alpha automatically through iterations" << endl;
2052  cout << " alphaIncrement = multiplicative increment for tuning ddCRP alpha through iterations" << endl;
2053  cout << "" << endl;
2054 }
void StopIterativeDataUpdateStep1(int a_thread)
Stop the timer for duration of iterative data update step 1.
FLTNB **** m4p_forwardImage
Definition: oImageSpace.hh:87
int UpdateVisitedVoxels()
Check for voxels which do not contribute to recorded observed data.
int GetCurrentCardImage(int a_th)
call the eponym function from the oDynamicDataManager object
int SampleConditionalClustering(int a_iteration)
Gibbs sampler : second conditional probability (ddCRP links)
HPFLTNB m_ddcrpAlphaIncrement
void GetEventIndexStartAndStop(int64_t *ap_indexStart, int64_t *ap_indexStop, int a_subsetNum=0, int a_NbSubsets=1)
Compute the index start and stop of the events loop with respect to the current subset and MPI size a...
Definition: vDataFile.cc:678
static sRandomNumberGenerator * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define MODE_HISTOGRAM
Definition: vDataFile.hh:58
void DeallocateBackwardImageFromDynamicBasis()
Free memory for the backward image matrices.
Definition: oImageSpace.cc:264
int InitSpecificOptions(string a_specificOptions)
int GetNbCardBasisFunctions()
Get the number of cardiac basis functions.
#define FLTNB
Definition: gVariables.hh:81
int ComputeFellowVoxelsList(vector< INTNB > &a_fellow_voxels, int a_current_voxel)
Fill the input list of fellow voxels for the current type of neighbourhood and the input voxel...
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
#define DYNAMIC_SWITCH_CONTINUE
HPFLTNB ** mpp_clusterMultiModal
FLTNB * mp_permanentBackwardImage
int StepPreProcessInsideSubsetLoop(int a_iteration, int a_subset)
This function is called right after starting the data subset loop.
#define HPFLTNB
Definition: gVariables.hh:83
HPFLTNB m_currentMeanClusterVolume
void Reduce()
Merge parallel results into the matrix of the backward image matrix of the first thread. Also for MPI.
FLTNB GetVoxSizeZ()
Get the voxel&#39;s size along the Z axis, in mm.
void StartCustomStep(int a_thread, int a_step)
Start the timer for duration of custom step of the given index for the given thread.
FLTNB GetRespBasisCoefficient(int a_respBasisFunction, int a_respGate)
Get the respiratory basis coefficients for the provided respiratory gate and basis function...
bool IsLoadedSensitivity()
Definition: oImageSpace.hh:635
int DynamicSwitch(int64_t a_currentEventIndex, uint32_t a_currentTime, int a_bed, int a_th)
Call the eponym function from the oDynamicDataManager class using the member object.
HPFLTNB * mp_clusterValues
int ApplyOutputFOVMasking()
Mask the outside of the transaxial FOV based on the m_fovOutPercent.
int ReadConfigurationFile(const string &a_configurationFile)
oProjectorManager * mp_ProjectorManager
Definition: vAlgorithm.hh:354
int StepInnerLoopInsideSubsetLoop(int a_iteration, int a_subset, int a_bed)
This function is called inside the subset loop. It contains the core operations of the algorithm an...
bool m_saveSensitivityHistoFlag
Definition: vAlgorithm.hh:367
int64_t GetSize()
Definition: vDataFile.hh:332
void StopIterativeDataUpdateStep3(int a_thread)
Stop the timer for duration of iterative data update step 3.
void InstantiateBackwardImageFromDynamicBasis(int a_nbBackwardImages)
Allocate memory for the backward image matrices and set the number of backward images for the whole c...
Definition: oImageSpace.cc:219
int GetNbMultiModalImages()
Get the number of additional multimodal images.
HPFLTNB GenerateExtraRdmNber(int a_nb=0)
Generate a random number using the specified additional not thread safe random generator, for use in sequential parts of an otherwise multithreaded code.
INTNB ** mp_listEventsIndices
int ApplyMaskToSensitivity()
Apply the mask to the sensitivity image (only for the first thread, the image must be reduced beforeh...
int GetNbTimeBasisFunctions()
Get the number of time basis functions.
HPFLTNB * mp_multiModalParam
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
int * mp_nbSubsets
Definition: vAlgorithm.hh:348
int InitImage(const string &a_pathToInitialImage, FLTNB a_value)
Initialize the main image, either using:
int ComputeSumsPerClusters(int a_iteration)
Compute sums of voxel features for each cluster.
bool IsLoadedMask()
Definition: oImageSpace.hh:646
void StartIterativeDataUpdateStep1(int a_thread)
Start the timer for duration of iterative data update step 1.
oImageSpace * mp_ImageSpace
Definition: vAlgorithm.hh:358
void StartIterativeDataUpdateStep2(int a_thread)
Start the timer for duration of iterative data update step 2.
FLTNB * mp_visitedVoxelsImage
Definition: oImageSpace.hh:121
vEvent * GetEvent(int64_t a_eventIndex, int a_th=0)
Definition: vDataFile.cc:605
vDataFile ** m2p_DataFile
Definition: vAlgorithm.hh:353
INTNB GetVoxelIndex(int a_direction, int a_TOFBin, INTNB a_voxelInLine)
This function is used to get the contributing voxel index of the provided direction, TOF bin and voxel rank.
bool * mp_listRelevantVoxelIndices
void PrepareForwardImage()
Copy current image matrix in the forward-image buffer matrix.
FLTNB GetVoxSizeY()
Get the voxel&#39;s size along the Y axis, in mm.
#define Cerr(MESSAGE)
HPFLTNB m_meanClusterVolumeMax
const string & GetPathName()
FLTNB ****** m6p_backwardImage
Definition: oImageSpace.hh:94
FLTNB **** m4p_image
Definition: oImageSpace.hh:80
HPFLTNB m_meanClusterVolumeMin
void CleanNeverVisitedVoxels()
Based on the visitedVoxelsImage, clean the never visited voxels in the image. This function must be c...
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
const string & GetPathToConfigDir()
Return the path to the CASTOR config directory.
string m_pathToInitialImg
Definition: vAlgorithm.hh:362
FLTNB ** m2p_multiModalImage
Definition: oImageSpace.hh:112
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
int StepPostProcessInsideSubsetLoop(int a_iteration, int a_subset)
int SaveOutputImage(int a_iteration, int a_subset=-1)
Call the interfile function to write output image on disk.
int StepBeforeIterationLoop()
This function is called at the beginning of the RunCPU function.
void ResetCurrentDynamicIndices()
Call the eponym function from the oDynamicDataManager class using the member object.
virtual int StepBeforeIterationLoop()
This function is called at the beginning of the RunCPU function.
Definition: vAlgorithm.cc:388
bool m_saveImageAfterSubsets
Definition: vAlgorithm.hh:368
INTNB GetCurrentNbVoxels(int a_direction, int a_TOFBin)
This function is used to get the current number of contributing voxels to the line.
void StartIterativeDataUpdateStep3(int a_thread)
Start the timer for duration of iterative data update step 3.
oProjectionLine * ComputeProjectionLine(vEvent *ap_Event, int a_th)
This function is used to compute system matrix elements from the associated projector or pre-computed...
void StopIterativeDataUpdateStep2(int a_thread)
Stop the timer for duration of iterative data update step 2.
FLTNB GetQuantificationFactor(int a_bed, int a_frame, int a_respGate, int a_cardGate)
Get the quantification factor corresponding to the provided bed, frame, respiratory and cardiac gates...
INTNB GetNbVoxXY()
Get the number of voxels in a slice.
#define VERBOSE_NORMAL
bool IsLoadedMultiModal()
Definition: oImageSpace.hh:640
FLTNB * mp_maskImage
Definition: oImageSpace.hh:117
int SampleConditionalCompleteData(int a_iteration, int a_subset, int a_bed)
Gibbs sampler : first conditional probability (backprojection)
void ApplyBedOffset(int a_bed)
Compute the bed offset from the provided bed index and apply it to all projection lines...
HPFLTNB * mp_clusterSensitivity
int StepAfterSubsetLoop(int a_iteration)
This function is called after finishing the data subset loop.
HPFLTNB GenerateRdmNber()
Generate a random number for the thread which index is recovered from the OpenMP function.
FLTNB GetTimeBasisCoefficient(int a_timeBasisFunction, int a_timeFrame)
Get the time basis coefficients for the provided frame and basis function.
bool NotEmptyLine()
This function is used to know if the line contains any voxel contribution.
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
int StepBeforeSubsetLoop(int a_iteration)
This function is called before starting the data subset loop.
#define INTNB
Definition: gVariables.hh:92
int InitializeHelperVar()
Allocate and initialize temporary variables, after the main variables have been initialized Assumptio...
INTNB **** m4p_EventsBackprojectionTrace
This class is designed to manage and store system matrix elements associated to a vEvent...
const string & GetBaseName()
int ApplyOutputFlip()
Just flip the output image.
oImageDimensionsAndQuantification * mp_ID
Definition: vAlgorithm.hh:352
virtual int StepAfterIterationLoop()
This function is called at the end of the RunCPU function.
Definition: vAlgorithm.cc:483
HPFLTNB * temp_count_multimodal
This is the base class for reconstructions, containing a framework with iteration and data subset loo...
Definition: vAlgorithm.hh:51
HPFLTNB * mp_multiModalNoiseSigma
Mother class for the Event objects.
Definition: vEvent.hh:42
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
int GetDataMode()
Definition: vDataFile.hh:299
INTNB GetNbVoxXYZ()
Get the total number of voxels.
vector< INTNB > * mpv_parentLinks
void ShowHelpSpecific()
Show help for the child algorithm.
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
FLTNB * AllocateMiscellaneousImage()
Allocate a new miscellaneous image on m2p_miscellaneousImages and return the pointer to this image...
Definition: oImageSpace.cc:527
int GetNbThreadsForProjection()
Get the number of threads used for projections.
FLTNB GetVoxelWeights(int a_direction, int a_TOFBin, INTNB a_voxelInLine)
This function is used to get the contributing voxel weight of the provided direction, TOF bin and voxel rank.
vector< INTNB > mv_newClusters
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
bool * mp_outputIterations
Definition: vAlgorithm.hh:349
INTNB * mp_voxelClusterMapping
int ProcessMultiModalInfo()
Check input multimodal images.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
#define FORWARD
#define Cout(MESSAGE)
int SampleConditionalClusterIntensity()
Gibbs sampler : third conditional probability (cluster intensity)
void StopCustomStep(int a_thread, int a_step)
Stop the timer for duration of custom step of the given index for the given thread.
This class is designed to manage some profiling of the code.
HPFLTNB ** mp_clusterN
FLTNB GetFrameDurationInSec(int a_bed, int a_frame)
Get the frame duration for the given bed, in seconds as a FLTNB.
int StepAfterIterationLoop()
This function is called at the end of the RunCPU function.
int SaveSensitivityImage(const string &a_pathToSensitivityImage)
Call the interfile function to write the sensitivity image on disk.
void InitBackwardImage()
Initialize each voxel of the backward images to 0, also for sensitivity if not loaded (estimated on t...
INTNB * mp_listVoxelIndices
int GetCurrentRespImage(int a_th)
call the eponym function from the oDynamicDataManager object
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
int GetNbRespBasisFunctions()
Get the number of respiratory basis functions.
static sChronoManager * GetInstance()
Instantiate the singleton if not already done, then return the pointer to its instance.
FLTNB ***** m5p_sensitivity
Definition: oImageSpace.hh:104
int GetMPIRank()
Get the MPI instance number (the rank)
int GenerateCurrentImage()
Generate the current image estimation from the current sample of partition/clustering and cluster int...
int GetCurrentTimeFrame(int a_th)
call the eponym function from the oDynamicDataManager object
int StepImageOutput(int a_iteration, int a_subset=-1)
This function deals with everything about saving output images from the reconstruction.
void Display()
Display the results of the duration buffers.
FLTNB GetCardBasisCoefficient(int a_cardBasisFunction, int a_cardGate)
Get the cardiac basis coefficients for the provided cardiac gate and basis function.