void run () { Image::Header H_in (argument[0]); DWI::Tractography::ACT::verify_5TT_image (H_in); Image::Buffer<float> in (H_in); Image::Header H_out (in); H_out.set_ndim (3); H_out.datatype() = DataType::Float32; Options opt = get_options ("bg"); const float bg_multiplier = opt.size() ? opt[0][0] : VALUE_DEFAULT_BG; opt = get_options ("cgm"); const float cgm_multiplier = opt.size() ? opt[0][0] : VALUE_DEFAULT_CGM; opt = get_options ("sgm"); const float sgm_multiplier = opt.size() ? opt[0][0] : VALUE_DEFAULT_SGM; opt = get_options ("wm"); const float wm_multiplier = opt.size() ? opt[0][0] : VALUE_DEFAULT_WM; opt = get_options ("csf"); const float csf_multiplier = opt.size() ? opt[0][0] : VALUE_DEFAULT_CSF; opt = get_options ("path"); const float path_multiplier = opt.size() ? opt[0][0] : VALUE_DEFAULT_PATH; Image::Buffer<float> out (argument[1], H_out); auto v_in = in.voxel(); auto v_out = out.voxel(); auto f = [&] (decltype(v_in)& in, decltype(v_out)& out) { const DWI::Tractography::ACT::Tissues t (in); const float bg = 1.0 - (t.get_cgm() + t.get_sgm() + t.get_wm() + t.get_csf() + t.get_path()); out.value() = (bg_multiplier * bg) + (cgm_multiplier * t.get_cgm()) + (sgm_multiplier * t.get_sgm()) + (wm_multiplier * t.get_wm()) + (csf_multiplier * t.get_csf()) + (path_multiplier * t.get_path()); }; Image::ThreadedLoop (v_out, 0, 3).run (f, v_in, v_out); }
Dynamic::Dynamic (const std::string& in, Image::Buffer<float>& fod_data, const Math::RNG& rng, const DWI::Directions::FastLookupSet& dirs) : Base (in, rng, "dynamic", MAX_TRACKING_SEED_ATTEMPTS_DYNAMIC), SIFT::ModelBase<Fixel_TD_seed> (fod_data, dirs), total_samples (0), total_seeds (0), transform (SIFT::ModelBase<Fixel_TD_seed>::info()) #ifdef DYNAMIC_SEED_DEBUGGING , seed_output ("seeds.tck", Tractography::Properties()) #endif { App::Options opt = App::get_options ("act"); if (opt.size()) act = new Dynamic_ACT_additions (opt[0][0]); perform_FOD_segmentation (fod_data); // Have to set a volume so that Seeding::List works correctly for (std::vector<Fixel>::const_iterator i = fixels.begin(); i != fixels.end(); ++i) volume += i->get_weight(); volume *= (fod_data.vox(0) * fod_data.vox(1) * fod_data.vox(2)); // Prevent divide-by-zero at commencement SIFT::ModelBase<Fixel_TD_seed>::TD_sum = DYNAMIC_SEED_INITIAL_TD_SUM; }
void run () { Image::Header H (argument[0]); Image::Info info (H); info.set_ndim (3); Image::BufferScratch<bool> mask (info); auto v_mask = mask.voxel(); std::string mask_path; Options opt = get_options ("mask"); if (opt.size()) { mask_path = std::string(opt[0][0]); Image::Buffer<bool> in (mask_path); if (!Image::dimensions_match (H, in, 0, 3)) throw Exception ("Input mask image does not match DWI"); if (!(in.ndim() == 3 || (in.ndim() == 4 && in.dim(3) == 1))) throw Exception ("Input mask image must be a 3D image"); auto v_in = in.voxel(); Image::copy (v_in, v_mask, 0, 3); } else { for (auto l = Image::LoopInOrder (v_mask) (v_mask); l; ++l) v_mask.value() = true; } DWI::CSDeconv<float>::Shared shared (H); const size_t lmax = DWI::lmax_for_directions (shared.DW_dirs); if (lmax < 4) throw Exception ("Cannot run dwi2response with lmax less than 4"); shared.lmax = lmax; Image::BufferPreload<float> dwi (H, Image::Stride::contiguous_along_axis (3)); DWI::Directions::Set directions (1281); Math::Vector<float> response (lmax/2+1); response.zero(); { // Initialise response function // Use lmax = 2, get the DWI intensity mean and standard deviation within the mask and // use these as the first two coefficients auto v_dwi = dwi.voxel(); double sum = 0.0, sq_sum = 0.0; size_t count = 0; Image::LoopInOrder loop (dwi, "initialising response function... ", 0, 3); for (auto l = loop (v_dwi, v_mask); l; ++l) { if (v_mask.value()) { for (size_t volume_index = 0; volume_index != shared.dwis.size(); ++volume_index) { v_dwi[3] = shared.dwis[volume_index]; const float value = v_dwi.value(); sum += value; sq_sum += Math::pow2 (value); ++count; } } } response[0] = sum / double (count); response[1] = - 0.5 * std::sqrt ((sq_sum / double(count)) - Math::pow2 (response[0])); // Account for scaling in SH basis response *= std::sqrt (4.0 * Math::pi); } INFO ("Initial response function is [" + str(response, 2) + "]"); // Algorithm termination options opt = get_options ("max_iters"); const size_t max_iters = opt.size() ? int(opt[0][0]) : DWI2RESPONSE_DEFAULT_MAX_ITERS; opt = get_options ("max_change"); const float max_change = 0.01 * (opt.size() ? float(opt[0][0]) : DWI2RESPONSE_DEFAULT_MAX_CHANGE); // Should all voxels (potentially within a user-specified mask) be tested at every iteration? opt = get_options ("test_all"); const bool reset_mask = opt.size(); // Single-fibre voxel selection options opt = get_options ("volume_ratio"); const float volume_ratio = opt.size() ? float(opt[0][0]) : DWI2RESPONSE_DEFAULT_VOLUME_RATIO; opt = get_options ("dispersion_multiplier"); const float dispersion_multiplier = opt.size() ? float(opt[0][0]) : DWI2RESPONSE_DEFAULT_DISPERSION_MULTIPLIER; opt = get_options ("integral_multiplier"); const float integral_multiplier = opt.size() ? float(opt[0][0]) : DWI2RESPONSE_DEFAULT_INTEGRAL_STDEV_MULTIPLIER; SFThresholds thresholds (volume_ratio); // Only threshold the lobe volume ratio for now; other two are not yet used size_t total_iter = 0; bool first_pass = true; size_t prev_sf_count = 0; { bool iterate = true; size_t iter = 0; ProgressBar progress ("optimising response function... "); do { ++iter; { MR::LogLevelLatch latch (0); shared.set_response (response); shared.init(); } ++progress; if (reset_mask) { if (mask_path.size()) { Image::Buffer<bool> in (mask_path); auto v_in = in.voxel(); Image::copy (v_in, v_mask, 0, 3); } else { for (auto l = Image::LoopInOrder(v_mask) (v_mask); l; ++l) v_mask.value() = true; } ++progress; } std::vector<FODSegResult> seg_results; { FODCalcAndSeg processor (dwi, mask, shared, directions, lmax, seg_results); Image::ThreadedLoop loop (mask, 0, 3); loop.run (processor); } ++progress; if (!first_pass) thresholds.update (seg_results, dispersion_multiplier, integral_multiplier, iter); ++progress; Response output (lmax); mask.zero(); { SFSelector selector (seg_results, thresholds, mask); ResponseEstimator estimator (dwi, shared, lmax, output); Thread::run_queue (selector, FODSegResult(), Thread::multi (estimator)); } if (!output.get_count()) throw Exception ("Cannot estimate response function; all voxels have been excluded from selection"); const Math::Vector<float> new_response = output.result(); const size_t sf_count = output.get_count(); ++progress; if (App::log_level >= 2) std::cerr << "\n"; INFO ("Iteration " + str(iter) + ", " + str(sf_count) + " SF voxels, new response function: [" + str(new_response, 2) + "]"); if (sf_count == prev_sf_count) { INFO ("terminating due to convergence of single-fibre voxel selection"); iterate = false; } if (iter == max_iters) { INFO ("terminating due to completing maximum number of iterations"); iterate = false; } bool rf_changed = false; for (size_t i = 0; i != response.size(); ++i) { if (std::abs ((new_response[i] - response[i]) / new_response[i]) > max_change) rf_changed = true; } if (!rf_changed) { INFO ("terminating due to negligible changes in the response function coefficients"); iterate = false; } if (!iterate && first_pass) { iterate = true; first_pass = false; INFO ("commencing second-pass of response function estimation"); total_iter = iter; iter = 0; } response = new_response; prev_sf_count = sf_count; //v_mask.save ("mask_pass_" + str(first_pass?1:2) + "_iter_" + str(iter) + ".mif"); } while (iterate); total_iter += iter; } CONSOLE ("final response function: [" + str(response, 2) + "] (reached after " + str(total_iter) + " iterations using " + str(prev_sf_count) + " voxels)"); response.save (argument[1]); opt = get_options ("sf"); if (opt.size()) v_mask.save (std::string (opt[0][0])); }