39 cout <<
"Usage: castor-mergeBedPositions -img image1.hdr -img image2.hdr -(f/d)out output [settings]" << endl;
41 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;
42 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;
43 cout <<
"a sensitivity image for each image to be merged, where the voxels' value of the sensitivity images are used as weights." << endl;
44 cout <<
"The bed displacement between two successive bed positions is recovered from the image headers as 'multiple bed displacement'." << endl;
45 cout <<
"If not found, the default one from the scanner is used." << endl;
46 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;
48 cout <<
"[Mandatory parameters]:" << endl;
49 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;
50 cout <<
" in which bed positions are merged)" << endl;
51 cout <<
" -fout name : Give the root name for all output files (no default, alternative to -dout)" << endl;
52 cout <<
" -dout name : Give the name of the output directory where all output files will be written (no default, alternative to -fout)" << endl;
54 cout <<
"[Options]:" << endl;
55 cout <<
" -sens sensX.hdr : Give the sensitivity image to weight the imageX.hdr while merging the bed positions (must provide as many sensitivity" << endl;
56 cout <<
" images as images to be merged; the association of sensitivity images to the images is done with respect to the order" << endl;
57 cout <<
" both images are provided)" << endl;
58 cout <<
" -oweight : Also save the global weights" << endl;
59 cout <<
" -inv : Virtually inverse the order of the provided images (and sensitivity images)" << endl;
60 cout <<
" -vb : Give the verbosity level, from 0 (no verbose) to 2 (default: 1)" << endl;
61 cout <<
" -conf : Give the path to the CASToR config directory (default: located through the CASTOR_CONFIG environment variable)." << endl;
62 cout <<
" --help,-h,-help : Print out this help page." << endl;
65 cout <<
" Build date: " << BUILD_DATE << endl;
69 cout <<
" This program is part of the CASToR release version " <<
CASTOR_VERSION <<
"." << endl;
82 int main(
int argc,
char** argv)
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;
112 uint32_t nb_beds = 0;
114 vector<string> path_to_image_filename;
116 vector<string> path_to_sens_filename;
118 bool invert_order_flag =
false;
125 string path_dout =
"";
127 string path_fout =
"";
129 bool save_weights =
false;
138 string path_to_config_dir =
"";
145 for (
int i=1; i<argc; i++)
148 string option = (string)argv[i];
155 if (option==
"-h" || option==
"--help" || option==
"-help")
161 else if (option==
"-vb")
165 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
170 Cerr(
"***** castor-mergeBedPositions() -> Exception when trying to read provided verbosity level '" << verbose <<
" for option: " << option << endl);
176 else if (option==
"-conf")
180 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
183 path_to_config_dir = (string)argv[i+1];
192 else if (option==
"-img")
196 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
199 string file_name = (string)argv[i+1];
200 path_to_image_filename.push_back(file_name);
205 else if (option==
"-sens")
209 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
212 string file_name = (string)argv[i+1];
213 path_to_sens_filename.push_back(file_name);
217 else if (option==
"-inv")
219 invert_order_flag =
true;
227 else if (option==
"-dout")
231 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
234 path_dout = argv[i+1];
238 else if (option==
"-fout")
242 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
245 path_fout = argv[i+1];
249 else if (option==
"-oweight")
260 Cerr(
"***** castor-mergeBedPositions() -> Unknown option '" << option <<
"' !" << endl);
272 Cerr(
"***** castor-mergeBedPositions() -> Please provide at least two image filename !" << endl);
276 if (path_fout.empty() && path_dout.empty())
278 Cerr(
"***** castor-mergeBedPositions() -> Please provide an output option for output files (-fout or -dout) !" << endl);
282 if (!path_fout.empty() && !path_dout.empty())
284 Cerr(
"***** castor-mergeBedPositions() -> Please provide either output option -fout or -dout but not both !" << endl);
288 if (path_to_sens_filename.size()>0 && path_to_sens_filename.size()!=path_to_image_filename.size())
290 Cerr(
"***** castor-mergeBedPositions() -> If using sensitivity images as weights, please provide as many sensitivity images as input images !" << endl);
305 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while checking for the config directory path !" << endl);
311 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while initializing output directory !" << endl);
317 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while logging command line arguments !" << endl);
332 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Load input images" << endl);
342 for (uint32_t bed=0; bed<nb_beds; bed++)
347 if (invert_order_flag) p_images[bed] =
IntfLoadImageFromScratch(path_to_image_filename[nb_beds-1-bed],p_image_fields[bed],verbose);
350 if (p_images[bed]==NULL)
352 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while reading image for bed " << bed+1 <<
" !" << endl);
358 for (uint32_t bed=1; bed<nb_beds; bed++)
362 Cerr(
"***** castor-mergeBedPositions() -> Inconsistency between image dimensions !" << endl);
368 uint32_t dim_x = p_image_fields[0]->
mtx_size[0];
369 uint32_t dim_y = p_image_fields[0]->
mtx_size[1];
370 uint32_t dim_z = p_image_fields[0]->
mtx_size[2];
371 uint32_t dim_xyz = dim_x * dim_y * dim_z;
381 if (multiple_bed_displacement<=0.)
384 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Bed displacement not found in image headers, search for it in the scanner configuration file" << endl);
388 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while searching for scanner system !" << endl);
394 Cerr(
"***** castor-mergeBedPositions() -> Multiple bed displacement field not found in the scanner configuration file '" <<
401 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Use a bed displacement of " << multiple_bed_displacement <<
" mm" << endl);
408 FLTNB tolerance = 1.e-4;
410 FLTNB nb_slices_fltnb = multiple_bed_displacement / p_image_fields[0]->
vox_size[2];
412 FLTNB nb_slices_intnb = ((
FLTNB)((uint32_t)nb_slices_fltnb));
414 uint32_t displacement_slices = 0;
417 if (nb_slices_fltnb-nb_slices_intnb<0.5)
420 if (nb_slices_fltnb-nb_slices_intnb<tolerance) displacement_slices = ((uint32_t)nb_slices_fltnb);
424 Cerr(
"***** castor-mergeBedPositions() -> Bed displacement is not compatible with the slice thickness of " << p_image_fields[0]->vox_size[2] <<
" mm !" << endl);
433 if (nb_slices_intnb+1.-nb_slices_fltnb<tolerance) displacement_slices = ((uint32_t)nb_slices_fltnb) + 1;
437 Cerr(
"***** castor-mergeBedPositions() -> Bed displacement is not compatible with the slice thickness of " << p_image_fields[0]->vox_size[2] <<
" mm !" << endl);
443 if (displacement_slices==0 || displacement_slices>dim_z)
445 Cerr(
"***** castor-mergeBedPositions() -> Computed displacement of " << displacement_slices <<
" slices makes no sense !" << endl);
450 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Bed displacement corresponds to " << displacement_slices <<
" slices" << endl);
453 uint32_t whole_dim_z = dim_z + ((uint32_t)(nb_beds-1)) * displacement_slices;
454 uint32_t whole_dim_xyz = dim_x * dim_y * whole_dim_z;
457 if (verbose>=1)
Cout(
"castor-mergeBedPositiosn() -> Output merged image has " << whole_dim_z <<
" slices" << endl);
465 for (uint32_t bed=0; bed<nb_beds; bed++) p_weights[bed] = NULL;
468 if (path_to_sens_filename.size()>0)
471 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Load sensitivity images" << endl);
475 for (uint32_t bed=0; bed<nb_beds; bed++)
480 if (invert_order_flag) p_weights[bed] =
IntfLoadImageFromScratch(path_to_sens_filename[nb_beds-1-bed],p_sens_fields[bed],verbose);
483 if (p_weights[bed]==NULL)
485 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while reading sensitivity for bed " << bed+1 <<
" !" << endl);
490 for (uint32_t bed=1; bed<nb_beds; bed++)
494 Cerr(
"***** castor-mergeBedPositions() -> Inconsistency between sensitivity dimensions !" << endl);
499 for (uint32_t bed=0; bed<nb_beds; bed++)
503 Cerr(
"***** castor-mergeBedPositions() -> Inconsistency between input image and sensitivity dimensions for bed " << bed+1 <<
" !" << endl);
512 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Initialize weights to 1." << endl);
514 for (uint32_t bed=0; bed<nb_beds; bed++)
516 p_weights[bed] = (
FLTNB*)malloc(dim_xyz*
sizeof(
FLTNB));
517 for (uint32_t v=0; v<dim_xyz; v++) p_weights[bed][v] = 1.;
526 if (verbose>=1)
Cout(
"castor-mergeBedPositiosn() -> Compute whole image" << endl);
529 for (uint32_t v=0; v<whole_dim_xyz; v++) p_whole_image[v] = 0.;
532 for (uint32_t v=0; v<whole_dim_xyz; v++) p_whole_weight[v] = 0.;
535 for (uint32_t bed=0; bed<nb_beds; bed++)
538 for (uint32_t z=0; z<dim_z; z++)
541 uint32_t whole_z = z + bed * displacement_slices;
543 uint32_t base_z = z * dim_x * dim_y;
544 uint32_t whole_base_z = whole_z * dim_x * dim_y;
546 for (uint32_t xy=0; xy<dim_x*dim_y; xy++)
549 p_whole_image[whole_base_z+xy] += p_images[bed][base_z+xy] * p_weights[bed][base_z+xy];
551 p_whole_weight[whole_base_z+xy] += p_weights[bed][base_z+xy];
556 for (uint32_t v=0; v<whole_dim_xyz; v++)
558 if (p_whole_weight[v]>0.) p_whole_image[v] /= p_whole_weight[v];
559 else p_whole_image[v] = 0.;
567 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Write output image(s)" << endl);
571 p_image_fields[0]->
mtx_size[2] = whole_dim_z;
589 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while writing output whole image !" << endl);
597 string whole_weight_filename = p_OutputManager->
GetPathName() + p_OutputManager->
GetBaseName() +
"_weights";
601 Cerr(
"***** castor-mergeBedPositions() -> A problem occured while writing global weights image !" << endl);
613 for (uint32_t bed=0; bed<nb_beds; bed++)
if (p_images[bed]) free(p_images[bed]);
619 for (uint32_t bed=0; bed<nb_beds; bed++)
if (p_weights[bed]) free(p_weights[bed]);
623 if (p_whole_image) free(p_whole_image);
625 if (p_whole_weight) free(p_whole_weight);
628 if (verbose>=1)
Cout(endl);
This header file is mainly used to declare some macro definitions and all includes needed from the st...
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
Declaration of class oImageDimensionsAndQuantification.
void SetVerbose(int a_verbose)
set verbosity
int FindScannerSystem(string a_scannerName)
Look for a file matching with the scanner name in parameter inside the scanner repository.
int IntfCheckDimensionsConsistency(Intf_fields ImgFields1, Intf_fields ImgFields2)
string GetPathToScannerFile()
int main(int argc, char **argv)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
Declaration of class iDataFilePET.
Declaration of class oIterativeAlgorithm.
Declaration of class iDataFileSPECT.
Declaration of class iScannerPET.
int LogCommandLine(int argc, char **argv)
Write log file header with the provided command line options and different informations.
int ConvertFromString(const string &a_str, string *a_result)
Copy the 'a_str' string in the position pointed by 'a_result'.
int CheckConfigDir(const string &a_path)
Set the path to the CASTOR config directory from the given path if not empty or through the existence...
const string & GetPathName()
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
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...
Singleton class that Instantiate and initialize the scanner object.
FLTNB * IntfLoadImageFromScratch(const string &a_pathToHeaderFile, Intf_fields *ap_ImgFields, int vb)
Declaration of class sScannerManager.
void SetMPIRank(int a_mpiRank)
Initialize the machine index for MPI.
#define KEYWORD_MANDATORY
Declaration of class sRandomNumberGenerator.
const string & GetBaseName()
Declaration of class oSensitivityGenerator.
Interfile fields. This structure contains all the Interfile keys currently managed by CASToR Decl...
FLTNB multiple_bed_displacement
Declaration of class sOutputManager.
int InitOutputDirectory(const string &a_pathFout, const string &a_pathDout)
Create the output directory if any, extract the base name and create the log file.
This file is used for all kind of different functions designed for options parsing and ASCII file rea...
void SetVerbose(int a_verboseLevel)
set verbosity
Declaration of class sAddonManager.
void GetUserEndianness()
Check user/host computer endianness and write it to the global variable User_Endianness.
string originating_system
int IntfWriteImageFromIntfFields(const string &a_pathToImg, FLTNB *ap_ImgMatrix, Intf_fields Img_fields, int vb)