libfs
Header-only C++11 library for accessing FreeSurfer neuroimaging data
libfs.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <iostream>
4 #include <climits>
5 #include <stdio.h>
6 #include <vector>
7 #include <fstream>
8 #include <cassert>
9 #include <sstream>
10 #include <stdexcept>
11 #include <map>
12 #include <unordered_set>
13 #include <unordered_map>
14 #include <cmath>
15 #include <algorithm>
16 #include <chrono>
17 #include <cstdint>
18 
19 #define LIBFS_VERSION "0.4.0"
20 #define LIBFS_VERISION_MAJOR 0
21 #define LIBFS_VERISION_MINOR 4
22 #define LIBFS_VERISION_PATCH 0
23 
26 
86 // Set apptag (printed as prefix of debug messages) for debug messages.
87 // Users can overwrite this by defining LIBFS_APPTAG before including 'libfs.h'.
88 #ifndef LIBFS_APPTAG
89 #define LIBFS_APPTAG "[libfs] "
90 #endif
91 
92 // Set default.
93 #define LIBFS_DBG_WARNING
94 
95 // If the user wants something below our default, remove our default.
96 #ifdef LIBFS_DBG_NONE
97 #undef LIBFS_DBG_WARNING
98 #endif
99 
100 #ifdef LIBFS_DBG_CRITICAL
101 #undef LIBFS_DBG_WARNING
102 #endif
103 
104 #ifdef LIBFS_DBG_ERROR
105 #undef LIBFS_DBG_WARNING
106 #endif
107 
108 // Ensure that the user does not have to define all debug levels
109 // up to the one they actually want, by defining all lower ones for them.
110 #ifdef LIBFS_DBG_EXCESSIVE
111 #define LIBFS_DBG_VERBOSE
112 #endif
113 
114 #ifdef LIBFS_DBG_VERBOSE
115 #define LIBFS_DBG_INFO
116 #endif
117 
118 #ifdef LIBFS_DBG_INFO
119 #define LIBFS_DBG_WARNING
120 #endif
121 
122 #ifdef LIBFS_DBG_WARNING
123 #define LIBFS_DBG_ERROR
124 #endif
125 
126 #ifdef LIBFS_DBG_ERROR
127 #define LIBFS_DBG_CRITICAL
128 #endif
129 
130 // End of debug handling.
131 
132 namespace fs
133 {
134 
135  namespace util
136  {
137 
141  tm _localtime(const std::time_t &time)
142  {
143  std::tm tm_snapshot;
144 #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
145  ::localtime_s(&tm_snapshot, &time);
146 #else
147  ::localtime_r(&time, &tm_snapshot); // POSIX
148 #endif
149  return tm_snapshot;
150  }
151 
160  std::string time_tag(std::chrono::system_clock::time_point t)
161  {
162  auto as_time_t = std::chrono::system_clock::to_time_t(t);
163  struct tm tm;
164  char time_buffer[64];
165  // if (::gmtime_r(&as_time_t, &tm)) {
166  tm = _localtime(as_time_t);
167  if (std::strftime(time_buffer, sizeof(time_buffer), "%F %T", &tm))
168  {
169  return std::string{time_buffer};
170  }
171  throw std::runtime_error("Failed to get current date as string");
172  }
173 
175  const std::string LOGTAG_CRITICAL = "CRITICAL";
176 
178  const std::string LOGTAG_ERROR = "ERROR";
179 
181  const std::string LOGTAG_WARNING = "WARNING";
182 
184  const std::string LOGTAG_INFO = "INFO";
185 
187  const std::string LOGTAG_VERBOSE = "VERBOSE";
188 
190  const std::string LOGTAG_EXCESSIVE = "EXCESSIVE";
191 
195  inline void log(std::string const &message, std::string const loglevel = "INFO")
196  {
197  std::cout << LIBFS_APPTAG << "[" << loglevel << "] [" << fs::util::time_tag(std::chrono::system_clock::now()) << "] " << message << "\n";
198  }
199 
208  inline bool ends_with(std::string const &value, std::string const &suffix)
209  {
210  if (suffix.size() > value.size())
211  return false;
212  return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
213  }
214 
223  inline bool ends_with(std::string const &value, std::initializer_list<std::string> suffixes)
224  {
225  for (auto suffix : suffixes)
226  {
227  if (ends_with(value, suffix))
228  {
229  return true;
230  }
231  }
232  return false;
233  }
234 
247  template <typename T>
248  std::vector<std::vector<T>> v2d(std::vector<T> values, size_t num_cols)
249  {
250  std::vector<std::vector<T>> result;
251  for (std::size_t i = 0; i < values.size(); ++i)
252  {
253  if (i % num_cols == 0)
254  {
255  result.resize(result.size() + 1);
256  }
257  result[i / num_cols].push_back(values[i]);
258  }
259  return result;
260  }
261 
272  template <typename T>
273  std::vector<T> vflatten(std::vector<std::vector<T>> values)
274  {
275  size_t total_size = 0;
276  for (std::size_t i = 0; i < values.size(); i++)
277  {
278  total_size += values[i].size();
279  }
280 
281  std::vector<T> result = std::vector<T>(total_size);
282  size_t cur_idx = 0;
283  for (std::size_t i = 0; i < values.size(); i++)
284  {
285  for (std::size_t j = 0; j < values[i].size(); j++)
286  {
287  result[cur_idx] = values[i][j];
288  cur_idx++;
289  }
290  }
291  return result;
292  }
293 
304  inline bool starts_with(std::string const &value, std::string const &prefix)
305  {
306  if (prefix.length() > value.length())
307  return false;
308  return value.rfind(prefix, 0) == 0;
309  }
310 
321  inline bool starts_with(std::string const &value, std::initializer_list<std::string> prefixes)
322  {
323  for (auto prefix : prefixes)
324  {
325  if (starts_with(value, prefix))
326  {
327  return true;
328  }
329  }
330  return false;
331  }
332 
344  inline bool file_exists(const std::string &name)
345  {
346  if (FILE *file = fopen(name.c_str(), "r"))
347  {
348  fclose(file);
349  return true;
350  }
351  else
352  {
353  return false;
354  }
355  }
356 
372  std::string fullpath(std::initializer_list<std::string> path_components, std::string path_sep = std::string("/"))
373  {
374  std::string fp;
375  if (path_components.size() == 0)
376  {
377  throw std::invalid_argument("The 'path_components' must not be empty.");
378  }
379 
380  std::string comp;
381  std::string comp_mod;
382  size_t idx = 0;
383  for (auto comp : path_components)
384  {
385  comp_mod = comp;
386  if (idx != 0)
387  { // We keep a leading slash intact for the first element (absolute path).
388  if (starts_with(comp, path_sep))
389  {
390  comp_mod = comp.substr(1, comp.size() - 1);
391  }
392  }
393 
394  if (ends_with(comp_mod, path_sep))
395  {
396  comp_mod = comp_mod.substr(0, comp_mod.size() - 1);
397  }
398 
399  fp += comp_mod;
400  if (idx < path_components.size() - 1)
401  {
402  fp += path_sep;
403  }
404  idx++;
405  }
406  return fp;
407  }
408 
419  void str_to_file(const std::string &filename, const std::string rep)
420  {
421  std::ofstream ofs;
422  ofs.open(filename, std::ofstream::out);
423 #ifdef LIBFS_DBG_VERBOSE
424  std::cout << LIBFS_APPTAG << "Opening file '" << filename << "' for writing.\n";
425 #endif
426  if (ofs.is_open())
427  {
428  ofs << rep;
429  ofs.close();
430  }
431  else
432  {
433  throw std::runtime_error("Unable to open file '" + filename + "' for writing.\n");
434  }
435  }
436 
475  std::vector<uint8_t> viridis(const std::vector<float> &data, float vmin = NAN, float vmax = NAN, uint8_t nan_r = 255, uint8_t nan_g = 255, uint8_t nan_b = 255)
476  {
477  std::vector<uint8_t> colors;
478  if (data.empty())
479  {
480  return colors;
481  }
482  colors.reserve(data.size() * 3);
483 
484  // The official 256-entry Viridis colormap (RGB, floats in [0, 1]), identical to the
485  // matplotlib viridis lookup table. Linearly interpolated between samples below.
486  static const float lut[768] = {
487  0.267004, 0.004874, 0.329415, 0.26851, 0.009605, 0.335427, 0.269944, 0.014625,
488  0.341379, 0.271305, 0.019942, 0.347269, 0.272594, 0.025563, 0.353093, 0.273809,
489  0.031497, 0.358853, 0.274952, 0.037752, 0.364543, 0.276022, 0.044167, 0.370164,
490  0.277018, 0.050344, 0.375715, 0.277941, 0.056324, 0.381191, 0.278791, 0.062145,
491  0.386592, 0.279566, 0.067836, 0.391917, 0.280267, 0.073417, 0.397163, 0.280894,
492  0.078907, 0.402329, 0.281446, 0.08432, 0.407414, 0.281924, 0.089666, 0.412415,
493  0.282327, 0.094955, 0.417331, 0.282656, 0.100196, 0.42216, 0.28291, 0.105393,
494  0.426902, 0.283091, 0.110553, 0.431554, 0.283197, 0.11568, 0.436115, 0.283229,
495  0.120777, 0.440584, 0.283187, 0.125848, 0.44496, 0.283072, 0.130895, 0.449241,
496  0.282884, 0.13592, 0.453427, 0.282623, 0.140926, 0.457517, 0.28229, 0.145912,
497  0.46151, 0.281887, 0.150881, 0.465405, 0.281412, 0.155834, 0.469201, 0.280868,
498  0.160771, 0.472899, 0.280255, 0.165693, 0.476498, 0.279574, 0.170599, 0.479997,
499  0.278826, 0.17549, 0.483397, 0.278012, 0.180367, 0.486697, 0.277134, 0.185228,
500  0.489898, 0.276194, 0.190074, 0.493001, 0.275191, 0.194905, 0.496005, 0.274128,
501  0.199721, 0.498911, 0.273006, 0.20452, 0.501721, 0.271828, 0.209303, 0.504434,
502  0.270595, 0.214069, 0.507052, 0.269308, 0.218818, 0.509577, 0.267968, 0.223549,
503  0.512008, 0.26658, 0.228262, 0.514349, 0.265145, 0.232956, 0.516599, 0.263663,
504  0.237631, 0.518762, 0.262138, 0.242286, 0.520837, 0.260571, 0.246922, 0.522828,
505  0.258965, 0.251537, 0.524736, 0.257322, 0.25613, 0.526563, 0.255645, 0.260703,
506  0.528312, 0.253935, 0.265254, 0.529983, 0.252194, 0.269783, 0.531579, 0.250425,
507  0.27429, 0.533103, 0.248629, 0.278775, 0.534556, 0.246811, 0.283237, 0.535941,
508  0.244972, 0.287675, 0.53726, 0.243113, 0.292092, 0.538516, 0.241237, 0.296485,
509  0.539709, 0.239346, 0.300855, 0.540844, 0.237441, 0.305202, 0.541921, 0.235526,
510  0.309527, 0.542944, 0.233603, 0.313828, 0.543914, 0.231674, 0.318106, 0.544834,
511  0.229739, 0.322361, 0.545706, 0.227802, 0.326594, 0.546532, 0.225863, 0.330805,
512  0.547314, 0.223925, 0.334994, 0.548053, 0.221989, 0.339161, 0.548752, 0.220057,
513  0.343307, 0.549413, 0.21813, 0.347432, 0.550038, 0.21621, 0.351535, 0.550627,
514  0.214298, 0.355619, 0.551184, 0.212395, 0.359683, 0.55171, 0.210503, 0.363727,
515  0.552206, 0.208623, 0.367752, 0.552675, 0.206756, 0.371758, 0.553117, 0.204903,
516  0.375746, 0.553533, 0.203063, 0.379716, 0.553925, 0.201239, 0.38367, 0.554294,
517  0.19943, 0.387607, 0.554642, 0.197636, 0.391528, 0.554969, 0.19586, 0.395433,
518  0.555276, 0.1941, 0.399323, 0.555565, 0.192357, 0.403199, 0.555836, 0.190631,
519  0.407061, 0.556089, 0.188923, 0.41091, 0.556326, 0.187231, 0.414746, 0.556547,
520  0.185556, 0.41857, 0.556753, 0.183898, 0.422383, 0.556944, 0.182256, 0.426184,
521  0.55712, 0.180629, 0.429975, 0.557282, 0.179019, 0.433756, 0.55743, 0.177423,
522  0.437527, 0.557565, 0.175841, 0.44129, 0.557685, 0.174274, 0.445044, 0.557792,
523  0.172719, 0.448791, 0.557885, 0.171176, 0.45253, 0.557965, 0.169646, 0.456262,
524  0.55803, 0.168126, 0.459988, 0.558082, 0.166617, 0.463708, 0.558119, 0.165117,
525  0.467423, 0.558141, 0.163625, 0.471133, 0.558148, 0.162142, 0.474838, 0.55814,
526  0.160665, 0.47854, 0.558115, 0.159194, 0.482237, 0.558073, 0.157729, 0.485932,
527  0.558013, 0.15627, 0.489624, 0.557936, 0.154815, 0.493313, 0.55784, 0.153364,
528  0.497, 0.557724, 0.151918, 0.500685, 0.557587, 0.150476, 0.504369, 0.55743,
529  0.149039, 0.508051, 0.55725, 0.147607, 0.511733, 0.557049, 0.14618, 0.515413,
530  0.556823, 0.144759, 0.519093, 0.556572, 0.143343, 0.522773, 0.556295, 0.141935,
531  0.526453, 0.555991, 0.140536, 0.530132, 0.555659, 0.139147, 0.533812, 0.555298,
532  0.13777, 0.537492, 0.554906, 0.136408, 0.541173, 0.554483, 0.135066, 0.544853,
533  0.554029, 0.133743, 0.548535, 0.553541, 0.132444, 0.552216, 0.553018, 0.131172,
534  0.555899, 0.552459, 0.129933, 0.559582, 0.551864, 0.128729, 0.563265, 0.551229,
535  0.127568, 0.566949, 0.550556, 0.126453, 0.570633, 0.549841, 0.125394, 0.574318,
536  0.549086, 0.124395, 0.578002, 0.548287, 0.123463, 0.581687, 0.547445, 0.122606,
537  0.585371, 0.546557, 0.121831, 0.589055, 0.545623, 0.121148, 0.592739, 0.544641,
538  0.120565, 0.596422, 0.543611, 0.120092, 0.600104, 0.54253, 0.119738, 0.603785,
539  0.5414, 0.119512, 0.607464, 0.540218, 0.119423, 0.611141, 0.538982, 0.119483,
540  0.614817, 0.537692, 0.119699, 0.61849, 0.536347, 0.120081, 0.622161, 0.534946,
541  0.120638, 0.625828, 0.533488, 0.12138, 0.629492, 0.531973, 0.122312, 0.633153,
542  0.530398, 0.123444, 0.636809, 0.528763, 0.12478, 0.640461, 0.527068, 0.126326,
543  0.644107, 0.525311, 0.128087, 0.647749, 0.523491, 0.130067, 0.651384, 0.521608,
544  0.132268, 0.655014, 0.519661, 0.134692, 0.658636, 0.517649, 0.137339, 0.662252,
545  0.515571, 0.14021, 0.665859, 0.513427, 0.143303, 0.669459, 0.511215, 0.146616,
546  0.67305, 0.508936, 0.150148, 0.676631, 0.506589, 0.153894, 0.680203, 0.504172,
547  0.157851, 0.683765, 0.501686, 0.162016, 0.687316, 0.499129, 0.166383, 0.690856,
548  0.496502, 0.170948, 0.694384, 0.493803, 0.175707, 0.6979, 0.491033, 0.180653,
549  0.701402, 0.488189, 0.185783, 0.704891, 0.485273, 0.19109, 0.708366, 0.482284,
550  0.196571, 0.711827, 0.479221, 0.202219, 0.715272, 0.476084, 0.20803, 0.718701,
551  0.472873, 0.214, 0.722114, 0.469588, 0.220124, 0.725509, 0.466226, 0.226397,
552  0.728888, 0.462789, 0.232815, 0.732247, 0.459277, 0.239374, 0.735588, 0.455688,
553  0.24607, 0.73891, 0.452024, 0.252899, 0.742211, 0.448284, 0.259857, 0.745492,
554  0.444467, 0.266941, 0.748751, 0.440573, 0.274149, 0.751988, 0.436601, 0.281477,
555  0.755203, 0.432552, 0.288921, 0.758394, 0.428426, 0.296479, 0.761561, 0.424223,
556  0.304148, 0.764704, 0.419943, 0.311925, 0.767822, 0.415586, 0.319809, 0.770914,
557  0.411152, 0.327796, 0.77398, 0.40664, 0.335885, 0.777018, 0.402049, 0.344074,
558  0.780029, 0.397381, 0.35236, 0.783011, 0.392636, 0.360741, 0.785964, 0.387814,
559  0.369214, 0.788888, 0.382914, 0.377779, 0.791781, 0.377939, 0.386433, 0.794644,
560  0.372886, 0.395174, 0.797475, 0.367757, 0.404001, 0.800275, 0.362552, 0.412913,
561  0.803041, 0.357269, 0.421908, 0.805774, 0.35191, 0.430983, 0.808473, 0.346476,
562  0.440137, 0.811138, 0.340967, 0.449368, 0.813768, 0.335384, 0.458674, 0.816363,
563  0.329727, 0.468053, 0.818921, 0.323998, 0.477504, 0.821444, 0.318195, 0.487026,
564  0.823929, 0.312321, 0.496615, 0.826376, 0.306377, 0.506271, 0.828786, 0.300362,
565  0.515992, 0.831158, 0.294279, 0.525776, 0.833491, 0.288127, 0.535621, 0.835785,
566  0.281908, 0.545524, 0.838039, 0.275626, 0.555484, 0.840254, 0.269281, 0.565498,
567  0.84243, 0.262877, 0.575563, 0.844566, 0.256415, 0.585678, 0.846661, 0.249897,
568  0.595839, 0.848717, 0.243329, 0.606045, 0.850733, 0.236712, 0.616293, 0.852709,
569  0.230052, 0.626579, 0.854645, 0.223353, 0.636902, 0.856542, 0.21662, 0.647257,
570  0.8584, 0.209861, 0.657642, 0.860219, 0.203082, 0.668054, 0.861999, 0.196293,
571  0.678489, 0.863742, 0.189503, 0.688944, 0.865448, 0.182725, 0.699415, 0.867117,
572  0.175971, 0.709898, 0.868751, 0.169257, 0.720391, 0.87035, 0.162603, 0.730889,
573  0.871916, 0.156029, 0.741388, 0.873449, 0.149561, 0.751884, 0.874951, 0.143228,
574  0.762373, 0.876424, 0.137064, 0.772852, 0.877868, 0.131109, 0.783315, 0.879285,
575  0.125405, 0.79376, 0.880678, 0.120005, 0.804182, 0.882046, 0.114965, 0.814576,
576  0.883393, 0.110347, 0.82494, 0.88472, 0.106217, 0.83527, 0.886029, 0.102646,
577  0.845561, 0.887322, 0.099702, 0.85581, 0.888601, 0.097452, 0.866013, 0.889868,
578  0.095953, 0.876168, 0.891125, 0.09525, 0.886271, 0.892374, 0.095374, 0.89632,
579  0.893616, 0.096335, 0.906311, 0.894855, 0.098125, 0.916242, 0.896091, 0.100717,
580  0.926106, 0.89733, 0.104071, 0.935904, 0.89857, 0.108131, 0.945636, 0.899815,
581  0.112838, 0.9553, 0.901065, 0.118128, 0.964894, 0.902323, 0.123941, 0.974417,
582  0.90359, 0.130215, 0.983868, 0.904867, 0.136897, 0.993248, 0.906157, 0.143936,
583  };
584 
585  const int n = 256;
586 
587  bool auto_min = std::isnan(vmin);
588  bool auto_max = std::isnan(vmax);
589 
590  // Determine the finite (non-NaN) min/max of the data, used for auto range.
591  float data_min = NAN;
592  float data_max = NAN;
593  bool have_finite = false;
594  for (size_t i = 0; i < data.size(); i++)
595  {
596  if (std::isnan(data[i]))
597  {
598  continue;
599  }
600  if (!have_finite)
601  {
602  data_min = data[i];
603  data_max = data[i];
604  have_finite = true;
605  }
606  else
607  {
608  if (data[i] < data_min)
609  {
610  data_min = data[i];
611  }
612  if (data[i] > data_max)
613  {
614  data_max = data[i];
615  }
616  }
617  }
618 
619  float lo = auto_min ? data_min : vmin;
620  float hi = auto_max ? data_max : vmax;
621 
622  if (!auto_min && !auto_max)
623  {
624  if (vmin > vmax)
625  {
626  throw std::invalid_argument("In viridis(): 'vmin' must not be greater than 'vmax'.");
627  }
628  }
629 
630  if (!have_finite)
631  {
632  // All input values are NaN: map the whole vector to the configured NaN color.
633  for (size_t i = 0; i < data.size(); i++)
634  {
635  colors.push_back(nan_r);
636  colors.push_back(nan_g);
637  colors.push_back(nan_b);
638  }
639  return colors;
640  }
641 
642  bool constant = (hi <= lo);
643 
644  for (size_t i = 0; i < data.size(); i++)
645  {
646  if (std::isnan(data[i]))
647  {
648  colors.push_back(nan_r);
649  colors.push_back(nan_g);
650  colors.push_back(nan_b);
651  continue;
652  }
653 
654  float t;
655  if (constant)
656  {
657  t = 0.5f;
658  }
659  else
660  {
661  t = (data[i] - lo) / (hi - lo);
662  if (t < 0.0f) { t = 0.0f; }
663  if (t > 1.0f) { t = 1.0f; }
664  }
665 
666  float pos = t * (n - 1);
667  int idx0 = static_cast<int>(pos);
668  if (idx0 < 0) { idx0 = 0; }
669  if (idx0 > n - 2) { idx0 = n - 2; }
670  int idx1 = idx0 + 1;
671  float frac = pos - static_cast<float>(idx0);
672 
673  for (int c = 0; c < 3; c++)
674  {
675  float val = lut[idx0 * 3 + c] * (1.0f - frac) + lut[idx1 * 3 + c] * frac;
676  int iv = static_cast<int>(val * 255.0f + 0.5f);
677  if (iv < 0) { iv = 0; }
678  if (iv > 255) { iv = 255; }
679  colors.push_back(static_cast<uint8_t>(iv));
680  }
681  }
682  return colors;
683  }
684  } // End namespace util.
685 
686  // MRI data types, used by the MGH functions.
687 
689  const int MRI_UCHAR = 0;
690 
692  const int MRI_INT = 1;
693 
695  const int MRI_FLOAT = 3;
696 
698  const int MRI_SHORT = 4;
699 
700  // Forward declarations.
701  int _fread3(std::istream &);
702  template <typename T>
703  T _freadt(std::istream &);
704  std::string _freadstringnewline(std::istream &);
705  std::string _freadfixedlengthstring(std::istream &, size_t, bool);
706  bool _ends_with(std::string const &fullString, std::string const &ending);
707  size_t _vidx_2d(size_t, size_t, size_t);
708  struct MghHeader;
709 
727  struct Mesh
728  {
729 
731  Mesh(std::vector<float> cvertices, std::vector<int32_t> cfaces)
732  {
733  vertices = cvertices;
734  faces = cfaces;
735  }
736 
737  // Construct from 2D vectors (Nx3).
738  Mesh(std::vector<std::vector<float>> cvertices, std::vector<std::vector<int32_t>> cfaces)
739  {
740  vertices = util::vflatten(cvertices);
741  faces = util::vflatten(cfaces);
742  }
743 
745  Mesh() {}
746 
747  std::vector<float> vertices;
748  std::vector<int32_t> faces;
749 
761  {
762  fs::Mesh mesh;
763  mesh.vertices = {1.0, 1.0, 1.0,
764  1.0, 1.0, -1.0,
765  1.0, -1.0, 1.0,
766  1.0, -1.0, -1.0,
767  -1.0, 1.0, 1.0,
768  -1.0, 1.0, -1.0,
769  -1.0, -1.0, 1.0,
770  -1.0, -1.0, -1.0};
771  mesh.faces = {0, 2, 3,
772  3, 1, 0,
773  4, 6, 7,
774  7, 5, 4,
775  0, 4, 5,
776  5, 1, 0,
777  2, 6, 7,
778  7, 3, 2,
779  0, 4, 6,
780  6, 2, 0,
781  1, 5, 7,
782  7, 3, 1};
783  return mesh;
784  }
785 
798  {
799  fs::Mesh mesh;
800  mesh.vertices = {0.0, 0.0, 0.0, // start with 4x base
801  0.0, 1.0, 0.0,
802  1.0, 1.0, 0.0,
803  1.0, 0.0, 0.0,
804  0.5, 0.5, 1.0}; // apex
805  mesh.faces = {0, 2, 1, // start with 2 base faces
806  0, 3, 2,
807  0, 4, 1, // now the 4 wall faces
808  1, 4, 2,
809  3, 2, 4,
810  0, 3, 4};
811  return mesh;
812  }
813 
830  static fs::Mesh construct_grid(const size_t nx = 4, const size_t ny = 5, const float distx = 1.0, const float disty = 1.0)
831  {
832  if (nx < 2 || ny < 2)
833  {
834  throw std::runtime_error("Parameters nx and ny must be at least 2.");
835  }
836  fs::Mesh mesh;
837  size_t num_vertices = nx * ny;
838  size_t num_faces = ((nx - 1) * (ny - 1)) * 2;
839  std::vector<float> vertices;
840  vertices.reserve(num_vertices * 3);
841  std::vector<int> faces;
842  faces.reserve(num_faces * 3);
843 
844  // Create vertices.
845  float cur_x, cur_y, cur_z;
846  cur_x = cur_y = cur_z = 0.0;
847  for (size_t i = 0; i < nx; i++)
848  {
849  for (size_t j = 0; j < ny; j++)
850  {
851  vertices.push_back(cur_x);
852  vertices.push_back(cur_y);
853  vertices.push_back(cur_z);
854  cur_y += disty;
855  }
856  cur_x += distx;
857  }
858 
859  // Create faces.
860  for (size_t i = 0; i < num_vertices; i++)
861  {
862  if ((i + 1) % ny == 0 || i >= num_vertices - ny)
863  {
864  // Do not use the last ones in row or column as source.
865  continue;
866  }
867  // Add the upper left triangle of this grid cell.
868  faces.push_back(int(i));
869  faces.push_back(int(i + ny + 1));
870  faces.push_back(int(i + 1));
871  // Add the lower right triangle of this grid cell.
872  faces.push_back(int(i));
873  faces.push_back(int(i + ny + 1));
874  faces.push_back(int(i + ny));
875  }
876 
877  mesh.vertices = vertices;
878  mesh.faces = faces;
879  return mesh;
880  }
881 
892  std::string to_obj() const
893  {
894  std::stringstream objs;
895  for (size_t vidx = 0; vidx < this->vertices.size(); vidx += 3)
896  { // vertex coords
897  objs << "v " << vertices[vidx] << " " << vertices[vidx + 1] << " " << vertices[vidx + 2] << "\n";
898  }
899  for (size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
900  { // faces: vertex indices, 1-based
901  objs << "f " << faces[fidx] + 1 << " " << faces[fidx + 1] + 1 << " " << faces[fidx + 2] + 1 << "\n";
902  }
903  return (objs.str());
904  }
905 
917  std::vector<std::vector<bool>> as_adjmatrix() const
918  {
919  std::vector<std::vector<bool>> adjm = std::vector<std::vector<bool>>(this->num_vertices(), std::vector<bool>(this->num_vertices(), false));
920  for (size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
921  { // faces: vertex indices
922  adjm[faces[fidx]][faces[fidx + 1]] = true;
923  adjm[faces[fidx + 1]][faces[fidx]] = true;
924  adjm[faces[fidx + 1]][faces[fidx + 2]] = true;
925  adjm[faces[fidx + 2]][faces[fidx + 1]] = true;
926  adjm[faces[fidx + 2]][faces[fidx]] = true;
927  adjm[faces[fidx]][faces[fidx + 2]] = true;
928  }
929  return adjm;
930  }
931 
933  struct _tupleHashFunction
934  {
935  size_t operator()(const std::tuple<size_t, size_t> &x) const
936  {
937  return std::get<0>(x) ^ std::get<1>(x);
938  }
939  };
940 
943  typedef std::unordered_set<std::tuple<size_t, size_t>, _tupleHashFunction> edge_set;
944 
956  edge_set as_edgelist() const
957  {
958  edge_set edges;
959  for (size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
960  { // faces: vertex indices
961  edges.insert(std::make_tuple(faces[fidx], faces[fidx + 1]));
962  edges.insert(std::make_tuple(faces[fidx + 1], faces[fidx]));
963 
964  edges.insert(std::make_tuple(faces[fidx + 1], faces[fidx + 2]));
965  edges.insert(std::make_tuple(faces[fidx + 2], faces[fidx + 1]));
966 
967  edges.insert(std::make_tuple(faces[fidx], faces[fidx + 2]));
968  edges.insert(std::make_tuple(faces[fidx + 2], faces[fidx]));
969  }
970  return edges;
971  }
972 
985  std::vector<std::vector<size_t>> as_adjlist(const bool via_matrix = true) const
986  {
987  if (!via_matrix)
988  {
989  return (this->_as_adjlist_via_edgeset());
990  }
991  std::vector<std::vector<bool>> adjm = this->as_adjmatrix();
992  std::vector<std::vector<size_t>> adjl = std::vector<std::vector<size_t>>(this->num_vertices(), std::vector<size_t>());
993  size_t nv = adjm.size();
994  for (size_t i = 0; i < nv; i++)
995  {
996  for (size_t j = i + 1; j < nv; j++)
997  {
998  if (adjm[i][j] == true)
999  {
1000  adjl[i].push_back(j);
1001  adjl[j].push_back(i);
1002  }
1003  }
1004  }
1005  return adjl;
1006  }
1007 
1018  std::vector<std::vector<size_t>> _as_adjlist_via_edgeset() const
1019  {
1020  edge_set edges = this->as_edgelist();
1021  std::vector<std::vector<size_t>> adjl = std::vector<std::vector<size_t>>(this->num_vertices(), std::vector<size_t>());
1022  for (const std::tuple<size_t, size_t> &e : edges)
1023  {
1024  adjl[std::get<0>(e)].push_back(std::get<1>(e));
1025  }
1026  return adjl;
1027  }
1028 
1044  std::vector<float> smooth_pvd_nn(const std::vector<float> pvd, const size_t num_iter = 1, const bool via_matrix = true, const bool with_nan = true, const bool detect_nan = true) const
1045  {
1046 
1047  const std::vector<std::vector<size_t>> adjlist = this->as_adjlist(via_matrix);
1048  return fs::Mesh::smooth_pvd_nn(adjlist, pvd, num_iter, with_nan, detect_nan);
1049  }
1050 
1067  static std::vector<float> smooth_pvd_nn(const std::vector<std::vector<size_t>> mesh_adj, const std::vector<float> pvd, const size_t num_iter = 1, const bool with_nan = true, const bool detect_nan = true)
1068  {
1069  assert(pvd.size() == mesh_adj.size());
1070  bool final_with_nan = with_nan;
1071  if (detect_nan)
1072  {
1073  final_with_nan = false;
1074  for (size_t i = 0; i < pvd.size(); i++)
1075  {
1076  if (std::isnan(pvd[i]))
1077  {
1078  final_with_nan = true;
1079  break;
1080  }
1081  }
1082  }
1083  if (final_with_nan)
1084  {
1085  return fs::Mesh::_smooth_pvd_nn_nan(mesh_adj, pvd, num_iter);
1086  }
1087  std::vector<float> current_pvd_source;
1088  std::vector<float> current_pvd_smoothed = std::vector<float>(pvd.size());
1089 
1090  float val_sum;
1091  size_t num_neigh;
1092  for (size_t i = 0; i < num_iter; i++)
1093  {
1094  if (i == 0)
1095  {
1096  current_pvd_source = pvd;
1097  }
1098  else
1099  {
1100  current_pvd_source = current_pvd_smoothed;
1101  }
1102  for (size_t v_idx = 0; v_idx < mesh_adj.size(); v_idx++)
1103  {
1104  num_neigh = mesh_adj[v_idx].size();
1105  val_sum = current_pvd_source[v_idx] / (num_neigh + 1);
1106  for (size_t neigh_rel_idx = 0; neigh_rel_idx < num_neigh; neigh_rel_idx++)
1107  {
1108  val_sum += current_pvd_source[mesh_adj[v_idx][neigh_rel_idx]] / (num_neigh + 1);
1109  }
1110  current_pvd_smoothed[v_idx] = val_sum;
1111  }
1112  }
1113  return current_pvd_smoothed;
1114  }
1115 
1132  static std::vector<float> _smooth_pvd_nn_nan(const std::vector<std::vector<size_t>> mesh_adj, const std::vector<float> pvd, const size_t num_iter = 1)
1133  {
1134  std::vector<float> current_pvd_source;
1135  std::vector<float> current_pvd_smoothed = std::vector<float>(pvd.size());
1136 
1137  float val_sum;
1138  size_t num_neigh;
1139  size_t num_non_nan_values;
1140  float neigh_val;
1141  for (size_t i = 0; i < num_iter; i++)
1142  {
1143 
1144  if (i == 0)
1145  {
1146  current_pvd_source = pvd;
1147  }
1148  else
1149  {
1150  current_pvd_source = current_pvd_smoothed;
1151  }
1152 
1153  for (size_t v_idx = 0; v_idx < mesh_adj.size(); v_idx++)
1154  {
1155  if (std::isnan(current_pvd_source[v_idx]))
1156  {
1157  current_pvd_smoothed[v_idx] = NAN;
1158  continue;
1159  }
1160  val_sum = current_pvd_source[v_idx];
1161  num_non_nan_values = 1; // If we get here, the source vertex value is not NAN.
1162  num_neigh = mesh_adj[v_idx].size();
1163  for (size_t neigh_rel_idx = 0; neigh_rel_idx < num_neigh; neigh_rel_idx++)
1164  {
1165  neigh_val = current_pvd_source[mesh_adj[v_idx][neigh_rel_idx]];
1166  if (std::isnan(neigh_val))
1167  {
1168  continue;
1169  }
1170  else
1171  {
1172  val_sum += neigh_val;
1173  num_non_nan_values++;
1174  }
1175  }
1176  current_pvd_smoothed[v_idx] = val_sum / (float)num_non_nan_values;
1177  }
1178  }
1179  return current_pvd_smoothed;
1180  }
1181 
1188  static std::vector<std::vector<size_t>> extend_adj(const std::vector<std::vector<size_t>> mesh_adj, const size_t extend_by = 1, std::vector<std::vector<size_t>> mesh_adj_ext = std::vector<std::vector<size_t>>())
1189  {
1190  size_t num_vertices = mesh_adj.size();
1191  if (mesh_adj_ext.size() == 0)
1192  {
1193  mesh_adj_ext = mesh_adj;
1194  }
1195  std::vector<size_t> neighborhood;
1196  std::vector<size_t> ext_neighborhood;
1197  for (size_t ext_idx = 0; ext_idx < extend_by; ext_idx++)
1198  {
1199  for (size_t source_vert_idx = 0; source_vert_idx < num_vertices; source_vert_idx++)
1200  {
1201  neighborhood = mesh_adj_ext[source_vert_idx]; // copy needed so we do not modify during iteration.
1202  // Extension: add all neighbors in distance one for all vertices in the neighborhood.
1203  for (size_t neigh_vert_rel_idx = 0; neigh_vert_rel_idx < neighborhood.size(); neigh_vert_rel_idx++)
1204  {
1205  for (size_t canidate_rel_idx = 0; canidate_rel_idx < mesh_adj[neighborhood[neigh_vert_rel_idx]].size(); canidate_rel_idx++)
1206  {
1207  if (mesh_adj[neighborhood[neigh_vert_rel_idx]][canidate_rel_idx] != source_vert_idx)
1208  {
1209  mesh_adj_ext[source_vert_idx].push_back(mesh_adj[neighborhood[neigh_vert_rel_idx]][canidate_rel_idx]);
1210  }
1211  }
1212  }
1213  // We need to remove duplicates.
1214  std::sort(mesh_adj_ext[source_vert_idx].begin(), mesh_adj_ext[source_vert_idx].end());
1215  mesh_adj_ext[source_vert_idx].erase(std::unique(mesh_adj_ext[source_vert_idx].begin(), mesh_adj_ext[source_vert_idx].end()), mesh_adj_ext[source_vert_idx].end());
1216  }
1217  }
1218  return mesh_adj_ext;
1219  }
1220 
1233  void to_obj_file(const std::string &filename) const
1234  {
1235  fs::util::str_to_file(filename, this->to_obj());
1236  }
1237 
1254  std::pair<std::unordered_map<int32_t, int32_t>, fs::Mesh> submesh_vertex(const std::vector<int32_t> &old_vertex_indices, const bool mapdir_fulltosubmesh = false) const
1255  {
1256  fs::Mesh submesh;
1257  std::vector<float> new_vertices;
1258  std::vector<int> new_faces;
1259  std::unordered_map<int32_t, int32_t> vertex_index_map_full2submesh;
1260  int32_t new_vertex_idx = 0;
1261  for (size_t i = 0; i < old_vertex_indices.size(); i++)
1262  {
1263  vertex_index_map_full2submesh[old_vertex_indices[i]] = new_vertex_idx;
1264  new_vertices.push_back(this->vertices[size_t(old_vertex_indices[i]) * 3]);
1265  new_vertices.push_back(this->vertices[size_t(old_vertex_indices[i]) * 3 + 1]);
1266  new_vertices.push_back(this->vertices[size_t(old_vertex_indices[i]) * 3 + 2]);
1267  new_vertex_idx++;
1268  }
1269  int face_v0;
1270  int face_v1;
1271  int face_v2;
1272  for (size_t i = 0; i < this->num_faces(); i++)
1273  {
1274  face_v0 = this->faces[i * 3];
1275  face_v1 = this->faces[i * 3 + 1];
1276  face_v2 = this->faces[i * 3 + 2];
1277  if ((vertex_index_map_full2submesh.find(face_v0) != vertex_index_map_full2submesh.end()) && (vertex_index_map_full2submesh.find(face_v1) != vertex_index_map_full2submesh.end()) && (vertex_index_map_full2submesh.find(face_v2) != vertex_index_map_full2submesh.end()))
1278  {
1279  new_faces.push_back(vertex_index_map_full2submesh[face_v0]);
1280  new_faces.push_back(vertex_index_map_full2submesh[face_v1]);
1281  new_faces.push_back(vertex_index_map_full2submesh[face_v2]);
1282  }
1283  }
1284  submesh.vertices = new_vertices;
1285  submesh.faces = new_faces;
1286 
1287  std::pair<std::unordered_map<int32_t, int32_t>, fs::Mesh> result;
1288  if (!mapdir_fulltosubmesh)
1289  { // Compute the new2old (reverse) vertex index map:
1290  std::unordered_map<int32_t, int32_t> vertex_index_map_submesh2full;
1291  for (auto const &pair : vertex_index_map_full2submesh)
1292  {
1293  vertex_index_map_submesh2full[pair.second] = pair.first;
1294  }
1295  result = std::pair<std::unordered_map<int32_t, int32_t>, fs::Mesh>(vertex_index_map_submesh2full, submesh);
1296  }
1297  else
1298  {
1299  result = std::pair<std::unordered_map<int32_t, int32_t>, fs::Mesh>(vertex_index_map_full2submesh, submesh);
1300  }
1301 
1302  return result;
1303  }
1304 
1311  static std::vector<float> curv_data_for_orig_mesh(const std::vector<float> data_submesh, const std::unordered_map<int32_t, int32_t> submesh_to_orig_mapping, const int32_t orig_mesh_num_vertices, const float fill_value = std::numeric_limits<float>::quiet_NaN())
1312  {
1313 
1314  if (submesh_to_orig_mapping.size() != data_submesh.size())
1315  {
1316  throw std::domain_error("The number of vertices of the submesh and the number of values in the submesh_to_orig_mapping do not match: got " + std::to_string(data_submesh.size()) + " and " + std::to_string(submesh_to_orig_mapping.size()) + ".");
1317  }
1318 
1319  std::vector<float> data_orig_mesh(orig_mesh_num_vertices, fill_value);
1320  for (size_t i = 0; i < data_submesh.size(); i++)
1321  {
1322  auto got = submesh_to_orig_mapping.find(int(i));
1323  if (got != submesh_to_orig_mapping.end())
1324  {
1325  data_orig_mesh[got->second] = data_submesh[i];
1326  }
1327  }
1328  return (data_orig_mesh);
1329  }
1330 
1345  static void from_obj(Mesh *mesh, std::istream *is)
1346  {
1347  std::string line;
1348  int line_idx = -1;
1349 
1350  std::vector<float> vertices;
1351  std::vector<int> faces;
1352 
1353 #ifdef LIBFS_DBG_INFO
1354  size_t num_lines_ignored = 0; // Not comments, but custom extensions or material data lines which are ignored by libfs.
1355 #endif
1356 
1357  while (std::getline(*is, line))
1358  {
1359  line_idx += 1;
1360  std::istringstream iss(line);
1361  if (fs::util::starts_with(line, "#"))
1362  {
1363  continue; // skip comment.
1364  }
1365  else
1366  {
1367  if (fs::util::starts_with(line, "v "))
1368  {
1369  std::string elem_type_identifier;
1370  float x, y, z;
1371  if (!(iss >> elem_type_identifier >> x >> y >> z))
1372  {
1373  throw std::domain_error("Could not parse vertex line " + std::to_string(line_idx + 1) + " of OBJ data, invalid format.\n");
1374  }
1375  assert(elem_type_identifier == "v");
1376  vertices.push_back(x);
1377  vertices.push_back(y);
1378  vertices.push_back(z);
1379  }
1380  else if (fs::util::starts_with(line, "f "))
1381  {
1382  std::string elem_type_identifier, v0raw, v1raw, v2raw;
1383  int v0, v1, v2;
1384  if (!(iss >> elem_type_identifier >> v0raw >> v1raw >> v2raw))
1385  {
1386  throw std::domain_error("Could not parse face line " + std::to_string(line_idx + 1) + " of OBJ data, invalid format.\n");
1387  }
1388  assert(elem_type_identifier == "f");
1389 
1390  // The OBJ format allows to specifiy face indices with slashes to also set normal and material indices.
1391  // So instead of a line like 'f 22 34 45', we could get 'f 3/1 4/2 5/3' or 'f 6/4/1 3/5/3 7/6/5' or 'f 7//1 8//2 9//3'.
1392  // We need to extract the stuff before the first slash and interprete it as int to get the vertex index we are looking for.
1393  std::size_t found_v0 = v0raw.find("/");
1394  std::size_t found_v1 = v1raw.find("/");
1395  std::size_t found_v2 = v2raw.find("/");
1396  if (found_v0 != std::string::npos)
1397  {
1398  v0raw = v0raw.substr(0, found_v0);
1399  }
1400  if (found_v1 != std::string::npos)
1401  {
1402  v1raw = v1raw.substr(0, found_v1);
1403  }
1404  if (found_v2 != std::string::npos)
1405  {
1406  v2raw = v0raw.substr(0, found_v2);
1407  }
1408  v0 = std::stoi(v0raw);
1409  v1 = std::stoi(v1raw);
1410  v2 = std::stoi(v2raw);
1411 
1412  // The vertex indices in Wavefront OBJ files are 1-based, so we have to substract 1 here.
1413  faces.push_back(v0 - 1);
1414  faces.push_back(v1 - 1);
1415  faces.push_back(v2 - 1);
1416  }
1417  else
1418  {
1419 #ifdef LIBFS_DBG_INFO
1420  num_lines_ignored++;
1421 #endif
1422 
1423  continue;
1424  }
1425  }
1426  }
1427 #ifdef LIBFS_DBG_INFO
1428  if (num_lines_ignored > 0)
1429  {
1430  std::cout << LIBFS_APPTAG << "Ignored " << num_lines_ignored << " lines in Wavefront OBJ format mesh file.\n";
1431  }
1432 #endif
1433  mesh->vertices = vertices;
1434  mesh->faces = faces;
1435  }
1436 
1451  static void from_obj(Mesh *mesh, const std::string &filename)
1452  {
1453 #ifdef LIBFS_DBG_INFO
1454  std::cout << LIBFS_APPTAG << "Reading brain mesh from Wavefront object format file " << filename << ".\n";
1455 #endif
1456  std::ifstream input(filename, std::fstream::in);
1457  if (input.is_open())
1458  {
1459  Mesh::from_obj(mesh, &input);
1460  input.close();
1461  }
1462  else
1463  {
1464  throw std::runtime_error("Could not open Wavefront object format mesh file '" + filename + "' for reading.\n");
1465  }
1466  }
1467 
1474  static void from_off(Mesh *mesh, std::istream *is, const std::string &source_filename = "")
1475  {
1476 
1477  std::string msg_source_file_part = source_filename.empty() ? "" : "'" + source_filename + "'";
1478 
1479  std::string line;
1480  int line_idx = -1;
1481  int noncomment_line_idx = -1;
1482 
1483  std::vector<float> vertices;
1484  std::vector<int> faces;
1485  size_t num_vertices = 0;
1486  size_t num_faces = 0;
1487  size_t num_edges = 0;
1488  size_t num_verts_parsed = 0;
1489  size_t num_faces_parsed = 0;
1490  float x, y, z; // vertex xyz coords
1491  // bool has_color;
1492  // int r, g, b, a; // vertex colors
1493  int num_verts_this_face, v0, v1, v2; // face, defined by number of vertices and vertex indices.
1494 
1495  while (std::getline(*is, line))
1496  {
1497  line_idx++;
1498  std::istringstream iss(line);
1499  if (fs::util::starts_with(line, "#"))
1500  {
1501  continue; // skip comment.
1502  }
1503  else
1504  {
1505  noncomment_line_idx++;
1506  if (noncomment_line_idx == 0)
1507  {
1508  std::string off_header_magic;
1509  if (!(iss >> off_header_magic))
1510  {
1511  throw std::domain_error("Could not parse first header line " + std::to_string(line_idx + 1) + " of OFF data, invalid format.\n");
1512  }
1513  if (!(off_header_magic == "OFF" || off_header_magic == "COFF"))
1514  {
1515  throw std::domain_error("OFF magic string invalid, file " + msg_source_file_part + " not in OFF format.\n");
1516  }
1517  // has_color = off_header_magic == "COFF";
1518  }
1519  else if (noncomment_line_idx == 1)
1520  {
1521  if (!(iss >> num_vertices >> num_faces >> num_edges))
1522  {
1523  throw std::domain_error("Could not parse element count header line " + std::to_string(line_idx + 1) + " of OFF data " + msg_source_file_part + ", invalid format.\n");
1524  }
1525  }
1526  else
1527  {
1528 
1529  if (num_verts_parsed < num_vertices)
1530  {
1531  if (!(iss >> x >> y >> z))
1532  {
1533  throw std::domain_error("Could not parse vertex coordinate line " + std::to_string(line_idx + 1) + " of OFF data " + msg_source_file_part + ", invalid format.\n");
1534  }
1535  vertices.push_back(x);
1536  vertices.push_back(y);
1537  vertices.push_back(z);
1538  num_verts_parsed++;
1539  }
1540  else
1541  {
1542  if (num_faces_parsed < num_faces)
1543  {
1544  if (!(iss >> num_verts_this_face >> v0 >> v1 >> v2))
1545  {
1546  throw std::domain_error("Could not parse face line " + std::to_string(line_idx + 1) + " of OFF data " + msg_source_file_part + ", invalid format.\n");
1547  }
1548  if (num_verts_this_face != 3)
1549  {
1550  throw std::domain_error("At OFF data " + msg_source_file_part + " line " + std::to_string(line_idx + 1) + ": only triangular meshes supported.\n");
1551  }
1552  faces.push_back(v0);
1553  faces.push_back(v1);
1554  faces.push_back(v2);
1555  num_faces_parsed++;
1556  }
1557  }
1558  }
1559  }
1560  }
1561  if (num_verts_parsed < num_vertices)
1562  {
1563  throw std::domain_error("Vertex count mismatch between OFF data " + msg_source_file_part + " header (" + std::to_string(num_vertices) + ") and data (" + std::to_string(num_verts_parsed) + ").\n");
1564  }
1565  if (num_faces_parsed < num_faces)
1566  {
1567  throw std::domain_error("Face count mismatch between OFF data " + msg_source_file_part + " header (" + std::to_string(num_faces) + ") and data (" + std::to_string(num_faces_parsed) + ").\n");
1568  }
1569  mesh->vertices = vertices;
1570  mesh->faces = faces;
1571  }
1572 
1587  static void from_off(Mesh *mesh, const std::string &filename)
1588  {
1589 #ifdef LIBFS_DBG_INFO
1590  std::cout << LIBFS_APPTAG << "Reading brain mesh from OFF format file " << filename << ".\n";
1591 #endif
1592  std::ifstream input(filename, std::fstream::in);
1593  if (input.is_open())
1594  {
1595  Mesh::from_off(mesh, &input);
1596  input.close();
1597  }
1598  else
1599  {
1600  throw std::runtime_error("Could not open Object file format (OFF) mesh file '" + filename + "' for reading.\n");
1601  }
1602  }
1603 
1609  static void from_ply(Mesh *mesh, std::istream *is)
1610  {
1611  std::string line;
1612  int line_idx = -1;
1613  int noncomment_line_idx = -1;
1614 
1615  std::vector<float> vertices;
1616  std::vector<int> faces;
1617 
1618  bool in_header = true; // current status
1619  int num_verts = -1;
1620  int num_faces = -1;
1621  while (std::getline(*is, line))
1622  {
1623  line_idx += 1;
1624  std::istringstream iss(line);
1625  if (fs::util::starts_with(line, "comment"))
1626  {
1627  continue; // skip comment.
1628  }
1629  else
1630  {
1631  noncomment_line_idx++;
1632  if (in_header)
1633  {
1634  if (noncomment_line_idx == 0)
1635  {
1636  if (line != "ply")
1637  throw std::domain_error("Invalid PLY file");
1638  }
1639  else if (noncomment_line_idx == 1)
1640  {
1641  if (line != "format ascii 1.0")
1642  throw std::domain_error("Unsupported PLY file format, only format 'format ascii 1.0' is supported.");
1643  }
1644 
1645  if (line == "end_header")
1646  {
1647  in_header = false;
1648  }
1649  else if (fs::util::starts_with(line, "element vertex"))
1650  {
1651  std::string elem, elem_type_identifier;
1652  if (!(iss >> elem >> elem_type_identifier >> num_verts))
1653  {
1654  throw std::domain_error("Could not parse element vertex line of PLY header, invalid format.\n");
1655  }
1656  }
1657  else if (fs::util::starts_with(line, "element face"))
1658  {
1659  std::string elem, elem_type_identifier;
1660  if (!(iss >> elem >> elem_type_identifier >> num_faces))
1661  {
1662  throw std::domain_error("Could not parse element face line of PLY header, invalid format.\n");
1663  }
1664  } // Other properties like vertex colors and normals are ignored for now.
1665  }
1666  else
1667  { // in data part.
1668  if (num_verts < 1 || num_faces < 1)
1669  {
1670  throw std::domain_error("Invalid PLY file: missing element count lines of header.");
1671  }
1672  // Read vertices
1673  if (vertices.size() < (size_t)num_verts * 3)
1674  {
1675  float x, y, z;
1676  if (!(iss >> x >> y >> z))
1677  {
1678  throw std::domain_error("Could not parse vertex line " + std::to_string(line_idx) + " of PLY data, invalid format.\n");
1679  }
1680  vertices.push_back(x);
1681  vertices.push_back(y);
1682  vertices.push_back(z);
1683  }
1684  else
1685  {
1686  if (faces.size() < (size_t)num_faces * 3)
1687  {
1688  int verts_per_face, v0, v1, v2;
1689  if (!(iss >> verts_per_face >> v0 >> v1 >> v2))
1690  {
1691  throw std::domain_error("Could not parse face line " + std::to_string(line_idx) + " of PLY data, invalid format.\n");
1692  }
1693  if (verts_per_face != 3)
1694  {
1695  throw std::domain_error("Only triangular meshes are supported: PLY faces lines must contain exactly 3 vertex indices.\n");
1696  }
1697  faces.push_back(v0);
1698  faces.push_back(v1);
1699  faces.push_back(v2);
1700  }
1701  }
1702  }
1703  }
1704  }
1705  if (vertices.size() != (size_t)num_verts * 3)
1706  {
1707  std::cerr << "PLY header mentions " << num_verts << " vertices, but found " << vertices.size() / 3 << ".\n";
1708  }
1709  if (faces.size() != (size_t)num_faces * 3)
1710  {
1711  std::cerr << "PLY header mentions " << num_faces << " faces, but found " << faces.size() / 3 << ".\n";
1712  }
1713  mesh->vertices = vertices;
1714  mesh->faces = faces;
1715  }
1716 
1730  static void from_ply(Mesh *mesh, const std::string &filename)
1731  {
1732 #ifdef LIBFS_DBG_INFO
1733  std::cout << LIBFS_APPTAG << "Reading brain mesh from PLY format file " << filename << ".\n";
1734 #endif
1735  std::ifstream input(filename, std::fstream::in);
1736  if (input.is_open())
1737  {
1738  Mesh::from_ply(mesh, &input);
1739  input.close();
1740  }
1741  else
1742  {
1743  throw std::runtime_error("Could not open Stanford PLY format mesh file '" + filename + "' for reading.\n");
1744  }
1745  }
1746 
1756  size_t num_vertices() const
1757  {
1758  return (this->vertices.size() / 3);
1759  }
1760 
1770  size_t num_faces() const
1771  {
1772  return (this->faces.size() / 3);
1773  }
1774 
1787  const int32_t &fm_at(const size_t i, const size_t j) const
1788  {
1789  size_t idx = _vidx_2d(i, j, 3);
1790  if (idx > this->faces.size() - 1)
1791  {
1792  throw std::range_error("Indices (" + std::to_string(i) + "," + std::to_string(j) + ") into Mesh.faces out of bounds. Hit " + std::to_string(idx) + " with max valid index " + std::to_string(this->faces.size() - 1) + ".\n");
1793  }
1794  return (this->faces[idx]);
1795  }
1796 
1808  std::vector<int32_t> face_vertices(const size_t face) const
1809  {
1810  if (face > this->num_faces() - 1)
1811  {
1812  throw std::range_error("Index " + std::to_string(face) + " into Mesh.faces out of bounds, max valid index is " + std::to_string(this->num_faces() - 1) + ".\n");
1813  }
1814  std::vector<int32_t> fv(3);
1815  fv[0] = this->fm_at(face, 0);
1816  fv[1] = this->fm_at(face, 1);
1817  fv[2] = this->fm_at(face, 2);
1818  return (fv);
1819  }
1820 
1832  std::vector<float> vertex_coords(const size_t vertex) const
1833  {
1834  if (vertex > this->num_vertices() - 1)
1835  {
1836  throw std::range_error("Index " + std::to_string(vertex) + " into Mesh.vertices out of bounds, max valid index is " + std::to_string(this->num_vertices() - 1) + ".\n");
1837  }
1838  std::vector<float> vc(3);
1839  vc[0] = this->vm_at(vertex, 0);
1840  vc[1] = this->vm_at(vertex, 1);
1841  vc[2] = this->vm_at(vertex, 2);
1842  return (vc);
1843  }
1844 
1858  const float &vm_at(const size_t i, const size_t j) const
1859  {
1860  size_t idx = _vidx_2d(i, j, 3);
1861  if (idx > this->vertices.size() - 1)
1862  {
1863  throw std::range_error("Indices (" + std::to_string(i) + "," + std::to_string(j) + ") into Mesh.vertices out of bounds. Hit " + std::to_string(idx) + " with max valid index " + std::to_string(this->vertices.size() - 1) + ".\n");
1864  }
1865  return (this->vertices[idx]);
1866  }
1867 
1876  std::string to_ply() const
1877  {
1878  std::vector<uint8_t> empty_col;
1879  return (this->to_ply(empty_col));
1880  }
1881 
1892  std::string to_ply(const std::vector<uint8_t> col) const
1893  {
1894  bool use_vertex_colors = col.size() != 0;
1895  std::stringstream plys;
1896  plys << "ply\nformat ascii 1.0\n";
1897  plys << "element vertex " << this->num_vertices() << "\n";
1898  plys << "property float x\nproperty float y\nproperty float z\n";
1899  if (use_vertex_colors)
1900  {
1901  if (col.size() != this->vertices.size())
1902  {
1903  throw std::invalid_argument("Number of vertex coordinates and vertex colors must match when writing PLY file, but got " + std::to_string(this->vertices.size()) + " and " + std::to_string(col.size()) + ".");
1904  }
1905  plys << "property uchar red\nproperty uchar green\nproperty uchar blue\n";
1906  }
1907  plys << "element face " << this->num_faces() << "\n";
1908  plys << "property list uchar int vertex_index\n";
1909  plys << "end_header\n";
1910 
1911 #ifdef LIBFS_DBG_DEBUG
1912  fs::util::log("Writing " + std::to_string(this->vertices.size() / 3) + " PLY format vertices.", "INFO");
1913 #endif
1914 
1915  for (size_t vidx = 0; vidx < this->vertices.size(); vidx += 3)
1916  { // vertex coords
1917  plys << vertices[vidx] << " " << vertices[vidx + 1] << " " << vertices[vidx + 2];
1918  if (use_vertex_colors)
1919  {
1920  plys << " " << (int)col[vidx] << " " << (int)col[vidx + 1] << " " << (int)col[vidx + 2];
1921  }
1922  plys << "\n";
1923  }
1924 
1925 #ifdef LIBFS_DBG_DEBUG
1926  fs::util::log("Writing " + std::to_string(this->faces.size() / 3) + " PLY format faces.", "INFO");
1927 #endif
1928 
1929  const int num_vertices_per_face = 3;
1930  for (size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
1931  { // faces: vertex indices, 0-based
1932  plys << num_vertices_per_face << " " << faces[fidx] << " " << faces[fidx + 1] << " " << faces[fidx + 2] << "\n";
1933  }
1934  return (plys.str());
1935  }
1936 
1946  void to_ply_file(const std::string &filename) const
1947  {
1948 #ifdef LIBFS_DBG_INFO
1949  fs::util::log("Writing mesh to PLY file '" + filename + "'.", "INFO");
1950 #endif
1951  fs::util::str_to_file(filename, this->to_ply());
1952  }
1953 
1956  void to_ply_file(const std::string &filename, const std::vector<uint8_t> col) const
1957  {
1958  fs::util::str_to_file(filename, this->to_ply(col));
1959  }
1960 
1969  std::string to_off() const
1970  {
1971  std::vector<uint8_t> empty_col;
1972  return (this->to_off(empty_col));
1973  }
1974 
1978  std::string to_off(const std::vector<uint8_t> col) const
1979  {
1980  bool use_vertex_colors = col.size() != 0;
1981  std::stringstream offs;
1982  if (use_vertex_colors)
1983  {
1984 #ifdef LIBFS_DBG_INFO
1985  fs::util::log("Writing OFF representation of mesh with vertex colors.", "INFO");
1986 #endif
1987  if (col.size() != this->vertices.size())
1988  {
1989  throw std::invalid_argument("Number of vertex coordinates and vertex colors must match when writing OFF file but got " + std::to_string(this->vertices.size()) + " and " + std::to_string(col.size()) + ".");
1990  }
1991  offs << "COFF\n";
1992  }
1993  else
1994  {
1995 #ifdef LIBFS_DBG_INFO
1996  fs::util::log("Writing OFF representation of mesh without vertex colors.", "INFO");
1997 #endif
1998  offs << "OFF\n";
1999  }
2000  offs << this->num_vertices() << " " << this->num_faces() << " 0\n";
2001 
2002  for (size_t vidx = 0; vidx < this->vertices.size(); vidx += 3)
2003  { // vertex coords
2004  offs << vertices[vidx] << " " << vertices[vidx + 1] << " " << vertices[vidx + 2];
2005  if (use_vertex_colors)
2006  {
2007  offs << " " << (int)col[vidx] << " " << (int)col[vidx + 1] << " " << (int)col[vidx + 2] << " 255";
2008  }
2009  offs << "\n";
2010  }
2011 
2012  const int num_vertices_per_face = 3;
2013  for (size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
2014  { // faces: vertex indices, 0-based
2015  offs << num_vertices_per_face << " " << faces[fidx] << " " << faces[fidx + 1] << " " << faces[fidx + 2] << "\n";
2016  }
2017  return (offs.str());
2018  }
2019 
2029  void to_off_file(const std::string &filename) const
2030  {
2031  fs::util::str_to_file(filename, this->to_off());
2032  }
2033 
2036  void to_off_file(const std::string &filename, const std::vector<uint8_t> col) const
2037  {
2038  fs::util::str_to_file(filename, this->to_off(col));
2039  }
2040  };
2041 
2043  struct Curv
2044  {
2045 
2047  Curv(std::vector<float> curv_data) : num_faces(100000), num_vertices(0), num_values_per_vertex(1)
2048  {
2049  data = curv_data;
2050  num_vertices = int(data.size());
2051  }
2052 
2054  Curv() : num_faces(100000), num_vertices(0), num_values_per_vertex(1) {}
2055 
2057  int32_t num_faces;
2058 
2060  std::vector<float> data;
2061 
2063  int32_t num_vertices;
2064 
2067  };
2068 
2070  struct Colortable
2071  {
2072  std::vector<int32_t> id;
2073  std::vector<std::string> name;
2074  std::vector<int32_t> r;
2075  std::vector<int32_t> g;
2076  std::vector<int32_t> b;
2077  std::vector<int32_t> a;
2078  std::vector<int32_t> label;
2079 
2081  size_t num_entries() const
2082  {
2083  size_t num_ids = this->id.size();
2084  if (this->name.size() != num_ids || this->r.size() != num_ids || this->g.size() != num_ids || this->b.size() != num_ids || this->a.size() != num_ids || this->label.size() != num_ids)
2085  {
2086  std::cerr << "Inconsistent Colortable, vector sizes do not match.\n";
2087  }
2088  return num_ids;
2089  }
2090 
2092  int32_t get_region_idx(const std::string &query_name) const
2093  {
2094  for (size_t i = 0; i < this->num_entries(); i++)
2095  {
2096  if (this->name[i] == query_name)
2097  {
2098  return (int32_t)i;
2099  }
2100  }
2101  return (-1);
2102  }
2103 
2105  int32_t get_region_idx(int32_t query_label) const
2106  {
2107  for (size_t i = 0; i < this->num_entries(); i++)
2108  {
2109  if (this->label[i] == query_label)
2110  {
2111  return (int32_t)i;
2112  }
2113  }
2114  return (-1);
2115  }
2116  };
2117 
2119  struct Annot
2120  {
2121  std::vector<int32_t> vertex_indices;
2122  std::vector<int32_t> vertex_labels;
2124 
2126  std::vector<int32_t> region_vertices(const std::string &region_name) const
2127  {
2128  int32_t region_idx = this->colortable.get_region_idx(region_name);
2129  if (region_idx >= 0)
2130  {
2131  return (this->region_vertices(this->colortable.label[region_idx]));
2132  }
2133  else
2134  {
2135  std::cerr << "No such region in annot, returning empty vector.\n";
2136  std::vector<int32_t> empty;
2137  return (empty);
2138  }
2139  }
2140 
2142  std::vector<int32_t> region_vertices(int32_t region_label) const
2143  {
2144  std::vector<int32_t> reg_verts;
2145  for (size_t i = 0; i < this->vertex_labels.size(); i++)
2146  {
2147  if (this->vertex_labels[i] == region_label)
2148  {
2149  reg_verts.push_back(int(i));
2150  }
2151  }
2152  return (reg_verts);
2153  }
2154 
2157  std::vector<uint8_t> vertex_colors(bool alpha = false) const
2158  {
2159  int num_channels = alpha ? 4 : 3;
2160  std::vector<uint8_t> col;
2161  col.reserve(this->num_vertices() * num_channels);
2162  std::vector<size_t> vertex_region_indices = this->vertex_regions();
2163  for (size_t i = 0; i < this->num_vertices(); i++)
2164  {
2165  col.push_back(this->colortable.r[vertex_region_indices[i]]);
2166  col.push_back(this->colortable.g[vertex_region_indices[i]]);
2167  col.push_back(this->colortable.b[vertex_region_indices[i]]);
2168  if (alpha)
2169  {
2170  col.push_back(this->colortable.a[vertex_region_indices[i]]);
2171  }
2172  }
2173  return (col);
2174  }
2175 
2178  size_t num_vertices() const
2179  {
2180  size_t nv = this->vertex_indices.size();
2181  if (this->vertex_labels.size() != nv)
2182  {
2183  throw std::runtime_error("Inconsistent annot, number of vertex indices and labels does not match.\n");
2184  }
2185  return nv;
2186  }
2187 
2190  std::vector<size_t> vertex_regions() const
2191  {
2192  std::vector<size_t> vert_reg;
2193  for (size_t i = 0; i < this->num_vertices(); i++)
2194  {
2195  vert_reg.push_back(0); // init with zeros.
2196  }
2197  for (size_t region_idx = 0; region_idx < this->colortable.num_entries(); region_idx++)
2198  {
2199  std::vector<int32_t> reg_vertices = this->region_vertices(this->colortable.label[region_idx]);
2200  for (size_t region_vert_local_idx = 0; region_vert_local_idx < reg_vertices.size(); region_vert_local_idx++)
2201  {
2202  int32_t region_vert_idx = reg_vertices[region_vert_local_idx];
2203  vert_reg[region_vert_idx] = region_idx;
2204  }
2205  }
2206  return vert_reg;
2207  }
2208 
2210  std::vector<std::string> vertex_region_names() const
2211  {
2212  std::vector<std::string> region_names;
2213  std::vector<size_t> vertex_region_indices = this->vertex_regions();
2214  for (size_t i = 0; i < this->num_vertices(); i++)
2215  {
2216  region_names.push_back(this->colortable.name[vertex_region_indices[i]]);
2217  }
2218  return (region_names);
2219  }
2220  };
2221 
2223  struct MghHeader
2224  {
2227  {
2228  dim1length = curv.data.size();
2229  dim2length = 1;
2230  dim3length = 1;
2231  dim4length = 1;
2232  dtype = fs::MRI_FLOAT;
2233  }
2234  MghHeader(std::vector<float> curv_data)
2235  {
2236  dim1length = curv_data.size();
2237  dim2length = 1;
2238  dim3length = 1;
2239  dim4length = 1;
2240  dtype = fs::MRI_FLOAT;
2241  }
2242  int32_t dim1length = 0;
2243  int32_t dim2length = 0;
2244  int32_t dim3length = 0;
2245  int32_t dim4length = 0;
2246 
2247  int32_t dtype = 0;
2248  int32_t dof = 0;
2249  int16_t ras_good_flag = 0;
2250 
2252  size_t num_values() const
2253  {
2254  return ((size_t)dim1length * dim2length * dim3length * dim4length);
2255  }
2256 
2257  float xsize = 0.0;
2258  float ysize = 0.0;
2259  float zsize = 0.0;
2260  std::vector<float> Mdc;
2261  std::vector<float> Pxyz_c;
2262  };
2263 
2265  struct MghData
2266  {
2267  MghData() {}
2268  MghData(std::vector<int32_t> curv_data) { data_mri_int = curv_data; }
2269  explicit MghData(std::vector<uint8_t> curv_data) { data_mri_uchar = curv_data; }
2270  explicit MghData(std::vector<short> curv_data) { data_mri_short = curv_data; }
2271  MghData(std::vector<float> curv_data) { data_mri_float = curv_data; }
2272  MghData(Curv curv) { data_mri_float = curv.data; }
2273  std::vector<int32_t> data_mri_int;
2274  std::vector<uint8_t> data_mri_uchar;
2275  std::vector<float> data_mri_float;
2276  std::vector<short> data_mri_short;
2277  };
2278 
2280  struct Mgh
2281  {
2284  Mgh() {}
2285  Mgh(Curv curv)
2286  {
2287  header = MghHeader(curv);
2288  data = MghData(curv);
2289  }
2290  Mgh(std::vector<float> curv_data)
2291  {
2292  header = MghHeader(curv_data);
2293  data = MghData(curv_data);
2294  }
2295  };
2296 
2299  template <class T>
2300  struct Array4D
2301  {
2303  Array4D(unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4) : d1(d1), d2(d2), d3(d3), d4(d4), data(d1 * d2 * d3 * d4) {}
2304 
2306  Array4D(MghHeader *mgh_header) : d1(mgh_header->dim1length), d2(mgh_header->dim2length), d3(mgh_header->dim3length), d4(mgh_header->dim4length), data(d1 * d2 * d3 * d4) {}
2307 
2309  Array4D(Mgh *mgh) : // This does NOT init the data atm.
2310  d1(mgh->header.dim1length), d2(mgh->header.dim2length), d3(mgh->header.dim3length), d4(mgh->header.dim4length), data(d1 * d2 * d3 * d4)
2311  {
2312  }
2313 
2315  const T &at(const unsigned int i1, const unsigned int i2, const unsigned int i3, const unsigned int i4) const
2316  {
2317  return data[get_index(i1, i2, i3, i4)];
2318  }
2319 
2321  unsigned int get_index(const unsigned int i1, const unsigned int i2, const unsigned int i3, const unsigned int i4) const
2322  {
2323  assert(i1 >= 0 && i1 < d1);
2324  assert(i2 >= 0 && i2 < d2);
2325  assert(i3 >= 0 && i3 < d3);
2326  assert(i4 >= 0 && i4 < d4);
2327  return (((i1 * d2 + i2) * d3 + i3) * d4 + i4);
2328  }
2329 
2331  unsigned int num_values() const
2332  {
2333  return (d1 * d2 * d3 * d4);
2334  }
2335 
2336  unsigned int d1;
2337  unsigned int d2;
2338  unsigned int d3;
2339  unsigned int d4;
2340  std::vector<T> data;
2341  };
2342 
2343  // More declarations, should also go to separate header.
2344  void read_mgh_header(MghHeader *, const std::string &);
2345  void read_mgh_header(MghHeader *, std::istream *);
2346  template <typename T>
2347  std::vector<T> _read_mgh_data(MghHeader *, const std::string &);
2348  template <typename T>
2349  std::vector<T> _read_mgh_data(MghHeader *, std::istream *);
2350  std::vector<int32_t> _read_mgh_data_int(MghHeader *, const std::string &);
2351  std::vector<int32_t> _read_mgh_data_int(MghHeader *, std::istream *);
2352  std::vector<uint8_t> _read_mgh_data_uchar(MghHeader *, const std::string &);
2353  std::vector<uint8_t> _read_mgh_data_uchar(MghHeader *, std::istream *);
2354  std::vector<short> _read_mgh_data_short(MghHeader *, const std::string &);
2355  std::vector<short> _read_mgh_data_short(MghHeader *, std::istream *);
2356  std::vector<float> _read_mgh_data_float(MghHeader *, const std::string &);
2357  std::vector<float> _read_mgh_data_float(MghHeader *, std::istream *);
2358 
2371  void read_mgh(Mgh *mgh, const std::string &filename)
2372  {
2373  MghHeader mgh_header;
2374  read_mgh_header(&mgh_header, filename);
2375  mgh->header = mgh_header;
2376  if (mgh->header.dtype == MRI_INT)
2377  {
2378  std::vector<int32_t> data = _read_mgh_data_int(&mgh_header, filename);
2379  mgh->data.data_mri_int = data;
2380  }
2381  else if (mgh->header.dtype == MRI_UCHAR)
2382  {
2383  std::vector<uint8_t> data = _read_mgh_data_uchar(&mgh_header, filename);
2384  mgh->data.data_mri_uchar = data;
2385  }
2386  else if (mgh->header.dtype == MRI_FLOAT)
2387  {
2388  std::vector<float> data = _read_mgh_data_float(&mgh_header, filename);
2389  mgh->data.data_mri_float = data;
2390  }
2391  else if (mgh->header.dtype == MRI_SHORT)
2392  {
2393  std::vector<short> data = _read_mgh_data_short(&mgh_header, filename);
2394  mgh->data.data_mri_short = data;
2395  }
2396  else
2397  {
2398 #ifdef LIBFS_DBG_INFO
2399  if (fs::util::ends_with(filename, ".mgz"))
2400  {
2401  std::cout << LIBFS_APPTAG << "Note: your MGH filename ends with '.mgz'. Keep in mind that MGZ format is not supported directly. You can ignore this message if you wrapped a gz stream.\n";
2402  }
2403 #endif
2404  throw std::runtime_error("Not reading MGH data from file '" + filename + "', data type " + std::to_string(mgh->header.dtype) + " not supported yet.\n");
2405  }
2406  }
2407 
2417  std::vector<std::string> read_subjectsfile(const std::string &filename)
2418  {
2419  std::vector<std::string> subjects;
2420  std::ifstream input(filename, std::fstream::in);
2421  std::string line;
2422 
2423  if (!input.is_open())
2424  {
2425  throw std::runtime_error("Could not open subjects file '" + filename + "'.\n");
2426  }
2427 
2428  while (std::getline(input, line))
2429  {
2430  subjects.push_back(line);
2431  }
2432  return (subjects);
2433  }
2434 
2440  void read_mgh(Mgh *mgh, std::istream *is)
2441  {
2442  MghHeader mgh_header;
2443  read_mgh_header(&mgh_header, is);
2444  mgh->header = mgh_header;
2445  if (mgh->header.dtype == MRI_INT)
2446  {
2447  std::vector<int32_t> data = _read_mgh_data_int(&mgh_header, is);
2448  mgh->data.data_mri_int = data;
2449  }
2450  else if (mgh->header.dtype == MRI_UCHAR)
2451  {
2452  std::vector<uint8_t> data = _read_mgh_data_uchar(&mgh_header, is);
2453  mgh->data.data_mri_uchar = data;
2454  }
2455  else if (mgh->header.dtype == MRI_FLOAT)
2456  {
2457  std::vector<float> data = _read_mgh_data_float(&mgh_header, is);
2458  mgh->data.data_mri_float = data;
2459  }
2460  else if (mgh->header.dtype == MRI_SHORT)
2461  {
2462  std::vector<short> data = _read_mgh_data_short(&mgh_header, is);
2463  mgh->data.data_mri_short = data;
2464  }
2465  else
2466  {
2467  throw std::runtime_error("Not reading data from MGH stream, data type " + std::to_string(mgh->header.dtype) + " not supported yet.\n");
2468  }
2469  }
2470 
2476  void read_mgh_header(MghHeader *mgh_header, std::istream *is)
2477  {
2478  const int MGH_VERSION = 1;
2479 
2480  int format_version = _freadt<int32_t>(*is);
2481  if (format_version != MGH_VERSION)
2482  {
2483  throw std::runtime_error("Invalid MGH file or unsupported file format version: expected version " + std::to_string(MGH_VERSION) + ", found " + std::to_string(format_version) + ".\n");
2484  }
2485  mgh_header->dim1length = _freadt<int32_t>(*is);
2486  mgh_header->dim2length = _freadt<int32_t>(*is);
2487  mgh_header->dim3length = _freadt<int32_t>(*is);
2488  mgh_header->dim4length = _freadt<int32_t>(*is);
2489 
2490  mgh_header->dtype = _freadt<int32_t>(*is);
2491  mgh_header->dof = _freadt<int32_t>(*is);
2492 
2493  int unused_header_space_size_left = 256; // in bytes
2494  mgh_header->ras_good_flag = _freadt<int16_t>(*is);
2495  unused_header_space_size_left -= 2; // for the ras_good_flag
2496 
2497  // Read the RAS part of the header.
2498  if (mgh_header->ras_good_flag == 1)
2499  {
2500  mgh_header->xsize = _freadt<float>(*is);
2501  mgh_header->ysize = _freadt<float>(*is);
2502  mgh_header->zsize = _freadt<float>(*is);
2503 
2504  for (int i = 0; i < 9; i++)
2505  {
2506  mgh_header->Mdc.push_back(_freadt<float>(*is));
2507  }
2508  for (int i = 0; i < 3; i++)
2509  {
2510  mgh_header->Pxyz_c.push_back(_freadt<float>(*is));
2511  }
2512  unused_header_space_size_left -= 60;
2513  }
2514 
2515  // Advance to data part. We do not seek here because that is not
2516  // possible if the stream is gzip-wrapped with zstr, as in the read_mgz example.
2517  uint8_t discarded;
2518  while (unused_header_space_size_left > 0)
2519  {
2520  discarded = _freadt<uint8_t>(*is);
2521  unused_header_space_size_left -= 1;
2522  }
2523  (void)discarded; // Suppress warnings about unused variable.
2524  }
2525 
2530  std::vector<int32_t> _read_mgh_data_int(MghHeader *mgh_header, const std::string &filename)
2531  {
2532  if (mgh_header->dtype != MRI_INT)
2533  {
2534  std::cerr << "Expected MRI data type " << MRI_INT << ", but found " << mgh_header->dtype << ".\n";
2535  }
2536  return (_read_mgh_data<int32_t>(mgh_header, filename));
2537  }
2538 
2543  std::vector<int32_t> _read_mgh_data_int(MghHeader *mgh_header, std::istream *is)
2544  {
2545  if (mgh_header->dtype != MRI_INT)
2546  {
2547  std::cerr << "Expected MRI data type " << MRI_INT << ", but found " << mgh_header->dtype << ".\n";
2548  }
2549  return (_read_mgh_data<int32_t>(mgh_header, is));
2550  }
2551 
2556  std::vector<short> _read_mgh_data_short(MghHeader *mgh_header, const std::string &filename)
2557  {
2558  if (mgh_header->dtype != MRI_SHORT)
2559  {
2560  std::cerr << "Expected MRI data type " << MRI_SHORT << ", but found " << mgh_header->dtype << ".\n";
2561  }
2562  return (_read_mgh_data<short>(mgh_header, filename));
2563  }
2564 
2569  std::vector<short> _read_mgh_data_short(MghHeader *mgh_header, std::istream *is)
2570  {
2571  if (mgh_header->dtype != MRI_SHORT)
2572  {
2573  std::cerr << "Expected MRI data type " << MRI_SHORT << ", but found " << mgh_header->dtype << ".\n";
2574  }
2575  return (_read_mgh_data<short>(mgh_header, is));
2576  }
2577 
2584  void read_mgh_header(MghHeader *mgh_header, const std::string &filename)
2585  {
2586  std::ifstream ifs;
2587  ifs.open(filename, std::ios_base::in | std::ios::binary);
2588  if (ifs.is_open())
2589  {
2590  read_mgh_header(mgh_header, &ifs);
2591  ifs.close();
2592  }
2593  else
2594  {
2595  throw std::runtime_error("Unable to open MGH file '" + filename + "'.\n");
2596  }
2597  }
2598 
2604  template <typename T>
2605  std::vector<T> _read_mgh_data(MghHeader *mgh_header, const std::string &filename)
2606  {
2607  std::ifstream ifs;
2608  ifs.open(filename, std::ios_base::in | std::ios::binary);
2609  if (ifs.is_open())
2610  {
2611  ifs.seekg(284, ifs.beg); // skip to end of header and beginning of data
2612 
2613  int num_values = int(mgh_header->num_values());
2614  std::vector<T> data;
2615  for (int i = 0; i < num_values; i++)
2616  {
2617  data.push_back(_freadt<T>(ifs));
2618  }
2619  ifs.close();
2620  return (data);
2621  }
2622  else
2623  {
2624  throw std::runtime_error("Unable to open MGH file '" + filename + "'.\n");
2625  }
2626  }
2627 
2632  template <typename T>
2633  std::vector<T> _read_mgh_data(MghHeader *mgh_header, std::istream *is)
2634  {
2635  int num_values = int(mgh_header->num_values());
2636  std::vector<T> data;
2637  for (int i = 0; i < num_values; i++)
2638  {
2639  data.push_back(_freadt<T>(*is));
2640  }
2641  return (data);
2642  }
2643 
2648  std::vector<float> _read_mgh_data_float(MghHeader *mgh_header, const std::string &filename)
2649  {
2650  if (mgh_header->dtype != MRI_FLOAT)
2651  {
2652  std::cerr << "Expected MRI data type " << MRI_FLOAT << ", but found " << mgh_header->dtype << ".\n";
2653  }
2654  return (_read_mgh_data<float>(mgh_header, filename));
2655  }
2656 
2661  std::vector<float> _read_mgh_data_float(MghHeader *mgh_header, std::istream *is)
2662  {
2663  if (mgh_header->dtype != MRI_FLOAT)
2664  {
2665  std::cerr << "Expected MRI data type " << MRI_FLOAT << ", but found " << mgh_header->dtype << ".\n";
2666  }
2667  return (_read_mgh_data<float>(mgh_header, is));
2668  }
2669 
2674  std::vector<uint8_t> _read_mgh_data_uchar(MghHeader *mgh_header, const std::string &filename)
2675  {
2676  if (mgh_header->dtype != MRI_UCHAR)
2677  {
2678  std::cerr << "Expected MRI data type " << MRI_UCHAR << ", but found " << mgh_header->dtype << ".\n";
2679  }
2680  return (_read_mgh_data<uint8_t>(mgh_header, filename));
2681  }
2682 
2687  std::vector<uint8_t> _read_mgh_data_uchar(MghHeader *mgh_header, std::istream *is)
2688  {
2689  if (mgh_header->dtype != MRI_UCHAR)
2690  {
2691  std::cerr << "Expected MRI data type " << MRI_UCHAR << ", but found " << mgh_header->dtype << ".\n";
2692  }
2693  return (_read_mgh_data<uint8_t>(mgh_header, is));
2694  }
2695 
2709  void read_surf(Mesh *surface, const std::string &filename)
2710  {
2711  const int SURF_TRIS_MAGIC = 16777214;
2712  std::ifstream is;
2713  is.open(filename, std::ios_base::in | std::ios::binary);
2714  if (is.is_open())
2715  {
2716  int magic = _fread3(is);
2717  if (magic != SURF_TRIS_MAGIC)
2718  {
2719  throw std::domain_error("Surf file '" + filename + "' magic code in header did not match: expected " + std::to_string(SURF_TRIS_MAGIC) + ", found " + std::to_string(magic) + ".\n");
2720  }
2721  std::string created_line = _freadstringnewline(is);
2722  std::string comment_line = _freadstringnewline(is);
2723  int num_verts = _freadt<int32_t>(is);
2724  int num_faces = _freadt<int32_t>(is);
2725 #ifdef LIBFS_DBG_INFO
2726  std::cout << LIBFS_APPTAG << "Read surface file with " << num_verts << " vertices, " << num_faces << " faces.\n";
2727 #endif
2728  std::vector<float> vdata;
2729  for (int i = 0; i < (num_verts * 3); i++)
2730  {
2731  vdata.push_back(_freadt<float>(is));
2732  }
2733  std::vector<int> fdata;
2734  for (int i = 0; i < (num_faces * 3); i++)
2735  {
2736  fdata.push_back(_freadt<int32_t>(is));
2737  }
2738  is.close();
2739  surface->vertices = vdata;
2740  surface->faces = fdata;
2741  }
2742  else
2743  {
2744  throw std::runtime_error("Unable to open surface file '" + filename + "'.\n");
2745  }
2746  }
2747 
2760  void read_mesh(Mesh *surface, const std::string &filename)
2761  {
2762  if (fs::util::ends_with(filename, ".obj"))
2763  {
2764  fs::Mesh::from_obj(surface, filename);
2765  }
2766  else if (fs::util::ends_with(filename, ".ply"))
2767  {
2768  fs::Mesh::from_ply(surface, filename);
2769  }
2770  else if (fs::util::ends_with(filename, ".off"))
2771  {
2772  fs::Mesh::from_off(surface, filename);
2773  }
2774  else
2775  {
2776  read_surf(surface, filename);
2777  }
2778  }
2779 
2785  bool _is_bigendian()
2786  {
2787  short int number = 0x1;
2788  char *numPtr = (char *)&number;
2789  // std::cout << "Platform is big endian: " << (numPtr[0] != 1) << ".\n";
2790  return (numPtr[0] != 1);
2791  }
2792 
2798  void read_curv(Curv *curv, std::istream *is, const std::string &source_filename = "")
2799  {
2800  const std::string msg_source_file_part = source_filename.empty() ? "" : "'" + source_filename + "' ";
2801  const int CURV_MAGIC = 16777215;
2802  int magic = _fread3(*is);
2803  if (magic != CURV_MAGIC)
2804  {
2805  throw std::domain_error("Curv file " + msg_source_file_part + "header magic did not match: expected " + std::to_string(CURV_MAGIC) + ", found " + std::to_string(magic) + ".\n");
2806  }
2807  curv->num_vertices = _freadt<int32_t>(*is);
2808  curv->num_faces = _freadt<int32_t>(*is);
2809  curv->num_values_per_vertex = _freadt<int32_t>(*is);
2810 #ifdef LIBFS_DBG_INFO
2811  std::cout << LIBFS_APPTAG << "Read curv file with " << curv->num_vertices << " vertices, " << curv->num_faces << " faces and " << curv->num_values_per_vertex << " values per vertex.\n";
2812 #endif
2813  if (curv->num_values_per_vertex != 1)
2814  { // Not supported, I know no case where this is used. Please submit a PR with a demo file if you have one, and let me know where it came from.
2815  throw std::domain_error("Curv file " + msg_source_file_part + "must contain exactly 1 value per vertex, found " + std::to_string(curv->num_values_per_vertex) + ".\n");
2816  }
2817  std::vector<float> data;
2818  for (int i = 0; i < curv->num_vertices; i++)
2819  {
2820  data.push_back(_freadt<float>(*is));
2821  }
2822  curv->data = data;
2823  }
2824 
2837  void read_curv(Curv *curv, const std::string &filename)
2838  {
2839  std::ifstream is(filename, std::fstream::in | std::fstream::binary);
2840  if (is.is_open())
2841  {
2842  read_curv(curv, &is, filename);
2843  is.close();
2844  }
2845  else
2846  {
2847  throw std::runtime_error("Could not open curv file '" + filename + "' for reading.\n");
2848  }
2849  }
2850 
2853  void _read_annot_colortable(Colortable *colortable, std::istream *is, int32_t num_entries)
2854  {
2855  int32_t num_chars_orig_filename = _freadt<int32_t>(*is); // The number of characters of the file this annot was built from.
2856 
2857  // It follows the name of the file this annot was built from. This is development metadata and irrelevant afaik. We skip it.
2858  uint8_t discarded;
2859  for (int32_t i = 0; i < num_chars_orig_filename; i++)
2860  {
2861  discarded = _freadt<uint8_t>(*is);
2862  }
2863  (void)discarded; // Suppress warnings about unused variable.
2864 
2865  int32_t num_entries_duplicated = _freadt<int32_t>(*is); // Yes, once more.
2866  if (num_entries != num_entries_duplicated)
2867  {
2868  std::cerr << "Warning: the two num_entries header fields of this annotation do not match. Use with care.\n";
2869  }
2870 
2871  int32_t entry_num_chars;
2872  for (int32_t i = 0; i < num_entries; i++)
2873  {
2874  colortable->id.push_back(_freadt<int32_t>(*is));
2875  entry_num_chars = _freadt<int32_t>(*is);
2876  colortable->name.push_back(_freadfixedlengthstring(*is, entry_num_chars, true));
2877  colortable->r.push_back(_freadt<int32_t>(*is));
2878  colortable->g.push_back(_freadt<int32_t>(*is));
2879  colortable->b.push_back(_freadt<int32_t>(*is));
2880  colortable->a.push_back(_freadt<int32_t>(*is));
2881  colortable->label.push_back(colortable->r[i] + colortable->g[i] * 256 + colortable->b[i] * 65536 + colortable->a[i] * 16777216);
2882  }
2883  }
2884 
2887  size_t _vidx_2d(size_t row, size_t column, size_t row_length = 3)
2888  {
2889  return (row + 1) * row_length - row_length + column;
2890  }
2891 
2897  void read_annot(Annot *annot, std::istream *is)
2898  {
2899 
2900  int32_t num_vertices = _freadt<int32_t>(*is);
2901  std::vector<int32_t> vertices;
2902  std::vector<int32_t> labels;
2903  for (int32_t i = 0; i < (num_vertices * 2); i++)
2904  { // The vertices and their labels are stored directly after one another: v1,v1_label,v2,v2_label,...
2905  if (i % 2 == 0)
2906  {
2907  vertices.push_back(_freadt<int32_t>(*is));
2908  }
2909  else
2910  {
2911  labels.push_back(_freadt<int32_t>(*is));
2912  }
2913  }
2914  annot->vertex_indices = vertices;
2915  annot->vertex_labels = labels;
2916  int32_t has_colortable = _freadt<int32_t>(*is);
2917  if (has_colortable == 1)
2918  {
2919  int32_t num_colortable_entries_old_format = _freadt<int32_t>(*is);
2920  if (num_colortable_entries_old_format > 0)
2921  {
2922  throw std::domain_error("Reading annotation in old format not supported. Please open an issue and supply an example file if you need this.\n");
2923  }
2924  else
2925  {
2926  int32_t colortable_format_version = -num_colortable_entries_old_format; // If the value is negative, we are in new format and its absolute value is the format version.
2927  if (colortable_format_version == 2)
2928  {
2929  int32_t num_colortable_entries = _freadt<int32_t>(*is); // This time for real.
2930  _read_annot_colortable(&annot->colortable, is, num_colortable_entries);
2931  }
2932  else
2933  {
2934  throw std::domain_error("Reading annotation in new format version !=2 not supported. Please open an issue and supply an example file if you need this.\n");
2935  }
2936  }
2937  }
2938  else
2939  {
2940  throw std::domain_error("Reading annotation without colortable not supported. Maybe invalid annotation file?\n");
2941  }
2942  }
2943 
2957  void read_annot(Annot *annot, const std::string &filename)
2958  {
2959  std::ifstream is(filename, std::fstream::in | std::fstream::binary);
2960  if (is.is_open())
2961  {
2962  read_annot(annot, &is);
2963  is.close();
2964  }
2965  else
2966  {
2967  throw std::runtime_error("Could not open annot file '" + filename + "' for reading.\n");
2968  }
2969  }
2970 
2983  std::vector<float> read_curv_data(const std::string &filename)
2984  {
2985  Curv curv;
2986  read_curv(&curv, filename);
2987  return (curv.data);
2988  }
2989 
3003  std::vector<float> read_desc_data(const std::string &filename)
3004  {
3005  if (fs::util::ends_with(filename, {".MGH", ".mgh"}))
3006  {
3007  fs::Mgh mgh;
3008  fs::read_mgh(&mgh, filename);
3009  assert(mgh.header.dtype == fs::MRI_FLOAT);
3010  int num_gt_1 = 0;
3011  std::vector<int> dims = {mgh.header.dim1length, mgh.header.dim2length, mgh.header.dim3length, mgh.header.dim4length};
3012  for (size_t i = 0; i < dims.size(); i++)
3013  {
3014  if (dims[i] > 1)
3015  {
3016  num_gt_1++;
3017  }
3018  }
3019  if (num_gt_1 > 1)
3020  {
3021  std::cerr << "MGH file '" << filename << "' contains more than one non-empty dimension. Returning concatinated data.\n";
3022  }
3023  return mgh.data.data_mri_float;
3024  }
3025  else
3026  {
3027  Curv curv;
3028  read_curv(&curv, filename);
3029  return (curv.data);
3030  }
3031  }
3032 
3037  template <typename T>
3038  T _swap_endian(T u)
3039  {
3040 
3041  static_assert(CHAR_BIT == 8, "CHAR_BIT != 8");
3042 
3043  union
3044  {
3045  T u;
3046  unsigned char u8[sizeof(T)];
3047  } source, dest;
3048 
3049  source.u = u;
3050 
3051  for (size_t k = 0; k < sizeof(T); k++)
3052  dest.u8[k] = source.u8[sizeof(T) - k - 1];
3053 
3054  return (dest.u);
3055  }
3056 
3061  template <typename T>
3062  T _freadt(std::istream &is)
3063  {
3064  T t;
3065  is.read(reinterpret_cast<char *>(&t), sizeof(t));
3066  if (!_is_bigendian())
3067  {
3068  t = _swap_endian<T>(t);
3069  }
3070  return (t);
3071  }
3072 
3077  int _fread3(std::istream &is)
3078  {
3079  uint32_t i;
3080  is.read(reinterpret_cast<char *>(&i), 3);
3081  if (!_is_bigendian())
3082  {
3083  i = _swap_endian<std::uint32_t>(i);
3084  }
3085  i = ((i >> 8) & 0xffffff);
3086  return (i);
3087  }
3088 
3093  template <typename T>
3094  void _fwritet(std::ostream &os, T t)
3095  {
3096  if (!_is_bigendian())
3097  {
3098  t = _swap_endian<T>(t);
3099  }
3100  os.write(reinterpret_cast<const char *>(&t), sizeof(t));
3101  }
3102 
3103  // Write big endian 24 bit integer to a stream, extracted from the first 3 bytes of an unsigned 32 bit integer.
3104  //
3105  // THIS FUNCTION IS INTERNAL AND SHOULD NOT BE CALLED BY API CLIENTS.
3107  void _fwritei3(std::ostream &os, uint32_t i)
3108  {
3109  unsigned char b1 = (i >> 16) & 255;
3110  unsigned char b2 = (i >> 8) & 255;
3111  unsigned char b3 = i & 255;
3112 
3113  if (!_is_bigendian())
3114  {
3115  b1 = _swap_endian<unsigned char>(b1);
3116  b2 = _swap_endian<unsigned char>(b2);
3117  b3 = _swap_endian<unsigned char>(b3);
3118  }
3119 
3120  os.write(reinterpret_cast<const char *>(&b1), sizeof(b1));
3121  os.write(reinterpret_cast<const char *>(&b2), sizeof(b2));
3122  os.write(reinterpret_cast<const char *>(&b3), sizeof(b3));
3123  }
3124 
3129  std::string _freadstringnewline(std::istream &is)
3130  {
3131  std::string s;
3132  std::getline(is, s, '\n');
3133  return s;
3134  }
3135 
3139  std::string _freadfixedlengthstring(std::istream &is, size_t length, bool strip_last_char = true)
3140  {
3141  std::string str;
3142  str.resize(length);
3143  is.read(&str[0], length);
3144  if (strip_last_char)
3145  {
3146  str = str.substr(0, length - 1);
3147  }
3148  return str;
3149  }
3150 
3156  void write_curv(std::ostream &os, std::vector<float> curv_data, int32_t num_faces = 100000)
3157  {
3158  const uint32_t CURV_MAGIC = 16777215;
3159  _fwritei3(os, CURV_MAGIC);
3160  _fwritet<int32_t>(os, int(curv_data.size()));
3161  _fwritet<int32_t>(os, num_faces);
3162  _fwritet<int32_t>(os, 1); // Number of values per vertex.
3163  for (size_t i = 0; i < curv_data.size(); i++)
3164  {
3165  _fwritet<float>(os, curv_data[i]);
3166  }
3167  }
3168 
3183  void write_curv(const std::string &filename, std::vector<float> curv_data, const int32_t num_faces = 100000)
3184  {
3185  std::ofstream ofs;
3186  ofs.open(filename, std::ofstream::out | std::ofstream::binary);
3187  if (ofs.is_open())
3188  {
3189  write_curv(ofs, curv_data, num_faces);
3190  ofs.close();
3191  }
3192  else
3193  {
3194  throw std::runtime_error("Unable to open curvature file '" + filename + "' for writing.\n");
3195  }
3196  }
3197 
3203  void write_mgh(const Mgh &mgh, std::ostream &os)
3204  {
3205  _fwritet<int32_t>(os, 1); // MGH file format version
3206  _fwritet<int32_t>(os, mgh.header.dim1length);
3207  _fwritet<int32_t>(os, mgh.header.dim2length);
3208  _fwritet<int32_t>(os, mgh.header.dim3length);
3209  _fwritet<int32_t>(os, mgh.header.dim4length);
3210 
3211  _fwritet<int32_t>(os, mgh.header.dtype);
3212  _fwritet<int32_t>(os, mgh.header.dof);
3213 
3214  size_t unused_header_space_size_left = 256; // in bytes
3215  _fwritet<int16_t>(os, mgh.header.ras_good_flag);
3216  unused_header_space_size_left -= 2; // for RAS flag
3217 
3218  // Write RAS part of of header if flag is 1.
3219  if (mgh.header.ras_good_flag == 1)
3220  {
3221  _fwritet<float>(os, mgh.header.xsize);
3222  _fwritet<float>(os, mgh.header.ysize);
3223  _fwritet<float>(os, mgh.header.zsize);
3224 
3225  for (int i = 0; i < 9; i++)
3226  {
3227  _fwritet<float>(os, mgh.header.Mdc[i]);
3228  }
3229  for (int i = 0; i < 3; i++)
3230  {
3231  _fwritet<float>(os, mgh.header.Pxyz_c[i]);
3232  }
3233 
3234  unused_header_space_size_left -= 60;
3235  }
3236 
3237  for (size_t i = 0; i < unused_header_space_size_left; i++)
3238  { // Fill rest of header space.
3239  _fwritet<uint8_t>(os, 0);
3240  }
3241 
3242  // Write data
3243  size_t num_values = mgh.header.num_values();
3244  if (mgh.header.dtype == MRI_INT)
3245  {
3246  if (mgh.data.data_mri_int.size() != num_values)
3247  {
3248  throw std::logic_error("Detected mismatch of MRI_INT data size and MGH header dim length values.\n");
3249  }
3250  for (size_t i = 0; i < num_values; i++)
3251  {
3252  _fwritet<int32_t>(os, mgh.data.data_mri_int[i]);
3253  }
3254  }
3255  else if (mgh.header.dtype == MRI_FLOAT)
3256  {
3257  if (mgh.data.data_mri_float.size() != num_values)
3258  {
3259  throw std::logic_error("Detected mismatch of MRI_FLOAT data size and MGH header dim length values.\n");
3260  }
3261  for (size_t i = 0; i < num_values; i++)
3262  {
3263  _fwritet<float>(os, mgh.data.data_mri_float[i]);
3264  }
3265  }
3266  else if (mgh.header.dtype == MRI_UCHAR)
3267  {
3268  if (mgh.data.data_mri_uchar.size() != num_values)
3269  {
3270  throw std::logic_error("Detected mismatch of MRI_UCHAR data size and MGH header dim length values.\n");
3271  }
3272  for (size_t i = 0; i < num_values; i++)
3273  {
3274  _fwritet<uint8_t>(os, mgh.data.data_mri_uchar[i]);
3275  }
3276  }
3277  else if (mgh.header.dtype == MRI_SHORT)
3278  {
3279  if (mgh.data.data_mri_short.size() != num_values)
3280  {
3281  throw std::logic_error("Detected mismatch of MRI_SHORT data size and MGH header dim length values.\n");
3282  }
3283  for (size_t i = 0; i < num_values; i++)
3284  {
3285  _fwritet<short>(os, mgh.data.data_mri_short[i]);
3286  }
3287  }
3288  else
3289  {
3290  throw std::domain_error("Unsupported MRI data type " + std::to_string(mgh.header.dtype) + ", cannot write MGH data.\n");
3291  }
3292  }
3293 
3309  void write_mgh(const Mgh &mgh, const std::string &filename)
3310  {
3311  std::ofstream ofs;
3312  ofs.open(filename, std::ofstream::out | std::ofstream::binary);
3313  if (ofs.is_open())
3314  {
3315  write_mgh(mgh, ofs);
3316  ofs.close();
3317  }
3318  else
3319  {
3320  throw std::runtime_error("Unable to open MGH file '" + filename + "' for writing.\n");
3321  }
3322  }
3323 
3330  struct Label
3331  {
3332 
3334  Label() {}
3335 
3337  Label(std::vector<int> vertices, std::vector<float> values)
3338  {
3339  assert(vertices.size() == values.size());
3340  vertex = vertices;
3341  value = values;
3342  coord_x = std::vector<float>(vertices.size(), 0.0f);
3343  coord_y = std::vector<float>(vertices.size(), 0.0f);
3344  coord_z = std::vector<float>(vertices.size(), 0.0f);
3345  }
3346 
3348  Label(std::vector<int> vertices)
3349  {
3350  vertex = vertices;
3351  value = std::vector<float>(vertices.size(), 0.0f);
3352  coord_x = std::vector<float>(vertices.size(), 0.0f);
3353  coord_y = std::vector<float>(vertices.size(), 0.0f);
3354  coord_z = std::vector<float>(vertices.size(), 0.0f);
3355  }
3356 
3357  std::vector<int> vertex;
3358  std::vector<float> coord_x;
3359  std::vector<float> coord_y;
3360  std::vector<float> coord_z;
3361  std::vector<float> value;
3362 
3364  std::vector<bool> vert_in_label(size_t surface_num_verts) const
3365  {
3366  if (surface_num_verts < this->vertex.size())
3367  { // nonsense, so we warn (but don't throw, maybe the user really wants this).
3368  std::cerr << "Invalid number of vertices for surface, must be at least " << this->vertex.size() << "\n";
3369  }
3370  std::vector<bool> is_in = std::vector<bool>(surface_num_verts, false);
3371 
3372  for (size_t i = 0; i < this->vertex.size(); i++)
3373  {
3374  is_in[this->vertex[i]] = true;
3375  }
3376  return (is_in);
3377  }
3378 
3380  size_t num_entries() const
3381  {
3382  size_t num_ent = this->vertex.size();
3383  if (this->coord_x.size() != num_ent || this->coord_y.size() != num_ent || this->coord_z.size() != num_ent || this->value.size() != num_ent)
3384  {
3385  std::cerr << "Inconsistent label: sizes of property vectors do not match.\n";
3386  }
3387  return (num_ent);
3388  }
3389  };
3390 
3397  void write_surf(std::vector<float> vertices, std::vector<int32_t> faces, std::ostream &os)
3398  {
3399  const uint32_t SURF_TRIS_MAGIC = 16777214;
3400  _fwritei3(os, SURF_TRIS_MAGIC);
3401  std::string created_and_comment_lines = "Created by fslib\n\n";
3402  os << created_and_comment_lines;
3403  _fwritet<int32_t>(os, int(vertices.size() / 3)); // number of vertices
3404  _fwritet<int32_t>(os, int(faces.size() / 3)); // number of faces
3405  for (size_t i = 0; i < vertices.size(); i++)
3406  {
3407  _fwritet<float>(os, vertices[i]);
3408  }
3409  for (size_t i = 0; i < faces.size(); i++)
3410  {
3411  _fwritet<int32_t>(os, faces[i]);
3412  }
3413  }
3414 
3428  void write_surf(std::vector<float> vertices, std::vector<int32_t> faces, const std::string &filename)
3429  {
3430  std::ofstream ofs;
3431  ofs.open(filename, std::ofstream::out | std::ofstream::binary);
3432  if (ofs.is_open())
3433  {
3434  write_surf(vertices, faces, ofs);
3435  ofs.close();
3436  }
3437  else
3438  {
3439  throw std::runtime_error("Unable to open surf file '" + filename + "' for writing.\n");
3440  }
3441  }
3442 
3455  void write_surf(const Mesh &mesh, const std::string &filename)
3456  {
3457  std::ofstream ofs;
3458  ofs.open(filename, std::ofstream::out | std::ofstream::binary);
3459  if (ofs.is_open())
3460  {
3461  write_surf(mesh.vertices, mesh.faces, ofs);
3462  ofs.close();
3463  }
3464  else
3465  {
3466  throw std::runtime_error("Unable to open surf file '" + filename + "' for writing.\n");
3467  }
3468  }
3469 
3476  void read_label(Label *label, std::istream *is)
3477  {
3478  std::string line;
3479  int line_idx = -1;
3480  size_t num_entries_header = 0; // number of vertices/voxels according to header
3481  size_t num_entries = 0; // number of vertices/voxels for which the file contains label entries.
3482  while (std::getline(*is, line))
3483  {
3484  line_idx += 1;
3485  std::istringstream iss(line);
3486  if (line_idx == 0)
3487  {
3488  continue; // skip comment.
3489  }
3490  else
3491  {
3492  if (line_idx == 1)
3493  {
3494  if (!(iss >> num_entries_header))
3495  {
3496  throw std::domain_error("Could not parse entry count from label file, invalid format.\n");
3497  }
3498  }
3499  else
3500  {
3501  int vertex;
3502  float x, y, z, value;
3503  if (!(iss >> vertex >> x >> y >> z >> value))
3504  {
3505  throw std::domain_error("Could not parse line " + std::to_string(line_idx + 1) + " of label file, invalid format.\n");
3506  }
3507  label->vertex.push_back(vertex);
3508  label->coord_x.push_back(x);
3509  label->coord_y.push_back(y);
3510  label->coord_z.push_back(z);
3511  label->value.push_back(value);
3512  num_entries++;
3513  }
3514  }
3515  }
3516  if (num_entries != num_entries_header)
3517  {
3518  throw std::domain_error("Expected " + std::to_string(num_entries_header) + " entries from label file header, but found " + std::to_string(num_entries) + " in file, invalid label file.\n");
3519  }
3520  if (label->vertex.size() != num_entries || label->coord_x.size() != num_entries || label->coord_y.size() != num_entries || label->coord_z.size() != num_entries || label->value.size() != num_entries)
3521  {
3522  throw std::domain_error("Expected " + std::to_string(num_entries) + " entries in all Label vectors, but some did not match.\n");
3523  }
3524  }
3525 
3539  void read_label(Label *label, const std::string &filename)
3540  {
3541  std::ifstream infile(filename, std::fstream::in);
3542  if (infile.is_open())
3543  {
3544  read_label(label, &infile);
3545  infile.close();
3546  }
3547  else
3548  {
3549  throw std::runtime_error("Could not open label file '" + filename + "' for reading.\n");
3550  }
3551  }
3552 
3557  void write_label(const Label &label, std::ostream &os)
3558  {
3559  const size_t num_entries = label.num_entries();
3560  os << "#!ascii label from subject anonymous\n"
3561  << num_entries << "\n";
3562  for (size_t i = 0; i < num_entries; i++)
3563  {
3564  os << label.vertex[i] << " " << label.coord_x[i] << " " << label.coord_y[i] << " " << label.coord_z[i] << " " << label.value[i] << "\n";
3565  }
3566  }
3567 
3581  void write_label(const Label &label, const std::string &filename)
3582  {
3583  std::ofstream ofs;
3584  ofs.open(filename, std::ofstream::out);
3585  if (ofs.is_open())
3586  {
3587  write_label(label, ofs);
3588  ofs.close();
3589  }
3590  else
3591  {
3592  throw std::runtime_error("Unable to open label file '" + filename + "' for writing.\n");
3593  }
3594  }
3595 
3611  void write_mesh(const Mesh &mesh, const std::string &filename)
3612  {
3613  if (fs::util::ends_with(filename, {".ply", ".PLY"}))
3614  {
3615  mesh.to_ply_file(filename);
3616  }
3617  else if (fs::util::ends_with(filename, {".obj", ".OBJ"}))
3618  {
3619  mesh.to_obj_file(filename);
3620  }
3621  else if (fs::util::ends_with(filename, {".off", ".OFF"}))
3622  {
3623  mesh.to_off_file(filename);
3624  }
3625  else
3626  {
3627  fs::write_surf(mesh, filename);
3628  }
3629  }
3630 
3631 } // End namespace fs
void write_curv(std::ostream &os, std::vector< float > curv_data, int32_t num_faces=100000)
Write curv data to a stream.
Definition: libfs.h:3156
std::vector< float > Mdc
matrix
Definition: libfs.h:2260
std::vector< float > vertices
n x 3 vector of the x,y,z coordinates for the n vertices. The x,y,z coordinates for a single vertex f...
Definition: libfs.h:747
std::vector< int32_t > vertex_indices
Indices of the vertices, these always go from 0 to N-1 (where N is the number of vertices in the resp...
Definition: libfs.h:2121
size_t num_entries() const
Get the number of enties (regions) in this Colortable.
Definition: libfs.h:2081
Mgh(Curv curv)
Definition: libfs.h:2285
std::vector< float > coord_x
x coordinates of the vertices in case of a surface label, or voxels coordinates for a volume label...
Definition: libfs.h:3358
std::string to_off(const std::vector< uint8_t > col) const
Return string representing the mesh in PLY format.
Definition: libfs.h:1978
Models a FreeSurfer curv file that contains per-vertex float data.
Definition: libfs.h:2043
const int MRI_SHORT
MRI data type representing a 16 bit signed integer.
Definition: libfs.h:698
std::vector< float > Pxyz_c
x,y,z coordinates of central vertex
Definition: libfs.h:2261
std::vector< float > coord_y
y coordinates of the vertices in case of a surface label, or voxels coordinates for a volume label...
Definition: libfs.h:3359
int32_t dof
typically ignored
Definition: libfs.h:2248
unsigned int d2
size of data along 2nd dimension
Definition: libfs.h:2337
const std::string LOGTAG_EXCESSIVE
Logging threshold for warning messages.
Definition: libfs.h:190
An annotation, also known as a brain surface parcellation. Assigns to each vertex a region...
Definition: libfs.h:2119
const std::string LOGTAG_VERBOSE
Logging threshold for warning messages.
Definition: libfs.h:187
void read_curv(Curv *curv, std::istream *is, const std::string &source_filename="")
Read per-vertex brain morphometry data from a FreeSurfer curv stream.
Definition: libfs.h:2798
edge_set as_edgelist() const
Return edge list representation of this mesh.
Definition: libfs.h:956
Definition: libfs.h:3330
MghHeader()
Empty default constuctor.
Definition: libfs.h:2225
std::vector< int32_t > label
label integer computed from rgba values. Maps to the Annot.vertex_label field.
Definition: libfs.h:2078
Array4D(MghHeader *mgh_header)
Constructor for creating an empty 4D array based on dimensions specified in an fs::MghHeader.
Definition: libfs.h:2306
std::string to_obj() const
Return string representing the mesh in Wavefront Object (.obj) format.
Definition: libfs.h:892
std::string time_tag(std::chrono::system_clock::time_point t)
Get current time as string, e.g. for log messages.
Definition: libfs.h:160
static void from_obj(Mesh *mesh, const std::string &filename)
Read a brainmesh from a Wavefront object format mesh file.
Definition: libfs.h:1451
const std::string LOGTAG_WARNING
Logging threshold for warning messages.
Definition: libfs.h:181
static std::vector< float > smooth_pvd_nn(const std::vector< std::vector< size_t >> mesh_adj, const std::vector< float > pvd, const size_t num_iter=1, const bool with_nan=true, const bool detect_nan=true)
Smooth given per-vertex data using nearest neighbor smoothing based on adjacency list mesh represenat...
Definition: libfs.h:1067
std::vector< uint8_t > viridis(const std::vector< float > &data, float vmin=NAN, float vmax=NAN, uint8_t nan_r=255, uint8_t nan_g=255, uint8_t nan_b=255)
Map per-vertex numeric data to RGB colors using the Viridis perceptually-uniform colormap.
Definition: libfs.h:475
bool file_exists(const std::string &name)
Check whether a file exists (can be read) at given path.
Definition: libfs.h:344
const std::string LOGTAG_INFO
Logging threshold for warning messages.
Definition: libfs.h:184
A simple 4D array datastructure, useful for representing volume data.
Definition: libfs.h:2300
std::vector< float > data
The curvature data, one value per vertex. Something like the cortical thickness at each vertex...
Definition: libfs.h:2060
static void from_ply(Mesh *mesh, std::istream *is)
Read a brainmesh from a Stanford PLY format stream.
Definition: libfs.h:1609
const int32_t & fm_at(const size_t i, const size_t j) const
Retrieve a vertex index of a face, treating the faces vector as an nx3 matrix.
Definition: libfs.h:1787
std::vector< T > data
the data, as a 1D vector. Use fs::Array4D::at for easy access in 4D.
Definition: libfs.h:2340
std::vector< int32_t > b
green channel of RGBA color
Definition: libfs.h:2076
static void from_ply(Mesh *mesh, const std::string &filename)
Read a brainmesh from a Stanford PLY format mesh file.
Definition: libfs.h:1730
std::vector< int32_t > g
blue channel of RGBA color
Definition: libfs.h:2075
std::vector< float > read_curv_data(const std::string &filename)
Read per-vertex brain morphometry data from a FreeSurfer curv format file.
Definition: libfs.h:2983
void write_mgh(const Mgh &mgh, std::ostream &os)
Write MGH data to a stream.
Definition: libfs.h:3203
static void from_off(Mesh *mesh, const std::string &filename)
Read a brainmesh from an OFF format mesh file.
Definition: libfs.h:1587
std::vector< bool > vert_in_label(size_t surface_num_verts) const
Compute for each vertex of the surface whether it is inside the label.
Definition: libfs.h:3364
static std::vector< float > curv_data_for_orig_mesh(const std::vector< float > data_submesh, const std::unordered_map< int32_t, int32_t > submesh_to_orig_mapping, const int32_t orig_mesh_num_vertices, const float fill_value=std::numeric_limits< float >::quiet_NaN())
Given per-vertex data for a submesh, add NAN values inbetween to restore the original mesh size...
Definition: libfs.h:1311
MghHeader(Curv curv)
Definition: libfs.h:2226
MghHeader(std::vector< float > curv_data)
Definition: libfs.h:2234
int32_t dim1length
size of data along 1st dimension
Definition: libfs.h:2242
Models a triangular mesh, used for brain surface meshes.
Definition: libfs.h:727
const int MRI_UCHAR
MRI data type representing an 8 bit unsigned integer.
Definition: libfs.h:689
float xsize
size of voxels along 1st axis (x or r)
Definition: libfs.h:2257
size_t num_entries() const
Return the number of entries (vertices/voxels) in this label.
Definition: libfs.h:3380
unsigned int get_index(const unsigned int i1, const unsigned int i2, const unsigned int i3, const unsigned int i4) const
Get the index in the vector for the given 4D position.
Definition: libfs.h:2321
Models the data of an MGH file. Currently these are 1D vectors, but one can compute the 4D array usin...
Definition: libfs.h:2265
void read_label(Label *label, std::istream *is)
Read a FreeSurfer ASCII label from a stream.
Definition: libfs.h:3476
void to_ply_file(const std::string &filename) const
Export this mesh to a file in Stanford PLY format.
Definition: libfs.h:1946
MghData(std::vector< uint8_t > curv_data)
constructor to create MghData from MRI_UCHAR (uint8_t) data.
Definition: libfs.h:2269
Mesh(std::vector< float > cvertices, std::vector< int32_t > cfaces)
Construct a Mesh from the given vertices and faces.
Definition: libfs.h:731
std::vector< size_t > vertex_regions() const
Compute the region indices in the Colortable for all vertices in this brain surface parcellation...
Definition: libfs.h:2190
std::vector< std::vector< bool > > as_adjmatrix() const
Return adjacency matrix representation of this mesh.
Definition: libfs.h:917
const T & at(const unsigned int i1, const unsigned int i2, const unsigned int i3, const unsigned int i4) const
Get the value at the given 4D position.
Definition: libfs.h:2315
size_t num_values() const
Compute the number of values based on the dim*length header fields.
Definition: libfs.h:2252
Array4D(Mgh *mgh)
Constructor for creating an empty 4D array based on dimensions specified in the header of an fs::Mgh...
Definition: libfs.h:2309
unsigned int d1
size of data along 1st dimension
Definition: libfs.h:2336
Models the header of an MGH file.
Definition: libfs.h:2223
Definition: libfs.h:132
std::string to_ply() const
Return string representing the mesh in PLY format. Overload that works without passing a color vector...
Definition: libfs.h:1876
The colortable from an Annot file, can be used for parcellations and integer labels. Typically each index (in all fields) describes a brain region.
Definition: libfs.h:2070
const int MRI_FLOAT
MRI data type representing a 32 bit float.
Definition: libfs.h:695
void write_surf(std::vector< float > vertices, std::vector< int32_t > faces, std::ostream &os)
Write a mesh to a stream in FreeSurfer surf format.
Definition: libfs.h:3397
std::vector< std::vector< size_t > > as_adjlist(const bool via_matrix=true) const
Return adjacency list representation of this mesh.
Definition: libfs.h:985
static fs::Mesh construct_pyramid()
Construct and return a simple pyramidal mesh.
Definition: libfs.h:797
std::vector< float > smooth_pvd_nn(const std::vector< float > pvd, const size_t num_iter=1, const bool via_matrix=true, const bool with_nan=true, const bool detect_nan=true) const
Smooth given per-vertex data using nearest neighbor smoothing.
Definition: libfs.h:1044
std::vector< int32_t > id
internal region index
Definition: libfs.h:2072
void write_mesh(const Mesh &mesh, const std::string &filename)
Write a mesh to a file in different formats.
Definition: libfs.h:3611
std::vector< int32_t > region_vertices(int32_t region_label) const
Get all vertices of a region given by label in the brain surface parcellation. Returns an integer vec...
Definition: libfs.h:2142
std::vector< short > data_mri_short
data of type MRI_SHORT, check the dtype to see whether this is relevant for this instance.
Definition: libfs.h:2276
const int MRI_INT
MRI data type representing a 32 bit signed integer.
Definition: libfs.h:692
float zsize
size of voxels along 3rd axis (z or s)
Definition: libfs.h:2259
static void from_obj(Mesh *mesh, std::istream *is)
Read a brainmesh from a Wavefront object format stream.
Definition: libfs.h:1345
Curv(std::vector< float > curv_data)
Construct a Curv instance from the given per-vertex data.
Definition: libfs.h:2047
unsigned int d3
size of data along 3rd dimension
Definition: libfs.h:2338
Models a whole MGH file.
Definition: libfs.h:2280
size_t num_vertices() const
Get the number of vertices of this parcellation (or the associated surface).
Definition: libfs.h:2178
int32_t num_values_per_vertex
The number of values per vertex, stored in this file. Almost all apps (including FreeSurfer itself) o...
Definition: libfs.h:2066
void write_surf(const Mesh &mesh, const std::string &filename)
Write a mesh to a binary file in FreeSurfer surf format.
Definition: libfs.h:3455
void str_to_file(const std::string &filename, const std::string rep)
Write the given text representation (any string) to a file.
Definition: libfs.h:419
void read_annot(Annot *annot, std::istream *is)
Read a FreeSurfer annotation or brain surface parcellation from an annot stream.
Definition: libfs.h:2897
int32_t num_vertices
The number of vertices of the mesh to which this belongs. Can be deduced from length of &#39;data&#39;...
Definition: libfs.h:2063
std::vector< float > vertex_coords(const size_t vertex) const
Get all coordinates of the vertex, given by its index.
Definition: libfs.h:1832
static void from_off(Mesh *mesh, std::istream *is, const std::string &source_filename="")
Read a brainmesh from an Object File format (OFF) stream.
Definition: libfs.h:1474
std::vector< int32_t > face_vertices(const size_t face) const
Get all vertex indices of the face, given by its index.
Definition: libfs.h:1808
Mgh(std::vector< float > curv_data)
Definition: libfs.h:2290
std::vector< std::string > read_subjectsfile(const std::string &filename)
Read a vector of subject identifiers from a FreeSurfer subjects file.
Definition: libfs.h:2417
std::vector< float > value
the value of the label, can represent continuous data like a p-value, or sometimes simply 1...
Definition: libfs.h:3361
std::vector< int32_t > r
red channel of RGBA color
Definition: libfs.h:2074
void read_mgh(Mgh *mgh, const std::string &filename)
Read a FreeSurfer volume file in MGH format into the given Mgh struct.
Definition: libfs.h:2371
void to_ply_file(const std::string &filename, const std::vector< uint8_t > col) const
Export this mesh to a file in Stanford PLY format with vertex colors.
Definition: libfs.h:1956
std::unordered_set< std::tuple< size_t, size_t >, _tupleHashFunction > edge_set
Datastructure for storing, and quickly querying the existence of, mesh edges.
Definition: libfs.h:943
int32_t dtype
the MRI data type
Definition: libfs.h:2247
size_t num_faces() const
Return the number of faces in this mesh.
Definition: libfs.h:1770
void write_label(const Label &label, std::ostream &os)
Write label data to a stream.
Definition: libfs.h:3557
std::vector< int > vertex
vertex indices for the data in this label if it is a surface label. These are indices into the vertic...
Definition: libfs.h:3357
std::string to_ply(const std::vector< uint8_t > col) const
Return string representing the mesh in PLY format.
Definition: libfs.h:1892
std::vector< std::string > vertex_region_names() const
Compute the region names in the Colortable for all vertices in this brain surface parcellation...
Definition: libfs.h:2210
std::vector< uint8_t > vertex_colors(bool alpha=false) const
Get the vertex colors as an array of uchar values, 3 consecutive values are the red, green and blue channel values for a single vertex.
Definition: libfs.h:2157
static fs::Mesh construct_cube()
Construct and return a simple cube mesh.
Definition: libfs.h:760
int32_t dim4length
size of data along 4th dimension
Definition: libfs.h:2245
std::vector< int32_t > data_mri_int
data of type MRI_INT, check the dtype to see whether this is relevant for this instance.
Definition: libfs.h:2273
void read_mgh(Mgh *mgh, std::istream *is)
Read MGH data from a stream.
Definition: libfs.h:2440
MghData(std::vector< float > curv_data)
constructor to create MghData from MRI_FLOAT (float) data.
Definition: libfs.h:2271
MghData(std::vector< short > curv_data)
constructor to create MghData from MRI_SHORT (short) data.
Definition: libfs.h:2270
int32_t dim2length
size of data along 2nd dimension
Definition: libfs.h:2243
const std::string LOGTAG_ERROR
Logging threshold for error messages.
Definition: libfs.h:178
Mesh()
Construct an empty Mesh.
Definition: libfs.h:745
std::pair< std::unordered_map< int32_t, int32_t >, fs::Mesh > submesh_vertex(const std::vector< int32_t > &old_vertex_indices, const bool mapdir_fulltosubmesh=false) const
Compute a new mesh that is a submesh of this mesh, based on a subset of the vertices of this mesh...
Definition: libfs.h:1254
int32_t dim3length
size of data along 3rd dimension
Definition: libfs.h:2244
std::vector< uint8_t > data_mri_uchar
data of type MRI_UCHAR, check the dtype to see whether this is relevant for this instance.
Definition: libfs.h:2274
Label(std::vector< int > vertices)
Construct a Label from the given vertices / voxel numbers.
Definition: libfs.h:3348
static std::vector< std::vector< size_t > > extend_adj(const std::vector< std::vector< size_t >> mesh_adj, const size_t extend_by=1, std::vector< std::vector< size_t >> mesh_adj_ext=std::vector< std::vector< size_t >>())
Extend mesh neighborhoods based on mesh adjacency representation.
Definition: libfs.h:1188
void to_obj_file(const std::string &filename) const
Export this mesh to a file in Wavefront OBJ format.
Definition: libfs.h:1233
void read_surf(Mesh *surface, const std::string &filename)
Read a brain mesh from a file in binary FreeSurfer &#39;surf&#39; format into the given Mesh instance...
Definition: libfs.h:2709
Curv()
Construct an empty Curv instance.
Definition: libfs.h:2054
std::string fullpath(std::initializer_list< std::string > path_components, std::string path_sep=std::string("/"))
Construct a UNIX file system path from the given path_components.
Definition: libfs.h:372
std::vector< int32_t > region_vertices(const std::string &region_name) const
Get all vertices of a region given by name in the brain surface parcellation. Returns an integer vect...
Definition: libfs.h:2126
void to_off_file(const std::string &filename, const std::vector< uint8_t > col) const
Export this mesh to a file in OFF format with vertex colors (COFF).
Definition: libfs.h:2036
void read_mesh(Mesh *surface, const std::string &filename)
Read a triangular mesh from a surf, obj, or ply file into the given Mesh instance.
Definition: libfs.h:2760
int32_t get_region_idx(const std::string &query_name) const
Get the index of a region in the Colortable by region name. Returns a negative value if the region is...
Definition: libfs.h:2092
int32_t num_faces
The number of faces of the mesh to which this belongs, typically irrelevant and ignored.
Definition: libfs.h:2057
float ysize
size of voxels along 2nd axis (y or a)
Definition: libfs.h:2258
std::vector< float > data_mri_float
data of type MRI_FLOAT, check the dtype to see whether this is relevant for this instance.
Definition: libfs.h:2275
unsigned int num_values() const
Get number of values/voxels.
Definition: libfs.h:2331
Colortable colortable
A Colortable defining the regions (most importantly, the region name and visualization color)...
Definition: libfs.h:2123
MghHeader header
Header for this MGH instance.
Definition: libfs.h:2282
int16_t ras_good_flag
flag indicating whether the data in the RAS fields (Mdc, Pxyz_c) are valid. 1 means valid...
Definition: libfs.h:2249
void log(std::string const &message, std::string const loglevel="INFO")
Log a message, goes to stdout.
Definition: libfs.h:195
const float & vm_at(const size_t i, const size_t j) const
Retrieve a single (x, y, or z) coordinate of a vertex, treating the vertices vector as an nx3 matrix...
Definition: libfs.h:1858
std::vector< T > vflatten(std::vector< std::vector< T >> values)
Flatten 2D vector.
Definition: libfs.h:273
Label(std::vector< int > vertices, std::vector< float > values)
Construct a Label from the given vertices / voxel numbers and values.
Definition: libfs.h:3337
std::vector< int32_t > a
alpha channel of RGBA color
Definition: libfs.h:2077
const std::string LOGTAG_CRITICAL
Logging threshold for critical messages.
Definition: libfs.h:175
int32_t get_region_idx(int32_t query_label) const
Get the index of a region in the Colortable by label. Returns a negative value if the region is not f...
Definition: libfs.h:2105
Label()
Default constructor for a label.
Definition: libfs.h:3334
static fs::Mesh construct_grid(const size_t nx=4, const size_t ny=5, const float distx=1.0, const float disty=1.0)
Construct and return a simple planar grid mesh.
Definition: libfs.h:830
std::vector< int32_t > faces
n x 3 vector of the 3 vertex indices for the n triangles or faces. The 3 vertices of a single face fo...
Definition: libfs.h:748
void to_off_file(const std::string &filename) const
Export this mesh to a file in OFF format.
Definition: libfs.h:2029
std::vector< int32_t > vertex_labels
The label code for each vertex, defining the region it belongs to. Check in the Colortable for a regi...
Definition: libfs.h:2122
MghData(Curv curv)
constructor to create MghData from a Curv instance
Definition: libfs.h:2272
std::vector< float > coord_z
z coordinates of the vertices in case of a surface label, or voxels coordinates for a volume label...
Definition: libfs.h:3360
Array4D(unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)
Constructor for creating an empty 4D array of the given dimensions.
Definition: libfs.h:2303
MghData(std::vector< int32_t > curv_data)
constructor to create MghData from MRI_INT (int32_t) data.
Definition: libfs.h:2268
std::vector< float > read_desc_data(const std::string &filename)
Read per-vertex brain morphometry data from a FreeSurfer curv format or MGH format file...
Definition: libfs.h:3003
Mgh()
Empty default constuctor.
Definition: libfs.h:2284
std::string to_off() const
Return string representing the mesh in OFF format. Overload that works without passing a color vector...
Definition: libfs.h:1969
std::vector< std::string > name
region name
Definition: libfs.h:2073
MghData data
4D data for this MGH instance.
Definition: libfs.h:2283
unsigned int d4
size of data along 4th dimension
Definition: libfs.h:2339
size_t num_vertices() const
Return the number of vertices in this mesh.
Definition: libfs.h:1756
void read_mgh_header(MghHeader *, const std::string &)
Read the header of a FreeSurfer volume file in MGH format into the given MghHeader struct...
Definition: libfs.h:2584