Worker::Thresholds::Thresholds (Tractography::Properties& properties) : max_length (std::numeric_limits<float>::infinity()), min_length (0.0f), max_weight (std::numeric_limits<float>::infinity()), min_weight (0.0f), step_size (get_step_size (properties)) { if (properties.find ("max_dist") != properties.end()) { try { max_length = to<float>(properties["max_dist"]); } catch (...) { } } if (properties.find ("min_dist") != properties.end()) { try { min_length = to<float>(properties["min_dist"]); } catch (...) { } } if (std::isfinite (step_size)) { // User may set these values to a precise value, which may then fail due to floating-point // calculation of streamline length // Therefore throw a bit of error margin in here float error_margin = 0.1; if (properties.find ("downsample_factor") != properties.end()) error_margin = 0.5 / to<float>(properties["downsample_factor"]); max_length += error_margin * step_size; min_length -= error_margin * step_size; } if (properties.find ("max_weight") != properties.end()) max_weight = to<float>(properties["max_weight"]); if (properties.find ("min_weight") != properties.end()) min_weight = to<float>(properties["min_weight"]); }
size_t determine_upsample_ratio (const Image::Info& info, const Tractography::Properties& properties, const float ratio) { if (info.ndim() < 3) throw Exception ("Cannot perform streamline mapping on image with less than three dimensions"); Properties::const_iterator i = properties.find ("output_step_size"); if (i == properties.end()) { i = properties.find ("step_size"); if (i == properties.end()) throw Exception ("Cannot perform streamline mapping: no step size information in track file header"); } const float step_size = to<float> (i->second); return determine_upsample_ratio (info, step_size, ratio); }
Worker::Thresholds::Thresholds (Tractography::Properties& properties) : max_num_points (std::numeric_limits<size_t>::max()), min_num_points (0), max_weight (std::numeric_limits<float>::infinity()), min_weight (0.0) { std::string step_size_string; if (properties.find ("output_step_size") == properties.end()) step_size_string = ((properties.find ("step_size") == properties.end()) ? "0.0" : properties["step_size"]); else step_size_string = properties["output_step_size"]; float maxlength = 0.0, minlength = 0.0; if (properties.find ("max_dist") != properties.end()) { try { maxlength = to<float>(properties["max_dist"]); } catch (...) { } } if (properties.find ("min_dist") != properties.end()) { try { minlength = to<float>(properties["min_dist"]); } catch (...) { } } if (step_size_string == "variable" && (maxlength || minlength)) throw Exception ("Cannot apply length threshold; step size is inconsistent between input track files"); const float step_size = to<float>(step_size_string); if ((!step_size || !std::isfinite (step_size)) && (maxlength || minlength)) throw Exception ("Cannot apply length threshold; step size information is incomplete"); if (maxlength) max_num_points = Math::round (maxlength / step_size) + 1; if (minlength) min_num_points = std::max (2, round (minlength/step_size) + 1); if (properties.find ("max_weight") != properties.end()) max_weight = to<float>(properties["max_weight"]); if (properties.find ("min_weight") != properties.end()) min_weight = to<float>(properties["min_weight"]); }
void run () { Tractography::Properties properties; Tractography::Reader<float> file (argument[0], properties); const size_t num_tracks = properties["count"].empty() ? 0 : to<size_t> (properties["count"]); float step_size = 0.0; if (properties.find ("output_step_size") != properties.end()) step_size = to<float> (properties["output_step_size"]); else step_size = to<float> (properties["step_size"]); std::vector<float> voxel_size; Options opt = get_options("vox"); if (opt.size()) voxel_size = opt[0][0]; if (voxel_size.size() == 1) voxel_size.assign (3, voxel_size.front()); else if (!voxel_size.empty() && voxel_size.size() != 3) throw Exception ("voxel size must either be a single isotropic value, or a list of 3 comma-separated voxel dimensions"); if (!voxel_size.empty()) INFO ("creating image with voxel dimensions [ " + str(voxel_size[0]) + " " + str(voxel_size[1]) + " " + str(voxel_size[2]) + " ]"); Image::Header header; opt = get_options ("template"); if (opt.size()) { Image::Header template_header (opt[0][0]); header = template_header; header.comments().clear(); if (!voxel_size.empty()) oversample_header (header, voxel_size); } else { if (voxel_size.empty()) throw Exception ("please specify either a template image or the desired voxel size"); generate_header (header, argument[0], voxel_size); } header.set_ndim (3); opt = get_options ("contrast"); contrast_t contrast = opt.size() ? contrast_t(int(opt[0][0])) : TDI; opt = get_options ("stat_vox"); vox_stat_t stat_vox = opt.size() ? vox_stat_t(int(opt[0][0])) : V_SUM; opt = get_options ("stat_tck"); tck_stat_t stat_tck = opt.size() ? tck_stat_t(int(opt[0][0])) : T_MEAN; float gaussian_fwhm_tck = 0.0, gaussian_denominator_tck = 0.0; opt = get_options ("fwhm_tck"); if (opt.size()) { if (stat_tck != GAUSSIAN) { INFO ("Overriding per-track statistic to Gaussian as a full-width half-maximum has been provided."); stat_tck = GAUSSIAN; } gaussian_fwhm_tck = opt[0][0]; const float gaussian_theta_tck = gaussian_fwhm_tck / (2.0 * sqrt (2.0 * log (2.0))); gaussian_denominator_tck = 2.0 * gaussian_theta_tck * gaussian_theta_tck; } else if (stat_tck == GAUSSIAN) { throw Exception ("If using Gaussian per-streamline statistic, need to provide a full-width half-maximum for the Gaussian kernel using the -fwhm option"); } opt = get_options ("colour"); const bool colour = opt.size(); opt = get_options ("map_zero"); const bool map_zero = opt.size(); if (colour) { header.set_ndim (4); header.dim(3) = 3; //header.set_description (3, "directionally-encoded colour"); Image::Stride::set (header, Image::Stride::contiguous_along_axis (3, header)); } // Deal with erroneous statistics & provide appropriate messages switch (contrast) { case TDI: if (stat_vox != V_SUM && stat_vox != V_MEAN) { INFO ("Cannot use voxel statistic other than 'sum' or 'mean' for TDI generation - ignoring"); stat_vox = V_SUM; } if (stat_tck != T_MEAN) INFO ("Cannot use track statistic other than default for TDI generation - ignoring"); stat_tck = T_MEAN; break; case PRECISE_TDI: if (stat_vox != V_SUM) { INFO ("Cannot use voxel statistic other than 'sum' for precise TDI generation - ignoring"); stat_vox = V_SUM; } if (stat_tck != T_MEAN) INFO ("Cannot use track statistic other than default for precise TDI generation - ignoring"); stat_tck = T_MEAN; break; case ENDPOINT: if (stat_vox != V_SUM && stat_vox != V_MEAN) { INFO ("Cannot use voxel statistic other than 'sum' or 'mean' for endpoint map generation - ignoring"); stat_vox = V_SUM; } if (stat_tck != T_MEAN) INFO ("Cannot use track statistic other than default for endpoint map generation - ignoring"); stat_tck = T_MEAN; break; case LENGTH: if (stat_tck != T_MEAN) INFO ("Cannot use track statistic other than default for length-weighted TDI generation - ignoring"); stat_tck = T_MEAN; break; case INVLENGTH: if (stat_tck != T_MEAN) INFO ("Cannot use track statistic other than default for inverse-length-weighted TDI generation - ignoring"); stat_tck = T_MEAN; break; case SCALAR_MAP: case SCALAR_MAP_COUNT: break; case FOD_AMP: if (stat_tck == ENDS_MIN || stat_tck == ENDS_MEAN || stat_tck == ENDS_MAX || stat_tck == ENDS_PROD) throw Exception ("Can't use endpoint-based track-wise statistics with FOD_AMP contrast"); break; case CURVATURE: break; default: throw Exception ("Undefined contrast mechanism"); } opt = get_options ("datatype"); bool manual_datatype = false; if (colour) { header.datatype() = DataType::Float32; manual_datatype = true; } if (opt.size()) { if (colour) { INFO ("Can't manually set datatype for directionally-encoded colour processing - overriding to Float32"); } else { header.datatype() = DataType::parse (opt[0][0]); manual_datatype = true; } } if (get_options ("tck_weights_in").size() || (manual_datatype && header.datatype().is_integer())) { if ((manual_datatype && header.datatype().is_integer())) INFO ("Can't use an integer type if streamline weights are provided; overriding to Float32"); header.datatype() = DataType::Float32; manual_datatype = true; } header.datatype().set_byte_order_native(); for (Tractography::Properties::iterator i = properties.begin(); i != properties.end(); ++i) header.comments().push_back (i->first + ": " + i->second); for (std::multimap<std::string,std::string>::const_iterator i = properties.roi.begin(); i != properties.roi.end(); ++i) header.comments().push_back ("ROI: " + i->first + " " + i->second); for (std::vector<std::string>::iterator i = properties.comments.begin(); i != properties.comments.end(); ++i) header.comments().push_back ("comment: " + *i); size_t upsample_ratio = 1; opt = get_options ("upsample"); if (opt.size()) { upsample_ratio = opt[0][0]; INFO ("track upsampling ratio manually set to " + str(upsample_ratio)); } else if (step_size && std::isfinite (step_size)) { // If accurately calculating the length through each voxel traversed, need a higher upsampling ratio // (1/10th of the voxel size was found to give a good quantification of chordal length) // For all other applications, making the upsampled step size about 1/3rd of a voxel seems sufficient const float voxel_step_ratio = (contrast == PRECISE_TDI) ? 0.1 : 0.333; upsample_ratio = Math::ceil<size_t> (step_size / (minvalue (header.vox(0), header.vox(1), header.vox(2)) * voxel_step_ratio)); INFO ("track upsampling ratio automatically set to " + str(upsample_ratio)); } else { if (contrast != PRECISE_TDI) WARN ("track upsampling off; track step size information in header is absent or malformed"); } const bool dump = get_options ("dump").size(); if (dump && Path::has_suffix (argument[1], "mih")) throw Exception ("Option -dump only works when outputting to .mih image format"); std::string msg = str("Generating ") + (colour ? "colour " : "") + "image with "; switch (contrast) { case TDI: msg += "density"; break; case PRECISE_TDI: msg += "density (precise)"; break; case ENDPOINT: msg += "endpoint density"; break; case LENGTH: msg += "length"; break; case INVLENGTH: msg += "inverse length"; break; case SCALAR_MAP: msg += "scalar map"; break; case SCALAR_MAP_COUNT: msg += "scalar-map-thresholded tdi"; break; case FOD_AMP: msg += "FOD amplitude"; break; case CURVATURE: msg += "curvature"; break; default: msg += "ERROR"; break; } msg += " contrast"; if (contrast == SCALAR_MAP || contrast == SCALAR_MAP_COUNT || contrast == FOD_AMP || contrast == CURVATURE) msg += ", "; else msg += " and "; switch (stat_vox) { case V_SUM: msg += "summed"; break; case V_MIN: msg += "minimum"; break; case V_MEAN: msg += "mean"; break; case V_MAX: msg += "maximum"; break; default: msg += "ERROR"; break; } msg += " per-voxel statistic"; if (contrast == SCALAR_MAP || contrast == SCALAR_MAP_COUNT || contrast == FOD_AMP || contrast == CURVATURE) { msg += " and "; switch (stat_tck) { case T_SUM: msg += "summed"; break; case T_MIN: msg += "minimum"; break; case T_MEAN: msg += "mean"; break; case T_MAX: msg += "maximum"; break; case T_MEDIAN: msg += "median"; break; case T_MEAN_NONZERO: msg += "mean (nonzero)"; break; case GAUSSIAN: msg += "gaussian (FWHM " + str (gaussian_fwhm_tck) + "mm)"; break; case ENDS_MIN: msg += "endpoints (minimum)"; break; case ENDS_MEAN: msg += "endpoints (mean)"; break; case ENDS_MAX: msg += "endpoints (maximum)"; break; case ENDS_PROD: msg += "endpoints (product)"; break; default: msg += "ERROR"; break; } msg += " per-track statistic"; } INFO (msg); switch (contrast) { case TDI: header.comments().push_back ("track density image"); break; case PRECISE_TDI: header.comments().push_back ("track density image (precise calculation)"); break; case ENDPOINT: header.comments().push_back ("track endpoint density image"); break; case LENGTH: header.comments().push_back ("track density image (weighted by track length)"); break; case INVLENGTH: header.comments().push_back ("track density image (weighted by inverse track length)"); break; case SCALAR_MAP: header.comments().push_back ("track-weighted image (using scalar image)"); break; case SCALAR_MAP_COUNT: header.comments().push_back ("track density image (using scalar image thresholding)"); break; case FOD_AMP: header.comments().push_back ("track-weighted image (using FOD amplitude)"); break; case CURVATURE: header.comments().push_back ("track-weighted image (using track curvature)"); break; } TrackLoader loader (file, num_tracks); // Use a branching IF instead of a switch statement to permit scope if (contrast == TDI || contrast == ENDPOINT || contrast == LENGTH || contrast == INVLENGTH || (contrast == CURVATURE && stat_tck != GAUSSIAN)) { if (!manual_datatype) { header.datatype() = (contrast == TDI || contrast == ENDPOINT) ? DataType::UInt32 : DataType::Float32; header.datatype().set_byte_order_native(); } if (colour) { TrackMapperTWI <SetVoxelDEC> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck); MapWriterColour<SetVoxelDEC> writer (header, argument[1], dump, stat_vox); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelDEC(), writer); } else { TrackMapperTWI <SetVoxel> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck); Ptr< MapWriterBase<SetVoxel> > writer (make_writer<SetVoxel> (header, argument[1], dump, stat_vox)); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxel(), *writer); } } else if (contrast == PRECISE_TDI) { if (!manual_datatype) { header.datatype() = DataType::Float32; header.datatype().set_byte_order_native(); } if (colour) { TrackMapperTWI <SetVoxelDir> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck); MapWriterColour<SetVoxelDir> writer (header, argument[1], dump, stat_vox); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelDir(), writer); } else { TrackMapperTWI < SetVoxelDir> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck); MapWriter <float, SetVoxelDir> writer (header, argument[1], dump, stat_vox); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelDir(), writer); } } else if (contrast == CURVATURE && stat_tck == GAUSSIAN) { if (!manual_datatype) { header.datatype() = DataType::Float32; header.datatype().set_byte_order_native(); } if (colour) { TrackMapperTWI <SetVoxelDECFactor> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck, gaussian_denominator_tck); MapWriterColour<SetVoxelDECFactor> writer (header, argument[1], dump, stat_vox); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelDECFactor(), writer); } else { TrackMapperTWI <SetVoxelFactor> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck, gaussian_denominator_tck); Ptr< MapWriterBase<SetVoxelFactor> > writer (make_writer<SetVoxelFactor> (header, argument[1], dump, stat_vox)); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelFactor(), *writer); } } else if (contrast == SCALAR_MAP || contrast == SCALAR_MAP_COUNT || contrast == FOD_AMP) { opt = get_options ("image"); if (!opt.size()) { if (contrast == SCALAR_MAP || contrast == SCALAR_MAP_COUNT) throw Exception ("If using 'scalar_map' or 'scalar_map_count' contrast, must provide the relevant scalar image using -image option"); else throw Exception ("If using 'fod_amp' contrast, must provide the relevant spherical harmonic image using -image option"); } Image::BufferPreload<float> input_image (opt[0][0]); if ((contrast == SCALAR_MAP || contrast == SCALAR_MAP_COUNT)) { if (!(input_image.ndim() == 3 || (input_image.ndim() == 4 && input_image.dim(3) == 1))) throw Exception ("Use of 'scalar_map' contrast option requires a 3-dimensional image; your image is " + str(input_image.ndim()) + "D"); } if (contrast == FOD_AMP && input_image.ndim() != 4) throw Exception ("Use of 'fod_amp' contrast option requires a 4-dimensional image; your image is " + str(input_image.ndim()) + "D"); if (!manual_datatype && (input_image.datatype() != DataType::Bit)) header.datatype() = input_image.datatype(); if (colour) { if (stat_tck == GAUSSIAN) { TrackMapperTWIImage <SetVoxelDECFactor> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck, gaussian_denominator_tck, input_image); MapWriterColour <SetVoxelDECFactor> writer (header, argument[1], dump, stat_vox); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelDECFactor(), writer); } else { TrackMapperTWIImage <SetVoxelDEC> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck, 0.0, input_image); MapWriterColour <SetVoxelDEC> writer (header, argument[1], dump, stat_vox); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelDEC(), writer); } } else { if (stat_tck == GAUSSIAN) { TrackMapperTWIImage <SetVoxelFactor> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck, gaussian_denominator_tck, input_image); Ptr< MapWriterBase <SetVoxelFactor> > writer (make_writer<SetVoxelFactor> (header, argument[1], dump, stat_vox)); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxelFactor(), *writer); } else { TrackMapperTWIImage <SetVoxel> mapper (header, upsample_ratio, map_zero, step_size, contrast, stat_tck, 0.0, input_image); Ptr< MapWriterBase <SetVoxel> > writer (make_writer<SetVoxel> (header, argument[1], dump, stat_vox)); Thread::run_queue (loader, Tractography::Streamline<float>(), Thread::multi (mapper), SetVoxel(), *writer); } } } else { throw Exception ("Undefined contrast mechanism for output image"); } }
void run () { const bool weights_provided = get_options ("tck_weights_in").size(); float step_size = NaN; size_t count = 0, header_count = 0; float min_length = std::numeric_limits<float>::infinity(); float max_length = 0.0f; double sum_lengths = 0.0, sum_weights = 0.0; std::vector<double> histogram; std::vector<LW> all_lengths; all_lengths.reserve (header_count); { Tractography::Properties properties; Tractography::Reader<float> reader (argument[0], properties); if (properties.find ("count") != properties.end()) header_count = to<size_t> (properties["count"]); if (properties.find ("output_step_size") != properties.end()) step_size = to<float> (properties["output_step_size"]); else step_size = to<float> (properties["step_size"]); if (!std::isfinite (step_size) || !step_size) { WARN ("Streamline step size undefined in header"); if (get_options ("histogram").size()) WARN ("Histogram will be henerated using a 1mm interval"); } std::unique_ptr<File::OFStream> dump; auto opt = get_options ("dump"); if (opt.size()) dump.reset (new File::OFStream (std::string(opt[0][0]), std::ios_base::out | std::ios_base::trunc)); ProgressBar progress ("Reading track file", header_count); Streamline<> tck; while (reader (tck)) { ++count; const float length = std::isfinite (step_size) ? tck.calc_length (step_size) : tck.calc_length(); if (std::isfinite (length)) { min_length = std::min (min_length, length); max_length = std::max (max_length, length); sum_lengths += tck.weight * length; sum_weights += tck.weight; all_lengths.push_back (LW (length, tck.weight)); const size_t index = std::isfinite (step_size) ? std::round (length / step_size) : std::round (length); while (histogram.size() <= index) histogram.push_back (0.0); histogram[index] += tck.weight; } if (dump) (*dump) << length << "\n"; ++progress; } } if (histogram.front()) WARN ("read " + str(histogram.front()) + " zero-length tracks"); if (count != header_count) WARN ("expected " + str(header_count) + " tracks according to header; read " + str(count)); const float mean_length = sum_lengths / sum_weights; float median_length = 0.0f; if (weights_provided) { // Perform a weighted median calculation std::sort (all_lengths.begin(), all_lengths.end()); size_t median_index = 0; double sum = sum_weights - all_lengths[0].get_weight(); while (sum > 0.5 * sum_weights) { sum -= all_lengths[++median_index].get_weight(); } median_length = all_lengths[median_index].get_length(); } else { median_length = Math::median (all_lengths).get_length(); } double stdev = 0.0; for (std::vector<LW>::const_iterator i = all_lengths.begin(); i != all_lengths.end(); ++i) stdev += i->get_weight() * Math::pow2 (i->get_length() - mean_length); stdev = std::sqrt (stdev / (((count - 1) / float(count)) * sum_weights)); const size_t width = 12; std::cout << " " << std::setw(width) << std::right << "mean" << " " << std::setw(width) << std::right << "median" << " " << std::setw(width) << std::right << "std. dev." << " " << std::setw(width) << std::right << "min" << " " << std::setw(width) << std::right << "max" << " " << std::setw(width) << std::right << "count\n"; std::cout << " " << std::setw(width) << std::right << (mean_length) << " " << std::setw(width) << std::right << (median_length) << " " << std::setw(width) << std::right << (stdev) << " " << std::setw(width) << std::right << (min_length) << " " << std::setw(width) << std::right << (max_length) << " " << std::setw(width) << std::right << (count) << "\n"; auto opt = get_options ("histogram"); if (opt.size()) { File::OFStream out (opt[0][0], std::ios_base::out | std::ios_base::trunc); if (!std::isfinite (step_size)) step_size = 1.0f; if (weights_provided) { out << "Length,Sum_weights\n"; for (size_t i = 0; i != histogram.size(); ++i) out << str(i * step_size) << "," << str(histogram[i]) << "\n"; } else { out << "Length,Count\n"; for (size_t i = 0; i != histogram.size(); ++i) out << str(i * step_size) << "," << str<size_t>(histogram[i]) << "\n"; } out << "\n"; } }
void run () { const std::string path = argument[0]; Tractography::Properties properties; Tractography::Reader<float> reader (path, properties); size_t init_count = 0; if (properties.find ("count") == properties.end()) { INFO ("\"count\" field not set in file " + path); } else { init_count = to<size_t>(properties["count"]); INFO ("Value of \"count\" in file " + path + " is " + str(init_count)); } // Read the actual number of streamlines in the file Tractography::Streamline<float> tck; size_t count = 0; { ProgressBar progress ("evaluating actual streamline data count..."); while (reader (tck)) { ++count; ++progress; } } reader.close(); INFO ("Actual number of streamlines read is " + str(count)); // Find the appropriate file offset in the header KeyValue kv (argument[0], "mrtrix tracks"); std::string data_file; int64_t count_offset = 0; int64_t current_offset = 0; while (kv.next()) { std::string key = lowercase (kv.key()); if (key == "count") count_offset = current_offset; current_offset = kv.tellg(); } kv.close(); if (!count_offset) throw Exception ("could not find location of \"count\" field in file \"" + path + "\""); DEBUG ("File offset for \"count\" field is " + str(count_offset)); // Overwrite this data // Used C-style file access here as ofstream would either wipe the file contents, or // append the data instead of inserting it FILE* fp; fp = fopen (path.c_str(), "r+"); if (!fp) throw Exception ("could not open tracks file \"" + path + "\" for repair"); if (fseek (fp, count_offset, SEEK_SET)) { fclose (fp); throw Exception ("unable to seek to the appropriate position in the file"); } const std::string string = "count: " + str(count) + "\ntotal_count: " + str(count) + "\nEND\n"; const int rvalue = fputs (string.c_str(), fp); fclose (fp); if (rvalue == EOF) { WARN ("\"count\" field may not have been properly updated"); } else { INFO ("\"count\" field updated successfully"); } }
void run () { const size_t num_inputs = argument.size() - 1; const std::string output_path = argument[num_inputs]; // Make sure configuration is sensible if (get_options("tck_weights_in").size() && num_inputs > 1) throw Exception ("Cannot use per-streamline weighting with multiple input files"); // Get the consensus streamline properties from among the multiple input files Tractography::Properties properties; size_t count = 0; vector<std::string> input_file_list; for (size_t file_index = 0; file_index != num_inputs; ++file_index) { input_file_list.push_back (argument[file_index]); Properties p; Reader<float> reader (argument[file_index], p); for (vector<std::string>::const_iterator i = p.comments.begin(); i != p.comments.end(); ++i) { bool present = false; for (vector<std::string>::const_iterator j = properties.comments.begin(); !present && j != properties.comments.end(); ++j) present = (*i == *j); if (!present) properties.comments.push_back (*i); } // ROI paths are ignored - otherwise tckedit will try to find the ROIs used // during streamlines generation! size_t this_count = 0, this_total_count = 0; for (Properties::const_iterator i = p.begin(); i != p.end(); ++i) { if (i->first == "count") { this_count = to<float>(i->second); } else if (i->first == "total_count") { this_total_count += to<float>(i->second); } else { Properties::iterator existing = properties.find (i->first); if (existing == properties.end()) properties.insert (*i); else if (i->second != existing->second) existing->second = "variable"; } } count += this_count; } DEBUG ("estimated number of input tracks: " + str(count)); load_rois (properties); // Some properties from tracking may be overwritten by this editing process // Due to the potential use of masking, we have no choice but to clear the // properties class of any fields that would otherwise propagate through // and be applied as part of this editing erase_if_present (properties, "min_dist"); erase_if_present (properties, "max_dist"); erase_if_present (properties, "min_weight"); erase_if_present (properties, "max_weight"); Editing::load_properties (properties); // Parameters that the worker threads need to be aware of, but do not appear in Properties const bool inverse = get_options ("inverse").size(); const bool ends_only = get_options ("ends_only").size(); // Parameters that the output thread needs to be aware of const size_t number = get_option_value ("number", size_t(0)); const size_t skip = get_option_value ("skip", size_t(0)); Loader loader (input_file_list); Worker worker (properties, inverse, ends_only); // This needs to be run AFTER creation of the Worker class // (worker needs to be able to set max & min number of points based on step size in input file, // receiver needs "output_step_size" field to have been updated before file creation) Receiver receiver (output_path, properties, number, skip); Thread::run_queue ( loader, Thread::batch (Streamline<>()), Thread::multi (worker), Thread::batch (Streamline<>()), receiver); }
void erase_if_present (Tractography::Properties& p, const std::string s) { auto i = p.find (s); if (i != p.end()) p.erase (i); }
void load_properties (Tractography::Properties& properties) { // ROIOption Options opt = get_options ("include"); for (size_t i = 0; i < opt.size(); ++i) properties.include.add (ROI (opt[i][0])); opt = get_options ("exclude"); for (size_t i = 0; i < opt.size(); ++i) properties.exclude.add (ROI (opt[i][0])); opt = get_options ("mask"); for (size_t i = 0; i < opt.size(); ++i) properties.mask.add (ROI (opt[i][0])); // LengthOption opt = get_options ("maxlength"); if (opt.size()) { if (properties.find ("max_dist") == properties.end()) { properties["max_dist"] = str(opt[0][0]); } else { try { const float maxlength = std::min (float(opt[0][0]), to<float>(properties["max_dist"])); properties["max_dist"] = str(maxlength); } catch (...) { properties["max_dist"] = str(opt[0][0]); } } } opt = get_options ("minlength"); if (opt.size()) { if (properties.find ("min_dist") == properties.end()) { properties["min_dist"] = str(opt[0][0]); } else { try { const float minlength = std::max (float(opt[0][0]), to<float>(properties["min_dist"])); properties["min_dist"] = str(minlength); } catch (...) { properties["min_dist"] = str(opt[0][0]); } } } // ResampleOption // The relevant entry in Properties is updated at a later stage // TruncateOption // These have no influence on Properties // WeightsOption // Only the thresholds have an influence on Properties opt = get_options ("maxweight"); if (opt.size()) properties["max_weight"] = std::string(opt[0][0]); opt = get_options ("minweight"); if (opt.size()) properties["min_weight"] = std::string(opt[0][0]); }
value_type AFDConnectivity::get (const std::string& path) { Tractography::Properties properties; Tractography::Reader<value_type> reader (path, properties); const size_t track_count = (properties.find ("count") == properties.end() ? 0 : to<size_t>(properties["count"])); DWI::Tractography::Mapping::TrackLoader loader (reader, track_count, "summing apparent fibre density within track"); // If WBFT is provided, this is the sum of (volume/length) across streamlines // Otherwise, it's a sum of lengths of all streamlines (for later scaling by mean streamline length) double sum_contributions = 0.0; size_t count = 0; Tractography::Streamline<value_type> tck; while (loader (tck)) { ++count; SetDixel dixels; mapper (tck, dixels); double this_length = 0.0, this_volume = 0.0; for (SetDixel::const_iterator i = dixels.begin(); i != dixels.end(); ++i) { this_length += i->get_length(); // If wbft has not been provided (i.e. FODs have not been pre-segmented), need to // check to see if any data have been provided for this voxel; and if not yet, // run the segmenter if (!have_wbft) { VoxelAccessor v (accessor()); assign_pos_of (*i, 0, 3).to (v); if (!v.value()) { assign_pos_of (*i, 0, 3).to (v_fod); DWI::FMLS::SH_coefs fod_data; DWI::FMLS::FOD_lobes fod_lobes; fod_data.vox[0] = v_fod.index (0); fod_data.vox[1] = v_fod.index (1); fod_data.vox[2] = v_fod.index (2); fod_data.resize (v_fod.size (3)); for (auto i = Loop(3) (v_fod); i; ++i) fod_data[v_fod.index (3)] = v_fod.value(); (*fmls) (fod_data, fod_lobes); (*this) (fod_lobes); } } const size_t fixel_index = dixel2fixel (*i); Fixel& fixel = fixels[fixel_index]; fixel.add_to_selection (i->get_length()); if (have_wbft) this_volume += fixel.get_selected_volume (i->get_length()); } if (have_wbft) sum_contributions += (this_volume / this_length); else sum_contributions += this_length; } if (!have_wbft) { // Streamlines define a fixel mask; go through and get all the fibre volumes double sum_volumes = 0.0; if (all_fixels) { // All fixels contribute to the result for (std::vector<Fixel>::const_iterator i = fixels.begin(); i != fixels.end(); ++i) { if (i->is_selected()) sum_volumes += i->get_FOD(); } } else { // Only allow one fixel per voxel to contribute to the result VoxelAccessor v (accessor()); for (auto l = Loop(v) (v); l; ++l) { if (v.value()) { value_type voxel_afd = 0.0, max_td = 0.0; for (Fixel_map<Fixel>::Iterator i = begin (v); i; ++i) { if (i().get_selected_length() > max_td) { max_td = i().get_selected_length(); voxel_afd = i().get_FOD(); } } sum_volumes += voxel_afd; } } } // sum_contributions currently stores sum of streamline lengths; // turn into a mean length, then combine with volume to get a connectivity value const double mean_length = sum_contributions / double(count); sum_contributions = sum_volumes / mean_length; } return sum_contributions; }