12 #include <unordered_set> 13 #include <unordered_map> 50 #define LIBFS_VERSION "0.5.0" 58 #define LIBFS_VERSION_MAJOR 0 64 #define LIBFS_VERSION_MINOR 5 70 #define LIBFS_VERSION_PATCH 0 77 #define LIBFS_MAX_ALLOC_BYTES_DEFAULT (2ULL * 1024ULL * 1024ULL * 1024ULL) 80 #ifndef LIBFS_MAX_ALLOC_BYTES 81 #define LIBFS_MAX_ALLOC_BYTES LIBFS_MAX_ALLOC_BYTES_DEFAULT 85 #ifndef LIBFS_MAX_STRING_LENGTH 86 #define LIBFS_MAX_STRING_LENGTH 4096 90 #ifndef LIBFS_MAX_COLORTABLE_ENTRIES 91 #define LIBFS_MAX_COLORTABLE_ENTRIES 10000 168 #define LIBFS_APPTAG "[libfs] " 178 #define LIBFS_DBG_WARNING 181 #ifdef LIBFS_DBG_NONE 182 #undef LIBFS_DBG_WARNING 185 #ifdef LIBFS_DBG_CRITICAL 186 #undef LIBFS_DBG_WARNING 189 #ifdef LIBFS_DBG_ERROR 190 #undef LIBFS_DBG_WARNING 195 #ifdef LIBFS_DBG_EXCESSIVE 196 #define LIBFS_DBG_VERBOSE 199 #ifdef LIBFS_DBG_VERBOSE 200 #define LIBFS_DBG_INFO 203 #ifdef LIBFS_DBG_INFO 204 #define LIBFS_DBG_WARNING 214 #ifdef LIBFS_DBG_WARNING 215 #define LIBFS_DBG_ERROR 224 #ifdef LIBFS_DBG_ERROR 225 #define LIBFS_DBG_CRITICAL 239 tm _localtime(
const std::time_t &time)
242 #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 243 ::localtime_s(&tm_snapshot, &time);
245 ::localtime_r(&time, &tm_snapshot);
258 std::string
time_tag(std::chrono::system_clock::time_point t)
260 auto as_time_t = std::chrono::system_clock::to_time_t(t);
262 char time_buffer[64];
264 tm = _localtime(as_time_t);
265 if (std::strftime(time_buffer,
sizeof(time_buffer),
"%F %T", &tm))
267 return std::string{time_buffer};
269 throw std::runtime_error(
"Failed to get current date as string");
293 inline void log(std::string
const &message, std::string
const loglevel =
"INFO")
302 inline bool safe_multiply(
size_t a,
size_t b,
size_t &result)
304 if (a == 0 || b == 0)
309 if (a > std::numeric_limits<size_t>::max() / b)
321 inline bool check_alloc(
size_t num_elements,
size_t bytes_per_element)
323 size_t total_bytes = 0;
324 if (!safe_multiply(num_elements, bytes_per_element, total_bytes))
337 inline size_t get_file_size(
const std::string &filename)
339 std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
344 std::streampos end = ifs.tellg();
349 return static_cast<size_t>(end);
354 inline bool is_finite_float(
float value)
356 return !std::isnan(value) && !std::isinf(value);
369 inline bool ends_with(std::string
const &value, std::string
const &suffix)
371 if (suffix.size() > value.size())
373 return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
384 inline bool ends_with(std::string
const &value, std::initializer_list<std::string> suffixes)
386 for (
auto suffix : suffixes)
388 if (ends_with(value, suffix))
408 template <
typename T>
409 std::vector<std::vector<T>> v2d(std::vector<T> values,
size_t num_cols)
411 std::vector<std::vector<T>> result;
412 for (std::size_t i = 0; i < values.size(); ++i)
414 if (i % num_cols == 0)
416 result.resize(result.size() + 1);
418 result[i / num_cols].push_back(values[i]);
433 template <
typename T>
434 std::vector<T>
vflatten(std::vector<std::vector<T>> values)
436 size_t total_size = 0;
437 for (std::size_t i = 0; i < values.size(); i++)
439 total_size += values[i].size();
442 std::vector<T> result = std::vector<T>(total_size);
444 for (std::size_t i = 0; i < values.size(); i++)
446 for (std::size_t j = 0; j < values[i].size(); j++)
448 result[cur_idx] = values[i][j];
465 inline bool starts_with(std::string
const &value, std::string
const &prefix)
467 if (prefix.length() > value.length())
469 return value.rfind(prefix, 0) == 0;
482 inline bool starts_with(std::string
const &value, std::initializer_list<std::string> prefixes)
484 for (
auto prefix : prefixes)
486 if (starts_with(value, prefix))
507 if (FILE *file = fopen(name.c_str(),
"r"))
533 std::string
fullpath(std::initializer_list<std::string> path_components, std::string path_sep = std::string(
"/"))
536 if (path_components.size() == 0)
538 throw std::invalid_argument(
"The 'path_components' must not be empty.");
542 std::string comp_mod;
544 for (
auto comp : path_components)
549 if (starts_with(comp, path_sep))
551 comp_mod = comp.substr(1, comp.size() - 1);
555 if (ends_with(comp_mod, path_sep))
557 comp_mod = comp_mod.substr(0, comp_mod.size() - 1);
561 if (idx < path_components.size() - 1)
580 void str_to_file(
const std::string &filename,
const std::string rep)
583 ofs.open(filename, std::ofstream::out);
584 #ifdef LIBFS_DBG_VERBOSE 585 std::cout <<
LIBFS_APPTAG <<
"Opening file '" << filename <<
"' for writing.\n";
594 throw std::runtime_error(
"Unable to open file '" + filename +
"' for writing.\n");
636 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)
638 std::vector<uint8_t> colors;
643 colors.reserve(data.size() * 3);
647 static const float lut[768] = {
648 0.267004, 0.004874, 0.329415, 0.26851, 0.009605, 0.335427, 0.269944, 0.014625,
649 0.341379, 0.271305, 0.019942, 0.347269, 0.272594, 0.025563, 0.353093, 0.273809,
650 0.031497, 0.358853, 0.274952, 0.037752, 0.364543, 0.276022, 0.044167, 0.370164,
651 0.277018, 0.050344, 0.375715, 0.277941, 0.056324, 0.381191, 0.278791, 0.062145,
652 0.386592, 0.279566, 0.067836, 0.391917, 0.280267, 0.073417, 0.397163, 0.280894,
653 0.078907, 0.402329, 0.281446, 0.08432, 0.407414, 0.281924, 0.089666, 0.412415,
654 0.282327, 0.094955, 0.417331, 0.282656, 0.100196, 0.42216, 0.28291, 0.105393,
655 0.426902, 0.283091, 0.110553, 0.431554, 0.283197, 0.11568, 0.436115, 0.283229,
656 0.120777, 0.440584, 0.283187, 0.125848, 0.44496, 0.283072, 0.130895, 0.449241,
657 0.282884, 0.13592, 0.453427, 0.282623, 0.140926, 0.457517, 0.28229, 0.145912,
658 0.46151, 0.281887, 0.150881, 0.465405, 0.281412, 0.155834, 0.469201, 0.280868,
659 0.160771, 0.472899, 0.280255, 0.165693, 0.476498, 0.279574, 0.170599, 0.479997,
660 0.278826, 0.17549, 0.483397, 0.278012, 0.180367, 0.486697, 0.277134, 0.185228,
661 0.489898, 0.276194, 0.190074, 0.493001, 0.275191, 0.194905, 0.496005, 0.274128,
662 0.199721, 0.498911, 0.273006, 0.20452, 0.501721, 0.271828, 0.209303, 0.504434,
663 0.270595, 0.214069, 0.507052, 0.269308, 0.218818, 0.509577, 0.267968, 0.223549,
664 0.512008, 0.26658, 0.228262, 0.514349, 0.265145, 0.232956, 0.516599, 0.263663,
665 0.237631, 0.518762, 0.262138, 0.242286, 0.520837, 0.260571, 0.246922, 0.522828,
666 0.258965, 0.251537, 0.524736, 0.257322, 0.25613, 0.526563, 0.255645, 0.260703,
667 0.528312, 0.253935, 0.265254, 0.529983, 0.252194, 0.269783, 0.531579, 0.250425,
668 0.27429, 0.533103, 0.248629, 0.278775, 0.534556, 0.246811, 0.283237, 0.535941,
669 0.244972, 0.287675, 0.53726, 0.243113, 0.292092, 0.538516, 0.241237, 0.296485,
670 0.539709, 0.239346, 0.300855, 0.540844, 0.237441, 0.305202, 0.541921, 0.235526,
671 0.309527, 0.542944, 0.233603, 0.313828, 0.543914, 0.231674, 0.318106, 0.544834,
672 0.229739, 0.322361, 0.545706, 0.227802, 0.326594, 0.546532, 0.225863, 0.330805,
673 0.547314, 0.223925, 0.334994, 0.548053, 0.221989, 0.339161, 0.548752, 0.220057,
674 0.343307, 0.549413, 0.21813, 0.347432, 0.550038, 0.21621, 0.351535, 0.550627,
675 0.214298, 0.355619, 0.551184, 0.212395, 0.359683, 0.55171, 0.210503, 0.363727,
676 0.552206, 0.208623, 0.367752, 0.552675, 0.206756, 0.371758, 0.553117, 0.204903,
677 0.375746, 0.553533, 0.203063, 0.379716, 0.553925, 0.201239, 0.38367, 0.554294,
678 0.19943, 0.387607, 0.554642, 0.197636, 0.391528, 0.554969, 0.19586, 0.395433,
679 0.555276, 0.1941, 0.399323, 0.555565, 0.192357, 0.403199, 0.555836, 0.190631,
680 0.407061, 0.556089, 0.188923, 0.41091, 0.556326, 0.187231, 0.414746, 0.556547,
681 0.185556, 0.41857, 0.556753, 0.183898, 0.422383, 0.556944, 0.182256, 0.426184,
682 0.55712, 0.180629, 0.429975, 0.557282, 0.179019, 0.433756, 0.55743, 0.177423,
683 0.437527, 0.557565, 0.175841, 0.44129, 0.557685, 0.174274, 0.445044, 0.557792,
684 0.172719, 0.448791, 0.557885, 0.171176, 0.45253, 0.557965, 0.169646, 0.456262,
685 0.55803, 0.168126, 0.459988, 0.558082, 0.166617, 0.463708, 0.558119, 0.165117,
686 0.467423, 0.558141, 0.163625, 0.471133, 0.558148, 0.162142, 0.474838, 0.55814,
687 0.160665, 0.47854, 0.558115, 0.159194, 0.482237, 0.558073, 0.157729, 0.485932,
688 0.558013, 0.15627, 0.489624, 0.557936, 0.154815, 0.493313, 0.55784, 0.153364,
689 0.497, 0.557724, 0.151918, 0.500685, 0.557587, 0.150476, 0.504369, 0.55743,
690 0.149039, 0.508051, 0.55725, 0.147607, 0.511733, 0.557049, 0.14618, 0.515413,
691 0.556823, 0.144759, 0.519093, 0.556572, 0.143343, 0.522773, 0.556295, 0.141935,
692 0.526453, 0.555991, 0.140536, 0.530132, 0.555659, 0.139147, 0.533812, 0.555298,
693 0.13777, 0.537492, 0.554906, 0.136408, 0.541173, 0.554483, 0.135066, 0.544853,
694 0.554029, 0.133743, 0.548535, 0.553541, 0.132444, 0.552216, 0.553018, 0.131172,
695 0.555899, 0.552459, 0.129933, 0.559582, 0.551864, 0.128729, 0.563265, 0.551229,
696 0.127568, 0.566949, 0.550556, 0.126453, 0.570633, 0.549841, 0.125394, 0.574318,
697 0.549086, 0.124395, 0.578002, 0.548287, 0.123463, 0.581687, 0.547445, 0.122606,
698 0.585371, 0.546557, 0.121831, 0.589055, 0.545623, 0.121148, 0.592739, 0.544641,
699 0.120565, 0.596422, 0.543611, 0.120092, 0.600104, 0.54253, 0.119738, 0.603785,
700 0.5414, 0.119512, 0.607464, 0.540218, 0.119423, 0.611141, 0.538982, 0.119483,
701 0.614817, 0.537692, 0.119699, 0.61849, 0.536347, 0.120081, 0.622161, 0.534946,
702 0.120638, 0.625828, 0.533488, 0.12138, 0.629492, 0.531973, 0.122312, 0.633153,
703 0.530398, 0.123444, 0.636809, 0.528763, 0.12478, 0.640461, 0.527068, 0.126326,
704 0.644107, 0.525311, 0.128087, 0.647749, 0.523491, 0.130067, 0.651384, 0.521608,
705 0.132268, 0.655014, 0.519661, 0.134692, 0.658636, 0.517649, 0.137339, 0.662252,
706 0.515571, 0.14021, 0.665859, 0.513427, 0.143303, 0.669459, 0.511215, 0.146616,
707 0.67305, 0.508936, 0.150148, 0.676631, 0.506589, 0.153894, 0.680203, 0.504172,
708 0.157851, 0.683765, 0.501686, 0.162016, 0.687316, 0.499129, 0.166383, 0.690856,
709 0.496502, 0.170948, 0.694384, 0.493803, 0.175707, 0.6979, 0.491033, 0.180653,
710 0.701402, 0.488189, 0.185783, 0.704891, 0.485273, 0.19109, 0.708366, 0.482284,
711 0.196571, 0.711827, 0.479221, 0.202219, 0.715272, 0.476084, 0.20803, 0.718701,
712 0.472873, 0.214, 0.722114, 0.469588, 0.220124, 0.725509, 0.466226, 0.226397,
713 0.728888, 0.462789, 0.232815, 0.732247, 0.459277, 0.239374, 0.735588, 0.455688,
714 0.24607, 0.73891, 0.452024, 0.252899, 0.742211, 0.448284, 0.259857, 0.745492,
715 0.444467, 0.266941, 0.748751, 0.440573, 0.274149, 0.751988, 0.436601, 0.281477,
716 0.755203, 0.432552, 0.288921, 0.758394, 0.428426, 0.296479, 0.761561, 0.424223,
717 0.304148, 0.764704, 0.419943, 0.311925, 0.767822, 0.415586, 0.319809, 0.770914,
718 0.411152, 0.327796, 0.77398, 0.40664, 0.335885, 0.777018, 0.402049, 0.344074,
719 0.780029, 0.397381, 0.35236, 0.783011, 0.392636, 0.360741, 0.785964, 0.387814,
720 0.369214, 0.788888, 0.382914, 0.377779, 0.791781, 0.377939, 0.386433, 0.794644,
721 0.372886, 0.395174, 0.797475, 0.367757, 0.404001, 0.800275, 0.362552, 0.412913,
722 0.803041, 0.357269, 0.421908, 0.805774, 0.35191, 0.430983, 0.808473, 0.346476,
723 0.440137, 0.811138, 0.340967, 0.449368, 0.813768, 0.335384, 0.458674, 0.816363,
724 0.329727, 0.468053, 0.818921, 0.323998, 0.477504, 0.821444, 0.318195, 0.487026,
725 0.823929, 0.312321, 0.496615, 0.826376, 0.306377, 0.506271, 0.828786, 0.300362,
726 0.515992, 0.831158, 0.294279, 0.525776, 0.833491, 0.288127, 0.535621, 0.835785,
727 0.281908, 0.545524, 0.838039, 0.275626, 0.555484, 0.840254, 0.269281, 0.565498,
728 0.84243, 0.262877, 0.575563, 0.844566, 0.256415, 0.585678, 0.846661, 0.249897,
729 0.595839, 0.848717, 0.243329, 0.606045, 0.850733, 0.236712, 0.616293, 0.852709,
730 0.230052, 0.626579, 0.854645, 0.223353, 0.636902, 0.856542, 0.21662, 0.647257,
731 0.8584, 0.209861, 0.657642, 0.860219, 0.203082, 0.668054, 0.861999, 0.196293,
732 0.678489, 0.863742, 0.189503, 0.688944, 0.865448, 0.182725, 0.699415, 0.867117,
733 0.175971, 0.709898, 0.868751, 0.169257, 0.720391, 0.87035, 0.162603, 0.730889,
734 0.871916, 0.156029, 0.741388, 0.873449, 0.149561, 0.751884, 0.874951, 0.143228,
735 0.762373, 0.876424, 0.137064, 0.772852, 0.877868, 0.131109, 0.783315, 0.879285,
736 0.125405, 0.79376, 0.880678, 0.120005, 0.804182, 0.882046, 0.114965, 0.814576,
737 0.883393, 0.110347, 0.82494, 0.88472, 0.106217, 0.83527, 0.886029, 0.102646,
738 0.845561, 0.887322, 0.099702, 0.85581, 0.888601, 0.097452, 0.866013, 0.889868,
739 0.095953, 0.876168, 0.891125, 0.09525, 0.886271, 0.892374, 0.095374, 0.89632,
740 0.893616, 0.096335, 0.906311, 0.894855, 0.098125, 0.916242, 0.896091, 0.100717,
741 0.926106, 0.89733, 0.104071, 0.935904, 0.89857, 0.108131, 0.945636, 0.899815,
742 0.112838, 0.9553, 0.901065, 0.118128, 0.964894, 0.902323, 0.123941, 0.974417,
743 0.90359, 0.130215, 0.983868, 0.904867, 0.136897, 0.993248, 0.906157, 0.143936,
748 bool auto_min = std::isnan(vmin);
749 bool auto_max = std::isnan(vmax);
752 float data_min = NAN;
753 float data_max = NAN;
754 bool have_finite =
false;
755 for (
size_t i = 0; i < data.size(); i++)
757 if (std::isnan(data[i]))
769 if (data[i] < data_min)
773 if (data[i] > data_max)
780 float lo = auto_min ? data_min : vmin;
781 float hi = auto_max ? data_max : vmax;
783 if (!auto_min && !auto_max)
787 throw std::invalid_argument(
"In viridis(): 'vmin' must not be greater than 'vmax'.");
794 for (
size_t i = 0; i < data.size(); i++)
796 colors.push_back(nan_r);
797 colors.push_back(nan_g);
798 colors.push_back(nan_b);
803 bool constant = (hi <= lo);
805 for (
size_t i = 0; i < data.size(); i++)
807 if (std::isnan(data[i]))
809 colors.push_back(nan_r);
810 colors.push_back(nan_g);
811 colors.push_back(nan_b);
822 t = (data[i] - lo) / (hi - lo);
823 if (t < 0.0f) { t = 0.0f; }
824 if (t > 1.0f) { t = 1.0f; }
827 float pos = t * (n - 1);
828 int idx0 =
static_cast<int>(pos);
829 if (idx0 < 0) { idx0 = 0; }
830 if (idx0 > n - 2) { idx0 = n - 2; }
832 float frac = pos -
static_cast<float>(idx0);
834 for (
int c = 0; c < 3; c++)
836 float val = lut[idx0 * 3 + c] * (1.0f - frac) + lut[idx1 * 3 + c] * frac;
837 int iv =
static_cast<int>(val * 255.0f + 0.5f);
838 if (iv < 0) { iv = 0; }
839 if (iv > 255) { iv = 255; }
840 colors.push_back(static_cast<uint8_t>(iv));
862 int _fread3(std::istream &);
863 template <
typename T>
864 T _freadt(std::istream &);
865 std::string _freadstringnewline(std::istream &);
866 std::string _freadfixedlengthstring(std::istream &,
size_t,
bool,
size_t);
867 bool _ends_with(std::string
const &fullString, std::string
const &ending);
868 size_t _vidx_2d(
size_t,
size_t,
size_t);
873 void read_nifti(
Mgh *, std::istream *,
bool force_standard =
false);
874 void read_nifti(
Mgh *,
const std::string &,
bool force_standard =
false);
875 #ifdef LIBFS_HAS_ZLIB 876 inline void read_nifti_gz(
Mgh *,
const std::string &,
bool force_standard =
false);
877 inline void write_nifti_gz(
const Mgh &,
const std::string &);
901 Mesh(std::vector<float> cvertices, std::vector<int32_t> cfaces)
903 vertices = cvertices;
916 Mesh(std::vector<std::vector<float>> cvertices, std::vector<std::vector<int32_t>> cfaces)
918 vertices = util::vflatten(cvertices);
919 faces = util::vflatten(cfaces);
950 mesh.
faces = {0, 2, 3,
984 mesh.
faces = {0, 1, 2,
1011 if (nx < 2 || ny < 2)
1013 throw std::runtime_error(
"Parameters nx and ny must be at least 2.");
1016 size_t num_vertices = nx * ny;
1017 size_t num_faces = ((nx - 1) * (ny - 1)) * 2;
1018 std::vector<float> vertices;
1019 vertices.reserve(num_vertices * 3);
1020 std::vector<int> faces;
1021 faces.reserve(num_faces * 3);
1024 float cur_x, cur_y, cur_z;
1025 cur_x = cur_y = cur_z = 0.0;
1026 for (
size_t i = 0; i < nx; i++)
1029 for (
size_t j = 0; j < ny; j++)
1031 vertices.push_back(cur_x);
1032 vertices.push_back(cur_y);
1033 vertices.push_back(cur_z);
1040 for (
size_t i = 0; i < num_vertices; i++)
1042 if ((i + 1) % ny == 0 || i >= num_vertices - ny)
1048 faces.push_back(
int(i));
1049 faces.push_back(
int(i + ny + 1));
1050 faces.push_back(
int(i + 1));
1052 faces.push_back(
int(i));
1053 faces.push_back(
int(i + ny + 1));
1054 faces.push_back(
int(i + ny));
1074 std::vector<uint8_t> empty_col;
1075 return (this->to_obj(empty_col));
1090 std::string
to_obj(
const std::vector<uint8_t> col)
const 1092 bool use_vertex_colors = col.size() != 0;
1093 std::stringstream objs;
1094 for (
size_t vidx = 0; vidx < this->vertices.size(); vidx += 3)
1096 objs <<
"v " << vertices[vidx] <<
" " << vertices[vidx + 1] <<
" " << vertices[vidx + 2];
1097 if (use_vertex_colors)
1099 if (col.size() != this->vertices.size())
1101 throw std::invalid_argument(
"Number of vertex coordinates and vertex colors must match when writing OBJ file, but got " + std::to_string(this->vertices.size()) +
" and " + std::to_string(col.size()) +
".");
1103 objs <<
" " << (col[vidx] / 255.0f) <<
" " << (col[vidx + 1] / 255.0f) <<
" " << (col[vidx + 2] / 255.0f);
1107 for (
size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
1109 objs <<
"f " << faces[fidx] + 1 <<
" " << faces[fidx + 1] + 1 <<
" " << faces[fidx + 2] + 1 <<
"\n";
1111 return (objs.str());
1127 std::vector<std::vector<bool>> adjm = std::vector<std::vector<bool>>(this->num_vertices(), std::vector<bool>(this->num_vertices(),
false));
1128 for (
size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
1130 adjm[faces[fidx]][faces[fidx + 1]] =
true;
1131 adjm[faces[fidx + 1]][faces[fidx]] =
true;
1132 adjm[faces[fidx + 1]][faces[fidx + 2]] =
true;
1133 adjm[faces[fidx + 2]][faces[fidx + 1]] =
true;
1134 adjm[faces[fidx + 2]][faces[fidx]] =
true;
1135 adjm[faces[fidx]][faces[fidx + 2]] =
true;
1141 struct _tupleHashFunction
1143 size_t operator()(
const std::tuple<size_t, size_t> &x)
const 1145 size_t a = std::get<0>(x);
1146 size_t b = std::get<1>(x);
1147 return a ^ (b << 1) ^ (b >> (
sizeof(size_t) * 8 - 1));
1153 typedef std::unordered_set<std::tuple<size_t, size_t>, _tupleHashFunction>
edge_set;
1169 for (
size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
1171 edges.insert(std::make_tuple(faces[fidx], faces[fidx + 1]));
1172 edges.insert(std::make_tuple(faces[fidx + 1], faces[fidx]));
1174 edges.insert(std::make_tuple(faces[fidx + 1], faces[fidx + 2]));
1175 edges.insert(std::make_tuple(faces[fidx + 2], faces[fidx + 1]));
1177 edges.insert(std::make_tuple(faces[fidx], faces[fidx + 2]));
1178 edges.insert(std::make_tuple(faces[fidx + 2], faces[fidx]));
1195 std::vector<std::vector<size_t>>
as_adjlist(
const bool via_matrix =
true)
const 1199 return (this->_as_adjlist_via_edgeset());
1201 std::vector<std::vector<bool>> adjm = this->as_adjmatrix();
1202 std::vector<std::vector<size_t>> adjl = std::vector<std::vector<size_t>>(this->num_vertices(), std::vector<size_t>());
1203 size_t nv = adjm.size();
1204 for (
size_t i = 0; i < nv; i++)
1206 for (
size_t j = i + 1; j < nv; j++)
1208 if (adjm[i][j] ==
true)
1210 adjl[i].push_back(j);
1211 adjl[j].push_back(i);
1228 std::vector<std::vector<size_t>> _as_adjlist_via_edgeset()
const 1230 edge_set edges = this->as_edgelist();
1231 std::vector<std::vector<size_t>> adjl = std::vector<std::vector<size_t>>(this->num_vertices(), std::vector<size_t>());
1232 for (
const std::tuple<size_t, size_t> &e : edges)
1234 adjl[std::get<0>(e)].push_back(std::get<1>(e));
1254 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 1257 const std::vector<std::vector<size_t>> adjlist = this->as_adjlist(via_matrix);
1277 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)
1279 assert(pvd.size() == mesh_adj.size());
1280 bool final_with_nan = with_nan;
1283 final_with_nan =
false;
1284 for (
size_t i = 0; i < pvd.size(); i++)
1286 if (std::isnan(pvd[i]))
1288 final_with_nan =
true;
1295 return fs::Mesh::_smooth_pvd_nn_nan(mesh_adj, pvd, num_iter);
1297 std::vector<float> current_pvd_source;
1298 std::vector<float> current_pvd_smoothed = std::vector<float>(pvd.size());
1302 for (
size_t i = 0; i < num_iter; i++)
1306 current_pvd_source = pvd;
1310 current_pvd_source = current_pvd_smoothed;
1312 for (
size_t v_idx = 0; v_idx < mesh_adj.size(); v_idx++)
1314 num_neigh = mesh_adj[v_idx].size();
1315 val_sum = current_pvd_source[v_idx] / (num_neigh + 1);
1316 for (
size_t neigh_rel_idx = 0; neigh_rel_idx < num_neigh; neigh_rel_idx++)
1318 val_sum += current_pvd_source[mesh_adj[v_idx][neigh_rel_idx]] / (num_neigh + 1);
1320 current_pvd_smoothed[v_idx] = val_sum;
1323 return current_pvd_smoothed;
1342 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)
1344 std::vector<float> current_pvd_source;
1345 std::vector<float> current_pvd_smoothed = std::vector<float>(pvd.size());
1349 size_t num_non_nan_values;
1351 for (
size_t i = 0; i < num_iter; i++)
1356 current_pvd_source = pvd;
1360 current_pvd_source = current_pvd_smoothed;
1363 for (
size_t v_idx = 0; v_idx < mesh_adj.size(); v_idx++)
1365 if (std::isnan(current_pvd_source[v_idx]))
1367 current_pvd_smoothed[v_idx] = NAN;
1370 val_sum = current_pvd_source[v_idx];
1371 num_non_nan_values = 1;
1372 num_neigh = mesh_adj[v_idx].size();
1373 for (
size_t neigh_rel_idx = 0; neigh_rel_idx < num_neigh; neigh_rel_idx++)
1375 neigh_val = current_pvd_source[mesh_adj[v_idx][neigh_rel_idx]];
1376 if (std::isnan(neigh_val))
1382 val_sum += neigh_val;
1383 num_non_nan_values++;
1386 current_pvd_smoothed[v_idx] = val_sum / (float)num_non_nan_values;
1389 return current_pvd_smoothed;
1398 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>>())
1400 size_t num_vertices = mesh_adj.size();
1401 if (mesh_adj_ext.size() == 0)
1403 mesh_adj_ext = mesh_adj;
1405 std::vector<size_t> neighborhood;
1406 std::vector<size_t> ext_neighborhood;
1407 for (
size_t ext_idx = 0; ext_idx < extend_by; ext_idx++)
1409 for (
size_t source_vert_idx = 0; source_vert_idx < num_vertices; source_vert_idx++)
1411 neighborhood = mesh_adj_ext[source_vert_idx];
1413 for (
size_t neigh_vert_rel_idx = 0; neigh_vert_rel_idx < neighborhood.size(); neigh_vert_rel_idx++)
1415 for (
size_t canidate_rel_idx = 0; canidate_rel_idx < mesh_adj[neighborhood[neigh_vert_rel_idx]].size(); canidate_rel_idx++)
1417 if (mesh_adj[neighborhood[neigh_vert_rel_idx]][canidate_rel_idx] != source_vert_idx)
1419 mesh_adj_ext[source_vert_idx].push_back(mesh_adj[neighborhood[neigh_vert_rel_idx]][canidate_rel_idx]);
1424 std::sort(mesh_adj_ext[source_vert_idx].begin(), mesh_adj_ext[source_vert_idx].end());
1425 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());
1428 return mesh_adj_ext;
1445 fs::util::str_to_file(filename, this->to_obj());
1450 void to_obj_file(
const std::string &filename,
const std::vector<uint8_t> col)
const 1452 fs::util::str_to_file(filename, this->to_obj(col));
1471 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 1474 std::vector<float> new_vertices;
1475 std::vector<int> new_faces;
1476 std::unordered_map<int32_t, int32_t> vertex_index_map_full2submesh;
1477 int32_t new_vertex_idx = 0;
1478 for (
size_t i = 0; i < old_vertex_indices.size(); i++)
1480 vertex_index_map_full2submesh[old_vertex_indices[i]] = new_vertex_idx;
1481 new_vertices.push_back(this->vertices[
size_t(old_vertex_indices[i]) * 3]);
1482 new_vertices.push_back(this->vertices[
size_t(old_vertex_indices[i]) * 3 + 1]);
1483 new_vertices.push_back(this->vertices[
size_t(old_vertex_indices[i]) * 3 + 2]);
1489 for (
size_t i = 0; i < this->num_faces(); i++)
1491 face_v0 = this->faces[i * 3];
1492 face_v1 = this->faces[i * 3 + 1];
1493 face_v2 = this->faces[i * 3 + 2];
1494 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()))
1496 new_faces.push_back(vertex_index_map_full2submesh[face_v0]);
1497 new_faces.push_back(vertex_index_map_full2submesh[face_v1]);
1498 new_faces.push_back(vertex_index_map_full2submesh[face_v2]);
1502 submesh.
faces = new_faces;
1504 std::pair<std::unordered_map<int32_t, int32_t>,
fs::Mesh> result;
1505 if (!mapdir_fulltosubmesh)
1507 std::unordered_map<int32_t, int32_t> vertex_index_map_submesh2full;
1508 for (
auto const &pair : vertex_index_map_full2submesh)
1510 vertex_index_map_submesh2full[pair.second] = pair.first;
1512 result = std::pair<std::unordered_map<int32_t, int32_t>,
fs::Mesh>(vertex_index_map_submesh2full, submesh);
1516 result = std::pair<std::unordered_map<int32_t, int32_t>,
fs::Mesh>(vertex_index_map_full2submesh, submesh);
1538 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())
1541 if (submesh_to_orig_mapping.size() != data_submesh.size())
1543 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()) +
".");
1546 std::vector<float> data_orig_mesh(orig_mesh_num_vertices, fill_value);
1547 for (
size_t i = 0; i < data_submesh.size(); i++)
1549 auto got = submesh_to_orig_mapping.find(
int(i));
1550 if (got != submesh_to_orig_mapping.end())
1552 data_orig_mesh[got->second] = data_submesh[i];
1555 return (data_orig_mesh);
1577 std::vector<float> vertices;
1578 std::vector<int> faces;
1579 std::vector<uint8_t> vertex_colors;
1580 int detected_format = -1;
1582 #ifdef LIBFS_DBG_INFO 1583 size_t num_lines_ignored = 0;
1586 while (std::getline(*is, line))
1589 std::istringstream iss(line);
1590 if (fs::util::starts_with(line,
"#"))
1596 if (fs::util::starts_with(line,
"v "))
1598 std::string elem_type_identifier;
1600 if (!(iss >> elem_type_identifier >> x >> y >> z))
1602 throw std::domain_error(
"Could not parse vertex line " + std::to_string(line_idx + 1) +
" of OBJ data, invalid format.\n");
1604 assert(elem_type_identifier ==
"v");
1605 vertices.push_back(x);
1606 vertices.push_back(y);
1607 vertices.push_back(z);
1612 if (detected_format == -1)
1615 if ((iss >> vr >> vg >> vb))
1622 detected_format = 0;
1626 detected_format = 1;
1628 int ri =
static_cast<int>(vr * 255.0f + 0.5f);
1629 int gi =
static_cast<int>(vg * 255.0f + 0.5f);
1630 int bi =
static_cast<int>(vb * 255.0f + 0.5f);
1631 if (ri < 0) { ri = 0; }
1632 if (ri > 255) { ri = 255; }
1633 if (gi < 0) { gi = 0; }
1634 if (gi > 255) { gi = 255; }
1635 if (bi < 0) { bi = 0; }
1636 if (bi > 255) { bi = 255; }
1637 vertex_colors.push_back(static_cast<uint8_t>(ri));
1638 vertex_colors.push_back(static_cast<uint8_t>(gi));
1639 vertex_colors.push_back(static_cast<uint8_t>(bi));
1644 detected_format = 0;
1647 else if (detected_format == 1)
1651 if (!(iss >> vr >> vg >> vb))
1653 throw std::domain_error(
"Expected vertex colors (r g b) on line " + std::to_string(line_idx + 1) +
" of OBJ data, but could not parse them.\n");
1655 int ri =
static_cast<int>(vr * 255.0f + 0.5f);
1656 int gi =
static_cast<int>(vg * 255.0f + 0.5f);
1657 int bi =
static_cast<int>(vb * 255.0f + 0.5f);
1658 if (ri < 0) { ri = 0; }
1659 if (ri > 255) { ri = 255; }
1660 if (gi < 0) { gi = 0; }
1661 if (gi > 255) { gi = 255; }
1662 if (bi < 0) { bi = 0; }
1663 if (bi > 255) { bi = 255; }
1664 vertex_colors.push_back(static_cast<uint8_t>(ri));
1665 vertex_colors.push_back(static_cast<uint8_t>(gi));
1666 vertex_colors.push_back(static_cast<uint8_t>(bi));
1669 else if (fs::util::starts_with(line,
"f "))
1671 std::string elem_type_identifier, v0raw, v1raw, v2raw;
1673 if (!(iss >> elem_type_identifier >> v0raw >> v1raw >> v2raw))
1675 throw std::domain_error(
"Could not parse face line " + std::to_string(line_idx + 1) +
" of OBJ data, invalid format.\n");
1677 assert(elem_type_identifier ==
"f");
1682 std::size_t found_v0 = v0raw.find(
"/");
1683 std::size_t found_v1 = v1raw.find(
"/");
1684 std::size_t found_v2 = v2raw.find(
"/");
1685 if (found_v0 != std::string::npos)
1687 v0raw = v0raw.substr(0, found_v0);
1689 if (found_v1 != std::string::npos)
1691 v1raw = v1raw.substr(0, found_v1);
1693 if (found_v2 != std::string::npos)
1695 v2raw = v2raw.substr(0, found_v2);
1697 v0 = std::stoi(v0raw);
1698 v1 = std::stoi(v1raw);
1699 v2 = std::stoi(v2raw);
1702 faces.push_back(v0 - 1);
1703 faces.push_back(v1 - 1);
1704 faces.push_back(v2 - 1);
1708 #ifdef LIBFS_DBG_INFO 1709 num_lines_ignored++;
1716 #ifdef LIBFS_DBG_INFO 1717 if (num_lines_ignored > 0)
1719 std::cout <<
LIBFS_APPTAG <<
"Ignored " << num_lines_ignored <<
" lines in Wavefront OBJ format mesh file.\n";
1723 mesh->
faces = faces;
1743 #ifdef LIBFS_DBG_INFO 1744 std::cout <<
LIBFS_APPTAG <<
"Reading brain mesh from Wavefront object format file " << filename <<
".\n";
1746 std::ifstream input(filename, std::fstream::in);
1747 if (input.is_open())
1754 throw std::runtime_error(
"Could not open Wavefront object format mesh file '" + filename +
"' for reading.\n");
1764 static void from_off(
Mesh *mesh, std::istream *is,
const std::string &source_filename =
"")
1767 std::string msg_source_file_part = source_filename.empty() ?
"" :
"'" + source_filename +
"'";
1771 int noncomment_line_idx = -1;
1773 std::vector<float> vertices;
1774 std::vector<int> faces;
1775 size_t num_vertices = 0;
1776 size_t num_faces = 0;
1777 size_t num_edges = 0;
1778 size_t num_verts_parsed = 0;
1779 size_t num_faces_parsed = 0;
1780 bool has_vertex_colors =
false;
1783 int num_verts_this_face, v0, v1, v2;
1784 std::vector<uint8_t> vertex_colors;
1786 while (std::getline(*is, line))
1789 std::istringstream iss(line);
1790 if (fs::util::starts_with(line,
"#"))
1796 noncomment_line_idx++;
1797 if (noncomment_line_idx == 0)
1799 std::string off_header_magic;
1800 if (!(iss >> off_header_magic))
1802 throw std::domain_error(
"Could not parse first header line " + std::to_string(line_idx + 1) +
" of OFF data, invalid format.\n");
1804 if (!(off_header_magic ==
"OFF" || off_header_magic ==
"COFF"))
1806 throw std::domain_error(
"OFF magic string invalid, file " + msg_source_file_part +
" not in OFF format.\n");
1808 has_vertex_colors = (off_header_magic ==
"COFF");
1810 else if (noncomment_line_idx == 1)
1812 if (!(iss >> num_vertices >> num_faces >> num_edges))
1814 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");
1820 if (num_verts_parsed < num_vertices)
1822 if (has_vertex_colors)
1824 if (!(iss >> x >> y >> z >> r >> g >> b >> a))
1826 throw std::domain_error(
"Could not parse vertex coordinate and color line " + std::to_string(line_idx + 1) +
" of COFF data " + msg_source_file_part +
", invalid format.\n");
1828 vertex_colors.push_back(static_cast<uint8_t>(r));
1829 vertex_colors.push_back(static_cast<uint8_t>(g));
1830 vertex_colors.push_back(static_cast<uint8_t>(b));
1834 if (!(iss >> x >> y >> z))
1836 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");
1839 vertices.push_back(x);
1840 vertices.push_back(y);
1841 vertices.push_back(z);
1846 if (num_faces_parsed < num_faces)
1848 if (!(iss >> num_verts_this_face >> v0 >> v1 >> v2))
1850 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");
1852 if (num_verts_this_face != 3)
1854 throw std::domain_error(
"At OFF data " + msg_source_file_part +
" line " + std::to_string(line_idx + 1) +
": only triangular meshes supported.\n");
1856 faces.push_back(v0);
1857 faces.push_back(v1);
1858 faces.push_back(v2);
1865 if (num_verts_parsed < num_vertices)
1867 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");
1869 if (num_faces_parsed < num_faces)
1871 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");
1874 mesh->
faces = faces;
1894 #ifdef LIBFS_DBG_INFO 1895 std::cout <<
LIBFS_APPTAG <<
"Reading brain mesh from OFF format file " << filename <<
".\n";
1897 std::ifstream input(filename, std::fstream::in);
1898 if (input.is_open())
1905 throw std::runtime_error(
"Could not open Object file format (OFF) mesh file '" + filename +
"' for reading.\n");
1918 int noncomment_line_idx = -1;
1920 std::vector<float> vertices;
1921 std::vector<int> faces;
1922 std::vector<uint8_t> vertex_colors;
1924 bool in_header =
true;
1927 bool in_vertex_element =
false;
1928 std::vector<std::string> vertex_properties;
1929 while (std::getline(*is, line))
1932 std::istringstream iss(line);
1933 if (fs::util::starts_with(line,
"comment"))
1939 noncomment_line_idx++;
1942 if (noncomment_line_idx == 0)
1945 throw std::domain_error(
"Invalid PLY file");
1947 else if (noncomment_line_idx == 1)
1949 if (line !=
"format ascii 1.0")
1950 throw std::domain_error(
"Unsupported PLY file format, only format 'format ascii 1.0' is supported.");
1953 if (line ==
"end_header")
1957 else if (fs::util::starts_with(line,
"element vertex"))
1959 std::string elem, elem_type_identifier;
1960 if (!(iss >> elem >> elem_type_identifier >> num_verts))
1962 throw std::domain_error(
"Could not parse element vertex line of PLY header, invalid format.\n");
1964 in_vertex_element =
true;
1966 else if (fs::util::starts_with(line,
"element face"))
1968 std::string elem, elem_type_identifier;
1969 if (!(iss >> elem >> elem_type_identifier >> num_faces))
1971 throw std::domain_error(
"Could not parse element face line of PLY header, invalid format.\n");
1973 in_vertex_element =
false;
1975 else if (fs::util::starts_with(line,
"element "))
1978 in_vertex_element =
false;
1980 else if (fs::util::starts_with(line,
"property ") && in_vertex_element)
1983 std::string kw, type, name;
1984 if (iss >> kw >> type >> name)
1986 vertex_properties.push_back(name);
1992 if (num_verts < 1 || num_faces < 1)
1994 throw std::domain_error(
"Invalid PLY file: missing element count lines of header.");
1997 if (vertices.size() < (size_t)num_verts * 3)
1999 float x = 0.0f, y = 0.0f, z = 0.0f;
2000 int r = 0, g = 0, b = 0;
2001 if (vertex_properties.empty())
2004 if (!(iss >> x >> y >> z))
2006 throw std::domain_error(
"Could not parse vertex line " + std::to_string(line_idx) +
" of PLY data, invalid format.\n");
2008 vertices.push_back(x);
2009 vertices.push_back(y);
2010 vertices.push_back(z);
2014 for (
size_t pi = 0; pi < vertex_properties.size(); pi++)
2016 const std::string &pname = vertex_properties[pi];
2017 if (pname ==
"x") { iss >> x; }
2018 else if (pname ==
"y") { iss >> y; }
2019 else if (pname ==
"z") { iss >> z; }
2020 else if (pname ==
"red") { iss >> r; }
2021 else if (pname ==
"green") { iss >> g; }
2022 else if (pname ==
"blue") { iss >> b; }
2023 else if (pname ==
"nx" || pname ==
"ny" || pname ==
"nz")
2026 float dummy; iss >> dummy;
2031 std::string dummy; iss >> dummy;
2035 throw std::domain_error(
"Could not parse vertex property '" + pname +
"' at line " + std::to_string(line_idx) +
" of PLY data.\n");
2040 throw std::domain_error(
"Could not parse vertex line " + std::to_string(line_idx) +
" of PLY data, invalid format.\n");
2042 vertices.push_back(x);
2043 vertices.push_back(y);
2044 vertices.push_back(z);
2046 bool has_r =
false, has_g =
false, has_b =
false;
2047 for (
size_t pi = 0; pi < vertex_properties.size(); pi++)
2049 if (vertex_properties[pi] ==
"red") has_r =
true;
2050 if (vertex_properties[pi] ==
"green") has_g =
true;
2051 if (vertex_properties[pi] ==
"blue") has_b =
true;
2053 if (has_r && has_g && has_b)
2055 vertex_colors.push_back(static_cast<uint8_t>(r));
2056 vertex_colors.push_back(static_cast<uint8_t>(g));
2057 vertex_colors.push_back(static_cast<uint8_t>(b));
2063 if (faces.size() < (size_t)num_faces * 3)
2065 int verts_per_face, v0, v1, v2;
2066 if (!(iss >> verts_per_face >> v0 >> v1 >> v2))
2068 throw std::domain_error(
"Could not parse face line " + std::to_string(line_idx) +
" of PLY data, invalid format.\n");
2070 if (verts_per_face != 3)
2072 throw std::domain_error(
"Only triangular meshes are supported: PLY faces lines must contain exactly 3 vertex indices.\n");
2074 faces.push_back(v0);
2075 faces.push_back(v1);
2076 faces.push_back(v2);
2082 if (vertices.size() != (size_t)num_verts * 3)
2084 std::cerr <<
"PLY header mentions " << num_verts <<
" vertices, but found " << vertices.size() / 3 <<
".\n";
2086 if (faces.size() != (size_t)num_faces * 3)
2088 std::cerr <<
"PLY header mentions " << num_faces <<
" faces, but found " << faces.size() / 3 <<
".\n";
2091 mesh->
faces = faces;
2110 #ifdef LIBFS_DBG_INFO 2111 std::cout <<
LIBFS_APPTAG <<
"Reading brain mesh from PLY format file " << filename <<
".\n";
2113 std::ifstream input(filename, std::fstream::in);
2114 if (input.is_open())
2121 throw std::runtime_error(
"Could not open Stanford PLY format mesh file '" + filename +
"' for reading.\n");
2136 return (this->vertices.size() / 3);
2150 return (this->faces.size() / 3);
2165 const int32_t &
fm_at(
const size_t i,
const size_t j)
const 2167 size_t idx = _vidx_2d(i, j, 3);
2168 if (idx > this->faces.size() - 1)
2170 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");
2172 return (this->faces[idx]);
2188 if (face > this->num_faces() - 1)
2190 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");
2192 std::vector<int32_t> fv(3);
2193 fv[0] = this->fm_at(face, 0);
2194 fv[1] = this->fm_at(face, 1);
2195 fv[2] = this->fm_at(face, 2);
2212 if (vertex > this->num_vertices() - 1)
2214 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");
2216 std::vector<float> vc(3);
2217 vc[0] = this->vm_at(vertex, 0);
2218 vc[1] = this->vm_at(vertex, 1);
2219 vc[2] = this->vm_at(vertex, 2);
2236 const float &
vm_at(
const size_t i,
const size_t j)
const 2238 size_t idx = _vidx_2d(i, j, 3);
2239 if (idx > this->vertices.size() - 1)
2241 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");
2243 return (this->vertices[idx]);
2256 std::vector<uint8_t> empty_col;
2257 return (this->to_ply(empty_col));
2270 std::string
to_ply(
const std::vector<uint8_t> col)
const 2272 bool use_vertex_colors = col.size() != 0;
2273 std::stringstream plys;
2274 plys <<
"ply\nformat ascii 1.0\n";
2275 plys <<
"element vertex " << this->num_vertices() <<
"\n";
2276 plys <<
"property float x\nproperty float y\nproperty float z\n";
2277 if (use_vertex_colors)
2279 if (col.size() != this->vertices.size())
2281 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()) +
".");
2283 plys <<
"property uchar red\nproperty uchar green\nproperty uchar blue\n";
2285 plys <<
"element face " << this->num_faces() <<
"\n";
2286 plys <<
"property list uchar int vertex_index\n";
2287 plys <<
"end_header\n";
2289 #ifdef LIBFS_DBG_DEBUG 2290 fs::util::log(
"Writing " + std::to_string(this->vertices.size() / 3) +
" PLY format vertices.",
"INFO");
2293 for (
size_t vidx = 0; vidx < this->vertices.size(); vidx += 3)
2295 plys << vertices[vidx] <<
" " << vertices[vidx + 1] <<
" " << vertices[vidx + 2];
2296 if (use_vertex_colors)
2298 plys <<
" " << (int)col[vidx] <<
" " << (
int)col[vidx + 1] <<
" " << (int)col[vidx + 2];
2303 #ifdef LIBFS_DBG_DEBUG 2304 fs::util::log(
"Writing " + std::to_string(this->faces.size() / 3) +
" PLY format faces.",
"INFO");
2307 const int num_vertices_per_face = 3;
2308 for (
size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
2310 plys << num_vertices_per_face <<
" " << faces[fidx] <<
" " << faces[fidx + 1] <<
" " << faces[fidx + 2] <<
"\n";
2312 return (plys.str());
2326 #ifdef LIBFS_DBG_INFO 2327 fs::util::log(
"Writing mesh to PLY file '" + filename +
"'.",
"INFO");
2329 fs::util::str_to_file(filename, this->to_ply());
2334 void to_ply_file(
const std::string &filename,
const std::vector<uint8_t> col)
const 2336 fs::util::str_to_file(filename, this->to_ply(col));
2349 std::vector<uint8_t> empty_col;
2350 return (this->to_off(empty_col));
2356 std::string
to_off(
const std::vector<uint8_t> col)
const 2358 bool use_vertex_colors = col.size() != 0;
2359 std::stringstream offs;
2360 if (use_vertex_colors)
2362 #ifdef LIBFS_DBG_INFO 2363 fs::util::log(
"Writing OFF representation of mesh with vertex colors.",
"INFO");
2365 if (col.size() != this->vertices.size())
2367 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()) +
".");
2373 #ifdef LIBFS_DBG_INFO 2374 fs::util::log(
"Writing OFF representation of mesh without vertex colors.",
"INFO");
2378 offs << this->num_vertices() <<
" " << this->num_faces() <<
" 0\n";
2380 for (
size_t vidx = 0; vidx < this->vertices.size(); vidx += 3)
2382 offs << vertices[vidx] <<
" " << vertices[vidx + 1] <<
" " << vertices[vidx + 2];
2383 if (use_vertex_colors)
2385 offs <<
" " << (int)col[vidx] <<
" " << (
int)col[vidx + 1] <<
" " << (int)col[vidx + 2] <<
" 255";
2390 const int num_vertices_per_face = 3;
2391 for (
size_t fidx = 0; fidx < this->faces.size(); fidx += 3)
2393 offs << num_vertices_per_face <<
" " << faces[fidx] <<
" " << faces[fidx + 1] <<
" " << faces[fidx + 2] <<
"\n";
2395 return (offs.str());
2409 fs::util::str_to_file(filename, this->to_off());
2414 void to_off_file(
const std::string &filename,
const std::vector<uint8_t> col)
const 2416 fs::util::str_to_file(filename, this->to_off(col));
2425 Curv(std::vector<float> curv_data) : num_faces(100000), num_vertices(0), num_values_per_vertex(1)
2428 num_vertices = int(data.size());
2432 Curv() : num_faces(100000), num_vertices(0), num_values_per_vertex(1) {}
2452 std::vector<int32_t>
r;
2453 std::vector<int32_t>
g;
2454 std::vector<int32_t>
b;
2455 std::vector<int32_t>
a;
2461 size_t num_ids = this->
id.size();
2462 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)
2464 std::cerr <<
"Inconsistent Colortable, vector sizes do not match.\n";
2472 for (
size_t i = 0; i < this->num_entries(); i++)
2474 if (this->name[i] == query_name)
2485 for (
size_t i = 0; i < this->num_entries(); i++)
2487 if (this->label[i] == query_label)
2506 int32_t region_idx = this->colortable.
get_region_idx(region_name);
2507 if (region_idx >= 0)
2509 return (this->region_vertices(this->colortable.
label[region_idx]));
2513 std::cerr <<
"No such region in annot, returning empty vector.\n";
2514 std::vector<int32_t> empty;
2522 std::vector<int32_t> reg_verts;
2523 for (
size_t i = 0; i < this->vertex_labels.size(); i++)
2525 if (this->vertex_labels[i] == region_label)
2527 reg_verts.push_back(
int(i));
2537 int num_channels = alpha ? 4 : 3;
2538 std::vector<uint8_t> col;
2539 col.reserve(this->num_vertices() * num_channels);
2540 std::vector<size_t> vertex_region_indices = this->vertex_regions();
2541 for (
size_t i = 0; i < this->num_vertices(); i++)
2543 col.push_back(this->colortable.
r[vertex_region_indices[i]]);
2544 col.push_back(this->colortable.
g[vertex_region_indices[i]]);
2545 col.push_back(this->colortable.
b[vertex_region_indices[i]]);
2548 col.push_back(this->colortable.
a[vertex_region_indices[i]]);
2558 size_t nv = this->vertex_indices.size();
2559 if (this->vertex_labels.size() != nv)
2561 throw std::runtime_error(
"Inconsistent annot, number of vertex indices and labels does not match.\n");
2570 std::vector<size_t> vert_reg;
2571 for (
size_t i = 0; i < this->num_vertices(); i++)
2573 vert_reg.push_back(0);
2575 for (
size_t region_idx = 0; region_idx < this->colortable.
num_entries(); region_idx++)
2577 std::vector<int32_t> reg_vertices = this->region_vertices(this->colortable.
label[region_idx]);
2578 for (
size_t region_vert_local_idx = 0; region_vert_local_idx < reg_vertices.size(); region_vert_local_idx++)
2580 int32_t region_vert_idx = reg_vertices[region_vert_local_idx];
2581 vert_reg[region_vert_idx] = region_idx;
2590 std::vector<std::string> region_names;
2591 std::vector<size_t> vertex_region_indices = this->vertex_regions();
2592 for (
size_t i = 0; i < this->num_vertices(); i++)
2594 region_names.push_back(this->colortable.
name[vertex_region_indices[i]]);
2596 return (region_names);
2606 dim1length = curv.
data.size();
2614 dim1length = curv_data.size();
2620 int32_t dim1length = 0;
2621 int32_t dim2length = 0;
2622 int32_t dim3length = 0;
2623 int32_t dim4length = 0;
2627 int16_t ras_good_flag = 0;
2632 return ((
size_t)dim1length * dim2length * dim3length * dim4length);
2646 MghData(std::vector<int32_t> curv_data) { data_mri_int = curv_data; }
2647 explicit MghData(std::vector<uint8_t> curv_data) { data_mri_uchar = curv_data; }
2648 explicit MghData(std::vector<short> curv_data) { data_mri_short = curv_data; }
2649 MghData(std::vector<float> curv_data) { data_mri_float = curv_data; }
2668 Mgh(std::vector<float> curv_data)
2684 Array4D(
unsigned int d1,
unsigned int d2,
unsigned int d3,
unsigned int d4) : d1(d1), d2(d2), d3(d3), d4(d4), data(_compute_4d_size(d1, d2, d3, d4)) {}
2691 Array4D(
MghHeader *mgh_header) : d1(_validate_mgh_dim(mgh_header->dim1length)), d2(_validate_mgh_dim(mgh_header->dim2length)), d3(_validate_mgh_dim(mgh_header->dim3length)), d4(_validate_mgh_dim(mgh_header->dim4length)), data(_compute_4d_size(d1, d2, d3, d4)) {}
2698 d1(_validate_mgh_dim(mgh->header.dim1length)), d2(_validate_mgh_dim(mgh->header.dim2length)), d3(_validate_mgh_dim(mgh->header.dim3length)), d4(_validate_mgh_dim(mgh->header.dim4length)), data(_compute_4d_size(d1, d2, d3, d4))
2703 const T &
at(
const unsigned int i1,
const unsigned int i2,
const unsigned int i3,
const unsigned int i4)
const 2705 return data[get_index(i1, i2, i3, i4)];
2709 unsigned int get_index(
const unsigned int i1,
const unsigned int i2,
const unsigned int i3,
const unsigned int i4)
const 2711 assert(i1 >= 0 && i1 < d1);
2712 assert(i2 >= 0 && i2 < d2);
2713 assert(i3 >= 0 && i3 < d3);
2714 assert(i4 >= 0 && i4 < d4);
2715 return (((i1 * d2 + i2) * d3 + i3) * d4 + i4);
2721 return (d1 * d2 * d3 * d4);
2733 static unsigned int _validate_mgh_dim(int32_t dim)
2737 throw std::domain_error(
"MGH dimension " + std::to_string(dim) +
" is not positive.\n");
2739 return static_cast<unsigned int>(dim);
2743 static size_t _compute_4d_size(
unsigned int d1,
unsigned int d2,
unsigned int d3,
unsigned int d4)
2745 if (d1 == 0 || d2 == 0 || d3 == 0 || d4 == 0)
2747 throw std::domain_error(
"Array4D dimensions must be positive.\n");
2750 if (!fs::util::safe_multiply(d1, d2, s1) ||
2751 !fs::util::safe_multiply(s1, d3, s2) ||
2752 !fs::util::safe_multiply(s2, d4, s3))
2754 throw std::overflow_error(
"Array4D dimensions cause size_t overflow.\n");
2758 throw std::runtime_error(
"Array4D size " + std::to_string(s3) +
2759 " elements exceeds maximum allowed allocation (" +
2768 void read_mgh_header(
MghHeader *, std::istream *);
2769 template <
typename T>
2770 std::vector<T> _read_mgh_data(
MghHeader *,
const std::string &);
2771 template <
typename T>
2772 std::vector<T> _read_mgh_data(
MghHeader *, std::istream *);
2773 std::vector<int32_t> _read_mgh_data_int(
MghHeader *,
const std::string &);
2774 std::vector<int32_t> _read_mgh_data_int(
MghHeader *, std::istream *);
2775 std::vector<uint8_t> _read_mgh_data_uchar(
MghHeader *,
const std::string &);
2776 std::vector<uint8_t> _read_mgh_data_uchar(
MghHeader *, std::istream *);
2777 std::vector<short> _read_mgh_data_short(
MghHeader *,
const std::string &);
2778 std::vector<short> _read_mgh_data_short(
MghHeader *, std::istream *);
2779 std::vector<float> _read_mgh_data_float(
MghHeader *,
const std::string &);
2780 std::vector<float> _read_mgh_data_float(
MghHeader *, std::istream *);
2797 read_mgh_header(&mgh_header, filename);
2798 mgh->
header = mgh_header;
2801 std::vector<int32_t> data = _read_mgh_data_int(&mgh_header, filename);
2806 std::vector<uint8_t> data = _read_mgh_data_uchar(&mgh_header, filename);
2811 std::vector<float> data = _read_mgh_data_float(&mgh_header, filename);
2816 std::vector<short> data = _read_mgh_data_short(&mgh_header, filename);
2821 #ifdef LIBFS_DBG_INFO 2822 if (fs::util::ends_with(filename,
".mgz"))
2824 #ifndef LIBFS_HAS_ZLIB 2825 std::cout <<
LIBFS_APPTAG <<
"Note: your MGH filename ends with '.mgz'. MGZ support requires zlib: link with -lz. If you already have zlib and see this, #define LIBFS_HAS_ZLIB before including libfs.h, or upgrade your compiler.\n";
2827 std::cout << LIBFS_APPTAG <<
"Note: your MGH filename ends with '.mgz'. Did you mean to call read_mgz() instead of read_mgh()?\n";
2831 throw std::runtime_error(
"Not reading MGH data from file '" + filename +
"', data type " + std::to_string(mgh->
header.
dtype) +
" not supported yet.\n");
2846 std::vector<std::string> subjects;
2847 std::ifstream input(filename, std::fstream::in);
2850 if (!input.is_open())
2852 throw std::runtime_error(
"Could not open subjects file '" + filename +
"'.\n");
2855 while (std::getline(input, line))
2857 subjects.push_back(line);
2876 ofs.open(filename, std::ofstream::out);
2879 for (
size_t i = 0; i < subjects.size(); i++)
2881 ofs << subjects[i] <<
"\n";
2887 throw std::runtime_error(
"Unable to open subjects file '" + filename +
"' for writing.\n");
2899 read_mgh_header(&mgh_header, is);
2900 mgh->
header = mgh_header;
2903 std::vector<int32_t> data = _read_mgh_data_int(&mgh_header, is);
2908 std::vector<uint8_t> data = _read_mgh_data_uchar(&mgh_header, is);
2913 std::vector<float> data = _read_mgh_data_float(&mgh_header, is);
2918 std::vector<short> data = _read_mgh_data_short(&mgh_header, is);
2923 throw std::runtime_error(
"Not reading data from MGH stream, data type " + std::to_string(mgh->
header.
dtype) +
" not supported yet.\n");
2932 void read_mgh_header(
MghHeader *mgh_header, std::istream *is)
2934 const int MGH_VERSION = 1;
2936 int format_version = _freadt<int32_t>(*is);
2937 if (format_version != MGH_VERSION)
2939 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");
2941 mgh_header->
dim1length = _freadt<int32_t>(*is);
2942 mgh_header->
dim2length = _freadt<int32_t>(*is);
2943 mgh_header->
dim3length = _freadt<int32_t>(*is);
2944 mgh_header->
dim4length = _freadt<int32_t>(*is);
2950 throw std::domain_error(
"MGH header contains non-positive dimension(s): dims=(" +
2951 std::to_string(mgh_header->
dim1length) +
"," +
2952 std::to_string(mgh_header->
dim2length) +
"," +
2953 std::to_string(mgh_header->
dim3length) +
"," +
2954 std::to_string(mgh_header->
dim4length) +
").\n");
2958 if (!fs::util::check_alloc(static_cast<size_t>(mgh_header->
dim1length) *
2959 static_cast<size_t>(mgh_header->
dim2length) *
2961 static_cast<size_t>(mgh_header->
dim4length)))
2963 throw std::runtime_error(
"MGH header volume size exceeds maximum allowed allocation (" +
2967 mgh_header->
dtype = _freadt<int32_t>(*is);
2968 mgh_header->
dof = _freadt<int32_t>(*is);
2970 int unused_header_space_size_left = 256;
2972 unused_header_space_size_left -= 2;
2977 mgh_header->
xsize = _freadt<float>(*is);
2978 mgh_header->
ysize = _freadt<float>(*is);
2979 mgh_header->
zsize = _freadt<float>(*is);
2983 if (!fs::util::is_finite_float(mgh_header->
xsize) ||
2984 !fs::util::is_finite_float(mgh_header->
ysize) ||
2985 !fs::util::is_finite_float(mgh_header->
zsize))
2987 throw std::domain_error(
"MGH header contains NaN or Inf voxel size(s): x=" +
2988 std::to_string(mgh_header->
xsize) +
" y=" +
2989 std::to_string(mgh_header->
ysize) +
" z=" +
2990 std::to_string(mgh_header->
zsize) +
".\n");
2992 if (mgh_header->
xsize == 0.0f || mgh_header->
ysize == 0.0f || mgh_header->
zsize == 0.0f)
2994 throw std::domain_error(
"MGH header contains zero voxel size(s): x=" +
2995 std::to_string(mgh_header->
xsize) +
" y=" +
2996 std::to_string(mgh_header->
ysize) +
" z=" +
2997 std::to_string(mgh_header->
zsize) +
".\n");
3000 for (
int i = 0; i < 9; i++)
3002 mgh_header->
Mdc.push_back(_freadt<float>(*is));
3004 for (
int i = 0; i < 3; i++)
3006 mgh_header->
Pxyz_c.push_back(_freadt<float>(*is));
3010 for (
size_t i = 0; i < mgh_header->
Mdc.size(); i++)
3012 if (!fs::util::is_finite_float(mgh_header->
Mdc[i]))
3014 throw std::domain_error(
"MGH header Mdc matrix contains NaN or Inf at index " +
3015 std::to_string(i) +
".\n");
3018 for (
size_t i = 0; i < mgh_header->
Pxyz_c.size(); i++)
3020 if (!fs::util::is_finite_float(mgh_header->
Pxyz_c[i]))
3022 throw std::domain_error(
"MGH header Pxyz_c contains NaN or Inf at index " +
3023 std::to_string(i) +
".\n");
3027 unused_header_space_size_left -= 60;
3033 while (unused_header_space_size_left > 0)
3035 discarded = _freadt<uint8_t>(*is);
3036 unused_header_space_size_left -= 1;
3045 std::vector<int32_t> _read_mgh_data_int(
MghHeader *mgh_header,
const std::string &filename)
3047 if (mgh_header->
dtype != MRI_INT)
3049 std::cerr <<
"Expected MRI data type " << MRI_INT <<
", but found " << mgh_header->
dtype <<
".\n";
3051 return (_read_mgh_data<int32_t>(mgh_header, filename));
3058 std::vector<int32_t> _read_mgh_data_int(
MghHeader *mgh_header, std::istream *is)
3060 if (mgh_header->
dtype != MRI_INT)
3062 std::cerr <<
"Expected MRI data type " << MRI_INT <<
", but found " << mgh_header->
dtype <<
".\n";
3064 return (_read_mgh_data<int32_t>(mgh_header, is));
3071 std::vector<short> _read_mgh_data_short(
MghHeader *mgh_header,
const std::string &filename)
3073 if (mgh_header->
dtype != MRI_SHORT)
3075 std::cerr <<
"Expected MRI data type " << MRI_SHORT <<
", but found " << mgh_header->
dtype <<
".\n";
3077 return (_read_mgh_data<short>(mgh_header, filename));
3084 std::vector<short> _read_mgh_data_short(
MghHeader *mgh_header, std::istream *is)
3086 if (mgh_header->
dtype != MRI_SHORT)
3088 std::cerr <<
"Expected MRI data type " << MRI_SHORT <<
", but found " << mgh_header->
dtype <<
".\n";
3090 return (_read_mgh_data<short>(mgh_header, is));
3099 void read_mgh_header(
MghHeader *mgh_header,
const std::string &filename)
3102 ifs.open(filename, std::ios_base::in | std::ios::binary);
3105 read_mgh_header(mgh_header, &ifs);
3110 throw std::runtime_error(
"Unable to open MGH file '" + filename +
"'.\n");
3119 template <
typename T>
3120 std::vector<T> _read_mgh_data(
MghHeader *mgh_header,
const std::string &filename)
3123 ifs.open(filename, std::ios_base::in | std::ios::binary);
3126 size_t num_values = mgh_header->
num_values();
3129 size_t file_size = fs::util::get_file_size(filename);
3132 const size_t HEADER_SIZE = 284;
3133 size_t expected_data_bytes = 0;
3134 if (!fs::util::safe_multiply(num_values,
sizeof(T), expected_data_bytes))
3136 throw std::overflow_error(
"MGH data size computation overflowed.\n");
3138 if (file_size < HEADER_SIZE || (file_size - HEADER_SIZE) < expected_data_bytes)
3140 throw std::runtime_error(
"MGH file '" + filename +
"' is too small (" +
3141 std::to_string(file_size) +
" bytes) for the data claimed in its header (" +
3142 std::to_string(HEADER_SIZE + expected_data_bytes) +
" bytes).\n");
3146 if (!fs::util::check_alloc(num_values,
sizeof(T)))
3148 throw std::runtime_error(
"MGH file data size exceeds maximum allowed allocation.\n");
3151 ifs.seekg(284, ifs.beg);
3153 std::vector<T> data;
3154 data.reserve(num_values);
3155 for (
size_t i = 0; i < num_values; i++)
3157 data.push_back(_freadt<T>(ifs));
3164 throw std::runtime_error(
"Unable to open MGH file '" + filename +
"'.\n");
3172 template <
typename T>
3173 std::vector<T> _read_mgh_data(
MghHeader *mgh_header, std::istream *is)
3175 size_t num_values = mgh_header->
num_values();
3176 if (!fs::util::check_alloc(num_values,
sizeof(T)))
3178 throw std::runtime_error(
"MGH stream data size exceeds maximum allowed allocation.\n");
3180 std::vector<T> data;
3181 data.reserve(num_values);
3182 for (
size_t i = 0; i < num_values; i++)
3184 data.push_back(_freadt<T>(*is));
3193 std::vector<float> _read_mgh_data_float(
MghHeader *mgh_header,
const std::string &filename)
3195 if (mgh_header->
dtype != MRI_FLOAT)
3197 std::cerr <<
"Expected MRI data type " << MRI_FLOAT <<
", but found " << mgh_header->
dtype <<
".\n";
3199 return (_read_mgh_data<float>(mgh_header, filename));
3206 std::vector<float> _read_mgh_data_float(
MghHeader *mgh_header, std::istream *is)
3208 if (mgh_header->
dtype != MRI_FLOAT)
3210 std::cerr <<
"Expected MRI data type " << MRI_FLOAT <<
", but found " << mgh_header->
dtype <<
".\n";
3212 return (_read_mgh_data<float>(mgh_header, is));
3219 std::vector<uint8_t> _read_mgh_data_uchar(
MghHeader *mgh_header,
const std::string &filename)
3221 if (mgh_header->
dtype != MRI_UCHAR)
3223 std::cerr <<
"Expected MRI data type " << MRI_UCHAR <<
", but found " << mgh_header->
dtype <<
".\n";
3225 return (_read_mgh_data<uint8_t>(mgh_header, filename));
3232 std::vector<uint8_t> _read_mgh_data_uchar(
MghHeader *mgh_header, std::istream *is)
3234 if (mgh_header->
dtype != MRI_UCHAR)
3236 std::cerr <<
"Expected MRI data type " << MRI_UCHAR <<
", but found " << mgh_header->
dtype <<
".\n";
3238 return (_read_mgh_data<uint8_t>(mgh_header, is));
3256 const int SURF_TRIS_MAGIC = 16777214;
3258 is.open(filename, std::ios_base::in | std::ios::binary);
3261 int magic = _fread3(is);
3262 if (magic != SURF_TRIS_MAGIC)
3264 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");
3266 std::string created_line = _freadstringnewline(is);
3267 std::string comment_line = _freadstringnewline(is);
3268 int num_verts = _freadt<int32_t>(is);
3269 int num_faces = _freadt<int32_t>(is);
3274 throw std::domain_error(
"Surf file '" + filename +
"' has invalid num_verts: " + std::to_string(num_verts) +
".\n");
3278 throw std::domain_error(
"Surf file '" + filename +
"' has invalid num_faces: " + std::to_string(num_faces) +
".\n");
3282 size_t num_vert_coords = 0;
3283 if (!fs::util::safe_multiply(static_cast<size_t>(num_verts), 3, num_vert_coords))
3285 throw std::overflow_error(
"Surf file '" + filename +
"': num_verts * 3 overflowed.\n");
3287 size_t num_face_indices = 0;
3288 if (!fs::util::safe_multiply(static_cast<size_t>(num_faces), 3, num_face_indices))
3290 throw std::overflow_error(
"Surf file '" + filename +
"': num_faces * 3 overflowed.\n");
3294 size_t file_size = fs::util::get_file_size(filename);
3297 size_t vert_bytes = 0, face_bytes = 0;
3298 if (!fs::util::safe_multiply(num_vert_coords,
sizeof(
float), vert_bytes) ||
3299 !fs::util::safe_multiply(num_face_indices,
sizeof(int32_t), face_bytes))
3301 throw std::overflow_error(
"Surf file '" + filename +
"': expected data size overflowed.\n");
3304 if (vert_bytes > std::numeric_limits<size_t>::max() - face_bytes)
3306 throw std::overflow_error(
"Surf file '" + filename +
"': total data size overflowed.\n");
3308 size_t expected_total = vert_bytes + face_bytes;
3310 if (file_size < expected_total)
3312 throw std::runtime_error(
"Surf file '" + filename +
"' is too small (" +
3313 std::to_string(file_size) +
" bytes) for the data claimed in its header.\n");
3317 if (!fs::util::check_alloc(num_vert_coords,
sizeof(
float)) ||
3318 !fs::util::check_alloc(num_face_indices,
sizeof(int32_t)))
3320 throw std::runtime_error(
"Surf file '" + filename +
"' data size exceeds maximum allowed allocation.\n");
3323 #ifdef LIBFS_DBG_INFO 3324 std::cout <<
LIBFS_APPTAG <<
"Read surface file with " << num_verts <<
" vertices, " << num_faces <<
" faces.\n";
3326 std::vector<float> vdata;
3327 vdata.reserve(num_vert_coords);
3328 for (
size_t i = 0; i < num_vert_coords; i++)
3330 vdata.push_back(_freadt<float>(is));
3332 std::vector<int> fdata;
3333 fdata.reserve(num_face_indices);
3334 for (
size_t i = 0; i < num_face_indices; i++)
3336 fdata.push_back(_freadt<int32_t>(is));
3340 surface->
faces = fdata;
3344 throw std::runtime_error(
"Unable to open surface file '" + filename +
"'.\n");
3362 if (fs::util::ends_with(filename,
".obj"))
3366 else if (fs::util::ends_with(filename,
".ply"))
3370 else if (fs::util::ends_with(filename,
".off"))
3385 bool _is_bigendian()
3387 const short int number = 0x1;
3388 const char *numPtr =
reinterpret_cast<const char *
>(&number);
3389 return (numPtr[0] != 1);
3401 void read_curv(
Curv *curv, std::istream *is,
const std::string &source_filename =
"")
3403 const std::string msg_source_file_part = source_filename.empty() ?
"" :
"'" + source_filename +
"' ";
3404 const int CURV_MAGIC = 16777215;
3405 int magic = _fread3(*is);
3406 if (magic != CURV_MAGIC)
3408 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");
3411 curv->
num_faces = _freadt<int32_t>(*is);
3417 throw std::domain_error(
"Curv file " + msg_source_file_part +
"has invalid num_vertices: " + std::to_string(curv->
num_vertices) +
".\n");
3421 throw std::domain_error(
"Curv file " + msg_source_file_part +
"has invalid num_faces: " + std::to_string(curv->
num_faces) +
".\n");
3424 #ifdef LIBFS_DBG_INFO 3429 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");
3433 if (!source_filename.empty())
3435 size_t file_size = fs::util::get_file_size(source_filename);
3439 const size_t CURV_HEADER_SIZE = 15;
3440 size_t expected_data_bytes = 0;
3441 if (!fs::util::safe_multiply(static_cast<size_t>(curv->
num_vertices),
sizeof(
float), expected_data_bytes))
3443 throw std::overflow_error(
"Curv file " + msg_source_file_part +
"data size computation overflowed.\n");
3445 if (file_size < CURV_HEADER_SIZE || (file_size - CURV_HEADER_SIZE) < expected_data_bytes)
3447 throw std::runtime_error(
"Curv file " + msg_source_file_part +
"is too small (" +
3448 std::to_string(file_size) +
" bytes) for the data claimed in its header (" +
3449 std::to_string(CURV_HEADER_SIZE + expected_data_bytes) +
" bytes expected).\n");
3454 std::vector<float> data;
3455 if (!fs::util::check_alloc(static_cast<size_t>(curv->
num_vertices),
sizeof(
float)))
3457 throw std::runtime_error(
"Curv file " + msg_source_file_part +
"data size exceeds maximum allowed allocation.\n");
3460 for (
size_t i = 0; i < static_cast<size_t>(curv->
num_vertices); i++)
3462 data.push_back(_freadt<float>(*is));
3481 std::ifstream is(filename, std::fstream::in | std::fstream::binary);
3489 throw std::runtime_error(
"Could not open curv file '" + filename +
"' for reading.\n");
3495 void _read_annot_colortable(
Colortable *colortable, std::istream *is, int32_t num_entries)
3500 throw std::domain_error(
"Annot colortable num_entries " + std::to_string(num_entries) +
3504 int32_t num_chars_orig_filename = _freadt<int32_t>(*is);
3509 throw std::domain_error(
"Annot colortable original filename length " + std::to_string(num_chars_orig_filename) +
3515 for (int32_t i = 0; i < num_chars_orig_filename; i++)
3517 discarded = _freadt<uint8_t>(*is);
3521 int32_t num_entries_duplicated = _freadt<int32_t>(*is);
3522 if (num_entries != num_entries_duplicated)
3524 std::cerr <<
"Warning: the two num_entries header fields of this annotation do not match. Use with care.\n";
3527 colortable->
id.reserve(static_cast<size_t>(num_entries));
3528 colortable->
name.reserve(static_cast<size_t>(num_entries));
3529 colortable->
r.reserve(static_cast<size_t>(num_entries));
3530 colortable->
g.reserve(static_cast<size_t>(num_entries));
3531 colortable->
b.reserve(static_cast<size_t>(num_entries));
3532 colortable->
a.reserve(static_cast<size_t>(num_entries));
3533 colortable->
label.reserve(static_cast<size_t>(num_entries));
3535 int32_t entry_num_chars;
3536 for (int32_t i = 0; i < num_entries; i++)
3538 colortable->
id.push_back(_freadt<int32_t>(*is));
3539 entry_num_chars = _freadt<int32_t>(*is);
3541 colortable->
name.push_back(_freadfixedlengthstring(*is, entry_num_chars,
true, 256));
3542 colortable->
r.push_back(_freadt<int32_t>(*is));
3543 colortable->
g.push_back(_freadt<int32_t>(*is));
3544 colortable->
b.push_back(_freadt<int32_t>(*is));
3545 colortable->
a.push_back(_freadt<int32_t>(*is));
3546 colortable->
label.push_back(static_cast<uint32_t>(colortable->
r[i]) + static_cast<uint32_t>(colortable->
g[i]) * 256u + static_cast<uint32_t>(colortable->
b[i]) * 65536u + static_cast<uint32_t>(colortable->
a[i]) * 16777216u);
3552 size_t _vidx_2d(
size_t row,
size_t column,
size_t row_length = 3)
3554 return (row + 1) * row_length - row_length + column;
3565 int32_t num_vertices = _freadt<int32_t>(*is);
3568 if (num_vertices <= 0)
3570 throw std::domain_error(
"Annot file has invalid num_vertices: " + std::to_string(num_vertices) +
".\n");
3574 size_t num_entries = 0;
3575 if (!fs::util::safe_multiply(static_cast<size_t>(num_vertices), 2, num_entries))
3577 throw std::overflow_error(
"Annot: num_vertices * 2 overflowed.\n");
3579 if (!fs::util::check_alloc(num_entries,
sizeof(int32_t)))
3581 throw std::runtime_error(
"Annot vertex/label data size exceeds maximum allowed allocation.\n");
3584 std::vector<int32_t> vertices;
3585 std::vector<int32_t> labels;
3586 vertices.reserve(num_vertices);
3587 labels.reserve(num_vertices);
3588 for (
size_t i = 0; i < num_entries; i++)
3592 vertices.push_back(_freadt<int32_t>(*is));
3596 labels.push_back(_freadt<int32_t>(*is));
3601 int32_t has_colortable = _freadt<int32_t>(*is);
3602 if (has_colortable == 1)
3604 int32_t num_colortable_entries_old_format = _freadt<int32_t>(*is);
3605 if (num_colortable_entries_old_format > 0)
3607 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");
3611 int32_t colortable_format_version = -num_colortable_entries_old_format;
3612 if (colortable_format_version == 2)
3614 int32_t num_colortable_entries = _freadt<int32_t>(*is);
3615 _read_annot_colortable(&annot->
colortable, is, num_colortable_entries);
3619 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");
3625 throw std::domain_error(
"Reading annotation without colortable not supported. Maybe invalid annotation file?\n");
3644 std::ifstream is(filename, std::fstream::in | std::fstream::binary);
3652 throw std::runtime_error(
"Could not open annot file '" + filename +
"' for reading.\n");
3692 if (fs::util::ends_with(filename, {
".MGH",
".mgh"}))
3699 for (
size_t i = 0; i < dims.size(); i++)
3708 std::cerr <<
"MGH file '" << filename <<
"' contains more than one non-empty dimension. Returning concatinated data.\n";
3712 else if (fs::util::ends_with(filename, {
".NII",
".nii",
".NII.GZ",
".nii.gz"}))
3718 throw std::runtime_error(
"read_desc_data currently only supports NIfTI files with FLOAT32 data.\n");
3722 for (
size_t i = 0; i < dims.size(); i++)
3731 std::cerr <<
"NIfTI file '" << filename <<
"' contains more than one non-empty dimension. Returning concatenated data.\n";
3750 template <
typename T>
3753 static_assert(CHAR_BIT == 8,
"CHAR_BIT != 8");
3755 unsigned char src[
sizeof(T)];
3756 unsigned char dst[
sizeof(T)];
3757 std::memcpy(src, &u,
sizeof(T));
3759 for (
size_t k = 0; k <
sizeof(T); k++)
3761 dst[k] = src[
sizeof(T) - k - 1];
3765 std::memcpy(&result, dst,
sizeof(T));
3773 template <
typename T>
3774 T _freadt(std::istream &is)
3777 is.read(reinterpret_cast<char *>(&t),
sizeof(t));
3778 if (static_cast<size_t>(is.gcount()) !=
sizeof(T))
3780 if (is.gcount() == 0)
3782 throw std::runtime_error(
"Unexpected end of binary stream: expected " + std::to_string(
sizeof(T)) +
" bytes, got EOF.\n");
3784 throw std::runtime_error(
"Short read in binary stream: expected " + std::to_string(
sizeof(T)) +
" bytes, got " + std::to_string(is.gcount()) +
".\n");
3786 if (!_is_bigendian())
3788 t = _swap_endian<T>(t);
3797 int _fread3(std::istream &is)
3800 is.read(reinterpret_cast<char *>(&i), 3);
3801 if (static_cast<size_t>(is.gcount()) != 3)
3803 if (is.gcount() == 0)
3805 throw std::runtime_error(
"Unexpected end of binary stream: expected 3 bytes, got EOF.\n");
3807 throw std::runtime_error(
"Short read in binary stream: expected 3 bytes, got " + std::to_string(is.gcount()) +
".\n");
3809 if (!_is_bigendian())
3811 i = _swap_endian<std::uint32_t>(i);
3813 i = ((i >> 8) & 0xffffff);
3821 template <
typename T>
3822 void _fwritet(std::ostream &os, T t)
3824 if (!_is_bigendian())
3826 t = _swap_endian<T>(t);
3828 os.write(reinterpret_cast<const char *>(&t),
sizeof(t));
3835 void _fwritei3(std::ostream &os, uint32_t i)
3837 unsigned char b1 = (i >> 16) & 255;
3838 unsigned char b2 = (i >> 8) & 255;
3839 unsigned char b3 = i & 255;
3841 os.write(reinterpret_cast<const char *>(&b1),
sizeof(b1));
3842 os.write(reinterpret_cast<const char *>(&b2),
sizeof(b2));
3843 os.write(reinterpret_cast<const char *>(&b3),
sizeof(b3));
3851 void _fwritefixedlengthstring(std::ostream &os,
const std::string &str,
size_t len)
3853 std::string buf(len,
'\0');
3854 size_t copy_len = str.size() < len ? str.size() : len;
3855 std::memcpy(&buf[0], str.data(), copy_len);
3856 os.write(buf.data(),
static_cast<std::streamsize
>(len));
3863 std::string _freadstringnewline(std::istream &is)
3866 std::getline(is, s,
'\n');
3874 std::string _freadfixedlengthstring(std::istream &is,
size_t length,
bool strip_last_char =
true,
size_t max_length =
LIBFS_MAX_STRING_LENGTH)
3878 throw std::domain_error(
"Fixed-length string read with zero length.\n");
3880 if (length > max_length)
3882 throw std::domain_error(
"Fixed-length string length " + std::to_string(length) +
" exceeds maximum " + std::to_string(max_length) +
".\n");
3886 is.read(&str[0], length);
3887 if (static_cast<size_t>(is.gcount()) != length)
3889 if (is.gcount() == 0)
3891 throw std::runtime_error(
"Unexpected end of binary stream while reading fixed-length string: expected " + std::to_string(length) +
" bytes, got EOF.\n");
3893 throw std::runtime_error(
"Short read in binary stream while reading fixed-length string: expected " + std::to_string(length) +
" bytes, got " + std::to_string(is.gcount()) +
".\n");
3895 if (strip_last_char)
3897 str = str.substr(0, length - 1);
3909 int32_t num_vertices =
static_cast<int32_t
>(annot.
num_vertices());
3910 _fwritet<int32_t>(os, num_vertices);
3913 for (
size_t i = 0; i < static_cast<size_t>(num_vertices); i++)
3920 _fwritet<int32_t>(os, 1);
3921 _fwritet<int32_t>(os, -2);
3924 _fwritet<int32_t>(os, num_entries);
3927 std::string orig_filename =
"unknown";
3928 int32_t orig_filename_len =
static_cast<int32_t
>(orig_filename.size());
3929 _fwritet<int32_t>(os, orig_filename_len);
3930 _fwritefixedlengthstring(os, orig_filename, static_cast<size_t>(orig_filename_len));
3933 _fwritet<int32_t>(os, num_entries);
3935 for (int32_t i = 0; i < num_entries; i++)
3939 int32_t name_len =
static_cast<int32_t
>(annot.
colortable.
name[i].size()) + 1;
3940 _fwritet<int32_t>(os, name_len);
3941 _fwritefixedlengthstring(os, annot.
colortable.
name[i] +
'\0', static_cast<size_t>(name_len));
3966 ofs.open(filename, std::ofstream::out | std::ofstream::binary);
3974 throw std::runtime_error(
"Unable to open annot file '" + filename +
"' for writing.\n");
3983 void write_curv(std::ostream &os, std::vector<float> curv_data, int32_t num_faces = 100000)
3985 const uint32_t CURV_MAGIC = 16777215;
3986 _fwritei3(os, CURV_MAGIC);
3987 _fwritet<int32_t>(os, int(curv_data.size()));
3988 _fwritet<int32_t>(os, num_faces);
3989 _fwritet<int32_t>(os, 1);
3990 for (
size_t i = 0; i < curv_data.size(); i++)
3992 _fwritet<float>(os, curv_data[i]);
4010 void write_curv(
const std::string &filename, std::vector<float> curv_data,
const int32_t num_faces = 100000)
4013 ofs.open(filename, std::ofstream::out | std::ofstream::binary);
4021 throw std::runtime_error(
"Unable to open curvature file '" + filename +
"' for writing.\n");
4032 _fwritet<int32_t>(os, 1);
4041 size_t unused_header_space_size_left = 256;
4043 unused_header_space_size_left -= 2;
4050 throw std::logic_error(
"MGH header ras_good_flag set but Mdc and/or Pxyz_c vectors are undersized.\n");
4056 for (
int i = 0; i < 9; i++)
4060 for (
int i = 0; i < 3; i++)
4065 unused_header_space_size_left -= 60;
4068 for (
size_t i = 0; i < unused_header_space_size_left; i++)
4070 _fwritet<uint8_t>(os, 0);
4079 throw std::logic_error(
"Detected mismatch of MRI_INT data size and MGH header dim length values.\n");
4081 for (
size_t i = 0; i < num_values; i++)
4090 throw std::logic_error(
"Detected mismatch of MRI_FLOAT data size and MGH header dim length values.\n");
4092 for (
size_t i = 0; i < num_values; i++)
4101 throw std::logic_error(
"Detected mismatch of MRI_UCHAR data size and MGH header dim length values.\n");
4103 for (
size_t i = 0; i < num_values; i++)
4112 throw std::logic_error(
"Detected mismatch of MRI_SHORT data size and MGH header dim length values.\n");
4114 for (
size_t i = 0; i < num_values; i++)
4121 throw std::domain_error(
"Unsupported MRI data type " + std::to_string(mgh.
header.
dtype) +
", cannot write MGH data.\n");
4143 ofs.open(filename, std::ofstream::out | std::ofstream::binary);
4151 throw std::runtime_error(
"Unable to open MGH file '" + filename +
"' for writing.\n");
4155 #ifdef LIBFS_HAS_ZLIB 4170 inline void read_mgz(
Mgh *mgh,
const std::string &filename)
4172 gzFile gz = gzopen(filename.c_str(),
"rb");
4176 const char *errstr = gzerror(gz, &errnum);
4177 throw std::runtime_error(
"Could not open MGZ file '" + filename +
"' for reading: " +
4178 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
4180 std::vector<char> buf;
4183 while ((n = gzread(gz, chunk,
sizeof(chunk))) > 0)
4185 buf.insert(buf.end(), chunk, chunk + n);
4190 const char *errstr = gzerror(gz, &errnum);
4192 throw std::runtime_error(
"Error decompressing MGZ file '" + filename +
"': " +
4193 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
4196 std::istringstream iss(std::string(buf.data(), buf.size()));
4215 inline void write_mgz(
const Mgh &mgh,
const std::string &filename)
4217 std::ostringstream oss;
4219 std::string data = oss.str();
4221 gzFile gz = gzopen(filename.c_str(),
"wb");
4225 const char *errstr = gzerror(gz, &errnum);
4226 throw std::runtime_error(
"Could not open MGZ file '" + filename +
"' for writing: " +
4227 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
4229 z_size_t total_written = 0;
4230 while (total_written < data.size())
4232 int written = gzwrite(gz, data.data() + total_written,
static_cast<unsigned int>(data.size() - total_written));
4236 const char *errstr = gzerror(gz, &errnum);
4238 throw std::runtime_error(
"Error writing MGZ file '" + filename +
"': " +
4239 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
4241 total_written +=
static_cast<z_size_t
>(written);
4246 #endif // LIBFS_HAS_ZLIB 4344 #pragma pack(push, 1) 4388 char intent_name[16];
4398 inline int _nifti_dtype_to_mri(int16_t nifti_dtype)
4400 switch (nifti_dtype)
4402 case NIFTI_DT_UINT8:
return MRI_UCHAR;
4403 case NIFTI_DT_INT16:
return MRI_SHORT;
4404 case NIFTI_DT_INT32:
return MRI_INT;
4405 case NIFTI_DT_FLOAT32:
return MRI_FLOAT;
4407 throw std::runtime_error(
"Unsupported NIfTI data type " + std::to_string(nifti_dtype) +
4408 ". Supported types: UINT8 (2), INT16 (4), INT32 (8), FLOAT32 (16).\n");
4415 inline int16_t _mri_dtype_to_nifti(int32_t mri_dtype)
4419 case MRI_UCHAR:
return NIFTI_DT_UINT8;
4420 case MRI_SHORT:
return NIFTI_DT_INT16;
4421 case MRI_INT:
return NIFTI_DT_INT32;
4422 case MRI_FLOAT:
return NIFTI_DT_FLOAT32;
4424 throw std::runtime_error(
"Unsupported MGH data type " + std::to_string(mri_dtype) +
4425 " for NIfTI output.\n");
4435 inline Nifti1Header _read_nifti1_header(std::istream &is,
bool &file_is_bigendian)
4438 is.read(reinterpret_cast<char *>(&hdr),
sizeof(
Nifti1Header));
4439 if (static_cast<size_t>(is.gcount()) !=
sizeof(
Nifti1Header))
4441 throw std::runtime_error(
"NIfTI file too small for header: expected " +
4448 int32_t swapped = _swap_endian(hdr.
sizeof_hdr);
4451 file_is_bigendian =
true;
4455 throw std::runtime_error(
"Invalid NIfTI file: sizeof_hdr = " +
4456 std::to_string(hdr.
sizeof_hdr) +
" (expected 348).\n");
4461 file_is_bigendian =
false;
4465 bool need_swap = (file_is_bigendian != _is_bigendian());
4472 for (
int i = 0; i < 8; i++) hdr.
dim[i] = _swap_endian(hdr.
dim[i]);
4480 for (
int i = 0; i < 8; i++) hdr.
pixdim[i] = _swap_endian(hdr.
pixdim[i]);
4500 for (
int i = 0; i < 4; i++) hdr.
srow_x[i] = _swap_endian(hdr.
srow_x[i]);
4501 for (
int i = 0; i < 4; i++) hdr.
srow_y[i] = _swap_endian(hdr.
srow_y[i]);
4502 for (
int i = 0; i < 4; i++) hdr.
srow_z[i] = _swap_endian(hdr.
srow_z[i]);
4506 if (std::memcmp(hdr.
magic,
"n+1\0", 4) != 0 &&
4507 std::memcmp(hdr.
magic,
"ni1\0", 4) != 0)
4510 throw std::runtime_error(
"NIfTI file has invalid magic string. " 4511 "Only single-file .nii (n+1) is supported.\n");
4519 template <
typename T>
4520 inline T _nifti_read_data_element(std::istream &is,
bool file_is_bigendian)
4523 is.read(reinterpret_cast<char *>(&val),
sizeof(T));
4524 if (static_cast<size_t>(is.gcount()) !=
sizeof(T))
4526 throw std::runtime_error(
"Unexpected end of NIfTI data stream.\n");
4528 if (file_is_bigendian != _is_bigendian())
4530 val = _swap_endian(val);
4537 template <
typename T>
4538 inline void _nifti_write_data_element(std::ostream &os, T val,
bool file_is_bigendian)
4540 if (file_is_bigendian != _is_bigendian())
4542 val = _swap_endian(val);
4544 os.write(reinterpret_cast<const char *>(&val),
sizeof(T));
4559 mgh_header->
Mdc.clear();
4560 mgh_header->
Pxyz_c.clear();
4577 float a = std::sqrt(std::max(0.0f, 1.0f - (b * b + c * c + d * d)));
4578 float qfac = (hdr.
pixdim[0] < 0.0f) ? -1.0f : 1.0f;
4584 mgh_header->
Mdc.clear();
4585 mgh_header->
Pxyz_c.clear();
4588 float R11 = a * a + b * b - c * c - d * d;
4589 float R12 = 2.0f * (b * c - a * d);
4590 float R13 = 2.0f * (b * d + a * c);
4591 float R21 = 2.0f * (b * c + a * d);
4592 float R22 = a * a + c * c - b * b - d * d;
4593 float R23 = 2.0f * (c * d - a * b);
4594 float R31 = 2.0f * (b * d - a * c);
4595 float R32 = 2.0f * (c * d + a * b);
4596 float R33 = a * a + d * d - b * b - c * c;
4599 float sx = hdr.
pixdim[1];
4600 float sy = hdr.
pixdim[2];
4601 float sz = hdr.
pixdim[3] * qfac;
4603 mgh_header->
Mdc.push_back(R11 * sx); mgh_header->
Mdc.push_back(R12 * sy); mgh_header->
Mdc.push_back(R13 * sz);
4604 mgh_header->
Mdc.push_back(R21 * sx); mgh_header->
Mdc.push_back(R22 * sy); mgh_header->
Mdc.push_back(R23 * sz);
4605 mgh_header->
Mdc.push_back(R31 * sx); mgh_header->
Mdc.push_back(R32 * sy); mgh_header->
Mdc.push_back(R33 * sz);
4631 std::streampos start_pos = is->tellg();
4632 is->seekg(0, std::ios::end);
4633 std::streamsize total_file_size = is->tellg();
4634 is->seekg(start_pos, std::ios::beg);
4637 bool file_is_bigendian =
false;
4638 Nifti1Header hdr = _read_nifti1_header(*is, file_is_bigendian);
4641 int64_t true_dim1 = hdr.
dim[1];
4642 bool hack_detected =
false;
4645 if (hdr.
dim[1] < 0 && hdr.
dim[2] == 1 && hdr.
dim[3] == 1)
4647 int bytes_per_element = hdr.
bitpix / 8;
4650 int64_t frames = (hdr.
dim[4] > 1) ? static_cast<int64_t>(hdr.
dim[4]) : 1;
4652 int64_t payload_bytes = total_file_size -
static_cast<int64_t
>(hdr.
vox_offset);
4653 int64_t computed_x = payload_bytes / (bytes_per_element * frames);
4657 if (computed_x >= 1000 && computed_x <= 5000000)
4659 hack_detected =
true;
4660 true_dim1 = computed_x;
4665 if (force_standard && hack_detected)
4667 throw std::runtime_error(
4668 "NIfTI file does not conform to the NIfTI-1 standard: " 4669 "dim[1] overflow detected (likely FreeSurfer hack). " 4670 "Re-run with force_standard=false to recover surface data.\n");
4674 int32_t dim2 = (hdr.
dim[2] > 0) ? hdr.
dim[2] : 1;
4675 int32_t dim3 = (hdr.
dim[3] > 0) ? hdr.
dim[3] : 1;
4676 int32_t dim4 = (hdr.
dim[4] > 0) ? hdr.
dim[4] : 1;
4677 int bytes_per_element = hdr.
bitpix / 8;
4680 uint64_t total_elements = static_cast<uint64_t>(true_dim1) *
4681 static_cast<uint64_t
>(dim2) *
4682 static_cast<uint64_t>(dim3) *
4683 static_cast<uint64_t
>(dim4);
4684 uint64_t expected_payload = total_elements *
static_cast<uint64_t
>(bytes_per_element);
4686 if (!fs::util::check_alloc(static_cast<size_t>(total_elements),
static_cast<size_t>(bytes_per_element)))
4688 throw std::runtime_error(
"NIfTI dimensions exceed maximum allowed allocation (" +
4692 uint64_t available_bytes =
static_cast<uint64_t
>(total_file_size) - static_cast<uint64_t>(hdr.
vox_offset);
4693 if (expected_payload > available_bytes)
4695 throw std::runtime_error(
"Corrupted NIfTI file: dimensions require " +
4696 std::to_string(expected_payload) +
" bytes but only " +
4697 std::to_string(available_bytes) +
" available.\n");
4700 if (hdr.
vox_offset < 348 || static_cast<uint64_t>(hdr.
vox_offset) >= static_cast<uint64_t>(total_file_size))
4702 throw std::runtime_error(
"Corrupted NIfTI file: invalid vox_offset " +
4707 int mri_dtype = _nifti_dtype_to_mri(hdr.
datatype);
4716 _nifti_extract_ras(hdr, &mgh->
header);
4719 is->seekg(start_pos + std::streamoff(static_cast<int64_t>(hdr.
vox_offset)), std::ios::beg);
4724 size_t num_voxels = static_cast<size_t>(total_elements);
4728 #ifdef LIBFS_DBG_INFO 4729 std::cout <<
LIBFS_APPTAG <<
"Reading NIfTI file: " << true_dim1 <<
"x" << dim2
4730 <<
"x" << dim3 <<
"x" << dim4 <<
" (" << num_voxels <<
" voxels), dtype=" 4731 << hdr.
datatype << (hack_detected ?
" [FS hack]" :
"") <<
"\n";
4734 if (mri_dtype == MRI_INT)
4737 for (
size_t i = 0; i < num_voxels; i++)
4739 int32_t raw = _nifti_read_data_element<int32_t>(*is, file_is_bigendian);
4740 mgh->
data.
data_mri_int.push_back(static_cast<int32_t>(std::round(raw * slope + inter)));
4743 else if (mri_dtype == MRI_FLOAT)
4746 for (
size_t i = 0; i < num_voxels; i++)
4748 float raw = _nifti_read_data_element<float>(*is, file_is_bigendian);
4752 else if (mri_dtype == MRI_UCHAR)
4755 for (
size_t i = 0; i < num_voxels; i++)
4757 uint8_t raw = _nifti_read_data_element<uint8_t>(*is, file_is_bigendian);
4758 mgh->
data.
data_mri_uchar.push_back(static_cast<uint8_t>(std::max(0.0f, std::min(255.0f, std::round(raw * slope + inter)))));
4761 else if (mri_dtype == MRI_SHORT)
4764 for (
size_t i = 0; i < num_voxels; i++)
4766 int16_t raw = _nifti_read_data_element<int16_t>(*is, file_is_bigendian);
4767 mgh->
data.
data_mri_short.push_back(static_cast<short>(std::round(raw * slope + inter)));
4778 inline void read_nifti(
Mgh *mgh,
const std::string &filename,
bool force_standard)
4780 if (fs::util::ends_with(filename,
".nii.gz") || fs::util::ends_with(filename,
".NII.GZ"))
4782 #ifdef LIBFS_HAS_ZLIB 4783 read_nifti_gz(mgh, filename, force_standard);
4786 throw std::runtime_error(
"Cannot read .nii.gz file '" + filename +
4787 "': zlib support not enabled. " 4788 "Link with -lz or decompress the file first.\n");
4792 std::ifstream ifs(filename, std::ios::binary);
4795 throw std::runtime_error(
"Could not open NIfTI file '" + filename +
"' for reading.\n");
4815 throw std::runtime_error(
"MGH dimensions exceed NIfTI-1 int16 limit (32767). " 4816 "Cannot write as NIfTI.\n");
4819 bool file_is_bigendian =
true;
4823 std::memset(&hdr, 0,
sizeof(hdr));
4838 case MRI_UCHAR: hdr.
bitpix = 8;
break;
4839 case MRI_SHORT: hdr.
bitpix = 16;
break;
4840 case MRI_INT: hdr.
bitpix = 32;
break;
4841 case MRI_FLOAT: hdr.
bitpix = 32;
break;
4881 std::memcpy(hdr.
magic,
"n+1\0", 4);
4884 bool need_swap = (file_is_bigendian != _is_bigendian());
4891 for (
int i = 0; i < 8; i++) hdr_swapped.
dim[i] = _swap_endian(hdr.
dim[i]);
4899 for (
int i = 0; i < 8; i++) hdr_swapped.
pixdim[i] = _swap_endian(hdr.
pixdim[i]);
4918 for (
int i = 0; i < 4; i++) hdr_swapped.
srow_x[i] = _swap_endian(hdr.
srow_x[i]);
4919 for (
int i = 0; i < 4; i++) hdr_swapped.
srow_y[i] = _swap_endian(hdr.
srow_y[i]);
4920 for (
int i = 0; i < 4; i++) hdr_swapped.
srow_z[i] = _swap_endian(hdr.
srow_z[i]);
4921 os.write(reinterpret_cast<const char *>(&hdr_swapped),
sizeof(
Nifti1Header));
4925 os.write(reinterpret_cast<const char *>(&hdr),
sizeof(
Nifti1Header));
4929 int32_t ext_indicator = 0;
4930 if (file_is_bigendian != _is_bigendian())
4932 ext_indicator = _swap_endian(ext_indicator);
4934 os.write(reinterpret_cast<const char *>(&ext_indicator), 4);
4940 for (
size_t i = 0; i < num_values; i++)
4942 _nifti_write_data_element<int32_t>(os, mgh.
data.
data_mri_int[i], file_is_bigendian);
4947 for (
size_t i = 0; i < num_values; i++)
4954 for (
size_t i = 0; i < num_values; i++)
4956 _nifti_write_data_element<uint8_t>(os, mgh.
data.
data_mri_uchar[i], file_is_bigendian);
4961 for (
size_t i = 0; i < num_values; i++)
4968 throw std::domain_error(
"Unsupported MRI data type " + std::to_string(mgh.
header.
dtype) +
4969 " for NIfTI output.\n");
4979 if (fs::util::ends_with(filename,
".nii.gz") || fs::util::ends_with(filename,
".NII.GZ"))
4981 #ifdef LIBFS_HAS_ZLIB 4982 write_nifti_gz(mgh, filename);
4985 throw std::runtime_error(
"Cannot write .nii.gz file '" + filename +
4986 "': zlib support not enabled. Link with -lz.\n");
4990 std::ofstream ofs(filename, std::ofstream::out | std::ofstream::binary);
4993 throw std::runtime_error(
"Unable to open NIfTI file '" + filename +
"' for writing.\n");
5001 #ifdef LIBFS_HAS_ZLIB 5008 inline void read_nifti_gz(
Mgh *mgh,
const std::string &filename,
bool force_standard)
5010 gzFile gz = gzopen(filename.c_str(),
"rb");
5014 const char *errstr = gzerror(gz, &errnum);
5015 throw std::runtime_error(
"Could not open NIfTI.GZ file '" + filename +
"' for reading: " +
5016 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
5018 std::vector<char> buf;
5021 while ((n = gzread(gz, chunk,
sizeof(chunk))) > 0)
5023 buf.insert(buf.end(), chunk, chunk + n);
5028 const char *errstr = gzerror(gz, &errnum);
5030 throw std::runtime_error(
"Error decompressing NIfTI.GZ file '" + filename +
"': " +
5031 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
5034 std::istringstream iss(std::string(buf.data(), buf.size()));
5042 inline void write_nifti_gz(
const Mgh &mgh,
const std::string &filename)
5044 std::ostringstream oss;
5046 std::string data = oss.str();
5048 gzFile gz = gzopen(filename.c_str(),
"wb");
5052 const char *errstr = gzerror(gz, &errnum);
5053 throw std::runtime_error(
"Could not open NIfTI.GZ file '" + filename +
"' for writing: " +
5054 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
5056 z_size_t total_written = 0;
5057 while (total_written < data.size())
5059 z_size_t remaining = data.size() - total_written;
5060 z_size_t chunk = (remaining > 131072) ? 131072 : remaining;
5061 int written = gzwrite(gz, data.data() + total_written,
static_cast<unsigned int>(chunk));
5065 const char *errstr = gzerror(gz, &errnum);
5067 throw std::runtime_error(
"Error writing NIfTI.GZ file '" + filename +
"': " +
5068 (errstr ? std::string(errstr) :
"unknown error") +
"\n");
5070 total_written +=
static_cast<z_size_t
>(written);
5075 #endif // LIBFS_HAS_ZLIB (NIfTI GZ support) 5110 Label(std::vector<int> vertices, std::vector<float> values)
5112 assert(vertices.size() == values.size());
5115 coord_x = std::vector<float>(vertices.size(), 0.0f);
5116 coord_y = std::vector<float>(vertices.size(), 0.0f);
5117 coord_z = std::vector<float>(vertices.size(), 0.0f);
5124 value = std::vector<float>(vertices.size(), 0.0f);
5125 coord_x = std::vector<float>(vertices.size(), 0.0f);
5126 coord_y = std::vector<float>(vertices.size(), 0.0f);
5127 coord_z = std::vector<float>(vertices.size(), 0.0f);
5139 if (surface_num_verts < this->vertex.size())
5141 std::cerr <<
"Invalid number of vertices for surface, must be at least " << this->vertex.size() <<
"\n";
5143 std::vector<bool> is_in = std::vector<bool>(surface_num_verts,
false);
5145 for (
size_t i = 0; i < this->vertex.size(); i++)
5147 is_in[this->vertex[i]] =
true;
5155 size_t num_ent = this->vertex.size();
5156 if (this->coord_x.size() != num_ent || this->coord_y.size() != num_ent || this->coord_z.size() != num_ent || this->value.size() != num_ent)
5158 std::cerr <<
"Inconsistent label: sizes of property vectors do not match.\n";
5170 void write_surf(std::vector<float> vertices, std::vector<int32_t> faces, std::ostream &os)
5172 const uint32_t SURF_TRIS_MAGIC = 16777214;
5173 _fwritei3(os, SURF_TRIS_MAGIC);
5174 std::string created_and_comment_lines =
"Created by fslib\n\n";
5175 os << created_and_comment_lines;
5176 _fwritet<int32_t>(os, int(vertices.size() / 3));
5177 _fwritet<int32_t>(os, int(faces.size() / 3));
5178 for (
size_t i = 0; i < vertices.size(); i++)
5180 _fwritet<float>(os, vertices[i]);
5182 for (
size_t i = 0; i < faces.size(); i++)
5184 _fwritet<int32_t>(os, faces[i]);
5201 void write_surf(std::vector<float> vertices, std::vector<int32_t> faces,
const std::string &filename)
5204 ofs.open(filename, std::ofstream::out | std::ofstream::binary);
5212 throw std::runtime_error(
"Unable to open surf file '" + filename +
"' for writing.\n");
5231 ofs.open(filename, std::ofstream::out | std::ofstream::binary);
5239 throw std::runtime_error(
"Unable to open surf file '" + filename +
"' for writing.\n");
5253 size_t num_entries_header = 0;
5254 size_t num_entries = 0;
5255 while (std::getline(*is, line))
5258 std::istringstream iss(line);
5267 if (!(iss >> num_entries_header))
5269 throw std::domain_error(
"Could not parse entry count from label file, invalid format.\n");
5275 float x, y, z, value;
5276 if (!(iss >> vertex >> x >> y >> z >> value))
5278 throw std::domain_error(
"Could not parse line " + std::to_string(line_idx + 1) +
" of label file, invalid format.\n");
5280 label->
vertex.push_back(vertex);
5284 label->
value.push_back(value);
5289 if (num_entries != num_entries_header)
5291 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");
5293 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)
5295 throw std::domain_error(
"Expected " + std::to_string(num_entries) +
" entries in all Label vectors, but some did not match.\n");
5314 std::ifstream infile(filename, std::fstream::in);
5315 if (infile.is_open())
5322 throw std::runtime_error(
"Could not open label file '" + filename +
"' for reading.\n");
5333 os <<
"#!ascii label from subject anonymous\n" 5334 << num_entries <<
"\n";
5335 for (
size_t i = 0; i < num_entries; i++)
5357 ofs.open(filename, std::ofstream::out);
5365 throw std::runtime_error(
"Unable to open label file '" + filename +
"' for writing.\n");
5386 if (fs::util::ends_with(filename, {
".ply",
".PLY"}))
5390 else if (fs::util::ends_with(filename, {
".obj",
".OBJ"}))
5394 else if (fs::util::ends_with(filename, {
".off",
".OFF"}))
5417 void write_mesh(
const Mesh &mesh,
const std::string &filename,
const std::vector<uint8_t> col)
5419 if (fs::util::ends_with(filename, {
".ply",
".PLY"}))
5423 else if (fs::util::ends_with(filename, {
".obj",
".OBJ"}))
5427 else if (fs::util::ends_with(filename, {
".off",
".OFF"}))
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:3983
const int16_t NIFTI_DT_UINT8
Definition: libfs.h:4272
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:925
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:2499
size_t num_entries() const
Get the number of enties (regions) in this Colortable.
Definition: libfs.h:2459
Mgh(Curv curv)
Definition: libfs.h:2663
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:5131
std::string to_off(const std::vector< uint8_t > col) const
Return string representing the mesh in PLY format.
Definition: libfs.h:2356
Models a FreeSurfer curv file that contains per-vertex float data.
Definition: libfs.h:2421
std::string to_obj(const std::vector< uint8_t > col) const
Return string representing the mesh in Wavefront Object (.obj) format with vertex colors...
Definition: libfs.h:1090
const int MRI_SHORT
MRI data type representing a 16 bit signed integer.
Definition: libfs.h:859
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:5132
const int16_t NIFTI_DT_FLOAT128
Definition: libfs.h:4329
unsigned int d2
size of data along 2nd dimension
Definition: libfs.h:2725
const std::string LOGTAG_EXCESSIVE
Logging threshold for warning messages.
Definition: libfs.h:288
An annotation, also known as a brain surface parcellation. Assigns to each vertex a region...
Definition: libfs.h:2497
const int16_t NIFTI_DT_FLOAT32
Definition: libfs.h:4287
const std::string LOGTAG_VERBOSE
Logging threshold for warning messages.
Definition: libfs.h:285
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:3401
edge_set as_edgelist() const
Return edge list representation of this mesh.
Definition: libfs.h:1166
std::vector< int32_t > label
label integer computed from rgba values. Maps to the Annot.vertex_label field.
Definition: libfs.h:2456
Array4D(MghHeader *mgh_header)
Definition: libfs.h:2691
std::string to_obj() const
Return string representing the mesh in Wavefront Object (.obj) format.
Definition: libfs.h:1072
std::string time_tag(std::chrono::system_clock::time_point t)
Get current time as string, e.g. for log messages.
Definition: libfs.h:258
static void from_obj(Mesh *mesh, const std::string &filename)
Read a brainmesh from a Wavefront object format mesh file.
Definition: libfs.h:1741
const std::string LOGTAG_WARNING
Logging threshold for warning messages.
Definition: libfs.h:279
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:1277
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:636
bool file_exists(const std::string &name)
Check whether a file exists (can be read) at given path.
Definition: libfs.h:505
const std::string LOGTAG_INFO
Logging threshold for warning messages.
Definition: libfs.h:282
#define LIBFS_MAX_ALLOC_BYTES
Maximum memory allocation limit.
Definition: libfs.h:81
A simple 4D array datastructure, useful for representing volume data.
Definition: libfs.h:2678
std::vector< float > data
The curvature data, one value per vertex. Something like the cortical thickness at each vertex...
Definition: libfs.h:2438
static void from_ply(Mesh *mesh, std::istream *is)
Read a brainmesh from a Stanford PLY format stream.
Definition: libfs.h:1914
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:2165
std::vector< T > data
the data, as a 1D vector. Use fs::Array4D::at for easy access in 4D.
Definition: libfs.h:2728
std::vector< int32_t > b
green channel of RGBA color
Definition: libfs.h:2454
void write_nifti(const Mgh &mgh, std::ostream &os)
Write MGH data to a NIfTI-1 file (stream overload).
Definition: libfs.h:4809
static void from_ply(Mesh *mesh, const std::string &filename)
Read a brainmesh from a Stanford PLY format mesh file.
Definition: libfs.h:2108
std::vector< int32_t > g
blue channel of RGBA color
Definition: libfs.h:2453
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:3668
void write_mgh(const Mgh &mgh, std::ostream &os)
Write MGH data to a stream.
Definition: libfs.h:4030
static void from_off(Mesh *mesh, const std::string &filename)
Read a brainmesh from an OFF format mesh file.
Definition: libfs.h:1892
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:5137
const int16_t NIFTI_DT_COMPLEX128
Definition: libfs.h:4333
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, expand it back to full mesh size.
Definition: libfs.h:1538
Models a triangular mesh, used for brain surface meshes.
Definition: libfs.h:897
const int MRI_UCHAR
MRI data type representing an 8 bit unsigned integer.
Definition: libfs.h:850
size_t num_entries() const
Return the number of entries (vertices/voxels) in this label.
Definition: libfs.h:5153
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:2709
void to_obj_file(const std::string &filename, const std::vector< uint8_t > col) const
Export this mesh to a file in Wavefront OBJ format with vertex colors.
Definition: libfs.h:1450
Models the data of an MGH file. Currently these are 1D vectors, but one can compute the 4D array usin...
Definition: libfs.h:2643
void read_label(Label *label, std::istream *is)
Read a FreeSurfer ASCII label from a stream.
Definition: libfs.h:5249
void to_ply_file(const std::string &filename) const
Export this mesh to a file in Stanford PLY format.
Definition: libfs.h:2324
MghData(std::vector< uint8_t > curv_data)
constructor to create MghData from MRI_UCHAR (uint8_t) data.
Definition: libfs.h:2647
Mesh(std::vector< float > cvertices, std::vector< int32_t > cfaces)
Construct a Mesh from the given vertices and faces.
Definition: libfs.h:901
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:2568
const int16_t NIFTI_DT_COMPLEX64
Definition: libfs.h:4292
std::vector< std::vector< bool > > as_adjmatrix() const
Return adjacency matrix representation of this mesh.
Definition: libfs.h:1125
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:2703
Array4D(Mgh *mgh)
Definition: libfs.h:2697
const int16_t NIFTI_DT_INT64
Definition: libfs.h:4320
unsigned int d1
size of data along 1st dimension
Definition: libfs.h:2724
#define LIBFS_MAX_STRING_LENGTH
Maximum length for fixed-length strings read from binary headers (e.g., filenames in annot colortable...
Definition: libfs.h:86
std::string to_ply() const
Return string representing the mesh in PLY format. Overload that works without passing a color vector...
Definition: libfs.h:2254
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:2448
const int MRI_FLOAT
MRI data type representing a 32 bit float.
Definition: libfs.h:856
const int16_t NIFTI_DT_FLOAT64
Definition: libfs.h:4297
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:5170
const int16_t NIFTI_DT_UINT64
Definition: libfs.h:4324
std::vector< std::vector< size_t > > as_adjlist(const bool via_matrix=true) const
Return adjacency list representation of this mesh.
Definition: libfs.h:1195
const int16_t NIFTI_DT_INT16
Definition: libfs.h:4277
static fs::Mesh construct_pyramid()
Construct and return a simple pyramidal mesh.
Definition: libfs.h:976
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:1254
std::vector< int32_t > id
internal region index
Definition: libfs.h:2450
const int16_t NIFTI_DT_UINT32
Definition: libfs.h:4315
void write_mesh(const Mesh &mesh, const std::string &filename)
Write a mesh to a file in different formats.
Definition: libfs.h:5384
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:2520
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:2654
const int MRI_INT
MRI data type representing a 32 bit signed integer.
Definition: libfs.h:853
static void from_obj(Mesh *mesh, std::istream *is)
Read a brainmesh from a Wavefront object format stream.
Definition: libfs.h:1572
Curv(std::vector< float > curv_data)
Construct a Curv instance from the given per-vertex data.
Definition: libfs.h:2425
const int16_t NIFTI_DT_NONE
No data / unknown type (value 0).
Definition: libfs.h:4264
unsigned int d3
size of data along 3rd dimension
Definition: libfs.h:2726
Models a whole MGH file.
Definition: libfs.h:2658
size_t num_vertices() const
Get the number of vertices of this parcellation (or the associated surface).
Definition: libfs.h:2556
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:2444
void write_surf(const Mesh &mesh, const std::string &filename)
Write a mesh to a binary file in FreeSurfer surf format.
Definition: libfs.h:5228
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:580
void read_annot(Annot *annot, std::istream *is)
Read a FreeSurfer annotation or brain surface parcellation from an annot stream.
Definition: libfs.h:3562
int32_t num_vertices
The number of vertices of the mesh to which this belongs. Can be deduced from length of 'data'...
Definition: libfs.h:2441
std::vector< float > vertex_coords(const size_t vertex) const
Get all coordinates of the vertex, given by its index.
Definition: libfs.h:2210
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:1764
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:2186
Mgh(std::vector< float > curv_data)
Definition: libfs.h:2668
std::vector< std::string > read_subjectsfile(const std::string &filename)
Read a vector of subject identifiers from a FreeSurfer subjects file.
Definition: libfs.h:2844
std::vector< float > value
the value of the label, can represent continuous data like a p-value, or sometimes simply 1...
Definition: libfs.h:5134
std::vector< int32_t > r
red channel of RGBA color
Definition: libfs.h:2452
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:2794
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:2334
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:1153
size_t num_faces() const
Return the number of faces in this mesh.
Definition: libfs.h:2148
void write_label(const Label &label, std::ostream &os)
Write label data to a stream.
Definition: libfs.h:5330
const int16_t NIFTI_DT_INT8
Definition: libfs.h:4307
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:5130
std::string to_ply(const std::vector< uint8_t > col) const
Return string representing the mesh in PLY format.
Definition: libfs.h:2270
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:2588
void write_annot(const Annot &annot, std::ostream &os)
Write a FreeSurfer annotation (brain surface parcellation) to a stream.
Definition: libfs.h:3907
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:2535
static fs::Mesh construct_cube()
Construct and return a simple cube mesh.
Definition: libfs.h:939
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:2651
Mgh nifti_to_mgh(const std::string &filename)
Convert a NIfTI-1 file directly to MGH by reading it.
Definition: libfs.h:5086
void read_mgh(Mgh *mgh, std::istream *is)
Read MGH data from a stream.
Definition: libfs.h:2896
MghData(std::vector< float > curv_data)
constructor to create MghData from MRI_FLOAT (float) data.
Definition: libfs.h:2649
void write_subjectsfile(const std::string &filename, const std::vector< std::string > &subjects)
Write a vector of subject identifiers to a FreeSurfer subjects file.
Definition: libfs.h:2873
MghData(std::vector< short > curv_data)
constructor to create MghData from MRI_SHORT (short) data.
Definition: libfs.h:2648
const std::string LOGTAG_ERROR
Logging threshold for error messages.
Definition: libfs.h:276
Mesh()
Construct an empty Mesh.
Definition: libfs.h:923
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:1471
void read_nifti(Mgh *, std::istream *, bool force_standard=false)
Read a NIfTI-1 file into an Mgh struct (stream overload).
Definition: libfs.h:4628
void read_nifti(Mgh *, const std::string &, bool force_standard=false)
Read a NIfTI-1 file into an Mgh struct (filename overload).
Definition: libfs.h:4778
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:2652
Label(std::vector< int > vertices)
Construct a Label from the given vertices / voxel numbers.
Definition: libfs.h:5121
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:1398
void to_obj_file(const std::string &filename) const
Export this mesh to a file in Wavefront OBJ format.
Definition: libfs.h:1443
void read_surf(Mesh *surface, const std::string &filename)
Read a brain mesh from a file in binary FreeSurfer 'surf' format into the given Mesh instance...
Definition: libfs.h:3254
Curv()
Construct an empty Curv instance.
Definition: libfs.h:2432
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:533
std::vector< int32_t > region_vertices(const std::string ®ion_name) const
Get all vertices of a region given by name in the brain surface parcellation. Returns an integer vect...
Definition: libfs.h:2504
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:2414
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:3360
std::vector< uint8_t > vertex_colors
n x 3 vector of RGB color values, 3 per vertex (v0_r, v0_g, v0_b, v1_r, ...). Empty if no vertex colo...
Definition: libfs.h:927
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:2470
int32_t num_faces
The number of faces of the mesh to which this belongs, typically irrelevant and ignored.
Definition: libfs.h:2435
const int16_t NIFTI_DT_UINT16
Definition: libfs.h:4311
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:2653
#define LIBFS_MAX_COLORTABLE_ENTRIES
Maximum number of entries in an annotation colortable.
Definition: libfs.h:91
unsigned int num_values() const
Get number of values/voxels.
Definition: libfs.h:2719
Colortable colortable
A Colortable defining the regions (most importantly, the region name and visualization color)...
Definition: libfs.h:2501
MghHeader header
Header for this MGH instance.
Definition: libfs.h:2660
Mesh(std::vector< std::vector< float >> cvertices, std::vector< std::vector< int32_t >> cfaces)
Construct a Mesh from 2-D vertex and face lists.
Definition: libfs.h:916
void log(std::string const &message, std::string const loglevel="INFO")
Log a message, goes to stdout.
Definition: libfs.h:293
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:2236
std::vector< T > vflatten(std::vector< std::vector< T >> values)
Flatten 2D vector.
Definition: libfs.h:434
Label(std::vector< int > vertices, std::vector< float > values)
Construct a Label from the given vertices / voxel numbers and values.
Definition: libfs.h:5110
const int16_t NIFTI_DT_BINARY
Definition: libfs.h:4268
std::vector< int32_t > a
alpha channel of RGBA color
Definition: libfs.h:2455
const int16_t NIFTI_DT_RGB24
Definition: libfs.h:4302
const std::string LOGTAG_CRITICAL
Logging threshold for critical messages.
Definition: libfs.h:273
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:2483
Label()
Default constructor for a label.
Definition: libfs.h:5107
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:1009
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:926
void to_off_file(const std::string &filename) const
Export this mesh to a file in OFF format.
Definition: libfs.h:2407
#define LIBFS_APPTAG
Application tag prepended to every debug message from libfs.
Definition: libfs.h:168
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:2500
MghData(Curv curv)
constructor to create MghData from a Curv instance
Definition: libfs.h:2650
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:5133
Array4D(unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)
Definition: libfs.h:2684
const int16_t NIFTI_DT_COMPLEX256
Definition: libfs.h:4339
const int16_t NIFTI_DT_INT32
Definition: libfs.h:4282
MghData(std::vector< int32_t > curv_data)
constructor to create MghData from MRI_INT (int32_t) data.
Definition: libfs.h:2646
std::vector< float > read_desc_data(const std::string &filename)
Read per-vertex brain morphometry data from a FreeSurfer curv, MGH, or NIfTI format file...
Definition: libfs.h:3690
Mgh()
Empty default constuctor.
Definition: libfs.h:2662
std::string to_off() const
Return string representing the mesh in OFF format. Overload that works without passing a color vector...
Definition: libfs.h:2347
std::vector< std::string > name
region name
Definition: libfs.h:2451
MghData data
4D data for this MGH instance.
Definition: libfs.h:2661
unsigned int d4
size of data along 4th dimension
Definition: libfs.h:2727
size_t num_vertices() const
Return the number of vertices in this mesh.
Definition: libfs.h:2134
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:3099