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