File: phase_encoding.h

package info (click to toggle)
mrtrix3 3.0.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 15,300 kB
  • sloc: cpp: 130,470; python: 9,603; sh: 597; makefile: 62; xml: 47
file content (196 lines) | stat: -rw-r--r-- 8,823 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* Copyright (c) 2008-2025 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#ifndef __metadata__phase_encoding_h__
#define __metadata__phase_encoding_h__

#include <Eigen/Dense>
#include <array>

#include "app.h"
#include "axes.h"
#include "file/nifti_utils.h"
#include "file/ofstream.h"
#include "header.h"
#include "metadata/bids.h"
#include "types.h"
#include "version.h"

namespace MR {
  namespace Metadata {
    namespace PhaseEncoding {



      extern const App::OptionGroup ImportOptions;
      extern const App::OptionGroup SelectOptions;
      extern const App::OptionGroup ExportOptions;

      using scheme_type = Eigen::MatrixXd;

      //! check that a phase-encoding table is valid
      void check(const scheme_type& PE);

      //! check that the PE scheme matches the DWI data in \a header
      void check(const scheme_type& PE, const Header& header);

      //! store the phase encoding matrix in a header
      /*! this will store the phase encoding matrix into the
       *  Header::keyval() structure of \a header.
       *  - If the phase encoding direction and/or total readout
       *    time varies between volumes, then the information
       *    will be stored in field "pe_scheme"; if not, it
       *    will instead be stored in fields "PhaseEncodingDirection"
       *    and "TotalReadoutTime"
       */
      void set_scheme(KeyValues& keyval, const scheme_type& PE);

      //! clear the phase encoding matrix from a key-value dictionary
      /*! this will delete any trace of phase encoding information
       *  from the dictionary.
       */
      void clear_scheme(KeyValues& keyval);

      //! parse the phase encoding matrix from a key-value dictionary
      /*! extract the phase encoding matrix stored in \a the key-value dictionary
       *  if one is present. The key-value dictionary is not in all use cases
       *  the "keyval" member of the Header class.
       */
      scheme_type parse_scheme(const KeyValues&, const Header&);

      //! get a phase encoding matrix
      /*! get a valid phase-encoding matrix, either from files specified at
       *  the command-line that exclusively provide phase encoding information
       *  (ie. NOT from .json; that is handled elsewhere),
       *  or from the contents of the image header.
       */
      scheme_type get_scheme(const Header&);

      //! Convert a phase-encoding scheme in TOPUP format into the EDDY config / indices format
      void topup2eddy(const scheme_type& PE, Eigen::MatrixXd& config, Eigen::Array<int, Eigen::Dynamic, 1>& indices);

      //! Convert phase-encoding infor from the EDDY config / indices format into a TOPUP format scheme
      scheme_type eddy2topup(const Eigen::MatrixXd&, const Eigen::Array<int, Eigen::Dynamic, 1>&);

      //! Modifies a phase encoding scheme if being imported alongside a non-RAS image
      //  and internal header realignment is performed
      void transform_for_image_load(KeyValues& keyval, const Header& H);
      scheme_type transform_for_image_load(const scheme_type& pe_scheme, const Header& H);

      //! Modifies a phase encoding scheme if being exported alongside a NIfTI image
      void transform_for_nifti_write(KeyValues& keyval, const Header& H);
      scheme_type transform_for_nifti_write(const scheme_type& pe_scheme, const Header& H);

      void save_table(const scheme_type& PE, const std::string& path, bool write_command_history);

      template <class HeaderType>
      void save_table(const HeaderType &header, const std::string &path) {
        const scheme_type scheme = get_scheme(header);
        if (scheme.rows() == 0)
          throw Exception ("No phase encoding scheme in header of image \"" + header.name() + "\" to save");
        save(scheme, header, path);
      }

