8 #include "gVariables.hh" 10 #include "sOutputManager.hh" 11 #include "sScannerManager.hh" 12 #include "oInterfileIO.hh" 32 cout <<
"Usage: castor-mergeBedPositions -img image1.hdr -img image2.hdr -(f/d)out output [settings]" << 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;
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;
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;
62 cout <<
" Compiled with OpenMP (will use as many cores as available)" << 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;
131 bool flip_output_axial =
false;
138 FLTNB tolerance_as_percent_slice_thickness = 1.;
142 string path_to_config_dir =
"";
149 for (
int i=1; i<argc; i++)
152 string option = (string)argv[i];
159 if (option==
"-h" || option==
"--help" || option==
"-help")
166 else if (option==
"-tolerance")
170 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
175 Cerr(
"***** castor-mergeBedPositions() -> Exception when trying to read provided verbosity level '" << verbose <<
" for option: " << option << endl);
181 else if (option==
"-vb")
185 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
190 Cerr(
"***** castor-mergeBedPositions() -> Exception when trying to read provided verbosity level '" << verbose <<
" for option: " << option << endl);
196 else if (option==
"-conf")
200 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
203 path_to_config_dir = (string)argv[i+1];
212 else if (option==
"-img")
216 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
219 string file_name = (string)argv[i+1];
220 path_to_image_filename.push_back(file_name);
225 else if (option==
"-sens")
229 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
232 string file_name = (string)argv[i+1];
233 path_to_sens_filename.push_back(file_name);
237 else if (option==
"-inv")
239 invert_order_flag =
true;
247 else if (option==
"-dout")
251 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
254 path_dout = argv[i+1];
258 else if (option==
"-fout")
262 Cerr(
"***** castor-mergeBedPositions() -> Argument missing for option: " << option << endl);
265 path_fout = argv[i+1];
269 else if (option==
"-oweight")
274 else if (option==
"-flipZ")
276 flip_output_axial =
true;
285 Cerr(
"***** castor-mergeBedPositions() -> Unknown option '" << option <<
"' !" << endl);
297 Cerr(
"***** castor-mergeBedPositions() -> Please provide at least two image filename !" << endl);
301 if (path_fout.empty() && path_dout.empty())
303 Cerr(
"***** castor-mergeBedPositions() -> Please provide an output option for output files (-fout or -dout) !" << endl);
307 if (!path_fout.empty() && !path_dout.empty())
309 Cerr(
"***** castor-mergeBedPositions() -> Please provide either output option -fout or -dout but not both !" << endl);
313 if (path_to_sens_filename.size()>0 && path_to_sens_filename.size()!=path_to_image_filename.size())
315 Cerr(
"***** castor-mergeBedPositions() -> If using sensitivity images as weights, please provide as many sensitivity images as input images !" << endl);
324 omp_set_num_threads(omp_get_max_threads());
338 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while checking for the config directory path !" << endl);
344 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while initializing output directory !" << endl);
350 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while logging command line arguments !" << endl);
365 if (verbose>=
VERBOSE_LIGHT)
Cout(
"castor-mergeBedPositions() -> Load input images" << endl);
375 for (uint32_t bed=0; bed<nb_beds; bed++)
380 if (invert_order_flag) p_images[bed] =
IntfLoadImageFromScratch(path_to_image_filename[nb_beds-1-bed],p_image_fields[bed],verbose);
383 if (p_images[bed]==NULL)
385 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while reading image for bed " << bed+1 <<
" !" << endl);
391 for (uint32_t bed=1; bed<nb_beds; bed++)
395 Cerr(
"***** castor-mergeBedPositions() -> Inconsistency between image dimensions !" << endl);
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);
415 INTNB dim_xy = dim_x * dim_y;
416 INTNB dim_xyz = dim_xy * dim_z;
423 bool one_position_not_zero =
false;
424 bool all_positions_not_zero =
true;
425 for (uint32_t bed=0; bed<nb_beds; bed++)
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);
433 if (one_position_not_zero && !all_positions_not_zero)
435 Cerr(
"!!!!! castor-mergeBedPositions() -> The bed relative position is provided in some but not all interfile headers !" << endl);
442 if (all_positions_not_zero)
445 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Recenter relative bed positions" << endl);
449 for (uint32_t bed=0; bed<nb_beds; bed++) center += p_image_fields[bed]->bed_relative_position;
450 center /= ((
FLTNB)nb_beds);
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;
459 if (!all_positions_not_zero)
462 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Recover default bed displacement from scanner configuration file" << endl);
466 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while searching for scanner system '" << p_image_fields[0]->originating_system <<
"' !" << endl);
470 FLTNB default_bed_displacement = -1.;
473 Cerr(
"***** castor-mergeBedPositions() -> Multiple bed displacement field not found in the scanner configuration file '" <<
478 if (default_bed_displacement<=0.)
480 Cerr(
"***** castor-mergeBedPositions() -> Default bed displacement from the scanner configuration file must be strictly positive !" << endl);
484 if (verbose>=1)
Cout(
" --> Bed displacement of " << default_bed_displacement <<
" mm" << endl);
486 for (uint32_t bed=0; bed<nb_beds; bed++)
489 FLTNB bed_offset = 0.;
491 if (nb_beds%2==1) bed_offset = ((
FLTNB)( ((int32_t)(bed))-((int32_t)(nb_beds))/2 )) * default_bed_displacement;
493 else bed_offset = (((
FLTNB)( ((int32_t)(bed))-((int32_t)(nb_beds))/2 )) + 0.5) * default_bed_displacement;
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);
512 for (uint32_t bed=1; bed<nb_beds; bed++)
513 if (p_image_fields[bed]->bed_relative_position<reference_minimum_position)
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;
526 FLTNB tolerance = p_image_fields[0]->
vox_size[2] * tolerance_as_percent_slice_thickness / 100.;
531 for (uint32_t bed=0; bed<nb_beds; bed++)
534 FLTNB nb_slices_fltnb = p_displacement_from_reference[bed] / p_image_fields[0]->
vox_size[2];
539 if (nb_slices_fltnb-nb_slices_intnb<0.5)
542 if (nb_slices_fltnb-nb_slices_intnb<tolerance) p_displacement_slices[bed] = ((
INTNB)nb_slices_fltnb);
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);
555 if (nb_slices_intnb+1.-nb_slices_fltnb<tolerance) p_displacement_slices[bed] = ((
INTNB)nb_slices_fltnb) + 1;
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);
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];
574 INTNB whole_dim_z = max_displacement_slices + dim_z;
575 INTNB whole_dim_xyz = dim_xy * whole_dim_z;
578 if (verbose>=1)
Cout(
"castor-mergeBedPositiosn() -> Output merged image has " << whole_dim_z <<
" slices" << endl);
586 for (uint32_t bed=0; bed<nb_beds; bed++) p_weights[bed] = NULL;
589 if (path_to_sens_filename.size()>0)
592 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Load sensitivity images" << endl);
596 for (uint32_t bed=0; bed<nb_beds; bed++)
601 if (invert_order_flag) p_weights[bed] =
IntfLoadImageFromScratch(path_to_sens_filename[nb_beds-1-bed],p_sens_fields[bed],verbose);
604 if (p_weights[bed]==NULL)
606 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while reading sensitivity for bed " << bed+1 <<
" !" << endl);
611 for (uint32_t bed=1; bed<nb_beds; bed++)
615 Cerr(
"***** castor-mergeBedPositions() -> Inconsistency between sensitivity dimensions !" << endl);
620 for (uint32_t bed=0; bed<nb_beds; bed++)
624 Cerr(
"***** castor-mergeBedPositions() -> Inconsistency between input image and sensitivity dimensions for bed " << bed+1 <<
" !" << endl);
633 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Initialize weights to 1." << endl);
635 for (uint32_t bed=0; bed<nb_beds; bed++)
637 p_weights[bed] = (
FLTNB*)malloc(dim_xyz*
sizeof(
FLTNB));
638 for (
INTNB v=0; v<dim_xyz; v++) p_weights[bed][v] = 1.;
647 if (verbose>=1)
Cout(
"castor-mergeBedPositiosn() -> Compute whole image" << endl);
650 for (
INTNB v=0; v<whole_dim_xyz; v++) p_whole_image[v] = 0.;
653 for (
INTNB v=0; v<whole_dim_xyz; v++) p_whole_weight[v] = 0.;
656 for (uint32_t bed=0; bed<nb_beds; bed++)
660 #pragma omp parallel for private(z) schedule(guided) 661 for (z=0; z<dim_z; z++)
664 INTNB whole_z = z + p_displacement_slices[bed];
666 INTNB base_z = z * dim_xy;
667 INTNB whole_base_z = whole_z * dim_xy;
669 for (
INTNB xy=0; xy<dim_xy; xy++)
672 p_whole_image[whole_base_z+xy] += p_images[bed][base_z+xy] * p_weights[bed][base_z+xy];
674 p_whole_weight[whole_base_z+xy] += p_weights[bed][base_z+xy];
679 for (
INTNB v=0; v<whole_dim_xyz; v++)
681 if (p_whole_weight[v]>0.) p_whole_image[v] /= p_whole_weight[v];
682 else p_whole_image[v] = 0.;
689 if (flip_output_axial)
692 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Flip output images axially" << endl);
694 for (
INTNB z_1=0; z_1<whole_dim_z/2; z_1++)
697 INTNB z_2 = whole_dim_z - 1 - z_1;
699 INTNB base_z1 = z_1 * dim_xy;
700 INTNB base_z2 = z_2 * dim_xy;
702 for (
INTNB xy=0; xy<dim_xy; xy++)
705 INTNB indice_1 = base_z1 + xy;
706 INTNB indice_2 = base_z2 + xy;
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;
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;
724 if (verbose>=1)
Cout(
"castor-mergeBedPositions() -> Write output image(s)" << endl);
728 p_image_fields[0]->
mtx_size[2] = whole_dim_z;
746 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while writing output whole image !" << endl);
754 string whole_weight_filename = p_OutputManager->
GetPathName() + p_OutputManager->
GetBaseName() +
"_weights";
758 Cerr(
"***** castor-mergeBedPositions() -> A problem occurred while writing global weights image !" << endl);
770 for (uint32_t bed=0; bed<nb_beds; bed++)
if (p_images[bed]) free(p_images[bed]);
776 for (uint32_t bed=0; bed<nb_beds; bed++)
if (p_weights[bed]) free(p_weights[bed]);
780 if (p_whole_image) free(p_whole_image);
782 if (p_whole_weight) free(p_whole_weight);
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
void SetVerbose(int a_verbose)
int FindScannerSystem(string a_scannerName)
FLTNB bed_relative_position
string GetPathToScannerFile()
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)
const string & GetPathName()
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 IntfCheckDimensionsConsistency(Intf_fields ImgFields1, Intf_fields ImgFields2)
Singleton class that Instantiate and initialize the scanner object.
void SetMPIRank(int a_mpiRank)
#define KEYWORD_MANDATORY
const string & GetBaseName()
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 'a_str' string in the position pointed by 'a_result'.
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...
string originating_system