File: slice_encoding.cpp

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 (158 lines) | stat: -rw-r--r-- 6,749 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
/* 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/.
 */

#include "metadata/slice_encoding.h"

#include "axes.h"
#include "file/nifti_utils.h"
#include "header.h"
#include "metadata/bids.h"

namespace MR {
  namespace Metadata {
    namespace SliceEncoding {



      void transform_for_image_load(KeyValues &keyval, const Header& header) {
        // If there's any slice encoding direction information present in the
        //   header, that's also necessary to update here
        auto slice_encoding_it = keyval.find("SliceEncodingDirection");
        auto slice_timing_it = keyval.find("SliceTiming");
        if (!(slice_encoding_it == keyval.end() && slice_timing_it == keyval.end())) {
          if (header.realignment().is_identity()) {
            INFO("No transformation of slice encoding direction for load of image \"" + header.name() + "\" required");
            return;
          }
          Metadata::BIDS::axis_vector_type orig_dir = Metadata::BIDS::axis_vector_type({0, 0, 1});
          if (slice_encoding_it != keyval.end()) {
            try {
              orig_dir = Metadata::BIDS::axisid2vector(slice_encoding_it->second);
            } catch (Exception& e) {
              WARN("Unable to conform slice encoding direction to image realignment "
                   "for image \"" + header.name() + "\"; erasing");
              clear(keyval);
              return;
            }
          }
          const Metadata::BIDS::axis_vector_type new_dir = header.realignment().applied_transform() * orig_dir;
          if (slice_encoding_it != keyval.end()) {
            slice_encoding_it->second = Metadata::BIDS::vector2axisid(new_dir);
            INFO("Slice encoding direction has been modified"
                  " to conform to MRtrix3 internal header transform realignment"
                  " of image \"" + header.name() + "\"");
          } else if ((new_dir * -1).dot(orig_dir) == 1) {
            auto slice_timing = parse_floats(slice_timing_it->second);
            std::reverse(slice_timing.begin(), slice_timing.end());
            slice_timing_it->second = join(slice_timing, ",");
            INFO("Slice timing vector reversed to conform to MRtrix3 internal transform realignment"
                 " of image \"" + header.name() + "\"");
          } else {
            keyval["SliceEncodingDirection"] = Metadata::BIDS::vector2axisid(new_dir);
            WARN("Slice encoding direction of image \"" + header.name() + "\""
                 " inferred to be \"k\" in order to preserve interpretation of existing \"SliceTiming\" field"
                 " after MRtrix3 internal transform realignment");
          }
        }
      }



      void transform_for_nifti_write(KeyValues& keyval, const Header &H) {
        auto slice_encoding_it = keyval.find("SliceEncodingDirection");
        auto slice_timing_it = keyval.find("SliceTiming");
        if (slice_encoding_it == keyval.end() && slice_timing_it == keyval.end())
          return;

        const Axes::Shuffle shuffle = File::NIfTI::axes_on_write(H);
        if (shuffle.is_identity()) {
          INFO("No need to transform slice encoding information for NIfTI image write:"
               " image is already RAS");
          return;
        }

        const Metadata::BIDS::axis_vector_type
            orig_dir(slice_encoding_it == keyval.end() ?
                     Metadata::BIDS::axis_vector_type({0, 0, 1}) :
                     Metadata::BIDS::axisid2vector(slice_encoding_it->second));
        Metadata::BIDS::axis_vector_type new_dir;
        for (size_t axis = 0; axis != 3; ++axis)
          new_dir[axis] = shuffle.flips[axis] ? -orig_dir[shuffle.permutations[axis]] : orig_dir[shuffle.permutations[axis]];

        if (slice_encoding_it != keyval.end()) {
          slice_encoding_it->second = Metadata::BIDS::vector2axisid(new_dir);
          INFO("Slice encoding direction modified according to output NIfTI strides");
        } else if ((new_dir * -1).dot(orig_dir) == 1) {
          auto slice_timing = parse_floats(slice_timing_it->second);
          std::reverse (slice_timing.begin(), slice_timing.end());
          slice_timing_it->second = join(slice_timing, ",");
          INFO("Slice timing vector reversed to conform to output NIfTI strides");
        } else {
          keyval["SliceEncodingDirection"] = Metadata::BIDS::vector2axisid(new_dir);
          WARN("Slice encoding direction added to metadata"
               " in order to preserve interpretation of existing \"SliceTiming\" field"
               " following output NIfTI strides");
        }
      }



      std::string resolve_slice_timing (const std::string &one, const std::string &two) {
        if (one == "variable" || two == "variable")
          return "variable";
        const vector<std::string> one_split = split(one, ",");
        const vector<std::string> two_split = split(two, ",");
        if (one_split.size() != two_split.size()) {
          DEBUG("Slice timing vectors of inequal length");
          return "invalid";
        }
        // Siemens CSA reports with 2.5ms precision = 0.0025s
        // Allow slice times to vary by 1.5x this amount, but no more
        for (size_t i = 0; i != one_split.size(); ++i) {
          default_type f_one, f_two;
          try {
            f_one = to<default_type>(one_split[i]);
            f_two = to<default_type>(two_split[i]);
          } catch (Exception&) {
            DEBUG("Error converting slice timing vector to floating-point");
            return "invalid";
          }
          const default_type diff = abs(f_two - f_one);
          if (diff > 0.00375) {
            DEBUG("Supra-threshold difference of " + str(diff) + "s in slice times");
            return "variable";
          }
        }
        return one;
      }



      void clear(KeyValues &keyval) {
        auto erase = [](KeyValues& keyval, const std::string& s) {
          auto it = keyval.find(s);
          if (it != keyval.end())
            keyval.erase(it);
        };
        erase(keyval, "SliceEncodingDirection");
        erase(keyval, "SliceTiming");
      }



    }
  }
}