Developer documentation
Version 3.0.3-105-gd3941f44
helpers.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __fixel_helpers_h__
18#define __fixel_helpers_h__
19
20#include "app.h"
21#include "image.h"
22#include "image_diff.h"
23#include "image_helpers.h"
24#include "algo/loop.h"
25#include "fixel/keys.h"
26#include "fixel/types.h"
28
29
30namespace MR
31{
34 public:
35 InvalidFixelDirectoryException (const std::string& msg) : Exception(msg) {}
36 InvalidFixelDirectoryException (const Exception& previous_exception, const std::string& msg)
37 : Exception(previous_exception, msg) {}
38 };
39
40 namespace Peaks
41 {
42 FORCE_INLINE void check (const Header& in)
43 {
44 if (!in.datatype().is_floating_point())
45 throw Exception ("Image \"" + in.name() + "\" is not a valid peaks image: Does not contain floating-point data");
46 try {
47 check_effective_dimensionality (in, 4);
48 } catch (Exception& e) {
49 throw Exception (e, "Image \"" + in.name() + "\" is not a valid peaks image: Expect 4 dimensions");
50 }
51 if (in.size(3) % 3)
52 throw Exception ("Image \"" + in.name() + "\" is not a valid peaks image: Number of volumes must be a multiple of 3");
53 }
54 }
55
56 namespace Fixel
57 {
58 FORCE_INLINE bool is_index_filename (const std::string& path)
59 {
60 for (std::initializer_list<const std::string>::iterator it = supported_sparse_formats.begin();
61 it != supported_sparse_formats.end(); ++it) {
62 if (Path::basename (path) == "index" + *it)
63 return true;
64 }
65 return false;
66 }
67
68 template <class HeaderType>
69 FORCE_INLINE bool is_index_image (const HeaderType& in)
70 {
71 return is_index_filename (in.name())
72 && in.ndim() == 4
73 && in.size(3) == 2;
74 }
75
76 template <class HeaderType>
77 FORCE_INLINE void check_index_image (const HeaderType& index)
78 {
79 if (!is_index_image (index))
80 throw InvalidImageException (index.name() + " is not a valid fixel index image. Image must be 4D with 2 volumes in the 4th dimension");
81 }
82
83 template <class HeaderType>
84 FORCE_INLINE bool is_data_file (const HeaderType& in)
85 {
86 return in.ndim() == 3 && in.size(2) == 1;
87 }
88
89
90 FORCE_INLINE bool is_directions_filename (const std::string& path)
91 {
92 for (std::initializer_list<const std::string>::iterator it = supported_sparse_formats.begin();
93 it != supported_sparse_formats.end(); ++it) {
94 if (Path::basename (path) == "directions" + *it)
95 return true;
96 }
97 return false;
98 }
99
100 template <class HeaderType>
101 FORCE_INLINE bool is_directions_file (const HeaderType& in)
102 {
103 return is_directions_filename (in.name())
104 && in.ndim() == 3
105 && in.size(1) == 3
106 && in.size(2) == 1;
107 }
108
109
110
111
112 template <class HeaderType>
113 FORCE_INLINE void check_data_file (const HeaderType& in)
114 {
115 if (!is_data_file (in))
116 throw InvalidImageException (in.name() + " is not a valid fixel data file. Expected a 3-dimensional image of size n x m x 1");
117 }
118
119 FORCE_INLINE std::string get_fixel_directory (const std::string& fixel_file) {
120 std::string fixel_directory = Path::dirname (fixel_file);
121 // assume the user is running the command from within the fixel directory
122 if (fixel_directory.empty())
123 fixel_directory = Path::cwd();
124 return fixel_directory;
125 }
126
127
128 template <class IndexHeaderType>
129 FORCE_INLINE index_type get_number_of_fixels (IndexHeaderType& index_header) {
130 check_index_image (index_header);
131 if (index_header.keyval().count (n_fixels_key)) {
132 return std::stoul (index_header.keyval().at(n_fixels_key));
133 } else {
134 auto index_image = Image<index_type>::open (index_header.name());
135 index_image.index(3) = 1;
136 index_type num_fixels = 0;
137 index_type max_offset = 0;
138 for (auto i = MR::Loop (index_image, 0, 3) (index_image); i; ++i) {
139 if (index_image.value() > max_offset) {
140 max_offset = index_image.value();
141 index_image.index(3) = 0;
142 num_fixels = index_image.value();
143 index_image.index(3) = 1;
144 }
145 }
146 return (max_offset + num_fixels);
147 }
148 }
149
150
151 template <class IndexHeaderType, class DataHeaderType>
152 FORCE_INLINE bool fixels_match (const IndexHeaderType& index_header, const DataHeaderType& data_header)
153 {
154 bool fixels_match (false);
155
156 if (is_index_image (index_header)) {
157 if (index_header.keyval().count (n_fixels_key)) {
158 fixels_match = std::stoul (index_header.keyval().at(n_fixels_key)) == (index_type)data_header.size(0);
159 } else {
160 auto index_image = Image<index_type>::open (index_header.name());
161 index_image.index(3) = 1;
162 index_type num_fixels = 0;
163 index_type max_offset = 0;
164 for (auto i = MR::Loop (index_image, 0, 3) (index_image); i; ++i) {
165 if (index_image.value() > max_offset) {
166 max_offset = index_image.value();
167 index_image.index(3) = 0;
168 num_fixels = index_image.value();
169 index_image.index(3) = 1;
170 }
171 }
172 fixels_match = (max_offset + num_fixels) == (index_type)data_header.size(0);
173 }
174 }
175
176 return fixels_match;
177 }
178
179
180 FORCE_INLINE void check_fixel_size (const Header& index_h, const Header& data_h)
181 {
182 check_index_image (index_h);
183 check_data_file (data_h);
184
185 if (!fixels_match (index_h, data_h))
186 throw InvalidImageException ("Fixel number mismatch between index image " + index_h.name() + " and data image " + data_h.name());
187 }
188
189
190 FORCE_INLINE void check_fixel_directory (const std::string &path, bool create_if_missing = false, bool check_if_empty = false)
191 {
192 std::string path_temp = path;
193 // handle the use case when a fixel command is run from inside a fixel directory
194 if (path.empty())
195 path_temp = Path::cwd();
196
197 bool exists (true);
198
199 if (!(exists = Path::exists (path_temp))) {
200 if (create_if_missing) File::mkdir (path_temp);
201 else throw Exception ("Fixel directory (" + str(path_temp) + ") does not exist");
202 }
203 else if (!Path::is_dir (path_temp))
204 throw Exception (str(path_temp) + " is not a directory");
205
206 if (check_if_empty && Path::Dir (path_temp).read_name ().size () != 0)
207 throw Exception ("Output fixel directory \"" + path_temp + "\" is not empty"
209 " (-force option cannot safely be applied on directories; please erase manually instead)" :
210 ""));
211 }
212
213
214 FORCE_INLINE Header find_index_header (const std::string &fixel_directory_path)
215 {
216 Header header;
217 check_fixel_directory (fixel_directory_path);
218
219 for (std::initializer_list<const std::string>::iterator it = supported_sparse_formats.begin();
220 it !=supported_sparse_formats.end(); ++it) {
221 std::string full_path = Path::join (fixel_directory_path, "index" + *it);
222 if (Path::exists(full_path)) {
223 if (header.valid())
224 throw InvalidFixelDirectoryException ("Multiple index images found in directory " + fixel_directory_path);
225 header = Header::open (full_path);
226 }
227 }
228 if (!header.valid())
229 throw InvalidFixelDirectoryException ("Could not find index image in directory " + fixel_directory_path);
230
231 check_index_image (header);
232 return header;
233 }
234
235
236 FORCE_INLINE vector<Header> find_data_headers (const std::string &fixel_directory_path, const Header &index_header, const bool include_directions = false)
237 {
238 check_index_image (index_header);
239 auto dir_walker = Path::Dir (fixel_directory_path);
240 vector<std::string> file_names;
241 {
242 std::string temp;
243 while ((temp = dir_walker.read_name()).size())
244 file_names.push_back (temp);
245 }
246 std::sort (file_names.begin(), file_names.end());
247
248 vector<Header> data_headers;
249 for (auto fname : file_names) {
251 try {
252 auto H = Header::open (Path::join (fixel_directory_path, fname));
253 if (is_data_file (H)) {
254 if (fixels_match (index_header, H)) {
255 if (!is_directions_file (H) || include_directions)
256 data_headers.emplace_back (std::move (H));
257 } else {
258 WARN ("fixel data file (" + fname + ") does not contain the same number of elements as fixels in the index file");
259 }
260 }
261 } catch (...) {
262 WARN ("unable to open file \"" + fname + "\" as potential fixel data file");
263 }
264 }
265 }
266
267 return data_headers;
268 }
269
270
271
272 FORCE_INLINE Header find_directions_header (const std::string fixel_directory_path)
273 {
274 bool directions_found (false);
275 Header header;
276 check_fixel_directory (fixel_directory_path);
277 Header index_header = Fixel::find_index_header (fixel_directory_path);
278
279 auto dir_walker = Path::Dir (fixel_directory_path);
280 std::string fname;
281 while ((fname = dir_walker.read_name()).size()) {
282 if (is_directions_filename (fname)) {
283 Header tmp_header = Header::open (Path::join (fixel_directory_path, fname));
284 if (is_directions_file (tmp_header)) {
285 if (fixels_match (index_header, tmp_header)) {
286 if (directions_found == true)
287 throw Exception ("multiple directions files found in fixel image directory: " + fixel_directory_path);
288 directions_found = true;
289 header = std::move (tmp_header);
290 } else {
291 WARN ("fixel directions file (" + fname + ") does not contain the same number of elements as fixels in the index file" );
292 }
293 }
294 }
295 }
296
297 if (!directions_found)
298 throw InvalidFixelDirectoryException ("Could not find directions image in directory " + fixel_directory_path);
299
300 return header;
301 }
302
304 template <class IndexHeaderType>
306 Header header (index);
307 header.ndim() = 3;
308 header.size(0) = get_number_of_fixels (index);
309 header.size(1) = 1;
310 header.size(2) = 1;
311 header.stride(0) = 1;
312 header.stride(1) = 2;
313 header.stride(2) = 3;
314 header.transform() = transform_type::Identity();
315 header.datatype() = DataType::Float32;
316 header.datatype().set_byte_order_native();
317 return header;
318 }
319
321 template <class IndexHeaderType>
324 header.size(1) = 3;
325 return header;
326 }
327
329 FORCE_INLINE void copy_fixel_file (const std::string& input_file_path, const std::string& output_directory) {
330 check_fixel_directory (output_directory, true);
331 std::string output_path = Path::join (output_directory, Path::basename (input_file_path));
332 Header input_header = Header::open (input_file_path);
333 auto input_image = input_header.get_image<float>();
334 auto output_image = Image<float>::create (output_path, input_header);
335 threaded_copy (input_image, output_image);
336 }
337
339 FORCE_INLINE void copy_index_file (const std::string& input_directory, const std::string& output_directory) {
340 Header input_header = Fixel::find_index_header (input_directory);
341 check_fixel_directory (output_directory, true);
342
343 std::string output_path = Path::join (output_directory, Path::basename (input_header.name()));
344
345 // If the index file already exists check it is the same as the input index file
346 if (Path::exists (output_path)) {
347 auto input_image = input_header.get_image<index_type>();
348 auto output_image = Image<index_type>::open (output_path);
349 if (!images_match_abs (input_image, output_image))
350 throw Exception ("output fixel directory \"" + output_directory + "\" already contains index file, "
351 + "which is not the same as the expected output"
353 " (-force option cannot safely be applied on directories; please erase manually instead)" :
354 ""));
355 } else {
356 auto output_image = Image<index_type>::create (Path::join (output_directory, Path::basename (input_header.name())), input_header);
357 auto input_image = input_header.get_image<index_type>();
358 threaded_copy (input_image, output_image);
359 }
360 }
361
363 FORCE_INLINE void copy_directions_file (const std::string& input_directory, const std::string& output_directory) {
364 Header input_header = Fixel::find_directions_header (input_directory);
365 std::string output_path = Path::join (output_directory, Path::basename (input_header.name()));
366
367 // If the directions file already exists check it is the same as the input directions file
368 if (Path::exists (output_path)) {
369 auto input_image = input_header.get_image<float>();
370 auto output_image = Image<float>::open (output_path);
371 if (!images_match_abs (input_image, output_image))
372 throw Exception ("output fixel directory \"" + output_directory + "\" already contains directions file, "
373 + "which is not the same as the expected output"
375 " (-force option cannot safely be applied on directories; please erase manually instead)" :
376 ""));
377 } else {
378 auto output_image = Image<float>::create (Path::join (output_directory, Path::basename (input_header.name())), input_header);
379 auto input_image = input_header.get_image<float>();
380 threaded_copy (input_image, output_image);
381 }
382
383 }
384
385 FORCE_INLINE void copy_index_and_directions_file (const std::string& input_directory, const std::string &output_directory) {
386 copy_index_file (input_directory, output_directory);
387 copy_directions_file (input_directory, output_directory);
388 }
389
390
392 FORCE_INLINE void copy_all_data_files (const std::string &input_directory, const std::string &output_directory) {
393 for (auto& input_header : Fixel::find_data_headers (input_directory, Fixel::find_index_header (input_directory)))
394 copy_fixel_file (input_header.name(), output_directory);
395 }
396
398 template <class ValueType>
399 Image<ValueType> open_fixel_data_file (const std::string& input_file) {
400 if (Path::is_dir (input_file))
401 throw Exception ("please input the specific fixel data file to be converted (not the fixel directory)");
402
403 Header in_data_header = Header::open (input_file);
404 Fixel::check_data_file (in_data_header);
405 auto in_data_image = in_data_header.get_image<ValueType>();
406
407 Header in_index_header = Fixel::find_index_header (Fixel::get_fixel_directory (input_file));
408 if (input_file == in_index_header.name())
409 throw Exception ("input fixel data file cannot be the index file");
410
411 return in_data_image;
412 }
413 }
414}
415
416#endif
static constexpr uint8_t Float32
Definition: datatype.h:147
functions and classes related to image data input/output
Definition: image.h:41
static Image create(const std::string &image_name, const Header &template_header, bool add_to_command_history=true)
Definition: image.h:192
static Image open(const std::string &image_name, bool read_write_if_existing=false)
Definition: image.h:189
InvalidFixelDirectoryException(const Exception &previous_exception, const std::string &msg)
Definition: helpers.h:36
InvalidFixelDirectoryException(const std::string &msg)
Definition: helpers.h:35
#define WARN(msg)
Definition: exception.h:73
FORCE_INLINE LoopAlongAxes Loop()
Definition: loop.h:419
constexpr double e
Definition: math.h:39
#define NOMEMALIGN
Definition: memory.h:22
bool overwrite_files
void mkdir(const std::string &folder)
Definition: utils.h:242
FORCE_INLINE bool fixels_match(const IndexHeaderType &index_header, const DataHeaderType &data_header)
Definition: helpers.h:152
FORCE_INLINE Header directions_header_from_index(IndexHeaderType &index)
Generate a header for a fixel directions data file (Nx3x1) using an index image as a template.
Definition: helpers.h:322
FORCE_INLINE void check_index_image(const HeaderType &index)
Definition: helpers.h:77
FORCE_INLINE void check_fixel_directory(const std::string &path, bool create_if_missing=false, bool check_if_empty=false)
Definition: helpers.h:190
FORCE_INLINE bool is_index_image(const HeaderType &in)
Definition: helpers.h:69
FORCE_INLINE std::string get_fixel_directory(const std::string &fixel_file)
Definition: helpers.h:119
FORCE_INLINE bool is_index_filename(const std::string &path)
Definition: helpers.h:58
FORCE_INLINE vector< Header > find_data_headers(const std::string &fixel_directory_path, const Header &index_header, const bool include_directions=false)
Definition: helpers.h:236
FORCE_INLINE void copy_all_data_files(const std::string &input_directory, const std::string &output_directory)
Copy all data files in a fixel directory into another directory. Data files do not include the index ...
Definition: helpers.h:392
FORCE_INLINE Header data_header_from_index(IndexHeaderType &index)
Generate a header for a sparse data file (Nx1x1) using an index image as a template.
Definition: helpers.h:305
FORCE_INLINE void copy_directions_file(const std::string &input_directory, const std::string &output_directory)
Copy the directions file from one fixel directory into another.
Definition: helpers.h:363
FORCE_INLINE bool is_directions_file(const HeaderType &in)
Definition: helpers.h:101
FORCE_INLINE void check_data_file(const HeaderType &in)
Definition: helpers.h:113
FORCE_INLINE void check_fixel_size(const Header &index_h, const Header &data_h)
Definition: helpers.h:180
FORCE_INLINE Header find_directions_header(const std::string fixel_directory_path)
Definition: helpers.h:272
FORCE_INLINE Header find_index_header(const std::string &fixel_directory_path)
Definition: helpers.h:214
uint32_t index_type
Definition: types.h:25
FORCE_INLINE void copy_index_file(const std::string &input_directory, const std::string &output_directory)
Copy the index file from one fixel directory into another.
Definition: helpers.h:339
const std::initializer_list< const std::string > supported_sparse_formats
Definition: keys.h:27
Image< ValueType > open_fixel_data_file(const std::string &input_file)
open a data file. checks that a user has not input a fixel directory or index image
Definition: helpers.h:399
FORCE_INLINE bool is_data_file(const HeaderType &in)
Definition: helpers.h:84
FORCE_INLINE index_type get_number_of_fixels(IndexHeaderType &index_header)
Definition: helpers.h:129
FORCE_INLINE void copy_fixel_file(const std::string &input_file_path, const std::string &output_directory)
Copy a file from one fixel directory into another.
Definition: helpers.h:329
const std::string n_fixels_key("nfixels")
FORCE_INLINE void copy_index_and_directions_file(const std::string &input_directory, const std::string &output_directory)
Definition: helpers.h:385
FORCE_INLINE bool is_directions_filename(const std::string &path)
Definition: helpers.h:90
std::string join(const std::string &first, const std::string &second)
Definition: path.h:74
std::string basename(const std::string &name)
Definition: path.h:60
std::string dirname(const std::string &name)
Definition: path.h:67
std::string cwd()
Definition: path.h:156
bool exists(const std::string &path)
Definition: path.h:88
bool is_dir(const std::string &path)
Definition: path.h:104
bool has_suffix(const std::string &name, const std::string &suffix)
Definition: path.h:130
FORCE_INLINE void check(const Header &in)
Definition: helpers.h:42
Definition: base.h:24
bool images_match_abs(ImageType1 &in1, ImageType2 &in2, const double tol=0.0)
check images are the same within a absolute tolerance
Definition: image_diff.h:172
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
void threaded_copy(InputImageType &source, OutputImageType &destination, const vector< size_t > &axes, size_t num_axes_in_thread=1)
Definition: threaded_copy.h:43
Eigen::MatrixXd H
size_t index
#define FORCE_INLINE
Definition: types.h:156