diff --git a/cmd/fixelconnectivity.cpp b/cmd/fixelconnectivity.cpp index 474dee0af8..54acf987d4 100644 --- a/cmd/fixelconnectivity.cpp +++ b/cmd/fixelconnectivity.cpp @@ -18,10 +18,12 @@ #include "command.h" #include "fixel/fixel.h" #include "fixel/helpers.h" -#include "fixel/index_remapper.h" + +#include "dwi/tractography/weights.h" #include "fixel/matrix.h" + #define DEFAULT_ANGLE_THRESHOLD 45.0 #define DEFAULT_CONNECTIVITY_THRESHOLD 0.01 @@ -60,7 +62,17 @@ void usage () + Argument ("value").type_float (0.0, 90.0) + Option ("mask", "provide a fixel data file containing a mask of those fixels to be computed; fixels outside the mask will be empty in the output matrix") - + Argument ("file").type_image_in(); + + Argument ("file").type_image_in() + + + DWI::Tractography::TrackWeightsInOption + + + OptionGroup ("Options for additional outputs to be generated") + + + Option ("count", "export a fixel data file encoding the number of connections for each fixel") + + Argument ("path").type_image_out() + + + Option ("extent", "export a fixel data file encoding the extent of connectivity (sum of weights) for each fixel") + + Argument ("path").type_image_out(); } @@ -69,6 +81,20 @@ using value_type = float; using Fixel::index_type; + +template +void set_optional_outputs (WriterType& writer) +{ + auto opt = get_options ("count"); + if (opt.size()) + writer.set_count_path (opt[0][0]); + opt = get_options ("extent"); + if (opt.size()) + writer.set_extent_path (opt[0][0]); +} + + + void run() { const value_type connectivity_threshold = get_option_value ("connectivity", value_type(DEFAULT_CONNECTIVITY_THRESHOLD)); @@ -96,14 +122,29 @@ void run() fixel_mask.value() = true; } - auto connectivity_matrix = Fixel::Matrix::generate (argument[1], - index_image, - fixel_mask, - angular_threshold); + if (get_options ("tck_weights_in").size()) { + + auto connectivity_matrix = Fixel::Matrix::generate_weighted (argument[1], + index_image, + fixel_mask, + angular_threshold); - Fixel::Matrix::normalise_and_write (connectivity_matrix, - connectivity_threshold, - argument[2]); + Fixel::Matrix::Writer writer (connectivity_matrix, connectivity_threshold); + set_optional_outputs (writer); + writer.save (argument[2]); + + } else { + + auto connectivity_matrix = Fixel::Matrix::generate_unweighted (argument[1], + index_image, + fixel_mask, + angular_threshold); + + Fixel::Matrix::Writer writer (connectivity_matrix, connectivity_threshold); + set_optional_outputs (writer); + writer.save (argument[2]); + + } } diff --git a/core/app.h b/core/app.h index e1dec0192b..18d90155af 100644 --- a/core/app.h +++ b/core/app.h @@ -171,7 +171,7 @@ namespace MR if (check_overwrite_files_func) check_overwrite_files_func (name); else - throw Exception ("output file \"" + name + "\" already exists (use -force option to force overwrite)"); + throw Exception ("output path \"" + name + "\" already exists (use -force option to force overwrite)"); } } diff --git a/core/fixel/helpers.h b/core/fixel/helpers.h index 480024305c..e932dec065 100644 --- a/core/fixel/helpers.h +++ b/core/fixel/helpers.h @@ -29,7 +29,7 @@ namespace MR { class InvalidFixelDirectoryException : public Exception - { + { public: InvalidFixelDirectoryException (const std::string& msg) : Exception(msg) {} InvalidFixelDirectoryException (const Exception& previous_exception, const std::string& msg) @@ -306,6 +306,7 @@ namespace MR header.size(0) = nfixels; header.size(1) = 1; header.size(2) = 1; + header.spacing(0) = header.spacing(1) = header.spacing(2) = 1.0; header.stride(0) = 1; header.stride(1) = 2; header.stride(2) = 3; diff --git a/docs/reference/commands/fixelconnectivity.rst b/docs/reference/commands/fixelconnectivity.rst index 2e4ebb0221..33d37fdf46 100644 --- a/docs/reference/commands/fixelconnectivity.rst +++ b/docs/reference/commands/fixelconnectivity.rst @@ -39,6 +39,15 @@ Options that influence generation of the connectivity matrix / matrices - **-mask file** provide a fixel data file containing a mask of those fixels to be computed; fixels outside the mask will be empty in the output matrix +- **-tck_weights_in path** specify a text scalar file containing the streamline weights + +Options for additional outputs to be generated +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- **-count path** export a fixel data file encoding the number of connections for each fixel + +- **-extent path** export a fixel data file encoding the extent of connectivity (sum of weights) for each fixel + Standard options ^^^^^^^^^^^^^^^^ diff --git a/src/dwi/tractography/weights.h b/src/dwi/tractography/weights.h index 8fcaa243d2..e06e58fe10 100644 --- a/src/dwi/tractography/weights.h +++ b/src/dwi/tractography/weights.h @@ -23,7 +23,6 @@ namespace MR { namespace DWI { - namespace Tractography { diff --git a/src/fixel/filter/smooth.h b/src/fixel/filter/smooth.h index a897505054..7bb5a6c999 100644 --- a/src/fixel/filter/smooth.h +++ b/src/fixel/filter/smooth.h @@ -18,6 +18,7 @@ #ifndef __fixel_filter_smooth_h__ #define __fixel_filter_smooth_h__ +#include "fixel/fixel.h" #include "fixel/matrix.h" #include "fixel/filter/base.h" @@ -51,7 +52,7 @@ namespace MR */ class Smooth : public Base - { + { public: Smooth (Image index_image, diff --git a/src/fixel/matrix.cpp b/src/fixel/matrix.cpp index 1cbc8ed797..d9dd2624d3 100644 --- a/src/fixel/matrix.cpp +++ b/src/fixel/matrix.cpp @@ -38,19 +38,12 @@ namespace MR - void InitFixel::add (const vector& indices) + template + void InitFixelBase::add (const MappedTrack& mapped_track) { - if ((*this).empty()) { - (*this).reserve (indices.size()); - for (auto i : indices) - (*this).emplace_back (InitElement (i)); - track_count = 1; - return; - } - ssize_t self_index = 0, in_index = 0; - // For anything in indices that doesn't yet appear in *this, + // For anything in mapped_track that doesn't yet appear in *this, // add to this list; once completed, extend *this by the appropriate // amount, and insert these into the appropriate locations // Need to continue making use of the existing allocated memory @@ -61,15 +54,15 @@ namespace MR // - On second pass, from back to front, move elements from previous back of vector to new back, // inserting new elements at appropriate locations to retain sortedness of list const ssize_t old_size = (*this).size(); - const ssize_t in_count = indices.size(); + const ssize_t in_count = mapped_track.size(); size_t intersection = 0; while (self_index < old_size && in_index < in_count) { - if ((*this)[self_index].index() == indices[in_index]) { - ++(*this)[self_index]; + if ((*this)[self_index].index() == mapped_track[in_index]) { + increment ((*this)[self_index], mapped_track); ++self_index; ++in_index; ++intersection; - } else if ((*this)[self_index].index() > indices[in_index]) { + } else if ((*this)[self_index].index() > mapped_track[in_index]) { ++in_index; } else { ++self_index; @@ -77,59 +70,55 @@ namespace MR } self_index = old_size - 1; - in_index = indices.size() - 1; + in_index = mapped_track.size() - 1; // It's possible that a resize() call may always result in requesting // a re-assignment of memory that exactly matches the size, which may in turn // lead to memory bloat due to inability to return the old memory // If this occurs, iteratively calling push_back() may instead engage the // memory-reservation-doubling behaviour - while ((*this).size() < old_size + indices.size() - intersection) - (*this).push_back (InitElement()); + while ((*this).size() < old_size + mapped_track.size() - intersection) + (*this).push_back (ElementType()); ssize_t out_index = (*this).size() - 1; // For each output vector location, need to determine whether it should come from copying an existing entry, // or creating a new one while (out_index > self_index && self_index >= 0 && in_index >= 0) { - if ((*this)[self_index].index() == indices[in_index]) { + if ((*this)[self_index].index() == mapped_track[in_index]) { (*this)[out_index] = (*this)[self_index]; --self_index; --in_index; - } else if ((*this)[self_index].index() > indices[in_index]) { + } else if ((*this)[self_index].index() > mapped_track[in_index]) { (*this)[out_index] = (*this)[self_index]; --self_index; } else { - (*this)[out_index] = InitElement (indices[in_index]); + (*this)[out_index] = ElementType (mapped_track[in_index], mapped_track); --in_index; } --out_index; } if (self_index < 0) { while (in_index >= 0 && out_index >= 0) - (*this)[out_index--] = InitElement (indices[in_index--]); + (*this)[out_index--] = ElementType (mapped_track[in_index--], mapped_track); } - // Track total number of streamlines intersecting this fixel, + // Track total number of streamlines / sum of streamline weights intersecting this fixel, // independently of the extent of fixel-fixel connectivity - ++track_count; + increment (mapped_track); } + template class InitFixelBase; + template class InitFixelBase; - - - init_matrix_type generate ( - const std::string& track_filename, - Image& index_image, - Image& fixel_mask, - const float angular_threshold) + namespace { - class TrackProcessor { + class TrackProcessor { public: TrackProcessor (const DWI::Tractography::Mapping::TrackMapperBase& mapper, @@ -144,7 +133,7 @@ namespace MR angular_threshold_dp (std::cos (angular_threshold * (Math::pi/180.0))) { } bool operator() (const DWI::Tractography::Streamline<>& tck, - vector& out) const + MappedTrack& out) const { using direction_type = Eigen::Vector3d; using SetVoxelDir = DWI::Tractography::Mapping::SetVoxelDir; @@ -152,9 +141,10 @@ namespace MR SetVoxelDir in; mapper (tck, in); - // For each voxel tract tangent, assign to a fixel + // For each voxel intersection, assign to a fixel out.clear(); out.reserve (in.size()); + out.set_weight (tck.weight); for (const auto& i : in) { assign_pos_of (i).to (fixel_indexer); fixel_indexer.index(3) = 0; @@ -197,52 +187,116 @@ namespace MR }; - auto directions_image = Fixel::find_directions_header (Path::dirname (index_image.name())).template get_image().with_direct_io ({+2,+1}); - DWI::Tractography::Properties properties; - DWI::Tractography::Reader track_file (track_filename, properties); - const uint32_t num_tracks = properties["count"].empty() ? 0 : to(properties["count"]); - DWI::Tractography::Mapping::TrackLoader loader (track_file, num_tracks, "computing fixel-fixel connectivity matrix"); - DWI::Tractography::Mapping::TrackMapperBase mapper (index_image); - mapper.set_upsample_ratio (DWI::Tractography::Mapping::determine_upsample_ratio (index_image, properties, 0.333f)); - mapper.set_use_precise_mapping (true); + + template + class Receiver + { + public: + Receiver (MatrixType& data) : + data (data) { } + bool operator() (const MappedTrack& in) const + { + try { + for (auto f : in) + data[f].add (in); + return true; + } catch (...) { + throw Exception ("Error assigning memory for CFE connectivity matrix"); + return false; + } + } + private: + MatrixType& data; + }; + } + + + +#define FIXEL_MATRIX_GENERATE_SHARED \ + auto directions_image = Fixel::find_directions_header (Path::dirname (index_image.name())).template get_image().with_direct_io ({+2,+1}); \ + DWI::Tractography::Properties properties; \ + DWI::Tractography::Reader track_file (track_filename, properties); \ + const uint32_t num_tracks = properties["count"].empty() ? 0 : to(properties["count"]); \ + DWI::Tractography::Mapping::TrackLoader loader (track_file, num_tracks, "computing fixel-fixel connectivity matrix"); \ + DWI::Tractography::Mapping::TrackMapperBase mapper (index_image); \ + mapper.set_upsample_ratio (DWI::Tractography::Mapping::determine_upsample_ratio (index_image, properties, 0.333f)); \ + mapper.set_use_precise_mapping (true); \ TrackProcessor track_processor (mapper, index_image, directions_image, fixel_mask, angular_threshold); - init_matrix_type connectivity_matrix (Fixel::get_number_of_fixels (index_image)); + + + + InitMatrixUnweighted generate_unweighted ( + const std::string& track_filename, + Image& index_image, + Image& fixel_mask, + const float angular_threshold) + { + FIXEL_MATRIX_GENERATE_SHARED + InitMatrixUnweighted connectivity_matrix (Fixel::get_number_of_fixels (index_image)); + Receiver receiver (connectivity_matrix); Thread::run_queue (loader, Thread::batch (DWI::Tractography::Streamline()), track_processor, - Thread::batch (vector()), - // Inline lambda function for receiving streamline fixel visitations and - // updating the connectivity matrix - [&] (const vector& fixels) - { - try { - for (auto f : fixels) - connectivity_matrix[f].add (fixels); - return true; - } catch (...) { - throw Exception ("Error assigning memory for CFE connectivity matrix"); - return false; - } - }); + Thread::batch (MappedTrack()), + receiver); return connectivity_matrix; } + InitMatrixWeighted generate_weighted ( + const std::string& track_filename, + Image& index_image, + Image& fixel_mask, + const float angular_threshold) + { + FIXEL_MATRIX_GENERATE_SHARED + InitMatrixWeighted connectivity_matrix (Fixel::get_number_of_fixels (index_image)); + Receiver receiver (connectivity_matrix); + Thread::run_queue (loader, + Thread::batch (DWI::Tractography::Streamline()), + track_processor, + Thread::batch (MappedTrack()), + receiver); + return connectivity_matrix; + } + + + + + + template + void Writer::set_count_path (const std::string& path) + { + assert (!count_image.valid()); + count_image = Image::create (path, MR::Fixel::data_header_from_nfixels (matrix.size())); + } - void normalise_and_write (init_matrix_type& matrix, - const connectivity_value_type threshold, - const std::string& path, - const KeyValues& keyvals) + template + void Writer::set_extent_path (const std::string& path) { + assert (!extent_image.valid()); + extent_image = Image::create (path, MR::Fixel::data_header_from_nfixels (matrix.size())); + } + + + template + void Writer::save (const std::string& path) const + { if (Path::exists (path)) { - if (!Path::is_dir (path)) { + if (Path::is_dir (path)) { + if (!App::overwrite_files && (Path::is_file (Path::join (path, "index.mif")) || + Path::is_file (Path::join (path, "fixels.mif")) || + Path::is_file (Path::join (path, "values.mif")))) + throw Exception ("Cannot create fixel-fixel connectivity matrix \"" + path + "\": " + "one or more files already exists (use -force to override)"); + } else { if (App::overwrite_files) { File::remove (path); } else { - throw Exception ("Cannot create fixel-fixel connectivity matrix \"" + path + "\": Already exists as file"); + throw Exception ("Cannot create fixel-fixel connectivity matrix directory \"" + path + "\": Already exists as file"); } } } else { @@ -264,13 +318,14 @@ namespace MR index_header.keyval() = keyvals; index_header.keyval()["nfixels"] = str(matrix.size()); index_header.datatype() = DataType::from(); - + index_header.datatype().set_byte_order_native(); + index_type num_connections = 0; { - ProgressBar progress ("Computing number of fixels in output", matrix.size()); - + ProgressBar progress ("Computing number of supra-threshold fixel-fixel connections", matrix.size()); + for (size_t fixel_index = 0; fixel_index != matrix.size(); ++fixel_index) { - const connectivity_value_type normalisation_factor = connectivity_value_type(1) / connectivity_value_type (matrix[fixel_index].count()); + const connectivity_value_type normalisation_factor = matrix[fixel_index].norm_factor(); for (auto& it : matrix[fixel_index]) { const connectivity_value_type connectivity = normalisation_factor * it.value(); if (connectivity >= threshold) @@ -294,6 +349,7 @@ namespace MR fixel_header.keyval()["nfixels"] = str(matrix.size()); fixel_header.datatype() = DataType::from(); fixel_header.datatype().set_byte_order_native(); + Header value_header (fixel_header); value_header.datatype() = DataType::from(); value_header.datatype().set_byte_order_native(); @@ -315,11 +371,13 @@ namespace MR const ssize_t connection_offset = fixel_image.index(0); index_type connection_count = 0; - const connectivity_value_type normalisation_factor = connectivity_value_type(1) / connectivity_value_type (matrix[fixel_index].count()); + connectivity_value_type sum_connectivity = connectivity_value_type (0.0); + const connectivity_value_type normalisation_factor = matrix[fixel_index].norm_factor(); for (auto& it : matrix[fixel_index]) { const connectivity_value_type connectivity = normalisation_factor * it.value(); if (connectivity >= threshold) { ++connection_count; + sum_connectivity += connectivity; fixel_image.value() = it.index(); ++fixel_image.index(0); value_image.value() = connectivity; @@ -331,14 +389,26 @@ namespace MR index_image.index (3) = 0; index_image.value() = uint64_t(connection_count); index_image.index (3) = 1; index_image.value() = connection_count ? connection_offset : uint64_t(0); + if (count_image.valid()) { + count_image.index(0) = fixel_index; + count_image.value() = connection_count; + } + if (extent_image.valid()) { + extent_image.index(0) = fixel_index; + extent_image.value() = sum_connectivity; + } + // Force deallocation of memory used for this fixel in the generated matrix - InitFixel().swap (matrix[fixel_index]); + typename MatrixType::value_type().swap (matrix[fixel_index]); ++progress; } } + template class Writer; + template class Writer; + diff --git a/src/fixel/matrix.h b/src/fixel/matrix.h index 30fe23e95c..3f8e1524fa 100644 --- a/src/fixel/matrix.h +++ b/src/fixel/matrix.h @@ -21,86 +21,178 @@ #include "image.h" #include "types.h" #include "file/ofstream.h" -#include "fixel/index_remapper.h" +#include "fixel/fixel.h" namespace MR { namespace Fixel { - - namespace Matrix { + using index_image_type = uint64_t; - using fixel_index_type = uint32_t; + using fixel_index_type = MR::Fixel::index_type; using count_type = uint32_t; using connectivity_value_type = float; - // Classes for dealing with dynamic multi-threaded construction of the - // fixel-fixel connectivity matrix - class InitElement - { + class MappedTrack : public vector + { + public: + using BaseType = vector; + default_type get_weight() const { return weight; } + void set_weight (const default_type w) { weight = w; } + private: + default_type weight; + }; + + + + class InitElementBase + { + public: + InitElementBase() : + fixel_index (std::numeric_limits::max()) { } + InitElementBase (const fixel_index_type fixel_index) : + fixel_index (fixel_index) { } + InitElementBase (const InitElementBase&) = default; + FORCE_INLINE InitElementBase& operator= (const InitElementBase& that) { fixel_index = that.fixel_index; return *this; } + FORCE_INLINE fixel_index_type index() const { return fixel_index; } + FORCE_INLINE bool operator< (const InitElementBase& that) const { return fixel_index < that.fixel_index; } + private: + fixel_index_type fixel_index; + }; + + + + class InitElementUnweighted : private InitElementBase + { public: - using ValueType = fixel_index_type; - InitElement() : - fixel_index (std::numeric_limits::max()), + using BaseType = InitElementBase; + using BaseType::operator<; + using ValueType = count_type; + InitElementUnweighted() : track_count (0) { } - InitElement (const fixel_index_type fixel_index) : - fixel_index (fixel_index), + InitElementUnweighted (const fixel_index_type fixel_index) : + BaseType (fixel_index), track_count (1) { } - InitElement (const fixel_index_type fixel_index, const ValueType track_count) : - fixel_index (fixel_index), - track_count (track_count) { } - InitElement (const InitElement&) = default; - FORCE_INLINE InitElement& operator++() { track_count++; return *this; } - FORCE_INLINE InitElement& operator= (const InitElement& that) { fixel_index = that.fixel_index; track_count = that.track_count; return *this; } - FORCE_INLINE fixel_index_type index() const { return fixel_index; } + InitElementUnweighted (const fixel_index_type fixel_index, const MappedTrack& all_data) : + BaseType (fixel_index), + track_count (1) { } + InitElementUnweighted (const InitElementUnweighted&) = default; + FORCE_INLINE fixel_index_type index() const { return BaseType::index(); } + FORCE_INLINE InitElementUnweighted& operator++() { track_count++; return *this; } + FORCE_INLINE InitElementUnweighted& operator= (const InitElementUnweighted& that) { BaseType::operator= (that); track_count = that.track_count; return *this; } FORCE_INLINE ValueType value() const { return track_count; } - FORCE_INLINE bool operator< (const InitElement& that) const { return fixel_index < that.fixel_index; } private: - fixel_index_type fixel_index; ValueType track_count; }; - class InitFixel : public vector - { + class InitElementWeighted : private InitElementBase + { + public: + using BaseType = InitElementBase; + using BaseType::operator<; + using ValueType = connectivity_value_type; + InitElementWeighted() : + sum_weights (ValueType(0)) { } + InitElementWeighted (const fixel_index_type fixel_index) = delete; + InitElementWeighted (const fixel_index_type fixel_index, const MappedTrack& all_data) : + BaseType (fixel_index), + sum_weights (all_data.get_weight()) { } + InitElementWeighted (const InitElementWeighted&) = default; + FORCE_INLINE fixel_index_type index() const { return BaseType::index(); } + FORCE_INLINE InitElementWeighted& operator+= (const ValueType increment) { sum_weights += increment; return *this; } + FORCE_INLINE InitElementWeighted& operator= (const InitElementWeighted& that) { BaseType::operator= (that); sum_weights = that.sum_weights; return *this; } + FORCE_INLINE ValueType value() const { return sum_weights; } + private: + ValueType sum_weights; + }; + + + + template + class InitFixelBase : public vector + { + public: + using BaseType = vector; + virtual ~InitFixelBase() { } + void add (const MappedTrack& mapped_track); + virtual default_type norm_factor() const = 0; + protected: + virtual void increment (const MappedTrack& data) = 0; + virtual void increment (ElementType& element, const MappedTrack& data) = 0; + }; + + class InitFixelUnweighted : public InitFixelBase + { public: - using ElementType = InitElement; - using BaseType = vector; - InitFixel() : + using BaseType = InitFixelBase; + InitFixelUnweighted() : track_count (0) { } - void add (const vector& indices); - count_type count() const { return track_count; } + default_type norm_factor() const override { return 1.0 / default_type(track_count); } private: count_type track_count; + void increment (const MappedTrack& data) override { ++track_count; } + void increment (InitElementUnweighted& element, const MappedTrack& data) override { ++element; } }; + class InitFixelWeighted : public InitFixelBase + { + public: + using BaseType = InitFixelBase; + InitFixelWeighted() : + sum_weights (default_type (0)) { } + default_type norm_factor() const override { return 1.0 / sum_weights; } + private: + default_type sum_weights; + void increment (const MappedTrack& data) override { sum_weights += data.get_weight(); } + void increment (InitElementWeighted& element, const MappedTrack& data) override { element += data.get_weight(); } + }; + + + + using InitMatrixUnweighted = vector; + using InitMatrixWeighted = vector; + + + + + + + + + + + + + + // A class to store fixel index / connectivity value pairs // only after the connectivity matrix has been thresholded / normalised class NormElement - { + { public: using ValueType = connectivity_value_type; - NormElement (const index_type fixel_index, + NormElement (const fixel_index_type fixel_index, const ValueType connectivity_value) : fixel_index (fixel_index), connectivity_value (connectivity_value) { } - FORCE_INLINE index_type index() const { return fixel_index; } + FORCE_INLINE fixel_index_type index() const { return fixel_index; } FORCE_INLINE ValueType value() const { return connectivity_value; } FORCE_INLINE void exponentiate (const ValueType C) { connectivity_value = std::pow (connectivity_value, C); } FORCE_INLINE void normalise (const ValueType norm_factor) { connectivity_value *= norm_factor; } private: - index_type fixel_index; - connectivity_value_type connectivity_value; + fixel_index_type fixel_index; + ValueType connectivity_value; }; @@ -109,28 +201,29 @@ namespace MR // With the internally normalised CFE expression, want to store a // multiplicative factor per fixel class NormFixel : public vector - { + { public: using ElementType = NormElement; using BaseType = vector; + using ValueType = connectivity_value_type; NormFixel() : - norm_multiplier (connectivity_value_type (1)) { } + norm_multiplier (ValueType (1)) { } NormFixel (const BaseType& i) : BaseType (i), - norm_multiplier (connectivity_value_type (1)) { } + norm_multiplier (ValueType (1)) { } NormFixel (BaseType&& i) : BaseType (std::move (i)), - norm_multiplier (connectivity_value_type (1)) { } + norm_multiplier (ValueType (1)) { } void normalise() { - norm_multiplier = connectivity_value_type (0); + norm_multiplier = ValueType (0); for (const auto& c : *this) norm_multiplier += c.value(); - norm_multiplier = norm_multiplier ? (connectivity_value_type (1) / norm_multiplier) : connectivity_value_type(0); + norm_multiplier = norm_multiplier ? (ValueType (1) / norm_multiplier) : ValueType(0); } - void normalise (const connectivity_value_type sum) { - norm_multiplier = sum ? (connectivity_value_type(1) / sum) : connectivity_value_type(0); + void normalise (const ValueType sum) { + norm_multiplier = sum ? (ValueType(1) / sum) : ValueType(0); } - connectivity_value_type norm_multiplier; + ValueType norm_multiplier; }; @@ -138,10 +231,7 @@ namespace MR - // Different types are used depending on whether the connectivity matrix - // is in the process of being built, or whether it has been normalised - // TODO Revise - using init_matrix_type = vector; + @@ -149,7 +239,13 @@ namespace MR // Generate a fixel-fixel connectivity matrix - init_matrix_type generate ( + InitMatrixUnweighted generate_unweighted ( + const std::string& track_filename, + Image& index_image, + Image& fixel_mask, + const float angular_threshold); + + InitMatrixWeighted generate_weighted ( const std::string& track_filename, Image& index_image, Image& fixel_mask, @@ -157,41 +253,36 @@ namespace MR + template + class Writer + { + public: + Writer (MatrixType& matrix, + const connectivity_value_type threshold) : + matrix (matrix), + threshold (threshold) { } + void set_keyvals (KeyValues& kv) { keyvals = kv; } + void set_count_path (const std::string& path); + void set_extent_path (const std::string& path); + void save (const std::string& path) const; + private: + MatrixType& matrix; + const connectivity_value_type threshold; + KeyValues keyvals; + mutable Image count_image; + mutable Image extent_image; + }; + - // New code for handling load/save of fixel-fixel connectivity matrix - // Use something akin to the fixel directory format, with a sub-directory - // within an existing fixel directory containing the following data: - // - index.mif: Similar to the index image in the fixel directory format, - // but has dimensions Nx1x1x2, where: - // - N is the number of fixels in the fixel template - // - The first volume contains the number of connected fixels for that fixel - // - The second volume contains the offset of the first connected fixel for that fixel - // - connectivity.mif: Floating-point image of dimension Cx1x1, where C is the total - // number of fixel-fixel connections stored in the entire matrix. Each value should be - // between 0.0 and 1.0, corresponding to the fraction of streamlines passing through - // the fixel that additionally pass through some other fixel. - // - fixel.mif: Unsigned integer image of dimension Cx1x1, where C is the total number of - // fixel-fixel connections stored in the entire matrix. Each value indexes the - // fixel to which the fixel-fixel connection refers. - // - // In order to avoid duplication of memory usage, the writing function should - // perform, for each fixel in turn: - // - Normalisation of the matrix - // - Writing to the three images - // - Erasing the memory used for that matrix in the initial building - void normalise_and_write (init_matrix_type& matrix, - const connectivity_value_type threshold, - const std::string& path, - const KeyValues& keyvals = KeyValues()); // Wrapper class for reading the connectivity matrix from the filesystem class Reader - { + { public: Reader (const std::string& path, const Image& mask); diff --git a/testing/binaries/data b/testing/binaries/data index 747223c963..309728c7c1 160000 --- a/testing/binaries/data +++ b/testing/binaries/data @@ -1 +1 @@ -Subproject commit 747223c9635094939d55dd6c639a6e07c1c84427 +Subproject commit 309728c7c12ac0e81eb9e572eda290b45f952f4c diff --git a/testing/binaries/tests/fixelconnectivity b/testing/binaries/tests/fixelconnectivity index 9a45f4d719..6ba11c37fc 100644 --- a/testing/binaries/tests/fixelconnectivity +++ b/testing/binaries/tests/fixelconnectivity @@ -1,3 +1,4 @@ -fixelconnectivity SIFT_phantom/fixels/ SIFT_phantom/tracks.tck tmp/ -force && testing_diff_image tmp/index.mif SIFT_phantom/matrix/index.mif && testing_diff_image tmp/fixels.mif SIFT_phantom/matrix/fixels.mif && testing_diff_image tmp/values.mif SIFT_phantom/matrix/values.mif -fixelconnectivity SIFT_phantom/fixels/ SIFT_phantom/tracks.tck tmp/ -mask SIFT_phantom/fixels/upper.mif -force && testing_diff_image tmp/index.mif fixelconnectivity/masked/index.mif && testing_diff_image tmp/fixels.mif fixelconnectivity/masked/fixels.mif && testing_diff_image tmp/values.mif fixelconnectivity/masked/values.mif +fixelconnectivity SIFT_phantom/fixels/ SIFT_phantom/tracks.tck tmp/ -count tmp-count.mif -extent tmp-extent.mif -force && testing_diff_image tmp/index.mif fixelconnectivity/default/index.mif && testing_diff_image tmp/fixels.mif fixelconnectivity/default/fixels.mif && testing_diff_image tmp/values.mif fixelconnectivity/default/values.mif && testing_diff_image tmp-count.mif fixelconnectivity/default_count.mif && testing_diff_image tmp-extent.mif fixelconnectivity/default_extent.mif +fixelconnectivity SIFT_phantom/fixels/ SIFT_phantom/tracks.tck tmp/ -count tmp-count.mif -extent tmp-extent.mif -mask SIFT_phantom/fixels/upper.mif -force && testing_diff_image tmp/index.mif fixelconnectivity/masked/index.mif && testing_diff_image tmp/fixels.mif fixelconnectivity/masked/fixels.mif && testing_diff_image tmp/values.mif fixelconnectivity/masked/values.mif && testing_diff_image tmp-count.mif fixelconnectivity/masked_count.mif && testing_diff_image tmp-extent.mif fixelconnectivity/masked_extent.mif +fixelconnectivity SIFT_phantom/fixels/ SIFT_phantom/tracks.tck tmp/ -count tmp-count.mif -extent tmp-extent.mif -tck_weights_in SIFT_phantom/weights.csv -force && testing_diff_image tmp/index.mif fixelconnectivity/weighted/index.mif && testing_diff_image tmp/fixels.mif fixelconnectivity/weighted/fixels.mif && testing_diff_image tmp/values.mif fixelconnectivity/weighted/values.mif && testing_diff_image tmp-count.mif fixelconnectivity/weighted_count.mif && testing_diff_image tmp-extent.mif fixelconnectivity/weighted_extent.mif