CASToR  3.1
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-2020 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF, Zacharias CHALAMPALAKIS
20 
21 This is CASToR version 3.1.
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;
553  if (m_ddCRP>0)
554  {
556  assert (!std::isnan(m_ddcrpLogAlpha));
557  }
558 
559  mp_listRelevantVoxelIndices = new bool[nbVoxels];
560  mp_voxelClusterMapping = new INTNB[nbVoxels];
561  // Due to the current implementation, cluster indices <= nbVoxels, cluster array size thus being = nbVoxels+1
562  mp_clusterValues = new HPFLTNB[nbVoxels+1];
563 
564  // the marginalized backprojection requires updates of mp_clusterN during parallel backprojection, so need for multithreaded mp_clusterN
565  // approximative implementation, because modifications in cluster sums can't be shared between threads
566  INTNB th_clusterN = (m_backprojection==2)?mp_ID->GetNbThreadsForProjection():1;
567  mp_clusterN = new HPFLTNB*[th_clusterN];
568  for (INTNB th=0; th<th_clusterN; th++) mp_clusterN[th] = new HPFLTNB[nbVoxels+1];
569 
570  mp_clusterSensitivity = new HPFLTNB[nbVoxels+1];
571 
572  // initialized only if multimodal info
573  if (multimodal_info)
574  {
576  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
577  {
578  mpp_clusterMultiModal[mmnb] = new HPFLTNB[nbVoxels+1];
579  }
580  mp_clusterCount = new INTNB[nbVoxels+1];
581  }
582 
583  // description of ddCRP links
584  mp_nextLink = new INTNB[nbVoxels];
585  mpv_parentLinks = new vector<INTNB>[nbVoxels];
586  mv_newClusters.assign(1,nbVoxels);
587  mp_listVoxelIndices = new INTNB[nbVoxels];
588 
589  // permanent backward image for backprojection modes 1 and 2
591 
592  // helper variables for the backprojection modes 1 and 2
593  // trace of voxels into which events were backprojected in the previous iteration
594  // initialize voxel indices at -1
595  if (m_backprojection==1 || m_backprojection==2)
596  {
597  //mp_listEventsIndices = new INTNB*[m_nbBeds];
599  for (int bed=0 ; bed<m_nbBeds ; bed++)
600  {
602  //mp_listEventsIndices[bed] = new INTNB[m2p_DataFile[bed]->GetSize()];
603 
604  // Compute start and stop indices taking MPI into account (the vDataFile does that)
605  int64_t index_start = 0;
606  int64_t index_stop = 0;
607  m2p_DataFile[bed]->GetEventIndexStartAndStop(&index_start, &index_stop, 0, 1);
608 
609  int64_t index;
610  // Keep the static scheduling with a chunk size at 1, it is important
611  #pragma omp parallel for private(index) schedule(static, 1)
612  for (index=0 ; index<m2p_DataFile[bed]->GetSize() ; index++)
613  {
614  // Get the thread index
615  INTNB th = 0;
616  #ifdef CASTOR_OMP
617  th = omp_get_thread_num();
618  #endif
619 
620  vEvent* event = m2p_DataFile[bed]->GetEvent(index, th);
621 
623 
624  m4p_EventsBackprojectionTrace[bed][index] = new INTNB*[line->GetNbTOFBins()];
625 
626  for (int b=0; b<line->GetNbTOFBins(); b++)
627  {
628  m4p_EventsBackprojectionTrace[bed][index][b] = new INTNB[(INTNB)round(event->GetEventValue(b))];
629  for (int e=0; e<(INTNB)round(event->GetEventValue(b)); e++)
630  {
631  m4p_EventsBackprojectionTrace[bed][index][b][e] = -1;
632  }
633  }
634  }
635  }
636  }
637 
638  // voxel and clusters variables
639  INTNB v;
640  #pragma omp parallel for private(v) schedule(guided)
641  for (v=0; v<nbVoxels; v++)
642  {
643  mp_listVoxelIndices[v] = v;
644 
645  mp_listRelevantVoxelIndices[v] = true;
646  if (background_mask && (((INTNB)round(mp_ImageSpace->mp_maskImage[v])) == 0))
647  mp_listRelevantVoxelIndices[v] = false;
648 
649  mp_voxelClusterMapping[v] = v;
650  mp_clusterValues[v] = mp_ImageSpace->m4p_image[0][0][0][v];
651  for (INTNB th=0; th<th_clusterN; th++) mp_clusterN[th][v] = 0.;
653  if (multimodal_info)
654  {
655  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
656  {
658  }
659  mp_clusterCount[v] = 1;
660  }
661  mp_nextLink[v] = v;
662  mpv_parentLinks[v].push_back(v);
663 
665  }
666 
667  // parameter initialization for the free available cluster, will be recomputed anyway
668  mp_clusterValues[nbVoxels] = 0.;
669  for (INTNB th=0; th<th_clusterN; th++) mp_clusterN[th][nbVoxels] = 0.;
670  mp_clusterSensitivity[nbVoxels] = 0.;
671  if (multimodal_info)
672  {
673  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) mpp_clusterMultiModal[mmnb][nbVoxels] = 0.;
674  mp_clusterCount[nbVoxels] = 0;
675  }
676 
679  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
680  {
681  temp_count_multimodal[mmnb] = 0.;
682  mp_multiModalParam[mmnb] = 0.;
683  }
684 
685  // End
686  return 0;
687 }
688 
689 // -----------------------------------------------------------------------------------------------------------------------------------------
690 
692 {
693  if (m_verbose>=2) Cout("iRCPGSAlgorithm::ProcessMultiModalInfo() "<<endl);
694 
695  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
696  {
697  FLTNB maxt = mp_ImageSpace->m2p_multiModalImage[mmnb][0];
698  FLTNB mint = mp_ImageSpace->m2p_multiModalImage[mmnb][0];
699 
700  for (int v=1; v<mp_ID->GetNbVoxXYZ(); v++)
701  {
702  if (mp_ImageSpace->m2p_multiModalImage[mmnb][v]>maxt) maxt = mp_ImageSpace->m2p_multiModalImage[mmnb][v];
703  if (mp_ImageSpace->m2p_multiModalImage[mmnb][v]<mint) mint = mp_ImageSpace->m2p_multiModalImage[mmnb][v];
704  }
705 
706  // compute the prior parameter rho
707  mp_multiModalParam[mmnb] = mp_multiModalNoiseSigma[mmnb]/maxt;
708 
709  if (m_verbose>=2) Cout("iRCPGSAlgorithm::StepBeforeIterationLoop() --> Multimodal image "<<mmnb<<" : noise std dev = "<<mp_multiModalNoiseSigma[mmnb]<<" , prior param rho = "<<mp_multiModalParam[mmnb]<<endl);
710  }
711 
712  // End
713  return 0;
714 }
715 
716 // -----------------------------------------------------------------------------------------------------------------------------------------
717 
719 {
721  {
722  Cerr("***** iRCPGSAlgorithm::StepBeforeIterationLoop() -> A problem occurred while calling StepBeforeIterationLoop() function !" << endl);
723  return 1;
724  }
725 
726  // Main image initialization
728  {
729  Cerr("***** vAlgorithm::StepBeforeIterationLoop() -> An error occurred while reading the initialization image !" << endl);
730  return 1;
731  }
732 
733  // allocate backward images
735 
736  // specific to this algorithm
738 
739  // currently the sensitivity image must be provided
741 
742  // If subsets and OSEM-like backprojection (mode 0), then divide by the number of subsets because the acqusition duration is included in the sensitivity
743  // No need for other backprojection modes because permanently dealing with the entire backprojected data for all the subsets
744  if (mp_nbSubsets[0]>1 && m_backprojection==0)
745  {
746  if (m_verbose>=2) Cout("iRCPGSAlgorithm::StepBeforeIterationLoop() --> Dividing the provided sensitivity image by the number of subsets "<<endl);
747  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++) mp_ImageSpace->m5p_sensitivity[0][0][0][0][v] /= ((FLTNB)mp_nbSubsets[0]);
748  }
749 
750  // process additional (multimodal) images
752 
753  // End
754  return 0;
755 }
756 
757 
758 // =====================================================================
759 // ---------------------------------------------------------------------
760 // ---------------------------------------------------------------------
761 // =====================================================================
762 
763 int iRCPGSAlgorithm::StepPreProcessInsideSubsetLoop(int a_iteration, int a_subset)
764 {
765  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepPreProcessInsideSubsetLoop ... " << endl);
766 
767  // Initialize the correction backward image(s)
769 
770  // Copy current image in forward-image buffer
772 
773  // End
774  return 0;
775 }
776 
777 // -----------------------------------------------------------------------------------------------------------------------------------------
778 
779 int iRCPGSAlgorithm::StepInnerLoopInsideSubsetLoop(int a_iteration, int a_subset, int a_bed)
780 {
781  // Get the chrono manager singleton pointer
782  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
783 
784  // Sample posterior complete data, backproject the acquired data, Gibbs Sampler Step 1
785  p_ChronoManager->StartCustomStep(0,0);
786  SampleConditionalCompleteData(a_iteration, a_subset, a_bed);
787  p_ChronoManager->StopCustomStep(0,0);
788 
789  // End
790  return 0;
791 }
792 
793 // -----------------------------------------------------------------------------------------------------------------------------------------
794 
795 int iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop(int a_iteration, int a_subset)
796 {
797  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop ... " << endl);
798 
799  // Get the chrono manager singleton pointer
800  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
801 
802  // Merge parallel results
804 
805  // If mask provided, apply mask to sensitivity, the CleanNeverVisitedVoxels will take care of the images
807 
808  // Finalize processing of backward images if permanent backward image
809  if (mp_permanentBackwardImage!=NULL)
810  {
811  INTNB v;
812  #pragma omp parallel for private(v) schedule(guided)
813  for (v=0;v<mp_ID->GetNbVoxXYZ();v++)
814  {
816  }
817  }
818 
819  // update algorithm variables based on the new backprojection
820  ComputeSumsPerClusters(a_iteration);
821 
822  // Sample posterior ddCRP (clustering), Gibbs Sampler Step 2
823  if (m_ddCRP>0)
824  {
825  p_ChronoManager->StartCustomStep(0,1);
826 
827  SampleConditionalClustering(a_iteration);
828 
829  p_ChronoManager->StopCustomStep(0,1);
830  }
831 
832  // Marginalized backprojection: as mp_clusterN threaded, and sampling step 2 (conditional clustering) modifies mp_clusterN only for the first thread,
833  // copy the modifications to all the other threads for the next backprojection
834  if (m_backprojection==2)
835  {
836  INTNB s;
837  #pragma omp parallel for private(s) schedule(guided)
838  for (s=0;s<mp_ID->GetNbVoxXYZ()+1;s++)
839  for (INTNB th=1;th<mp_ID->GetNbThreadsForProjection();th++)
840  mp_clusterN[th][s] = mp_clusterN[0][s];
841  }
842 
843  // Sample posterior cluster intensities, Gibbs Sampler Step 3, once per iteration
844  p_ChronoManager->StartCustomStep(0,2);
846  p_ChronoManager->StopCustomStep(0,2);
847 
848  // display performance info at each iteration
849  p_ChronoManager->Display();
850 
851  // computed at each iteration because the variable is cleaned at each iteration after image clean-up
853 
854  // Generate current posterior image sample from clustering/partition and cluster intensities
856 
857  // if required, try to roughly update ddCRP alpha to obtain approximately the requested target average cluster volume
858  // every 5 iterations and once per iteration
859  if (m_meanClusterVolumeMax>0. && a_iteration>=5 && (a_iteration+1)%5==0 && a_subset==mp_nbSubsets[a_iteration]/2)
860  {
861  bool alpha_changed = false;
862  // modify alpha if current average cluster volume less than m_meanClusterVolumeMax
864  {
866  alpha_changed = true;
867  }
868  // modify alpha if current average cluster volume more than m_meanClusterVolumeMin
870  {
872  alpha_changed = true;
873  }
874  // Log the modification
875  if (alpha_changed)
876  {
878  Cout("iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop() -> Updated ddCRP alpha to "<<m_ddcrpAlpha<<endl);
879  }
880  }
881 
882  // Save the sensitivity image in histogram mode, if asked for
884  {
885  // Get output manager to build the file name
886  sOutputManager* p_output_manager = sOutputManager::GetInstance();
887  // Build the file name
888  string temp_sens = p_output_manager->GetPathName() + p_output_manager->GetBaseName();
889  stringstream temp_it; temp_it << a_iteration + 1;
890  stringstream temp_ss; temp_ss << a_subset + 1;
891  temp_sens.append("_it").append(temp_it.str()).append("_ss").append(temp_ss.str()).append("_sensitivity");
892  // Save sensitivity
894  }
895 
896  // Save the current image estimate if asked for
897  if (mp_ID->GetMPIRank()==0 && mp_outputIterations[a_iteration] && m_saveImageAfterSubsets)
898  {
899  // Verbose
900  if (m_verbose>=1) Cout("iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop() -> Save image at iteration " << a_iteration+1 << " for subset " << a_subset+1 << endl);
901  // Save image
902  if (StepImageOutput(a_iteration,a_subset))
903  {
904  Cerr("***** iRCPGSAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while saving images at iteration " << a_iteration+1 << " !" << endl);
905  return 1;
906  }
907  }
908 
909  // End
910  return 0;
911 }
912 
913 // -----------------------------------------------------------------------------------------------------------------------------------------
914 
915 int iRCPGSAlgorithm::SampleConditionalCompleteData(int a_iteration, int a_subset, int a_bed)
916 {
917 
918  // Verbose
919  if (m_verbose>=2)
920  {
921  if (m_nbBeds>1) Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Start loop over events for bed " << a_bed+1 << endl << flush);
922  else Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Start loop over events" << endl << flush);
923  }
924 
925  // Reinitialize 4D gate indexes
927 
928  // Apply the bed offset for this bed position
930 
931  // Progression (increments of 2%)
933  {
934  cout << "0 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%" << endl;
935  cout << "|----|----|----|----|----|----|----|----|----|----|" << endl;
936  cout << "|" << flush;
937  }
938 
939  int progression_percentage_old = 0;
940  int progression_nb_bars = 0;
941  uint64_t progression_printing_index = 0;
942 
943  // Compute start and stop indices taking MPI into account (the vDataFile does that)
944  int64_t index_start = 0;
945  int64_t index_stop = 0;
946 
947  m2p_DataFile[a_bed]->GetEventIndexStartAndStop(&index_start, &index_stop, a_subset, mp_nbSubsets[a_iteration]);
948 
949  // Synchronize MPI processes
950  #ifdef CASTOR_MPI
951  MPI_Barrier(MPI_COMM_WORLD);
952  #endif
953 
954  // Set the number of threads for projections (right after this loop, we set back the number of threads for image computations)
955  #ifdef CASTOR_OMP
956  omp_set_num_threads(mp_ID->GetNbThreadsForProjection());
957  #endif
958 
959  // This boolean is used to report any problem inside the parallel loop
960  bool problem = false;
961 
962  // Get the chrono manager singleton pointer
963  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
964 
965  // shuffle events
966  if (m_backprojection==1)
967  {
968  //random_shuffle(mp_listEventsIndices[a_bed], mp_listEventsIndices[a_bed]+(index_stop-index_start));
969  }
970 
971  // Launch the loop on events
972  int64_t index_temp;
973  // Keep the static scheduling with a chunk size at 1, it is important
974  #pragma omp parallel for private(index_temp) schedule(static, 1)
975  for ( index_temp = index_start ; index_temp < index_stop ; index_temp += mp_nbSubsets[a_iteration])
976  {
977  // Get the thread index
978  int th = 0;
979  #ifdef CASTOR_OMP
980  th = omp_get_thread_num();
981  #endif
982 
983  // get random event
984  INTNB index = index_temp;
985  //if (m_backprojection==1) index = mp_listEventsIndices[a_bed][index_temp];
986 
987  // Print progression (do not log out with Cout here)
988  if (m_verbose>=2 && th==0 && mp_ID->GetMPIRank()==0)
989  {
990  if (progression_printing_index%1000==0)
991  {
992  int progression_percentage_new = ((int)( (((float)(index-index_start+1))/((float)(index_stop-index_start)) ) * 100.));
993  if (progression_percentage_new>=progression_percentage_old+2) // Increments of 2%
994  {
995  int nb_steps = (progression_percentage_new-progression_percentage_old)/2;
996  for (int i=0; i<nb_steps; i++)
997  {
998  cout << "-" << flush;
999  progression_nb_bars++;
1000  }
1001  progression_percentage_old += nb_steps*2;
1002  }
1003  }
1004  progression_printing_index++;
1005  }
1006 
1007  // Step 1: Get the current event for that thread index
1008  p_ChronoManager->StartIterativeDataUpdateStep1(th);
1009 
1010  vEvent* event = m2p_DataFile[a_bed]->GetEvent(index, th);
1011 
1012  if (event==NULL)
1013  {
1014  Cerr("***** iRCPGSAlgorithm::SampleConditionalCompleteData() -> An error occured while getting the event from index "
1015  << index << " (thread " << th << ") !" << endl);
1016  // Specify that there was a problem
1017  problem = true;
1018  // We must continue here because we are inside an OpenMP loop
1019  continue;
1020  }
1021  p_ChronoManager->StopIterativeDataUpdateStep1(th);
1022 
1023  // Step 2: Call the dynamic switch function that updates the current frame and gate numbers, and also detects involuntary patient motion
1024  p_ChronoManager->StartIterativeDataUpdateStep2(th);
1025  #ifdef CASTOR_DEBUG
1026  if (m_verbose>=4)
1027  {
1028  Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Step2: Check for Dynamic event (frame/gate switch, image-based deformation " << endl);
1029  }
1030  #endif
1031  int dynamic_switch_value = mp_ID->DynamicSwitch(index, event->GetTimeInMs(), a_bed, th);
1032  // If the DYNAMIC_SWITCH_CONTINUE is returned, then it means that we are not yet at the first frame
1033  if ( dynamic_switch_value == DYNAMIC_SWITCH_CONTINUE )
1034  {
1035  // Then we just skip this event
1036  continue;
1037  }
1038 
1039  p_ChronoManager->StopIterativeDataUpdateStep2(th);
1040 
1041  // Skip if empty event
1042  bool emptyEvent = true;
1043  for (int b=0; b<event->GetNbValueBins(); b++)
1044  if (event->GetEventValue(b)>0.)
1045  {
1046  emptyEvent = false;
1047  break;
1048  }
1049 
1050  if (emptyEvent) continue;
1051 
1052  // Step 3: Compute the projection line
1053  p_ChronoManager->StartIterativeDataUpdateStep3(th);
1054  #ifdef CASTOR_DEBUG
1055  if (m_verbose>=4) Cout("iRCPGSAlgorithm::SampleConditionalCompleteData() -> Step3: Compute the projection line " << endl);
1056  #endif
1058  if (line==NULL)
1059  {
1060  Cerr("***** iRCPGSAlgorithm::SampleConditionalCompleteData() -> A problem occured while computing the projection line !" << endl);
1061  // Specify that there was a problem
1062  problem = true;
1063  // We must continue here because we are inside an OpenMP loop
1064  continue;
1065  }
1066  p_ChronoManager->StopIterativeDataUpdateStep3(th);
1067 
1068  if(line->NotEmptyLine())
1069  {
1070  // get time respiratory and cardiac frame indices
1071  int timeFrame = mp_ID->GetCurrentTimeFrame(th);
1072  int respGate = mp_ID->GetCurrentRespImage(th);
1073  int cardGate = mp_ID->GetCurrentCardImage(th);
1074 
1075  assert(timeFrame==0);assert(respGate==0);assert(cardGate==0);
1076 
1077  // Loop over time basis functions
1078  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1079  {
1080  // Get frame/basis coefficient and continue if null
1081  FLTNB time_basis_coef = mp_ID->GetTimeBasisCoefficient(tbf,timeFrame);
1082  if (time_basis_coef==0.) continue;
1083  // Loop over respiratory basis functions
1084  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1085  {
1086  // Get resp_gate/basis coefficient and continue if null
1087  FLTNB resp_basis_coef = mp_ID->GetRespBasisCoefficient(rbf,respGate);
1088  if (resp_basis_coef==0.) continue;
1089  // Loop over cardiac basis functions
1090  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1091  {
1092  // Get card_gate_basis coefficient and continue if null
1093  FLTNB card_basis_coef = mp_ID->GetCardBasisCoefficient(cbf,cardGate);
1094  if (card_basis_coef==0.) continue;
1095  // Compute global coefficient
1096  FLTNB global_basis_coef = time_basis_coef * resp_basis_coef * card_basis_coef;
1097  assert(global_basis_coef==1.);
1098 
1099  // Compute multiplicative correction for this LOR
1100  FLTNB multiplicative_corr = 1./(event->GetMultiplicativeCorrections() * mp_ID->GetQuantificationFactor(a_bed, timeFrame, respGate, cardGate));
1101 
1102  // Loop over TOF bins
1103  for (int b=0; b<line->GetNbTOFBins(); b++)
1104  {
1105  FLTNB temp_eventValue = event->GetEventValue(b);
1106  if (temp_eventValue>0.)
1107  {
1108  // multinomial backprojection step with permanent backward image, backprojection variables only updated
1109  if (m_backprojection==1 || m_backprojection==2)
1110  {
1111  INTNB numCounts = (INTNB)round(temp_eventValue);
1112  // the number of counts must be integer for this backprojection,
1113  // otherwise events tracking gets complicated
1114  assert(round(temp_eventValue)==temp_eventValue);
1115 
1116  // process acquired counts one by one
1117  for (int e=0; e<numCounts; e++)
1118  {
1119  // remove the count backprojected at the previous iteration from all the variables
1120  // if backprojected into random/scatter there is nothing to remove
1121  if (a_iteration>0 && !(m4p_EventsBackprojectionTrace[a_bed][index][b][e]<0))
1122  {
1123  // voxel into which the count was backprojected at the previous iteration
1124  INTNB previous_voxel = m4p_EventsBackprojectionTrace[a_bed][index][b][e];
1125  INTNB previous_voxel_current_cluster = mp_voxelClusterMapping[previous_voxel];
1126 
1127  // remove this count from the backprojected complete data
1128  if (mp_permanentBackwardImage!=NULL)
1129  {
1130  mp_ImageSpace->m6p_backwardImage[1][th][tbf][rbf][cbf][previous_voxel] -= 1.;
1131  }
1132 
1133  if (m_backprojection==2)
1134  {
1135  // update cluster sums of backprojected counts, because this variable is used in marginalized backprojection,
1136  // only the current thread is updated, modifications in other threads can't be taken into account,
1137  // so approximative implementation when using more than one thread
1138  mp_clusterN[th][previous_voxel_current_cluster] -= 1.;
1139  assert(mp_clusterN[th][previous_voxel_current_cluster]>-0.001);
1140  }
1141  }
1142 
1143  p_ChronoManager->StartCustomStep(th,3);
1144  HPFLTNB temp_value = 0.;
1145 
1146  // compute probabilities for backprojection
1147  INTNB current_nb_voxels = line->GetCurrentNbVoxels(FORWARD,b);
1148  vector<HPFLTNB> voxelProbabilities(current_nb_voxels+1,0.);
1149  HPFLTNB probabilities_sum = 0.;
1150 
1151  INTNB temp_voxelIndex = -1;
1152  // voxels outcomes
1153  if (a_iteration==0)
1154  {
1155  // first iteration : if the initial image = 1, the probability becomes aij
1156  for (int vl=0; vl<current_nb_voxels; vl++)
1157  {
1158  temp_voxelIndex = line->GetVoxelIndex(FORWARD,b,vl);
1159  temp_value = (!mp_listRelevantVoxelIndices[temp_voxelIndex]) ? 0. : line->GetVoxelWeights(FORWARD,b,vl) * multiplicative_corr * global_basis_coef;
1160 
1161  probabilities_sum += temp_value;
1162  voxelProbabilities.at(vl+1) = temp_value;
1163  }
1164  }
1165  else
1166  {
1167  // voxels outcomes
1168  for (int vl=0; vl<current_nb_voxels; vl++)
1169  {
1170  temp_voxelIndex = line->GetVoxelIndex(FORWARD,b,vl);
1171  // Aij (system matrix element) * expectation of conditional cluster intensity
1172  temp_value = (!mp_listRelevantVoxelIndices[temp_voxelIndex]) ? 0. : line->GetVoxelWeights(FORWARD,b,vl) * multiplicative_corr * global_basis_coef;
1173  if (m_backprojection==2)
1174  { // multinomial backprojection marginalized over cluster intensity
1175  temp_value *= ((mp_clusterN[th][mp_voxelClusterMapping[temp_voxelIndex]] + m_gammaShape)
1177  }
1178  else
1179  { // multinomial backprojection
1180  temp_value *= mp_ImageSpace->m4p_forwardImage[tbf][rbf][cbf][temp_voxelIndex];
1181  }
1182 
1183  probabilities_sum += temp_value;
1184  voxelProbabilities.at(vl) = temp_value;
1185  }
1186  }
1187 
1188 
1189  // random and scatter counts as the last outcome
1190  temp_value = event->GetAdditiveCorrections(b) * mp_ID->GetFrameDurationInSec(a_bed, timeFrame);
1191  voxelProbabilities[current_nb_voxels] = temp_value;
1192  probabilities_sum += temp_value;
1193 
1194  p_ChronoManager->StopCustomStep(th,3);
1195  assert(!(probabilities_sum<0.));
1196  // if the probability distribution is not empty for this line
1197  if (probabilities_sum>0.)
1198  {
1199  // get a random number from uniform distribution and multiply it by the sum of probabilities,
1200  // faster than normalizing probabilities before sampling
1201  //HPFLTNB current_random = gsl_rng_uniform(mpp_threadRandomGenerators[th])*probabilities_sum;
1202  HPFLTNB current_random = sRandomNumberGenerator::GetInstance()->GenerateRdmNber()*probabilities_sum;
1203  HPFLTNB cumulative_sum = 0.;
1204  INTNB sampledIndex = 0;
1205  for (sampledIndex=current_nb_voxels; sampledIndex>=0; sampledIndex--)
1206  {
1207  cumulative_sum += voxelProbabilities[sampledIndex];
1208  if (cumulative_sum>current_random) break;
1209  }
1210 
1211  // write the realization into the corresponding voxel and update other variables
1212  if (sampledIndex>=0 && sampledIndex<current_nb_voxels)
1213  {
1214  INTNB drawn_voxel_index = line->GetVoxelIndex(FORWARD,b,sampledIndex);
1215  INTNB new_cluster = mp_voxelClusterMapping[drawn_voxel_index];
1216 
1217  // add the count into the backprojected complete data
1218  mp_ImageSpace->m6p_backwardImage[0][th][tbf][rbf][cbf][drawn_voxel_index] += 1.;
1219 
1220  if (m_backprojection==2)
1221  {
1222  // update cluster sums of backprojected counts, because this variable is used in the backprojection
1223  mp_clusterN[th][new_cluster] += 1.;
1224  }
1225 
1226  // update backprojection trace for the next iteration/subset
1227  m4p_EventsBackprojectionTrace[a_bed][index][b][e] = drawn_voxel_index;
1228  }
1229  else // random/scatter outcome
1230  {
1231  // flag random/scatter outcome, not really used at present
1232  m4p_EventsBackprojectionTrace[a_bed][index][b][e] = -2;
1233  }
1234  }
1235  }
1236  }
1237  else // ordinary multinomial backprojection step, overwrites all the backprojection variables
1238  {
1239  p_ChronoManager->StartCustomStep(th,3);
1240  HPFLTNB temp_value = 0.;
1241 
1242  // Compute categorical probability distribution over voxels contributing to the line
1243  // and the scatter/random source contributing to the line;
1244  // The probabilities are not normalized, the normalization factor is taken into
1245  // account during sampling
1246  INTNB current_nb_voxels = line->GetCurrentNbVoxels(FORWARD,b);
1247  vector<HPFLTNB> voxelProbabilities(current_nb_voxels+1,0.);
1248  HPFLTNB probabilities_sum = 0.;
1249 
1250  // voxels
1251  for (int vl=0; vl<current_nb_voxels; vl++)
1252  {
1253  INTNB temp_voxelIndex = line->GetVoxelIndex(FORWARD,b,vl);
1254  temp_value = (!mp_listRelevantVoxelIndices[temp_voxelIndex]) ? 0. :line->GetVoxelWeights(FORWARD,b,vl) * multiplicative_corr * global_basis_coef
1255  * mp_ImageSpace->m4p_forwardImage[tbf][rbf][cbf][temp_voxelIndex];
1256  //* mp_clusterValues[mp_voxelClusterMapping[temp_voxelIndex]];
1257 
1258  probabilities_sum += temp_value;
1259  voxelProbabilities.at(vl) = temp_value;
1260  }
1261 
1262  // random and scatter counts as the last outcome
1263  temp_value = event->GetAdditiveCorrections(b) * mp_ID->GetFrameDurationInSec(a_bed, timeFrame);
1264  voxelProbabilities[current_nb_voxels] = temp_value;
1265  probabilities_sum += temp_value;
1266 
1267  assert(!(probabilities_sum<0.));
1268  p_ChronoManager->StopCustomStep(th,3);
1269 
1270  // if the probability distribution is not empty for this line
1271  if (probabilities_sum>0.)
1272  {
1273  p_ChronoManager->StartCustomStep(th,4);
1274  INTNB numCounts = (INTNB)round(temp_eventValue);
1275  // if the number of counts is not integer (might happen for GE PET/MR sinogram data),
1276  // pick randomly floor or ceil rounding
1277  if (fabs(round(temp_eventValue)-temp_eventValue)>1.e-7)
1278  {
1280  {
1281  numCounts = (INTNB)ceil(temp_eventValue);
1282  }
1283  else
1284  {
1285  numCounts = (INTNB)floor(temp_eventValue);
1286  }
1287  }
1288  p_ChronoManager->StopCustomStep(th,4);
1289  p_ChronoManager->StartCustomStep(th,5);
1290  // the number of repetitions for the multinomial distribution = event value
1291  for (int e=0; e<numCounts; e++)
1292  {
1293  // get a random number from uniform distribution and multiply it by the sum of probabilities,
1294  // faster than normalizing probabilities before sampling
1295  //HPFLTNB current_random = gsl_rng_uniform(mpp_threadRandomGenerators[th])*probabilities_sum;
1296  HPFLTNB current_random = sRandomNumberGenerator::GetInstance()->GenerateRdmNber()*probabilities_sum;
1297  HPFLTNB cumulative_sum = 0.;
1298  INTNB sampledIndex = 0;
1299  // faster if start from the random/scatter outcome, which probably has higher probability
1300  for (sampledIndex=current_nb_voxels; sampledIndex>=0; sampledIndex--)
1301  {
1302  cumulative_sum += voxelProbabilities[sampledIndex];
1303  if (cumulative_sum>current_random) break;
1304  }
1305  // write the realization into the corresponding voxel, and don't do anything if the sampled index refers to
1306  // random and scatter source (the last probability in the list)
1307  if (sampledIndex>=0 && sampledIndex<current_nb_voxels)
1308  {
1309  mp_ImageSpace->m6p_backwardImage[0][th][tbf][rbf][cbf][line->GetVoxelIndex(FORWARD,b,sampledIndex)] += 1.;
1310  }
1311  else if (sampledIndex<0)
1312  {
1313  // check : if nothing sampled throw a warning, though it should not happen
1314  Cout("Warning: Nothing sampled, sampledIndex "<<sampledIndex<<", current_nb_voxels "<<current_nb_voxels<<endl);
1315  }
1316  }
1317  p_ChronoManager->StopCustomStep(th,5);
1318  }
1319  }
1320  }
1321  }
1322  }
1323  }
1324  }
1325  }
1326  } // End of events loop (OpenMP stops here)
1327 
1328  // Synchronize MPI processes
1329  #ifdef CASTOR_MPI
1330  MPI_Barrier(MPI_COMM_WORLD);
1331  #endif
1332 
1333  // Set back the number of threads for image computation
1334  #ifdef CASTOR_OMP
1335  omp_set_num_threads(mp_ID->GetNbThreadsForImageComputation());
1336  #endif
1337 
1338  // End of progression printing (do not log out with Cout here)
1339  if (m_verbose>=2 && mp_ID->GetMPIRank()==0)
1340  {
1341  int progression_total_bars = 49;
1342  for (int i=0; i<progression_total_bars-progression_nb_bars; i++) cout << "-";
1343  cout << "|" << endl;
1344  }
1345 
1346  // If a problem was encountered, then report it here
1347  if (problem)
1348  {
1349  Cerr("***** iRCPGSAlgorithm::SampleConditionalCompleteData() -> A problem occured inside the parallel loop over events !" << endl);
1350  return 1;
1351  }
1352 
1353  // End
1354  return 0;
1355 }
1356 
1357 // -----------------------------------------------------------------------------------------------------------------------------------------
1358 
1360 {
1361  if (m_verbose>=2) Cout("iRCPGSAlgorithm::ComputeSumsPerClusters()... " << endl);
1362 
1363  bool multimodal_info = mp_ImageSpace->IsLoadedMultiModal();
1364 
1365  // reinitialize and compute sums over clusters
1366  // sum of nij over clusters must be recomputed at each iteration, because nij have been resampled
1367  // recomputing sensitivity sum over clusters is not compulsory because the sensitivity is constant over iterations
1368  // and the sums are updated at each clustering sampling, but it is done nevertheless, in order to avoid accumulations of numerical errors
1369 
1370  // marginalized backprojection needs threaded cluster sums
1371  INTNB th_clusterN = (m_backprojection==2)?mp_ID->GetNbThreadsForProjection():1;
1372 
1373  // reinitialize
1374  INTNB vp;
1375  #pragma omp parallel for private(vp) schedule(guided)
1376  for (vp=0; vp<(mp_ID->GetNbVoxXYZ()+1); vp++)
1377  {
1378  mp_clusterN[0][vp] = 0.;
1379  for (INTNB th=1;th<th_clusterN;th++) mp_clusterN[th][vp] = 0.;
1380  mp_clusterSensitivity[vp] = 0.;
1381  if (multimodal_info)
1382  {
1383  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) mpp_clusterMultiModal[mmnb][vp] = 0.;
1384  mp_clusterCount[vp] = 0;
1385  }
1386  }
1387 
1388  // sum using voxel to cluster mapping
1389  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1390  {
1391  // skip if background voxel
1392  if (!mp_listRelevantVoxelIndices[v]) continue;
1393 
1394  if (mp_permanentBackwardImage!=NULL)
1395  {
1396  assert(mp_permanentBackwardImage[v]>=-0.0001);
1398  }
1399  else
1400  {
1401  assert(mp_ImageSpace->m6p_backwardImage[0][0][0][0][0][v]>=0.);
1403  }
1404 
1405  assert(mp_ImageSpace->m5p_sensitivity[0][0][0][0][v]>=0.);
1407  if (multimodal_info)
1408  {
1409  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1410  {
1411  mpp_clusterMultiModal[mmnb][mp_voxelClusterMapping[v]] += mp_ImageSpace->m2p_multiModalImage[mmnb][v] ;
1412  }
1413  mp_clusterCount[mp_voxelClusterMapping[v]] += 1;
1414  }
1415  }
1416 
1417  // End
1418  return 0;
1419 }
1420 
1421 // -----------------------------------------------------------------------------------------------------------------------------------------
1422 
1424 {
1425  // Verbose
1426  if (m_verbose>=3) Cout("iRCPGSAlgorithm::UpdateVisitedVoxels() -> Tick visited voxels based on sensitivity" << endl);
1427 
1428  // zeros in the sensitivity map can be used to detect visited voxels, though not needed in this algorithm?
1429  int count=0;
1430  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1431  {
1432  // Use the sensitivity image to find visited voxels (we just add 1 to discriminate between visited and not visited voxels)
1433  if (mp_ImageSpace->m5p_sensitivity[0][0][0][0][v]>0.) mp_ImageSpace->mp_visitedVoxelsImage[v] += 1.;
1434  else count++;
1435  }
1436  if (m_verbose>=2 && count>0) Cout(" --> Invisible voxels : "<<count<<" over "<<mp_ID->GetNbVoxXYZ()<<" !"<<endl);
1437 
1438  // End
1439  return 0;
1440 }
1441 
1442 // -----------------------------------------------------------------------------------------------------------------------------------------
1443 
1445 {
1446  if (m_verbose>=2) Cout("iRCPGSAlgorithm::SampleConditionalClustering()... " << endl);
1447  // voxels eligible for assignment to the same cluster (here simple voxel neighbourhood)
1448  // variable size with maximum at m_neighbourhood+1
1449 
1450  //HPFLTNB mean_proba = 0., mean_proba_prev = 0., nb_proba = 0., std_proba = 0.;
1451 
1452  bool multimodal_info = mp_ImageSpace->IsLoadedMultiModal();
1453 
1454  vector<INTNB> fellow_voxels;
1455  fellow_voxels.reserve(m_neighbourhood+1);
1456 
1457  // non-normalized probabilities for the sampling of the next link
1458  // variable size with maximum at m_neighbourhood+1
1459  vector<HPFLTNB> probabilities;
1460  probabilities.reserve(m_neighbourhood+1);
1461 
1462  // voxels belonging to the same cluster as the current voxel,
1463  // after the next link of the current voxel has been cut (all parent voxels)
1464  vector<INTNB> voxels_after_cut;
1465 
1466  // dimensions
1467  INTNB dim_3D = mp_ID->GetNbVoxXYZ();
1468 
1469  // randomize voxel indices before sampling the ddCRP
1470  //random_shuffle(mp_listVoxelIndices, mp_listVoxelIndices+dim_3D);
1471  shuffle(mp_listVoxelIndices, mp_listVoxelIndices+dim_3D, sRandomNumberGenerator::GetInstance()->GetExtraGenerator(1));
1472 
1473  // resample voxels links
1474  for(int v=0; v<dim_3D; v++)
1475  {
1476  const int current_voxel = mp_listVoxelIndices[v];
1477 
1478  // skip if background voxel
1479  if (!mp_listRelevantVoxelIndices[current_voxel]) continue;
1480 
1481  const int previous_cluster = mp_voxelClusterMapping[current_voxel];
1482 
1483  // temporary new cluster
1484  int temp_new_cluster = mv_newClusters.at(mv_newClusters.size()-1);
1485  mv_newClusters.pop_back();
1486  assert( temp_new_cluster<=dim_3D );
1487  assert(temp_new_cluster!=previous_cluster);
1488 
1489  // get voxel indices in the neighbourhood
1490  ComputeFellowVoxelsList(fellow_voxels, current_voxel);
1491 
1492  // explore the parent links of the current voxel, check whether these links make a loop,
1493  // and build the list of voxels connected to the current voxel by parent links
1494  bool loop = false;
1495  voxels_after_cut.resize(0);
1496 
1497  // lambda function for optimization purposes
1498  function<void (int)> explorePreviousLinks= [this,&explorePreviousLinks,current_voxel,&voxels_after_cut,&loop] (int index)
1499  {
1500  voxels_after_cut.push_back(index);
1501  for(int ip: mpv_parentLinks[index])
1502  {
1503  if (ip!=current_voxel) explorePreviousLinks(ip);
1504  else loop=true;
1505  }
1506  };
1507  explorePreviousLinks(current_voxel);
1508  assert( voxels_after_cut.size()>0 );
1509 
1510  // cut the next (child) link of the current voxel, and update parent links
1511  size_t previous_next_link = mp_nextLink[current_voxel];
1512  for(size_t p=0; p<mpv_parentLinks[previous_next_link].size(); p++)
1513  {
1514  if (mpv_parentLinks[previous_next_link][p]==current_voxel)
1515  {
1516  mpv_parentLinks[previous_next_link].erase(mpv_parentLinks[previous_next_link].begin()+p);
1517  break;
1518  }
1519  }
1520 
1521  // assign the temporary new cluster to the current voxel and to all its parent voxels
1522  // compute also modifications of sums over the cluster
1523  HPFLTNB temp_count_N = 0.;
1524  HPFLTNB temp_count_sens = 0.;
1525  INTNB temp_count = 0;
1526  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++) temp_count_multimodal[mmnb] = 0.;
1527 
1528  for(int nv : voxels_after_cut)
1529  {
1530  if (mp_permanentBackwardImage!=NULL) temp_count_N += mp_permanentBackwardImage[nv];
1531  else temp_count_N += mp_ImageSpace->m6p_backwardImage[0][0][0][0][0][nv];
1532 
1533  temp_count_sens += mp_ImageSpace->m5p_sensitivity[0][0][0][0][nv];
1534  if (multimodal_info)
1535  {
1536  temp_count++;
1537  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1538  {
1540  assert (temp_count_multimodal[mmnb]>=0.);
1541  }
1542  }
1543  mp_voxelClusterMapping[nv] = temp_new_cluster;
1544  }
1545  assert (temp_count_N>=0. && temp_count_sens>=0.);
1546  assert (temp_count>=0);
1547 
1548  // if this cluster contains the same voxels as the previous cluster (looped links),
1549  // free the previous cluster, make it available for assignment
1550  // (all these voxels have already been assigned to the temporary new cluster)
1551  if (loop) mv_newClusters.push_back(previous_cluster);
1552 
1553  // update sums for the temporary new cluster and the remaining part of the previous_cluster (might be empty)
1554  mp_clusterN[0][previous_cluster] -= temp_count_N;
1555  mp_clusterSensitivity[previous_cluster] -= temp_count_sens;
1556  mp_clusterN[0][temp_new_cluster] = temp_count_N;
1557  mp_clusterSensitivity[temp_new_cluster] = temp_count_sens;
1558 
1559  assert(mp_clusterN[0][previous_cluster]>=0.);
1560  //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);
1561  assert(mp_clusterN[0][temp_new_cluster]>=0.);
1562  //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);
1563 
1564  if (multimodal_info)
1565  {
1566  mp_clusterCount[previous_cluster] -= temp_count;
1567  mp_clusterCount[temp_new_cluster] = temp_count;
1568  assert(mp_clusterCount[previous_cluster]>=0);
1569  assert(mp_clusterCount[temp_new_cluster]>=0);
1570  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1571  {
1572  mpp_clusterMultiModal[mmnb][previous_cluster] -= temp_count_multimodal[mmnb];
1573  mpp_clusterMultiModal[mmnb][temp_new_cluster] = temp_count_multimodal[mmnb];
1574  assert(mpp_clusterMultiModal[mmnb][previous_cluster]>=0.);
1575  assert(mpp_clusterMultiModal[mmnb][temp_new_cluster]>=0.);
1576  }
1577  }
1578 
1579  // compute categorical probabilities for sampling the next link for the current voxel
1580  probabilities.resize(0);
1581 
1582  // add self-link if alpha not zero
1583  if (m_ddcrpAlpha>0.) probabilities.push_back(m_ddcrpLogAlpha);
1584 
1585  for(size_t fv=1; fv<fellow_voxels.size(); fv++)
1586  {
1587  int fellow_cluster = mp_voxelClusterMapping[fellow_voxels.at(fv)];
1588  if(fellow_cluster!=temp_new_cluster)
1589  {
1590  // probability of a link that will cause the fusion of two clusters
1591  HPFLTNB n0 = mp_clusterN[0][temp_new_cluster];
1592  HPFLTNB n1 = mp_clusterN[0][fellow_cluster];
1593  HPFLTNB s0 = mp_clusterSensitivity[temp_new_cluster];
1594  HPFLTNB s1 = mp_clusterSensitivity[fellow_cluster];
1595 
1596  assert (!(n0<0. || n1<0. || s0<0. || s1<0.));
1597 
1598  // ln (probability) to avoid numerical issues
1599  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)
1600  -lgamma(n0+m_gammaShape)-lgamma(n1+m_gammaShape)+lgamma(n0+n1+m_gammaShape)
1601  +lgamma(m_gammaShape)-m_gammaShape*log(m_gammaRate);
1602 
1603  // compute and add the multimodal influence only if beyond the specified number of iterations
1604  if (multimodal_info && a_iteration>=m_multiModalLag)
1605  {
1606  HPFLTNB c0 = mp_clusterCount[temp_new_cluster];
1607  HPFLTNB c1 = mp_clusterCount[fellow_cluster];
1608  assert (c0>0. && c1>0.);
1609  HPFLTNB help1 = c0*c1/(c0+c1);
1610 
1611  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1612  {
1613  HPFLTNB a0 = mpp_clusterMultiModal[mmnb][temp_new_cluster];
1614  HPFLTNB a1 = mpp_clusterMultiModal[mmnb][fellow_cluster];
1615  assert (a0>=0. && a1>=0.);
1616  HPFLTNB help2 = a0/c0-a1/c1;
1617 
1618  HPFLTNB temp_mm = (-log(mp_multiModalParam[mmnb]) - 0.5*( (help2*help2*help1)/(mp_multiModalNoiseSigma[mmnb]*mp_multiModalNoiseSigma[mmnb]) - log(help1) ));
1619 
1620  // if subsets, divide the MRI log influence by the number of subsets only if ordinary OSEM-like backprojection
1621  if (mp_nbSubsets[a_iteration]>1 && m_backprojection==0) temp_mm /= (HPFLTNB)mp_nbSubsets[a_iteration];
1622 
1623  // add the multimodal influence
1624  temp += temp_mm;
1625 
1626  }
1627  }
1628  assert (!std::isnan(temp));
1629  probabilities.push_back(temp);
1630  /*
1631  mean_proba += temp;
1632  nb_proba += 1.;
1633  std_proba += (temp-mean_proba_prev)*(temp-mean_proba_prev);
1634  */
1635  }
1636  else
1637  {
1638  // probability of a link which does not alter clusters, a link which loops into the same cluster (log(1))
1639  if (m_ddCRP==1)
1640  probabilities.push_back (0.);
1641  else if (m_ddCRP==2)
1642  probabilities.push_back(m_ddcrpLogAlpha);
1643  }
1644  }
1645 
1646  // apply exponential to log probabilities (tip to avoid numerical issues)
1647  HPFLTNB probabilities_sum=0.;
1648  for(size_t p=0; p<probabilities.size(); p++)
1649  {
1650  probabilities[p] = exp(probabilities[p]);
1651  assert (!std::isnan(probabilities[p]));
1652  assert (!std::isinf(probabilities[p]));
1653  probabilities_sum += probabilities[p];
1654  assert (!std::isinf(probabilities_sum));
1655  }
1656 
1657 
1658  INTNB p=0;
1659  // sample the categorical distribution (multinomial distribution with a single repetition)
1660  // if at least one probability greater than the HPFLTNB minimum
1661  if (probabilities_sum>std::numeric_limits<HPFLTNB>::min())
1662  {
1663  HPFLTNB current_random = sRandomNumberGenerator::GetInstance()->GenerateExtraRdmNber(0)*probabilities_sum;
1664  HPFLTNB cumulative_sum = 0.;
1665  for(p=0; p<(INTNB)probabilities.size(); p++)
1666  {
1667  cumulative_sum += probabilities[p];
1668  if(cumulative_sum>current_random) break;
1669  }
1670  }
1671  // choose a self link in the (rare) cases when the probabilities are too low to be represented by HPFLTNB
1672  // or there are no or few fellow voxels (might occur with a mask)
1673  // this can happen only if alpha is zero and hasn't been added to the list of probabilities
1674  else
1675  {
1676  assert(!(m_ddcrpAlpha>0.));
1677  p = -1;
1678  }
1679 
1680  assert(p<(INTNB)probabilities.size());
1681 
1682  // if alpha is zero, the self link probability wasn't added in the list of probabilities,
1683  // so there is an index shift of 1 with respect to the list of fellow voxels
1684  if (!(m_ddcrpAlpha>0.)) p+=1;
1685 
1686  // take into account the new next link : update parent and next links
1687  int new_next_link = fellow_voxels.at(p);
1688 
1689  mpv_parentLinks[new_next_link].push_back(current_voxel);
1690  mp_nextLink[current_voxel] = new_next_link;
1691 
1692  // take into account the new next link : update clusters
1693  int new_next_link_cluster = mp_voxelClusterMapping[new_next_link];
1694  // if the new link does not cause clusters fusion, there is nothing to do, the temporary new cluster
1695  // becomes the actual new cluster
1696  // if the new link causes clusters fusion, assign all the voxels to the cluster of the next link voxel
1697  // and release the temp_new_cluster as empty and free for assignment, and update sums per clusters
1698  if (new_next_link_cluster!=temp_new_cluster)
1699  {
1700  // update sums
1701  mp_clusterN[0][new_next_link_cluster] += mp_clusterN[0][temp_new_cluster];
1702  mp_clusterSensitivity[new_next_link_cluster] += mp_clusterSensitivity[temp_new_cluster];
1703 
1704  assert(mp_clusterN[0][new_next_link_cluster]>=0.);
1705  assert(mp_clusterSensitivity[new_next_link_cluster]>=0.);
1706 
1707  if (multimodal_info)
1708  {
1709  mp_clusterCount[new_next_link_cluster] += mp_clusterCount[temp_new_cluster];
1710  assert(mp_clusterCount[new_next_link_cluster]>=0);
1711  for (int mmnb=0; mmnb<mp_ID->GetNbMultiModalImages(); mmnb++)
1712  {
1713  mpp_clusterMultiModal[mmnb][new_next_link_cluster] += mpp_clusterMultiModal[mmnb][temp_new_cluster];
1714  assert(mpp_clusterMultiModal[mmnb][new_next_link_cluster]>=0.);
1715  }
1716  }
1717 
1718  // release the temporary new cluster
1719  mv_newClusters.push_back(temp_new_cluster);
1720  // assign all the fusioned voxels to the cluster of the child voxel
1721  for(size_t fv=0; fv<voxels_after_cut.size(); fv++)
1722  {
1723  mp_voxelClusterMapping[voxels_after_cut.at(fv)] = new_next_link_cluster;
1724  }
1725  }
1726  }
1727 
1728  /*
1729  mean_proba/=nb_proba;
1730  mean_proba_prev = mean_proba;
1731  std_proba = sqrt(std_proba/nb_proba);
1732  Cout("iRCPGSAlgorithm::SampleConditionalClustering() -> fusion probabilities mean "<< mean_proba<<" std "<<std_proba<<endl);
1733  */
1734 
1735  // End
1736  return 0;
1737 }
1738 
1739 // -----------------------------------------------------------------------------------------------------------------------------------------
1740 
1742 {
1743  if (m_verbose>=2) Cout("iRCPGSAlgorithm::SampleConditionalClusterIntensity()... " << endl);
1744 
1745  // reinitialize clusters values to -1,
1746  // trick for ensuring that the value of each cluster is sampled only once
1747  int vp;
1748  #pragma omp parallel for private(vp) schedule(guided)
1749  for (vp=0; vp<(mp_ID->GetNbVoxXYZ()+1); vp++) mp_clusterValues[vp] = -1.;
1750 
1751  INTNB nb_clusters = 0; INTNB count_empty = 0; HPFLTNB maxN = 0.; INTNB nb_relevant_voxels = 0;
1752  // sample voxel intensity for each cluster, using the updated Gamma distribution
1753  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1754  {
1755  // skip if background voxel
1756  if (!mp_listRelevantVoxelIndices[v]) continue;
1757 
1758  nb_relevant_voxels ++;
1759 
1760  int index = mp_voxelClusterMapping[v];
1761 
1762  if (m_ddCRP==0) assert(index==v);
1763 
1764  if (mp_clusterValues[index]<0.)
1765  {
1766  gamma_distribution<HPFLTNB> updated_gamma_distrib(mp_clusterN[0][index] + m_gammaShape, 1./(mp_clusterSensitivity[index] + m_gammaRate));
1767  mp_clusterValues[index] = updated_gamma_distrib(sRandomNumberGenerator::GetInstance()->GetExtraGenerator(1));
1768 
1769  assert(mp_clusterN[0][index]>=0. && mp_clusterSensitivity[index]>=0.);
1770  assert(!std::isnan(mp_clusterValues[index]) && !std::isinf(mp_clusterValues[index]) && mp_clusterValues[index]>=0.);
1771 
1772  nb_clusters++;
1773  if (mp_clusterValues[index]>maxN) maxN = mp_clusterValues[index];
1774  if (!(mp_clusterN[0][index]>0.)) count_empty++;
1775  }
1776  }
1777 
1778  // compute the current average cluster volume
1779  m_currentMeanClusterVolume = mp_ID->GetVoxSizeX() * mp_ID->GetVoxSizeY() * mp_ID->GetVoxSizeZ()* (FLTNB)nb_relevant_voxels / (FLTNB)nb_clusters;
1780 
1781  if (m_verbose>=2)
1782  {
1783  Cout(" --> Mean number of voxels per cluster = "<<((FLTNB)nb_relevant_voxels/(FLTNB)nb_clusters)<<endl);
1784  Cout(" --> Average cluster volume = "<<m_currentMeanClusterVolume<<" mm3"<<endl);
1785  }
1786 
1787  // End
1788  return 0;
1789 }
1790 
1791 // -----------------------------------------------------------------------------------------------------------------------------------------
1792 
1793 int iRCPGSAlgorithm::ComputeFellowVoxelsList(vector<INTNB>& a_fellow_voxels, int a_current_voxel)
1794 {
1795  // X,Y,Z dimensions, ordered according to the array storage :
1796  // X is the innermost and Z is the outermost dimension
1797  INTNB dim_3D = mp_ID->GetNbVoxXYZ();
1798  INTNB dim_X = mp_ID->GetNbVoxX();
1799  INTNB dim_Z = mp_ID->GetNbVoxZ();
1800  INTNB dim_XY = mp_ID->GetNbVoxXY();
1801 
1802  // build the list of fellow voxels for the current voxel
1803  a_fellow_voxels.resize(0);
1804  // add first the current voxel itself
1805  a_fellow_voxels.push_back(a_current_voxel);
1806 
1807  if (dim_Z==1)
1808  {
1809  // build the list of 2D neighbours excluding diagonal voxels
1810  const int ix = a_current_voxel%dim_X;
1811  bool y_low = (a_current_voxel>=dim_X) && mp_listRelevantVoxelIndices[a_current_voxel-dim_X];
1812  bool y_high = (a_current_voxel<dim_XY-dim_X) && mp_listRelevantVoxelIndices[a_current_voxel+dim_X];
1813  bool x_low = (ix>0) && mp_listRelevantVoxelIndices[a_current_voxel-1];
1814  bool x_high = (ix<dim_X-1) && mp_listRelevantVoxelIndices[a_current_voxel+1];
1815 
1816  if (y_low) a_fellow_voxels.push_back(a_current_voxel-dim_X);
1817  if (y_high) a_fellow_voxels.push_back(a_current_voxel+dim_X);
1818  if (x_low) a_fellow_voxels.push_back(a_current_voxel-1);
1819  if (x_high) a_fellow_voxels.push_back(a_current_voxel+1);
1820 
1821  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 );
1822  assert(a_fellow_voxels.size()>0);
1823  }
1824  else
1825  {
1826  // build the list of 3D neighbours excluding diagonal voxels
1827  const int current_voxel_xy = a_current_voxel%(dim_XY);
1828  // perform the same processing as for 2D on current_voxel_xy
1829  const int ix = current_voxel_xy%dim_X;
1830  bool y_low = (current_voxel_xy>=dim_X) && mp_listRelevantVoxelIndices[a_current_voxel-dim_X];
1831  bool y_high = (current_voxel_xy<dim_XY-dim_X) && mp_listRelevantVoxelIndices[a_current_voxel+dim_X];
1832  bool x_low = (ix>0) && mp_listRelevantVoxelIndices[a_current_voxel-1];
1833  bool x_high = (ix<dim_X-1) && mp_listRelevantVoxelIndices[a_current_voxel+1];
1834  // check z dimension
1835  bool z_low = (a_current_voxel >= dim_XY) && mp_listRelevantVoxelIndices[a_current_voxel-dim_XY];
1836  bool z_high = (a_current_voxel < dim_3D-dim_XY) && mp_listRelevantVoxelIndices[a_current_voxel+dim_XY];
1837 
1838  if (y_low) a_fellow_voxels.push_back(a_current_voxel-dim_X);
1839  if (y_high) a_fellow_voxels.push_back(a_current_voxel+dim_X);
1840  if (x_low) a_fellow_voxels.push_back(a_current_voxel-1);
1841  if (x_high) a_fellow_voxels.push_back(a_current_voxel+1);
1842  if (z_low) a_fellow_voxels.push_back(a_current_voxel-dim_XY);
1843  if (z_high) a_fellow_voxels.push_back(a_current_voxel+dim_XY);
1844 
1845  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 );
1846  assert(a_fellow_voxels.size()>0);
1847  }
1848 
1849  // End
1850  return 0;
1851 }
1852 
1853 // -----------------------------------------------------------------------------------------------------------------------------------------
1854 
1856 {
1857  // Loop on voxels with OpenMP
1858  int vp;
1859  #pragma omp parallel for private(vp) schedule(guided)
1860  for (vp=0; vp<mp_ID->GetNbVoxXYZ(); vp++)
1861  {
1862  if (!mp_listRelevantVoxelIndices[vp])
1863  {
1864  mp_ImageSpace->m4p_image[0][0][0][vp] = 0.;
1865  }
1866  else
1867  {
1868  // Compute output image value
1870  }
1871  }
1872 
1873  // End
1874  return 0;
1875 }
1876 
1877 // =====================================================================
1878 // ---------------------------------------------------------------------
1879 // ---------------------------------------------------------------------
1880 // =====================================================================
1881 
1883 {
1885  {
1886  Cerr("***** iRCPGSAlgorithm::StepAfterIterationLoop() -> A problem occurred while calling StepAfterIterationLoop() function !" << endl);
1887  return 1;
1888  }
1889  if (m_verbose>=2) Cout("iRCPGSAlgorithm::StepAfterIterationLoop ... " << endl);
1890 
1892 
1893  // Normal end
1894  return 0;
1895 }
1896 
1897 // =====================================================================
1898 // ---------------------------------------------------------------------
1899 // ---------------------------------------------------------------------
1900 // =====================================================================
1901 
1903 {
1904  (void)a_iteration; // avoid 'unused parameter' compil. warnings
1905  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepBeforeSubsetLoop ... " << endl);
1906 
1907  // End
1908  return 0;
1909 }
1910 
1911 // -----------------------------------------------------------------------------------------------------------------------------------------
1912 
1914 {
1915  if (m_verbose>=3) Cout("iRCPGSAlgorithm::StepAfterSubsetLoop() -> Clean never visited voxels and save images if needed" << endl);
1916  // Clean never visited voxels
1918  // Save the main image
1919  if (mp_ID->GetMPIRank()==0 && mp_outputIterations[a_iteration])
1920  {
1921  // Verbose
1922  if (m_verbose>=1) Cout("iRCPGSAlgorithm::StepAfterSubsetLoop() -> Save image at iteration " << a_iteration+1 << endl);
1923  // Save image
1924  if (StepImageOutput(a_iteration))
1925  {
1926  Cerr("***** iRCPGSAlgorithm::StepAfterSubsetLoop() -> A problem occurred while saving images at iteration " << a_iteration+1 << " !" << endl);
1927  return 1;
1928  }
1929  }
1930 
1931  // End
1932  return 0;
1933 }
1934 
1935 
1936 // =====================================================================
1937 // ---------------------------------------------------------------------
1938 // ---------------------------------------------------------------------
1939 // =====================================================================
1940 
1941 int iRCPGSAlgorithm::StepImageOutput(int a_iteration, int a_subset)
1942 {
1943  // =================================================================================================
1944  // Apply pre-processing steps
1945  // =================================================================================================
1946  // Copy the current image into the forward image
1948 
1949  // Apply output transaxial FOV masking
1951  {
1952  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> A problem occurred while applying output FOV masking !" << endl);
1953  return 1;
1954  }
1955  // Apply output flip
1957  {
1958  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> A problem occurred while applying output flip !" << endl);
1959  return 1;
1960  }
1961 
1962  // =================================================================================================
1963  // Save frames/gates
1964  // =================================================================================================
1965 
1966  // Save output image (note that if no basis functions at all are in use, the the output image already points to the forward image)
1967  if (mp_ImageSpace->SaveOutputImage(a_iteration, a_subset))
1968  {
1969  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> A problem occurred while saving output image !" << endl);
1970  return 1;
1971  }
1972 
1973  if (((a_iteration+1)%50)==0)
1974  {
1975  // Get the output manager
1976  sOutputManager* p_output_manager = sOutputManager::GetInstance();
1977 
1978  // ----------------------
1979  // Build the file name
1980  // ----------------------
1981 
1982  string data_file = p_output_manager->GetPathName() + p_output_manager->GetBaseName()+"_intermediary";
1983  // Add a suffix for iteration
1984  if (a_iteration >= 0)
1985  {
1986  stringstream ss; ss << a_iteration + 1;
1987  data_file.append("_it").append(ss.str());
1988  }
1989  // Add extension
1990  data_file.append(".img");
1991 
1992  // ----------------------
1993  // Write image in file
1994  // ----------------------
1995 
1996  // Open file
1997  FILE* fout = fopen(data_file.c_str(),"wb");
1998  if (fout==NULL)
1999  {
2000  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> Failed to create output file '" << data_file << "' !" << endl);
2001  return 1;
2002  }
2003 
2004  // Write the file by converting the data to the output type, currently no specific output type,
2005  // so the current data type is used
2006  INTNB nb_data = 0;
2007  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2008  {
2009  INTNB buffer = -1;
2010  if (mp_listRelevantVoxelIndices[v]) buffer = ((INTNB)(mp_voxelClusterMapping[v]));
2011  nb_data += fwrite(&buffer,sizeof(INTNB),1,fout);
2012  }
2013 
2014  // Close file
2015  fclose(fout);
2016 
2017  // Check writing
2018  if (nb_data!=mp_ID->GetNbVoxXYZ())
2019  {
2020  Cerr("***** iRCPGSAlgorithm::StepImageOutput() -> Failed to write all data into the output file '" << data_file << "' !" << endl);
2021  return 1;
2022  }
2023  }
2024 
2025  // Normal end
2026  return 0;
2027 }
2028 
2029 // =====================================================================
2030 // ---------------------------------------------------------------------
2031 // ---------------------------------------------------------------------
2032 // =====================================================================
2033 
2035 {
2036  cout << "" << endl;
2037  cout << "Random Clustering Prior - Gibbs Sampler : probabilistic Bayesian inference algorithm (no optimization)" << endl;
2038  cout << "M. Filipovic et al : PET reconstruction of the posterior image probability, including multimodal images." << endl;
2039  cout << "" << endl;
2040  cout << "The algorithm is launched using -prob, either with command line options (-prob RCPGS:option1name=option1value,option2name=option2value,...)" << endl;
2041  cout << "or with a config file (-prob RCPGS:config_file.conf), see also the default config file for examples of option values." << endl;
2042  cout << "" << endl;
2043  cout << "Required options :" << endl;
2044  cout << " ddCRP = type of ddCRP prior (0 = no ddCRP, 1 = original ddCRP (recommended), 2 = modified ddCRP)" << endl;
2045  cout << " alpha = ddCRP hyperparameter, the unnormalized probability of drawing a self link" << endl;
2046  cout << " gammaShape = Gamma prior distribution shape parameter (0.5 recommended)" << endl;
2047  cout << " gammaRate = Gamma prior distribution rate parameter (1.e-18 recommended)" << endl;
2048  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;
2049  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;
2050  cout << " multiModalLag = number of iterations after which the multimodal images start affecting voxels clustering" << endl;
2051  cout << "" << endl;
2052  cout << "Optional options (if one of these is specified, all the others must be specified as well):" << endl;
2053  cout << " meanClusterVolumeMin = minimum threshold for average cluster volume (in mm3), used for tuning ddCRP alpha automatically through iterations" << endl;
2054  cout << " meanClusterVolumeMax = maximum threshold for average cluster volume (in mm3), used for tuning ddCRP alpha automatically through iterations" << endl;
2055  cout << " alphaIncrement = multiplicative increment for tuning ddCRP alpha through iterations" << endl;
2056  cout << "" << endl;
2057 }
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.