CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
toolkits/castor-mergeBedPositions.cc
Go to the documentation of this file.
1 
8 #include "gVariables.hh"
9 #include "gOptions.hh"
10 #include "sOutputManager.hh"
11 #include "sScannerManager.hh"
12 #include "oInterfileIO.hh"
13 
14 
15 // =============================================================================================================================================
16 // =============================================================================================================================================
17 // =============================================================================================================================================
18 // H E L P F U N C T I O N S
19 // =============================================================================================================================================
20 // =============================================================================================================================================
21 // =============================================================================================================================================
22 
23 
28 void ShowHelp()
29 {
30  // Show help
31  cout << endl;
32  cout << "Usage: castor-mergeBedPositions -img image1.hdr -img image2.hdr -(f/d)out output [settings]" << endl;
33  cout << endl;
34  cout << "This program can be used to merge several images corresponding to multiple bed positions of the same acquisition. The slices of any image" << endl;
35  cout << "are weighted during merging. By default, these weights are uniform (1 for all slices of all images). The -sens option can be used to provide" << endl;
36  cout << "a sensitivity image for each image to be merged, where the voxels' value of the sensitivity images are used as weights." << endl;
37  cout << "The bed relative positions are recovered from the image headers as 'bed relative position (mm)'." << endl;
38  cout << "If not found, the default bed displacement between two successive bed positions from the scanner is used." << endl;
39  cout << "Important note: this program assumes the provided images exactly match the axial FOV of the scanner; if bigger or smaller results will be wrong." << endl;
40  cout << "Important note: this program only works with 3D images (without time dimension)." << endl;
41  cout << endl;
42  cout << "[Mandatory parameters]:" << endl;
43  cout << " -img imageX.hdr : Give an image corresponding to a bed position (must give at least two images; the provided order defines the order" << endl;
44  cout << " in which bed positions are merged)" << endl;
45  cout << " -fout name : Give the root name for all output files (no default, alternative to -dout)" << endl;
46  cout << " -dout name : Give the name of the output directory where all output files will be written (no default, alternative to -fout)" << endl;
47  cout << endl;
48  cout << "[Options]:" << endl;
49  cout << " -sens sensX.hdr : Give the sensitivity image to weight the imageX.hdr while merging the bed positions (must provide as many sensitivity" << endl;
50  cout << " images as images to be merged; the association of sensitivity images to the images is done with respect to the order" << endl;
51  cout << " both images are provided)" << endl;
52  cout << " -oweight : Also save the global weights" << endl;
53  cout << " -inv : Inverse the order of the provided images (and sensitivity images)" << endl;
54  cout << " -vb value : Give the verbosity level, from 0 (no verbose) to 2 (default: 1)" << endl;
55  cout << " -conf value : Give the path to the CASToR config directory (default: located through the CASTOR_CONFIG environment variable)." << endl;
56  cout << " -tolerance value : Give the tolerance of bed positions with respect to the output image slices, which will be interpreted as a" << endl;
57  cout << " percentage of the slice thickness [default: 1.]." << endl;
58  cout << " -flipZ : Flip the output image, once merged." << endl;
59  cout << " --help,-h,-help : Print out this help page." << endl; // managed by main
60  cout << endl;
61  #ifdef CASTOR_OMP
62  cout << " Compiled with OpenMP (will use as many cores as available)" << endl;
63  #endif
64  #ifdef BUILD_DATE
65  cout << " Build date: " << BUILD_DATE << endl;
66  cout << endl;
67  #endif
68  #ifdef CASTOR_VERSION
69  cout << " This program is part of the CASToR release version " << CASTOR_VERSION << "." << endl;
70  cout << endl;
71  #endif
72 }
73 
74 // =============================================================================================================================================
75 // =============================================================================================================================================
76 // =============================================================================================================================================
77 // M A I N P R O G R A M
78 // =============================================================================================================================================
79 // =============================================================================================================================================
80 // =============================================================================================================================================
81 
82 int main(int argc, char** argv)
83 {
84  // ============================================================================================================
85  // MPI stuff (we make all instances but the first one returning 0 directly)
86  // ============================================================================================================
87  int mpi_rank = 0;
88  #ifdef CASTOR_MPI
89  int mpi_size = 1;
90  MPI_Init(&argc, &argv);
91  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
92  MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
93  if (mpi_rank!=0) return 0;
94  #endif
95 
96  // No argument, then show help
97  if (argc==1)
98  {
99  ShowHelp();
100  Exit(EXIT_SUCCESS);
101  }
102 
103  // ============================================================================================================
104  // Parameterized variables with their default values
105  // ============================================================================================================
106 
107  // --------------------------------------------------------------------------------
108  // Input settings
109  // --------------------------------------------------------------------------------
110 
111  // Number of bed position
112  uint32_t nb_beds = 0;
113  // Vector containing string pointing to the images to be merged
114  vector<string> path_to_image_filename;
115  // Vector containing string pointing to the sensitivity images used as weights
116  vector<string> path_to_sens_filename;
117  // Invert bed datafile names order
118  bool invert_order_flag = false;
119 
120  // --------------------------------------------------------------------------------
121  // Output settings
122  // --------------------------------------------------------------------------------
123 
124  // Output directory name.
125  string path_dout = "";
126  // Or root name
127  string path_fout = "";
128  // Save global weight
129  bool save_weights = false;
130  // Flip output image axially
131  bool flip_output_axial = false;
132 
133  // --------------------------------------------------------------------------------
134  // Miscellaneous settings
135  // --------------------------------------------------------------------------------
136 
137  // Tolerance of bed positions with respect to image slices, defined as a percentage of the slice thickness (default is 1%)
138  FLTNB tolerance_as_percent_slice_thickness = 1.;
139  // Verbose level
140  int verbose = 1;
141  // Path to config directory
142  string path_to_config_dir = "";
143 
144  // ============================================================================================================
145  // Read command-line parameters
146  // ============================================================================================================
147 
148  // Must manually increment the option index when an argument is needed after an option
149  for (int i=1; i<argc; i++)
150  {
151  // Get the option as a string
152  string option = (string)argv[i];
153 
154  // --------------------------------------------------------------------------------
155  // Miscellaneous settings
156  // --------------------------------------------------------------------------------
157 
158  // Show help
159  if (option=="-h" || option=="--help" || option=="-help")
160  {
161  ShowHelp();
162  Exit(EXIT_SUCCESS);
163  }
164 
165  // Tolerance of the bed positions with respect to the output image slices, interpreted as a percentage of the slice thickness
166  else if (option=="-tolerance")
167  {
168  if (i>=argc-1)
169  {
170  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
171  Exit(EXIT_FAILURE);
172  }
173  if (ConvertFromString(argv[i+1], &tolerance_as_percent_slice_thickness))
174  {
175  Cerr("***** castor-mergeBedPositions() -> Exception when trying to read provided verbosity level '" << verbose << " for option: " << option << endl);
176  Exit(EXIT_FAILURE);
177  }
178  i++;
179  }
180  // General verbosity level
181  else if (option=="-vb")
182  {
183  if (i>=argc-1)
184  {
185  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
186  Exit(EXIT_FAILURE);
187  }
188  if (ConvertFromString(argv[i+1], &verbose))
189  {
190  Cerr("***** castor-mergeBedPositions() -> Exception when trying to read provided verbosity level '" << verbose << " for option: " << option << endl);
191  Exit(EXIT_FAILURE);
192  }
193  i++;
194  }
195  // Path to config directory
196  else if (option=="-conf")
197  {
198  if (i>=argc-1)
199  {
200  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
201  Exit(EXIT_FAILURE);
202  }
203  path_to_config_dir = (string)argv[i+1];
204  i++;
205  }
206 
207  // --------------------------------------------------------------------------------
208  // Input settings
209  // --------------------------------------------------------------------------------
210 
211  // Images
212  else if (option=="-img") // This is a mandatory option
213  {
214  if (i>=argc-1)
215  {
216  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
217  Exit(EXIT_FAILURE);
218  }
219  string file_name = (string)argv[i+1];
220  path_to_image_filename.push_back(file_name);
221  nb_beds++;
222  i++;
223  }
224  // Sensitivity images
225  else if (option=="-sens")
226  {
227  if (i>=argc-1)
228  {
229  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
230  Exit(EXIT_FAILURE);
231  }
232  string file_name = (string)argv[i+1];
233  path_to_sens_filename.push_back(file_name);
234  i++;
235  }
236  // Invert bed positions order
237  else if (option=="-inv")
238  {
239  invert_order_flag = true;
240  }
241 
242  // --------------------------------------------------------------------------------
243  // Output settings
244  // --------------------------------------------------------------------------------
245 
246  // Name of the output directory
247  else if (option=="-dout") // This is a mandatory option
248  {
249  if (i>=argc-1)
250  {
251  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
252  Exit(EXIT_FAILURE);
253  }
254  path_dout = argv[i+1];
255  i++;
256  }
257  // Base name of the output files
258  else if (option=="-fout") // This is a mandatory option
259  {
260  if (i>=argc-1)
261  {
262  Cerr("***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
263  Exit(EXIT_FAILURE);
264  }
265  path_fout = argv[i+1];
266  i++;
267  }
268  // Save global weights
269  else if (option=="-oweight")
270  {
271  save_weights = true;
272  }
273  // Flip output image axially
274  else if (option=="-flipZ")
275  {
276  flip_output_axial = true;
277  }
278 
279  // --------------------------------------------------------------------------------
280  // Unknown option!
281  // --------------------------------------------------------------------------------
282 
283  else
284  {
285  Cerr("***** castor-mergeBedPositions() -> Unknown option '" << option << "' !" << endl);
286  Exit(EXIT_FAILURE);
287  }
288  }
289 
290  // ============================================================================================================
291  // Some checks
292  // ============================================================================================================
293 
294  // Data files
295  if (nb_beds < 2)
296  {
297  Cerr("***** castor-mergeBedPositions() -> Please provide at least two image filename !" << endl);
298  Exit(EXIT_FAILURE);
299  }
300  // Output files
301  if (path_fout.empty() && path_dout.empty())
302  {
303  Cerr("***** castor-mergeBedPositions() -> Please provide an output option for output files (-fout or -dout) !" << endl);
304  Exit(EXIT_FAILURE);
305  }
306  // Check that only one option has been provided
307  if (!path_fout.empty() && !path_dout.empty())
308  {
309  Cerr("***** castor-mergeBedPositions() -> Please provide either output option -fout or -dout but not both !" << endl);
310  Exit(EXIT_FAILURE);
311  }
312  // If sensitivity is provided, then check that there are as many files as input images
313  if (path_to_sens_filename.size()>0 && path_to_sens_filename.size()!=path_to_image_filename.size())
314  {
315  Cerr("***** castor-mergeBedPositions() -> If using sensitivity images as weights, please provide as many sensitivity images as input images !" << endl);
316  Exit(EXIT_FAILURE);
317  }
318 
319  // ============================================================================================================
320  // Set maximum number of threads if compiled with OpenMP
321  // ============================================================================================================
322 
323  #ifdef CASTOR_OMP
324  omp_set_num_threads(omp_get_max_threads());
325  #endif
326 
327  // ============================================================================================================
328  // Create sOutputManager
329  // ============================================================================================================
330  sOutputManager* p_OutputManager = sOutputManager::GetInstance();
331  // Set verbose level
332  p_OutputManager->SetVerbose(verbose);
333  // Set MPI rank
334  p_OutputManager->SetMPIRank(mpi_rank);
335  // Set path to the config directory
336  if (p_OutputManager->CheckConfigDir(path_to_config_dir))
337  {
338  Cerr("***** castor-mergeBedPositions() -> A problem occurred while checking for the config directory path !" << endl);
339  Exit(EXIT_FAILURE);
340  }
341  // Initialize output directory and base name
342  if (p_OutputManager->InitOutputDirectory(path_fout, path_dout))
343  {
344  Cerr("***** castor-mergeBedPositions() -> A problem occurred while initializing output directory !" << endl);
345  Exit(EXIT_FAILURE);
346  }
347  // Log command line
348  if (p_OutputManager->LogCommandLine(argc,argv))
349  {
350  Cerr("***** castor-mergeBedPositions() -> A problem occurred while logging command line arguments !" << endl);
351  Exit(EXIT_FAILURE);
352  }
353 
354  // ============================================================================================================
355  // Create sScannerManager
356  // ============================================================================================================
357  sScannerManager* p_ScannerManager = sScannerManager::GetInstance();
358  p_ScannerManager->SetVerbose(verbose);
359 
360  // ============================================================================================================
361  // Read all input images
362  // ============================================================================================================
363 
364  // Verbose
365  if (verbose>=VERBOSE_LIGHT) Cout("castor-mergeBedPositions() -> Load input images" << endl);
366 
367  // Get user endianness (interfile I/O)
369 
370  // Create interfile image fields and image pointers
371  Intf_fields** p_image_fields = (Intf_fields**)malloc(nb_beds*sizeof(Intf_fields*));
372  FLTNB** p_images = (FLTNB**)malloc(nb_beds*sizeof(FLTNB*));
373 
374  // Read interfile images
375  for (uint32_t bed=0; bed<nb_beds; bed++)
376  {
377  // Create the image fields
378  p_image_fields[bed] = new Intf_fields;
379  // Read the image
380  if (invert_order_flag) p_images[bed] = IntfLoadImageFromScratch(path_to_image_filename[nb_beds-1-bed],p_image_fields[bed],verbose);
381  else p_images[bed] = IntfLoadImageFromScratch(path_to_image_filename[bed],p_image_fields[bed],verbose);
382  // Check reading
383  if (p_images[bed]==NULL)
384  {
385  Cerr("***** castor-mergeBedPositions() -> A problem occurred while reading image for bed " << bed+1 << " !" << endl);
386  return 1;
387  }
388  }
389 
390  // Check dimensions consistency between all image fields, as well as originating system and bed displacement
391  for (uint32_t bed=1; bed<nb_beds; bed++)
392  {
393  if (IntfCheckDimensionsConsistency(*p_image_fields[0],*p_image_fields[bed]))
394  {
395  Cerr("***** castor-mergeBedPositions() -> Inconsistency between image dimensions !" << endl);
396  Exit(EXIT_FAILURE);
397  }
398  }
399 
400  // Verbose
401  if (verbose>=VERBOSE_NORMAL)
402  {
403  Cout(" --> Image dimensions: [ " << p_image_fields[0]->mtx_size[0] << " : "
404  << p_image_fields[0]->mtx_size[1] << " : "
405  << p_image_fields[0]->mtx_size[2] << " ]" << endl);
406  Cout(" --> Voxel sizes: [ " << p_image_fields[0]->vox_size[0] << " : "
407  << p_image_fields[0]->vox_size[1] << " : "
408  << p_image_fields[0]->vox_size[2] << " ]" << endl);
409  }
410 
411  // Get dimensions locally
412  INTNB dim_x = p_image_fields[0]->mtx_size[0];
413  INTNB dim_y = p_image_fields[0]->mtx_size[1];
414  INTNB dim_z = p_image_fields[0]->mtx_size[2];
415  INTNB dim_xy = dim_x * dim_y;
416  INTNB dim_xyz = dim_xy * dim_z;
417 
418  // ============================================================================================================
419  // Check if at least one or all image headers have the bed relative position field
420  // ============================================================================================================
421 
422  // Analyze if one is not zero and if all are not zero
423  bool one_position_not_zero = false;
424  bool all_positions_not_zero = true;
425  for (uint32_t bed=0; bed<nb_beds; bed++)
426  {
427  // Here, we compare the bed relative position to FLT_MIN as it is the default value of the interfile field
428  one_position_not_zero = (one_position_not_zero || p_image_fields[0]->bed_relative_position!=FLT_MIN);
429  all_positions_not_zero = (all_positions_not_zero && p_image_fields[0]->bed_relative_position!=FLT_MIN);
430  }
431 
432  // If one is not zero but not all, then throw a warning
433  if (one_position_not_zero && !all_positions_not_zero)
434  {
435  Cerr("!!!!! castor-mergeBedPositions() -> The bed relative position is provided in some but not all interfile headers !" << endl);
436  }
437 
438  // ============================================================================================================
439  // If the provided relative positions are used, then recenter the mass to 0.
440  // ============================================================================================================
441 
442  if (all_positions_not_zero)
443  {
444  // Verbose
445  if (verbose>=1) Cout("castor-mergeBedPositions() -> Recenter relative bed positions" << endl);
446  // We have to compute the center of mass of the provided bed positions, and move it to the 0-center of the Z axis
447  FLTNB center = 0.;
448  // Compute the center of mass
449  for (uint32_t bed=0; bed<nb_beds; bed++) center += p_image_fields[bed]->bed_relative_position;
450  center /= ((FLTNB)nb_beds);
451  // Compute the new bed positions from their relative values to recenter the mass at 0
452  for (uint32_t bed=0; bed<nb_beds; bed++) p_image_fields[bed]->bed_relative_position = p_image_fields[bed]->bed_relative_position - center;
453  }
454 
455  // ============================================================================================================
456  // If no relative positions provided, then compute them from the default scanner displacement
457  // ============================================================================================================
458 
459  if (!all_positions_not_zero)
460  {
461  // Verbose
462  if (verbose>=1) Cout("castor-mergeBedPositions() -> Recover default bed displacement from scanner configuration file" << endl);
463  // Find scanner configuration file
464  if (p_ScannerManager->FindScannerSystem(p_image_fields[0]->originating_system) )
465  {
466  Cerr("***** castor-mergeBedPositions() -> A problem occurred while searching for scanner system '" << p_image_fields[0]->originating_system << "' !" << endl);
467  Exit(EXIT_FAILURE);
468  }
469  // Get the bed displacement from the scanner configuration file
470  FLTNB default_bed_displacement = -1.;
471  if (ReadDataASCIIFile(p_ScannerManager->GetPathToScannerFile(), "multiple bed displacement", &default_bed_displacement, 1, KEYWORD_MANDATORY))
472  {
473  Cerr("***** castor-mergeBedPositions() -> Multiple bed displacement field not found in the scanner configuration file '" <<
474  p_ScannerManager->GetPathToScannerFile() << " !" << endl);
475  Exit(EXIT_FAILURE);
476  }
477  // Check that the default bed displacement is more than 0. in the scanner
478  if (default_bed_displacement<=0.)
479  {
480  Cerr("***** castor-mergeBedPositions() -> Default bed displacement from the scanner configuration file must be strictly positive !" << endl);
481  Exit(EXIT_FAILURE);
482  }
483  // Verbose
484  if (verbose>=1) Cout(" --> Bed displacement of " << default_bed_displacement << " mm" << endl);
485  // Loop on the bed positions
486  for (uint32_t bed=0; bed<nb_beds; bed++)
487  {
488  // Compute bed offset (remember here that the 0. on the Z axis is at the center of the whole image)
489  FLTNB bed_offset = 0.;
490  // For odd numbers of bed positions
491  if (nb_beds%2==1) bed_offset = ((FLTNB)( ((int32_t)(bed))-((int32_t)(nb_beds))/2 )) * default_bed_displacement;
492  // For even numbers of bed positions
493  else bed_offset = (((FLTNB)( ((int32_t)(bed))-((int32_t)(nb_beds))/2 )) + 0.5) * default_bed_displacement;
494  // Record the value
495  p_image_fields[bed]->bed_relative_position = bed_offset;
496  }
497  }
498 
499  // ============================================================================================================
500  // Find the bed with the minimum position and compute the displacement of each bed with respect to that
501  // ============================================================================================================
502 
503  // Verbose
504  if (verbose>=1)
505  {
506  Cout("castor-mergeBedPositions() -> Use following relative bed positions:" << endl);
507  for (uint32_t bed=0; bed<nb_beds; bed++) Cout(" --> Bed " << bed << " | Relative position (mm): " << p_image_fields[bed]->bed_relative_position << endl);
508  }
509 
510  // Find the minimum position that will be used as the reference
511  FLTNB reference_minimum_position = p_image_fields[0]->bed_relative_position;
512  for (uint32_t bed=1; bed<nb_beds; bed++)
513  if (p_image_fields[bed]->bed_relative_position<reference_minimum_position)
514  reference_minimum_position = p_image_fields[bed]->bed_relative_position;
515 
516  // Compute bed displacement of each bed position from the reference
517  FLTNB* p_displacement_from_reference = (FLTNB*)malloc(nb_beds*sizeof(FLTNB));
518  for (uint32_t bed=0; bed<nb_beds; bed++)
519  p_displacement_from_reference[bed] = p_image_fields[bed]->bed_relative_position - reference_minimum_position;
520 
521  // ============================================================================================================
522  // Check that the displacement of each bed is compatible with the slice thickness
523  // ============================================================================================================
524 
525  // Define the tolerance associated to the rounding from the slice thickness and the tolerance provided as a percent of the slice thickness
526  FLTNB tolerance = p_image_fields[0]->vox_size[2] * tolerance_as_percent_slice_thickness / 100.;
527  // The number of slices defining the displacement of each bed with respect to the reference
528  INTNB* p_displacement_slices = (INTNB*)calloc(nb_beds,sizeof(INTNB));
529 
530  // Loop on beds
531  for (uint32_t bed=0; bed<nb_beds; bed++)
532  {
533  // Compute floating point number of slices
534  FLTNB nb_slices_fltnb = p_displacement_from_reference[bed] / p_image_fields[0]->vox_size[2];
535  // Compute integer number of slices
536  FLTNB nb_slices_intnb = ((FLTNB)((INTNB)nb_slices_fltnb));
537  // Case where the floating number minus the integer number is less than 0.5; this means that
538  // the closest integer to the floating number of slices is below it.
539  if (nb_slices_fltnb-nb_slices_intnb<0.5)
540  {
541  // If the difference is below the tolerance, we are ok taking the 'integerized' number of slices as the displacement
542  if (nb_slices_fltnb-nb_slices_intnb<tolerance) p_displacement_slices[bed] = ((INTNB)nb_slices_fltnb);
543  // Otherwise, the displacement is not compatible with the slice thickness
544  else
545  {
546  Cerr("***** castor-mergeBedPositions() -> Bed position of bed " << bed << " is not compatible with the slice thickness of " << p_image_fields[0]->vox_size[2] << " mm !" << endl);
547  Exit(EXIT_FAILURE);
548  }
549  }
550  // Case where the floating number minus the integer number is more than 0.5; this means that
551  // the closest integer to the floating number of slices is above it.
552  else
553  {
554  // If minus the difference + 1 is below the tolerance, we are ok taking the 'integerized' number of slices + 1 as the displacement
555  if (nb_slices_intnb+1.-nb_slices_fltnb<tolerance) p_displacement_slices[bed] = ((INTNB)nb_slices_fltnb) + 1;
556  // Otherwise, the displacement is not compatible with the slice thickness
557  else
558  {
559  Cerr("***** castor-mergeBedPositions() -> Bed position of bed " << bed << " is not compatible with the slice thickness of " << p_image_fields[0]->vox_size[2] << " mm !" << endl);
560  Exit(EXIT_FAILURE);
561  }
562  }
563  }
564 
565  // ============================================================================================================
566  // Compute the dimension of the output image based on the reference bed position and the maximum displacement
567  // ============================================================================================================
568 
569  // Search for the maximum displacement
570  INTNB max_displacement_slices = p_displacement_slices[0];
571  for (uint32_t bed=1; bed<nb_beds; bed++) if (p_displacement_slices[bed]>max_displacement_slices) max_displacement_slices = p_displacement_slices[bed];
572 
573  // The output image dimension will be the size of the input image plus the maximum displacement in number of slices
574  INTNB whole_dim_z = max_displacement_slices + dim_z;
575  INTNB whole_dim_xyz = dim_xy * whole_dim_z;
576 
577  // Verbose
578  if (verbose>=1) Cout("castor-mergeBedPositiosn() -> Output merged image has " << whole_dim_z << " slices" << endl);
579 
580  // ============================================================================================================
581  // If sensitivity, then load it as weights, otherwise set everything to 1
582  // ============================================================================================================
583 
584  // Create the weights matrix
585  FLTNB** p_weights = (FLTNB**)malloc(nb_beds*sizeof(FLTNB*));
586  for (uint32_t bed=0; bed<nb_beds; bed++) p_weights[bed] = NULL;
587 
588  // If we have sensitivity
589  if (path_to_sens_filename.size()>0)
590  {
591  // Verbose
592  if (verbose>=1) Cout("castor-mergeBedPositions() -> Load sensitivity images" << endl);
593  // Create interfile image fields and image pointers for sensitivity
594  Intf_fields** p_sens_fields = (Intf_fields**)malloc(nb_beds*sizeof(Intf_fields*));
595  // Read interfile images
596  for (uint32_t bed=0; bed<nb_beds; bed++)
597  {
598  // Create the image fields
599  p_sens_fields[bed] = new Intf_fields;
600  // Read the image
601  if (invert_order_flag) p_weights[bed] = IntfLoadImageFromScratch(path_to_sens_filename[nb_beds-1-bed],p_sens_fields[bed],verbose);
602  else p_weights[bed] = IntfLoadImageFromScratch(path_to_sens_filename[bed],p_sens_fields[bed],verbose);
603  // Check reading
604  if (p_weights[bed]==NULL)
605  {
606  Cerr("***** castor-mergeBedPositions() -> A problem occurred while reading sensitivity for bed " << bed+1 << " !" << endl);
607  return 1;
608  }
609  }
610  // Check dimensions consistency between all image fields
611  for (uint32_t bed=1; bed<nb_beds; bed++)
612  {
613  if (IntfCheckDimensionsConsistency(*p_sens_fields[0],*p_sens_fields[bed]))
614  {
615  Cerr("***** castor-mergeBedPositions() -> Inconsistency between sensitivity dimensions !" << endl);
616  Exit(EXIT_FAILURE);
617  }
618  }
619  // Check dimensions consistency between input images and sensitivity images
620  for (uint32_t bed=0; bed<nb_beds; bed++)
621  {
622  if (IntfCheckDimensionsConsistency(*p_image_fields[bed],*p_sens_fields[bed]))
623  {
624  Cerr("***** castor-mergeBedPositions() -> Inconsistency between input image and sensitivity dimensions for bed " << bed+1 << " !" << endl);
625  Exit(EXIT_FAILURE);
626  }
627  }
628  }
629  // If we do not have sensitivity
630  else
631  {
632  // Verbose
633  if (verbose>=1) Cout("castor-mergeBedPositions() -> Initialize weights to 1." << endl);
634  // We set everything to 1.
635  for (uint32_t bed=0; bed<nb_beds; bed++)
636  {
637  p_weights[bed] = (FLTNB*)malloc(dim_xyz*sizeof(FLTNB));
638  for (INTNB v=0; v<dim_xyz; v++) p_weights[bed][v] = 1.;
639  }
640  }
641 
642  // ============================================================================================================
643  // Compute the whole body image
644  // ============================================================================================================
645 
646  // Verbose
647  if (verbose>=1) Cout("castor-mergeBedPositiosn() -> Compute whole image" << endl);
648  // Allocate whole image
649  FLTNB* p_whole_image = (FLTNB*)malloc(whole_dim_xyz*sizeof(FLTNB));
650  for (INTNB v=0; v<whole_dim_xyz; v++) p_whole_image[v] = 0.;
651  // Allocate whole weights
652  FLTNB* p_whole_weight = (FLTNB*)malloc(whole_dim_xyz*sizeof(FLTNB));
653  for (INTNB v=0; v<whole_dim_xyz; v++) p_whole_weight[v] = 0.;
654 
655  // Loop on all beds
656  for (uint32_t bed=0; bed<nb_beds; bed++)
657  {
658  // Loop on all slices
659  INTNB z;
660  #pragma omp parallel for private(z) schedule(guided)
661  for (z=0; z<dim_z; z++)
662  {
663  // Compute whole z index
664  INTNB whole_z = z + p_displacement_slices[bed];
665  // For efficiency
666  INTNB base_z = z * dim_xy;
667  INTNB whole_base_z = whole_z * dim_xy;
668  // Loop on all voxels inside the slice
669  for (INTNB xy=0; xy<dim_xy; xy++)
670  {
671  // Add the contribution of this bed to the whole image
672  p_whole_image[whole_base_z+xy] += p_images[bed][base_z+xy] * p_weights[bed][base_z+xy];
673  // Add the weights to the global weights
674  p_whole_weight[whole_base_z+xy] += p_weights[bed][base_z+xy];
675  }
676  }
677  }
678  // Normalize the whole image with respect to the weights
679  for (INTNB v=0; v<whole_dim_xyz; v++)
680  {
681  if (p_whole_weight[v]>0.) p_whole_image[v] /= p_whole_weight[v];
682  else p_whole_image[v] = 0.;
683  }
684 
685  // ============================================================================================================
686  // Flip output image axially
687  // ============================================================================================================
688 
689  if (flip_output_axial)
690  {
691  // Verbose
692  if (verbose>=1) Cout("castor-mergeBedPositions() -> Flip output images axially" << endl);
693  // Half loop over Z
694  for (INTNB z_1=0; z_1<whole_dim_z/2; z_1++)
695  {
696  // Compute opposite Z
697  INTNB z_2 = whole_dim_z - 1 - z_1;
698  // For efficiency
699  INTNB base_z1 = z_1 * dim_xy;
700  INTNB base_z2 = z_2 * dim_xy;
701  // Loop over Y and X
702  for (INTNB xy=0; xy<dim_xy; xy++)
703  {
704  // Compute both indices
705  INTNB indice_1 = base_z1 + xy;
706  INTNB indice_2 = base_z2 + xy;
707  // Switch voxels for the image
708  FLTNB buffer = p_whole_image[indice_1];
709  p_whole_image[indice_1] = p_whole_image[indice_2];
710  p_whole_image[indice_2] = buffer;
711  // Switch voxels for the sensitivity
712  buffer = p_whole_weight[indice_1];
713  p_whole_weight[indice_1] = p_whole_weight[indice_2];
714  p_whole_weight[indice_2] = buffer;
715  }
716  }
717  }
718 
719  // ============================================================================================================
720  // Save output image
721  // ============================================================================================================
722 
723  // Verbose
724  if (verbose>=1) Cout("castor-mergeBedPositions() -> Write output image(s)" << endl);
725 
726  // Build interfile fields (we use first one of the input images and modify the relevant fields)
727  // Set header metadata using Image Dimensions object
728  p_image_fields[0]->mtx_size[2] = whole_dim_z;
729  p_image_fields[0]->nb_total_imgs = whole_dim_z;
730  p_image_fields[0]->nb_bytes_pixel = sizeof(FLTNB);
731  /* TODO: UPDATE TIME START AND DURATION BUT WILL HAVE TO WRITE THEM IN THE HEADERS OF IMAGES WRITTEN DURING ITERATIONS
732  ap_IF->study_duration = ap_ID->GetFinalTimeStopInSec(0) -
733  ap_ID->GetFrameTimeStartInSec(0,0);
734  for(int fr=0 ; fr<ap_ID->GetNbTimeFrames() ; fr++)
735  {
736  ap_IF->image_duration.push_back(ap_ID->GetFrameDurationInSec(0, fr));
737  ap_IF->frame_group_pause.push_back((fr == 0) ? 0
738  : ap_ID->GetFrameTimeStartInSec(0,fr) - ap_ID->GetFrameTimeStopInSec(0,fr-1));
739  }
740  */
741  // File name
742  string whole_image_filename = p_OutputManager->GetPathName() + p_OutputManager->GetBaseName() + "_whole";
743  // Write the image
744  if (IntfWriteImageFromIntfFields(whole_image_filename, p_whole_image, *p_image_fields[0], verbose))
745  {
746  Cerr("***** castor-mergeBedPositions() -> A problem occurred while writing output whole image !" << endl);
747  Exit(EXIT_FAILURE);
748  }
749 
750  // Save global weights if asked for
751  if (save_weights)
752  {
753  // File name
754  string whole_weight_filename = p_OutputManager->GetPathName() + p_OutputManager->GetBaseName() + "_weights";
755  // Write the image
756  if (IntfWriteImageFromIntfFields(whole_weight_filename, p_whole_weight, *p_image_fields[0], verbose))
757  {
758  Cerr("***** castor-mergeBedPositions() -> A problem occurred while writing global weights image !" << endl);
759  Exit(EXIT_FAILURE);
760  }
761  }
762 
763  // ============================================================================================================
764  // Free memory properly
765  // ============================================================================================================
766 
767  // Input images
768  if (p_images)
769  {
770  for (uint32_t bed=0; bed<nb_beds; bed++) if (p_images[bed]) free(p_images[bed]);
771  free(p_images);
772  }
773  // Weights
774  if (p_weights)
775  {
776  for (uint32_t bed=0; bed<nb_beds; bed++) if (p_weights[bed]) free(p_weights[bed]);
777  free(p_weights);
778  }
779  // Whole image
780  if (p_whole_image) free(p_whole_image);
781  // Whole weights
782  if (p_whole_weight) free(p_whole_weight);
783 
784  // Ending
785  #ifdef CASTOR_MPI
786  MPI_Finalize();
787  #endif
788  return EXIT_SUCCESS;
789 }
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define Cerr(MESSAGE)
int FindScannerSystem(string a_scannerName)
void Exit(int code)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
int LogCommandLine(int argc, char **argv)
int CheckConfigDir(const string &a_path)
int IntfWriteImageFromIntfFields(const string &a_pathToImg, FLTNB *ap_ImgMatrix, Intf_fields Img_fields, int vb)
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
int main(int argc, char **argv)
int IntfCheckDimensionsConsistency(Intf_fields ImgFields1, Intf_fields ImgFields2)
Singleton class that Instantiate and initialize the scanner object.
#define KEYWORD_MANDATORY
Interfile fields. This structure contains all the Interfile keys currently managed by CASToR Decl...
void GetUserEndianness()
Check user/host computer endianness and write it to the global variable User_Endianness.
FLTNB * IntfLoadImageFromScratch(const string &a_pathToHeaderFile, Intf_fields *ap_ImgFields, int vb)
int InitOutputDirectory(const string &a_pathFout, const string &a_pathDout)
int ConvertFromString(const string &a_str, string *a_result)
Copy the &#39;a_str&#39; string in the position pointed by &#39;a_result&#39;.
void SetVerbose(int a_verboseLevel)
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
#define Cout(MESSAGE)