void run () { Loader loader (argument[0]); Image::BufferPreload<value_type> data (argument[1]); auto vox = data.voxel(); Warper warper (vox); Writer writer (argument[2], loader.properties); Thread::run_queue ( loader, Thread::batch (TrackType(), 1024), Thread::multi (warper), Thread::batch (TrackType(), 1024), writer); }
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])); }