17#ifndef __phaseencoding_h__
18#define __phaseencoding_h__
33 namespace PhaseEncoding
45 template <
class MatrixType>
46 void check (
const MatrixType& PE)
49 throw Exception (
"No valid phase encoding table found");
52 throw Exception (
"Phase-encoding matrix must have at least 3 columns");
54 for (ssize_t row = 0; row != PE.rows(); ++row) {
57 throw Exception (
"Phase-encoding matrix contains non-integral axis designation");
65 template <
class MatrixType,
class HeaderType>
66 void check (
const MatrixType& PE,
const HeaderType& header)
69 const ssize_t num_volumes = (header.ndim() > 3) ? header.size (3) : 1;
70 if (num_volumes != PE.rows())
71 throw Exception (
"Number of volumes in image \"" + header.name() +
"\" (" +
str(num_volumes) +
") does not match that in phase encoding table (" +
str(PE.rows()) +
")");
89 template <
class MatrixType>
92 auto erase = [&] (
const std::string& s) {
auto it = header.keyval().find (s);
if (it != header.keyval().end()) header.keyval().erase (it); };
95 erase (
"PhaseEncodingDirection");
96 erase (
"TotalReadoutTime");
100 std::string pe_scheme;
101 std::string first_line;
102 bool variation =
false;
103 for (ssize_t row = 0; row < PE.rows(); ++row) {
104 std::string line =
str(PE(row,0));
105 for (ssize_t col = 1; col < PE.cols(); ++col)
106 line +=
"," +
str(PE(row,col), 3);
108 if (first_line.empty())
110 else if (line != first_line)
114 header.keyval()[
"pe_scheme"] = pe_scheme;
115 erase (
"PhaseEncodingDirection");
116 erase (
"TotalReadoutTime");
119 const Eigen::Vector3d dir { PE(0, 0), PE(0, 1), PE(0, 2) };
120 header.keyval()[
"PhaseEncodingDirection"] =
Axes::dir2id (dir);
122 header.keyval()[
"TotalReadoutTime"] =
str(PE(0, 3), 3);
124 erase (
"TotalReadoutTime");
159 template <
class MatrixType>
160 void scheme2eddy (
const MatrixType& PE, Eigen::MatrixXd& config, Eigen::Array<int, Eigen::Dynamic, 1>& indices)
165 throw Exception (
e,
"Cannot convert phase-encoding scheme to eddy format");
168 throw Exception (
"Phase-encoding matrix requires 4 columns to convert to eddy format");
169 config.resize (0, 0);
170 indices = Eigen::Array<int, Eigen::Dynamic, 1>::Constant (PE.rows(), PE.rows());
171 for (ssize_t PE_row = 0; PE_row != PE.rows(); ++PE_row) {
172 for (ssize_t config_row = 0; config_row != config.rows(); ++config_row) {
173 bool dir_match = PE.template block<1,3>(PE_row, 0).isApprox (config.block<1,3>(config_row, 0));
174 bool time_match =
abs (PE(PE_row, 3) - config(config_row, 3)) < 1
e-3;
175 if (dir_match && time_match) {
177 indices[PE_row] = config_row + 1;
181 if (indices[PE_row] == PE.rows()) {
183 config.conservativeResize (config.rows()+1, 4);
184 config.row(config.rows()-1) = PE.row(PE_row);
185 indices[PE_row] = config.rows();
191 Eigen::MatrixXd
eddy2scheme (
const Eigen::MatrixXd&,
const Eigen::Array<int, Eigen::Dynamic, 1>&);
198 template <
class MatrixType,
class HeaderType>
201 std::array<size_t, 3> perm;
202 std::array<bool, 3> flip;
203 H.realignment (perm, flip);
204 if (perm[0] == 0 && perm[1] == 1 && perm[2] == 2 && !flip[0] && !flip[1] && !flip[2]) {
205 INFO (
"No transformation of external phase encoding data required to accompany image \"" +
H.name() +
"\"");
208 Eigen::MatrixXd result (pe_scheme.rows(), pe_scheme.cols());
209 for (ssize_t row = 0; row != pe_scheme.rows(); ++row) {
210 Eigen::VectorXd new_line = pe_scheme.row (row);
212 new_line[
axis] = pe_scheme(row, perm[
axis]);
213 if (new_line[
axis] && flip[perm[
axis]])
216 result.row (row) = new_line;
218 INFO (
"External phase encoding data transformed to match RAS realignment of image \"" +
H.name() +
"\"");
226 template <
class MatrixType,
class HeaderType>
232 if (
order[0] == 0 &&
order[1] == 1 &&
order[2] == 2 && !flip[0] && !flip[1] && !flip[2]) {
233 INFO (
"No transformation of phase encoding data required for export to file");
236 Eigen::Matrix<default_type, Eigen::Dynamic, Eigen::Dynamic> result (pe_scheme.rows(), pe_scheme.cols());
237 for (ssize_t row = 0; row != pe_scheme.rows(); ++row) {
238 Eigen::VectorXd new_line = pe_scheme.row (row);
243 result.row (row) = new_line;
245 INFO (
"Phase encoding data transformed to match NIfTI / MGH image export prior to writing to file");
254 template <
class MatrixType>
255 void __save (
const MatrixType& PE,
const std::string& path)
258 for (ssize_t row = 0; row != PE.rows(); ++row) {
260 out << PE.template block<1, 3>(row, 0).template cast<int>();
262 out <<
" " << PE.block(row, 3, 1, PE.cols()-3);
272 template <
class MatrixType,
class HeaderType>
273 void save (
const MatrixType& PE,
const HeaderType& header,
const std::string& path)
278 throw Exception (
e,
"Cannot export phase-encoding table to file \"" + path +
"\"");
281 if (
Path::has_suffix (header.name(), {
".mgh",
".mgz",
".nii",
".nii.gz",
".img"})) {
289 template <
class MatrixType,
class HeaderType>
290 void save_eddy (
const MatrixType& PE,
const HeaderType& header,
const std::string& config_path,
const std::string& index_path)
292 Eigen::MatrixXd config;
293 Eigen::Array<int, Eigen::Dynamic, 1> indices;
307 template <
class HeaderType>
308 Eigen::MatrixXd
load (
const std::string& path,
const HeaderType& header)
320 template <
class HeaderType>
321 Eigen::MatrixXd
load_eddy (
const std::string& config_path,
const std::string& index_path,
const HeaderType& header)
323 const Eigen::MatrixXd config =
load_matrix (config_path);
324 const Eigen::Array<int, Eigen::Dynamic, 1> indices = load_vector<int> (index_path);
325 const Eigen::MatrixXd PE =
eddy2scheme (config, indices);
a class to hold a named list of Option's
open output files for writing, checking for pre-existing file if necessary
constexpr I round(const T x)
std::string dir2id(const Eigen::Vector3d &)
convert axis directions between formats
void axes_on_write(const Header &H, vector< size_t > &order, vector< bool > &flip)
bool has_suffix(const std::string &name, const std::string &suffix)
void save_eddy(const MatrixType &PE, const HeaderType &header, const std::string &config_path, const std::string &index_path)
Save a phase-encoding scheme to EDDY format config / index files.
void save(const MatrixType &PE, const HeaderType &header, const std::string &path)
Save a phase-encoding scheme to file.
Eigen::MatrixXd parse_scheme(const Header &)
parse the phase encoding matrix from a header
void scheme2eddy(const MatrixType &PE, Eigen::MatrixXd &config, Eigen::Array< int, Eigen::Dynamic, 1 > &indices)
Convert a phase-encoding scheme into the EDDY config / indices format.
Eigen::MatrixXd get_scheme(const Header &)
get a phase encoding matrix
const App::OptionGroup ExportOptions
Eigen::MatrixXd transform_for_nifti_write(const MatrixType &pe_scheme, const HeaderType &H)
Modifies a phase encoding scheme if being exported alongside a NIfTI image.
void export_commandline(const Header &)
Save the phase-encoding scheme from a header to file depending on command-line options.
void clear_scheme(Header &header)
clear the phase encoding matrix from a header
const App::OptionGroup ImportOptions
Eigen::MatrixXd load(const std::string &path, const HeaderType &header)
Load a phase-encoding scheme from a matrix text file.
Eigen::MatrixXd transform_for_image_load(const MatrixType &pe_scheme, const HeaderType &H)
Modifies a phase encoding scheme if being imported alongside a non-RAS image.
Eigen::MatrixXd eddy2scheme(const Eigen::MatrixXd &, const Eigen::Array< int, Eigen::Dynamic, 1 > &)
Convert phase-encoding infor from the EDDY config / indices format into a standard scheme.
Eigen::MatrixXd load_eddy(const std::string &config_path, const std::string &index_path, const HeaderType &header)
Load a phase-encoding scheme from an EDDY-format config / indices file pair.
void check(const MatrixType &PE)
check that a phase-encoding table is valid
const App::OptionGroup SelectOptions
void set_scheme(Header &header, const MatrixType &PE)
store the phase encoding matrix in a header
vector< size_t > order(const HeaderType &header, size_t from_axis=0, size_t to_axis=std::numeric_limits< size_t >::max())
sort range of axes with respect to their absolute stride.
std::map< std::string, std::string > KeyValues
used in various places for storing key-value pairs
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > load_matrix(const std::string &filename)
read matrix data into an Eigen::Matrix filename
constexpr std::enable_if< std::is_arithmetic< X >::value &&std::is_unsigned< X >::value, X >::type abs(X x)
std::string str(const T &value, int precision=0)
void save_matrix(const MatrixType &M, const std::string &filename, const KeyValues &keyvals=KeyValues(), const bool add_to_command_history=true)
write the matrix M to file
void save_vector(const VectorType &V, const std::string &filename, const KeyValues &keyvals=KeyValues(), const bool add_to_command_history=true)
write the vector V to file
std::string & add_line(std::string &original, const std::string &new_line)
add a line to a string, taking care of inserting a newline if needed