CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
oImageSpace.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
22 */
23 
30 #include "oImageSpace.hh"
31 #include "vOptimizer.hh"
32 
33 // =====================================================================
34 // ---------------------------------------------------------------------
35 // ---------------------------------------------------------------------
36 // =====================================================================
37 
39 {
40  // Initialize the member variables to their default values
41  m_loadedSensitivity = false;
42  m_loadedMultiModal = false;
43  m_loadedMask = false;
44  mp_ID = NULL;
45  m_verbose = -1;
49  // Set all images pointers to NULL
50  m4p_image = NULL;
51  m4p_forwardImage = NULL;
52  m6p_backwardImage = NULL;
53  m5p_sensitivity = NULL;
54  m4p_attenuation = NULL;
57  m4p_outputImage = NULL;
58  mp_visitedVoxelsImage = NULL;
59  m2p_projectionImage = NULL;
60  m2p_multiModalImage = NULL;
61  mp_maskImage = NULL;
62 }
63 
64 // =====================================================================
65 // ---------------------------------------------------------------------
66 // ---------------------------------------------------------------------
67 // =====================================================================
68 
70 {
71  // Call the deallocation of the miscellaneous images here
73 }
74 
75 // =====================================================================
76 // ---------------------------------------------------------------------
77 // ---------------------------------------------------------------------
78 // =====================================================================
79 
81 {
82  // Verbose
83  if (m_verbose>=3) Cout("oImageSpace::InstantiateImage() -> Initialize to 0."<< endl);
84 
85  // For each temporal dimensions, the number of related images is recovered from the number of
86  // intrinsic time basis functions stored in mp_ID. If no intrinsic temporal basis functions
87  // are initialized (standard reconstruction), this is equivalent to the standard calls to
88  // GetNbTimeFrames() / GetNbRespGates() / GetNbCardGates()
89 
90  // First pointer is for the number of time basis functions
91  m4p_image = (FLTNB****)malloc(mp_ID->GetNbTimeBasisFunctions()*sizeof(FLTNB***));
92  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
93  {
94  // Second pointer is for the number of respiratory basis functions
95  m4p_image[tbf] = (FLTNB***)malloc(mp_ID->GetNbRespBasisFunctions()*sizeof(FLTNB**));
96  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
97  {
98  // Third pointer is for the number of cardiac basis functions
99  m4p_image[tbf][rbf] = (FLTNB**)malloc(mp_ID->GetNbCardBasisFunctions()*sizeof(FLTNB*));
100  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
101  {
102  // Fourth pointer is for the 3D space
103  m4p_image[tbf][rbf][cbf] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
104  // Initialize to 0.
105  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++) m4p_image[tbf][rbf][cbf][v] = 0.;
106  }
107  }
108  }
109 }
110 
111 // =====================================================================
112 // ---------------------------------------------------------------------
113 // ---------------------------------------------------------------------
114 // =====================================================================
115 
117 {
118  // Check the first pointer
119  if (m4p_image)
120  {
121  // Verbose
122  if (m_verbose>=3) Cout("oImageSpace::DeallocateImage() -> Free memory"<< endl);
123  // Loop on time basis functions
124  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++) if (m4p_image[tbf])
125  {
126  // Loop on respiratory basis functions
127  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++) if (m4p_image[tbf][rbf])
128  {
129  // Loop on cardiac basis functions
130  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++) if (m4p_image[tbf][rbf][cbf])
131  {
132  free(m4p_image[tbf][rbf][cbf]);
133  }
134  free(m4p_image[tbf][rbf]);
135  }
136  free(m4p_image[tbf]);
137  }
138  free(m4p_image);
139  }
140  // Reset the pointer to NULL
141  m4p_image = NULL;
142 }
143 
144 // =====================================================================
145 // ---------------------------------------------------------------------
146 // ---------------------------------------------------------------------
147 // =====================================================================
148 
150 {
151  // Verbose
152  if (m_verbose>=3) Cout("oImageSpace::InstantiateForwardImage() -> Initialize to 0."<< endl);
153 
154  // For each temporal dimensions, the number of related images is recovered from the number of
155  // intrinsic time basis functions stored in mp_ID. If no intrinsic temporal basis functions
156  // are initialized (standard reconstruction), this is equivalent to the standard calls to
157  // GetNbTimeFrames() / GetNbRespGates() / GetNbCardGates()
158 
159  // First pointer is for the number of time basis functions
160  m4p_forwardImage = (FLTNB****)malloc(mp_ID->GetNbTimeBasisFunctions()*sizeof(FLTNB***));
161  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
162  {
163  // Second pointer is for the number of respiratory basis functions
164  m4p_forwardImage[tbf] = (FLTNB***)malloc(mp_ID->GetNbRespBasisFunctions()*sizeof(FLTNB**));
165  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
166  {
167  // Third pointer is for the number of cardiac basis functions
168  m4p_forwardImage[tbf][rbf] = (FLTNB**)malloc(mp_ID->GetNbCardBasisFunctions()*sizeof(FLTNB*));
169  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
170  {
171  // Fourth pointer is for the 3D space
172  m4p_forwardImage[tbf][rbf][cbf] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
173  // Initialize to 0.
174  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++) m4p_forwardImage[tbf][rbf][cbf][v] = 0.;
175  }
176  }
177  }
178 }
179 
180 // =====================================================================
181 // ---------------------------------------------------------------------
182 // ---------------------------------------------------------------------
183 // =====================================================================
184 
186 {
187  // Check the first pointer
188  if (m4p_forwardImage)
189  {
190  // Verbose
191  if (m_verbose>=3) Cout("oImageSpace::DeallocateForwardImage() -> Free memory"<< endl);
192  // Loop on time basis functions
193  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++) if (m4p_forwardImage[tbf])
194  {
195  // Loop on respiratory basis functions
196  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++) if (m4p_forwardImage[tbf][rbf])
197  {
198  // Loop on cardiac basis functions
199  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++) if (m4p_forwardImage[tbf][rbf][cbf])
200  {
201  free(m4p_forwardImage[tbf][rbf][cbf]);
202  }
203  free(m4p_forwardImage[tbf][rbf]);
204  }
205  free(m4p_forwardImage[tbf]);
206  }
207  free(m4p_forwardImage);
208  }
209 
210  // Reset the pointer to NULL
211  m4p_forwardImage = NULL;
212 }
213 
214 // =====================================================================
215 // ---------------------------------------------------------------------
216 // ---------------------------------------------------------------------
217 // =====================================================================
218 
220 {
221  // Verbose
222  if (m_verbose>=3) Cout("oImageSpace::InstantiateBackwardImageFromDynamicBasis() -> Initialize to 0."<< endl);
223 
224  // For each temporal dimensions, the number of related images is recovered from the number of
225  // intrinsic time basis functions stored in mp_ID. If no intrinsic temporal basis functions
226  // are initialized (standard reconstruction), this is equivalent to the standard calls to
227  // GetNbTimeFrames() / GetNbRespGates() / GetNbCardGates()
228 
229  // First pointer is for the number of backward images (in case the optimizer does need more than 1)
230  m_nbBackwardImages = a_nbBackwardImages;
231  m6p_backwardImage = (FLTNB******)malloc(m_nbBackwardImages*sizeof(FLTNB*****));
232  for (int img=0; img<m_nbBackwardImages; img++)
233  {
234  // Second pointer is for the number of threads, in order to have thread-safe backward projections
235  m6p_backwardImage[img] = (FLTNB*****)malloc(mp_ID->GetNbThreadsForProjection()*sizeof(FLTNB****));
236  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
237  {
238  // The third pointer is for the number of time basis functions
239  m6p_backwardImage[img][th] = (FLTNB****)malloc(mp_ID->GetNbTimeBasisFunctions()*sizeof(FLTNB***));
240  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
241  {
242  // The fourth pointer is for the number of respiratory basis functions
243  m6p_backwardImage[img][th][tbf] = (FLTNB***)malloc(mp_ID->GetNbRespBasisFunctions()*sizeof(FLTNB**));
244  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
245  {
246  // The fifth pointer is for the number of cardiac basis functions
247  m6p_backwardImage[img][th][tbf][rbf] = (FLTNB**)malloc(mp_ID->GetNbCardBasisFunctions()*sizeof(FLTNB*));
248  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
249  {
250  // The sixth pointer is for the 3D space
251  m6p_backwardImage[img][th][tbf][rbf][cbf] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
252  }
253  }
254  }
255  }
256  }
257 }
258 
259 // =====================================================================
260 // ---------------------------------------------------------------------
261 // ---------------------------------------------------------------------
262 // =====================================================================
263 
265 {
266  // Check the first pointer
267  if (m6p_backwardImage)
268  {
269  // Verbose
270  if (m_verbose>=3) Cout("oImageSpace::DeallocateBackwardImageFromDynamicBasis() -> Free memory" << endl);
271  // Loop on backward images (in case the optimizer does need more than 1)
272  for (int img=0; img<m_nbBackwardImages; img++) if (m6p_backwardImage[img]!=NULL)
273  {
274  // Loop on threads
275  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++) if (m6p_backwardImage[img][th])
276  {
277  // Loop on time basis functions
278  for (int tbf=0 ; tbf<mp_ID->GetNbTimeBasisFunctions() ; tbf++) if (m6p_backwardImage[img][th][tbf])
279  {
280  // Loop on respiratory basis functions
281  for (int rbf=0 ; rbf<mp_ID->GetNbRespBasisFunctions() ; rbf++) if (m6p_backwardImage[img][th][tbf][rbf])
282  {
283  // Loop on cardiac basis functions
284  for (int cbf=0 ; cbf<mp_ID->GetNbCardBasisFunctions() ; cbf++) if (m6p_backwardImage[img][th][tbf][rbf][cbf])
285  {
286  free(m6p_backwardImage[img][th][tbf][rbf][cbf]);
287  }
288  free(m6p_backwardImage[img][th][tbf][rbf]);
289  }
290  free(m6p_backwardImage[img][th][tbf]);
291  }
292  free(m6p_backwardImage[img][th]);
293  }
294  free(m6p_backwardImage[img]);
295  }
296  free(m6p_backwardImage);
297  }
298  // Reset the pointer to NULL
299  m6p_backwardImage = NULL;
300 }
301 
302 // =====================================================================
303 // ---------------------------------------------------------------------
304 // ---------------------------------------------------------------------
305 // =====================================================================
306 
308 {
309  // Verbose
310  if (m_verbose>=3) Cout("oImageSpace::InstantiateBackwardImageFromDynamicBins() -> Initialize to 0."<< endl);
311  // Not related to an optimizer, so we consider only one image
312  m6p_backwardImage = (FLTNB******)malloc(1*sizeof(FLTNB*****));
313  // Projection threads
314  m6p_backwardImage[0] = (FLTNB*****)malloc(mp_ID->GetNbThreadsForProjection()*sizeof(FLTNB****));
315  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
316  {
317  // Time frames
318  m6p_backwardImage[0][th] = (FLTNB****)malloc(mp_ID->GetNbTimeFrames()*sizeof(FLTNB***));
319  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
320  {
321  // Respiratory gates
322  m6p_backwardImage[0][th][fr] = (FLTNB***)malloc(mp_ID->GetNb1stMotImgsForLMS(fr)*sizeof(FLTNB**));
323  for (int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
324  {
325  // Cardiac gates
326  m6p_backwardImage[0][th][fr][rg] = (FLTNB**)malloc(mp_ID->GetNb2ndMotImgsForLMS()*sizeof(FLTNB*));
327  for (int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
328  {
329  // Number of voxels
330  m6p_backwardImage[0][th][fr][rg][cg] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
331  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++) m6p_backwardImage[0][th][fr][rg][cg][v] = 0.;
332  }
333  }
334  }
335  }
336 }
337 
338 // =====================================================================
339 // ---------------------------------------------------------------------
340 // ---------------------------------------------------------------------
341 // =====================================================================
342 
344 {
345  // Check the first pointer
346  if (m6p_backwardImage)
347  {
348  // Verbose
349  if (m_verbose>=3) Cout("oImageSpace::DeallocateBackwardImageFromDynamicBins() -> Free memory"<< endl);
350  // We assumed only one image when considering dynamic bins
351  if (m6p_backwardImage[0]!=NULL)
352  {
353  // Loop on threads
354  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++) if (m6p_backwardImage[0][th])
355  {
356  // Loop on time frames
357  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++) if (m6p_backwardImage[0][th][fr])
358  {
359  // Loop on respiratory gates
360  for (int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++) if (m6p_backwardImage[0][th][fr][rg])
361  {
362  // Loop on cardiac gates
363  for (int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++) if (m6p_backwardImage[0][th][fr][rg][cg])
364  {
365  free(m6p_backwardImage[0][th][fr][rg][cg]);
366  }
367  free(m6p_backwardImage[0][th][fr][rg]);
368  }
369  free(m6p_backwardImage[0][th][fr]);
370  }
371  free(m6p_backwardImage[0][th]);
372  }
373  free(m6p_backwardImage[0]);
374  }
375  free(m6p_backwardImage);
376  }
377  // Reset the pointer to NULL
378  m6p_backwardImage = NULL;
379 }
380 
381 // =====================================================================
382 // ---------------------------------------------------------------------
383 // ---------------------------------------------------------------------
384 // =====================================================================
385 
386 void oImageSpace::InstantiateSensitivityImage(const string& a_pathToSensitivityImage)
387 {
388  // -----------------------------------------------------------------------------------------------------
389  // Case 1: a path to a sensitivity image is provided, meaning that we will not compute the sensitivity
390  // on-the-fly. In that case, we do not take the multi-threading into account. Also note that in
391  // histogram mode, the same sensitivity image will be used in optimization algorithms for all
392  // subsets.
393  // -----------------------------------------------------------------------------------------------------
394 
395  if (!a_pathToSensitivityImage.empty())
396  {
397  // Verbose
398  if (m_verbose>=3) Cout("oImageSpace::InstantiateSensitivityImage() -> Will be loaded from '" << a_pathToSensitivityImage << "'" << endl);
399  // Allocate for all threads first
400  m5p_sensitivity = (FLTNB*****)malloc(mp_ID->GetNbThreadsForProjection()*sizeof(FLTNB****));
401  // Allocate the rest only for the first thread
402  int thread_0 = 0;
403  m5p_sensitivity[thread_0] = (FLTNB****)malloc(mp_ID->GetNbTimeFrames()*sizeof(FLTNB***));
404  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
405  {
406  m5p_sensitivity[thread_0][fr] = (FLTNB***)malloc(mp_ID->GetNbRespGates()*sizeof(FLTNB**));
407  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
408  {
409  m5p_sensitivity[thread_0][fr][rg] = (FLTNB**)malloc(mp_ID->GetNbCardGates()*sizeof(FLTNB*));
410  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
411  m5p_sensitivity[thread_0][fr][rg][cg] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
412  }
413  }
414  // Make all thread pointers pointing to the first
415  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++)
416  m5p_sensitivity[th] = m5p_sensitivity[thread_0];
417  // Set the flag for loaded sensitivity to true
418  m_loadedSensitivity = true;
419  }
420 
421  // -----------------------------------------------------------------------------------------------------
422  // Case 2: no sensitivity provided, meaning we will commpute it on-the-fly. We thus need it to be
423  // thread-safe, so we allocate for all threads.
424  // -----------------------------------------------------------------------------------------------------
425 
426  else
427  {
428  // Verbose
429  if (m_verbose>=3) Cout("oImageSpace::InstantiateSensitivityImage() -> For all threads"<< endl);
430  // Allocate for all threads first
431  m5p_sensitivity = (FLTNB*****)malloc(mp_ID->GetNbThreadsForProjection()*sizeof(FLTNB****));
432  // Allocate the rest only for all threads
433  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
434  {
435  m5p_sensitivity[th] = (FLTNB****)malloc(mp_ID->GetNbTimeFrames()*sizeof(FLTNB***));
436  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
437  {
438  m5p_sensitivity[th][fr] = (FLTNB***)malloc(mp_ID->GetNbRespGates()*sizeof(FLTNB**));
439  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
440  {
441  m5p_sensitivity[th][fr][rg] = (FLTNB**)malloc(mp_ID->GetNbCardGates()*sizeof(FLTNB*));
442  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
443  {
444  m5p_sensitivity[th][fr][rg][cg] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
445  // Initialize to 0.
446  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++) m5p_sensitivity[th][fr][rg][cg][v] = 0.;
447  }
448  }
449  }
450  }
451  // Set the flag for loaded sensitivity to false
452  m_loadedSensitivity = false;
453  }
454 }
455 
456 // =====================================================================
457 // ---------------------------------------------------------------------
458 // ---------------------------------------------------------------------
459 // =====================================================================
460 
462 {
463  // Check the first pointer
464  if (m5p_sensitivity)
465  {
466  // Verbose
467  if (m_verbose>=3) Cout("oImageSpace::DeallocateSensitivityImage() -> Free memory"<< endl);
468  // If loaded sensitivity then fully deallocate only the first thread pointer
470  {
471  int thread_0 = 0;
472  if (m5p_sensitivity[thread_0])
473  {
474  // Loop on time frames
475  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++) if (m5p_sensitivity[thread_0][fr])
476  {
477  // Loop on respiratory gates
478  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++) if (m5p_sensitivity[thread_0][fr][rg])
479  {
480  // Loop on cardiac gates
481  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++) if (m5p_sensitivity[thread_0][fr][rg][cg])
482  {
483  free(m5p_sensitivity[thread_0][fr][rg][cg]);
484  }
485  free(m5p_sensitivity[thread_0][fr][rg]);
486  }
487  free(m5p_sensitivity[thread_0][fr]);
488  }
489  free(m5p_sensitivity[thread_0]);
490  }
491  }
492  // Otherwise, loop on all threads
493  else
494  {
495  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++) if (m5p_sensitivity[th])
496  {
497  // Loop on time frames
498  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++) if (m5p_sensitivity[th][fr])
499  {
500  // Loop on respiratory gates
501  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++) if (m5p_sensitivity[th][fr][rg])
502  {
503  // Loop on cardiac gates
504  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++) if (m5p_sensitivity[th][fr][rg][cg])
505  {
506  free(m5p_sensitivity[th][fr][rg][cg]);
507  }
508  free(m5p_sensitivity[th][fr][rg]);
509  }
510  free(m5p_sensitivity[th][fr]);
511  }
512  free(m5p_sensitivity[th]);
513  }
514  }
515  // Free the first pointer
516  free(m5p_sensitivity);
517  }
518  // And set the pointer to NULL
519  m5p_sensitivity = NULL;
520 }
521 
522 // =====================================================================
523 // ---------------------------------------------------------------------
524 // ---------------------------------------------------------------------
525 // =====================================================================
526 
528 {
529  // In this function, we want to allocate a new miscellaneous image on the double pointer member
530  // m2p_miscellaneousImage, and then we return the pointer to the allocated image.
531 
532  // Update the number of miscellaneous images
534  // Case 1: the current number of image is 0
536  {
537  // Allocate the first pointer
539  }
540  // Case 2: we already have some miscellaneous images
541  else
542  {
543  // Reallocate the first pointer
545  }
546  // Allocate the image
548  // Return the pointer
550 }
551 
552 // =====================================================================
553 // ---------------------------------------------------------------------
554 // ---------------------------------------------------------------------
555 // =====================================================================
556 
558 {
559  // Test the pointer existence
560  if (m2p_miscellaneousImage!=NULL)
561  {
562  // Verbose
563  if (m_verbose>=3) Cout("oImageSpace::DeallocateMiscellaneousImage() -> Free memory"<< endl);
564  // Loop over all miscellaneous images
565  for (int m=0; m<m_nbMiscellaneousImages; m++)
566  {
567  // Test the pointer and free it
569  }
570  // Free the first pointer
572  }
573 }
574 
575 // =====================================================================
576 // ---------------------------------------------------------------------
577 // ---------------------------------------------------------------------
578 // =====================================================================
579 
581 {
582  if(m_verbose>=3) Cout("oImageSpace::InstantiateVisitedVoxelsImage ..."<< endl);
583  mp_visitedVoxelsImage = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
584  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++) mp_visitedVoxelsImage[v] = 0.;
585 }
586 
587 // =====================================================================
588 // ---------------------------------------------------------------------
589 // ---------------------------------------------------------------------
590 // =====================================================================
591 
593 {
594  if(m_verbose>=3) Cout("oImageSpace::InstantiateVisitedVoxelsImage ..."<< endl);
596  mp_visitedVoxelsImage = NULL;
597 }
598 
599 // =====================================================================
600 // ---------------------------------------------------------------------
601 // ---------------------------------------------------------------------
602 // =====================================================================
603 
604 int oImageSpace::InitMultiModalImage(const vector<string>& a_pathToImage)
605 {
606  // Allocate and read only if a file is provided
607  if (!a_pathToImage.empty())
608  {
609  // Allocate memory (if not already done)
610  if (!m_loadedMultiModal)
611  {
612  m2p_multiModalImage = (FLTNB**)malloc(mp_ID->GetNbMultiModalImages()*sizeof(FLTNB*));
613  for (int nb=0; nb<mp_ID->GetNbMultiModalImages(); nb++)
614  {
615  // Verbose
616  if (m_verbose>=3) Cout("oImageSpace::InitMultiModalImage() -> From file '" << a_pathToImage.at(nb) << "'"<< endl);
617  // Open file
618  ifstream image_file(a_pathToImage.at(nb).c_str(), ios::in|ios::binary);
619  if (!image_file.is_open())
620  {
621  Cerr("***** oImageSpace::InitMultiModalImage() -> Input file '" << a_pathToImage.at(nb) << "' is missing or corrupted !" << endl);
622  // Read failure implies that the multiModal image will never be used, so free all the allocated memory
624  return 1;
625  }
626  m2p_multiModalImage[nb] = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
627  // Interfile image reading (INTF_LERP_ENABLED = interpolation allowed)
628  if (IntfReadImage(a_pathToImage.at(nb), m2p_multiModalImage[nb], mp_ID, m_verbose, INTF_LERP_ENABLED))
629  {
630  Cerr("***** oImageSpace::InitMultiModalImage() -> Error reading interfile image '" << a_pathToImage.at(nb) << "' !" << endl);
631  // Read failure implies that the multiModal image will never be used, so free all the allocated memory
633  return 1;
634  }
635  // Close file
636  image_file.close();
637  }
638  // Flag as loaded
639  m_loadedMultiModal = true;
640  }
641  }
642  // End
643  return 0;
644 }
645 
646 // =====================================================================
647 // ---------------------------------------------------------------------
648 // ---------------------------------------------------------------------
649 // =====================================================================
650 
652 {
653  // Check the first pointer
655  {
656  // Verbose
657  if (m_verbose>=3) Cout("oImageSpace::DeallocateMultiModalImage() -> Free memory" << endl);
658  for (int nb=0; nb<mp_ID->GetNbMultiModalImages(); nb++) if (m2p_multiModalImage[nb]) free(m2p_multiModalImage[nb]);
659  free(m2p_multiModalImage);
660  }
661  // Reset the pointer to NULL
662  m2p_multiModalImage = NULL;
663 }
664 
665 // =====================================================================
666 // ---------------------------------------------------------------------
667 // ---------------------------------------------------------------------
668 // =====================================================================
669 
670 int oImageSpace::InitMaskImage(const string& a_pathToImage)
671 {
672  // Allocate and read only if a file is provided
673  if (!a_pathToImage.empty())
674  {
675  // Verbose
676  if (m_verbose>=3) Cout("oImageSpace::InitMaskImage() -> From file '" << a_pathToImage << "'"<< endl);
677  // Open file
678  ifstream image_file(a_pathToImage.c_str(), ios::in|ios::binary);
679  if (!image_file.is_open())
680  {
681  Cerr("***** oImageSpace::InitMaskImage() -> Input file '" << a_pathToImage << "' is missing or corrupted !" << endl);
682  return 1;
683  }
684  // Allocate memory (if not already done)
685  if (!m_loadedMask)
686  {
687  mp_maskImage = (FLTNB*)malloc(mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
688  }
689  // Interfile image reading (INTF_LERP_ENABLED = interpolation allowed)
691  {
692  Cerr("***** oImageSpace::InitMaskImage() -> Error reading interfile image '" << a_pathToImage << "' !" << endl);
693  // Read failure implies that the mask image will never be used, so free all the allocated memory
695  return 1;
696  }
697  // Flag as loaded
698  m_loadedMask = true;
699  // Close file
700  image_file.close();
701  }
702  // End
703  return 0;
704 }
705 
706 // =====================================================================
707 // ---------------------------------------------------------------------
708 // ---------------------------------------------------------------------
709 // =====================================================================
710 
712 {
713  // Check pointer
714  if (mp_maskImage)
715  {
716  // Verbose
717  if (m_verbose>=3) Cout("oImageSpace::DeallocateMaskImage() -> Free memory" << endl);
718  free(mp_maskImage);
719  }
720  // Set the pointer to NULL
721  mp_maskImage = NULL;
722  // flag as not loaded
723  m_loadedMask = false;
724 }
725 
726 
727 
728 
729 // =====================================================================
730 // ---------------------------------------------------------------------
731 // ---------------------------------------------------------------------
732 // =====================================================================
733 /*
734  \fn InstantiateOutputImage()
735  \brief Instanciate Image matrices dedicated to output writing on disk
736  \details Additionnal output image matrix is needed in the case the reconstruction uses intrinsic temporal basis functions
737  In this case, the image matrices are defined in the temporal image basis functions space, therefore requiring an additional step to recover the images in the regular image-space.
738  If no intrinsic temporal basis functions are used, m4p_outputImage just refers to the address of the image matrix containing the regular image.
739 */
741 {
742  if(m_verbose>=3) Cout("oImageSpace::InstantiateOutputImage ..."<< endl);
743 
744  // Here, we will allocate the first 3 pointers to the dynamic dimensions
746  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
747  {
748  m4p_outputImage[fr] = new FLTNB**[mp_ID->GetNbRespGates()];
749  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
750  {
751  m4p_outputImage[fr][rg] = new FLTNB*[mp_ID->GetNbCardGates()];
752  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
753  {
754  // For the last pointer (i.e. the voxels), we allocate them only if we strictly need them, which
755  // means in the case where one of the dynamic dimensions makes internal use of basis functions,
756  // otherwise, we will just make this output image pointing to the forward image which is useless
757  // at the moment of computing the output image.
758  if ( mp_ID->GetTimeStaticFlag() &&
761  {
762  // In this case, the time frames and basis functions represent the same thing, same for respiratory/cardiac
763  // gates and their equivalent basis functions
764  m4p_outputImage[fr][rg][cg] = m4p_forwardImage[fr][rg][cg];
765  }
766  else
767  {
768  // We allocate the output image
769  m4p_outputImage[fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
770  }
771  }
772  }
773  }
774 }
775 
776 
777 
778 
779 // =====================================================================
780 // ---------------------------------------------------------------------
781 // ---------------------------------------------------------------------
782 // =====================================================================
783 /*
784  \fn DeallocateOutputImage()
785  \brief Free memory for the Image matrices dedicated to output writing on disk
786 */
788 {
789  if(m_verbose>=3) Cout("oImageSpace::DeallocateOutputImage ..."<< endl);
790 
791  if (m4p_outputImage)
792  {
793  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
794  {
795  if (m4p_outputImage[fr])
796  {
797  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
798  {
799  if (m4p_outputImage[fr][rg])
800  {
801  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
802  {
803  // Distinguish between intrinsic use of dynamic basis functions or not (in the latter,
804  // the m4p_outputImage points to the m4p_forwardImage, so we do not touch it)
805  if ( (!mp_ID->GetTimeStaticFlag() ||
806  !mp_ID->GetRespStaticFlag() ||
807  !mp_ID->GetCardStaticFlag()) &&
808  m4p_outputImage[fr][rg][cg] )
809  {
810  delete m4p_outputImage[fr][rg][cg];
811  }
812  }
813  delete[] m4p_outputImage[fr][rg];
814  }
815  }
816  delete[] m4p_outputImage[fr];
817  }
818  }
819  delete[] m4p_outputImage;
820  }
821  m4p_outputImage = NULL;
822 }
823 
824 
825 
826 // =====================================================================
827 // ---------------------------------------------------------------------
828 // ---------------------------------------------------------------------
829 // =====================================================================
830 /*
831  \fn InstantiateBwdImageForDeformation()
832  \brief Memory allocation for the buffer backward image required for image-based deformation
833 
834 void oImageSpace::InstantiateRefImagesForDeformation()
835 {
836  if(m_verbose>=3) Cout("oImageSpace::InstantiateBwdImageForDeformation ..."<< endl);
837 
838  m5p_refDynBackwardImage = new FLTNB****[m_nbBackwardImages];
839 
840  for (int img=0; img<m_nbBackwardImages; img++)
841  {
842  m5p_refDynBackwardImage[img] = new FLTNB***[mp_ID->GetNbTimeBasisFunctions()];
843  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
844  {
845  // The fourth pointer is for the number of respiratory basis functions
846  m5p_refDynBackwardImage[img][tbf] = new FLTNB**[mp_ID->GetNbRespBasisFunctions()];
847  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
848  {
849  // The fifth pointer is for the number of cardiac basis functions
850  m5p_refDynBackwardImage[img][tbf][rbf] = new FLTNB*[mp_ID->GetNbCardBasisFunctions()];
851  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
852  {
853  // The sixth pointer is for the 3D space
854  m5p_refDynBackwardImage[img][tbf][rbf][cbf] = new FLTNB[mp_ID->GetNbVoxXYZ()];
855  }
856  }
857  }
858  }
859 
860 }
861 */
862 
863 /*
864  \fn oImageSpace::InstantiateRefImagesForDeformation
865  \brief Allocate memory for the buffer sensitivity image required for image-based deformation. This function is called from the Deformation Manager
866 */
868 {
869  if(m_verbose>=3) Cout("oImageSpace::InstantiateRefImagesForDeformation ..."<< endl);
870 
871 
872  // Forward image
874  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
875  {
876  // The fourth pointer is for the number of respiratory basis functions
878  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
879  {
880  // The fifth pointer is for the number of cardiac basis functions
882  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
883  {
884  // The sixth pointer is for the 3D space
885  m4p_refDynForwardImage[tbf][rbf][cbf] = new FLTNB[mp_ID->GetNbVoxXYZ()];
886  }
887  }
888  }
889 
890 
891  // Backward image
893 
894  for (int img=0; img<m_nbBackwardImages; img++)
895  {
897  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
898  {
899  // The fourth pointer is for the number of respiratory basis functions
901  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
902  {
903  // The fifth pointer is for the number of cardiac basis functions
905  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
906  {
907  // The sixth pointer is for the 3D space
908  m5p_refDynBackwardImage[img][tbf][rbf][cbf] = new FLTNB[mp_ID->GetNbVoxXYZ()];
909  }
910  }
911  }
912  }
913 
914 
915  // Sensitivity image
916  if(m_loadedSensitivity == false)
917  {
919 
920  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
921  {
923  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
924  {
926  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
927  m4p_refDynSensitivityImage[fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
928  }
929  }
930  }
931 
932 }
933 
934 
935 // =====================================================================
936 // ---------------------------------------------------------------------
937 // ---------------------------------------------------------------------
938 // =====================================================================
939 /*
940  \fn DeallocateBwdImageForDeformation()
941  \brief Free memory for the buffer backward image required for image-based deformation
942 
943 void oImageSpace::DeallocateBwdImageForDeformation()
944 {
945  if(m_verbose>=3) Cout("oImageSpace::DeallocateBwdImageForDeformation ..."<< endl);
946 
947  for (int img=0; img<m_nbBackwardImages; img++)
948  {
949  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
950  {
951  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
952  {
953  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
954  {
955  if (m5p_refDynBackwardImage[img][tbf][rbf][cbf]) delete m5p_refDynBackwardImage[img][tbf][rbf][cbf];
956  }
957  if (m5p_refDynBackwardImage[img][tbf][rbf]) delete m5p_refDynBackwardImage[img][tbf][rbf];
958  }
959  if (m5p_refDynBackwardImage[img][tbf]) delete[] m5p_refDynBackwardImage[img][tbf];
960  }
961  if (m5p_refDynBackwardImage[img]) delete[] m5p_refDynBackwardImage[img];
962  }
963  if (m5p_refDynBackwardImage) delete[] m5p_refDynBackwardImage;
964  m5p_refDynBackwardImage = NULL;
965 }
966 */
967 
968 
969 
970 
971 // =====================================================================
972 // ---------------------------------------------------------------------
973 // ---------------------------------------------------------------------
974 // =====================================================================
975 /*
976  \fn oImageSpace::DeallocateRefImagesForDeformation
977  \brief Free memory for the buffer sensitivity image required for image-based deformation. This function is called from the Deformation Manager
978 */
980 {
981  if(m_verbose>=3) Cout("oImageSpace::DeallocateRefImagesForDeformation ..."<< endl);
982 
983  // Forward image
984  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
985  {
986  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
987  {
988  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
989  {
990  if (m4p_refDynForwardImage[tbf][rbf][cbf]) delete[] m4p_refDynForwardImage[tbf][rbf][cbf];
991  }
992  if (m4p_refDynForwardImage[tbf][rbf]) delete[] m4p_refDynForwardImage[tbf][rbf];
993  }
994  if (m4p_refDynForwardImage[tbf]) delete[] m4p_refDynForwardImage[tbf];
995  }
997 
998  m4p_refDynForwardImage = NULL;
999 
1000  // Backward image
1001  for (int img=0; img<m_nbBackwardImages; img++)
1002  {
1003  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1004  {
1005  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1006  {
1007  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1008  {
1009  if (m5p_refDynBackwardImage[img][tbf][rbf][cbf]) delete[] m5p_refDynBackwardImage[img][tbf][rbf][cbf];
1010  }
1011  if (m5p_refDynBackwardImage[img][tbf][rbf]) delete[] m5p_refDynBackwardImage[img][tbf][rbf];
1012  }
1013  if (m5p_refDynBackwardImage[img][tbf]) delete[] m5p_refDynBackwardImage[img][tbf];
1014  }
1015  if (m5p_refDynBackwardImage[img]) delete[] m5p_refDynBackwardImage[img];
1016  }
1018  m5p_refDynBackwardImage = NULL;
1019 
1020  // Sensitivity image
1021  if(m_loadedSensitivity == false)
1022  {
1023  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1024  {
1025  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1026  {
1027  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1028  if (m4p_refDynSensitivityImage[fr][rg][cg]) delete[] m4p_refDynSensitivityImage[fr][rg][cg];
1029 
1030  if (m4p_refDynSensitivityImage[fr][rg]) delete[] m4p_refDynSensitivityImage[fr][rg];
1031  }
1033  }
1036  }
1037 }
1038 
1039 // =====================================================================
1040 // ---------------------------------------------------------------------
1041 // ---------------------------------------------------------------------
1042 // =====================================================================
1043 /*
1044  \fn InstantiateSensImageForDeformation()
1045  \brief Memory allocation for the buffer sensitivity image required for image-based deformation (only for PET HISTOGRAM mode)
1046 
1047 void oImageSpace::InstantiateSensImageForDeformation()
1048 {
1049  if(m_verbose>=3) Cout("oImageSpace::InstantiateSensImageForDeformation ..."<< endl);
1050 
1051  m4p_refDynSensitivityImage = new FLTNB***[mp_ID->GetNbTimeFrames()];
1052 
1053  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1054  {
1055  m4p_refDynSensitivityImage[fr] = new FLTNB**[mp_ID->GetNbRespGates()];
1056  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1057  {
1058  m4p_refDynSensitivityImage[fr][rg] = new FLTNB*[mp_ID->GetNbCardGates()];
1059  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1060  m4p_refDynSensitivityImage[fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
1061  }
1062  }
1063 }
1064 */
1065 
1066 
1067 
1068 // =====================================================================
1069 // ---------------------------------------------------------------------
1070 // ---------------------------------------------------------------------
1071 // =====================================================================
1072 /*
1073  \fn DeallocateBwdImageForDeformation()
1074  \brief Free memory for the buffer sensitivity image required for image-based deformation
1075 
1076 void oImageSpace::DeallocateSensImageForDeformation()
1077 {
1078  if(m_verbose>=3) Cout("oImageSpace::DeallocateSensImageForDeformation ..."<< endl);
1079 
1080  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1081  {
1082  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1083  {
1084  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1085  if (m4p_refDynSensitivityImage[fr][rg][cg]) delete[] m4p_refDynSensitivityImage[fr][rg][cg];
1086 
1087  if (m4p_refDynSensitivityImage[fr][rg]) delete[] m4p_refDynSensitivityImage[fr][rg];
1088  }
1089  if (m4p_refDynSensitivityImage[fr]) delete[] m4p_refDynSensitivityImage[fr];
1090  }
1091  if (m4p_refDynSensitivityImage) delete[] m4p_refDynSensitivityImage;
1092  m4p_refDynSensitivityImage = NULL;
1093 }
1094 */
1095 
1096 
1097 // =====================================================================
1098 // ---------------------------------------------------------------------
1099 // ---------------------------------------------------------------------
1100 // =====================================================================
1101 /*
1102  \fn LMS_DeallocateAttenuationImage()
1103  \brief Free memory for the Attenuation image matrices (for analytical projection or list-mode sensitivity generation)
1104 */
1106 {
1107  if(m_verbose>=3) Cout("oImageSpace::LMS_DeallocateAttenuationImage ..."<< endl);
1108 
1109  if(m4p_attenuation != NULL)
1110  {
1111  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1112  {
1113  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
1114  {
1115  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
1116  if(m4p_attenuation[fr][rg][cg] != NULL) delete m4p_attenuation[fr][rg][cg];
1117 
1118  if(m4p_attenuation[fr][rg] != NULL) delete m4p_attenuation[fr][rg];
1119  }
1120  if(m4p_attenuation[fr] != NULL) delete m4p_attenuation[fr];
1121  }
1122  if(m4p_attenuation != NULL) delete[] m4p_attenuation;
1123  m4p_attenuation = NULL;
1124  }
1125 }
1126 
1127 
1128 
1129 
1130 // =====================================================================
1131 // ---------------------------------------------------------------------
1132 // ---------------------------------------------------------------------
1133 // =====================================================================
1134 
1135 int oImageSpace::InitAttenuationImage(const string& a_pathToAtnImage)
1136 {
1137  // todo : read the number of dynamic images from Interfile header
1138  // Allocate memory and initialize matrices according to the number of fr/rg/cg
1139 
1140  // Allocate only if an image has been provided
1141  if (a_pathToAtnImage.empty()) return 0;
1142  // Verbose
1143  if (m_verbose>=VERBOSE_NORMAL) Cout("oImageSpace::InitAttenuationImage() -> Initialize and read attenuation image from file '" << a_pathToAtnImage << "'"<< endl);
1144  // Allocate only if not already done
1145  if (!m4p_attenuation)
1146  {
1148  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1149  {
1151  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
1152  {
1153  m4p_attenuation[fr][rg] = new FLTNB*[mp_ID->GetNb2ndMotImgsForLMS()];
1154 
1155  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
1156  m4p_attenuation[fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
1157  }
1158  }
1159  }
1160 
1161  // Open file
1162  ifstream image_file(a_pathToAtnImage.c_str(), ios::in|ios::binary); // Read the corresponding attenuation image
1163  // Check opening
1164  if (!image_file.is_open())
1165  {
1166  Cerr("***** oImageSpace::InitAttenuationImage() -> Failed to open attenuation image from file '" << a_pathToAtnImage << "' !" << endl);
1167  return 1;
1168  }
1169  else
1170  {
1171  // Interfile image reading (INTF_LERP_ENABLED = interpolation allowed)
1172  if (IntfReadImage(a_pathToAtnImage, m4p_attenuation, mp_ID, m_verbose, INTF_LERP_ENABLED) )
1173  {
1174  Cerr("***** oImageSpace::InitAttenuationImage() -> An error occurred while reading from file '" << a_pathToAtnImage << "' !" << endl);
1175  return 1;
1176  }
1177 
1178  // Check if we have to copy the attenuation images to potential other frames/gates
1179 
1180  // Recover the number of dynamic images in the attenuation imag file
1181  int nb_atn_frames = 1, nb_atn_rgates = 1, nb_atn_cgates = 1;
1182 
1183  if( (IntfKeyGetValueFromFile(a_pathToAtnImage, "number of time frames := " , &nb_atn_frames, 1, KEYWORD_OPTIONAL) == KEYWORD_OPTIONAL_ERROR )
1184  ||(IntfKeyGetValueFromFile(a_pathToAtnImage, "number of respiratory gates := ", &nb_atn_rgates, 1, KEYWORD_OPTIONAL) == KEYWORD_OPTIONAL_ERROR )
1185  ||(IntfKeyGetValueFromFile(a_pathToAtnImage, "number of cardiac gates := " , &nb_atn_cgates, 1, KEYWORD_OPTIONAL) == KEYWORD_OPTIONAL_ERROR ) )
1186  {
1187  Cerr("***** oImageSpace::InitAttenuationImage() -> An error occurred while reading'number of time frames', 'number of respiratory gates' or 'number of cardiac gates' keys from file '" << a_pathToAtnImage << "' !" << endl);
1188  return 1;
1189  }
1190 
1191 
1192  // If more than one frame, Copy attenuation image to each other frames
1193 
1194  if(nb_atn_cgates == 1 && mp_ID->GetNb2ndMotImgsForLMS()>1)
1195  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1196  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
1197  for(int cg=1 ; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
1198  for(int v=0 ; v<mp_ID->GetNbVoxXYZ(); v++)
1199  m4p_attenuation[fr][rg][cg][v] = m4p_attenuation[fr][rg][0][v];
1200 
1201 
1202 
1203  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1204  if(nb_atn_rgates == 1 && mp_ID->GetNb1stMotImgsForLMS(fr)>1)
1205  for(int rg=1 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
1206  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
1207  for(int v=0 ; v<mp_ID->GetNbVoxXYZ(); v++)
1208  m4p_attenuation[fr][rg][cg][v] = m4p_attenuation[fr][0][cg][v];
1209 
1210 
1211 
1212  if(nb_atn_frames == 1 && mp_ID->GetNbTimeFrames()>1)
1213  for(int fr=1 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1214  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
1215  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
1216  for(int v=0 ; v<mp_ID->GetNbVoxXYZ(); v++)
1217  // If chronological motion correction is enabled, the number of motion image may be different for each frame
1218  // Check that here, just copy the attenuation image of the first motion image if it is the case
1219  m4p_attenuation[fr][rg][cg][v] = ( mp_ID->GetNb1stMotImgsForLMS(fr) == mp_ID->GetNb1stMotImgsForLMS(0) ) ?
1220  m4p_attenuation[0][rg][cg][v] :
1221  m4p_attenuation[0][0][cg][v] ;
1222 
1223 
1224  }
1225 
1226  // Close file
1227  image_file.close();
1228  return 0;
1229 }
1230 
1231 // =====================================================================
1232 // ---------------------------------------------------------------------
1233 // ---------------------------------------------------------------------
1234 // =====================================================================
1235 /*
1236  \fn InitImage
1237  \param a_pathToInitialImage : path to an existing image
1238  \param a_value : value to initialize each voxel with, if an input image is not provided
1239  \brief Initialize the main image, either using:
1240  - an existing image at path 'a_pathToInitialImage'
1241  - initialize each voxel with 'a_value'.
1242  \return 0 if success, positive value otherwise
1243 */
1244 int oImageSpace::InitImage(const string& a_pathToInitialImage, FLTNB a_value)
1245 {
1246  if(m_verbose>=3) Cout("oImageSpace::InitImage ..."<< endl);
1247 
1248  if (!a_pathToInitialImage.empty()) // Image initiale has been provided
1249  {
1250  if (LoadInitialImage(a_pathToInitialImage) )
1251  {
1252  Cerr("***** oImageSpace::InitImage()-> Error while trying to read file at " << a_pathToInitialImage << endl);
1253  return 1;
1254  }
1255  }
1256 
1257  else // Uniform initialization
1258  {
1259  // We initialize the content of the cylindrical FOV with the provided
1260  // value, and the exterior with a ratio of the provided value
1261  FLTNB exterior_fov_value = a_value * 1.; // For the moment we let the same value everywhere
1262  // Precast half the number of voxels over X and Y minus 1 (for efficiency)
1263  FLTNB flt_base_x = 0.5*((FLTNB)(mp_ID->GetNbVoxX()-1));
1264  FLTNB flt_base_y = 0.5*((FLTNB)(mp_ID->GetNbVoxY()-1));
1265  // Compute FOV elipse radius over X and Y, then squared
1266  FLTNB squared_radius_x = 0.5 * ((FLTNB)(mp_ID->GetNbVoxX())) * mp_ID->GetVoxSizeX();
1267  squared_radius_x *= squared_radius_x;
1268  FLTNB squared_radius_y = 0.5 * ((FLTNB)(mp_ID->GetNbVoxY())) * mp_ID->GetVoxSizeY();
1269  squared_radius_y *= squared_radius_y;
1270  // We assume that the computation of the distance from the center for a given
1271  // voxel and comparing it with the output FOV percentage costs more than performing
1272  // the loops in an inverse order compared to how the image is stored in memory.
1273  // Thus we begin the loops over X, then Y, then we test and if test passes, we
1274  // do the remaining loop over Z and over all dynamic dimensions.
1275  int x;
1276  #pragma omp parallel for private(x) schedule(guided)
1277  for (x=0; x<mp_ID->GetNbVoxX(); x++)
1278  {
1279  // Compute X distance from image center, then squared
1280  FLTNB squared_distance_x = (((FLTNB)x)-flt_base_x) * mp_ID->GetVoxSizeX();
1281  squared_distance_x *= squared_distance_x;
1282  // Loop over Y
1283  for (int y=0; y<mp_ID->GetNbVoxY(); y++)
1284  {
1285  // Compute Y distance from image center, then squared
1286  FLTNB squared_distance_y = (((FLTNB)y)-flt_base_y) * mp_ID->GetVoxSizeY();
1287  squared_distance_y *= squared_distance_y;
1288  // The value to affect to the voxel
1289  FLTNB affected_value = 0.;
1290  // Test if the voxel is inside the FOV elipse, then we affect the provided value
1291  if ( squared_distance_x/squared_radius_x + squared_distance_y/squared_radius_y <= 1. ) affected_value = a_value;
1292  // Else if outside, we affect the exterior value
1293  else affected_value = exterior_fov_value;
1294  // Loop over Z
1295  for (int z=0; z<mp_ID->GetNbVoxZ(); z++)
1296  {
1297  // Compute global voxel index
1298  INTNB index = z*mp_ID->GetNbVoxXY() + y*mp_ID->GetNbVoxX() + x;
1299  // Loops on dynamic dimensions
1300  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1301  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1302  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1303  // Affect image value
1304  m4p_image[tbf][rbf][cbf][index] = affected_value;
1305  }
1306  }
1307  }
1308  }
1309 
1310  return 0;
1311 }
1312 
1313 
1314 
1315 
1316 // =====================================================================
1317 // ---------------------------------------------------------------------
1318 // ---------------------------------------------------------------------
1319 // =====================================================================
1320 /*
1321  \fn LoadInitialImage()
1322  \param a_pathToImage : path to an existing image
1323  \brief Load the initial image provided by the user in the corresponding matrix
1324  \return 0 if success, positive value otherwise
1325 */
1326 int oImageSpace::LoadInitialImage(const string& a_pathToImage)
1327 {
1328  if(m_verbose>=3) Cout("oImageSpace::LoadInitialImage ..."<< endl);
1329 
1330  ifstream image_file;
1331  image_file.open(a_pathToImage.c_str(), ios::in | ios::binary);
1332 
1333  if(!image_file.is_open())
1334  {
1335  Cerr("***** oImageSpace::LoadInitialImage()-> Error reading file!" << endl);
1336  return 1;
1337  }
1338  else
1339  {
1340  // Interfile image reading (INTF_LERP_DISABLED = no interpolation allowed)
1341  // if(IntfReadImage(a_pathToImage, m4p_image, mp_ID, m_verbose, INTF_LERP_DISABLED))
1343  {
1344  Cerr("***** oImageSpace::LoadInitialImage()-> Error reading Interfile : " << a_pathToImage << " !" << endl);
1345  return 1;
1346  }
1347  }
1348  image_file.close();
1349 
1350  return 0;
1351 }
1352 
1353 
1354 
1355 
1356 // =====================================================================
1357 // ---------------------------------------------------------------------
1358 // ---------------------------------------------------------------------
1359 // =====================================================================
1360 /*
1361  \fn InitializationBackwardImage()
1362  \brief Initialize each voxel of the backward images to 0, also for sensitivity if not loaded (estimated on the fly).
1363 */
1365 {
1366  if(m_verbose>=3) Cout("oImageSpace::InitBackwardImage ..." << endl);
1367 
1368  // Reset backward images to 0.
1369  for (int img=0; img<m_nbBackwardImages; img++)
1370  {
1371  int th;
1372  #pragma omp parallel for private(th) schedule(static, 1)
1373  for (th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
1374  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1375  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1376  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1377  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1378  {
1379  m6p_backwardImage[img][th][tbf][rbf][cbf][v] = 0.;
1380  }
1381  }
1382 
1383  // If on-the-fly sensitivity, then reset to 0.
1384  if (!m_loadedSensitivity)
1385  {
1386  int th;
1387  #pragma omp parallel for private(th) schedule(static, 1)
1388  for (th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
1389  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1390  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1391  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1392  for (INTNB v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1393  m5p_sensitivity[th][fr][rg][cg][v] = 0.;
1394  }
1395 }
1396 
1397 
1398 
1399 
1400 
1401 
1402 // =====================================================================
1403 // ---------------------------------------------------------------------
1404 // ---------------------------------------------------------------------
1405 // =====================================================================
1406 /*
1407  \fn InitSensitivityImage()
1408  \param a_pathToSensitivityImage : path to the sensitivity image (should be provided only in list-mode reconstruction)
1409  \brief Initialization for the sensitivity image matrices
1410  \details Sensitivity image initialization depends on the reconstruction mode :
1411  - list-mode: Sensitivity image has been computed before reconstruction, it is loaded from the path provided in parameter
1412  - histogram: Sensitivity image is calculated on the fly during reconstruction.
1413  First dimension (thread) is only used in histogram mode, as the on-the-fly sensitivity image computation must be thread safe
1414  \return 0 if success, positive value otherwise
1415 */
1416 int oImageSpace::InitSensitivityImage(const string& a_pathToSensitivityImage)
1417 {
1418  if(m_verbose>=3) Cout("oImageSpace::InitSensitivityImage ..."<< endl);
1419 
1420  // =====================================================================================================
1421  // Case 1: a path to a sensitivity image is provided, meaning that we are in listmode and do not compute
1422  // the sensitivity on-the-fly. In that case, we do not take the multi-threading into account.
1423  // =====================================================================================================
1424 
1425  if (!a_pathToSensitivityImage.empty())
1426  {
1427  // Open file
1428  ifstream input_file;
1429  input_file.open(a_pathToSensitivityImage.c_str(), ios::binary | ios::in);
1430  // Read file
1431  if (input_file.is_open())
1432  {
1433  // Interfile image reading (INTF_LERP_DISABLED = no interpolation allowed)
1434  if(IntfReadImage(a_pathToSensitivityImage, m5p_sensitivity[0], mp_ID, m_verbose, INTF_LERP_DISABLED) )
1435  {
1436  Cerr("***** oImageSpace::InitSensitivityImage()-> Error reading Interfile : " << a_pathToSensitivityImage << " !" << endl);
1437  return 1;
1438  }
1439  input_file.close();
1440  m_loadedSensitivity = true;
1441  }
1442  else
1443  {
1444  Cerr("***** oImageSpace::InitSensitivityImage() -> Input sensitivity file '" << a_pathToSensitivityImage << "' is missing or corrupted !" << endl);
1445  return 1;
1446  }
1447  }
1448 
1449  // =====================================================================================================
1450  // Case 2: no sensitivity provided, thus we are in histogram mode and will commpute it on-the-fly. We
1451  // thus need it to be thread-safe, so we allocated for all threads.
1452  // =====================================================================================================
1453 
1454  else
1455  {
1456 /*
1457  // Standard initialization
1458  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
1459  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1460  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1461  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1462  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1463  m5p_sensitivity[th][fr][rg][cg][v] = -1.;
1464 * */
1465  // No need to initialize to any value, as it will be done by the InitBackwardImage function which is called at the beginning
1466  // of each subset, and deals with the sensitivity initialization to 0 when m_loadedSensitivity is false
1467  m_loadedSensitivity = false;
1468  }
1469 
1470  // End
1471  return 0;
1472 }
1473 
1474 
1475 
1476 
1477 
1478 // =====================================================================
1479 // ---------------------------------------------------------------------
1480 // ---------------------------------------------------------------------
1481 // =====================================================================
1482 /*
1483  \fn oImageSpace::InitRefImagesForDeformation
1484  \brief Initialize the references image dedicated to image-based deformation. This function is called from the Deformation Manager
1485 */
1487 {
1488  if(m_verbose>=3) Cout("oDeformationManager::InitBwdImageForDeformation ..." << endl);
1489 
1490  // forward image
1491  for (int img=0; img<m_nbBackwardImages; img++)
1492  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1493  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1494  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1495  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1496  m4p_refDynForwardImage[tbf][rbf][cbf][v] = m4p_forwardImage[tbf][rbf][cbf][v];
1497 
1498  // backward image
1499  for (int img=0; img<m_nbBackwardImages; img++)
1500  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1501  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1502  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1503  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1504  m5p_refDynBackwardImage[img][tbf][rbf][cbf][v] = 0.;
1505 
1506  if(m_loadedSensitivity == false)
1507  {
1508  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1509  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1510  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1511  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1512  m4p_refDynSensitivityImage[fr][rg][cg][v] = 0.;
1513  }
1514 }
1515 
1516 
1517 
1518 
1519 
1520 // =====================================================================
1521 // ---------------------------------------------------------------------
1522 // ---------------------------------------------------------------------
1523 // =====================================================================
1524 /*
1525  \fn InitializationSensImageForDeformation()
1526  \brief Initialize the buffer sensitivity image dedicated to image-based deformation, if required (histogram mode, as sensitivity is not loaded)
1527 
1528 void oImageSpace::InitSensImageForDeformation()
1529 {
1530  if(m_verbose>=3) Cout("oImageSpace::InitSensImageForDeformation ..."<< endl);
1531 
1532  if(m_loadedSensitivity == false)
1533  {
1534  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1535  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1536  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1537  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1538  m4p_refDynSensitivityImage[fr][rg][cg][v] = 0.;
1539  }
1540 }
1541 */
1542 
1543 
1544 
1545 
1546 // =====================================================================
1547 // ---------------------------------------------------------------------
1548 // ---------------------------------------------------------------------
1549 // =====================================================================
1550 /*
1551  \fn InstantiateOutputImage()
1552  \brief Compute output image using the m4p_image matrix and the time/respiratory/cardiac basis functions. Store the result in the m4p_outputImage matrix
1553  \details If time/respiratory/cardiac basis functions have been initialized, this function has no effect.
1554 */
1556 {
1557  if(m_verbose>=3) Cout("oImageSpace::ComputeOutputImage ..."<< endl);
1558 
1559  // First loop on frames
1560  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1561  {
1562  // Second loop on respiratory gates
1563  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1564  {
1565  // Third loop on cardiac gates
1566  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1567  {
1568  // Reset m4p_outputImage to 0
1569  int v;
1570  #pragma omp parallel for private(v) schedule(guided)
1571  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1572  m4p_outputImage[fr][rg][cg][v] = 0.;
1573  // First loop on time basis functions
1574  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1575  {
1576  // Get frame/basis coefficient and continue if null
1577  FLTNB time_basis_coef = mp_ID->GetTimeBasisCoefficient(tbf,fr);
1578  if (time_basis_coef==0.) continue;
1579  // Second loop on respiratory basis functions
1580  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1581  {
1582  // Get resp_gate/basis coefficient and continue if null
1583  FLTNB resp_basis_coef = mp_ID->GetRespBasisCoefficient(rbf,rg);
1584  if (resp_basis_coef==0.) continue;
1585  // Third loop on cardiac basis functions
1586  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1587  {
1588  // Get card_gate_basis coefficient and continue if null
1589  FLTNB card_basis_coef = mp_ID->GetCardBasisCoefficient(cbf,cg);
1590  if (card_basis_coef==0.) continue;
1591  // Compute global coefficient
1592  FLTNB global_basis_coeff = time_basis_coef * resp_basis_coef * card_basis_coef;
1593  // Loop on voxel with OpenMP (let the chunk size by default as all values are aligned in memory)
1594  #pragma omp parallel for private(v) schedule(guided)
1595  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1596  {
1597  // Add contribution from these basis functions
1598  m4p_outputImage[fr][rg][cg][v] += m4p_image[tbf][rbf][cbf][v] * global_basis_coeff;
1599  }
1600  }
1601  }
1602  }
1603  }
1604  }
1605  }
1606 }
1607 
1608 // =====================================================================
1609 // ---------------------------------------------------------------------
1610 // ---------------------------------------------------------------------
1611 // =====================================================================
1612 
1614 {
1615  // Note: The output image is copied in the forward image at this step
1616 
1617  // If no flip, then return
1618  if ( !mp_ID->GetFlipOutX() &&
1619  !mp_ID->GetFlipOutY() &&
1620  !mp_ID->GetFlipOutZ() ) return 0;
1621  // Verbose
1622  if (m_verbose>=2)
1623  {
1624  Cout("oImageSpace::ApplyOutputFlip() -> Flip image" << endl);
1625  if (mp_ID->GetFlipOutX()) Cout(" --> Over X" << endl);
1626  if (mp_ID->GetFlipOutY()) Cout(" --> Over Y" << endl);
1627  if (mp_ID->GetFlipOutZ()) Cout(" --> Over Z" << endl);
1628  }
1629 /*
1630  // First loop on frames
1631  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1632  {
1633  // Second loop on respiratory gates
1634  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1635  {
1636  // Third loop on cardiac gates
1637  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1638  {
1639 */
1640  // First loop on time basis functions
1641  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1642  {
1643  // Second loop on respiratory basis functions
1644  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1645  {
1646  // Third loop on cardiac basis functions
1647  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1648  {
1649  // Flip over Z
1650  if (mp_ID->GetFlipOutZ())
1651  {
1652  // Half loop over Z
1653  for (INTNB z_1=0; z_1<mp_ID->GetNbVoxZ()/2; z_1++)
1654  {
1655  // Compute opposite Z
1656  INTNB z_2 = mp_ID->GetNbVoxZ() - 1 - z_1;
1657  // For efficiency
1658  INTNB base_z1 = z_1 * mp_ID->GetNbVoxXY();
1659  INTNB base_z2 = z_2 * mp_ID->GetNbVoxXY();
1660  // Loop over Y
1661  for (INTNB y=0; y<mp_ID->GetNbVoxY(); y++)
1662  {
1663  // For efficiency
1664  INTNB base_y = y * mp_ID->GetNbVoxX();
1665  // Loop over X
1666  for (INTNB x=0; x<mp_ID->GetNbVoxX(); x++)
1667  {
1668  // Compute both indices
1669  INTNB indice_1 = base_z1 + base_y + x;
1670  INTNB indice_2 = base_z2 + base_y + x;
1671  // Switch voxels
1672 /*
1673  FLTNB buffer = m4p_outputImage[fr][rg][cg][indice_1];
1674  m4p_outputImage[fr][rg][cg][indice_1] = m4p_outputImage[fr][rg][cg][indice_2];
1675  m4p_outputImage[fr][rg][cg][indice_2] = buffer;
1676 */
1677  FLTNB buffer = m4p_forwardImage[tbf][rbf][cbf][indice_1];
1678  m4p_forwardImage[tbf][rbf][cbf][indice_1] = m4p_forwardImage[tbf][rbf][cbf][indice_2];
1679  m4p_forwardImage[tbf][rbf][cbf][indice_2] = buffer;
1680  }
1681  }
1682  }
1683  }
1684  // Flip over Y
1685  if (mp_ID->GetFlipOutY())
1686  {
1687  // Loop over Z
1688  for (INTNB z=0; z<mp_ID->GetNbVoxZ(); z++)
1689  {
1690  // For efficiency
1691  INTNB base_z = z * mp_ID->GetNbVoxXY();
1692  // Half loop over Y
1693  for (INTNB y_1=0; y_1<mp_ID->GetNbVoxY()/2; y_1++)
1694  {
1695  // Compute opposite Y
1696  INTNB y_2 = mp_ID->GetNbVoxY() - 1 - y_1;
1697  // For efficiency
1698  INTNB base_y1 = y_1 * mp_ID->GetNbVoxX();
1699  INTNB base_y2 = y_2 * mp_ID->GetNbVoxX();
1700  // Loop over X
1701  for (INTNB x=0; x<mp_ID->GetNbVoxX(); x++)
1702  {
1703  // Compute both indices
1704  INTNB indice_1 = base_z + base_y1 + x;
1705  INTNB indice_2 = base_z + base_y2 + x;
1706  // Switch voxels
1707 /*
1708  FLTNB buffer = m4p_outputImage[fr][rg][cg][indice_1];
1709  m4p_outputImage[fr][rg][cg][indice_1] = m4p_outputImage[fr][rg][cg][indice_2];
1710  m4p_outputImage[fr][rg][cg][indice_2] = buffer;
1711 */
1712  FLTNB buffer = m4p_forwardImage[tbf][rbf][cbf][indice_1];
1713  m4p_forwardImage[tbf][rbf][cbf][indice_1] = m4p_forwardImage[tbf][rbf][cbf][indice_2];
1714  m4p_forwardImage[tbf][rbf][cbf][indice_2] = buffer;
1715  }
1716  }
1717  }
1718  }
1719  // Flip over X
1720  if (mp_ID->GetFlipOutX())
1721  {
1722  // Loop over Z
1723  for (INTNB z=0; z<mp_ID->GetNbVoxZ(); z++)
1724  {
1725  // For efficiency
1726  INTNB base_z = z * mp_ID->GetNbVoxXY();
1727  // Loop over Y
1728  for (INTNB y=0; y<mp_ID->GetNbVoxY(); y++)
1729  {
1730  // For efficiency
1731  INTNB base_y = y * mp_ID->GetNbVoxX();
1732  // Half loop over X
1733  for (INTNB x_1=0; x_1<mp_ID->GetNbVoxX()/2; x_1++)
1734  {
1735  // Compute opposite X
1736  INTNB x_2 = mp_ID->GetNbVoxX() - 1 - x_1;
1737  // Compute both indices
1738  INTNB indice_1 = base_z + base_y + x_1;
1739  INTNB indice_2 = base_z + base_y + x_2;
1740  // Switch voxels
1741 /*
1742  FLTNB buffer = m4p_outputImage[fr][rg][cg][indice_1];
1743  m4p_outputImage[fr][rg][cg][indice_1] = m4p_outputImage[fr][rg][cg][indice_2];
1744  m4p_outputImage[fr][rg][cg][indice_2] = buffer;
1745 */
1746  FLTNB buffer = m4p_forwardImage[tbf][rbf][cbf][indice_1];
1747  m4p_forwardImage[tbf][rbf][cbf][indice_1] = m4p_forwardImage[tbf][rbf][cbf][indice_2];
1748  m4p_forwardImage[tbf][rbf][cbf][indice_2] = buffer;
1749  }
1750  }
1751  }
1752  }
1753  }
1754  }
1755  }
1756 
1757  // Normal end
1758  return 0;
1759 }
1760 
1761 // =====================================================================
1762 // ---------------------------------------------------------------------
1763 // ---------------------------------------------------------------------
1764 // =====================================================================
1765 
1767 {
1768  // Note: The output image is copied in the forward image at this step
1769 
1770  // If the output FOV percent is under 0 (the default value) and number of axial slices to be removed is 0, we do not mask anything
1771  if ( mp_ID->GetFOVOutPercent()<=0. &&
1772  mp_ID->GetNbSliceOutMask()==0 ) return 0;
1773  // Verbose
1774  if (m_verbose>=2)
1775  {
1776  Cout("oImageSpace::ApplyOutputFOVMasking() -> Mask output image" << endl);
1777  if (mp_ID->GetFOVOutPercent()>0.) Cout(" --> Mask transaxial FOV outside " << mp_ID->GetFOVOutPercent() << " %" << endl);
1778  if (mp_ID->GetNbSliceOutMask()>0) Cout(" --> Mask " << mp_ID->GetNbSliceOutMask() << " slices from both axial FOV limits" << endl);
1779  }
1780  // -----------------------------------------------
1781  // Transaxial FOV masking
1782  // -----------------------------------------------
1783  if (mp_ID->GetFOVOutPercent()>0.)
1784  {
1785  // Precast half the number of voxels over X and Y minus 1 (for efficiency)
1786  FLTNB flt_base_x = 0.5*((FLTNB)(mp_ID->GetNbVoxX()-1));
1787  FLTNB flt_base_y = 0.5*((FLTNB)(mp_ID->GetNbVoxY()-1));
1788  // Compute FOV elipse radius over X and Y, then squared
1789  FLTNB squared_radius_x = 0.5 * ((FLTNB)(mp_ID->GetNbVoxX())) * mp_ID->GetVoxSizeX()
1790  * mp_ID->GetFOVOutPercent() / 100.;
1791  squared_radius_x *= squared_radius_x;
1792  FLTNB squared_radius_y = 0.5 * ((FLTNB)(mp_ID->GetNbVoxY())) * mp_ID->GetVoxSizeY()
1793  * mp_ID->GetFOVOutPercent() / 100.;
1794  squared_radius_y *= squared_radius_y;
1795  // We assume that the computation of the distance from the center for a given
1796  // voxel and comparing it with the output FOV percentage costs more than performing
1797  // the loops in an inverse order compared to how the image is stored in memory.
1798  // Thus we begin the loops over X, then Y, then we test and if test passes, we
1799  // do the remaining loop over Z and over all dynamic dimensions.
1800  int x;
1801  #pragma omp parallel for private(x) schedule(guided)
1802  for (x=0; x<mp_ID->GetNbVoxX(); x++)
1803  {
1804  // Compute X distance from image center, then squared
1805  FLTNB squared_distance_x = (((FLTNB)x)-flt_base_x) * mp_ID->GetVoxSizeX();
1806  squared_distance_x *= squared_distance_x;
1807  // Loop over Y
1808  for (int y=0; y<mp_ID->GetNbVoxY(); y++)
1809  {
1810  // Compute Y distance from image center, then squared
1811  FLTNB squared_distance_y = (((FLTNB)y)-flt_base_y) * mp_ID->GetVoxSizeY();
1812  squared_distance_y *= squared_distance_y;
1813  // Test if the voxel is inside the FOV elipse, then we skip this voxel
1814  if ( squared_distance_x/squared_radius_x + squared_distance_y/squared_radius_y <= 1. ) continue;
1815  // Loop over Z
1816  for (int z=0; z<mp_ID->GetNbVoxZ(); z++)
1817  {
1818  // Compute global voxel index
1819  INTNB index = z*mp_ID->GetNbVoxXY() + y*mp_ID->GetNbVoxX() + x;
1820 /*
1821  // First loop on frames
1822  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1823  // Second loop on respiratory gates
1824  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1825  // Third loop on cardiac gates
1826  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1827 */
1828  // First loop on time basis functions
1829  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1830  // Second loop on respiratory basis functions
1831  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1832  // Third loop on cardiac basis functions
1833  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1834  // Put 0 into this voxel
1835 // m4p_outputImage[fr][rg][cg][index] = 0.;
1836  m4p_forwardImage[tbf][rbf][cbf][index] = 0.;
1837  }
1838  }
1839  }
1840  }
1841  // -----------------------------------------------
1842  // Axial FOV masking
1843  // -----------------------------------------------
1844  if (mp_ID->GetNbSliceOutMask()>0)
1845  {
1846  INTNB removed_slices = mp_ID->GetNbSliceOutMask();
1847 /*
1848  // First loop on frames
1849  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1850  {
1851  // Second loop on respiratory gates
1852  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1853  {
1854  // Third loop on cardiac gates
1855  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1856  {
1857 */
1858  // First loop on time basis functions
1859  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
1860  {
1861  // Second loop on respiratory basis functions
1862  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
1863  {
1864  // Third loop on cardiac basis functions
1865  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
1866  {
1867  // Mask slices
1868  for (int z=0; z<removed_slices; z++)
1869  {
1870  // First slices
1871  INTNB base_z_first = z*mp_ID->GetNbVoxXY();
1872  // Loop over Y and X
1873  for (int i=0; i<mp_ID->GetNbVoxXY(); i++)
1874  {
1875  INTNB index = base_z_first + i;
1876 // m4p_outputImage[fr][rg][cg][index] = 0.;
1877  m4p_forwardImage[tbf][rbf][cbf][index] = 0.;
1878  }
1879  // Last slices
1880  INTNB base_z_last = (mp_ID->GetNbVoxZ()-1-z)*mp_ID->GetNbVoxXY();
1881  // Loop over Y and X
1882  for (int i=0; i<mp_ID->GetNbVoxXY(); i++)
1883  {
1884  INTNB index = base_z_last + i;
1885 // m4p_outputImage[fr][rg][cg][index] = 0.;
1886  m4p_forwardImage[tbf][rbf][cbf][index] = 0.;
1887  }
1888  }
1889  }
1890  }
1891  }
1892  }
1893  // End
1894  return 0;
1895 }
1896 
1897 // =====================================================================
1898 // ---------------------------------------------------------------------
1899 // ---------------------------------------------------------------------
1900 // =====================================================================
1901 
1903 {
1904  // If no mask image return
1905  if ( !IsLoadedMask() ) return 0;
1906  // Verbose
1907  if (m_verbose>=2)
1908  {
1909  Cout("oImageSpace::ApplyOutputMaskImage() -> Mask output image with the provided input mask image" << endl);
1910  }
1911  // -----------------------------------------------
1912  // Masking
1913  // -----------------------------------------------
1914  int v;
1915  #pragma omp parallel for private(v) schedule(guided)
1916  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1917  {
1918  // First loop on frames
1919  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1920  // Second loop on respiratory gates
1921  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1922  // Third loop on cardiac gates
1923  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1924  {
1925  if (!(mp_maskImage[v]>0.)) m4p_outputImage[fr][rg][cg][v] = 0.;
1926  }
1927  }
1928 
1929  // End
1930  return 0;
1931 }
1932 
1933 // =====================================================================
1934 // ---------------------------------------------------------------------
1935 // ---------------------------------------------------------------------
1936 // =====================================================================
1937 
1939 {
1940  // If no mask image return
1941  if ( !IsLoadedMask() ) return 0;
1942 
1943  // Verbose
1944  if (m_verbose>=2)
1945  {
1946  Cout("oImageSpace::ApplyMaskToSensitivity() -> Mask the sensitivity image with the provided input mask image" << endl);
1947  }
1948  // -----------------------------------------------
1949  // Masking
1950  // -----------------------------------------------
1951  int thread_0 = 0;
1952  int v;
1953  #pragma omp parallel for private(v) schedule(guided)
1954  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1955  {
1956  // First loop on frames
1957  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1958  // Second loop on respiratory gates
1959  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1960  // Third loop on cardiac gates
1961  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
1962  {
1963  if (!(mp_maskImage[v]>0.)) m5p_sensitivity[thread_0][fr][rg][cg][v] = 0.;
1964  }
1965  }
1966 
1967  // End
1968  return 0;
1969 }
1970 
1971 // =====================================================================
1972 // ---------------------------------------------------------------------
1973 // ---------------------------------------------------------------------
1974 // =====================================================================
1975 
1976 int oImageSpace::ApplyMaskToBackwardImage(int a_imageIndex, int a_timeIndex, int a_respIndex, int a_cardIndex)
1977 {
1978  // If no mask image return
1979  if ( !IsLoadedMask() ) return 0;
1980 
1981  // Verbose
1982  if (m_verbose>=2)
1983  {
1984  Cout("oImageSpace::ApplyMaskToBackwardImage() -> Mask the backward image with the provided input mask image" << endl);
1985  }
1986  // -----------------------------------------------
1987  // Masking
1988  // -----------------------------------------------
1989  int thread_0 = 0;
1990  int v;
1991  #pragma omp parallel for private(v) schedule(guided)
1992  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1993  {
1994  // First loop on frames
1995  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
1996  // Second loop on respiratory gates
1997  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
1998  // Third loop on cardiac gates
1999  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
2000  {
2001  if (!(mp_maskImage[v]>0.)) m6p_backwardImage[a_imageIndex][thread_0][a_timeIndex][a_respIndex][a_cardIndex][v] = 0.;
2002  }
2003  }
2004 
2005  // End
2006  return 0;
2007 }
2008 
2009 // =====================================================================
2010 // ---------------------------------------------------------------------
2011 // ---------------------------------------------------------------------
2012 // =====================================================================
2013 /*
2014  \fn SaveOutputImage
2015  \param a_iteration : current iteration index
2016  \param a_subset : current number of subsets (or -1 by default)
2017  \brief Call the interfile function to write output image on disk
2018  \return 0 if success, positive value otherwise
2019 */
2020 int oImageSpace::SaveOutputImage(int a_iteration, int a_subset)
2021 {
2022  if (m_verbose>=3) Cout("oImageSpace::SaveOutputImage ..."<< endl);
2023 
2024  // Get the output manager
2025  sOutputManager* p_output_manager = sOutputManager::GetInstance();
2026 
2027  // Get base name
2028  string path_to_img = p_output_manager->GetPathName() + p_output_manager->GetBaseName();
2029 
2030  // Add a suffix for iteration
2031  if (a_iteration >= 0)
2032  {
2033  stringstream ss; ss << a_iteration + 1;
2034  path_to_img.append("_it").append(ss.str());
2035  }
2036 
2037  // Add a suffix for subset (if not negative by default), this means that we save a 'subset' image
2038  if (a_subset >= 0)
2039  {
2040  stringstream ss; ss << a_subset + 1;
2041  path_to_img.append("_ss").append(ss.str());
2042  }
2043 
2044  // We need one "mode" parameter which indicates if we would like to write 1 image file or several
2045  // Adjust functions according to that
2046  if (IntfWriteImgFile(path_to_img, m4p_outputImage, mp_ID, m_verbose) )
2047  {
2048  Cerr("***** oImageSpace::SaveOutputImage()-> Error writing Interfile of output image !" << endl);
2049  return 1;
2050  }
2051 
2052  // Normal end
2053  return 0;
2054 }
2055 
2056 // =====================================================================
2057 // ---------------------------------------------------------------------
2058 // ---------------------------------------------------------------------
2059 // =====================================================================
2060 
2061 int oImageSpace::SaveOutputBasisCoefficientImage(int a_iteration, int a_subset)
2062 {
2063  if (m_verbose>=3) Cout("oImageSpace::SaveOutputBasisCoefficientImage ..."<< endl);
2064 
2065  // Get the output manager
2066  sOutputManager* p_output_manager = sOutputManager::GetInstance();
2067 
2068  // Get base name
2069  string path_to_img = p_output_manager->GetPathName() + p_output_manager->GetBaseName();
2070 
2071  // Add a suffix for iteration
2072  if (a_iteration >= 0)
2073  {
2074  stringstream ss; ss << a_iteration + 1;
2075  path_to_img.append("_it").append(ss.str());
2076  }
2077 
2078  // Add a suffix for subset (if not negative by default), this means that we save a 'subset' image
2079  if (a_subset >= 0)
2080  {
2081  stringstream ss; ss << a_subset + 1;
2082  path_to_img.append("_ss").append(ss.str());
2083  }
2084 
2085  // At this step, the m4p_forwardImage contains the actual image basis coefficients for output
2087  {
2088  Cerr("***** oImageSpace::SaveOutputImage()-> Error writing Interfile of output image !" << endl);
2089  return 1;
2090  }
2091 
2092  // Normal end
2093  return 0;
2094 }
2095 
2096 
2097 
2098 // =====================================================================
2099 // ---------------------------------------------------------------------
2100 // ---------------------------------------------------------------------
2101 // =====================================================================
2102 /*
2103  \fn SaveDebugImage
2104  \param a_name : output name of the image
2105  \brief Just a debug function dedicated to write any kind of image on disk in raw format, for debugging purposes
2106 */
2107 void oImageSpace::SaveDebugImage(const string& a_name)
2108 {
2109  #ifdef CASTOR_DEBUG
2110  if(m_verbose>=3) Cout("oImageSpace::SaveDebugImage ..."<< endl);
2111  #endif
2112 
2113  ofstream output_file;
2114  output_file.open(a_name.c_str(), ios::binary | ios::out);
2115 
2116  //for (int fr=0; fr<ap_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++)
2117  // for (int rg=0; rg<ap_ImageDimensionsAndQuantification->GetNbRespGates(); rg++)
2118  // for (int cg=0; cg<ap_ImageDimensionsAndQuantification->GetNbCardGates(); cg++)
2119  // {
2120  // Write file
2121  output_file.write(reinterpret_cast<char*>(m6p_backwardImage[0][0][0][0][0]), mp_ID->GetNbVoxXYZ()*sizeof(FLTNB));
2122  // Close file
2123  output_file.close();
2124  // }
2125  // }
2126  //}
2127 }
2128 
2129 
2130 
2131 
2132 // =====================================================================
2133 // ---------------------------------------------------------------------
2134 // ---------------------------------------------------------------------
2135 // =====================================================================
2136 /*
2137  \fn PrepareForwardImage
2138  \brief Copy current image matrix in the forward-image buffer matrix
2139 */
2141 {
2142  if(m_verbose>=3) Cout("oImageSpace::PrepareForwardImage ..."<< endl);
2143 
2144  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
2145  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
2146  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
2147  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2148  m4p_forwardImage[tbf][rbf][cbf][v] = m4p_image[tbf][rbf][cbf][v];
2149 }
2150 
2151 
2152 
2153 
2154 // =====================================================================
2155 // ---------------------------------------------------------------------
2156 // ---------------------------------------------------------------------
2157 // =====================================================================
2158 /*
2159  \fn Reduce
2160  \brief Merge parallel results into the matrix of the backward image matrix of the first thread. Also for MPI.
2161  \todo Choose computation alternative (do not know yet which one is faster...)
2162 */
2164 {
2165  #if defined(CASTOR_OMP) || defined(CASTOR_MPI)
2166  if (m_verbose>=3) Cout("oImageSpace::Reduce() -> Merge parallel results" << endl);
2167  #endif
2168 
2169  // --------------------------------------------------------------------------------
2170  // Step 1: merge multi-threads results
2171  // --------------------------------------------------------------------------------
2172 
2173  #ifdef CASTOR_OMP
2174  if (m_verbose>=3) Cout(" --> Over threads ..." << endl);
2175 
2176  // Special case here where it appears to be always beneficial to use as many threads
2177  // as the number of threads used for projections. So we set it here, and set it back
2178  // at the end.
2179  omp_set_num_threads(mp_ID->GetNbThreadsForProjection());
2180 
2181  // todo : Choose computation alternative (do not know yet which one is faster...)
2182  int alternative = 2;
2183 
2184  // Alternative 1: standard loops based on the pointers hierarchy but mono-thread
2185  if (alternative==1 || mp_ID->GetNbThreadsForImageComputation()==1)
2186  {
2187  for (int img=0; img<m_nbBackwardImages; img++)
2188  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++)
2189  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
2190  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
2191  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
2192  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2193  m6p_backwardImage[img][0][tbf][rbf][cbf][v] += m6p_backwardImage[img][th][tbf][rbf][cbf][v];
2194  }
2195  // Alternative 2: multi-thread merging with the voxels loop at first to be thread safe
2196  // Maybe faster than alternative 1, but the first loop on voxels breaks the memory order... have to try
2197  else if (alternative==2)
2198  {
2199  int v;
2200  #pragma omp parallel for private(v) schedule(guided)
2201  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2202  {
2203  for (int img=0; img<m_nbBackwardImages; img++)
2204  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++)
2205  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
2206  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
2207  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
2208  m6p_backwardImage[img][0][tbf][rbf][cbf][v] += m6p_backwardImage[img][th][tbf][rbf][cbf][v];
2209  }
2210  }
2211  // If on-the-fly sensitivity, then do it also for it.
2212  if (!m_loadedSensitivity)
2213  {
2214  if (alternative==1 || mp_ID->GetNbThreadsForImageComputation()==1)
2215  {
2216  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++)
2217  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
2218  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
2219  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
2220  for (int v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2221  m5p_sensitivity[0][fr][rg][cg][v] += m5p_sensitivity[th][fr][rg][cg][v];
2222  }
2223  else if (alternative==2)
2224  {
2225  int v;
2226  #pragma omp parallel for private(v) schedule(guided)
2227  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2228  {
2229  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++)
2230  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
2231  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
2232  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
2233  m5p_sensitivity[0][fr][rg][cg][v] += m5p_sensitivity[th][fr][rg][cg][v];
2234  }
2235  }
2236  }
2237  // Set back the number of threads to the one for image computation
2238  omp_set_num_threads(mp_ID->GetNbThreadsForImageComputation());
2239  #endif
2240 
2241  // --------------------------------------------------------------------------------
2242  // Step 2: merge multi-instance results (MPI)
2243  // --------------------------------------------------------------------------------
2244 
2245  #ifdef CASTOR_MPI
2246  // We use the MPI_IN_PLACE as the send buffer so that it is the same as the result buffer
2247  if (m_verbose>=3) Cout(" --> Over instances ..." << endl);
2248 
2249  // Merge backward images
2250  for (int img=0; img<m_nbBackwardImages; img++)
2251  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
2252  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
2253  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
2254  MPI_Allreduce( MPI_IN_PLACE, &m6p_backwardImage[img][0][tbf][rbf][cbf][0], mp_ID->GetNbVoxXYZ(), FLTNBMPI, MPI_SUM, MPI_COMM_WORLD );
2255 
2256  // If on-the-fly sensitivity, then do it also for it
2257  if (!m_loadedSensitivity)
2258  {
2259  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
2260  for (int rg=0; rg<mp_ID->GetNbRespGates(); rg++)
2261  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
2262  MPI_Allreduce( MPI_IN_PLACE, &m5p_sensitivity[0][fr][rg][cg][0], mp_ID->GetNbVoxXYZ(), FLTNBMPI, MPI_SUM, MPI_COMM_WORLD );
2263  }
2264  #endif
2265 }
2266 
2267 
2268 
2269 
2270 
2271 // =====================================================================
2272 // ---------------------------------------------------------------------
2273 // ---------------------------------------------------------------------
2274 // =====================================================================
2275 /*
2276  \fn CleanNeverVisitedVoxels
2277  \brief Based on the visitedVoxelsImage, clean the never visited voxels in the image.
2278  This function must be called at the end of each iteration
2279 */
2281 {
2282  if(m_verbose>=3) Cout("oImageSpace::CleanNeverVisitedVoxels ..."<< endl);
2283  // Multi-threaded loop over voxels
2284  INTNB v;
2285  #pragma omp parallel for private(v) schedule(guided)
2286  for (v=0; v<mp_ID->GetNbVoxXYZ(); v++)
2287  {
2288  // Clean this image voxel if the voxel was never visited
2289  if (mp_visitedVoxelsImage[v]==0.)
2290  {
2291  for (int tbf=0; tbf<mp_ID->GetNbTimeBasisFunctions(); tbf++)
2292  for (int rbf=0; rbf<mp_ID->GetNbRespBasisFunctions(); rbf++)
2293  for (int cbf=0; cbf<mp_ID->GetNbCardBasisFunctions(); cbf++)
2294  m4p_image[tbf][rbf][cbf][v] = 0.;
2295  }
2296  // Reset the image
2297  mp_visitedVoxelsImage[v] = 0.;
2298  }
2299 }
2300 
2301 
2302 
2303 
2304 
2305 
2306 
2307 
2308 
2309 // LIST-MODE SENSITIVITY GENERATION FUNCTIONS
2310 // =====================================================================
2311 // ---------------------------------------------------------------------
2312 // ---------------------------------------------------------------------
2313 // =====================================================================
2314 /*
2315  \fn LMS_InstantiateImage()
2316  \brief Allocate memory for the main image matrices (for list-mode sensitivity generation)
2317  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2318 */
2320 {
2321  if(m_verbose>=3) Cout("oImageSpace::LMS_InstantiateImage ..."<< endl);
2322 
2323  m4p_image = new FLTNB***[mp_ID->GetNbTimeFrames()];
2324 
2325  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2326  {
2327  m4p_image[fr] = new FLTNB**[mp_ID->GetNb1stMotImgsForLMS(fr)];
2328 
2329  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2330  {
2331  m4p_image[fr][rg] = new FLTNB*[mp_ID->GetNb2ndMotImgsForLMS()];
2332 
2333  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2334  m4p_image[fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
2335  }
2336  }
2337 }
2338 
2339 
2340 
2341 
2342 // =====================================================================
2343 // ---------------------------------------------------------------------
2344 // ---------------------------------------------------------------------
2345 // =====================================================================
2346 /*
2347  \fn LMS_DeallocateImage()
2348  \brief Free memory for the main image matrices (for list-mode sensitivity generation)
2349  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2350 */
2352 {
2353  if(m_verbose>=3) Cout("oImageSpace::LMS_DeallocateImage ..."<< endl);
2354 
2355  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2356  {
2357  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2358  {
2359  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2360  if(m4p_image[fr][rg][cg] != NULL) delete m4p_image[fr][rg][cg];
2361 
2362  if(m4p_image[fr][rg] != NULL) delete[] m4p_image[fr][rg];
2363  }
2364  if(m4p_image[fr] != NULL) delete[] m4p_image[fr] ;
2365  }
2366 
2367  if(m4p_image != NULL) delete[] m4p_image;
2368  m4p_image = NULL;
2369 }
2370 
2371 
2372 
2373 // =====================================================================
2374 // ---------------------------------------------------------------------
2375 // ---------------------------------------------------------------------
2376 // =====================================================================
2377 /*
2378  \fn LMS_InstantiateForwardImage()
2379  \brief Allocate memory for the forward image matrices (for list-mode sensitivity generation)
2380  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2381 */
2383 {
2384  if(m_verbose>=3) Cout("oImageSpace::LMS_InstantiateForwardImage ..."<< endl);
2385 
2387 
2388  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2389  {
2391 
2392  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2393  {
2395 
2396  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2397  {
2398  m4p_forwardImage[fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
2399  }
2400  }
2401 
2402  }
2403 }
2404 
2405 
2406 
2407 // =====================================================================
2408 // ---------------------------------------------------------------------
2409 // ---------------------------------------------------------------------
2410 // =====================================================================
2411 /*
2412  \fn LMS_DeallocateForwardImage()
2413  \brief Free memory for the forward image matrices (for list-mode sensitivity generation)
2414  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2415 */
2417 {
2418  if(m_verbose>=3) Cout("oImageSpace::LMS_DeallocateForwardImage ..."<< endl);
2419 
2420  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2421  {
2422  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2423  {
2424  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2425  {
2426  if(m4p_forwardImage[fr][rg][cg] != NULL) delete m4p_forwardImage[fr][rg][cg];
2427  }
2428 
2429  if(m4p_forwardImage[fr][rg] != NULL) delete[] m4p_forwardImage[fr][rg];
2430  }
2431 
2432  if(m4p_forwardImage[fr] != NULL) delete[] m4p_forwardImage[fr];
2433  }
2434 
2435  if(m4p_forwardImage != NULL) delete[] m4p_forwardImage;
2436  m4p_forwardImage = NULL;
2437 }
2438 
2439 // =====================================================================
2440 // ---------------------------------------------------------------------
2441 // ---------------------------------------------------------------------
2442 // =====================================================================
2443 /*
2444  \fn LMS_InstantiateSensitivityImage()
2445  \brief Allocate memory for the sensitivity image matrices (for list-mode sensitivity generation)
2446  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2447 */
2449 {
2450  if(m_verbose>=3) Cout("oImageSpace::LMS_InstantiateSensitivityImage ..."<< endl);
2451 
2452  m5p_sensitivity = new FLTNB****[1];
2453  m5p_sensitivity[0] = new FLTNB***[mp_ID->GetNbTimeFrames()];
2454  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
2455  {
2456  m5p_sensitivity[0][fr] = new FLTNB**[mp_ID->GetNb1stMotImgsForLMS(fr)];
2457  for (int rg=0; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
2458  {
2459  m5p_sensitivity[0][fr][rg] = new FLTNB*[mp_ID->GetNb2ndMotImgsForLMS()];
2460  for (int cg=0; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
2461  m5p_sensitivity[0][fr][rg][cg] = new FLTNB[mp_ID->GetNbVoxXYZ()];
2462  }
2463  }
2464 }
2465 
2466 
2467 
2468 
2469 // =====================================================================
2470 // ---------------------------------------------------------------------
2471 // ---------------------------------------------------------------------
2472 // =====================================================================
2473 /*
2474  \fn LMS_DeallocateSensitivityImage()
2475  \brief Free memory for the sensitivity image matrices (for list-mode sensitivity generation)
2476  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2477 */
2479 {
2480  if(m_verbose>=3) Cout("oImageSpace::LMS_DeallocateSensitivityImage ..."<< endl);
2481 
2482  for (int fr=0; fr<mp_ID->GetNbTimeFrames(); fr++)
2483  {
2484  for (int rg=0; rg<mp_ID->GetNb1stMotImgsForLMS(fr); rg++)
2485  {
2486  for (int cg=0; cg<mp_ID->GetNb2ndMotImgsForLMS(); cg++)
2487  {
2488  if(m5p_sensitivity[0][fr][rg][cg] != NULL) delete m5p_sensitivity[0][fr][rg][cg];
2489  }
2490  if(m5p_sensitivity[0][fr][rg] != NULL)delete m5p_sensitivity[0][fr][rg];
2491  }
2492  if(m5p_sensitivity[0][fr] != NULL)delete m5p_sensitivity[0][fr];
2493  }
2494  if(m5p_sensitivity[0] != NULL) delete[] m5p_sensitivity[0];
2495 
2496  if(m5p_sensitivity != NULL) delete[] m5p_sensitivity;
2497  m5p_sensitivity = NULL;
2498 }
2499 
2500 
2501 
2502 
2503 // =====================================================================
2504 // ---------------------------------------------------------------------
2505 // ---------------------------------------------------------------------
2506 // =====================================================================
2507 /*
2508  \fn LMS_CopyAtnToImage()
2509  \brief Copy the attenuation image contained in the 'm2p_attenuation' matrix inside the m2p_image matrix (for list-mode sensitivity generation)
2510  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2511 */
2513 {
2514  if(m_verbose>=3) Cout("oImageSpace::LMS_CopyAtnToImage ..."<< endl);
2515 
2516  if(m4p_attenuation != NULL)
2517  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2518  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2519  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2520  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2521  {
2522  m4p_image[fr][rg][cg][v] = m4p_attenuation[fr][rg][cg][v];
2523  }
2524  else
2525  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2526  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2527  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2528  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2529  {
2530  m4p_image[fr][rg][cg][v] = 0;
2531  }
2532 
2533 }
2534 
2535 
2536 // =====================================================================
2537 // ---------------------------------------------------------------------
2538 // ---------------------------------------------------------------------
2539 // =====================================================================
2540 /*
2541  \fn LMS_CopyAtnToForwardImage()
2542  \param a_use1stMotion
2543  \param a_use2ndMotion
2544  \brief Copy the attenuation image contained in the 'm2p_attenuation' matrix inside the m2p_forwardImage matrix (for list-mode sensitivity generation)
2545  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2546 */
2547 void oImageSpace::LMS_CopyAtnToForwardImage(bool a_use1stMotion, bool a_use2ndMotion)
2548 {
2549  if(m_verbose>=3) Cout("oImageSpace::LMS_CopyAtnToForwardImage ..."<< endl);
2550 
2551  if(m4p_attenuation != NULL)
2552  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2553  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2554  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2555  {
2556  // If motion is enabled, copy the attenuation image in all forward image matrices
2557  int rg_atn = a_use1stMotion ? 0 : rg ;
2558  int cg_atn = a_use1stMotion ? 0 : cg ;
2559 
2560  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2561  {
2562  m4p_forwardImage[fr][rg][cg][v] = m4p_attenuation[fr][rg_atn][cg_atn][v];
2563  }
2564  }
2565  else
2566  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2567  for(int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2568  for(int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2569  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2570  {
2571  m4p_forwardImage[fr][rg][cg][v] = 0;
2572  }
2573 }
2574 
2575 // =====================================================================
2576 // ---------------------------------------------------------------------
2577 // ---------------------------------------------------------------------
2578 // =====================================================================
2579 /*
2580  \fn LMS_CopyBackwardToSensitivityImage()
2581  \brief Copy the backward image containing the result of the sensitivity back-projection, into the sensitivity matrix.
2582  \details This function is dedicated to list-mode sensitivity (LMS) generation, it is useful to apply convolution and to save the image.
2583 */
2585 {
2586  if(m_verbose>=3) Cout("oImageSpace::LMS_CopyBackwardToSensitivity ..."<< endl);
2587  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2588  for (int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2589  for (int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2590  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2591  m5p_sensitivity[0][fr][rg][cg][v] = m6p_backwardImage[0][0][fr][rg][cg][v];
2592 }
2593 
2594 
2595 // =====================================================================
2596 // ---------------------------------------------------------------------
2597 // ---------------------------------------------------------------------
2598 // =====================================================================
2599 /*
2600  \fn LMS_PrepareForwardImage()
2601  \brief Copy current image in forward-image buffer (for list-mode sensitivity generation)
2602  \details This function is dedicated to list-mode sensitivity (LMS) generation.
2603 */
2605 {
2606  if(m_verbose>=3) Cout("oImageSpace::LMS_PrepareForwardImage ..."<< endl);
2607 
2608  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
2609  for (int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2610  for (int cg=0 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2611  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2612  m4p_forwardImage[fr][rg][cg][v] = m4p_image[fr][rg][cg][v];
2613 }
2614 
2615 
2616 
2617 
2618 // =====================================================================
2619 // ---------------------------------------------------------------------
2620 // ---------------------------------------------------------------------
2621 // =====================================================================
2622 
2623 void oImageSpace::ReduceBackwardImage(int a_imageIndex, int a_timeIndex, int a_respIndex, int a_cardIndex)
2624 {
2625  #if defined(CASTOR_OMP) || defined(CASTOR_MPI)
2626  if (m_verbose>=3) Cout("oImageSpace::ReduceBackwardImage() -> Merge parallel results" << endl);
2627  #endif
2628 
2629  // --------------------------------------------------------------------------------
2630  // Step 1: merge multi-threads results
2631  // --------------------------------------------------------------------------------
2632 
2633  #ifdef CASTOR_OMP
2634  // Verbose
2635  if (m_verbose>=3) Cout(" --> Over threads ..." << endl);
2636  // Do it
2637  for (int th=1 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
2638  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2639  m6p_backwardImage[a_imageIndex][0][a_timeIndex][a_respIndex][a_cardIndex][v] += m6p_backwardImage[a_imageIndex][th][a_timeIndex][a_respIndex][a_cardIndex][v];
2640  #endif
2641 
2642  // --------------------------------------------------------------------------------
2643  // Step 2: merge multi-instance results (MPI)
2644  // --------------------------------------------------------------------------------
2645 
2646  #ifdef CASTOR_MPI
2647  // We use the MPI_IN_PLACE as the send buffer so that it is the same as the result buffer
2648  if (m_verbose>=3) Cout(" --> Over instances ..." << endl);
2649  // Merge backward images
2650  MPI_Allreduce( MPI_IN_PLACE, &m6p_backwardImage[a_imageIndex][0][a_timeIndex][a_respIndex][a_cardIndex][0], mp_ID->GetNbVoxXYZ(), FLTNBMPI, MPI_SUM, MPI_COMM_WORLD );
2651  #endif
2652 }
2653 
2654 
2655 
2656 
2657 // =====================================================================
2658 // ---------------------------------------------------------------------
2659 // ---------------------------------------------------------------------
2660 // =====================================================================
2661 /*
2662  \fn LMS_SaveSensitivityImage
2663  \param a_pathToSensitivityImage : path to the sensitivity image (should be provided only in list-mode reconstruction)
2664  \param ap_DeformationManager : Pointer to the deformation manager objet (required to retrieve the number of gates in the sensitivity image)
2665  \brief Call the interfile function to write the sensitivity image on disk
2666  \details If image deformation is enabled for respiratory/cardiac gated data, the gated images are summed up into one image and normalize
2667  \return 0 if success, positive value otherwise
2668  \todo Interfile management : if the number of sensitivity image to write is different to the actual number of image
2669  as it could be the case for dynamic imaging, if one sensitivity image is required for the whole dynamic serie,
2670  we will have to give this information to IntfWriteImgFile(), otherwise the Interfile functions will try to write several times the image (leading to segfault or error)
2671 */
2672 int oImageSpace::LMS_SaveSensitivityImage(const string& a_pathToSensitivityImage, oDeformationManager* ap_DeformationManager)
2673 {
2674  if(m_verbose>=3) Cout("oImageSpace::LMS_SaveSensitivityImage ..."<< endl);
2675 
2676  // If image-based motion is enabled, list-mode sensitivity images code generates a set of several provisional images of the reference position.
2677  // Each image is generated using one different forward/backward deformations parameters which will be used during reconstruction.
2678  // - For gated (respiratory/cardiac) motion correction, a simple average of these images is performed here for each frame.
2679  // - For timestamp-based motion (involuntary patient motion), the averaging depends on which transformation occurs in each frame.
2680  // The provisional sensitivity images are weighs accordingly to the duration of each motion subset inside the frame.
2681  // (i.e the sensitivity image of a 10 min frame, which contains a transformation at 9mn, will be composed of an average of two sensitivity images with weights of 0.9 and 0.1)
2682 
2683  int nb_reco_card_images = ap_DeformationManager->GetNbSensImagesCardDeformation(mp_ID->GetNb2ndMotImgsForLMS());
2684 
2685  // Average of the cardiac images, if cardiac gating is enabled (no entry in the loop of cardiac images otherwise)
2686  if (ap_DeformationManager->GetNbSensImagesCardDeformation(mp_ID->GetNb2ndMotImgsForLMS()) == 0)
2687  {
2688  nb_reco_card_images = 1;
2689  FLTNB card_normalization = mp_ID->GetNb2ndMotImgsForLMS();
2690 
2691  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2692  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames(); fr++)
2693  for (int rg=0 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2694  {
2695  for (int cg=1 ; cg<mp_ID->GetNb2ndMotImgsForLMS() ; cg++)
2696  m5p_sensitivity[0][fr][rg][0][v] += m5p_sensitivity[0][fr][rg][cg][v];
2697  m5p_sensitivity[0][fr][rg][0][v] /= card_normalization;
2698  }
2699  }
2700 
2701  // Average of the respiratory images, if respiratory gating is enabled (no entry in the loop of respiratory images)
2702  if (!mp_ID->IsPMotionEnabled() // No patient motion correction
2703  && ap_DeformationManager->GetNbSensImagesRespDeformation(mp_ID->GetNb1stMotImgsForLMS(0)) == 0)
2704  {
2705  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames(); fr++)
2706  {
2707  FLTNB resp_normalization = mp_ID->GetNb1stMotImgsForLMS(fr);
2708 
2709  for (int cg=0 ; cg<nb_reco_card_images ; cg++) // Dual gating : Be sure to get the right number of card images
2710  {
2711  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2712  {
2713  for (int rg=1 ; rg<mp_ID->GetNb1stMotImgsForLMS(fr) ; rg++)
2714  m5p_sensitivity[0][fr][0][cg][v] += m5p_sensitivity[0][fr][rg][cg][v];
2715 
2716  m5p_sensitivity[0][fr][0][cg][v] /= resp_normalization;
2717  }
2718  }
2719  }
2720  }
2721 
2722  // Average of the sensitivity images when involuntary patient motion correction is enabled
2723  if (mp_ID->IsPMotionEnabled() )
2724  {
2725  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames(); fr++)
2726  {
2727  //FLTNB ipm_normalization = GetNb1stMotImgsForLMS(fr);
2728 
2729  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
2730  {
2731 
2732 
2733  //for (int ipm=1 ; ipm<mp_ID->GetNb1stMotImgsForLMS(fr) ; ipm++)
2734  // m5p_sensitivity[0][fr][0][0][v] += m5p_sensitivity[0][fr][ipm][0][v] ;
2735 
2736  //m5p_sensitivity[0][fr][0][0][v] /= ipm_normalization;
2737 
2738  FLTNB sensitivity_value_avg = 0.;
2739 
2740  // Add the contribution of the sensitivity images with appropriate weighing
2741  for (int ipm=0 ; ipm<mp_ID->GetNb1stMotImgsForLMS(fr) ; ipm++)
2742  sensitivity_value_avg += m5p_sensitivity[0][fr][ipm][0][v] * mp_ID->GetListPMotionWeightInFrameForLMS(fr, ipm);
2743 
2744  // Recover the average value in the first sensitivity image
2745  m5p_sensitivity[0][fr][0][0][v] = sensitivity_value_avg;
2746  }
2747 
2748  }
2749  }
2750 
2751 
2752 
2753  if (SaveSensitivityImage(a_pathToSensitivityImage) )
2754  {
2755  Cerr("***** oImageSpace::LMS_SaveSensitivityImage()-> Error writing Sensitivity image !" << endl);
2756  return 1;
2757  }
2758 
2759  return 0;
2760 }
2761 
2762 
2763 
2764 
2765 // =====================================================================
2766 // ---------------------------------------------------------------------
2767 // ---------------------------------------------------------------------
2768 // =====================================================================
2769 /*
2770  \fn SaveSensitivityImage
2771  \param a_pathToSensitivityImage : path to the sensitivity image (should be provided only in list-mode reconstruction)
2772  \brief Call the interfile function to write the sensitivity image on disk
2773  \return 0 if success, positive value otherwise
2774 */
2775 int oImageSpace::SaveSensitivityImage(const string& a_pathToSensitivityImage)
2776 {
2777  if(m_verbose>=3) Cout("oImageSpace::SaveSensitivityImage ..."<< endl);
2778 
2779  if (IntfWriteImgFile(a_pathToSensitivityImage, m5p_sensitivity[0], mp_ID, m_verbose) )
2780  {
2781  Cerr("***** oImageSpace::SaveSensitivityImage()-> Error writing Interfile of output image !" << endl);
2782  return 1;
2783  }
2784 
2785  return 0;
2786 }
2787 
2788 
2789 
2790 
2791 // PROJECTION SCRIPT FUNCTIONS
2792 // =====================================================================
2793 // ---------------------------------------------------------------------
2794 // ---------------------------------------------------------------------
2795 // =====================================================================
2796 /*
2797  \fn PROJ_InstantiateProjectionImage()
2798  \param a_nbProjs : a number of projection slices in the projection
2799  \param a_nbPixels : a total number of pixels in the projection slices
2800  \brief Instanciate and initialize projection image for analytical projection
2801  \details This function is currently only dedicated to SPECT projection, and used by the analytical projection script
2802  \todo support for PET (requires projection format)
2803 */
2804 void oImageSpace::PROJ_InstantiateProjectionImage(int a_nbProjs, int a_nbPixels)
2805 {
2806  if(m_verbose>=3) Cout("oImageSpace::PROJ_InstantiateProjectionImage ..."<< endl);
2807 
2808  m2p_projectionImage = new FLTNB*[a_nbProjs];
2809 
2810  for(int p=0 ; p<a_nbProjs ; p++)
2811  {
2812  m2p_projectionImage[p] = new FLTNB[a_nbPixels];
2813  for(int px=0 ; px<a_nbPixels ; px++)
2814  m2p_projectionImage[p][px] = 0;
2815  }
2816 }
2817 
2818 
2819 
2820 
2821 // =====================================================================
2822 // ---------------------------------------------------------------------
2823 // ---------------------------------------------------------------------
2824 // =====================================================================
2825 /*
2826  \fn PROJ_DeallocateProjectionImage()
2827  \param a_nbProjs : a number of projection slices in the projection
2828  \brief Free memory for the projection image for analytical projection
2829  \details This function is currently only dedicated to SPECT projection, and used by the analytical projection script
2830  \todo support for PET (requires projection format)
2831 */
2833 {
2834  if(m_verbose>=3) Cout("oImageSpace::PROJ_DeallocateProjectionImage ..."<< endl);
2835 
2836  for(int p=0 ; p<a_nbProjs ; p++)
2837  {
2838  delete[] m2p_projectionImage[p];
2839  }
2840 
2841  delete[] m2p_projectionImage;
2842  m2p_projectionImage = NULL;
2843 }
2844 
2845 
2846 
2847 
2848 // =====================================================================
2849 // ---------------------------------------------------------------------
2850 // ---------------------------------------------------------------------
2851 // =====================================================================
2852 /*
2853  \fn PROJ_InitImage()
2854  \param a_pathToInitialImage : path to the image to project
2855  \brief Load the initial image for the analytical projection
2856  \return 0 if success, positive value otherwise
2857 */
2858 int oImageSpace::PROJ_InitImage(const string& a_pathToInitialImage)
2859 {
2860  if(m_verbose>=3) Cout("oImageSpace::PROJ_InitImage ..."<< endl);
2861 
2862  if (!a_pathToInitialImage.empty()) // Image initiale
2863  {
2864  if(PROJ_LoadInitialImage(a_pathToInitialImage) )
2865  {
2866  Cerr("***** oImageSpace::PROJ_InitImage()-> Error while trying to read file at " << a_pathToInitialImage << endl);
2867  return 1;
2868  }
2869  }
2870  else
2871  {
2872  {
2873  Cerr("***** oImageSpace::PROJ_InitImage()-> No projected image provided ! " << endl);
2874  return 1;
2875  }
2876  }
2877  return 0;
2878 }
2879 
2880 
2881 
2882 
2883 // =====================================================================
2884 // ---------------------------------------------------------------------
2885 // ---------------------------------------------------------------------
2886 // =====================================================================
2887 /*
2888  \fn PROJ_LoadInitialImage()
2889  \param a_pathToImage : path to the image to project
2890  \brief Load the initial image for the analytical projection
2891  \return 0 if success, positive value otherwise
2892 */
2893 int oImageSpace::PROJ_LoadInitialImage(const string& a_pathToImage)
2894 {
2895  if(m_verbose>=3) Cout("oImageSpace::PROJ_LoadInitialImage ..."<< endl);
2896 
2897  ifstream image_file;
2898  image_file.open(a_pathToImage.c_str(), ios::in | ios::binary);
2899 
2900  if(!image_file.is_open())
2901  {
2902  Cerr("***** oImageSpace::PROJ_LoadInitialImage()-> Error reading file !" << endl);
2903  return 1;
2904  }
2905  else
2906  {
2907  // Interfile image reading (INTF_LERP_DISABLED = no interpolation allowed)
2909  {
2910  Cerr("***** oImageSpace::PROJ_LoadInitialImage()-> Error reading Interfile : " << a_pathToImage << " !" << endl);
2911  return 1;
2912  }
2913 
2914  }
2915  image_file.close();
2916  return 0;
2917 }
2918 
2919 
2920 
2921 
2922 
2923 // =====================================================================
2924 // ---------------------------------------------------------------------
2925 // ---------------------------------------------------------------------
2926 // =====================================================================
2927 /*
2928  \fn PROJ_SaveProjectionImage
2929  \brief Save an image of the projected data
2930  \return 0 if success, positive value otherwise
2931  \todo Support for PET projeted data
2932  implement interfile file recovery in a scanner class function
2933 */
2935 {
2936  if(m_verbose>=3) Cout("oImageSpace::PROJ_SaveProjectionImage ..."<< endl);
2937 
2938  string img_name = "_ProjectionImage";
2939 
2940  // Initialize interfile fields structure
2941  Intf_fields Img_fields;
2942  IntfKeySetFieldsOutput(&Img_fields , mp_ID);
2943 
2944  Img_fields.process_status = "acquired";
2945 
2946  // Get specific info about projection
2948 
2949  // Recover the values in the interfile structure.
2950  // todo : This step for PET projection
2951  // todo : Implement this step in scanner class functions
2952  // todo : For spect, deal with systems with several detector heads (currently assume one head)
2953  if(p_ScanMgr->GetScannerType() == 2)
2954  {
2955  uint16_t nb_projs, nb_heads;
2956  FLTNB zoom;
2957  uint16_t nb_bins[2];
2958  FLTNB pix_size[2];
2959  FLTNB* p_angles;
2960  FLTNB* p_radius;
2961  int dir_rot;
2962  p_ScanMgr->GetSPECTSpecificParameters(&nb_projs,
2963  &nb_heads,
2964  &zoom,
2965  nb_bins,
2966  pix_size,
2967  p_angles,
2968  p_radius,
2969  &dir_rot);
2970 
2971  Img_fields.nb_projections = nb_projs;
2972  Img_fields.nb_detector_heads = nb_heads;
2973  Img_fields.mtx_size[0] = nb_bins[0];
2974  Img_fields.mtx_size[1] = nb_bins[1];
2975  Img_fields.vox_size[0] = pix_size[0];
2976  Img_fields.vox_size[1] = pix_size[1];
2977  Img_fields.extent_rotation = 360; // 360 by default
2978  // Check rotation direction from the two first angles
2979  Img_fields.direction_rotation = (dir_rot == GEO_ROT_CCW)?
2980  "CCW" :
2981  "CW";
2982  Img_fields.first_angle = p_angles[0];
2983 
2984  // Note: In Interfile v3.3, doesn't seem to have a field to provide
2985  // individually each angle and Center of rotation.
2986  // Looks like it was planned for ulterior version, at least for the
2987  // center of rotation
2988  // For now, we just write each projection angle and radius as an array key
2989 
2990  string angles_str = "{";
2991  string radius_str = "{";
2992 
2993  // Flag to check if the radius is similar for each projection angle
2994  bool has_single_radius = true;
2995 
2996  for(uint16_t p=0 ; p<nb_projs ; p++)
2997  {
2998  stringstream ss_a, ss_r;
2999  // projection angles
3000  ss_a << p_angles[p];
3001  angles_str.append(ss_a.str());
3002  (p<nb_projs-1) ? angles_str.append(",") : angles_str.append("}");
3003 
3004  // radius
3005  ss_r << p_radius[p];
3006  radius_str.append(ss_r.str());
3007  (p<nb_projs-1) ? radius_str.append(",") : radius_str.append("}");
3008  if(p_radius[p] != p_radius[0])
3009  has_single_radius = false;
3010 
3011  // Some interfile editors struggle with long line, perhaps because
3012  // they are stored in char[256] or something like that
3013  // Hence, break line after a certain number of elements
3014  if((p+1)%30 == 0)
3015  {
3016  angles_str.append("\n");
3017  radius_str.append("\n");
3018  }
3019  }
3020 
3021  // If there is one common radius, write its value in the related string
3022  if(has_single_radius)
3023  {
3024  stringstream ss;
3025  ss << p_radius[0];
3026  radius_str = ss.str();
3027  }
3028 
3029  Img_fields.projection_angles = angles_str;
3030  Img_fields.radius = radius_str;
3031 
3032 
3033  // Common code to all modalities
3034  sOutputManager* p_output_manager = sOutputManager::GetInstance();
3035  string img_file_name = p_output_manager->GetPathName() + p_output_manager->GetBaseName();
3036  img_file_name.append("_ProjImage");
3037 
3038  if(IntfWriteProjFile(img_file_name, m2p_projectionImage, mp_ID, Img_fields, m_verbose) )
3039  {
3040  Cerr("***** oImageSpace::PROJ_SaveProjectionImage()-> Error writing Interfile of output image !" << endl);
3041  return 1;
3042  }
3043  }
3044  return 0;
3045 }
3046 
3047 
int GetNbSensImagesCardDeformation(int a_value)
return the required number of cardiac images in the sensitivity image depending on the cardiac deform...
int ApplyMaskToBackwardImage(int a_imageIndex, int a_timeIndex, int a_respIndex, int a_cardIndex)
Apply the mask to the backward image matrix of the first thread for the specific image / time / respi...
#define INTF_LERP_DISABLED
Definition: oInterfileIO.hh:96
FLTNB **** m4p_forwardImage
Definition: oImageSpace.hh:87
void LMS_InstantiateImage()
Allocate memory for the main image matrices (for list-mode sensitivity generation) ...
int GetNbSensImagesRespDeformation(int a_value)
return the required number of respiratory images in the sensitivity image depending on the respirator...
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
FLTNB first_angle
void DeallocateMiscellaneousImage()
Deallocate all allocated miscellaneous images.
Definition: oImageSpace.cc:557
FLTNB ** m2p_projectionImage
Definition: oImageSpace.hh:174
string direction_rotation
FLTNB **** m4p_refDynForwardImage
Definition: oImageSpace.hh:144
void DeallocateBackwardImageFromDynamicBasis()
Free memory for the backward image matrices.
Definition: oImageSpace.cc:264
int m_nbBackwardImages
Definition: oImageSpace.hh:671
FLTNB vox_size[3]
void LMS_CopyAtnToForwardImage(bool a_use1stMotion, bool a_use2ndMotion)
Copy the attenuation image contained in the &#39;m2p_attenuation&#39; matrix inside the m4p_forwardImage matr...
int GetNbCardBasisFunctions()
Get the number of cardiac basis functions.
#define FLTNB
Definition: gVariables.hh:81
#define INTF_LERP_ENABLED
Definition: oInterfileIO.hh:98
int InitMaskImage(const string &a_pathToImage)
Memory allocation and initialization for the mask image.
Definition: oImageSpace.cc:670
int PROJ_InitImage(const string &a_pathToInitialImage)
Load the initial image for the analytical projection.
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
int ApplyOutputMaskImage()
Mask the outside of the provided input mask image.
void DeallocateImage()
Free memory for the main image matrices.
Definition: oImageSpace.cc:116
#define FLTNBMPI
Definition: gVariables.hh:85
int LMS_SaveSensitivityImage(const string &a_pathToSensitivityImage, oDeformationManager *ap_DeformationManager)
Call the interfile function to write the sensitivity image on disk.
void LMS_PrepareForwardImage()
Copy current image in forward-image buffer (for list-mode sensitivity generation) ...
FLTNB GetFOVOutPercent()
Get the percentage of transaxial unmasked FOV.
int GetNb1stMotImgsForLMS(int a_fr)
call the eponym function from the oDynamicDataManager object
void Reduce()
Merge parallel results into the matrix of the backward image matrix of the first thread. Also for MPI.
int InitMultiModalImage(const vector< string > &a_pathToMultiModalImage)
Memory allocation and initialization for the multimodal image matrices.
Definition: oImageSpace.cc:604
FLTNB extent_rotation
FLTNB GetRespBasisCoefficient(int a_respBasisFunction, int a_respGate)
Get the respiratory basis coefficients for the provided respiratory gate and basis function...
int ApplyOutputFOVMasking()
Mask the outside of the transaxial FOV based on the m_fovOutPercent.
#define GEO_ROT_CCW
Definition: vScanner.hh:48
void DeallocateMultiModalImage()
Free memory for the multimodal image.
Definition: oImageSpace.cc:651
~oImageSpace()
oImageSpace destructor.
Definition: oImageSpace.cc:69
FLTNB ***** m5p_refDynBackwardImage
Definition: oImageSpace.hh:154
void DeallocateSensitivityImage()
Free memory for the sensitivity image matrices.
Definition: oImageSpace.cc:461
void InstantiateImage()
Allocate memory for the main image matrices.
Definition: oImageSpace.cc:80
int IntfKeyGetValueFromFile(const string &a_pathToHeader, const string &a_key, T *ap_return, int a_nbElts, int a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_pathToHeader" interfile header matching the "a_keyword" key passed...
Definition: oInterfileIO.cc:61
int PROJ_SaveProjectionImage()
Save an image of the projected data (for analytic projection)
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.
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.
bool m_loadedSensitivity
Definition: oImageSpace.hh:668
void LMS_InstantiateForwardImage()
Allocate memory for the forward image matrices (for list-mode sensitivity generation) ...
void InstantiateSensitivityImage(const string &a_pathToSensitivityImage)
Allocate the sensitivity image matrices.
Definition: oImageSpace.cc:386
int GetSPECTSpecificParameters(uint16_t *ap_nbOfProjections, uint16_t *ap_nbHeads, FLTNB *ap_acquisitionZoom, uint16_t *ap_nbOfBins, FLTNB *ap_pixSizeXY, FLTNB *&ap_angles, FLTNB *&ap_CORtoDetectorDistance, int *ap_headRotDirection)
Transfer geometric information recovered from the datafile to the scanner object. ...
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
FLTNB **** m4p_refDynSensitivityImage
Definition: oImageSpace.hh:165
oImageDimensionsAndQuantification * mp_ID
Definition: oImageSpace.hh:666
void LMS_CopyAtnToImage()
Copy the attenuation image contained in the &#39;m2p_attenuation&#39; matrix inside the m2p_image matrix...
int InitAttenuationImage(const string &a_pathToAtnImage)
Memory allocation and initialisation for the attenuation image using either :
int InitImage(const string &a_pathToInitialImage, FLTNB a_value)
Initialize the main image, either using:
void DeallocateVisitedVoxelsImage()
Free memory for the image matrix containing binary information regarding which 3D voxels have been vi...
Definition: oImageSpace.cc:592
void SaveDebugImage(const string &a_name)
Just a debug function dedicated to write any kind of image on disk in raw format, for debugging purpo...
bool IsLoadedMask()
Definition: oImageSpace.hh:646
void DeallocateMaskImage()
Free memory for the mask image.
Definition: oImageSpace.cc:711
bool GetRespStaticFlag()
Get the respiratory static flag that says if the reconstruction has only one respiratory gate or not...
int PROJ_LoadInitialImage(const string &a_pathToImage)
Load the initial image for the analytical projection.
FLTNB * mp_visitedVoxelsImage
Definition: oImageSpace.hh:121
void PROJ_DeallocateProjectionImage(int a_nbProjs)
Free memory for the projection image for analytical projection.
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.
int SaveOutputBasisCoefficientImage(int a_iteration, int a_subset=-1)
Call the interfile function to write output basis function coefficient image on disk.
#define Cerr(MESSAGE)
const string & GetPathName()
FLTNB ****** m6p_backwardImage
Definition: oImageSpace.hh:94
bool GetCardStaticFlag()
Get the cardiac static flag that says if the reconstruction has only one cardiac gate or not...
FLTNB **** m4p_image
Definition: oImageSpace.hh:80
void InstantiateVisitedVoxelsImage()
Memory allocation and initialization for the image matrix containing binary information regarding whi...
Definition: oImageSpace.cc:580
void InitRefImagesForDeformation()
bool m_loadedMultiModal
Definition: oImageSpace.hh:669
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...
bool m_loadedMask
Definition: oImageSpace.hh:670
void LMS_DeallocateSensitivityImage()
Free memory for the sensitivity image matrices (for list-mode sensitivity generation) ...
FLTNB ** m2p_multiModalImage
Definition: oImageSpace.hh:112
Singleton class that Instantiate and initialize the scanner object.
int SaveOutputImage(int a_iteration, int a_subset=-1)
Call the interfile function to write output image on disk.
void LMS_DeallocateImage()
Free memory for the main image matrices (for list-mode sensitivity generation)
Declaration of class vOptimizer.
void ComputeOutputImage()
int LoadInitialImage(const string &a_pathToImage)
Load the initial image provided by the user in the corresponding matrix.
FLTNB **** m4p_outputImage
Definition: oImageSpace.hh:135
void DeallocateRefImagesForDeformation()
Free memory for the buffer sensitivity image required for image-based deformation. This function is called from the Deformation Manager.
Definition: oImageSpace.cc:979
INTNB GetNbVoxXY()
Get the number of voxels in a slice.
#define VERBOSE_NORMAL
FLTNB ** m2p_miscellaneousImage
Definition: oImageSpace.hh:125
FLTNB * mp_maskImage
Definition: oImageSpace.hh:117
uint32_t mtx_size[7]
FLTNB GetTimeBasisCoefficient(int a_timeBasisFunction, int a_timeFrame)
Get the time basis coefficients for the provided frame and basis function.
void InstantiateForwardImage()
Allocate memory for the forward image matrices.
Definition: oImageSpace.cc:149
bool GetTimeStaticFlag()
Get the time static flag that says if the reconstruction has only one frame or not.
uint16_t nb_projections
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
void PROJ_InstantiateProjectionImage(int a_nbProjs, int a_nbPixels)
Instanciate and initialize projection image for analytical projection.
bool GetFlipOutX()
Get the boolean saying if the output image must be flipped along the X axis.
#define INTNB
Definition: gVariables.hh:92
void ReduceBackwardImage(int a_imageIndex, int a_timeIndex, int a_respIndex, int a_cardIndex)
Merge parallel results into the backward image matrix of the first thread for the specific image / ti...
bool GetFlipOutY()
Get the boolean saying if the output image must be flipped along the Y axis.
const string & GetBaseName()
int ApplyOutputFlip()
Just flip the output image.
This class is designed to manage the image-based deformation part of the reconstruction.
void InstantiateBackwardImageFromDynamicBins()
Allocate memory for the backward image matrices and initialize them.
Definition: oImageSpace.cc:307
Declaration of class oImageSpace.
int IntfWriteProjFile(const string &a_pathToImg, FLTNB **a2p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, Intf_fields a_Imgfields, int vb)
Function dedicated to Interfile image writing for projected data.
void LMS_DeallocateAttenuationImage()
Free memory for the Attenuation image matrices (for analytical projection or list-mode sensitivity ge...
int GetNbCardGates()
Get the number of cardiac gates.
void DeallocateOutputImage()
Free memory for the Image matrices dedicated to output writing on disk.
Definition: oImageSpace.cc:787
Interfile fields. This structure contains all the Interfile keys currently managed by CASToR Decl...
uint16_t nb_detector_heads
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
int GetNbTimeFrames()
Get the number of time frames.
INTNB GetNbVoxXYZ()
Get the total number of voxels.
int GetNb2ndMotImgsForLMS()
call the eponym function from the oDynamicDataManager object
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.
oImageSpace()
oImageSpace constructor. Initialize the member variables to their default values. ...
Definition: oImageSpace.cc:38
void IntfKeySetFieldsOutput(Intf_fields *ap_IF, oImageDimensionsAndQuantification *ap_ID)
Init the keys of the Interfile header of an image to be written on disk.
int IntfWriteWholeDynBasisCoeffImgFile(const string &a_pathToImg, FLTNB ****a4p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int vb)
Main function dedicated to Interfile 6D (dynamic dims + 3D ) image writing of basis function coeffici...
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
string projection_angles
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
int GetNbRespGates()
Get the number of respiratory gates.
#define Cout(MESSAGE)
string process_status
FLTNB **** m4p_attenuation
Definition: oImageSpace.hh:128
void InstantiateRefImagesForDeformation()
Allocate memory for the buffer sensitivity image required for image-based deformation. This function is called from the Deformation Manager.
Definition: oImageSpace.cc:867
int m_nbMiscellaneousImages
Definition: oImageSpace.hh:672
int SaveSensitivityImage(const string &a_pathToSensitivityImage)
Call the interfile function to write the sensitivity image on disk.
bool GetFlipOutZ()
Get the boolean saying if the output image must be flipped along the Z axis.
void DeallocateBackwardImageFromDynamicBins()
Free memory of the backward image matrices.
Definition: oImageSpace.cc:343
void InitBackwardImage()
Initialize each voxel of the backward images to 0, also for sensitivity if not loaded (estimated on t...
int InitSensitivityImage(const string &a_pathToSensitivityImage)
Initialization for the sensitivity image matrices.
void LMS_InstantiateSensitivityImage()
Allocate memory for the sensitivity image matrices (for list-mode sensitivity generation) ...
void DeallocateForwardImage()
Free memory for the forward image matrices.
Definition: oImageSpace.cc:185
int IntfReadImage(const string &a_pathToHeaderFile, FLTNB *ap_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int vb, bool a_lerpFlag)
Main function dedicated to Interfile 3D image loading.
int GetNbRespBasisFunctions()
Get the number of respiratory basis functions.
int IntfWriteImgFile(const string &a_pathToImg, FLTNB *ap_ImgMatrix, const Intf_fields &ap_IntfF, int vb)
Main function dedicated to Interfile 3D image writing. Recover image information from a provided In...
FLTNB ***** m5p_sensitivity
Definition: oImageSpace.hh:104
FLTNB GetListPMotionWeightInFrameForLMS(int a_fr, int a_pmsset)
call the eponym function from the oDynamicDataManager object
void LMS_CopyBackwardToSensitivity()
INTNB GetNbSliceOutMask()
Get the number of extrem slices that will be masked at each side.
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.
#define KEYWORD_OPTIONAL_ERROR
Definition: gOptions.hh:55
void InstantiateOutputImage()
Instanciate Image matrices dedicated to output writing on disk.
Definition: oImageSpace.cc:740
void LMS_DeallocateForwardImage()
Free memory for the forward image matrices (for list-mode sensitivity generation) ...
FLTNB GetCardBasisCoefficient(int a_cardBasisFunction, int a_cardGate)
Get the cardiac basis coefficients for the provided cardiac gate and basis function.