      //! Save a phase-encoding scheme associated with an image to file
      // Note that because the output table requires permutation / sign flipping
      //   only if the output target image is a NIfTI,
      //   for this function to operate as intended it is necessary for it to be executed
      //   after having set the output file name in the image header
      template <class HeaderType>
      void save_table(const scheme_type &PE, const HeaderType &header, const std::string &path) {
        try {
          check(PE, header);
        } catch (Exception &e) {
          throw Exception(e, "Cannot export phase-encoding table to file \"" + path + "\"");
        }

        if (Path::has_suffix(header.name(), {".mgh", ".mgz", ".nii", ".nii.gz", ".img"})) {
          WARN("External phase encoding table \"" + path + "\" for image \"" + header.name() + "\""
               " may not be suitable for FSL topup; consider use of -export_pe_topup instead"
               " (see: mrtrix.readthedocs.org/en/" MRTRIX_BASE_VERSION "/concepts/pe_scheme.html#reference-axes-for-phase-encoding-directions)");
          save_table(transform_for_nifti_write(PE, header), path, true);
        } else {
          save_table(PE, path, true);
        }
      }

      template <class HeaderType>
      void save_topup(const scheme_type &PE, const HeaderType &header, const std::string &path) {
        try {
          check(PE, header);
        } catch (Exception &e) {
          throw Exception(e, "Cannot export phase-encoding table to file \"" + path + "\"");
        }

        if (!Path::has_suffix(header.name(), {".mgh", ".mgz", ".nii", ".nii.gz", ".img"})) {
          WARN("Beware use of -export_pe_topup in conjunction image format other than MGH / NIfTI;"
               " -export_pe_table may be more suitable"
               " (see: mrtrix.readthedocs.org/en/" MRTRIX_BASE_VERSION "/concepts/pe_scheme.html#reference-axes-for-phase-encoding-directions)");
        }

        scheme_type table = transform_for_nifti_write(PE, header);
        // The flipping of first axis based on the determinant of the image header transform
        //   applies to what is stored on disk
        Axes::permutations_type order;
        const auto adjusted_transform = File::NIfTI::adjust_transform(header, order);
        if (adjusted_transform.linear().determinant() > 0.0)
          table.col(0) *= -1.0;
        save_table(table, path, false);
      }

      //! Save a phase-encoding scheme to EDDY format config / index files
      template <class HeaderType>
      void save_eddy(const scheme_type& PE,
                     const HeaderType& header,
                     const std::string& config_path,
                     const std::string& index_path) {
        if (!Path::has_suffix(header.name(), {".mgh", ".mgz", ".nii", ".nii.gz", ".img"})) {
          WARN("Exporting phase encoding table to FSL eddy format"
               " in conjunction with format other than MGH / NIfTI"
               " risks erroneous interpretation due to possible flipping of first image axis"
               " (see: mrtrix.readthedocs.org/en/" MRTRIX_BASE_VERSION "/concepts/pe_scheme.html#reference-axes-for-phase-encoding-directions)");
        }
        scheme_type table = transform_for_nifti_write(PE, header);
        Axes::permutations_type order;
        const auto adjusted_transform = File::NIfTI::adjust_transform(header, order);
        if (adjusted_transform.linear().determinant() > 0.0)
          table.col(0) *= -1.0;
        Eigen::MatrixXd config;
        Eigen::Array<int, Eigen::Dynamic, 1> indices;
        topup2eddy(table, config, indices);
        save_matrix(config, config_path, KeyValues(), false);
        save_vector(indices, index_path, KeyValues(), false);
      }

      //! Save the phase-encoding scheme from a header to file depending on command-line options
      void export_commandline(const Header&);

      //! Load a phase-encoding scheme from a matrix text file
      scheme_type load_table(const std::string& path, const Header& header);

      //! Load a phase-encoding scheme from a FSL topup format text file
      scheme_type load_topup(const std::string& path, const Header& header);

      //! Load a phase-encoding scheme from an EDDY-format config / indices file pair
      scheme_type load_eddy(const std::string& config_path, const std::string& index_path, const Header& header);



    }
  }
}

#endif