// Definitions of a class that does the parsing and // sanity checking of commnad line options for the // "eddy" application. // // EddyCommandLineOptions.cpp // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2011 University of Oxford // /* Part of FSL - FMRIB's Software Library http://www.fmrib.ox.ac.uk/fsl fsl@fmrib.ox.ac.uk Developed at FMRIB (Oxford Centre for Functional Magnetic Resonance Imaging of the Brain), Department of Clinical Neurology, Oxford University, Oxford, UK LICENCE FMRIB Software Library, Release 6.0 (c) 2018, The University of Oxford (the "Software") The Software remains the property of the Oxford University Innovation ("the University"). The Software is distributed "AS IS" under this Licence solely for non-commercial use in the hope that it will be useful, but in order that the University as a charitable foundation protects its assets for the benefit of its educational and research purposes, the University makes clear that no condition is made or to be implied, nor is any warranty given or to be implied, as to the accuracy of the Software, or that it will be suitable for any particular purpose or for use under any specific conditions. Furthermore, the University disclaims all responsibility for the use which is made of the Software. It further disclaims any liability for the outcomes arising from using the Software. The Licensee agrees to indemnify the University and hold the University harmless from and against any and all claims, damages and liabilities asserted by third parties (including claims for negligence) which arise directly or indirectly from the use of the Software or the sale of any products based on the Software. No part of the Software may be reproduced, modified, transmitted or transferred in any form or by any means, electronic or mechanical, without the express permission of the University. The permission of the University is not required if the said reproduction, modification, transmission or transference is done without financial return, the conditions of this Licence are imposed upon the receiver of the product, and all original and amended source code is included in any transmitted product. You may be held legally responsible for any copyright infringement that is caused or encouraged by your failure to abide by these terms and conditions. You are not permitted under this Licence to use this Software commercially. Use for which any financial return is received shall be defined as commercial use, and includes (1) integration of all or part of the source code or the Software into a product for sale or license by or on behalf of Licensee to third parties or (2) use of the Software or any derivative of it for research with the final aim of developing software products for sale or license to a third party or (3) use of the Software or any derivative of it for research with the final aim of developing non-software products for sale or license to a third party, or (4) use of the Software to provide any service to an external organisation for which payment is received. If you are interested in using the Software commercially, please contact Oxford University Innovation ("OUI"), the technology transfer company of the University, to negotiate a licence. Contact details are: fsl@innovation.ox.ac.uk quoting Reference Project 9564, FSL.*/ #include #include #include #include #include #include "utils/options.h" #include "topup/topup_file_io.h" #include "EddyHelperClasses.h" #include "EddyUtils.h" #include "EddyCommandLineOptions.h" using namespace std; using namespace EDDY; EddyCommandLineOptions::diff::diff() try : // Parameters related to specification of diffusion weighting _bvecs(string("--bvecs"),string(""),string("\tFile containing the b-vectors for all volumes in --imain"),true,Utilities::requires_argument), _bvals(string("--bvals"),string(""),string("\tFile containing the b-values for all volumes in --imain"),true,Utilities::requires_argument), _bdeltas(string("--bdeltas"),string(""),string("File containing the b-delta values for all volumes in --imain"),false,Utilities::requires_argument), _data_is_shelled(string("--data_is_shelled"),false,string("Assume, don't check, that data is shelled (default false)"),false, Utilities::no_argument), _brange(string("--b_range"),100,string("Range of b-values considered to be the same shell (default 100)"),false, Utilities::requires_argument), // Parameters related to least squares reconstruction of the data _resamp(string("--resamp"),string("jac"),string("Final resampling method (jac/lsr, default jac)"),false, Utilities::requires_argument), _lsr_lambda(string("--lsr_lambda"),0.01,string("Regularisation weight for LSR-resampling."),false,Utilities::requires_argument), // Parameters related to eddy current modelling _flm(string("--flm"),string("quadratic"),string("\tFirst level EC model (movement/linear/quadratic/cubic, default quadratic)"),false,Utilities::requires_argument), _slm_str(string("--slm"),string("none"),string("\tSecond level EC model (none/linear/quadratic, default none)"),false,Utilities::requires_argument), _b0_flm(string("--b0_flm"),string("movement"),string("First level EC model for b0 scans (movement/linear/quadratic, default movement)"),false,Utilities::requires_argument), _b0_slm_str(string("--b0_slm"),string("none"),string("Second level EC model for b0 scans (none/linear/quadratic, default none)"),false,Utilities::requires_argument), _rep_time(string("--rep_time"),5.0,string("Repetition time (seconds)"),false,Utilities::requires_argument), _long_ec_str(string("--lecm"),string("none"),string("Model for long (time-varying) EC (none/weights/tc/jointweights/jointtc default none)"),false,Utilities::requires_argument), _long_ec_dont_reest(string("--lecm_dont_reest"),false,string("Don't reestimate EC when estimating long EC (default false)"),false, Utilities::no_argument), _long_ec_sep_offs_move(string("--lecm_sep_offs_move"),false,string("Attempt to separate field offset from subject movement when estimating long EC (default false)"),false, Utilities::no_argument), // Parameters related to outlier detection/replacement _rep_noise(string("--rep_noise"),false,string("Add noise to replaced outliers (default false)"),false, Utilities::no_argument), _ol_ss(string("--ol_ss"),string("sw"),string("Summary statistics for OL, shell-wise (sw) or pooled (pooled). (default sw)"),false,Utilities::requires_argument), _ol_pos(string("--ol_pos"),false,string("Consider both positive and negative outliers if set (default false)"),false,Utilities::no_argument), _ol_sqr(string("--ol_sqr"),false,string("Consider outliers among sums-of-squared differences if set (default false)"),false,Utilities::no_argument), // Parameters related to the Gaussian process _covfunc(string("--covfunc"),string("spheri"),string("Covariance function for GP (spheri/expo/old, default spheri)"),false, Utilities::requires_argument), // Should bvecs be rotated during estimations (probably not) _rbvde(string("--rbvde"),false,string("\tRotate b-vecs during estimation (default false)"),false,Utilities::no_argument), // Parameters related to "tricky" movements for diffusion _dont_sep_offs_move(string("--dont_sep_offs_move"),false,string("Do NOT attempt to separate field offset from subject movement (default false)"),false, Utilities::no_argument), _offset_model(string("--offset_model"),string("linear_lag"),string("Second level model for field offset (default linear_lag)"),false, Utilities::requires_argument), _dont_peas(string("--dont_peas"),false,string("Do NOT perform a post-eddy alignment of shells (default false)"),false, Utilities::no_argument), _use_b0s_for_peas(string("--b0_peas"),false,string("Use interspersed b0s to perform post-eddy alignment of shells (default false)"),false, Utilities::no_argument), _session(string("--session"),string(""),string("File containing session indices for all volumes in --imain"),false,Utilities::requires_argument), // Parameters related to the writing of various "extra" output _fields(string("--fields"),false,string("Write EC fields as images (default false)"),false, Utilities::no_argument), _write_cnr_maps(string("--cnr_maps"),false,string("Write shell-wise cnr-maps (default false)"),false, Utilities::no_argument), _write_range_cnr_maps(string("--range_cnr_maps"),false,string("Write shell-wise range-cnr-maps (default false)"),false, Utilities::no_argument), _write_scatter_brain_predictions(string("--write_scatter_brain_predictions"),false,string("Write predictions obtained with a scattered data approach (in addition to corrected, default false)"),false, Utilities::no_argument), // Parameters related to things that can be useful for debugging/development _dwi_only(string("--dwi_only"),false,string("Only register the dwi images (default false)"),false, Utilities::no_argument), _b0_only(string("--b0_only"),false,string("Only register the b0 images (default false)"),false, Utilities::no_argument), _test_rot(string("--test_rot"),std::vector(0),string("Do a large rotation to test b-vecs"),false,Utilities::requires_argument), _print_mi_values(string("--pmiv"),false,string("Write text file of MI values between shells (default false)"),false,Utilities::no_argument), _print_mi_planes(string("--pmip"),false,string("Write text file of (2D) MI values between shells (default false)"),false,Utilities::no_argument), // --mb_offs will soon be deprecated, so we should warn about it _mb_offs(string("--mb_offs"),0,string("Multi-band offset (-1 if bottom slice removed, 1 if top slice removed) (DEPRECATED!)"),false,Utilities::requires_argument), // The remaining are deprecated parameters _sep_offs_move(string("--sep_offs_move"),true,string("Attempt to separate field offset from subject movement (DEPRECATED!)"),false, Utilities::no_argument), _peas(string("--peas"),true,string("Perform a post-eddy alignment of shells (DEPRECATED!)"),false, Utilities::no_argument), _rms(string("--rms"),true,string("Write movement induced RMS (DEPRECATED!)"),false, Utilities::no_argument), _slorder(string("--slorder"),string(""),string("Name of text file defining slice/group order (DEPRECATED!)"),false,Utilities::requires_argument) {} catch (const EddyInputError& e) { cout << e.what() << endl; cout << "Terminating program" << endl; exit(EXIT_FAILURE); } EddyCatch EddyCommandLineOptions::fmri::fmri() try : _session(string("--session"),string(""),string("File containing session indices for all volumes in --imain"),true,Utilities::requires_argument), _rep_time(string("--rep_time"),3.0,string("Repetition time (seconds)"),true,Utilities::requires_argument), _echo_time(string("--echo_time"),40.0,string("Echo time (milliseconds)"),false,Utilities::requires_argument), _covfunc(string("--covfunc"),string("sqexp"),string("Covariance function for GP (sqexp, default sqexp)"),false, Utilities::requires_argument), _write_snr_maps(string("--snr_maps"),false,string("Write session-wise SNR maps (default false)"),false, Utilities::no_argument), _fast_map(string("--fast_map"),string(""),string("FAST pve map for CSF"),false, Utilities::requires_argument) {} catch (const EddyInputError& e) { cout << e.what() << endl; cout << "Terminating program" << endl; exit(EXIT_FAILURE); } EddyCatch EddyCommandLineOptions::EddyCommandLineOptions(int argc, char *argv[]) try : _title("eddy \nCopyright(c) 2015, University of Oxford (Jesper Andersson)"), _examples("eddy --monsoon"), // Mandatory parameters _imain(string("--imain"),string(""),string("\tFile containing all the images to estimate distortions for"),true,Utilities::requires_argument), _mask(string("--mask"),string(""),string("\tMask to indicate brain"),true,Utilities::requires_argument), _acqp(string("--acqp"),string(""),string("\tFile containing acquisition parameters"),true,Utilities::requires_argument), _index(string("--index"),string(""),string("\tFile containing indices for all volumes in --imain into --acqp and --topup"),true,Utilities::requires_argument), _out(string("--out"),string(""),string("\tBasename for output"),true,Utilities::requires_argument), // Parameters related to input susceptibility field _topup(string("--topup"),string(""),string("\tBase name for output files from topup"),false,Utilities::requires_argument), _field(string("--field"),string(""),string("\tName of file with susceptibility field (in Hz)"),false,Utilities::requires_argument), _field_mat(string("--field_mat"),string(""),string("Name of rigid body transform for susceptibility field"),false,Utilities::requires_argument), // Paramaters related to the general running of vanilla eddy _niter_tmp(string("--niter"),5,string("\tNumber of iterations (default 5)"),false,Utilities::requires_argument), _fwhm_tmp(string("--fwhm"),std::vector(1,0.0),string("\tFWHM for conditioning filter when estimating the parameters (default 0)"),false,Utilities::requires_argument), _ref_scan_no(string("--ref_scan_no"),0,string("Zero-offset # of ref (for location) volume (default 0)"),false,Utilities::requires_argument), _shape_ref_scan_nos(string("--shape_ref_scan_nos"),std::vector(0),string("Zero-offset #s of refs (for shape) volumes"),false,Utilities::requires_argument), _interp(string("--interp"),string("spline"),string("Interpolation model for estimation step (spline/trilinear, default spline)"),false,Utilities::requires_argument), _extrap(string("--extrap"),string("periodic"),string("Extrapolation model for estimation step (periodic/mirror, default periodic)"),false,Utilities::requires_argument), _epvalid(string("--epvalid"),false,string("Indicates that extrapolation is valid in EP direction (default false)"),false, Utilities::no_argument), // Parameters related to outlier detection/replacement _rep_ol(string("--repol"),false,string("\tDetect and replace outlier slices (default false))"),false, Utilities::no_argument), _ol_nstd(string("--ol_nstd"),4.0,string("Number of std off to qualify as outlier (default 4)"),false,Utilities::requires_argument), _ol_nvox(string("--ol_nvox"),250,string("Min # of voxels in a slice for inclusion in outlier detection (default 250)"),false,Utilities::requires_argument), _ol_ec(string("--ol_ec"),1,string("\tError type (1 or 2) to keep constant for outlier detection (default 1)"),false,Utilities::requires_argument), _ol_type(string("--ol_type"),string("sw"),string("Type of outliers, slicewise (sw), groupwise (gw) or both (both). (default sw)"),false,Utilities::requires_argument), _ol_jacut(string("--ol_jacut"),3.0,string("Upper threshold for Jacobian outlier detection mask (default 3)"),false,Utilities::requires_argument), // Parameters related to slice-to-volume motion correction _mb(string("--mb"),1,string("\tMulti-band factor"),false,Utilities::requires_argument), _slspec(string("--slspec"),string(""),string("Name of text file completely specifying slice/group acuistion. N.B. --slspec and --json are mutually exclusive."),false,Utilities::requires_argument), _json(string("--json"),string(""),string("\tName of .json text file with information about slice timing. N.B. --json and --slspec are mutually exclusive."),false,Utilities::requires_argument), _mp_order(string("--mporder"),std::vector(1,0),string("Order of slice-to-vol movement model (default 0, i.e. vol-to-vol"),false,Utilities::requires_argument), _s2v_lambda(string("--s2v_lambda"),std::vector(1,1.0),string("Regularisation weight for slice-to-vol movement. (default 1, reasonable range 1--10"),false,Utilities::requires_argument), _s2v_niter(string("--s2v_niter"),std::vector(1,5),string("Number of iterations for slice-to-vol (default 5)"),false,Utilities::requires_argument), _s2v_fwhm(string("--s2v_fwhm"),std::vector(1,0.0),string("FWHM for conditioning filter when estimating slice-to-vol parameters (default 0)"),false,Utilities::requires_argument), _s2v_interp(string("--s2v_interp"),string("trilinear"),string("Slice-to-vol interpolation model for estimation step (spline/trilinear, default trilinear)"),false,Utilities::requires_argument), // Parameters related to susceptibility-by-movement interaction estimation _estimate_mbs(string("--estimate_move_by_susceptibility"),false,string("Estimate how susceptibility field changes with subject movement (default false)"),false,Utilities::no_argument), _mbs_niter(string("--mbs_niter"),10,string("Number of iterations for MBS estimation (default 10)"),false,Utilities::requires_argument), _mbs_lambda(string("--mbs_lambda"),10.0,string("Weighting of regularisation for MBS estimation (default 10)"),false,Utilities::requires_argument), _mbs_ksp(string("--mbs_ksp"),10.0,string("Knot-spacing for MBS field estimation (default 10mm)"),false,Utilities::requires_argument), // Parameters related to the Gaussian process _hyparcostfunc(string("--hpcf"),string("CV"),string("\tCost-function for GP hyperparameters (MML/CV/GPP/CC, default CV)"),false, Utilities::requires_argument), _nvoxhp(string("--nvoxhp"),1000,string("# of voxels used to estimate the hyperparameters (default 1000)"),false, Utilities::requires_argument), _initrand(string("--initrand"),0,string("Seeds rand for when selecting voxels (default 0=no seeding)"),false, Utilities::optional_argument), _hyparfudgefactor(string("--ff"),10.0,string("\tFudge factor for hyperparameter error variance (default 10.0)"),false, Utilities::requires_argument), _hypar(string("--hypar"),std::vector(0),string("\tUser specified values for GP hyperparameters"),false,Utilities::requires_argument), // Miscallenous parameters related to missing planes/output masking _fep(string("--fep"),false,string("\tFill empty planes in x- or y-directions (default false)"),false, Utilities::no_argument), _dont_mask_output(string("--dont_mask_output"),false,string("Do not mask output to include only voxels present for all volumes (default false)"),false, Utilities::no_argument), // Parameters related to initialisation of movement/distortion parameters _init(string("--init"),string(""),string("\tText file with parameters for initialisation"),false,Utilities::requires_argument), _init_s2v(string("--init_s2v"),string(""),string("Text file with parameters for initialisation of slice-to-vol movement"),false,Utilities::requires_argument), _init_mbs(string("--init_mbs"),string(""),string("4D image file for initialisation of movement-by-susceptibility"),false,Utilities::requires_argument), // Parameters related to the writing of various "extra" output _dfields(string("--dfields"),false,string("Write total displacement fields (default false)"),false, Utilities::no_argument), _residuals(string("--residuals"),false,string("Write residuals (between GP and observations), (default false)"),false, Utilities::no_argument), _with_outliers(string("--with_outliers"),false,string("Write corrected data (additionally) with outliers retained (default false)"),false, Utilities::no_argument), _history(string("--history"),false,string("Write history of mss and parameter estimates (default false)"),false, Utilities::no_argument), _write_slice_stats(string("--wss"),false,string("\tWrite slice-wise stats for each iteration (default false)"),false,Utilities::no_argument), _write_predictions(string("--write_predictions"),false,string("Write predicted data (in addition to corrected, default false)"),false, Utilities::no_argument), _no_text_files(string("--no_textout"),false,string("Supress writing of separate text output files (default false)"),false, Utilities::no_argument), // Parameters related to things that can be useful for debugging/development _debug_tmp(string("--debug"),0,string("\tLevel of debug print-outs (default 0)"),false,Utilities::requires_argument), _dbg_indx_str(string("--dbgindx"),string(""),string("Indicies (zero offset) of volumes for debug print-outs"),false,Utilities::requires_argument), _log_timings(string("--log_timings"),false,string("Write timing information (defaut false)"),false, Utilities::no_argument), // Parameters related to architecture/threads/cores etc _nthr(string("--nthr"),1,string("# of threads used when running eddy (default 1)"),false, Utilities::requires_argument), // Finally some generally helpful parameters _very_verbose(string("--very_verbose"),false,string("Switch on detailed diagnostic messages (default false)"),false, Utilities::no_argument), _verbose(string("-v,--verbose"),false,string("switch on diagnostic messages"),false, Utilities::no_argument), _help(string("-h,--help"),false,string("display this message"),false, Utilities::no_argument), _is_fmri(false), _init_rand(false), _fixed_hpar(false), _shape_references_set(false) { // All the commmon parameters have been initialised. Now we need to initialise also the modality specific ones. if (argc > 1) { if (std::string(argv[1]) != "fmri") { if (std::string(argv[1]) != "diffusion") std::cout << std::endl << "Warning: In a future release the first argument will have to be \"diffusion\" when using eddy on diffusion data, i.e." << std::endl << "eddy diffusion --imain='my_ima' --acqp='my_acqp' ..." << std::endl << std::endl; _is_fmri = false; } else { // This means that the first argument is "fmri" _is_fmri = true; // Temporarily block the fMRI branch to allow for bugfix release. throw EddyInputError("fMRI functionality is currently disabled awaiting more testing"); } } else { // This means that someone only typed eddy std::cout << std::endl << "Warning: In a future release the first argument will have to be \"diffusion\" when using eddy on diffusion data, i.e." << std::endl << "eddy diffusion --imain='my_ima' --acqp='my_acqp' ..." << std::endl << std::endl; std::cout << "If you want to see the help screen for eddy for fmri, just type \"eddy fmri\"" << std::endl << std::endl; } do_initial_parsing(argc,argv); // Do sanity checking and some initial reading of files // First warn about use of deprecated parameters if (IsDiffusion()) { if (_diff._sep_offs_move.set()) std::cout << "Warning: --sep_offs_move parameter is deprecated and will crash a future version" << std::endl << std::endl; if (_diff._peas.set()) std::cout << "Warning: --peas parameter is deprecated and will crash a future version" << std::endl << std::endl; if (_diff._rms.set()) std::cout << "Warning: --rms parameter is deprecated and will crash a future version" << std::endl << std::endl; if (_diff._mb_offs.set()) std::cout << "Warning: --mb_offs parameter is deprecated and will crash a future version" << std::endl << std::endl; if (_diff._slorder.set()) throw EddyInputError("--slorder has been deprecated"); } if (!_no_text_files.value()) std::cout << "Warning: Writing of individual text files will be discontinued in favour of a single .json file in future versions" << std::endl << std::endl; // Next, check on all common (to diffusion and fmri) parameters. // I have tried to keep the order of the checks to be the same as the order in which they are added to options. // First mandatory parameters // Image file exists? NEWIMAGE::volume4D imahdr; try { NEWIMAGE::read_volume_hdr_only(imahdr,_imain.value()); } // Throws if there is a problem catch(...) { throw EddyInputError("Error when attempting to read --imain file " + _imain.value()); } NEWIMAGE::volume maskhdr; try { NEWIMAGE::read_volume_hdr_only(maskhdr,_mask.value()); } // Throws if there is a problem catch(...) { throw EddyInputError("Error when attempting to read --mask file " + _mask.value()); } if (!samesize(imahdr[0],maskhdr,3,true)) throw EddyInputError("--imain and --mask images must have the same dimensions"); if (maskhdr.tsize() != 1) throw EddyInputError("--mask image must be 3D volume"); // File with acquisition parameters exists and makes sense NEWMAT::Matrix acqpM; try { acqpM = MISCMATHS::read_ascii_matrix(_acqp.value()); if (acqpM.Ncols() != 4) throw EddyInputError("Each row of the --acqp file must contain 4 values"); } catch (...) { throw EddyInputError("Error when attempting to read --acqp file " + _acqp.value()); } // Index file exists and makes sense given image file NEWMAT::Matrix index; try { index = MISCMATHS::read_ascii_matrix(_index.value()); if (std::min(index.Nrows(),index.Ncols()) != 1 || std::max(index.Nrows(),index.Ncols()) != imahdr.tsize()) { throw EddyInputError("--index must be an 1xN or Nx1 matrix where N is the number of volumes in --imain"); } } catch (...) { throw EddyInputError("Error when attempting to read --index file " + _index.value()); } if (!this->indicies_kosher(index,acqpM)) throw EddyInputError("Mismatch between --index and --acqp"); if (_out.value() == string("")) throw EddyInputError("--out must be a non-empty string"); // Parameters related to input susceptibility field // Check that not both topup and field files have been specified if (_topup.set() && _topup.value() != string("") && _field.set() && _field.value() != string("")) { throw EddyInputError("One cannot specify both --field and --topup file"); } // Topup file exists if one has been specified if (_topup.set() && _topup.value() != string("")) { try { TOPUP::TopupFileReader tfr(_topup.value()); } catch (const TOPUP::TopupFileIOException& e) { cout << e.what() << endl; throw EddyInputError("Error when attempting to read --topup files " + _topup.value() + "_fieldcoef.nii.gz and " + _topup.value() + "_movpar.txt"); } catch (...) { throw EddyInputError("Error when attempting to read --topup files " + _topup.value() + "_fieldcoef.nii.gz and " + _topup.value() + "_movpar.txt"); } } // Field file exists if one has been specified if (_field.set() && _field.value() != string("")) { try { TOPUP::TopupFileReader tfr(_field.value()); } catch (const TOPUP::TopupFileIOException& e) { cout << e.what() << endl; throw EddyInputError("Error when attempting to read --field file " + _field.value()); } catch (...) { throw EddyInputError("Error when attempting to read --field file " + _field.value()); } } // Field mat file exists if one has been specified if (_field_mat.set() && _field_mat.value() != string("")) { try { NEWMAT::Matrix fieldM = MISCMATHS::read_ascii_matrix(_field_mat.value()); if (fieldM.Nrows() != 4 || fieldM.Ncols() != 4) throw EddyInputError("--field_mat must be a 4x4 matrix"); NEWMAT::Matrix ul = fieldM.SubMatrix(1,3,1,3); float det = ul.Determinant(); if (std::abs(det-1.0) > 1e-6) throw EddyInputError("--field_mat must be a rigid body transformation"); } catch (...) { throw EddyInputError("Error when attempting to read --field_mat file" + _field_mat.value()); } } // Paramaters related to the general running of vanilla eddy // FWHM either once and for all or one per iteration _niter = static_cast(_niter_tmp.value()); _fwhm = _fwhm_tmp.value(); if (_fwhm.size() != 1 && _fwhm.size() != _niter) throw EddyInputError("--fwhm must be one value or one per iteration"); if (_fwhm.size() != _niter) _fwhm.resize(_niter,_fwhm[0]); // Reasonable FWHM for (unsigned int i=0; i<_fwhm.size(); i++) { if (_fwhm[i] < 0.0 || _fwhm[i] > 20.0) throw EddyInputError("--fwhm value outside valid range 0-20mm"); } // Location ref value is reasonable. if (_ref_scan_no.value() < 0 || _ref_scan_no.value() > imahdr.tsize()-1) throw EddyInputError("--ref_scan_no out of range"); // Shape reference reasonable if set. Must have exactly one ref per shell. if (_shape_ref_scan_nos.set()) { if (IsfMRI()) { // fMRI counts as a single shell if (_shape_ref_scan_nos.value().size() != 1) throw EddyInputError("--shape_ref_scan_nos has wrong number of entries"); if (_shape_ref_scan_nos.value()[0] < 0 || _shape_ref_scan_nos.value()[0] > imahdr.tsize()-1) throw EddyInputError("--shape_ref_scan_nos out of range"); _fmri._shape_reference = _shape_ref_scan_nos.value()[0]; _shape_references_set = true; } else { // It is diffusion, so we must check it against shells NEWMAT::Matrix bvalsM; try { // First read bvals file so we know what the shells are bvalsM = MISCMATHS::read_ascii_matrix(_diff._bvals.value()); if (std::min(bvalsM.Nrows(),bvalsM.Ncols()) != 1 || std::max(bvalsM.Nrows(),bvalsM.Ncols()) != imahdr.tsize()) { throw EddyInputError("--bvals should contain a 1xN or Nx1 matrix where N is the number of volumes in --imain"); } } catch (...) { throw EddyInputError("Error when attempting to read --bvals file " + _diff._bvals.value()); } // Now create a vector of DiffPara objects so we can use existing functions if (bvalsM.Nrows() < bvalsM.Ncols()) bvalsM = bvalsM.t(); std::vector dpvec; for (int i=0; i shell_b_values; std::vector shell_indicies; double original_b_range = EddyUtils::GetbRange(); EddyUtils::SetbRange(_diff._brange.value()); // Temporarily set b-range to value we will check later if (!EddyUtils::GetGroups(dpvec,shell_indicies,shell_b_values) && !_diff._data_is_shelled.value()) throw EddyInputError("Data not shelled"); EddyUtils::SetbRange(original_b_range); if (shell_b_values.size() != _shape_ref_scan_nos.value().size()) throw EddyInputError("There must be one index per shell in --shape_ref_scan_nos"); std::vector shells(_shape_ref_scan_nos.value().size()); // The shell index for each of the shape-refs for (unsigned int i=0; i<_shape_ref_scan_nos.value().size(); i++) shells[i] = shell_indicies[_shape_ref_scan_nos.value()[i]]; for (unsigned int i=0; iset_shape_refs(_shape_ref_scan_nos.value(),shells,shell_b_values); } } // Valid interpolation method? if (_interp.value() != string("spline") && _interp.value() != string("trilinear")) throw EddyInputError("Invalid --interp parameter"); // Valid extrapolation method? if (_extrap.value() != string("mirror") && _extrap.value() != string("periodic")) throw EddyInputError("Invalid --extrap parameter"); // Correct extrapolation method for valid extrapolation if (_extrap.value() == string("mirror") && _epvalid.value()) throw EddyInputError("--extrap=mirror cannot be combined with --epvalid"); // Parameters related to outlier detection/replacement // Reasonable values for outlier detection if (_ol_nstd.value() < 1.96) throw EddyInputError("--ol_nstd value too low (below 1.96)"); if (_ol_nvox.value() < 250) throw EddyInputError("--ol_nvox value below 250"); if (IsDiffusion()) _oldef = EDDY::OutlierDefinition(_ol_nstd.value(),_ol_nvox.value(),_diff._ol_pos.value(),_diff._ol_sqr.value()); else throw EddyInputError("Must look into outlier definition for eddy-fmri"); if (_ol_ec.value() != 1 && _ol_ec.value() != 2) throw EddyInputError("--ol_ec value must be 1 or 2"); if (_ol_type.value() != string("sw") && _ol_type.value() != string("gw") && _ol_type.value() != string("both")) throw EddyInputError("--ol_type must be \"sw\", \"gw\" or \"both\""); if ((_ol_type.value() == string("gw") || _ol_type.value() == string("both")) && MultiBand().MBFactor() < 2) throw EddyInputError("--ol_type indicating mb-groups without providing mb structure"); if (_ol_jacut.value() < 0.0) throw EddyInputError("--ol_jacut must be a positive number"); // Parameters related to slice-to-volume motion correction // Make sure that slices only specified in one way (i.e. that only one of --mb, --slspec and --json has been used) if ((_mb.set() + _slspec.set() + _json.set()) > 1) throw EddyInputError("--mb, --slspec and --json mutually exclusive"); // Reasonable values for mb factor and offset if (_mb.value() < 1 || _mb.value() > 10) throw EddyInputError("--mb value outside valid range 1-10"); if (std::abs(_diff._mb_offs.value()) > 1) throw EddyInputError("--mb_offs must be -1, 0 or 1"); // MB offset only makes sense in context of MB if (!_mb.set() && _diff._mb_offs.set()) throw EddyInputError("--mb_offs makes no sense without --mb"); // Check for valid multi-band structure EDDY::MultiBandGroups mbg(imahdr.zsize()); if (_mb.set() || _slspec.set() || _json.set()) { try { mbg = this->MultiBand(); } catch (const std::exception& e) { throw EddyInputError("Failed to decode valid multi-band structure. Exception thrown with message: " + std::string(e.what())); } if (mbg.NSlices() != imahdr.zsize()) throw EddyInputError("Mismatch between no of slices in multi-band structure and in image file."); } // Check that movement model order not greater than number of groups if (*std::max_element(_mp_order.value().begin(),_mp_order.value().end()) > mbg.NGroups()) throw EddyInputError("--mporder can not be greater than number of slices/mb-groups"); // Valid slice-to-vol interpolation method? if (_s2v_interp.value() != string("spline") && _s2v_interp.value() != string("trilinear")) throw EddyInputError("Invalid --s2v_interp parameter"); // Valid and reasonable parameters for slice-to-vol (throws with error message from within constructor) _s2vparam = EDDY::S2VParam(_mp_order.value(),_s2v_lambda.value(),_s2v_fwhm.value(),_s2v_niter.value()); // Parameters related to susceptibility-by-movement interaction // Check for reasonable values pertaining to MBS (Movement By Susceptibility). if (_init_mbs.set() && _init_mbs.value() != std::string("")) { if (_mbs_niter.value() < 0 || _mbs_niter.value() > 50) throw EddyInputError("--mbs_niter value outside valid range 0-50"); } else if (_mbs_niter.value() < 1 || _mbs_niter.value() > 50) throw EddyInputError("--mbs_niter value outside valid range 1-50"); if (_mbs_lambda.value() < 1.0 || _mbs_lambda.value() > 10000.0) throw EddyInputError("--mbs_lambda value outside valid range 1.0-10000.0"); if (_mbs_ksp.value() < 2.0 || _mbs_ksp.value() > 30.0) throw EddyInputError("--mbs_ksp value outside valid range 2.0-30.0"); // Parameters related to the Gaussian process // Valid hypar cost-function? if (_hyparcostfunc.value() != string("MML") && _hyparcostfunc.value() != string("CV") && _hyparcostfunc.value() != string("GPP") && _hyparcostfunc.value() != string("CC")) throw EddyInputError("Invalid --hpcf parameter"); // Reasonable # of voxels for estimation of hyperparameters if (_nvoxhp.value() < 100 || _nvoxhp.value() > 50000) throw EddyInputError("--nvoxhp value outside valid range 100-50000"); _nvoxhp_internal = static_cast(_nvoxhp.value()); // Mimic old behaviour by setting srand to one if --initrand is set without an explicit value if (_initrand.set()) { if (_initrand.value() == 0) _init_rand = 1; else _init_rand = _initrand.value(); } else _init_rand = 0; // Reasonable fudge factor for hyperparameters if (_hyparfudgefactor.value() < 1.0 || _hyparfudgefactor.value() > 10.0) throw EddyInputError("--ff value outside valid range 1.0-10.0"); else _hypar_ff_internal = static_cast(_hyparfudgefactor.value()); // User specified hyperparameters? if (_hypar.value().size()) { _fixed_hpar = true; _hypar_internal = _hypar.value(); } else { _fixed_hpar = false; } // Parameters related to initialisation of movement/distortion parameters // Initialisation file exists if one has been specified if (_init.set() && _init.value() != string("")) { NEWMAT::Matrix tmp = MISCMATHS::read_ascii_matrix(_init.value()); if (tmp.Nrows() == 0) throw EddyInputError("Error when attempting to read --init file " + _init.value()); } // Initialisation for s2v not set if EC/volumetric initialisation not set if (_init_s2v.set() && _init_s2v.value() != string("") && (!_init.set() || _init.value() == string(""))) { throw EddyInputError("--init_s2v can not be used without --init"); } // Check that s2v initialisation file exists if one has been specified if (_init_s2v.set() && _init_s2v.value() != string("")) { NEWMAT::Matrix tmp = MISCMATHS::read_ascii_matrix(_init_s2v.value()); if (tmp.Nrows() == 0) throw EddyInputError("Error when attempting to read --init_s2v file " + _init_s2v.value()); } // Check that we are not to run any vol-to-vol iterations if s2v initialisation is set if (_init_s2v.set() && _init_s2v.value() != string("") && _niter != 0) { throw EddyInputError("--init_s2v does not make sense unless --niter=0"); } // Movement-by-susceptibility file exists and has correct dimensions if (_init_mbs.set() && _init_mbs.value() != string("")) { NEWIMAGE::volume4D mbshdr; try { NEWIMAGE::read_volume_hdr_only(mbshdr,_init_mbs.value()); } // Throws if there is a problem catch(...) { throw EddyInputError("Error when attempting to read --init_mbs file " + _init_mbs.value()); } if (!samesize(imahdr[0],mbshdr,3,true)) throw EddyInputError("--imain and --init_mbs images must have the same dimensions"); if (mbshdr.tsize() != int(this->MoveBySuscParam().size())) throw EddyInputError("--init_mbs has wrong number of volumes"); } // Parameters related to the writing of various "extra" output // Check that --with_outliers hasn't been set unless --repol is also set if (_with_outliers.value() && !_rep_ol.value()) throw EddyInputError("--with_outliers does not make sense without also specifying --repol"); // Parameters related to things that can be useful for debugging/development // Make sure that debug requests are reasonable if (_debug_tmp.value() < 0 || _debug_tmp.value() > 4) { throw EddyInputError("--debug must be a value between 0 and 4"); } _debug = static_cast(_debug_tmp.value()); if (_debug > 0 && (!_dbg_indx_str.set() || _dbg_indx_str.value() == string(""))) { _dbg_indx = DebugIndexClass(0,imahdr.tsize()-1); } else if (_dbg_indx_str.set() && _dbg_indx_str.value() != string("")) { _dbg_indx = DebugIndexClass(_dbg_indx_str.value()); } if (_dbg_indx.Max() > static_cast(imahdr.tsize()-1)) { throw EddyInputError("--dbg_indx out of range"); } // If debug is set we should also set initialisation of rand if (_debug > 0) _init_rand = true; // Make sure we don't attempt to use multiple CPU threads for GPU version #ifdef COMPILE_GPU if (_nthr.value() > 1) { throw EddyInputError("The version compiled for GPU can only use 1 CPU thread (i.e. --nthr=1)"); } #endif // Make sure we don't try to use more CPU threads than the hardware supports if (_nthr.value() > std::thread::hardware_concurrency()) { if (std::thread::hardware_concurrency()) { // This means that if hardware_concurrency isn't implemented we trust the user. _nthr.set_T(std::thread::hardware_concurrency()); } } else if (_nthr.value() < 1) _nthr.set_T(1); // Finally some generally helpful parameters // If very_verbose is set we should also set verbose if (_very_verbose.value() && !_verbose.value()) _verbose.set_T(true); // The session parameter is mandatory for fMRI, but not for diffusion. // That is why there are two different fields for it. NEWMAT::Matrix sessM; // Check for valid session file if (IsfMRI() || _diff._session.set()) { // There is/should be a session file try { if (IsDiffusion()) sessM = MISCMATHS::read_ascii_matrix(_diff._session.value()); else sessM = MISCMATHS::read_ascii_matrix(_fmri._session.value()); if (std::min(sessM.Nrows(),sessM.Ncols()) != 1 || std::max(sessM.Nrows(),sessM.Ncols()) != imahdr.tsize()) { throw EddyInputError("--session must be an 1xN or Nx1 matrix where N is the number of volumes in --imain"); } } catch (...) { if (IsDiffusion()) throw EddyInputError("Error when attempting to read --session file " + _diff._session.value()); else throw EddyInputError("Error when attempting to read --session file " + _fmri._session.value()); } if (!session_indicies_kosher(sessM)) throw EddyInputError("Values in --session file seems dodgy"); } else { // There is no (and should not be) a session file. _sessvec.resize(imahdr.tsize(),1); // Set single session } // This ends the checking of common parameters // Next, check on diffusion parameters. if (IsDiffusion()) { // First mandatory parameters // bvals and bvecs exists and are consistent with image file try { NEWMAT::Matrix bvecsM = MISCMATHS::read_ascii_matrix(_diff._bvecs.value()); if (std::min(bvecsM.Nrows(),bvecsM.Ncols()) != 3 || std::max(bvecsM.Nrows(),bvecsM.Ncols()) != imahdr.tsize()) { throw EddyInputError("--bvecs should contain a 3xN or Nx3 matrix where N is the number of volumes in --imain"); } } catch (...) { throw EddyInputError("Error when attempting to read --bvecs file " + _diff._bvecs.value()); } try { NEWMAT::Matrix bvalsM = MISCMATHS::read_ascii_matrix(_diff._bvals.value()); if (std::min(bvalsM.Nrows(),bvalsM.Ncols()) != 1 || std::max(bvalsM.Nrows(),bvalsM.Ncols()) != imahdr.tsize()) { throw EddyInputError("--bvals should contain a 1xN or Nx1 matrix where N is the number of volumes in --imain"); } } catch (...) { throw EddyInputError("Error when attempting to read --bvals file " + _diff._bvals.value()); } if (_diff._bdeltas.value() != std::string("")) { try { NEWMAT::Matrix bdeltasM = MISCMATHS::read_ascii_matrix(_diff._bdeltas.value()); if (bdeltasM.Nrows()>bdeltasM.Ncols()) bdeltasM = bdeltasM.t(); if (bdeltasM.Nrows()!=1 || bdeltasM.Ncols()!=imahdr.tsize()) { throw EddyInputError("--bdeltas should contain a 1xN or Nx1 matrix where N is the number of volumes in --imain"); } for (int i=0; i1e-4 && std::fabs(bdeltasM(1,i+1)+0.5)>1e-4 && std::fabs(bdeltasM(1,i+1))>1e-4) { throw EddyInputError("--bdeltas should only contain values 1.0, -0.5 or 0.0"); } } } catch (...) { throw EddyInputError("Error when attempting to read --bdeltas file " + _diff._bdeltas.value()); } } // Paramaters related to the general running of vanilla eddy // Valid range of b-values within a single shell if (_diff._brange.value() < 0.0 || _diff._brange.value() > 200.0) throw EddyInputError("Invalid --brange parameter"); // Valid final resampling method? if (_diff._resamp.value() != string("jac") && _diff._resamp.value() != string("jac_nn") && _diff._resamp.value() != string("lsr")) { throw EddyInputError("Invalid --resamp parameter"); } // Check that output masking is not off for LSR resampling if (_diff._resamp.value() == string("lsr") && _dont_mask_output.value()) throw EddyInputError("You cannot combine --resamp=lsr with --dont_mask_output"); // Reasonable LSR regularisation weighting if (_diff._lsr_lambda.value() < 0.0 || _diff._lsr_lambda.value() > 1.0) throw EddyInputError("--lsr_lambda value outside valid range"); // Parameters related to eddy current modelling // Valid first level model? if (_diff._flm.value() != string("movement") && _diff._flm.value() != string("linear") && _diff._flm.value() != string("quadratic") && _diff._flm.value() != string("cubic")) throw EddyInputError("Invalid --flm parameter"); // Valid second level model? if (_diff._slm_str.value() == string("none")) _diff._slm = EDDY::SecondLevelECModelType::None; else if (_diff._slm_str.value() == string("linear")) _diff._slm = EDDY::SecondLevelECModelType::Linear; else if (_diff._slm_str.value() == string("quadratic")) _diff._slm = EDDY::SecondLevelECModelType::Quadratic; else throw EddyInputError("Invalid --slm parameter"); // Valid first level b0-model? if (_diff._b0_flm.value() != string("movement") && _diff._b0_flm.value() != string("linear") && _diff._b0_flm.value() != string("quadratic")) throw EddyInputError("Invalid --b0_flm parameter"); // Valid second level b0 model? if (_diff._b0_slm_str.value() == string("none")) _diff._b0_slm = EDDY::SecondLevelECModelType::None; else if (_diff._b0_slm_str.value() == string("linear")) _diff._b0_slm = EDDY::SecondLevelECModelType::Linear; else if (_diff._b0_slm_str.value() == string("quadratic")) _diff._b0_slm = EDDY::SecondLevelECModelType::Quadratic; else throw EddyInputError("Invalid --b0_slm parameter"); // Valid model for long (time varying) EC? if (_diff._long_ec_str.value() == string("none")) _diff._long_ec_model = EDDY::LongECModelType::None; else if (_diff._long_ec_str.value() == string("weights")) _diff._long_ec_model = EDDY::LongECModelType::Individual; else if (_diff._long_ec_str.value() == string("tc")) _diff._long_ec_model = EDDY::LongECModelType::IndividualTimeConstant; else if (_diff._long_ec_str.value() == string("jointweights")) _diff._long_ec_model = EDDY::LongECModelType::Joint; else if (_diff._long_ec_str.value() == string("jointtc")) _diff._long_ec_model = EDDY::LongECModelType::JointTimeConstant; else throw EddyInputError("Invalid --lecm parameter"); if (_diff._long_ec_model != EDDY::LongECModelType::None && mbg.MBFactor() < 2) { throw EddyInputError("--lecm can only be used for multi-band data"); } if (_diff._long_ec_str.value() == string("none") && _diff._long_ec_dont_reest.value() == true) { throw EddyInputError("--lecm_dont_reest only makes sense together with --lecm"); } if (_diff._long_ec_str.value() == string("none") && _diff._long_ec_sep_offs_move.value() == true) { throw EddyInputError("--lecm_sep_offs_move only makes sense together with --lecm"); } // Parameters related to outlier detection/replacement if (_diff._ol_ss.value() != string("sw") && _diff._ol_ss.value() != string("pooled")) throw EddyInputError("--ol_ss must be \"sw\" (shell-wise) or \"pooled\""); // Parameters related to the Gaussian process // Valid covariance function? if (_diff._covfunc.value() != string("spheri") && _diff._covfunc.value() != string("expo") && _diff._covfunc.value() != string("old")) throw EddyInputError("Invalid --covfunc parameter"); // Miscallenous parameters related to missing planes/output masking/bvec-rotation // Set "internal" copy of _rbvde to allow us to set/reset internally _diff._rbvde_internal = _diff._rbvde.value(); // Parameters related to "tricky" movements for diffusion // Valid offset model? if (_diff._offset_model.value() != string("linear") && _diff._offset_model.value() != string("linear_lag") && _diff._offset_model.value() != string("quadratic") && _diff._offset_model.value() != string("linear_lag_quadratic") && _diff._offset_model.value() != string("linear_lag_quadratic_lag")) throw EddyInputError("Invalid --offset_model parameter"); // Set internal parameter for "dont_sep_offs_move" _diff._dont_sep_offs_move_internal = _diff._dont_sep_offs_move.value(); // Check that peas options are compatible if (_diff._dont_peas.value() && _diff._use_b0s_for_peas.value()) throw EddyInputError("--dont_peas and --b0_peas cannot both be set"); // Parameters related to writing of extra output // Check that --write_scatter_brain_predictions hasn't been specified for volume-to-volume registration if (_diff._write_scatter_brain_predictions.value() && !IsSliceToVol()) throw EddyInputError("--write_scatter_brain_predictions does not make sense without also specifying --mporder > 0"); // Check that --write_scatter_brain_predictions hasn't been specified without --write_predictions if (_diff._write_scatter_brain_predictions.value() && !_write_predictions.value()) throw EddyInputError("--write_scatter_brain_predictions can only be used together with --write_predictions"); // Parameters related to things that can be useful for debugging/development // Make sure the "only" flags are mutually exclusive _diff._rb0 = true; _diff._rdwi = true; if (_diff._dwi_only.value() && _diff._b0_only.value()) throw EddyInputError("--dwi_only and --b0_only cannot both be set"); if (_diff._dwi_only.value()) _diff._rb0 = false; else if (_diff._b0_only.value()) _diff._rdwi = false; // Make sure that test_rot vector is valid if set if (_diff._test_rot.set()) { if (_diff._test_rot.value().size() < 1 || _diff._test_rot.value().size() > 3) throw EddyInputError("--test_rot must be one, two or three angles"); } } // Here ends the checking of diffusion parameters else if (IsfMRI()) { // Here starts checking of parameters specific for fMRI // Check for reasonable repetition time if (_fmri._rep_time.value() < 0.5 || _fmri._rep_time.value() > 10.0) throw EddyInputError("--rep_time must be between 0.5 and 10 seconds"); // Check for reasonable echo time if (_fmri._echo_time.set() && (_fmri._echo_time.value() < 10.0 || _fmri._echo_time.value() > 100.0)) throw EddyInputError("--echo_time must be between 10 and 100 milliseconds"); } } catch (const EddyInputError& e) { cout << e.what() << endl; cout << "Terminating program" << endl; exit(EXIT_FAILURE); } EddyCatch EDDY::FinalResamplingType EddyCommandLineOptions::ResamplingMethod() const EddyTry { if (IsfMRI() || _diff._resamp.value() == std::string("jac")) return(FinalResamplingType::Jac); else if (_diff._resamp.value() == std::string("jac_nn")) return(FinalResamplingType::Jac_NN); else if (_diff._resamp.value() == std::string("lsr")) return(FinalResamplingType::LSR); else return(FinalResamplingType::Unknown); } EddyCatch EDDY::CovarianceFunctionType EddyCommandLineOptions::CovarianceFunction() const EddyTry { if (IsDiffusion()) { if (_diff._covfunc.value() == std::string("spheri")) return(EDDY::CovarianceFunctionType::NewSpherical); else if (_diff._covfunc.value() == std::string("expo")) return(EDDY::CovarianceFunctionType::Exponential); else if (_diff._covfunc.value() == std::string("old")) return(EDDY::CovarianceFunctionType::Spherical); else return(EDDY::CovarianceFunctionType::Unknown); } else if (IsfMRI()) { if (_fmri._covfunc.value() == std::string("sqexp")) return(EDDY::CovarianceFunctionType::SquaredExponential); else return(EDDY::CovarianceFunctionType::Unknown); } else return(EDDY::CovarianceFunctionType::Unknown); } EddyCatch EDDY::HyParCostFunctionType EddyCommandLineOptions::HyParCostFunction() const EddyTry { if (_hyparcostfunc.value() == std::string("MML")) return(EDDY::HyParCostFunctionType::MML); else if (_hyparcostfunc.value() == std::string("CV")) return(EDDY::HyParCostFunctionType::CV); else if (_hyparcostfunc.value() == std::string("GPP")) return(EDDY::HyParCostFunctionType::GPP); else if (_hyparcostfunc.value() == std::string("CC")) return(EDDY::HyParCostFunctionType::CC); else return(EDDY::HyParCostFunctionType::Unknown); } EddyCatch float EddyCommandLineOptions::FWHM(unsigned int iter) const EddyTry { if (_fwhm.size()==1) return(_fwhm[0]); else if (iter >= _fwhm.size()) throw EddyException("EddyCommandLineOptions::FWHM: iter out of range"); return(_fwhm[iter]); } EddyCatch void EddyCommandLineOptions::SetNIterAndFWHM(unsigned int niter, const std::vector& fwhm) EddyTry { if (fwhm.size() != 1 && fwhm.size() != niter) throw EddyException("EddyCommandLineOptions::SetNIterAndFWHM: mismatch between niter and fwhm"); _niter = niter; if (fwhm.size() == niter) _fwhm = fwhm; else _fwhm = std::vector(niter,fwhm[0]); } EddyCatch void EddyCommandLineOptions::SetS2VParam(unsigned int order, float lambda, float fwhm, unsigned int niter) EddyTry { std::vector lorder(1,static_cast(order)); std::vector llambda(1,lambda); std::vector lfwhm(1,fwhm); std::vector lniter(1,static_cast(niter)); _s2vparam = EDDY::S2VParam(lorder,llambda,lfwhm,lniter); } EddyCatch unsigned int S2VParam::NIter(unsigned int oi) const EddyTry { if (oi >= _niter.size()) throw EddyException("S2VParam::NIter: oi out of range"); return(static_cast(_niter[oi])); } EddyCatch unsigned int S2VParam::NOrder() const EddyTry { if (_order.size() > 1) return(_order.size()); else return(static_cast(_order[0] > 0 ? 1 : 0)); } EddyCatch unsigned int S2VParam::Order(unsigned int oi) const EddyTry { if (oi >= _order.size()) throw EddyException("S2VParam::Order: oi out of range"); return(static_cast(_order[oi])); } EddyCatch std::vector S2VParam::Order() const EddyTry { std::vector rval(_order.size()); for (unsigned int i=0; i<_order.size(); i++) rval[i] = static_cast(_order[i]); return(rval); } EddyCatch double S2VParam::Lambda(unsigned int oi) const EddyTry { if (oi >= _lambda.size()) throw EddyException("S2VParam::Lambda: oi out of range"); return(static_cast(_lambda[oi])); } EddyCatch std::vector S2VParam::Lambda() const EddyTry { std::vector rval(_lambda.size()); for (unsigned int i=0; i<_lambda.size(); i++) rval[i] = static_cast(_lambda[i]); return(rval); } EddyCatch float S2VParam::FWHM(unsigned int oi, unsigned int iter) const EddyTry { if (oi >= this->NOrder()) throw EddyException("S2VParam::FWHM: oi out of range"); if (iter >= this->NIter(oi)) throw EddyException("S2VParam::FWHM: iter out of range"); if (_fwhm.size()==1) return(_fwhm[0]); else { unsigned int indx=0; for (unsigned int i=0; iNIter(i); return(_fwhm[indx+iter]); } } EddyCatch std::vector S2VParam::FWHM(unsigned int oi) const EddyTry { if (oi >= NOrder()) throw EddyException("S2VParam::FWHM: oi out of range"); if (_fwhm.size()==1) { std::vector rval(this->NIter(oi),_fwhm[0]); return(rval); } else { unsigned int indx=0; for (unsigned int i=0; iNIter(i); std::vector rval(this->NIter(oi),0.0); for (unsigned int i=0; iNIter(oi); i++) rval[i] = _fwhm[indx+i]; return(rval); } } EddyCatch EDDY::MultiBandGroups EddyCommandLineOptions::MultiBand() const EddyTry { NEWIMAGE::volume mhdr; NEWIMAGE::read_volume_hdr_only(mhdr,_mask.value()); if (_slspec.set()) { EDDY::MultiBandGroups mbg(_slspec.value()); return(mbg); } else if (_json.set()) { EDDY::JsonReader jr(_json.value()); EDDY:: MultiBandGroups mbg(jr.SliceOrdering()); return(mbg); } else { EDDY::MultiBandGroups mbg(mhdr.zsize(),static_cast(_mb.value()),static_cast(_diff._mb_offs.value())); if (IsDiffusion()) { if (_diff._slorder.set() && _diff._slorder.value() != string("")) mbg.SetTemporalOrder(get_slorder(_diff._slorder.value(),mbg.NGroups())); } return(mbg); } } EddyCatch EDDY::ECModelType EddyCommandLineOptions::FirstLevelModel() const EddyTry { if (IsDiffusion()) { if (_diff._flm.value() == string("movement")) return(EDDY::ECModelType::NoEC); else if (_diff._flm.value() == string("linear")) return(EDDY::ECModelType::Linear); else if (_diff._flm.value() == string("quadratic")) return(EDDY::ECModelType::Quadratic); else if (_diff._flm.value() == string("cubic")) return(EDDY::ECModelType::Cubic); return(EDDY::ECModelType::Unknown); } return(EDDY::ECModelType::NoEC); // No EC for fMRI } EddyCatch EDDY::ECModelType EddyCommandLineOptions::b0_FirstLevelModel() const EddyTry { if (IsDiffusion()) { if (_diff._b0_flm.value() == string("movement")) return(EDDY::ECModelType::NoEC); else if (_diff._b0_flm.value() == string("linear")) return(EDDY::ECModelType::Linear); else if (_diff._b0_flm.value() == string("quadratic")) return(EDDY::ECModelType::Quadratic); return(EDDY::ECModelType::Unknown); } return(EDDY::ECModelType::NoEC); // No EC for fMRI } EddyCatch std::string EddyCommandLineOptions::LongECModelString() const EddyTry { LongECModelType lmt = this->LongECModel(); switch (lmt) { case LongECModelType::None: return("No modelling of long EC"); case LongECModelType::Individual: return("Modelling weight for each MB-group of each volume separately."); case LongECModelType::Joint: return("Modelling weight for each MB-group pooled across all volumes."); case LongECModelType::IndividualTimeConstant: return("Modelling a separate time constant for each volume."); case LongECModelType::JointTimeConstant: return("Modelling a single time constant across all volumes."); default: throw EddyException("EddyCommandLineOptions::LongECModelString: Unknown model type."); } } EddyCatch enum class OffsetModelType { Linear, LinearPlusLag, Quadratic, QuadraticPlusLinearLag, QuadraticPlusLag, Unknown }; EDDY::OffsetModelType EddyCommandLineOptions::OffsetModel() const EddyTry { if (IsDiffusion()) { if (_diff._offset_model.value() == string("linear")) return(EDDY::OffsetModelType::Linear); else if (_diff._offset_model.value() == string("linear_lag")) return(EDDY::OffsetModelType::LinearPlusLag); else if (_diff._offset_model.value() == string("quadratic")) return(EDDY::OffsetModelType::Quadratic); else if (_diff._offset_model.value() == string("linear_lag_quadratic")) return(EDDY::OffsetModelType::QuadraticPlusLinearLag); else if (_diff._offset_model.value() == string("linear_lag_quadratic_lag")) return(EDDY::OffsetModelType::QuadraticPlusLag); return(EDDY::OffsetModelType::Unknown); } return(EDDY::OffsetModelType::Unknown); } EddyCatch EDDY::OLSumStats EddyCommandLineOptions::OLSummaryStatistics() const EddyTry { if (IsDiffusion()) { if (_diff._ol_ss.value()==std::string("sw")) return(OLSumStats::ShellWise); else if (_ol_type.value()==std::string("pooled")) return(OLSumStats::Pooled); else return(OLSumStats::Unknown); } else return(OLSumStats::Unknown); } EddyCatch NEWMAT::ColumnVector EddyCommandLineOptions::HyperParValues() const EddyTry { NEWMAT::ColumnVector rval(_hypar_internal.size()); for (unsigned int i=0; i<_hypar_internal.size(); i++) rval(i+1) = _hypar_internal[i]; return(rval); } EddyCatch void EddyCommandLineOptions::SetHyperParValues(const NEWMAT::ColumnVector& p) EddyTry { _hypar_internal.resize(p.Nrows()); for (unsigned int i=0; i<_hypar_internal.size(); i++) _hypar_internal[i] = p(i+1); this->SetHyperParFixed(true); return; } EddyCatch std::vector EddyCommandLineOptions::TestRotAngles() const EddyTry { std::vector rot = _diff._test_rot.value(); if (rot.size() < 3) rot.resize(3,0.0); return(rot); } EddyCatch std::pair EddyCommandLineOptions::ShellShapeReference(unsigned int i) const EddyTry { if (IsDiffusion()) { if (i >= _diff._shell_shape_references.size()) throw EddyException("EddyCommandLineOptions::ShellShapeReference: Index out of bounds"); return(std::pair(_diff._shell_shape_references[i]._indx,_diff._shell_shape_references[i]._b_val)); } return(std::pair(-1,-999.0)); // Not difusion } EddyCatch NEWIMAGE::interpolation EddyCommandLineOptions::InterpolationMethod() const EddyTry { if (_interp.value() == string("spline")) return(NEWIMAGE::spline); else if (_interp.value() == string("trilinear")) return(NEWIMAGE::trilinear); else throw EddyException("EddyCommandLineOptions::InterpolationMethod: Invalid interpolation option."); } EddyCatch NEWIMAGE::extrapolation EddyCommandLineOptions::ExtrapolationMethod() const EddyTry { if (_extrap.value() == string("periodic")) return(NEWIMAGE::periodic); else if (_extrap.value() == string("mirror")) return(NEWIMAGE::mirror); else throw EddyException("EddyCommandLineOptions::ExtrapolationMethod: Invalid extrapolation option."); } EddyCatch NEWIMAGE::interpolation EddyCommandLineOptions::S2VInterpolationMethod() const EddyTry { if (_s2v_interp.value() == string("spline")) return(NEWIMAGE::spline); else if (_s2v_interp.value() == string("trilinear")) return(NEWIMAGE::trilinear); else throw EddyException("EddyCommandLineOptions::S2VInterpolationMethod: Invalid interpolation option."); } EddyCatch DebugIndexClass::DebugIndexClass(const std::string& in) EddyTry { _indx = parse_commaseparated_numbers(in); } EddyCatch std::vector DebugIndexClass::parse_commaseparated_numbers(const std::string& list) const EddyTry { std::vector str_list = parse_commaseparated_list(list); std::vector number_list(str_list.size(),0); for (unsigned int i=0; i DebugIndexClass::parse_commaseparated_list(const std::string& list) const EddyTry { std::vector ostr; size_t cur_pos = 0; size_t new_pos = 0; unsigned int n=0; while ((new_pos = list.find_first_of(',',cur_pos)) != string::npos) { ostr.resize(++n); ostr[n-1] = list.substr(cur_pos,new_pos-cur_pos); cur_pos = new_pos+1; } ostr.resize(++n); ostr[n-1] = list.substr(cur_pos,string::npos); return(ostr); } EddyCatch S2VParam::S2VParam(const std::vector& order, const std::vector& lambda, const std::vector& fwhm, const std::vector& niter) EddyTry : _order(order), _lambda(lambda), _fwhm(fwhm), _niter(niter) { if (_order.size() != _lambda.size()) throw EddyException("Size of --s2v_lambda must match size of --mporder"); if (_order.size() != _niter.size()) throw EddyException("Size of --s2v_niter must match size of --mporder"); if (_fwhm.size() != 1 && _fwhm.size() != this->total_niter()) throw EddyException("--s2v_fwhm value must be given once or once per iteration"); if (this->total_niter() > 100) throw EddyException("You have asked for more than 100 slice-to-volume iterations. Seriously?"); // Reasonable slice-to-vol FWHM for (unsigned int i=0; i<_fwhm.size(); i++) { if (_fwhm[i] < 0.0 || _fwhm[i] > 20.0) throw EddyException("--s2v_fwhm value outside valid range 0-20mm"); } } EddyCatch void EddyCommandLineOptions::do_initial_parsing(int argc, char *argv[]) EddyTry { Utilities::OptionParser options(_title,_examples); try { // First mandatory parameters options.add(_imain); options.add(_mask); options.add(_acqp); options.add(_index); options.add(_out); if (IsDiffusion()) { options.add(_diff._bvecs); options.add(_diff._bvals); options.add(_diff._bdeltas); // Not mandatory, but groups naturally with bvals and bvecs options.add(_diff._data_is_shelled); // Not mandatory, but groups naturally with bvals and bvecs } if (IsfMRI()) { options.add(_fmri._session); options.add(_fmri._rep_time); options.add(_fmri._echo_time); // Not mandatory, but groups naturally with repetition time } // Parameters related to input susceptibility field options.add(_topup); options.add(_field); options.add(_field_mat); // Paramaters related to the general running of vanilla eddy options.add(_niter_tmp); options.add(_fwhm_tmp); options.add(_ref_scan_no); options.add(_interp); options.add(_extrap); options.add(_epvalid); if (IsDiffusion()) { options.add(_diff._resamp); options.add(_diff._lsr_lambda); options.add(_diff._brange); } // Parameters related to eddy current modelling if (IsDiffusion()) { options.add(_diff._flm); options.add(_diff._slm_str); options.add(_diff._b0_flm); options.add(_diff._b0_slm_str); options.add(_diff._rep_time); options.add(_diff._long_ec_str); options.add(_diff._long_ec_dont_reest); options.add(_diff._long_ec_sep_offs_move); } // Parameters related to outlier detection/replacement options.add(_rep_ol); if (IsDiffusion()) options.add(_diff._rep_noise); options.add(_ol_nstd); options.add(_ol_nvox); options.add(_ol_ec); options.add(_ol_type); options.add(_ol_jacut); if (IsDiffusion()) { options.add(_diff._ol_ss); options.add(_diff._ol_pos); options.add(_diff._ol_sqr); } // Parameters related to slice-to-volume motion correction options.add(_mb); options.add(_slspec); options.add(_json); options.add(_mp_order); options.add(_s2v_lambda); options.add(_s2v_niter); options.add(_s2v_fwhm); options.add(_s2v_interp); options.add(_shape_ref_scan_nos); // Parameters related to susceptibility-by-movement interaction estimation options.add(_estimate_mbs); options.add(_mbs_niter); options.add(_mbs_lambda); options.add(_mbs_ksp); if (IsfMRI()) options.add(_fmri._fast_map); // Parameters related to the Gaussian process if (IsDiffusion()) options.add(_diff._covfunc); else if (IsfMRI()) options.add(_fmri._covfunc); options.add(_hyparcostfunc); options.add(_nvoxhp); options.add(_initrand); options.add(_hyparfudgefactor); options.add(_hypar); // Miscallenous parameters related to missing planes/output masking/bvec-rotation options.add(_fep); options.add(_dont_mask_output); options.add(_diff._rbvde); // Parameters related to initialisation of movement/distortion parameters options.add(_init); options.add(_init_s2v); options.add(_init_mbs); // Parameters related to "tricky" movements for diffusion if (IsDiffusion()) { options.add(_diff._dont_sep_offs_move); options.add(_diff._offset_model); options.add(_diff._dont_peas); options.add(_diff._use_b0s_for_peas); options.add(_diff._session); } // Parameters related to the writing of various "extra" output if (IsDiffusion()) { options.add(_diff._fields); options.add(_diff._write_cnr_maps); options.add(_diff._write_range_cnr_maps); options.add(_diff._write_scatter_brain_predictions); } if (IsfMRI()) options.add(_fmri._write_snr_maps); options.add(_dfields); options.add(_residuals); options.add(_with_outliers); options.add(_history); options.add(_write_slice_stats); options.add(_write_predictions); options.add(_no_text_files); // Parameters related to things that can be useful for debugging/development if (IsDiffusion()) { options.add(_diff._dwi_only); options.add(_diff._b0_only); options.add(_diff._test_rot); options.add(_diff._print_mi_values); options.add(_diff._print_mi_planes); } options.add(_debug_tmp); options.add(_dbg_indx_str); options.add(_log_timings); options.add(_nthr); // Finally some generally helpful parameters options.add(_verbose); options.add(_very_verbose); options.add(_help); if (IsDiffusion()) { // Here we add all the deprecated parameters from the "old" diffusion eddy options.add(_diff._sep_offs_move); options.add(_diff._peas); options.add(_diff._rms); options.add(_diff._mb_offs); options.add(_diff._slorder); } // Copies of argc and argv char **cargv = argv; int cargc = argc; // Remove fmri/diffusion tags if they are there if (argc > 1) { std::string tag(argv[1]); std::for_each(tag.begin(),tag.end(),[](char& c){c = std::tolower(c);}); if (tag==std::string("fmri") || tag==std::string("diffusion")) { cargc = argc-1; cargv = new char*[cargc]; cargv[0] = new char[strlen(argv[0])+1]; std::strcpy(cargv[0],argv[0]); for (int i=1; i indx.Nrows()) indx = indx.t(); _indvec.resize(indx.Nrows()); unsigned int max_indx = static_cast(indx(1,1)); for (int i=0; i(indx(i+1,1)); if (fabs(static_cast(_indvec[i])-indx(i+1,1)) > 1e-6) return(false); max_indx = std::max(max_indx,_indvec[i]); } if (max_indx > static_cast(acqp.Nrows())) return(false); else return(true); } EddyCatch // Makes sure that the session indicies makes sense and stores them // in _sessvec. N.B. It is _not_ a mistake that the matrix is passed // by value. bool EddyCommandLineOptions::session_indicies_kosher(NEWMAT::Matrix indx) EddyTry { if (indx.Ncols() > indx.Nrows()) indx = indx.t(); _sessvec.resize(indx.Nrows()); std::vector bins; for (int i=0; i(indx(i+1,1)); _sessvec[i] = ii; if (bins.size() < ii) bins.resize(ii,0); bins[ii-1]++; } // Checks that all sessions have a "reasonable" number of volumes and // that they are not too unevenly distributed. unsigned int maxbin = 0; unsigned int minbin = 1e6; double meanbin = 0.0; for (int i=0; i(bins[i]); } meanbin /= static_cast(bins.size()); if (minbin==0 || maxbin > 3*minbin || maxbin > 6*meanbin) return(false); return(true); } EddyCatch /****************************************************************//** * * \brief Routine for sorting shape-reference indicies * * Sorts the indices in 'shape_refs' using the shell indicies * in 'shells' as a key. * ********************************************************************/ void EddyCommandLineOptions::set_shape_refs(const std::vector& shape_refs, const std::vector& shells, const std::vector shell_b_vals) EddyTry { struct shape_ref_shell_pair { shape_ref_shell_pair(int ri, int si) : _ri(ri), _si(si) {} int _ri; // Index (into vector of all scans) of refs int _si; // Index of shells }; std::vector ri_si_v; for (unsigned int i=0; i EddyCommandLineOptions::get_slorder(const std::string& fname, unsigned int ngrp) const EddyTry { std::vector rval; try { NEWMAT::Matrix tmp = MISCMATHS::read_ascii_matrix(fname); if (tmp.Ncols() > tmp.Nrows()) tmp = tmp.t(); int n = tmp.Nrows(); int one = tmp.Ncols(); rval.resize(n); if (n != int(ngrp) || one != 1) throw EddyException("Size mismatch between --imain and --slorder file"); for (int i=0; ingrp-1) throw EddyException("--slorder file contains invalid entry"); rval[i] = tmp(i+1,1); } } catch(...) { throw EddyException("Error when attempting to read --slorder file " + fname); } return(rval); } EddyCatch