diff --git a/Modules/DiffusionCmdApps/Misc/CMakeLists.txt b/Modules/DiffusionCmdApps/Misc/CMakeLists.txt index d4746a8..886bdfa 100644 --- a/Modules/DiffusionCmdApps/Misc/CMakeLists.txt +++ b/Modules/DiffusionCmdApps/Misc/CMakeLists.txt @@ -1,48 +1,49 @@ option(BUILD_DiffusionMiscCmdApps "Build miscellaneous commandline tools for diffusion" OFF) if(BUILD_DiffusionMiscCmdApps OR MITK_BUILD_ALL_APPS) # needed include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of diffusion cmdapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( diffusionMisccmdapps PeakExtraction^^ FileFormatConverter^^ FlipPeaks^^ ImageResampler^^ CopyGeometry^^ Registration^^ DiffusionDICOMLoader^^ ResampleGradients^^ ShToOdfImage^^ DImp^^ DReg^^ HeadMotionCorrection^^ DmriDenoising^^MitkImageDenoising RoundBvalues^^ + FiberScreenshot^^ ) foreach(diffusionMisccmdapp ${diffusionMisccmdapps}) # extract cmd app name and dependencies string(REPLACE "^^" "\\;" cmdapp_info ${diffusionMisccmdapp}) set(cmdapp_info_list ${cmdapp_info}) list(GET cmdapp_info_list 0 appname) list(GET cmdapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitkFunctionCreateCommandLineApp( NAME ${appname} - DEPENDS MitkDiffusionCmdApps ${dependencies_list} + DEPENDS MitkDiffusionCmdApps MitkTestingHelper ${dependencies_list} PACKAGE_DEPENDS ) endforeach() endif() diff --git a/Modules/DiffusionCmdApps/Misc/FiberScreenshot.cpp b/Modules/DiffusionCmdApps/Misc/FiberScreenshot.cpp new file mode 100644 index 0000000..df5a813 --- /dev/null +++ b/Modules/DiffusionCmdApps/Misc/FiberScreenshot.cpp @@ -0,0 +1,130 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center. + +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include <vector> +#include <iostream> +#include <fstream> +#include <algorithm> + +#include <itkImageFileWriter.h> +#include <itkMetaDataObject.h> +#include <itkVectorImage.h> + +#include <mitkBaseData.h> +#include <mitkFiberBundle.h> +#include "mitkDiffusionCommandLineParser.h" +#include <mitkLexicalCast.h> +#include <mitkCoreObjectFactory.h> +#include <mitkIOUtil.h> +#include <mitkDiffusionDataIOHelper.h> +#include <mitkImage.h> +#include <mitkRenderingTestHelper.h> +#include <vtkRenderLargeImage.h> +#include <vtkImageWriter.h> +#include <vtkPNGWriter.h> + + +/*! +\brief Modify input tractogram: fiber resampling, compression, pruning and transformation. +*/ +int main(int argc, char* argv[]) +{ + mitkDiffusionCommandLineParser parser; + + parser.setTitle("Fiber Screenshot"); + parser.setDescription("Take a screenshot of the loaded fiber bundle"); + parser.setContributor("MIC"); + + parser.setArgumentPrefix("--", "-"); + + parser.beginGroup("1. Mandatory arguments:"); + parser.addArgument("", "i", mitkDiffusionCommandLineParser::StringList, "Input:", "input tractograms", us::Any(), false); + parser.addArgument("", "o", mitkDiffusionCommandLineParser::String, "Output:", "Output png", us::Any(), false, false, false, mitkDiffusionCommandLineParser::Output); + parser.addArgument("resample", "", mitkDiffusionCommandLineParser::Float, "", ""); + parser.endGroup(); + + parser.addArgument("rotate_x", "", mitkDiffusionCommandLineParser::Float, "Rotate x-axis:", "Rotate around x-axis (in deg)"); + parser.addArgument("rotate_y", "", mitkDiffusionCommandLineParser::Float, "Rotate y-axis:", "Rotate around y-axis (in deg)"); + parser.addArgument("rotate_z", "", mitkDiffusionCommandLineParser::Float, "Rotate z-axis:", "Rotate around z-axis (in deg)"); + + + std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + + mitkDiffusionCommandLineParser::StringContainerType inFibs = us::any_cast<mitkDiffusionCommandLineParser::StringContainerType>(parsedArgs["i"]); + std::string outFileName = us::any_cast<std::string>(parsedArgs["o"]); + + float rotateX = 0; + if (parsedArgs.count("rotate_x")) + rotateX = us::any_cast<float>(parsedArgs["rotate_x"]); + + float rotateY = 0; + if (parsedArgs.count("rotate_y")) + rotateY = us::any_cast<float>(parsedArgs["rotate_y"]); + + float rotateZ = 0; + if (parsedArgs.count("rotate_z")) + rotateZ = us::any_cast<float>(parsedArgs["rotate_z"]); + + try + { + mitk::RenderingTestHelper renderingHelper(640*2, 480*2); + std::vector< mitk::FiberBundle::Pointer > tractograms = mitk::DiffusionDataIOHelper::load_fibs(inFibs); + for (auto fib : tractograms) + { + fib->RotateAroundAxis(rotateX, rotateY, rotateZ); + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData(fib); + + node->SetFloatProperty("shape.tuberadius", 0.5); + renderingHelper.AddNodeToStorage(node); + } + + renderingHelper.SetMapperIDToRender3D(); + + vtkSmartPointer<vtkRenderer> renderer = renderingHelper.GetVtkRenderer(); + renderer->GetRenderWindow()->DoubleBufferOn(); + + vtkSmartPointer<vtkRenderLargeImage> magnifier = vtkSmartPointer<vtkRenderLargeImage>::New(); + magnifier->SetInput(renderer); + magnifier->SetMagnification(4); + + vtkSmartPointer<vtkImageWriter> fileWriter = vtkSmartPointer<vtkPNGWriter>::New(); + fileWriter->SetInputConnection(magnifier->GetOutputPort()); + fileWriter->SetFileName(outFileName.c_str()); + + fileWriter->Write(); + + } + catch (const itk::ExceptionObject& e) + { + std::cout << e.what(); + return EXIT_FAILURE; + } + catch (std::exception& e) + { + std::cout << e.what(); + return EXIT_FAILURE; + } + catch (...) + { + std::cout << "ERROR!?!"; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/Modules/DiffusionCmdApps/Tractography/StreamlineTractography.cpp b/Modules/DiffusionCmdApps/Tractography/StreamlineTractography.cpp index 346123c..ae11ebc 100644 --- a/Modules/DiffusionCmdApps/Tractography/StreamlineTractography.cpp +++ b/Modules/DiffusionCmdApps/Tractography/StreamlineTractography.cpp @@ -1,582 +1,590 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include <mitkBaseData.h> #include <mitkImageCast.h> #include <mitkImageToItk.h> #include <metaCommand.h> #include <mitkDiffusionCommandLineParser.h> #include <mitkLog.h> #include <usAny.h> #include <mitkIOUtil.h> #include <iostream> #include <fstream> #include <itksys/SystemTools.hxx> #include <mitkCoreObjectFactory.h> #include <itksys/SystemTools.hxx> #include <mitkFiberBundle.h> #include <itkStreamlineTrackingFilter.h> #include <Algorithms/TrackingHandlers/mitkTrackingDataHandler.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h> #include <itkTensorImageToOdfImageFilter.h> #include <mitkTractographyForest.h> #include <mitkPreferenceListReaderOptionsFunctor.h> #include <mitkStreamlineTractographyParameters.h> #define _USE_MATH_DEFINES #include <math.h> const int numOdfSamples = 200; typedef itk::Image< itk::Vector< float, numOdfSamples > , 3 > SampledShImageType; /*! \brief */ int main(int argc, char* argv[]) { mitkDiffusionCommandLineParser parser; parser.setTitle("Streamline Tractography"); parser.setCategory("Fiber Tracking and Processing Methods"); parser.setDescription("Perform streamline tractography"); parser.setContributor("MIC"); // parameters fo all methods parser.setArgumentPrefix("--", "-"); parser.beginGroup("1. Mandatory arguments:"); parser.addArgument("", "i", mitkDiffusionCommandLineParser::StringList, "Input:", "input image (multiple possible for 'DetTensor' algorithm)", us::Any(), false, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("", "o", mitkDiffusionCommandLineParser::String, "Output:", "output fiberbundle/probability map", us::Any(), false, false, false, mitkDiffusionCommandLineParser::Output); parser.addArgument("type", "", mitkDiffusionCommandLineParser::String, "Type:", "which tracker to use (Peaks; Tensor; ODF; ODF-DIPY/FSL; RF)", us::Any(), false); parser.addArgument("probabilistic", "", mitkDiffusionCommandLineParser::Bool, "Probabilistic:", "Probabilistic tractography", us::Any(false)); parser.endGroup(); parser.beginGroup("2. Seeding:"); parser.addArgument("seeds", "", mitkDiffusionCommandLineParser::Int, "Seeds per voxel:", "number of seed points per voxel", 1); parser.addArgument("seed_image", "", mitkDiffusionCommandLineParser::String, "Seed image:", "mask image defining seed voxels", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("trials_per_seed", "", mitkDiffusionCommandLineParser::Int, "Max. trials per seed:", "try each seed N times until a valid streamline is obtained (only for probabilistic tractography)", 10); parser.addArgument("max_tracts", "", mitkDiffusionCommandLineParser::Int, "Max. number of tracts:", "tractography is stopped if the reconstructed number of tracts is exceeded", -1); parser.endGroup(); parser.beginGroup("3. Tractography constraints:"); parser.addArgument("tracking_mask", "", mitkDiffusionCommandLineParser::String, "Mask image:", "streamlines leaving the mask will stop immediately", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("stop_image", "", mitkDiffusionCommandLineParser::String, "Stop ROI image:", "streamlines entering the mask will stop immediately", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("exclusion_image", "", mitkDiffusionCommandLineParser::String, "Exclusion ROI image:", "streamlines entering the mask will be discarded", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("ep_constraint", "", mitkDiffusionCommandLineParser::String, "Endpoint constraint:", "determines which fibers are accepted based on their endpoint location - options are NONE, EPS_IN_TARGET, EPS_IN_TARGET_LABELDIFF, EPS_IN_SEED_AND_TARGET, MIN_ONE_EP_IN_TARGET, ONE_EP_IN_TARGET and NO_EP_IN_TARGET", us::Any()); parser.addArgument("target_image", "", mitkDiffusionCommandLineParser::String, "Target ROI image:", "effact depends on the chosen endpoint constraint (option ep_constraint)", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.endGroup(); parser.beginGroup("4. Streamline integration parameters:"); parser.addArgument("sharpen_odfs", "", mitkDiffusionCommandLineParser::Int, "Sharpen ODFs:", "if you are using dODF images as input, it is advisable to sharpen the ODFs (power of X). this is not necessary for CSD fODFs, since they are narurally much sharper."); parser.addArgument("cutoff", "", mitkDiffusionCommandLineParser::Float, "Cutoff:", "set the FA, GFA or Peak amplitude cutoff for terminating tracks", 0.1); parser.addArgument("odf_cutoff", "", mitkDiffusionCommandLineParser::Float, "ODF Cutoff:", "threshold on the ODF magnitude. this is useful in case of CSD fODF tractography.", 0.0); parser.addArgument("step_size", "", mitkDiffusionCommandLineParser::Float, "Step size:", "step size (in voxels)", 0.5); parser.addArgument("min_tract_length", "", mitkDiffusionCommandLineParser::Float, "Min. tract length:", "minimum fiber length (in mm)", 20); + parser.addArgument("max_tract_length", "", mitkDiffusionCommandLineParser::Float, "Max. tract length:", "maximum fiber length (in mm)", 400); parser.addArgument("angular_threshold", "", mitkDiffusionCommandLineParser::Float, "Angular threshold:", "angular threshold between two successive steps, (default: 90° * step_size, minimum 15°)"); parser.addArgument("loop_check", "", mitkDiffusionCommandLineParser::Float, "Check for loops:", "threshold on angular stdev over the last 4 voxel lengths"); parser.addArgument("peak_jitter", "", mitkDiffusionCommandLineParser::Float, "Peak jitter:", "important for probabilistic peak tractography and peak prior. actual jitter is drawn from a normal distribution with peak_jitter*fabs(direction_value) as standard deviation.", 0.01); + parser.addArgument("first_order", "", mitkDiffusionCommandLineParser::Bool, "First order integration:", "use first order integration. default is second order to avoids streamlineovershoot", false); parser.endGroup(); parser.beginGroup("5. Tractography prior:"); parser.addArgument("prior_image", "", mitkDiffusionCommandLineParser::String, "Peak prior:", "tractography prior in thr for of a peak image", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("prior_weight", "", mitkDiffusionCommandLineParser::Float, "Prior weight", "weighting factor between prior and data.", 0.5); parser.addArgument("dont_restrict_to_prior", "", mitkDiffusionCommandLineParser::Bool, "Don't restrict to prior:", "don't restrict tractography to regions where the prior is valid.", us::Any(false)); parser.addArgument("no_new_directions_from_prior", "", mitkDiffusionCommandLineParser::Bool, "No new directios from prior:", "the prior cannot create directions where there are none in the data.", us::Any(false)); parser.addArgument("prior_flip_x", "", mitkDiffusionCommandLineParser::Bool, "Prior Flip X:", "multiply x-coordinate of prior direction by -1"); parser.addArgument("prior_flip_y", "", mitkDiffusionCommandLineParser::Bool, "Prior Flip Y:", "multiply y-coordinate of prior direction by -1"); parser.addArgument("prior_flip_z", "", mitkDiffusionCommandLineParser::Bool, "Prior Flip Z:", "multiply z-coordinate of prior direction by -1"); parser.endGroup(); parser.beginGroup("6. Neighborhood sampling:"); parser.addArgument("num_samples", "", mitkDiffusionCommandLineParser::Int, "Num. neighborhood samples:", "number of neighborhood samples that are use to determine the next progression direction", 0); parser.addArgument("sampling_distance", "", mitkDiffusionCommandLineParser::Float, "Sampling distance:", "distance of neighborhood sampling points (in voxels)", 0.25); parser.addArgument("use_stop_votes", "", mitkDiffusionCommandLineParser::Bool, "Use stop votes:", "use stop votes"); parser.addArgument("use_only_forward_samples", "", mitkDiffusionCommandLineParser::Bool, "Use only forward samples:", "use only forward samples"); parser.endGroup(); parser.beginGroup("7. Tensor tractography specific:"); parser.addArgument("tend_f", "", mitkDiffusionCommandLineParser::Float, "Weight f", "weighting factor between first eigenvector (f=1 equals FACT tracking) and input vector dependent direction (f=0).", 1.0); parser.addArgument("tend_g", "", mitkDiffusionCommandLineParser::Float, "Weight g", "weighting factor between input vector (g=0) and tensor deflection (g=1 equals TEND tracking)", 0.0); parser.endGroup(); parser.beginGroup("8. Random forest tractography specific:"); parser.addArgument("forest", "", mitkDiffusionCommandLineParser::String, "Forest:", "input random forest (HDF5 file)", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.addArgument("use_sh_features", "", mitkDiffusionCommandLineParser::Bool, "Use SH features:", "use SH features"); parser.endGroup(); parser.beginGroup("9. Additional input:"); parser.addArgument("additional_images", "", mitkDiffusionCommandLineParser::StringList, "Additional images:", "specify a list of float images that hold additional information (FA, GFA, additional features for RF tractography)", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.endGroup(); parser.beginGroup("10. Misc:"); parser.addArgument("flip_x", "", mitkDiffusionCommandLineParser::Bool, "Flip X:", "multiply x-coordinate of direction proposal by -1"); parser.addArgument("flip_y", "", mitkDiffusionCommandLineParser::Bool, "Flip Y:", "multiply y-coordinate of direction proposal by -1"); parser.addArgument("flip_z", "", mitkDiffusionCommandLineParser::Bool, "Flip Z:", "multiply z-coordinate of direction proposal by -1"); parser.addArgument("no_data_interpolation", "", mitkDiffusionCommandLineParser::Bool, "Don't interpolate input data:", "don't interpolate input image values"); parser.addArgument("no_mask_interpolation", "", mitkDiffusionCommandLineParser::Bool, "Don't interpolate masks:", "don't interpolate mask image values"); parser.addArgument("compress", "", mitkDiffusionCommandLineParser::Bool, "Compress:", "compress output fibers (lossy)"); parser.addArgument("fix_seed", "", mitkDiffusionCommandLineParser::Bool, "Fix Random Seed:", "always use the same random numbers"); parser.addArgument("parameter_file", "", mitkDiffusionCommandLineParser::String, "Parameter File:", "load parameters from json file (svae using MITK Diffusion GUI). the parameters loaded form this file are overwritten by the manually set parameters.", us::Any(), true, false, false, mitkDiffusionCommandLineParser::Input); parser.endGroup(); std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; mitkDiffusionCommandLineParser::StringContainerType input_files = us::any_cast<mitkDiffusionCommandLineParser::StringContainerType>(parsedArgs["i"]); std::string outFile = us::any_cast<std::string>(parsedArgs["o"]); std::string type = us::any_cast<std::string>(parsedArgs["type"]); std::shared_ptr< mitk::StreamlineTractographyParameters > params = std::make_shared<mitk::StreamlineTractographyParameters>(); if (parsedArgs.count("parameter_file")) { auto parameter_file = us::any_cast<std::string>(parsedArgs["parameter_file"]); params->LoadParameters(parameter_file); } if (parsedArgs.count("probabilistic")) params->m_Mode = mitk::StreamlineTractographyParameters::MODE::PROBABILISTIC; else { params->m_Mode = mitk::StreamlineTractographyParameters::MODE::DETERMINISTIC; } std::string prior_image = ""; if (parsedArgs.count("prior_image")) prior_image = us::any_cast<std::string>(parsedArgs["prior_image"]); if (parsedArgs.count("prior_weight")) params->m_Weight = us::any_cast<float>(parsedArgs["prior_weight"]); if (parsedArgs.count("fix_seed")) params->m_FixRandomSeed = us::any_cast<bool>(parsedArgs["fix_seed"]); if (parsedArgs.count("dont_restrict_to_prior")) params->m_RestrictToPrior = !us::any_cast<bool>(parsedArgs["dont_restrict_to_prior"]); if (parsedArgs.count("no_new_directions_from_prior")) params->m_NewDirectionsFromPrior = !us::any_cast<bool>(parsedArgs["no_new_directions_from_prior"]); if (parsedArgs.count("sharpen_odfs")) params->m_SharpenOdfs = us::any_cast<int>(parsedArgs["sharpen_odfs"]); if (parsedArgs.count("no_data_interpolation")) params->m_InterpolateTractographyData = !us::any_cast<bool>(parsedArgs["no_data_interpolation"]); params->m_InterpolateRoiImages = true; if (parsedArgs.count("no_mask_interpolation")) params->m_InterpolateRoiImages = !us::any_cast<bool>(parsedArgs["no_mask_interpolation"]); bool use_sh_features = false; if (parsedArgs.count("use_sh_features")) use_sh_features = us::any_cast<bool>(parsedArgs["use_sh_features"]); if (parsedArgs.count("use_stop_votes")) params->m_StopVotes = us::any_cast<bool>(parsedArgs["use_stop_votes"]); if (parsedArgs.count("use_only_forward_samples")) params->m_OnlyForwardSamples = us::any_cast<bool>(parsedArgs["use_only_forward_samples"]); if (parsedArgs.count("flip_x")) params->m_FlipX = us::any_cast<bool>(parsedArgs["flip_x"]); if (parsedArgs.count("flip_y")) params->m_FlipY = us::any_cast<bool>(parsedArgs["flip_y"]); if (parsedArgs.count("flip_z")) params->m_FlipZ = us::any_cast<bool>(parsedArgs["flip_z"]); if (parsedArgs.count("prior_flip_x")) params->m_PriorFlipX = us::any_cast<bool>(parsedArgs["prior_flip_x"]); if (parsedArgs.count("prior_flip_y")) params->m_PriorFlipY = us::any_cast<bool>(parsedArgs["prior_flip_y"]); if (parsedArgs.count("prior_flip_z")) params->m_PriorFlipZ = us::any_cast<bool>(parsedArgs["prior_flip_z"]); if (parsedArgs.count("apply_image_rotation")) params->m_ApplyDirectionMatrix = us::any_cast<bool>(parsedArgs["apply_image_rotation"]); if (parsedArgs.count("compress")) params->m_CompressFibers = us::any_cast<bool>(parsedArgs["compress"]); if (parsedArgs.count("min_tract_length")) params->m_MinTractLengthMm = us::any_cast<float>(parsedArgs["min_tract_length"]); + if (parsedArgs.count("max_tract_length")) + params->m_MaxTractLengthMm = us::any_cast<float>(parsedArgs["max_tract_length"]); + if (parsedArgs.count("loop_check")) params->SetLoopCheckDeg(us::any_cast<float>(parsedArgs["loop_check"])); + if (parsedArgs.count("first_order")) + params->m_SecondOrder = false; + std::string forestFile; if (parsedArgs.count("forest")) forestFile = us::any_cast<std::string>(parsedArgs["forest"]); std::string maskFile = ""; if (parsedArgs.count("tracking_mask")) maskFile = us::any_cast<std::string>(parsedArgs["tracking_mask"]); std::string seedFile = ""; if (parsedArgs.count("seed_image")) seedFile = us::any_cast<std::string>(parsedArgs["seed_image"]); std::string targetFile = ""; if (parsedArgs.count("target_image")) targetFile = us::any_cast<std::string>(parsedArgs["target_image"]); std::string exclusionFile = ""; if (parsedArgs.count("exclusion_image")) exclusionFile = us::any_cast<std::string>(parsedArgs["exclusion_image"]); std::string stopFile = ""; if (parsedArgs.count("stop_image")) stopFile = us::any_cast<std::string>(parsedArgs["stop_image"]); if (parsedArgs.count("ep_constraint")) { if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "NONE") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::NONE; else if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "EPS_IN_TARGET") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::EPS_IN_TARGET; else if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "EPS_IN_TARGET_LABELDIFF") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::EPS_IN_TARGET_LABELDIFF; else if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "EPS_IN_SEED_AND_TARGET") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::EPS_IN_SEED_AND_TARGET; else if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "MIN_ONE_EP_IN_TARGET") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::MIN_ONE_EP_IN_TARGET; else if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "ONE_EP_IN_TARGET") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::ONE_EP_IN_TARGET; else if (us::any_cast<std::string>(parsedArgs["ep_constraint"]) == "NO_EP_IN_TARGET") params->m_EpConstraints = mitk::StreamlineTractographyParameters::EndpointConstraints::NO_EP_IN_TARGET; } if (parsedArgs.count("cutoff")) params->m_Cutoff = us::any_cast<float>(parsedArgs["cutoff"]); if (parsedArgs.count("odf_cutoff")) params->m_OdfCutoff = us::any_cast<float>(parsedArgs["odf_cutoff"]); if (parsedArgs.count("peak_jitter")) params->m_PeakJitter = us::any_cast<float>(parsedArgs["peak_jitter"]); if (parsedArgs.count("step_size")) params->SetStepSizeVox(us::any_cast<float>(parsedArgs["step_size"])); if (parsedArgs.count("sampling_distance")) params->SetSamplingDistanceVox(us::any_cast<float>(parsedArgs["sampling_distance"])); if (parsedArgs.count("num_samples")) params->m_NumSamples = static_cast<unsigned int>(us::any_cast<int>(parsedArgs["num_samples"])); if (parsedArgs.count("seeds")) params->m_SeedsPerVoxel = us::any_cast<int>(parsedArgs["seeds"]); if (parsedArgs.count("trials_per_seed")) params->m_TrialsPerSeed = static_cast<unsigned int>(us::any_cast<int>(parsedArgs["trials_per_seed"])); if (parsedArgs.count("tend_f")) params->m_F = us::any_cast<float>(parsedArgs["tend_f"]); if (parsedArgs.count("tend_g")) params->m_G = us::any_cast<float>(parsedArgs["tend_g"]); if (parsedArgs.count("angular_threshold")) params->SetAngularThresholdDeg(us::any_cast<float>(parsedArgs["angular_threshold"])); if (parsedArgs.count("max_tracts")) params->m_MaxNumFibers = us::any_cast<int>(parsedArgs["max_tracts"]); std::string ext = itksys::SystemTools::GetFilenameExtension(outFile); if (ext != ".fib" && ext != ".trk") { MITK_INFO << "Output file format not supported. Use one of .fib, .trk, .nii, .nii.gz, .nrrd"; return EXIT_FAILURE; } // LOAD DATASETS mitkDiffusionCommandLineParser::StringContainerType addFiles; if (parsedArgs.count("additional_images")) addFiles = us::any_cast<mitkDiffusionCommandLineParser::StringContainerType>(parsedArgs["additional_images"]); typedef itk::Image<float, 3> ItkFloatImgType; ItkFloatImgType::Pointer mask = nullptr; if (!maskFile.empty()) { MITK_INFO << "loading mask image"; mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(maskFile); mask = ItkFloatImgType::New(); mitk::CastToItkImage(img, mask); } ItkFloatImgType::Pointer seed = nullptr; if (!seedFile.empty()) { MITK_INFO << "loading seed ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(seedFile); seed = ItkFloatImgType::New(); mitk::CastToItkImage(img, seed); } ItkFloatImgType::Pointer stop = nullptr; if (!stopFile.empty()) { MITK_INFO << "loading stop ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(stopFile); stop = ItkFloatImgType::New(); mitk::CastToItkImage(img, stop); } ItkFloatImgType::Pointer target = nullptr; if (!targetFile.empty()) { MITK_INFO << "loading target ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(targetFile); target = ItkFloatImgType::New(); mitk::CastToItkImage(img, target); } ItkFloatImgType::Pointer exclusion = nullptr; if (!exclusionFile.empty()) { MITK_INFO << "loading exclusion ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(exclusionFile); exclusion = ItkFloatImgType::New(); mitk::CastToItkImage(img, exclusion); } MITK_INFO << "loading additional images"; std::vector< std::vector< ItkFloatImgType::Pointer > > addImages; addImages.push_back(std::vector< ItkFloatImgType::Pointer >()); for (auto file : addFiles) { mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(file); ItkFloatImgType::Pointer itkimg = ItkFloatImgType::New(); mitk::CastToItkImage(img, itkimg); addImages.at(0).push_back(itkimg); } // ////////////////////////////////////////////////////////////////// // omp_set_num_threads(1); typedef itk::StreamlineTrackingFilter TrackerType; TrackerType::Pointer tracker = TrackerType::New(); if (!prior_image.empty()) { mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"Peak Image"}, std::vector<std::string>()); mitk::PeakImage::Pointer priorImage = mitk::IOUtil::Load<mitk::PeakImage>(prior_image, &functor); if (priorImage.IsNull()) { MITK_INFO << "Only peak images are supported as prior at the moment!"; return EXIT_FAILURE; } mitk::TrackingDataHandler* priorhandler = new mitk::TrackingHandlerPeaks(); typedef mitk::ImageToItk< mitk::TrackingHandlerPeaks::PeakImgType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(priorImage); caster->Update(); mitk::TrackingHandlerPeaks::PeakImgType::Pointer itkImg = caster->GetOutput(); std::shared_ptr< mitk::StreamlineTractographyParameters > prior_params = std::make_shared< mitk::StreamlineTractographyParameters >(*params); prior_params->m_FlipX = params->m_PriorFlipX; prior_params->m_FlipY = params->m_PriorFlipY; prior_params->m_FlipZ = params->m_PriorFlipZ; prior_params->m_Cutoff = 0.0; dynamic_cast<mitk::TrackingHandlerPeaks*>(priorhandler)->SetPeakImage(itkImg); priorhandler->SetParameters(prior_params); tracker->SetTrackingPriorHandler(priorhandler); } mitk::TrackingDataHandler* handler; mitk::Image::Pointer reference_image; if (type == "RF") { mitk::TractographyForest::Pointer forest = mitk::IOUtil::Load<mitk::TractographyForest>(forestFile); if (forest.IsNull()) mitkThrow() << "Forest file " << forestFile << " could not be read."; std::vector<std::string> include = {"Diffusion Weighted Images"}; std::vector<std::string> exclude = {}; mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(include, exclude); auto input = mitk::IOUtil::Load<mitk::Image>(input_files.at(0), &functor); reference_image = input; if (use_sh_features) { handler = new mitk::TrackingHandlerRandomForest<6,28>(); dynamic_cast<mitk::TrackingHandlerRandomForest<6,28>*>(handler)->SetForest(forest); dynamic_cast<mitk::TrackingHandlerRandomForest<6,28>*>(handler)->AddDwi(input); dynamic_cast<mitk::TrackingHandlerRandomForest<6,28>*>(handler)->SetAdditionalFeatureImages(addImages); } else { handler = new mitk::TrackingHandlerRandomForest<6,100>(); dynamic_cast<mitk::TrackingHandlerRandomForest<6,100>*>(handler)->SetForest(forest); dynamic_cast<mitk::TrackingHandlerRandomForest<6,100>*>(handler)->AddDwi(input); dynamic_cast<mitk::TrackingHandlerRandomForest<6,100>*>(handler)->SetAdditionalFeatureImages(addImages); } } else if (type == "Peaks") { handler = new mitk::TrackingHandlerPeaks(); MITK_INFO << "loading input peak image"; mitk::Image::Pointer mitkImage = mitk::IOUtil::Load<mitk::Image>(input_files.at(0)); reference_image = mitkImage; mitk::TrackingHandlerPeaks::PeakImgType::Pointer itkImg = mitk::convert::GetItkPeakFromPeakImage(mitkImage); dynamic_cast<mitk::TrackingHandlerPeaks*>(handler)->SetPeakImage(itkImg); } else if (type == "Tensor" && params->m_Mode == mitk::StreamlineTractographyParameters::MODE::DETERMINISTIC) { handler = new mitk::TrackingHandlerTensor(); MITK_INFO << "loading input tensor images"; std::vector< mitk::Image::Pointer > input_images; for (unsigned int i=0; i<input_files.size(); i++) { mitk::Image::Pointer mitkImage = mitk::IOUtil::Load<mitk::Image>(input_files.at(i)); reference_image = mitkImage; mitk::TensorImage::ItkTensorImageType::Pointer itkImg = mitk::convert::GetItkTensorFromTensorImage(mitkImage); dynamic_cast<mitk::TrackingHandlerTensor*>(handler)->AddTensorImage(itkImg.GetPointer()); } if (addImages.at(0).size()>0) dynamic_cast<mitk::TrackingHandlerTensor*>(handler)->SetFaImage(addImages.at(0).at(0)); } else if (type == "ODF" || type == "ODF-DIPY/FSL" || (type == "Tensor" && params->m_Mode == mitk::StreamlineTractographyParameters::MODE::PROBABILISTIC)) { handler = new mitk::TrackingHandlerOdf(); mitk::OdfImage::ItkOdfImageType::Pointer itkImg = nullptr; if (type == "Tensor") { MITK_INFO << "Converting Tensor to ODF image"; auto input = mitk::IOUtil::Load<mitk::Image>(input_files.at(0)); reference_image = input; itkImg = mitk::convert::GetItkOdfFromTensorImage(input); dynamic_cast<mitk::TrackingHandlerOdf*>(handler)->SetIsOdfFromTensor(true); } else { std::vector<std::string> include = {"SH Image", "ODF Image"}; std::vector<std::string> exclude = {}; mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(include, exclude); auto input = mitk::IOUtil::Load(input_files.at(0), &functor)[0]; reference_image = dynamic_cast<mitk::Image*>(input.GetPointer()); if (dynamic_cast<mitk::ShImage*>(input.GetPointer())) { MITK_INFO << "Converting SH to ODF image"; mitk::ShImage::Pointer mitkShImage = dynamic_cast<mitk::ShImage*>(input.GetPointer()); if (type == "ODF-DIPY/FSL") mitkShImage->SetShConvention(mitk::ShImage::SH_CONVENTION::FSL); mitk::Image::Pointer mitkImg = dynamic_cast<mitk::Image*>(mitkShImage.GetPointer()); itkImg = mitk::convert::GetItkOdfFromShImage(mitkImg); } else if (dynamic_cast<mitk::OdfImage*>(input.GetPointer())) { mitk::Image::Pointer mitkImg = dynamic_cast<mitk::Image*>(input.GetPointer()); itkImg = mitk::convert::GetItkOdfFromOdfImage(mitkImg); } else mitkThrow() << ""; } dynamic_cast<mitk::TrackingHandlerOdf*>(handler)->SetOdfImage(itkImg); if (addImages.at(0).size()>0) dynamic_cast<mitk::TrackingHandlerOdf*>(handler)->SetGfaImage(addImages.at(0).at(0)); } else { MITK_INFO << "Unknown tractography algorithm (" + type+"). Known types are Peaks, DetTensor, ProbTensor, DetODF, ProbODF, DetRF, ProbRF."; return EXIT_FAILURE; } float max_size = 0; for (int i=0; i<3; ++i) if (reference_image->GetGeometry()->GetExtentInMM(i)>max_size) max_size = reference_image->GetGeometry()->GetExtentInMM(i); if (params->m_MinTractLengthMm >= max_size) { MITK_INFO << "Max. image size: " << max_size << "mm"; MITK_INFO << "Min. tract length: " << params->m_MinTractLengthMm << "mm"; MITK_ERROR << "Minimum tract length exceeds the maximum image extent! Recommended value is about 1/10 of the image extent."; return EXIT_FAILURE; } else if (params->m_MinTractLengthMm > max_size/10) { MITK_INFO << "Max. image size: " << max_size << "mm"; MITK_INFO << "Min. tract length: " << params->m_MinTractLengthMm << "mm"; MITK_WARN << "Minimum tract length is larger than 1/10 the maximum image extent! Decrease recommended."; } MITK_INFO << "Tractography algorithm: " << type; tracker->SetMaskImage(mask); tracker->SetSeedImage(seed); tracker->SetStoppingRegions(stop); tracker->SetTargetRegions(target); tracker->SetExclusionRegions(exclusion); tracker->SetTrackingHandler(handler); if (ext != ".fib" && ext != ".trk") params->m_OutputProbMap = true; tracker->SetParameters(params); tracker->Update(); if (ext == ".fib" || ext == ".trk") { vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); if (params->m_CompressFibers) { float min_sp = 999; auto spacing = handler->GetSpacing(); if (spacing[0] < min_sp) min_sp = spacing[0]; if (spacing[1] < min_sp) min_sp = spacing[1]; if (spacing[2] < min_sp) min_sp = spacing[2]; params->m_Compression = min_sp/10; outFib->Compress(params->m_Compression); } outFib->SetTrackVisHeader(reference_image->GetGeometry()); mitk::IOUtil::Save(outFib, outFile); } else { TrackerType::ItkDoubleImgType::Pointer outImg = tracker->GetOutputProbabilityMap(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); if (ext != ".nii" && ext != ".nii.gz" && ext != ".nrrd") outFile += ".nii.gz"; mitk::IOUtil::Save(img, outFile); } delete handler; return EXIT_SUCCESS; } diff --git a/Modules/DiffusionCore/IODataStructures/mitkFiberBundle.cpp b/Modules/DiffusionCore/IODataStructures/mitkFiberBundle.cpp index 828d2a6..0059159 100644 --- a/Modules/DiffusionCore/IODataStructures/mitkFiberBundle.cpp +++ b/Modules/DiffusionCore/IODataStructures/mitkFiberBundle.cpp @@ -1,2847 +1,2849 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFiberBundle.h" #include <mitkPlanarCircle.h> #include <mitkPlanarPolygon.h> #include <mitkPlanarFigureComposite.h> #include <mitkDiffusionFunctionCollection.h> #include <mitkPixelTypeMultiplex.h> #include <vtkPointData.h> #include <vtkDataArray.h> #include <vtkUnsignedCharArray.h> #include <vtkPolyLine.h> #include <vtkCellArray.h> #include <vtkCellData.h> #include <vtkIdFilter.h> #include <vtkClipPolyData.h> #include <vtkPlane.h> #include <vtkDoubleArray.h> #include <vtkKochanekSpline.h> #include <vtkParametricFunctionSource.h> #include <vtkParametricSpline.h> #include <vtkPolygon.h> #include <vtkCleanPolyData.h> #include <boost/timer/progress_display.hpp> #include <vtkTransformPolyDataFilter.h> #include <mitkTransferFunction.h> #include <vtkLookupTable.h> #include <mitkLookupTable.h> #include <vtkCardinalSpline.h> #include <vtkAppendPolyData.h> #include <random> #include <algorithm> const char* mitk::FiberBundle::FIBER_ID_ARRAY = "Fiber_IDs"; mitk::FiberBundle::FiberBundle( vtkPolyData* fiberPolyData ) : m_NumFibers(0) { m_TrackVisHeader.hdr_size = 0; m_FiberWeights = vtkSmartPointer<vtkFloatArray>::New(); m_FiberWeights->SetName("FIBER_WEIGHTS"); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); if (fiberPolyData != nullptr) m_FiberPolyData = fiberPolyData; else { this->m_FiberPolyData->SetPoints(vtkSmartPointer<vtkPoints>::New()); this->m_FiberPolyData->SetLines(vtkSmartPointer<vtkCellArray>::New()); } this->UpdateFiberGeometry(); this->GenerateFiberIds(); this->ColorFibersByOrientation(); } mitk::FiberBundle::~FiberBundle() { } mitk::FiberBundle::Pointer mitk::FiberBundle::GetDeepCopy() { mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(m_FiberPolyData); newFib->SetFiberColors(this->m_FiberColors); newFib->SetFiberWeights(this->m_FiberWeights); newFib->SetTrackVisHeader(this->GetTrackVisHeader()); return newFib; } vtkSmartPointer<vtkPolyData> mitk::FiberBundle::GeneratePolyDataByIds(std::vector<unsigned int> fiberIds, vtkSmartPointer<vtkFloatArray> weights) { vtkSmartPointer<vtkPolyData> newFiberPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> newLineSet = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> newPointSet = vtkSmartPointer<vtkPoints>::New(); weights->SetNumberOfValues(fiberIds.size()); int counter = 0; auto finIt = fiberIds.begin(); while ( finIt != fiberIds.end() ) { if (*finIt>GetNumFibers()){ MITK_INFO << "FiberID can not be negative or >NumFibers!!! check id Extraction!" << *finIt; break; } vtkSmartPointer<vtkCell> fiber = m_FiberIdDataSet->GetCell(*finIt);//->DeepCopy(fiber); vtkSmartPointer<vtkPoints> fibPoints = fiber->GetPoints(); vtkSmartPointer<vtkPolyLine> newFiber = vtkSmartPointer<vtkPolyLine>::New(); newFiber->GetPointIds()->SetNumberOfIds( fibPoints->GetNumberOfPoints() ); for(int i=0; i<fibPoints->GetNumberOfPoints(); i++) { newFiber->GetPointIds()->SetId(i, newPointSet->GetNumberOfPoints()); newPointSet->InsertNextPoint(fibPoints->GetPoint(i)[0], fibPoints->GetPoint(i)[1], fibPoints->GetPoint(i)[2]); } weights->InsertValue(counter, this->GetFiberWeight(*finIt)); newLineSet->InsertNextCell(newFiber); ++finIt; ++counter; } newFiberPolyData->SetPoints(newPointSet); newFiberPolyData->SetLines(newLineSet); return newFiberPolyData; } // merge two fiber bundles mitk::FiberBundle::Pointer mitk::FiberBundle::AddBundles(std::vector< mitk::FiberBundle::Pointer > fibs) { vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); // add current fiber bundle vtkSmartPointer<vtkFloatArray> weights = vtkSmartPointer<vtkFloatArray>::New(); auto num_weights = this->GetNumFibers(); for (auto fib : fibs) num_weights += fib->GetNumFibers(); weights->SetNumberOfValues(num_weights); unsigned int counter = 0; for (unsigned int i=0; i<m_FiberPolyData->GetNumberOfCells(); ++i) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (unsigned int j=0; j<numPoints; ++j) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, this->GetFiberWeight(i)); vNewLines->InsertNextCell(container); counter++; } for (auto fib : fibs) { // add new fiber bundle for (unsigned int i=0; i<fib->GetFiberPolyData()->GetNumberOfCells(); i++) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (unsigned int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, fib->GetFiberWeight(i)); vNewLines->InsertNextCell(container); counter++; } } // initialize PolyData vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(vNewPolyData); newFib->SetFiberWeights(weights); return newFib; } // merge two fiber bundles mitk::FiberBundle::Pointer mitk::FiberBundle::AddBundle(mitk::FiberBundle* fib) { if (fib==nullptr) return this->GetDeepCopy(); MITK_INFO << "Adding fibers"; vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); // add current fiber bundle vtkSmartPointer<vtkFloatArray> weights = vtkSmartPointer<vtkFloatArray>::New(); weights->SetNumberOfValues(this->GetNumFibers()+fib->GetNumFibers()); unsigned int counter = 0; for (unsigned int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (unsigned int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, this->GetFiberWeight(i)); vNewLines->InsertNextCell(container); counter++; } // add new fiber bundle for (unsigned int i=0; i<fib->GetFiberPolyData()->GetNumberOfCells(); i++) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (unsigned int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, fib->GetFiberWeight(i)); vNewLines->InsertNextCell(container); counter++; } // initialize PolyData vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(vNewPolyData); newFib->SetFiberWeights(weights); return newFib; } // Only retain fibers with a weight larger than the specified threshold mitk::FiberBundle::Pointer mitk::FiberBundle::FilterByWeights(float weight_thr, bool invert) { vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); std::vector<float> weights; for (unsigned int i=0; i<this->GetNumFibers(); i++) { if ( (invert && this->GetFiberWeight(i)>weight_thr) || (!invert && this->GetFiberWeight(i)<=weight_thr)) continue; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); weights.push_back(this->GetFiberWeight(i)); } // initialize PolyData vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(vNewPolyData); for (unsigned int i=0; i<weights.size(); ++i) newFib->SetFiberWeight(i, weights.at(i)); newFib->SetTrackVisHeader(this->GetTrackVisHeader()); return newFib; } // Only retain a subsample of the fibers mitk::FiberBundle::Pointer mitk::FiberBundle::SubsampleFibers(float factor, bool random_seed) { vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); unsigned int new_num_fibs = static_cast<unsigned int>(std::round(this->GetNumFibers()*factor)); MITK_INFO << "Subsampling fibers with factor " << factor << "(" << new_num_fibs << "/" << this->GetNumFibers() << ")"; // add current fiber bundle vtkSmartPointer<vtkFloatArray> weights = vtkSmartPointer<vtkFloatArray>::New(); weights->SetNumberOfValues(new_num_fibs); std::vector< unsigned int > ids; for (unsigned int i=0; i<this->GetNumFibers(); i++) ids.push_back(i); if (random_seed) std::srand(static_cast<unsigned int>(std::time(nullptr))); else std::srand(0); std::random_device rd; std::mt19937 g(rd()); std::shuffle(ids.begin(), ids.end(), g); unsigned int counter = 0; for (unsigned int i=0; i<new_num_fibs; i++) { vtkCell* cell = m_FiberPolyData->GetCell(ids.at(i)); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, this->GetFiberWeight(ids.at(i))); vNewLines->InsertNextCell(container); counter++; } // initialize PolyData vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(vNewPolyData); newFib->SetFiberWeights(weights); newFib->SetTrackVisHeader(this->GetTrackVisHeader()); return newFib; } // subtract two fiber bundles mitk::FiberBundle::Pointer mitk::FiberBundle::SubtractBundle(mitk::FiberBundle* fib) { if (fib==nullptr) return this->GetDeepCopy(); MITK_INFO << "Subtracting fibers"; vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); std::vector< std::vector< itk::Point<float, 3> > > points1; for(unsigned int i=0; i<m_NumFibers; i++ ) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; itk::Point<float, 3> start = mitk::imv::GetItkPoint(points->GetPoint(0)); itk::Point<float, 3> end = mitk::imv::GetItkPoint(points->GetPoint(numPoints-1)); points1.push_back( {start, end} ); } std::vector< std::vector< itk::Point<float, 3> > > points2; for(unsigned int i=0; i<fib->GetNumFibers(); i++ ) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; itk::Point<float, 3> start =mitk::imv::GetItkPoint(points->GetPoint(0)); itk::Point<float, 3> end =mitk::imv::GetItkPoint(points->GetPoint(numPoints-1)); points2.push_back( {start, end} ); } // int progress = 0; std::vector< int > ids; #pragma omp parallel for for (int i=0; i<static_cast<int>(points1.size()); i++) { bool match = false; for (unsigned int j=0; j<points2.size(); j++) { auto v1 = points1.at(static_cast<unsigned int>(i)); auto v2 = points2.at(j); float dist=0; for (unsigned int c=0; c<v1.size(); c++) { float d = v1[c][0]-v2[c][0]; dist += d*d; d = v1[c][1]-v2[c][1]; dist += d*d; d = v1[c][2]-v2[c][2]; dist += d*d; } dist /= v1.size(); if (dist<0.000001f) { match = true; break; } dist=0; for (unsigned int c=0; c<v1.size(); c++) { float d = v1[v1.size()-1-c][0]-v2[c][0]; dist += d*d; d = v1[v1.size()-1-c][1]-v2[c][1]; dist += d*d; d = v1[v1.size()-1-c][2]-v2[c][2]; dist += d*d; } dist /= v1.size(); if (dist<0.000001f) { match = true; break; } } #pragma omp critical if (!match) ids.push_back(i); } for( int i : ids ) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for( int j=0; j<numPoints; j++) { vtkIdType id = vNewPoints->InsertNextPoint(points->GetPoint(j)); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } if(vNewLines->GetNumberOfCells()==0) return mitk::FiberBundle::New(); // initialize PolyData vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle return mitk::FiberBundle::New(vNewPolyData); } /* * set PolyData (additional flag to recompute fiber geometry, default = true) */ void mitk::FiberBundle::SetFiberPolyData(vtkSmartPointer<vtkPolyData> fiberPD, bool updateGeometry) { if (fiberPD == nullptr) this->m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); else { m_FiberPolyData->CopyStructure(fiberPD); // m_FiberPolyData->DeepCopy(fiberPD); } m_NumFibers = static_cast<unsigned int>(m_FiberPolyData->GetNumberOfLines()); if (updateGeometry) UpdateFiberGeometry(); GenerateFiberIds(); ColorFibersByOrientation(); } /* * return vtkPolyData */ vtkSmartPointer<vtkPolyData> mitk::FiberBundle::GetFiberPolyData() const { return m_FiberPolyData; } void mitk::FiberBundle::ColorFibersByLength(bool opacity, bool weight_fibers, mitk::LookupTable::LookupTableType type) { if (m_MaxFiberLength<=0) return; auto numOfPoints = this->GetNumberOfPoints(); //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(numOfPoints * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); auto numOfFibers = m_FiberPolyData->GetNumberOfLines(); if (numOfFibers < 1) return; mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); mitkLookup->SetType(type); if (type!=mitk::LookupTable::MULTILABEL) mitkLookup->GetVtkLookupTable()->SetTableRange(m_MinFiberLength, m_MaxFiberLength); unsigned int count = 0; for (unsigned int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); float l = m_FiberLengths.at(i)/m_MaxFiberLength; double color[3]; mitkLookup->GetColor(m_FiberLengths.at(i), color); for (int j=0; j<numPoints; j++) { rgba[0] = static_cast<unsigned char>(255.0 * color[0]); rgba[1] = static_cast<unsigned char>(255.0 * color[1]); rgba[2] = static_cast<unsigned char>(255.0 * color[2]); if (opacity) rgba[3] = static_cast<unsigned char>(255.0f * l); else rgba[3] = static_cast<unsigned char>(255.0); m_FiberColors->InsertTypedTuple(cell->GetPointId(j), rgba); count++; } if (weight_fibers) this->SetFiberWeight(i, m_FiberLengths.at(i)); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorSinglePoint(int f_idx, int p_idx, double rgb[3]) { // vtkPoints* extrPoints = m_FiberPolyData->GetPoints(); // vtkIdType numOfPoints = 0; // if (extrPoints!=nullptr) // numOfPoints = extrPoints->GetNumberOfPoints(); // //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; // m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); // m_FiberColors->Allocate(numOfPoints * 4); // m_FiberColors->SetNumberOfComponents(4); // m_FiberColors->SetName("FIBER_COLORS"); // auto numOfFibers = m_FiberPolyData->GetNumberOfLines(); // if (numOfFibers < 1) // return; // /* extract single fibers of fiberBundle */ // vtkCellArray* fiberList = m_FiberPolyData->GetLines(); // fiberList->InitTraversal(); // for (int fi=0; fi<numOfFibers; ++fi) // { // vtkIdType* idList; // contains the point id's of the line // vtkIdType num_points; // number of points for current line //// fiberList->GetNextCell(num_points, idList); // fiberList->GetCell(f_idx, num_points, idList); vtkCell* cell = m_FiberPolyData->GetCell(f_idx); // /* single fiber checkpoints: is number of points valid */ // if (p_idx < num_points) // { rgba[0] = static_cast<unsigned char>(255.0 * rgb[0]); rgba[1] = static_cast<unsigned char>(255.0 * rgb[1]); rgba[2] = static_cast<unsigned char>(255.0 * rgb[2]); rgba[3] = 255; m_FiberColors->InsertTypedTuple(cell->GetPointId(p_idx), rgba); // } // } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorFibersByOrientation() { //===== FOR WRITING A TEST ======================== // colorT size == tupelComponents * tupelElements // compare color results // to cover this code 100% also PolyData needed, where colorarray already exists // + one fiber with exactly 1 point // + one fiber with 0 points //================================================= vtkPoints* extrPoints = m_FiberPolyData->GetPoints(); vtkIdType numOfPoints = 0; if (extrPoints!=nullptr) numOfPoints = extrPoints->GetNumberOfPoints(); //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(numOfPoints * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); auto numOfFibers = m_FiberPolyData->GetNumberOfLines(); if (numOfFibers < 1) return; /* extract single fibers of fiberBundle */ vtkCellArray* fiberList = m_FiberPolyData->GetLines(); fiberList->InitTraversal(); for (int fi=0; fi<numOfFibers; ++fi) { vtkIdType const* idList; // contains the point id's of the line vtkIdType pointsPerFiber; // number of points for current line fiberList->GetNextCell(pointsPerFiber, idList); /* single fiber checkpoints: is number of points valid */ if (pointsPerFiber > 1) { /* operate on points of single fiber */ for (int i=0; i <pointsPerFiber; ++i) { /* process all points elastV[0]ept starting and endpoint for calculating color value take current point, previous point and next point */ if (i<pointsPerFiber-1 && i > 0) { /* The color value of the current point is influenced by the previous point and next point. */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; vnl_vector_fixed< double, 3 > diff; diff = (diff1 - diff2) / 2.0; diff.normalize(); rgba[0] = static_cast<unsigned char>(255.0 * std::fabs(diff[0])); rgba[1] = static_cast<unsigned char>(255.0 * std::fabs(diff[1])); rgba[2] = static_cast<unsigned char>(255.0 * std::fabs(diff[2])); rgba[3] = static_cast<unsigned char>(255.0); } else if (i==0) { /* First point has no previous point, therefore only diff1 is taken */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; diff1.normalize(); rgba[0] = static_cast<unsigned char>(255.0 * std::fabs(diff1[0])); rgba[1] = static_cast<unsigned char>(255.0 * std::fabs(diff1[1])); rgba[2] = static_cast<unsigned char>(255.0 * std::fabs(diff1[2])); rgba[3] = static_cast<unsigned char>(255.0); } else if (i==pointsPerFiber-1) { /* Last point has no next point, therefore only diff2 is taken */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; diff2.normalize(); rgba[0] = static_cast<unsigned char>(255.0 * std::fabs(diff2[0])); rgba[1] = static_cast<unsigned char>(255.0 * std::fabs(diff2[1])); rgba[2] = static_cast<unsigned char>(255.0 * std::fabs(diff2[2])); rgba[3] = static_cast<unsigned char>(255.0); } m_FiberColors->InsertTypedTuple(idList[i], rgba); } } else if (pointsPerFiber == 1) { /* a single point does not define a fiber (use vertex mechanisms instead */ continue; } else { MITK_DEBUG << "Fiber with 0 points detected... please check your tractography algorithm!" ; continue; } } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorFibersByCurvature(bool opacity, bool weight_fibers, mitk::LookupTable::LookupTableType type) { double window = 5; //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); std::vector< double > values; double min = 1; double max = 0; MITK_INFO << "Coloring fibers by curvature"; boost::timer::progress_display disp(static_cast<unsigned long>(m_FiberPolyData->GetNumberOfCells())); for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); double mean_curv = 0; // calculate curvatures for (int j=0; j<numPoints; j++) { double dist = 0; int c = j; std::vector< vnl_vector_fixed< double, 3 > > vectors; vnl_vector_fixed< double, 3 > meanV; meanV.fill(0.0); while(dist<window/2 && c>1) { double p1[3]; points->GetPoint(c-1, p1); double p2[3]; points->GetPoint(c, p2); vnl_vector_fixed< double, 3 > v; v[0] = p2[0]-p1[0]; v[1] = p2[1]-p1[1]; v[2] = p2[2]-p1[2]; dist += v.magnitude(); v.normalize(); vectors.push_back(v); meanV += v; c--; } c = j; dist = 0; while(dist<window/2 && c<numPoints-1) { double p1[3]; points->GetPoint(c, p1); double p2[3]; points->GetPoint(c+1, p2); vnl_vector_fixed< double, 3 > v; v[0] = p2[0]-p1[0]; v[1] = p2[1]-p1[1]; v[2] = p2[2]-p1[2]; dist += v.magnitude(); v.normalize(); vectors.push_back(v); meanV += v; c++; } meanV.normalize(); double dev = 0; for (unsigned int c=0; c<vectors.size(); c++) { double angle = dot_product(meanV, vectors.at(c)); if (angle>1.0) angle = 1.0; if (angle<-1.0) angle = -1.0; dev += acos(angle)*180/itk::Math::pi; } if (vectors.size()>0) dev /= vectors.size(); if (weight_fibers) mean_curv += dev; dev = 1.0-dev/180.0; values.push_back(dev); if (dev<min) min = dev; if (dev>max) max = dev; } if (weight_fibers) this->SetFiberWeight(i, mean_curv/numPoints); } mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); mitkLookup->SetType(type); if (type!=mitk::LookupTable::MULTILABEL) mitkLookup->GetVtkLookupTable()->SetTableRange(min, max); unsigned int count = 0; for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); for (int j=0; j<numPoints; j++) { double color[3]; double dev = values.at(count); mitkLookup->GetColor(dev, color); rgba[0] = static_cast<unsigned char>(255.0 * color[0]); rgba[1] = static_cast<unsigned char>(255.0 * color[1]); rgba[2] = static_cast<unsigned char>(255.0 * color[2]); if (opacity) rgba[3] = static_cast<unsigned char>(255.0f * dev/max); else rgba[3] = static_cast<unsigned char>(255.0); m_FiberColors->InsertTypedTuple(cell->GetPointId(j), rgba); count++; } } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::SetFiberOpacity(vtkDoubleArray* FAValArray) { for(long i=0; i<m_FiberColors->GetNumberOfTuples(); i++) { double faValue = FAValArray->GetValue(i); faValue = faValue * 255.0; m_FiberColors->SetComponent(i,3, static_cast<unsigned char>(faValue) ); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ResetFiberOpacity() { for(long i=0; i<m_FiberColors->GetNumberOfTuples(); i++) m_FiberColors->SetComponent(i,3, 255.0 ); m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorFibersByScalarMap(mitk::Image::Pointer FAimage, bool opacity, bool weight_fibers, mitk::LookupTable::LookupTableType type, double max_cap, bool interpolate) { if (FAimage->GetPixelType().GetComponentTypeAsString()=="unsigned char" || FAimage->GetPixelType().GetComponentTypeAsString()=="char" || FAimage->GetPixelType().GetComponentTypeAsString()=="long" || FAimage->GetPixelType().GetComponentTypeAsString()=="unsigned long" || FAimage->GetPixelType().GetComponentTypeAsString()=="short" || FAimage->GetPixelType().GetComponentTypeAsString()=="unsigned short" || FAimage->GetPixelType().GetComponentTypeAsString()=="unsigned int" || FAimage->GetPixelType().GetComponentTypeAsString()=="int") { typedef itk::Image<int, 3> ImageType; ImageType::Pointer itkImage = ImageType::New(); CastToItkImage(FAimage, itkImage); ColorFibersByScalarMap<int>(itkImage, opacity, weight_fibers, type, max_cap, interpolate ); } else { typedef itk::Image<float, 3> ImageType; ImageType::Pointer itkImage = ImageType::New(); CastToItkImage(FAimage, itkImage); ColorFibersByScalarMap<float>(itkImage, opacity, weight_fibers, type, max_cap, interpolate ); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } template <typename TPixel> void mitk::FiberBundle::ColorFibersByScalarMap(typename itk::Image<TPixel, 3>::Pointer image, bool opacity, bool weight_fibers, mitk::LookupTable::LookupTableType type, double max_cap, bool interpolate) { m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); unsigned char rgba[4] = {0,0,0,0}; vtkPoints* pointSet = m_FiberPolyData->GetPoints(); if (type==mitk::LookupTable::MULTILABEL) interpolate = false; auto interpolator = itk::LinearInterpolateImageFunction< itk::Image<TPixel, 3>, float >::New(); interpolator->SetInputImage(image); double min = 999999; double max = -999999; for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); double mean_val = 0; for (int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); auto pixelValue = mitk::imv::GetImageValue<TPixel>(mitk::imv::GetItkPoint(p), interpolate, interpolator); if (pixelValue>max) max = pixelValue; if (pixelValue<min) min = pixelValue; if (weight_fibers) mean_val += pixelValue; } if (weight_fibers) this->SetFiberWeight(i, mean_val/numPoints); } mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); mitkLookup->SetType(type); if (type!=mitk::LookupTable::MULTILABEL) mitkLookup->GetVtkLookupTable()->SetTableRange(min, max*max_cap); for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++i) { itk::Point<float, 3> px; px[0] = pointSet->GetPoint(i)[0]; px[1] = pointSet->GetPoint(i)[1]; px[2] = pointSet->GetPoint(i)[2]; auto pixelValue = mitk::imv::GetImageValue<TPixel>(px, interpolate, interpolator); double color[3]; mitkLookup->GetColor(pixelValue, color); rgba[0] = static_cast<unsigned char>(255.0 * color[0]); rgba[1] = static_cast<unsigned char>(255.0 * color[1]); rgba[2] = static_cast<unsigned char>(255.0 * color[2]); if (opacity) rgba[3] = static_cast<unsigned char>(255.0 * pixelValue); else rgba[3] = static_cast<unsigned char>(255.0); m_FiberColors->InsertTypedTuple(i, rgba); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorFibersByFiberWeights(bool opacity, mitk::LookupTable::LookupTableType type) { m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); unsigned char rgba[4] = {0,0,0,0}; unsigned int counter = 0; float max = -999999; float min = 999999; for (unsigned int i=0; i<m_NumFibers; i++) { float weight = this->GetFiberWeight(i); if (weight>max) max = weight; if (weight<min) min = weight; } if (fabs(max-min)<0.00001f) { max = 1; min = 0; } mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); mitkLookup->SetType(type); if (type!=mitk::LookupTable::MULTILABEL) mitkLookup->GetVtkLookupTable()->SetTableRange(min, max); for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); auto weight = this->GetFiberWeight(i); double color[3]; mitkLookup->GetColor(weight, color); for (int j=0; j<numPoints; j++) { rgba[0] = static_cast<unsigned char>(255.0 * color[0]); rgba[1] = static_cast<unsigned char>(255.0 * color[1]); rgba[2] = static_cast<unsigned char>(255.0 * color[2]); if (opacity) rgba[3] = static_cast<unsigned char>(255.0f * weight/max); else rgba[3] = static_cast<unsigned char>(255.0); m_FiberColors->InsertTypedTuple(counter, rgba); counter++; } } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::SetFiberColors(float r, float g, float b, float alpha) { m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); unsigned char rgba[4] = {0,0,0,0}; for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++i) { rgba[0] = static_cast<unsigned char>(r); rgba[1] = static_cast<unsigned char>(g); rgba[2] = static_cast<unsigned char>(b); rgba[3] = static_cast<unsigned char>(alpha); m_FiberColors->InsertTypedTuple(i, rgba); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::GenerateFiberIds() { if (m_FiberPolyData == nullptr) return; vtkSmartPointer<vtkIdFilter> idFiberFilter = vtkSmartPointer<vtkIdFilter>::New(); idFiberFilter->SetInputData(m_FiberPolyData); idFiberFilter->CellIdsOn(); // idFiberFilter->PointIdsOn(); // point id's are not needed idFiberFilter->SetCellIdsArrayName(FIBER_ID_ARRAY); idFiberFilter->FieldDataOn(); idFiberFilter->Update(); m_FiberIdDataSet = idFiberFilter->GetOutput(); } float mitk::FiberBundle::GetNumEpFractionInMask(ItkUcharImgType* mask, bool different_label) { vtkSmartPointer<vtkPolyData> PolyData = m_FiberPolyData; MITK_INFO << "Calculating EP-Fraction"; boost::timer::progress_display disp(m_NumFibers); unsigned int in_mask = 0; for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = PolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); itk::Point<float, 3> startVertex =mitk::imv::GetItkPoint(points->GetPoint(0)); itk::Index<3> startIndex; mask->TransformPhysicalPointToIndex(startVertex, startIndex); itk::Point<float, 3> endVertex =mitk::imv::GetItkPoint(points->GetPoint(numPoints-1)); itk::Index<3> endIndex; mask->TransformPhysicalPointToIndex(endVertex, endIndex); if (mask->GetLargestPossibleRegion().IsInside(startIndex) && mask->GetLargestPossibleRegion().IsInside(endIndex)) { float v1 = mask->GetPixel(startIndex); if (v1 < 0.5f) continue; float v2 = mask->GetPixel(startIndex); if (v2 < 0.5f) continue; if (!different_label) ++in_mask; else if (fabs(v1-v2)>0.00001f) ++in_mask; } } return float(in_mask)/m_NumFibers; } std::tuple<float, float> mitk::FiberBundle::GetDirectionalOverlap(ItkUcharImgType* mask, mitk::PeakImage::ItkPeakImageType* peak_image) { vtkSmartPointer<vtkPolyData> PolyData = m_FiberPolyData; MITK_INFO << "Calculating overlap"; auto spacing = mask->GetSpacing(); boost::timer::progress_display disp(m_NumFibers); double length_sum = 0; double in_mask_length = 0; double aligned_length = 0; for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = PolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints-1; j++) { itk::Point<float, 3> startVertex =mitk::imv::GetItkPoint(points->GetPoint(j)); itk::Index<3> startIndex; itk::ContinuousIndex<float, 3> startIndexCont; mask->TransformPhysicalPointToIndex(startVertex, startIndex); mask->TransformPhysicalPointToContinuousIndex(startVertex, startIndexCont); itk::Point<float, 3> endVertex =mitk::imv::GetItkPoint(points->GetPoint(j + 1)); itk::Index<3> endIndex; itk::ContinuousIndex<float, 3> endIndexCont; mask->TransformPhysicalPointToIndex(endVertex, endIndex); mask->TransformPhysicalPointToContinuousIndex(endVertex, endIndexCont); vnl_vector_fixed< float, 3 > fdir; fdir[0] = endVertex[0] - startVertex[0]; fdir[1] = endVertex[1] - startVertex[1]; fdir[2] = endVertex[2] - startVertex[2]; fdir.normalize(); std::vector< std::pair< itk::Index<3>, double > > segments = mitk::imv::IntersectImage(spacing, startIndex, endIndex, startIndexCont, endIndexCont); for (std::pair< itk::Index<3>, double > segment : segments) { if ( mask->GetLargestPossibleRegion().IsInside(segment.first) && mask->GetPixel(segment.first) > 0 ) { in_mask_length += segment.second; mitk::PeakImage::ItkPeakImageType::IndexType idx4; idx4[0] = segment.first[0]; idx4[1] = segment.first[1]; idx4[2] = segment.first[2]; vnl_vector_fixed< float, 3 > peak; idx4[3] = 0; peak[0] = peak_image->GetPixel(idx4); idx4[3] = 1; peak[1] = peak_image->GetPixel(idx4); idx4[3] = 2; peak[2] = peak_image->GetPixel(idx4); if (std::isnan(peak[0]) || std::isnan(peak[1]) || std::isnan(peak[2]) || peak.magnitude()<0.0001f) continue; peak.normalize(); double f = 1.0 - std::acos(std::fabs(static_cast<double>(dot_product(fdir, peak)))) * 2.0/itk::Math::pi; aligned_length += segment.second * f; } length_sum += segment.second; } } } if (length_sum<=0.0001) { MITK_INFO << "Fiber length sum is zero!"; return std::make_tuple(0,0); } return std::make_tuple(aligned_length/length_sum, in_mask_length/length_sum); } float mitk::FiberBundle::GetOverlap(ItkUcharImgType* mask) { vtkSmartPointer<vtkPolyData> PolyData = m_FiberPolyData; MITK_INFO << "Calculating overlap"; auto spacing = mask->GetSpacing(); boost::timer::progress_display disp(m_NumFibers); double length_sum = 0; double in_mask_length = 0; for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = PolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints-1; j++) { itk::Point<float, 3> startVertex =mitk::imv::GetItkPoint(points->GetPoint(j)); itk::Index<3> startIndex; itk::ContinuousIndex<float, 3> startIndexCont; mask->TransformPhysicalPointToIndex(startVertex, startIndex); mask->TransformPhysicalPointToContinuousIndex(startVertex, startIndexCont); itk::Point<float, 3> endVertex =mitk::imv::GetItkPoint(points->GetPoint(j + 1)); itk::Index<3> endIndex; itk::ContinuousIndex<float, 3> endIndexCont; mask->TransformPhysicalPointToIndex(endVertex, endIndex); mask->TransformPhysicalPointToContinuousIndex(endVertex, endIndexCont); std::vector< std::pair< itk::Index<3>, double > > segments = mitk::imv::IntersectImage(spacing, startIndex, endIndex, startIndexCont, endIndexCont); for (std::pair< itk::Index<3>, double > segment : segments) { if ( mask->GetLargestPossibleRegion().IsInside(segment.first) && mask->GetPixel(segment.first) > 0 ) in_mask_length += segment.second; length_sum += segment.second; } } } if (length_sum<=0.000001) { MITK_INFO << "Fiber length sum is zero!"; return 0; } return static_cast<float>(in_mask_length/length_sum); } mitk::FiberBundle::Pointer mitk::FiberBundle::RemoveFibersOutside(ItkUcharImgType* mask, bool invert) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); std::vector< float > fib_weights; MITK_INFO << "Cutting fibers"; boost::timer::progress_display disp(m_NumFibers); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); int newNumPoints = 0; if (numPoints>1) { for (int j=0; j<numPoints; j++) { itk::Point<float, 3> itkP =mitk::imv::GetItkPoint(points->GetPoint(j)); itk::Index<3> idx; mask->TransformPhysicalPointToIndex(itkP, idx); bool inside = false; if ( mask->GetLargestPossibleRegion().IsInside(idx) && mask->GetPixel(idx)!=0 ) inside = true; if (inside && !invert) { vtkIdType id = vtkNewPoints->InsertNextPoint(itkP.GetDataPointer()); container->GetPointIds()->InsertNextId(id); newNumPoints++; } else if ( !inside && invert ) { vtkIdType id = vtkNewPoints->InsertNextPoint(itkP.GetDataPointer()); container->GetPointIds()->InsertNextId(id); newNumPoints++; } else if (newNumPoints>1) { fib_weights.push_back(this->GetFiberWeight(i)); vtkNewCells->InsertNextCell(container); newNumPoints = 0; container = vtkSmartPointer<vtkPolyLine>::New(); } else { newNumPoints = 0; container = vtkSmartPointer<vtkPolyLine>::New(); } } if (newNumPoints>1) { fib_weights.push_back(this->GetFiberWeight(i)); vtkNewCells->InsertNextCell(container); } } } vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(static_cast<vtkIdType>(fib_weights.size())); if (vtkNewCells->GetNumberOfCells()<=0) return nullptr; for (unsigned int i=0; i<newFiberWeights->GetNumberOfValues(); i++) newFiberWeights->SetValue(i, fib_weights.at(i)); // vtkSmartPointer<vtkUnsignedCharArray> newFiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); // newFiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); // newFiberColors->SetNumberOfComponents(4); // newFiberColors->SetName("FIBER_COLORS"); // unsigned char rgba[4] = {0,0,0,0}; // for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++i) // { // rgba[0] = (unsigned char) r; // rgba[1] = (unsigned char) g; // rgba[2] = (unsigned char) b; // rgba[3] = (unsigned char) alpha; // m_FiberColors->InsertTypedTuple(i, rgba); // } vtkSmartPointer<vtkPolyData> newPolyData = vtkSmartPointer<vtkPolyData>::New(); newPolyData->SetPoints(vtkNewPoints); newPolyData->SetLines(vtkNewCells); mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(newPolyData); newFib->SetFiberWeights(newFiberWeights); // newFib->Compress(0.1); newFib->SetTrackVisHeader(this->GetTrackVisHeader()); return newFib; } mitk::FiberBundle::Pointer mitk::FiberBundle::ExtractFiberSubset(DataNode* roi, DataStorage* storage) { if (roi==nullptr || !(dynamic_cast<PlanarFigure*>(roi->GetData()) || dynamic_cast<PlanarFigureComposite*>(roi->GetData())) ) return nullptr; std::vector<unsigned int> tmp = ExtractFiberIdSubset(roi, storage); if (tmp.size()<=0) return mitk::FiberBundle::New(); vtkSmartPointer<vtkFloatArray> weights = vtkSmartPointer<vtkFloatArray>::New(); vtkSmartPointer<vtkPolyData> pTmp = GeneratePolyDataByIds(tmp, weights); mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(pTmp); fib->SetFiberWeights(weights); fib->SetTrackVisHeader(this->GetTrackVisHeader()); return fib; } std::vector<unsigned int> mitk::FiberBundle::ExtractFiberIdSubset(DataNode *roi, DataStorage* storage) { std::vector<unsigned int> result; if (roi==nullptr || roi->GetData()==nullptr) return result; mitk::PlanarFigureComposite::Pointer pfc = dynamic_cast<mitk::PlanarFigureComposite*>(roi->GetData()); if (!pfc.IsNull()) // handle composite { DataStorage::SetOfObjects::ConstPointer children = storage->GetDerivations(roi); if (children->size()==0) return result; switch (pfc->getOperationType()) { case 0: // AND { MITK_INFO << "AND"; result = this->ExtractFiberIdSubset(children->ElementAt(0), storage); std::vector<unsigned int>::iterator it; for (unsigned int i=1; i<children->Size(); ++i) { std::vector<unsigned int> inRoi = this->ExtractFiberIdSubset(children->ElementAt(i), storage); std::vector<unsigned int> rest(std::min(result.size(),inRoi.size())); it = std::set_intersection(result.begin(), result.end(), inRoi.begin(), inRoi.end(), rest.begin() ); rest.resize( static_cast<unsigned int>(it - rest.begin()) ); result = rest; } break; } case 1: // OR { MITK_INFO << "OR"; result = ExtractFiberIdSubset(children->ElementAt(0), storage); std::vector<unsigned int>::iterator it; for (unsigned int i=1; i<children->Size(); ++i) { it = result.end(); std::vector<unsigned int> inRoi = ExtractFiberIdSubset(children->ElementAt(i), storage); result.insert(it, inRoi.begin(), inRoi.end()); } // remove duplicates sort(result.begin(), result.end()); it = unique(result.begin(), result.end()); result.resize( static_cast<unsigned int>(it - result.begin()) ); break; } case 2: // NOT { MITK_INFO << "NOT"; for(unsigned int i=0; i<this->GetNumFibers(); i++) result.push_back(i); std::vector<unsigned int>::iterator it; for (unsigned int i=0; i<children->Size(); ++i) { std::vector<unsigned int> inRoi = ExtractFiberIdSubset(children->ElementAt(i), storage); std::vector<unsigned int> rest(result.size()-inRoi.size()); it = std::set_difference(result.begin(), result.end(), inRoi.begin(), inRoi.end(), rest.begin() ); rest.resize( static_cast<unsigned int>(it - rest.begin()) ); result = rest; } break; } } } else if ( dynamic_cast<mitk::PlanarFigure*>(roi->GetData()) ) // actual extraction { if ( dynamic_cast<mitk::PlanarPolygon*>(roi->GetData()) ) { mitk::PlanarFigure::Pointer planarPoly = dynamic_cast<mitk::PlanarFigure*>(roi->GetData()); //create vtkPolygon using controlpoints from planarFigure polygon vtkSmartPointer<vtkPolygon> polygonVtk = vtkSmartPointer<vtkPolygon>::New(); for (unsigned int i=0; i<planarPoly->GetNumberOfControlPoints(); ++i) { itk::Point<double,3> p = planarPoly->GetWorldControlPoint(i); vtkIdType id = polygonVtk->GetPoints()->InsertNextPoint(p[0], p[1], p[2] ); polygonVtk->GetPointIds()->InsertNextId(id); } MITK_INFO << "Extracting with polygon"; boost::timer::progress_display disp(m_NumFibers); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints-1; j++) { // Inputs double p1[3] = {0,0,0}; points->GetPoint(j, p1); double p2[3] = {0,0,0}; points->GetPoint(j+1, p2); double tolerance = 0.001; // Outputs double t = 0; // Parametric coordinate of intersection (0 (corresponding to p1) to 1 (corresponding to p2)) double x[3] = {0,0,0}; // The coordinate of the intersection double pcoords[3] = {0,0,0}; int subId = 0; int iD = polygonVtk->IntersectWithLine(p1, p2, tolerance, t, x, pcoords, subId); if (iD!=0) { result.push_back(i); break; } } } } else if ( dynamic_cast<mitk::PlanarCircle*>(roi->GetData()) ) { mitk::PlanarFigure::Pointer planarFigure = dynamic_cast<mitk::PlanarFigure*>(roi->GetData()); Vector3D planeNormal = planarFigure->GetPlaneGeometry()->GetNormal(); planeNormal.Normalize(); //calculate circle radius mitk::Point3D V1w = planarFigure->GetWorldControlPoint(0); //centerPoint mitk::Point3D V2w = planarFigure->GetWorldControlPoint(1); //radiusPoint double radius = V1w.EuclideanDistanceTo(V2w); radius *= radius; MITK_INFO << "Extracting with circle"; boost::timer::progress_display disp(m_NumFibers); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints-1; j++) { // Inputs double p1[3] = {0,0,0}; points->GetPoint(j, p1); double p2[3] = {0,0,0}; points->GetPoint(j+1, p2); // Outputs double t = 0; // Parametric coordinate of intersection (0 (corresponding to p1) to 1 (corresponding to p2)) double x[3] = {0,0,0}; // The coordinate of the intersection int iD = vtkPlane::IntersectWithLine(p1,p2,planeNormal.GetDataPointer(),V1w.GetDataPointer(),t,x); if (iD!=0) { double dist = (x[0]-V1w[0])*(x[0]-V1w[0])+(x[1]-V1w[1])*(x[1]-V1w[1])+(x[2]-V1w[2])*(x[2]-V1w[2]); if( dist <= radius) { result.push_back(i); break; } } } } } return result; } return result; } void mitk::FiberBundle::UpdateFiberGeometry() { vtkSmartPointer<vtkCleanPolyData> cleaner = vtkSmartPointer<vtkCleanPolyData>::New(); cleaner->SetInputData(m_FiberPolyData); cleaner->PointMergingOff(); cleaner->Update(); m_FiberPolyData = cleaner->GetOutput(); m_FiberLengths.clear(); m_MeanFiberLength = 0; m_MedianFiberLength = 0; m_LengthStDev = 0; m_NumFibers = static_cast<unsigned int>(m_FiberPolyData->GetNumberOfCells()); if (m_FiberColors==nullptr || m_FiberColors->GetNumberOfTuples()!=m_FiberPolyData->GetNumberOfPoints()) this->ColorFibersByOrientation(); if (m_FiberWeights->GetNumberOfValues()!=m_NumFibers) { m_FiberWeights = vtkSmartPointer<vtkFloatArray>::New(); m_FiberWeights->SetName("FIBER_WEIGHTS"); m_FiberWeights->SetNumberOfValues(m_NumFibers); this->SetFiberWeights(1); } if (m_NumFibers<=0) // no fibers present; apply default geometry { m_MinFiberLength = 0; m_MaxFiberLength = 0; mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetImageGeometry(false); float b[] = {0, 1, 0, 1, 0, 1}; geometry->SetFloatBounds(b); SetGeometry(geometry); return; } double b[6]; m_FiberPolyData->GetBounds(b); // calculate statistics for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto p = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); float length = 0; for (int j=0; j<p-1; j++) { double p1[3]; points->GetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2])); length += static_cast<float>(dist); } m_FiberLengths.push_back(length); m_MeanFiberLength += length; if (i==0) { m_MinFiberLength = length; m_MaxFiberLength = length; } else { if (length<m_MinFiberLength) m_MinFiberLength = length; if (length>m_MaxFiberLength) m_MaxFiberLength = length; } } m_MeanFiberLength /= m_NumFibers; std::vector< float > sortedLengths = m_FiberLengths; std::sort(sortedLengths.begin(), sortedLengths.end()); for (unsigned int i=0; i<m_NumFibers; i++) m_LengthStDev += (m_MeanFiberLength-sortedLengths.at(i))*(m_MeanFiberLength-sortedLengths.at(i)); if (m_NumFibers>1) m_LengthStDev /= (m_NumFibers-1); else m_LengthStDev = 0; m_LengthStDev = std::sqrt(m_LengthStDev); m_MedianFiberLength = sortedLengths.at(m_NumFibers/2); mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetFloatBounds(b); this->SetGeometry(geometry); GetTrackVisHeader(); m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } float mitk::FiberBundle::GetFiberWeight(unsigned int fiber) const { return m_FiberWeights->GetValue(fiber); } void mitk::FiberBundle::SetFiberWeights(float newWeight) { for (int i=0; i<m_FiberWeights->GetNumberOfValues(); i++) m_FiberWeights->SetValue(i, newWeight); } void mitk::FiberBundle::SetFiberWeights(vtkSmartPointer<vtkFloatArray> weights) { if (m_NumFibers!=weights->GetNumberOfValues()) { MITK_INFO << "Weights array not equal to number of fibers! " << weights->GetNumberOfValues() << " vs " << m_NumFibers; return; } for (int i=0; i<weights->GetNumberOfValues(); i++) m_FiberWeights->SetValue(i, weights->GetValue(i)); m_FiberWeights->SetName("FIBER_WEIGHTS"); } void mitk::FiberBundle::SetFiberWeight(unsigned int fiber, float weight) { m_FiberWeights->SetValue(fiber, weight); } void mitk::FiberBundle::SetFiberColors(vtkSmartPointer<vtkUnsignedCharArray> fiberColors) { for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++i) { unsigned char source[4] = {0,0,0,0}; fiberColors->GetTypedTuple(i, source); unsigned char target[4] = {0,0,0,0}; target[0] = source[0]; target[1] = source[1]; target[2] = source[2]; target[3] = source[3]; m_FiberColors->InsertTypedTuple(i, target); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } itk::Matrix< double, 3, 3 > mitk::FiberBundle::TransformMatrix(itk::Matrix< double, 3, 3 > m, double rx, double ry, double rz) { rx = rx*itk::Math::pi/180; ry = ry*itk::Math::pi/180; rz = rz*itk::Math::pi/180; itk::Matrix< double, 3, 3 > rotX; rotX.SetIdentity(); rotX[1][1] = cos(rx); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(rx); rotX[2][1] = -rotX[1][2]; itk::Matrix< double, 3, 3 > rotY; rotY.SetIdentity(); rotY[0][0] = cos(ry); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(ry); rotY[2][0] = -rotY[0][2]; itk::Matrix< double, 3, 3 > rotZ; rotZ.SetIdentity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; itk::Matrix< double, 3, 3 > rot = rotZ*rotY*rotX; m = rot*m; return m; } void mitk::FiberBundle::TransformFibers(itk::ScalableAffineTransform< mitk::ScalarType >::Pointer transform) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { itk::Point<float, 3> p =mitk::imv::GetItkPoint(points->GetPoint(j)); p = transform->TransformPoint(p); vtkIdType id = vtkNewPoints->InsertNextPoint(p.GetDataPointer()); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::TransformFibers(double rx, double ry, double rz, double tx, double ty, double tz) { vnl_matrix_fixed< double, 3, 3 > rot = mitk::imv::GetRotationMatrixVnl(rx, ry, rz); mitk::BaseGeometry::Pointer geom = this->GetGeometry(); mitk::Point3D center = geom->GetCenter(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vnl_vector_fixed< double, 3 > dir; dir[0] = p[0]-center[0]; dir[1] = p[1]-center[1]; dir[2] = p[2]-center[2]; dir = rot*dir; dir[0] += center[0]+tx; dir[1] += center[1]+ty; dir[2] += center[2]+tz; vtkIdType id = vtkNewPoints->InsertNextPoint(dir.data_block()); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::RotateAroundAxis(double x, double y, double z) { x = x*itk::Math::pi/180; y = y*itk::Math::pi/180; z = z*itk::Math::pi/180; vnl_matrix_fixed< double, 3, 3 > rotX; rotX.set_identity(); rotX[1][1] = cos(x); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(x); rotX[2][1] = -rotX[1][2]; vnl_matrix_fixed< double, 3, 3 > rotY; rotY.set_identity(); rotY[0][0] = cos(y); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(y); rotY[2][0] = -rotY[0][2]; vnl_matrix_fixed< double, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(z); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(z); rotZ[1][0] = -rotZ[0][1]; mitk::BaseGeometry::Pointer geom = this->GetGeometry(); mitk::Point3D center = geom->GetCenter(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vnl_vector_fixed< double, 3 > dir; dir[0] = p[0]-center[0]; dir[1] = p[1]-center[1]; dir[2] = p[2]-center[2]; dir = rotZ*rotY*rotX*dir; dir[0] += center[0]; dir[1] += center[1]; dir[2] += center[2]; vtkIdType id = vtkNewPoints->InsertNextPoint(dir.data_block()); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); + auto colors = GetFiberColors(); this->SetFiberPolyData(m_FiberPolyData, true); + SetFiberColors(colors); } void mitk::FiberBundle::ScaleFibers(double x, double y, double z, bool subtractCenter) { MITK_INFO << "Scaling fibers"; boost::timer::progress_display disp(m_NumFibers); mitk::BaseGeometry* geom = this->GetGeometry(); mitk::Point3D c = geom->GetCenter(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); if (subtractCenter) { p[0] -= c[0]; p[1] -= c[1]; p[2] -= c[2]; } p[0] *= x; p[1] *= y; p[2] *= z; if (subtractCenter) { p[0] += c[0]; p[1] += c[1]; p[2] += c[2]; } vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::TranslateFibers(double x, double y, double z) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); p[0] += x; p[1] += y; p[2] += z; vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::MirrorFibers(unsigned int axis) { if (axis>2) return; MITK_INFO << "Mirroring fibers"; boost::timer::progress_display disp(m_NumFibers); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); p[axis] = -p[axis]; vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::RemoveDir(vnl_vector_fixed<double,3> dir, double threshold) { dir.normalize(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); boost::timer::progress_display disp(static_cast<unsigned long>(m_FiberPolyData->GetNumberOfCells())); for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); bool discard = false; for (int j=0; j<numPoints-1; j++) { double p1[3]; points->GetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); vnl_vector_fixed< double, 3 > v1; v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; if (v1.magnitude()>0.001) { v1.normalize(); if (fabs(dot_product(v1,dir))>threshold) { discard = true; break; } } } if (!discard) { for (int j=0; j<numPoints; j++) { double p1[3]; points->GetPoint(j, p1); vtkIdType id = vtkNewPoints->InsertNextPoint(p1); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); // UpdateColorCoding(); // UpdateFiberGeometry(); } bool mitk::FiberBundle::ApplyCurvatureThreshold(float minRadius, bool deleteFibers) { if (minRadius<0) return true; vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Applying curvature threshold"; boost::timer::progress_display disp(static_cast<unsigned long>(m_FiberPolyData->GetNumberOfCells())); for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints-2; j++) { double p1[3]; points->GetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); double p3[3]; points->GetPoint(j+2, p3); vnl_vector_fixed< float, 3 > v1, v2, v3; v1[0] = static_cast<float>(p2[0]-p1[0]); v1[1] = static_cast<float>(p2[1]-p1[1]); v1[2] = static_cast<float>(p2[2]-p1[2]); v2[0] = static_cast<float>(p3[0]-p2[0]); v2[1] = static_cast<float>(p3[1]-p2[1]); v2[2] = static_cast<float>(p3[2]-p2[2]); v3[0] = static_cast<float>(p1[0]-p3[0]); v3[1] = static_cast<float>(p1[1]-p3[1]); v3[2] = static_cast<float>(p1[2]-p3[2]); float a = v1.magnitude(); float b = v2.magnitude(); float c = v3.magnitude(); float r = a*b*c/std::sqrt((a+b+c)*(a+b-c)*(b+c-a)*(a-b+c)); // radius of triangle via Heron's formula (area of triangle) vtkIdType id = vtkNewPoints->InsertNextPoint(p1); container->GetPointIds()->InsertNextId(id); if (deleteFibers && r<minRadius) break; if (r<minRadius) { j += 2; vtkNewCells->InsertNextCell(container); container = vtkSmartPointer<vtkPolyLine>::New(); } else if (j==numPoints-3) { id = vtkNewPoints->InsertNextPoint(p2); container->GetPointIds()->InsertNextId(id); id = vtkNewPoints->InsertNextPoint(p3); container->GetPointIds()->InsertNextId(id); vtkNewCells->InsertNextCell(container); } } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); return true; } bool mitk::FiberBundle::RemoveShortFibers(float lengthInMM) { MITK_INFO << "Removing short fibers"; if (lengthInMM<=0 || lengthInMM<m_MinFiberLength) { MITK_INFO << "No fibers shorter than " << lengthInMM << " mm found!"; return true; } if (lengthInMM>m_MaxFiberLength) // can't remove all fibers { MITK_WARN << "Process aborted. No fibers would be left!"; return false; } vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); float min = m_MaxFiberLength; boost::timer::progress_display disp(m_NumFibers); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (m_FiberLengths.at(i)>=lengthInMM) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); if (m_FiberLengths.at(i)<min) min = m_FiberLengths.at(i); } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); return true; } bool mitk::FiberBundle::RemoveLongFibers(float lengthInMM) { if (lengthInMM<=0 || lengthInMM>m_MaxFiberLength) return true; if (lengthInMM<m_MinFiberLength) // can't remove all fibers return false; vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Removing long fibers"; boost::timer::progress_display disp(m_NumFibers); for (unsigned int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (m_FiberLengths.at(i)<=lengthInMM) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); return true; } void mitk::FiberBundle::ResampleSpline(float pointDistance, double tension, double continuity, double bias ) { if (pointDistance<=0) return; vtkSmartPointer<vtkPoints> vtkSmoothPoints = vtkSmartPointer<vtkPoints>::New(); //in smoothpoints the interpolated points representing a fiber are stored. //in vtkcells all polylines are stored, actually all id's of them are stored vtkSmartPointer<vtkCellArray> vtkSmoothCells = vtkSmartPointer<vtkCellArray>::New(); //cellcontainer for smoothed lines MITK_INFO << "Smoothing fibers"; vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); std::vector< vtkSmartPointer<vtkPolyLine> > resampled_streamlines; resampled_streamlines.resize(m_NumFibers); boost::timer::progress_display disp(m_NumFibers); #pragma omp parallel for for (int i=0; i<static_cast<int>(m_NumFibers); i++) { vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New(); float length = 0; #pragma omp critical { length = m_FiberLengths.at(static_cast<unsigned int>(i)); ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) newPoints->InsertNextPoint(points->GetPoint(j)); } int sampling = static_cast<int>(std::ceil(length/pointDistance)); vtkSmartPointer<vtkKochanekSpline> xSpline = vtkSmartPointer<vtkKochanekSpline>::New(); vtkSmartPointer<vtkKochanekSpline> ySpline = vtkSmartPointer<vtkKochanekSpline>::New(); vtkSmartPointer<vtkKochanekSpline> zSpline = vtkSmartPointer<vtkKochanekSpline>::New(); xSpline->SetDefaultBias(bias); xSpline->SetDefaultTension(tension); xSpline->SetDefaultContinuity(continuity); ySpline->SetDefaultBias(bias); ySpline->SetDefaultTension(tension); ySpline->SetDefaultContinuity(continuity); zSpline->SetDefaultBias(bias); zSpline->SetDefaultTension(tension); zSpline->SetDefaultContinuity(continuity); vtkSmartPointer<vtkParametricSpline> spline = vtkSmartPointer<vtkParametricSpline>::New(); spline->SetXSpline(xSpline); spline->SetYSpline(ySpline); spline->SetZSpline(zSpline); spline->SetPoints(newPoints); vtkSmartPointer<vtkParametricFunctionSource> functionSource = vtkSmartPointer<vtkParametricFunctionSource>::New(); functionSource->SetParametricFunction(spline); functionSource->SetUResolution(sampling); functionSource->SetVResolution(sampling); functionSource->SetWResolution(sampling); functionSource->Update(); vtkPolyData* outputFunction = functionSource->GetOutput(); vtkPoints* tmpSmoothPnts = outputFunction->GetPoints(); //smoothPoints of current fiber vtkSmartPointer<vtkPolyLine> smoothLine = vtkSmartPointer<vtkPolyLine>::New(); #pragma omp critical { for (int j=0; j<tmpSmoothPnts->GetNumberOfPoints(); j++) { vtkIdType id = vtkSmoothPoints->InsertNextPoint(tmpSmoothPnts->GetPoint(j)); smoothLine->GetPointIds()->InsertNextId(id); } resampled_streamlines[static_cast<unsigned long>(i)] = smoothLine; } } for (auto container : resampled_streamlines) { vtkSmoothCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkSmoothPoints); m_FiberPolyData->SetLines(vtkSmoothCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::ResampleSpline(float pointDistance) { ResampleSpline(pointDistance, 0, 0, 0 ); } unsigned int mitk::FiberBundle::GetNumberOfPoints() const { unsigned int points = 0; for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); points += cell->GetNumberOfPoints(); } return points; } void mitk::FiberBundle::Compress(float error) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Compressing fibers with max. error " << error << "mm"; unsigned int numRemovedPoints = 0; boost::timer::progress_display disp(static_cast<unsigned long>(m_FiberPolyData->GetNumberOfCells())); vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); #pragma omp parallel for for (int i=0; i<static_cast<int>(m_FiberPolyData->GetNumberOfCells()); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; float weight = 1; #pragma omp critical { ++disp; weight = m_FiberWeights->GetValue(i); vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) { double cand[3]; points->GetPoint(j, cand); vnl_vector_fixed< double, 3 > candV; candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; vertices.push_back(candV); } } // calculate curvatures auto numPoints = vertices.size(); std::vector< int > removedPoints; removedPoints.resize(numPoints, 0); removedPoints[0]=-1; removedPoints[numPoints-1]=-1; vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); unsigned int remCounter = 0; bool pointFound = true; while (pointFound) { pointFound = false; double minError = static_cast<double>(error); unsigned int removeIndex = 0; for (unsigned int j=0; j<vertices.size(); j++) { if (removedPoints[j]==0) { vnl_vector_fixed< double, 3 > candV = vertices.at(j); int validP = -1; vnl_vector_fixed< double, 3 > pred; for (int k=static_cast<int>(j)-1; k>=0; k--) if (removedPoints[static_cast<unsigned int>(k)]<=0) { pred = vertices.at(static_cast<unsigned int>(k)); validP = k; break; } int validS = -1; vnl_vector_fixed< double, 3 > succ; for (unsigned int k=j+1; k<numPoints; k++) if (removedPoints[k]<=0) { succ = vertices.at(k); validS = static_cast<int>(k); break; } if (validP>=0 && validS>=0) { double a = (candV-pred).magnitude(); double b = (candV-succ).magnitude(); double c = (pred-succ).magnitude(); double s=0.5*(a+b+c); double hc=(2.0/c)*sqrt(fabs(s*(s-a)*(s-b)*(s-c))); if (hc<minError) { removeIndex = j; minError = hc; pointFound = true; } } } } if (pointFound) { removedPoints[removeIndex] = 1; remCounter++; } } for (unsigned int j=0; j<numPoints; j++) { if (removedPoints[j]<=0) { #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(vertices.at(j).data_block()); container->GetPointIds()->InsertNextId(id); } } } #pragma omp critical { newFiberWeights->SetValue(vtkNewCells->GetNumberOfCells(), weight); numRemovedPoints += remCounter; vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()>0) { MITK_INFO << "Removed points: " << numRemovedPoints; SetFiberWeights(newFiberWeights); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } } void mitk::FiberBundle::ResampleToNumPoints(unsigned int targetPoints) { if (targetPoints<2) mitkThrow() << "Minimum two points required for resampling!"; MITK_INFO << "Resampling fibers (number of points " << targetPoints << ")"; bool unequal_fibs = true; while (unequal_fibs) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); unequal_fibs = false; for (unsigned int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; float weight = 1; double seg_len = 0; { weight = m_FiberWeights->GetValue(i); vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); if (numPoints!=targetPoints) seg_len = static_cast<double>(this->GetFiberLength(i)/(targetPoints-1)); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) { double cand[3]; points->GetPoint(j, cand); vnl_vector_fixed< double, 3 > candV; candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; vertices.push_back(candV); } } vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); vnl_vector_fixed< double, 3 > lastV = vertices.at(0); { vtkIdType id = vtkNewPoints->InsertNextPoint(lastV.data_block()); container->GetPointIds()->InsertNextId(id); } for (unsigned int j=1; j<vertices.size(); j++) { vnl_vector_fixed< double, 3 > vec = vertices.at(j) - lastV; double new_dist = vec.magnitude(); if (new_dist >= seg_len && seg_len>0) { vnl_vector_fixed< double, 3 > newV = lastV; if ( new_dist-seg_len <= mitk::eps ) { vec.normalize(); newV += vec * seg_len; } else { // intersection between sphere (radius 'pointDistance', center 'lastV') and line (direction 'd' and point 'p') vnl_vector_fixed< double, 3 > p = vertices.at(j-1); vnl_vector_fixed< double, 3 > d = vertices.at(j) - p; double a = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; double b = 2 * (d[0] * (p[0] - lastV[0]) + d[1] * (p[1] - lastV[1]) + d[2] * (p[2] - lastV[2])); double c = (p[0] - lastV[0])*(p[0] - lastV[0]) + (p[1] - lastV[1])*(p[1] - lastV[1]) + (p[2] - lastV[2])*(p[2] - lastV[2]) - seg_len*seg_len; double v1 =(-b + std::sqrt(b*b-4*a*c))/(2*a); double v2 =(-b - std::sqrt(b*b-4*a*c))/(2*a); if (v1>0) newV = p + d * v1; else if (v2>0) newV = p + d * v2; else MITK_INFO << "ERROR1 - linear resampling"; j--; } //#pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(newV.data_block()); container->GetPointIds()->InsertNextId(id); } lastV = newV; } else if ( (j==vertices.size()-1 && new_dist>0.0001) || seg_len<=0.0000001) { //#pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(vertices.at(j).data_block()); container->GetPointIds()->InsertNextId(id); } } } //#pragma omp critical { newFiberWeights->SetValue(vtkNewCells->GetNumberOfCells(), weight); vtkNewCells->InsertNextCell(container); if (container->GetNumberOfPoints()!=targetPoints) unequal_fibs = true; } } if (vtkNewCells->GetNumberOfCells()>0) { SetFiberWeights(newFiberWeights); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } } } void mitk::FiberBundle::ResampleLinear(double pointDistance) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Resampling fibers (linear)"; boost::timer::progress_display disp(static_cast<unsigned long>(m_FiberPolyData->GetNumberOfCells())); vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); std::vector< vtkSmartPointer<vtkPolyLine> > resampled_streamlines; resampled_streamlines.resize(static_cast<unsigned long>(m_FiberPolyData->GetNumberOfCells())); #pragma omp parallel for for (int i=0; i<static_cast<int>(m_FiberPolyData->GetNumberOfCells()); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; #pragma omp critical { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) { double cand[3]; points->GetPoint(j, cand); vnl_vector_fixed< double, 3 > candV; candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; vertices.push_back(candV); } } vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); vnl_vector_fixed< double, 3 > lastV = vertices.at(0); #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(lastV.data_block()); container->GetPointIds()->InsertNextId(id); } for (unsigned int j=1; j<vertices.size(); j++) { vnl_vector_fixed< double, 3 > vec = vertices.at(j) - lastV; double new_dist = vec.magnitude(); if (new_dist >= pointDistance) { vnl_vector_fixed< double, 3 > newV = lastV; if ( new_dist-pointDistance <= mitk::eps ) { vec.normalize(); newV += vec * pointDistance; } else { // intersection between sphere (radius 'pointDistance', center 'lastV') and line (direction 'd' and point 'p') vnl_vector_fixed< double, 3 > p = vertices.at(j-1); vnl_vector_fixed< double, 3 > d = vertices.at(j) - p; double a = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; double b = 2 * (d[0] * (p[0] - lastV[0]) + d[1] * (p[1] - lastV[1]) + d[2] * (p[2] - lastV[2])); double c = (p[0] - lastV[0])*(p[0] - lastV[0]) + (p[1] - lastV[1])*(p[1] - lastV[1]) + (p[2] - lastV[2])*(p[2] - lastV[2]) - pointDistance*pointDistance; double v1 =(-b + std::sqrt(b*b-4*a*c))/(2*a); double v2 =(-b - std::sqrt(b*b-4*a*c))/(2*a); if (v1>0) newV = p + d * v1; else if (v2>0) newV = p + d * v2; else MITK_INFO << "ERROR1 - linear resampling"; j--; } #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(newV.data_block()); container->GetPointIds()->InsertNextId(id); } lastV = newV; } else if (j==vertices.size()-1 && new_dist>0.0001) { #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(vertices.at(j).data_block()); container->GetPointIds()->InsertNextId(id); } } } #pragma omp critical { resampled_streamlines[static_cast<unsigned int>(i)] = container; } } for (auto container : resampled_streamlines) { vtkNewCells->InsertNextCell(container); } if (vtkNewCells->GetNumberOfCells()>0) { m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } } // reapply selected colorcoding in case PolyData structure has changed bool mitk::FiberBundle::Equals(mitk::FiberBundle* fib, double eps) { if (fib==nullptr) { MITK_INFO << "Reference bundle is nullptr!"; return false; } if (m_NumFibers!=fib->GetNumFibers()) { MITK_INFO << "Unequal number of fibers!"; MITK_INFO << m_NumFibers << " vs. " << fib->GetNumFibers(); return false; } for (unsigned int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkCell* cell2 = fib->GetFiberPolyData()->GetCell(i); auto numPoints2 = cell2->GetNumberOfPoints(); vtkPoints* points2 = cell2->GetPoints(); if (numPoints2!=numPoints) { MITK_INFO << "Unequal number of points in fiber " << i << "!"; MITK_INFO << numPoints2 << " vs. " << numPoints; return false; } for (int j=0; j<numPoints; j++) { double* p1 = points->GetPoint(j); double* p2 = points2->GetPoint(j); if (fabs(p1[0]-p2[0])>eps || fabs(p1[1]-p2[1])>eps || fabs(p1[2]-p2[2])>eps) { MITK_INFO << "Unequal points in fiber " << i << " at position " << j << "!"; MITK_INFO << "p1: " << p1[0] << ", " << p1[1] << ", " << p1[2]; MITK_INFO << "p2: " << p2[0] << ", " << p2[1] << ", " << p2[2]; return false; } } } return true; } void mitk::FiberBundle::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << "Number of fibers: " << this->GetNumFibers() << std::endl; os << indent << "Min. fiber length: " << this->GetMinFiberLength() << std::endl; os << indent << "Max. fiber length: " << this->GetMaxFiberLength() << std::endl; os << indent << "Mean fiber length: " << this->GetMeanFiberLength() << std::endl; os << indent << "Median fiber length: " << this->GetMedianFiberLength() << std::endl; os << indent << "STDEV fiber length: " << this->GetLengthStDev() << std::endl; os << indent << "Number of points: " << this->GetNumberOfPoints() << std::endl; os << indent << "Extent x: " << this->GetGeometry()->GetExtentInMM(0) << "mm" << std::endl; os << indent << "Extent y: " << this->GetGeometry()->GetExtentInMM(1) << "mm" << std::endl; os << indent << "Extent z: " << this->GetGeometry()->GetExtentInMM(2) << "mm" << std::endl; os << indent << "Diagonal: " << this->GetGeometry()->GetDiagonalLength() << "mm" << std::endl; os << "\nReference geometry:" << std::endl; os << indent << "Size: [" << std::defaultfloat << m_TrackVisHeader.dim[0] << " " << m_TrackVisHeader.dim[1] << " " << m_TrackVisHeader.dim[2] << "]" << std::endl; os << indent << "Voxel size: [" << m_TrackVisHeader.voxel_size[0] << " " << m_TrackVisHeader.voxel_size[1] << " " << m_TrackVisHeader.voxel_size[2] << "]" << std::endl; os << indent << "Origin: [" << m_TrackVisHeader.origin[0] << " " << m_TrackVisHeader.origin[1] << " " << m_TrackVisHeader.origin[2] << "]" << std::endl; os << indent << "Matrix: " << std::scientific << std::endl; os << indent << "[[" << m_TrackVisHeader.vox_to_ras[0][0] << ", " << m_TrackVisHeader.vox_to_ras[0][1] << ", " << m_TrackVisHeader.vox_to_ras[0][2] << ", " << m_TrackVisHeader.vox_to_ras[0][3] << "]" << std::endl; os << indent << " [" << m_TrackVisHeader.vox_to_ras[1][0] << ", " << m_TrackVisHeader.vox_to_ras[1][1] << ", " << m_TrackVisHeader.vox_to_ras[1][2] << ", " << m_TrackVisHeader.vox_to_ras[1][3] << "]" << std::endl; os << indent << " [" << m_TrackVisHeader.vox_to_ras[2][0] << ", " << m_TrackVisHeader.vox_to_ras[2][1] << ", " << m_TrackVisHeader.vox_to_ras[2][2] << ", " << m_TrackVisHeader.vox_to_ras[2][3] << "]" << std::endl; os << indent << " [" << m_TrackVisHeader.vox_to_ras[3][0] << ", " << m_TrackVisHeader.vox_to_ras[3][1] << ", " << m_TrackVisHeader.vox_to_ras[3][2] << ", " << m_TrackVisHeader.vox_to_ras[3][3] << "]]" << std::defaultfloat << std::endl; if (m_FiberWeights!=nullptr) { std::vector< float > weights; for (int i=0; i<m_FiberWeights->GetSize(); i++) weights.push_back(m_FiberWeights->GetValue(i)); std::sort(weights.begin(), weights.end()); os << "\nFiber weight statistics" << std::endl; os << indent << "Min: " << weights.front() << std::endl; os << indent << "1% quantile: " << weights.at(static_cast<unsigned long>(weights.size()*0.01)) << std::endl; os << indent << "5% quantile: " << weights.at(static_cast<unsigned long>(weights.size()*0.05)) << std::endl; os << indent << "25% quantile: " << weights.at(static_cast<unsigned long>(weights.size()*0.25)) << std::endl; os << indent << "Median: " << weights.at(static_cast<unsigned long>(weights.size()*0.5)) << std::endl; os << indent << "75% quantile: " << weights.at(static_cast<unsigned long>(weights.size()*0.75)) << std::endl; os << indent << "95% quantile: " << weights.at(static_cast<unsigned long>(weights.size()*0.95)) << std::endl; os << indent << "99% quantile: " << weights.at(static_cast<unsigned long>(weights.size()*0.99)) << std::endl; os << indent << "Max: " << weights.back() << std::endl; } else os << indent << "\n\nNo fiber weight array found." << std::endl; Superclass::PrintSelf(os, 0); } mitk::FiberBundle::TrackVis_header mitk::FiberBundle::GetTrackVisHeader() { if (m_TrackVisHeader.hdr_size==0) { mitk::Geometry3D::Pointer geom = dynamic_cast<mitk::Geometry3D*>(this->GetGeometry()); SetTrackVisHeader(geom); } return m_TrackVisHeader; } void mitk::FiberBundle::SetTrackVisHeader(const mitk::FiberBundle::TrackVis_header &TrackVisHeader) { m_TrackVisHeader = TrackVisHeader; } void mitk::FiberBundle::SetTrackVisHeader(mitk::BaseGeometry* geometry) { vtkSmartPointer< vtkMatrix4x4 > matrix = vtkSmartPointer< vtkMatrix4x4 >::New(); matrix->Identity(); if (geometry==nullptr) return; for(int i=0; i<3 ;i++) { m_TrackVisHeader.dim[i] = geometry->GetExtent(i); m_TrackVisHeader.voxel_size[i] = geometry->GetSpacing()[i]; m_TrackVisHeader.origin[i] = geometry->GetOrigin()[i]; matrix = geometry->GetVtkMatrix(); } for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) m_TrackVisHeader.vox_to_ras[i][j] = matrix->GetElement(i, j); m_TrackVisHeader.n_scalars = 0; m_TrackVisHeader.n_properties = 0; - sprintf(m_TrackVisHeader.voxel_order,"LPS"); + sprintf(m_TrackVisHeader.voxel_order,"RAS"); m_TrackVisHeader.image_orientation_patient[0] = 1.0; m_TrackVisHeader.image_orientation_patient[1] = 0.0; m_TrackVisHeader.image_orientation_patient[2] = 0.0; m_TrackVisHeader.image_orientation_patient[3] = 0.0; m_TrackVisHeader.image_orientation_patient[4] = 1.0; m_TrackVisHeader.image_orientation_patient[5] = 0.0; m_TrackVisHeader.pad1[0] = 0; m_TrackVisHeader.pad1[1] = 0; m_TrackVisHeader.pad2[0] = 0; m_TrackVisHeader.pad2[1] = 0; m_TrackVisHeader.invert_x = 0; m_TrackVisHeader.invert_y = 0; m_TrackVisHeader.invert_z = 0; m_TrackVisHeader.swap_xy = 0; m_TrackVisHeader.swap_yz = 0; m_TrackVisHeader.swap_zx = 0; m_TrackVisHeader.n_count = 0; m_TrackVisHeader.version = 2; m_TrackVisHeader.hdr_size = 1000; std::string id = "TRACK"; strcpy(m_TrackVisHeader.id_string, id.c_str()); } /* ESSENTIAL IMPLEMENTATION OF SUPERCLASS METHODS */ void mitk::FiberBundle::UpdateOutputInformation() { } void mitk::FiberBundle::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::FiberBundle::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::FiberBundle::VerifyRequestedRegion() { return true; } void mitk::FiberBundle::SetRequestedRegion(const itk::DataObject* ) { } diff --git a/Modules/DiffusionCore/IODataStructures/mitkTrackvis.cpp b/Modules/DiffusionCore/IODataStructures/mitkTrackvis.cpp index 7058fb7..818ab87 100644 --- a/Modules/DiffusionCore/IODataStructures/mitkTrackvis.cpp +++ b/Modules/DiffusionCore/IODataStructures/mitkTrackvis.cpp @@ -1,310 +1,312 @@ #include <mitkTrackvis.h> #include <vtkTransformPolyDataFilter.h> TrackVisFiberReader::TrackVisFiberReader() { m_Filename = ""; m_FilePointer = nullptr; } TrackVisFiberReader::~TrackVisFiberReader() { if (m_FilePointer) fclose( m_FilePointer ); } // Create a TrackVis file and store standard metadata. The file is ready to append fibers. // --------------------------------------------------------------------------------------- -short TrackVisFiberReader::create(std::string filename , mitk::FiberBundle *fib, bool print_header) +short TrackVisFiberReader::create(std::string filename , mitk::FiberBundle *fib, bool print_header, bool use_lps) { m_Header = fib->GetTrackVisHeader(); + if (use_lps) + sprintf(m_Header.voxel_order,"LPS"); if (print_header) this->print_header(); // write the header to the file m_FilePointer = fopen(filename.c_str(),"w+b"); if (m_FilePointer == nullptr) { printf("[ERROR] Unable to create file '%s'\n",filename.c_str()); return 0; } if (fwrite((char*)&m_Header, 1, 1000, m_FilePointer) != 1000) MITK_ERROR << "TrackVis::create : Error occurding during writing fiber."; this->m_Filename = filename; return 1; } // Open an existing TrackVis file and read metadata information. // The file pointer is positiond at the beginning of fibers data // ------------------------------------------------------------- short TrackVisFiberReader::open( std::string filename ) { m_FilePointer = std::fopen(filename.c_str(), "rb"); if (m_FilePointer == nullptr) { printf("[ERROR] Unable to open file '%s'\n",filename.c_str()); return 0; } this->m_Filename = filename; return fread((char*)(&m_Header), 1, 1000, m_FilePointer); } short TrackVisFiberReader::write(const mitk::FiberBundle *fib) { vtkSmartPointer<vtkPolyData> poly = fib->GetFiberPolyData(); { mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); vtkSmartPointer< vtkMatrix4x4 > matrix = vtkSmartPointer< vtkMatrix4x4 >::New(); matrix->Identity(); for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) { if (j<3) matrix->SetElement(i, j, m_Header.vox_to_ras[i][j]/m_Header.voxel_size[j]); else matrix->SetElement(i, j, m_Header.vox_to_ras[i][j]); } if (m_Header.voxel_order[0]=='R') { matrix->SetElement(0,0,-matrix->GetElement(0,0)); matrix->SetElement(0,1,-matrix->GetElement(0,1)); matrix->SetElement(0,2,-matrix->GetElement(0,2)); matrix->SetElement(0,3,-matrix->GetElement(0,3)); } if (m_Header.voxel_order[1]=='A') { matrix->SetElement(1,0,-matrix->GetElement(1,0)); matrix->SetElement(1,1,-matrix->GetElement(1,1)); matrix->SetElement(1,2,-matrix->GetElement(1,2)); matrix->SetElement(1,3,-matrix->GetElement(1,3)); } if (m_Header.voxel_order[2]=='I') { matrix->SetElement(2,0,-matrix->GetElement(2,0)); matrix->SetElement(2,1,-matrix->GetElement(2,1)); matrix->SetElement(2,2,-matrix->GetElement(2,2)); matrix->SetElement(2,3,-matrix->GetElement(2,3)); } geometry->SetIndexToWorldTransformByVtkMatrix(matrix); vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); transformFilter->SetInputData(poly); transformFilter->SetTransform(geometry->GetVtkTransform()->GetInverse()); transformFilter->Update(); poly = transformFilter->GetOutput(); } for (unsigned int i=0; i<fib->GetNumFibers(); i++) { vtkCell* cell = poly->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); unsigned int numSaved, pos = 0; //float* tmp = new float[3*maxSteps]; std::vector< float > tmp; tmp.reserve(3*numPoints); numSaved = numPoints; for(unsigned int i=0; i<numSaved ;i++) { double* p = points->GetPoint(i); // TRK coordinates are corner based, so we have to shift the center based vtk coordinates by half a voxel tmp[pos++] = p[0] + m_Header.voxel_size[0]/2; tmp[pos++] = p[1] + m_Header.voxel_size[1]/2; tmp[pos++] = p[2] + m_Header.voxel_size[2]/2; } // write the coordinates to the file if ( fwrite((char*)&numSaved, 1, 4, m_FilePointer) != 4 ) { printf( "[ERROR] Problems saving the fiber!\n" ); return 1; } if ( fwrite((char*)&(tmp.front()), 1, 4*pos, m_FilePointer) != 4*pos ) { printf( "[ERROR] Problems saving the fiber!\n" ); return 1; } } return 0; } void TrackVisFiberReader::print_header() { std::cout << "--------------------------------------------------------" << std::endl; std::cout << "see http://trackvis.org/docs/?subsect=fileformat" << std::endl; std::cout << "ONLY vox_to_ras AND voxel_order HEADER ENTRIES ARE USED FOR FIBER COORDINATE TRANSFORMATIONS!" << std::endl; std::cout << "\nid_string (should be \"TRACK\"): " << m_Header.id_string << std::endl; std::cout << "dim: [" << std::defaultfloat << m_Header.dim[0] << " " << m_Header.dim[1] << " " << m_Header.dim[2] << "]" << std::endl; std::cout << "voxel_size: [" << m_Header.voxel_size[0] << " " << m_Header.voxel_size[1] << " " << m_Header.voxel_size[2] << "]" << std::endl; std::cout << "origin: [" << m_Header.origin[0] << " " << m_Header.origin[1] << " " << m_Header.origin[2] << "]" << std::endl; std::cout << "vox_to_world: " << std::scientific << std::endl; std::cout << "[[" << m_Header.vox_to_ras[0][0] << ", " << m_Header.vox_to_ras[0][1] << ", " << m_Header.vox_to_ras[0][2] << ", " << m_Header.vox_to_ras[0][3] << "]" << std::endl; std::cout << " [" << m_Header.vox_to_ras[1][0] << ", " << m_Header.vox_to_ras[1][1] << ", " << m_Header.vox_to_ras[1][2] << ", " << m_Header.vox_to_ras[1][3] << "]" << std::endl; std::cout << " [" << m_Header.vox_to_ras[2][0] << ", " << m_Header.vox_to_ras[2][1] << ", " << m_Header.vox_to_ras[2][2] << ", " << m_Header.vox_to_ras[2][3] << "]" << std::endl; std::cout << " [" << m_Header.vox_to_ras[3][0] << ", " << m_Header.vox_to_ras[3][1] << ", " << m_Header.vox_to_ras[3][2] << ", " << m_Header.vox_to_ras[3][3] << "]]" << std::defaultfloat << std::endl; std::cout << "voxel_order: " << m_Header.voxel_order[0] << m_Header.voxel_order[1] << m_Header.voxel_order[2] << std::endl; std::cout << "pad1: " << m_Header.pad1[0] << m_Header.pad1[1] << std::endl; std::cout << "pad2: " << m_Header.pad2[0] << m_Header.pad2[1] << m_Header.pad2[2] << std::endl; std::cout << "image_orientation_patient: [" << m_Header.image_orientation_patient[0] << " " << m_Header.image_orientation_patient[1] << " " << m_Header.image_orientation_patient[2] << " " << m_Header.image_orientation_patient[3] << " " << m_Header.image_orientation_patient[4] << " " << m_Header.image_orientation_patient[5] << "]" << std::endl; std::cout << "invert_x: " << static_cast<bool>(m_Header.invert_x) << std::endl; std::cout << "invert_y: " << static_cast<bool>(m_Header.invert_y) << std::endl; std::cout << "invert_z: " << static_cast<bool>(m_Header.invert_z) << std::endl; std::cout << "swap_xy: " << static_cast<bool>(m_Header.swap_xy) << std::endl; std::cout << "swap_yz: " << static_cast<bool>(m_Header.swap_yz) << std::endl; std::cout << "swap_zx: " << static_cast<bool>(m_Header.swap_zx) << std::endl; std::cout << "n_count: " << m_Header.n_count << std::endl; std::cout << "version: " << m_Header.version << std::endl; std::cout << "hdr_size: " << m_Header.hdr_size << std::endl; std::cout << "\nNot printed: n_scalars, scalar_name, n_properties, property_name, reserved" << std::endl; std::cout << "--------------------------------------------------------" << std::endl; } short TrackVisFiberReader::read( mitk::FiberBundle* fib, bool use_matrix, bool flip_x, bool flip_y, bool flip_z, bool print_header) { int numPoints; vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); if (print_header) this->print_header(); while (fread((char*)&numPoints, 1, 4, m_FilePointer)==4) { if ( numPoints <= 0 ) { printf( "[ERROR] Trying to read a fiber with %d points!\n", numPoints ); return -1; } vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); float tmp[3]; for(int i=0; i<numPoints; i++) { if (fread((char*)tmp, 1, 12, m_FilePointer) == 0) MITK_ERROR << "TrackVis::read: Error during read."; // TRK coordinates are corner based, so we have to shift them back to center based coordinates tmp[0] -= m_Header.voxel_size[0]/2; tmp[1] -= m_Header.voxel_size[1]/2; tmp[2] -= m_Header.voxel_size[2]/2; vtkIdType id = vtkNewPoints->InsertNextPoint(tmp); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } vtkSmartPointer<vtkPolyData> fiberPolyData = vtkSmartPointer<vtkPolyData>::New(); fiberPolyData->SetPoints(vtkNewPoints); fiberPolyData->SetLines(vtkNewCells); mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); vtkSmartPointer< vtkMatrix4x4 > matrix = vtkSmartPointer< vtkMatrix4x4 >::New(); matrix->Identity(); if (use_matrix) for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) { if (j<3) matrix->SetElement(i, j, m_Header.vox_to_ras[i][j]/m_Header.voxel_size[j]); else matrix->SetElement(i, j, m_Header.vox_to_ras[i][j]); } if (m_Header.voxel_order[0]=='R') { matrix->SetElement(0,0,-matrix->GetElement(0,0)); matrix->SetElement(0,1,-matrix->GetElement(0,1)); matrix->SetElement(0,2,-matrix->GetElement(0,2)); matrix->SetElement(0,3,-matrix->GetElement(0,3)); } if (m_Header.voxel_order[1]=='A') { matrix->SetElement(1,0,-matrix->GetElement(1,0)); matrix->SetElement(1,1,-matrix->GetElement(1,1)); matrix->SetElement(1,2,-matrix->GetElement(1,2)); matrix->SetElement(1,3,-matrix->GetElement(1,3)); } if (m_Header.voxel_order[2]=='I') { matrix->SetElement(2,0,-matrix->GetElement(2,0)); matrix->SetElement(2,1,-matrix->GetElement(2,1)); matrix->SetElement(2,2,-matrix->GetElement(2,2)); matrix->SetElement(2,3,-matrix->GetElement(2,3)); } if (flip_x) { matrix->SetElement(0,0,-matrix->GetElement(0,0)); matrix->SetElement(0,1,-matrix->GetElement(0,1)); matrix->SetElement(0,2,-matrix->GetElement(0,2)); matrix->SetElement(0,3,-matrix->GetElement(0,3)); } if (flip_y) { matrix->SetElement(1,0,-matrix->GetElement(1,0)); matrix->SetElement(1,1,-matrix->GetElement(1,1)); matrix->SetElement(1,2,-matrix->GetElement(1,2)); matrix->SetElement(1,3,-matrix->GetElement(1,3)); } if (flip_z) { matrix->SetElement(2,0,-matrix->GetElement(2,0)); matrix->SetElement(2,1,-matrix->GetElement(2,1)); matrix->SetElement(2,2,-matrix->GetElement(2,2)); matrix->SetElement(2,3,-matrix->GetElement(2,3)); } geometry->SetIndexToWorldTransformByVtkMatrix(matrix); vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); transformFilter->SetInputData(fiberPolyData); transformFilter->SetTransform(geometry->GetVtkTransform()); transformFilter->Update(); fib->SetFiberPolyData(transformFilter->GetOutput()); fib->SetTrackVisHeader(geometry.GetPointer()); fib->SetTrackVisHeader(m_Header); return numPoints; } // Update the field in the header to the new FIBER TOTAL. // ------------------------------------------------------ void TrackVisFiberReader::updateTotal( int totFibers ) { fseek(m_FilePointer, 1000-12, SEEK_SET); if (fwrite((char*)&totFibers, 1, 4, m_FilePointer) != 4) MITK_ERROR << "[ERROR] Problems saving the fiber!"; } void TrackVisFiberReader::writeHdr() { fseek(m_FilePointer, 0, SEEK_SET); if (fwrite((char*)&m_Header, 1, 1000, m_FilePointer) != 1000) MITK_ERROR << "[ERROR] Problems saving the fiber!"; } // Close the TrackVis file, but keep the metadata in the header. // ------------------------------------------------------------- void TrackVisFiberReader::close() { fclose(m_FilePointer); m_FilePointer = nullptr; } bool TrackVisFiberReader::IsTransformValid() { if (fabs(m_Header.image_orientation_patient[0])<=0.001 || fabs(m_Header.image_orientation_patient[3])<=0.001 || fabs(m_Header.image_orientation_patient[5])<=0.001) return false; return true; } diff --git a/Modules/DiffusionCore/IODataStructures/mitkTrackvis.h b/Modules/DiffusionCore/IODataStructures/mitkTrackvis.h index 1a021b6..7c98e3a 100644 --- a/Modules/DiffusionCore/IODataStructures/mitkTrackvis.h +++ b/Modules/DiffusionCore/IODataStructures/mitkTrackvis.h @@ -1,53 +1,53 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TRACKVIS #define _TRACKVIS #include <mitkCommon.h> #include <mitkFiberBundle.h> #include <vtkSmartPointer.h> #include <vtkPolyData.h> #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkPolyLine.h> #include <itkSize.h> // Class to handle TrackVis files. // ------------------------------- class MITKDIFFUSIONCORE_EXPORT TrackVisFiberReader { private: std::string m_Filename; FILE* m_FilePointer; mitk::FiberBundle::TrackVis_header m_Header; public: - short create(std::string m_Filename, mitk::FiberBundle *fib, bool print_header); + short create(std::string m_Filename, mitk::FiberBundle *fib, bool print_header, bool use_lps); short open(std::string m_Filename ); short read( mitk::FiberBundle* fib, bool use_matrix, bool flip_x, bool flip_y, bool flip_z, bool print_header); short write(const mitk::FiberBundle* fib ); void writeHdr(); void updateTotal( int totFibers ); void close(); bool IsTransformValid(); void print_header(); TrackVisFiberReader(); ~TrackVisFiberReader(); }; #endif diff --git a/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisReader.cpp b/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisReader.cpp index dfa927c..1b22f33 100644 --- a/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisReader.cpp +++ b/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisReader.cpp @@ -1,100 +1,100 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFiberBundleTrackVisReader.h" #include <itkMetaDataObject.h> #include <vtkPolyData.h> #include <vtkDataReader.h> #include <vtkPolyDataReader.h> #include <vtkMatrix4x4.h> #include <vtkPolyLine.h> #include <vtkCellArray.h> #include <vtkDataArray.h> #include <vtkFloatArray.h> #include <vtkCellData.h> #include <vtkPointData.h> #include <itksys/SystemTools.hxx> #include <vtkCleanPolyData.h> #include <mitkTrackvis.h> #include <mitkCustomMimeType.h> #include "mitkDiffusionIOMimeTypes.h" mitk::FiberBundleTrackVisReader::FiberBundleTrackVisReader() : mitk::AbstractFileReader( mitk::DiffusionIOMimeTypes::FIBERBUNDLE_TRK_MIMETYPE_NAME(), "TrackVis Fiber Bundle Reader" ) { Options defaultOptions; defaultOptions["Apply index to world transform stored in the TRK header"] = true; defaultOptions["Print header"] = true; - defaultOptions["Flip x"] = true; + defaultOptions["Flip x"] = false; defaultOptions["Flip y"] = false; defaultOptions["Flip z"] = false; this->SetDefaultOptions(defaultOptions); m_ServiceReg = this->RegisterService(); } mitk::FiberBundleTrackVisReader::FiberBundleTrackVisReader(const FiberBundleTrackVisReader &other) :mitk::AbstractFileReader(other) { } mitk::FiberBundleTrackVisReader * mitk::FiberBundleTrackVisReader::Clone() const { return new FiberBundleTrackVisReader(*this); } std::vector<itk::SmartPointer<mitk::BaseData> > mitk::FiberBundleTrackVisReader::DoRead() { std::vector<itk::SmartPointer<mitk::BaseData> > result; try { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); setlocale(LC_ALL, locale.c_str()); std::string filename = this->GetInputLocation(); MITK_INFO << "Loading tractogram (TrackVis format): " << itksys::SystemTools::GetFilenameName(filename); std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename); ext = itksys::SystemTools::LowerCase(ext); if (ext==".trk") { Options options = this->GetOptions(); bool apply_matrix = us::any_cast<bool>(options["Apply index to world transform stored in the TRK header"]); bool flip_x = us::any_cast<bool>(options["Flip x"]); bool flip_y = us::any_cast<bool>(options["Flip y"]); bool flip_z = us::any_cast<bool>(options["Flip z"]); bool print_header = us::any_cast<bool>(options["Print header"]); FiberBundle::Pointer mitk_fib = FiberBundle::New(); TrackVisFiberReader reader; reader.open(this->GetInputLocation().c_str()); reader.read(mitk_fib.GetPointer(), apply_matrix, flip_x, flip_y, flip_z, print_header); result.push_back(mitk_fib.GetPointer()); return result; } setlocale(LC_ALL, currLocale.c_str()); MITK_INFO << "Fiber bundle read"; } catch(...) { throw; } return result; } diff --git a/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisWriter.cpp b/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisWriter.cpp index bcd6abb..cf8a924 100644 --- a/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisWriter.cpp +++ b/Modules/DiffusionIO/ReaderWriter/mitkFiberBundleTrackVisWriter.cpp @@ -1,108 +1,110 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFiberBundleTrackVisWriter.h" #include <vtkSmartPointer.h> #include <vtkCleanPolyData.h> #include <itksys/SystemTools.hxx> #include <mitkTrackvis.h> #include <itkSize.h> #include <vtkFloatArray.h> #include <vtkCellData.h> #include <vtkPointData.h> #include <mitkAbstractFileWriter.h> #include <mitkCustomMimeType.h> #include "mitkDiffusionIOMimeTypes.h" mitk::FiberBundleTrackVisWriter::FiberBundleTrackVisWriter() : mitk::AbstractFileWriter(mitk::FiberBundle::GetStaticNameOfClass(), mitk::DiffusionIOMimeTypes::FIBERBUNDLE_TRK_MIMETYPE_NAME(), "TrackVis Fiber Bundle Reader") { Options defaultOptions; defaultOptions["Print header"] = false; + defaultOptions["Use LPS coordinate system (default is RAS)"] = false; this->SetDefaultOptions(defaultOptions); RegisterService(); } mitk::FiberBundleTrackVisWriter::FiberBundleTrackVisWriter(const mitk::FiberBundleTrackVisWriter & other) :mitk::AbstractFileWriter(other) {} mitk::FiberBundleTrackVisWriter::~FiberBundleTrackVisWriter() {} mitk::FiberBundleTrackVisWriter * mitk::FiberBundleTrackVisWriter::Clone() const { return new mitk::FiberBundleTrackVisWriter(*this); } void mitk::FiberBundleTrackVisWriter::Write() { std::ostream* out; std::ofstream outStream; if( this->GetOutputStream() ) { out = this->GetOutputStream(); }else{ outStream.open( this->GetOutputLocation().c_str() ); out = &outStream; } if ( !out->good() ) { mitkThrow() << "Stream not good."; } try { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); setlocale(LC_ALL, locale.c_str()); std::locale I("C"); out->imbue(I); std::string filename = this->GetOutputLocation().c_str(); mitk::FiberBundle::ConstPointer input = dynamic_cast<const mitk::FiberBundle*>(this->GetInput()); std::string ext = itksys::SystemTools::GetFilenameLastExtension(this->GetOutputLocation().c_str()); // default extension is .fib if(ext == "") { ext = ".trk"; this->SetOutputLocation(this->GetOutputLocation() + ext); } Options options = this->GetOptions(); bool print_header = us::any_cast<bool>(options["Print header"]); + bool use_lps = us::any_cast<bool>(options["Use LPS coordinate system (default is RAS)"]); MITK_INFO << "Writing fiber bundle as TRK"; TrackVisFiberReader trk; - trk.create(filename, const_cast<mitk::FiberBundle*>(input.GetPointer()), print_header); + trk.create(filename, const_cast<mitk::FiberBundle*>(input.GetPointer()), print_header, use_lps); trk.writeHdr(); trk.write(input.GetPointer()); setlocale(LC_ALL, currLocale.c_str()); MITK_INFO << "TrackVis Fiber bundle written to " << filename; } catch(...) { throw; } } diff --git a/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp b/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp index 469bb13..8fd9e0d 100644 --- a/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp +++ b/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp @@ -1,300 +1,300 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTrackingHandlerOdf.h" #include <itkDiffusionOdfGeneralizedFaImageFilter.h> #include <itkImageRegionIterator.h> #include <itkPointShell.h> #include <omp.h> #include <cmath> namespace mitk { TrackingHandlerOdf::TrackingHandlerOdf() : m_NumProbSamples(1) , m_OdfFromTensor(false) { m_GfaInterpolator = itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::New(); m_OdfInterpolator = itk::LinearInterpolateImageFunction< itk::Image< ItkOdfImageType::PixelType, 3 >, float >::New(); } TrackingHandlerOdf::~TrackingHandlerOdf() { } bool TrackingHandlerOdf::WorldToIndex(itk::Point<float, 3>& pos, itk::Index<3>& index) { m_OdfImage->TransformPhysicalPointToIndex(pos, index); return m_OdfImage->GetLargestPossibleRegion().IsInside(index); } void TrackingHandlerOdf::InitForTracking() { MITK_INFO << "Initializing ODF tracker."; if (m_NeedsDataInit) { m_OdfHemisphereIndices.clear(); itk::OrientationDistributionFunction< float, ODF_SAMPLING_SIZE > odf; vnl_vector_fixed<double,3> ref; ref.fill(0); ref[0]=1; for (int i=0; i<ODF_SAMPLING_SIZE; i++) if (dot_product(ref, odf.GetDirection(i))>0) m_OdfHemisphereIndices.push_back(i); m_OdfFloatDirs.set_size(m_OdfHemisphereIndices.size(), 3); auto double_dir = m_OdfImage->GetDirection().GetVnlMatrix(); for (int r=0; r<3; r++) for (int c=0; c<3; c++) { m_FloatImageRotation[r][c] = double_dir[r][c]; } for (unsigned int i=0; i<m_OdfHemisphereIndices.size(); i++) { m_OdfFloatDirs[i][0] = odf.GetDirection(m_OdfHemisphereIndices[i])[0]; m_OdfFloatDirs[i][1] = odf.GetDirection(m_OdfHemisphereIndices[i])[1]; m_OdfFloatDirs[i][2] = odf.GetDirection(m_OdfHemisphereIndices[i])[2]; } if (m_GfaImage.IsNull()) { MITK_INFO << "Calculating GFA image."; typedef itk::DiffusionOdfGeneralizedFaImageFilter<float,float,ODF_SAMPLING_SIZE> GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); gfaFilter->SetInput(m_OdfImage); gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); gfaFilter->Update(); m_GfaImage = gfaFilter->GetOutput(); } m_NeedsDataInit = false; } if (m_OdfFromTensor) { // m_Parameters->m_OdfCutoff = 0; // m_Parameters->m_SharpenOdfs = false; } m_GfaInterpolator->SetInputImage(m_GfaImage); m_OdfInterpolator->SetInputImage(m_OdfImage); this->CalculateMinVoxelSize(); std::cout << "TrackingHandlerOdf - GFA threshold: " << m_Parameters->m_Cutoff << std::endl; std::cout << "TrackingHandlerOdf - ODF threshold: " << m_Parameters->m_OdfCutoff << std::endl; if (m_Parameters->m_SharpenOdfs > 1) - std::cout << "TrackingHandlerOdf - Raising ODf values to the power of " << m_Parameters->m_SharpenOdfs << std::endl; + std::cout << "TrackingHandlerOdf - Raising ODF values to the power of " << m_Parameters->m_SharpenOdfs << std::endl; } int TrackingHandlerOdf::SampleOdf(vnl_vector< float >& probs, vnl_vector< float >& angles) { boost::random::discrete_distribution<int, float> dist(probs.begin(), probs.end()); int sampled_idx = 0; int max_sample_idx = -1; float max_prob = 0; int trials = 0; for (int i=0; i<m_NumProbSamples; i++) // we sample m_NumProbSamples times and retain the sample with maximum probabilty { trials++; #pragma omp critical { boost::random::variate_generator<boost::random::mt19937&, boost::random::discrete_distribution<int,float>> sampler(m_Rng, dist); sampled_idx = sampler(); } if (probs[sampled_idx]>max_prob && probs[sampled_idx]>m_Parameters->m_OdfCutoff && fabs(angles[sampled_idx])>=m_Parameters->GetAngularThresholdDot()) { max_prob = probs[sampled_idx]; max_sample_idx = sampled_idx; } else if ( (probs[sampled_idx]<=m_Parameters->m_OdfCutoff || fabs(angles[sampled_idx])<m_Parameters->GetAngularThresholdDot()) && trials<50) // we allow 50 trials to exceed the ODF threshold i--; } return max_sample_idx; } void TrackingHandlerOdf::SetIsOdfFromTensor(bool OdfFromTensor) { m_OdfFromTensor = OdfFromTensor; } bool TrackingHandlerOdf::GetIsOdfFromTensor() const { return m_OdfFromTensor; } vnl_vector_fixed<float,3> TrackingHandlerOdf::ProposeDirection(const itk::Point<float, 3>& pos, std::deque<vnl_vector_fixed<float, 3> >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed<float,3> output_direction; output_direction.fill(0); itk::Index<3> idx; m_OdfImage->TransformPhysicalPointToIndex(pos, idx); if ( !m_OdfImage->GetLargestPossibleRegion().IsInside(idx) ) return output_direction; // check GFA threshold for termination float gfa = mitk::imv::GetImageValue<float>(pos, m_Parameters->m_InterpolateTractographyData, m_GfaInterpolator); if (gfa<m_Parameters->m_Cutoff) return output_direction; vnl_vector_fixed<float,3> last_dir; if (!olddirs.empty()) last_dir = olddirs.back(); if (!m_Parameters->m_InterpolateTractographyData && oldIndex==idx) return last_dir; ItkOdfImageType::PixelType odf_values = mitk::imv::GetImageValue<ItkOdfImageType::PixelType>(pos, m_Parameters->m_InterpolateTractographyData, m_OdfInterpolator); vnl_vector< float > probs; probs.set_size(m_OdfHemisphereIndices.size()); vnl_vector< float > angles; angles.set_size(m_OdfHemisphereIndices.size()); angles.fill(1.0); // Find ODF maximum and remove <0 values float max_odf_val = 0; float odf_sum = 0; int max_idx_d = -1; int c = 0; for (int i : m_OdfHemisphereIndices) { if (odf_values[i]<0) probs[c] = 0; else { probs[c] = pow(odf_values[i], m_Parameters->m_SharpenOdfs); if (probs[c]>max_odf_val) { max_odf_val = probs[c]; max_idx_d = c; } odf_sum += probs[c]; } c++; } if (odf_sum>0) { probs /= odf_sum; max_odf_val = max_odf_val/odf_sum; } // no previous direction if (max_odf_val>m_Parameters->m_OdfCutoff && (olddirs.empty() || last_dir.magnitude()<=0.5)) { if (m_Parameters->m_Mode==MODE::DETERMINISTIC) // return maximum peak { output_direction = m_OdfFloatDirs.get_row(max_idx_d); return output_direction * max_odf_val; } else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) // sample from complete ODF { int max_sample_idx = SampleOdf(probs, angles); if (max_sample_idx>=0) output_direction = m_OdfFloatDirs.get_row(max_sample_idx) * probs[max_sample_idx]; return output_direction; } } else if (max_odf_val<=m_Parameters->m_OdfCutoff) // return (0,0,0) { return output_direction; } // correct previous direction if (m_Parameters->m_FlipX) last_dir[0] *= -1; if (m_Parameters->m_FlipY) last_dir[1] *= -1; if (m_Parameters->m_FlipZ) last_dir[2] *= -1; // calculate angles between previous direction and ODF directions angles = m_OdfFloatDirs*last_dir; float probs_sum = 0; float max_prob = 0; for (unsigned int i=0; i<m_OdfHemisphereIndices.size(); i++) { float prob = probs[i]; float angle = angles[i]; float abs_angle = fabs(angle); prob *= abs_angle; // weight probabilities according to deviation from last direction if (m_Parameters->m_Mode==MODE::DETERMINISTIC && prob>max_prob && prob>m_Parameters->m_OdfCutoff) { // use maximum peak of the ODF weighted with the directional prior max_prob = prob; vnl_vector_fixed<float,3> d = m_OdfFloatDirs.get_row(i); if (angle<0) d *= -1; output_direction = prob*d; } else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) { probs[i] = prob; probs_sum += probs[i]; } } // do probabilistic sampling if (m_Parameters->m_Mode==MODE::PROBABILISTIC && probs_sum>0.0001) { probs /= probs_sum; // we have to normalize again since the probabilities have been weighted by the directional prior int max_sample_idx = SampleOdf(probs, angles); if (max_sample_idx>=0) { output_direction = m_OdfFloatDirs.get_row(max_sample_idx); if (angles[max_sample_idx]<0) output_direction *= -1; output_direction *= probs[max_sample_idx]; } } // check hard angular threshold float mag = output_direction.magnitude(); if (mag>=0.0001) { output_direction.normalize(); float a = dot_product(output_direction, last_dir); if (a<m_Parameters->GetAngularThresholdDot()) output_direction.fill(0); } else output_direction.fill(0); if (m_Parameters->m_FlipX) output_direction[0] *= -1; if (m_Parameters->m_FlipY) output_direction[1] *= -1; if (m_Parameters->m_FlipZ) output_direction[2] *= -1; if (m_Parameters->m_ApplyDirectionMatrix) output_direction = m_FloatImageRotation*output_direction; return output_direction; } void TrackingHandlerOdf::SetNumProbSamples(int NumProbSamples) { m_NumProbSamples = NumProbSamples; } } diff --git a/Modules/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp b/Modules/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp index 86abf58..1e8d617 100644 --- a/Modules/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp +++ b/Modules/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp @@ -1,997 +1,1009 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include <ctime> #include <cstdio> #include <cstdlib> #include <omp.h> #include "itkStreamlineTrackingFilter.h" #include <itkImageRegionConstIterator.h> #include <itkImageRegionConstIteratorWithIndex.h> #include <itkImageRegionIterator.h> #include "itkPointShell.h" #include <itkRescaleIntensityImageFilter.h> #include <mitkLexicalCast.h> #include <TrackingHandlers/mitkTrackingDataHandler.h> #include <TrackingHandlers/mitkTrackingHandlerOdf.h> #include <TrackingHandlers/mitkTrackingHandlerPeaks.h> #include <TrackingHandlers/mitkTrackingHandlerTensor.h> #include <TrackingHandlers/mitkTrackingHandlerRandomForest.h> #include <mitkDiffusionFunctionCollection.h> #include <random> namespace itk { StreamlineTrackingFilter ::StreamlineTrackingFilter() : m_PauseTracking(false) , m_AbortTracking(false) , m_BuildFibersFinished(false) , m_BuildFibersReady(0) , m_FiberPolyData(nullptr) , m_Points(nullptr) , m_Cells(nullptr) , m_StoppingRegions(nullptr) , m_TargetRegions(nullptr) , m_SeedImage(nullptr) , m_MaskImage(nullptr) , m_ExclusionRegions(nullptr) , m_OutputProbabilityMap(nullptr) , m_Verbose(true) , m_DemoMode(false) , m_CurrentTracts(0) , m_Progress(0) , m_StopTracking(false) , m_TrackingPriorHandler(nullptr) { this->SetNumberOfRequiredInputs(0); } std::string StreamlineTrackingFilter::GetStatusText() { std::string status = "Seedpoints processed: " + boost::lexical_cast<std::string>(m_Progress) + "/" + boost::lexical_cast<std::string>(m_SeedPoints.size()); if (m_SeedPoints.size()>0) status += " (" + boost::lexical_cast<std::string>(100*m_Progress/m_SeedPoints.size()) + "%)"; if (m_Parameters->m_MaxNumFibers>0) status += "\nFibers accepted: " + boost::lexical_cast<std::string>(m_CurrentTracts) + "/" + boost::lexical_cast<std::string>(m_Parameters->m_MaxNumFibers); else status += "\nFibers accepted: " + boost::lexical_cast<std::string>(m_CurrentTracts); return status; } void StreamlineTrackingFilter::BeforeTracking() { m_StopTracking = false; m_TrackingHandler->SetParameters(m_Parameters); m_TrackingHandler->InitForTracking(); m_FiberPolyData = PolyDataType::New(); m_Points = vtkSmartPointer< vtkPoints >::New(); m_Cells = vtkSmartPointer< vtkCellArray >::New(); if (m_TrackingPriorHandler!=nullptr) { m_TrackingPriorHandler->InitForTracking(); } auto imageSpacing = m_TrackingHandler->GetSpacing(); if (m_Parameters->m_OutputProbMap) { m_OutputProbabilityMap = ItkDoubleImgType::New(); m_OutputProbabilityMap->SetSpacing(imageSpacing); m_OutputProbabilityMap->SetOrigin(m_TrackingHandler->GetOrigin()); m_OutputProbabilityMap->SetDirection(m_TrackingHandler->GetDirection()); m_OutputProbabilityMap->SetRegions(m_TrackingHandler->GetLargestPossibleRegion()); m_OutputProbabilityMap->Allocate(); m_OutputProbabilityMap->FillBuffer(0); } m_MaskInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_StopInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_SeedInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_TargetInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_ExclusionInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); if (m_StoppingRegions.IsNull()) { m_StoppingRegions = ItkFloatImgType::New(); m_StoppingRegions->SetSpacing( imageSpacing ); m_StoppingRegions->SetOrigin( m_TrackingHandler->GetOrigin() ); m_StoppingRegions->SetDirection( m_TrackingHandler->GetDirection() ); m_StoppingRegions->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_StoppingRegions->Allocate(); m_StoppingRegions->FillBuffer(0); } else std::cout << "StreamlineTracking - Using stopping region image" << std::endl; m_StopInterpolator->SetInputImage(m_StoppingRegions); if (m_ExclusionRegions.IsNotNull()) { std::cout << "StreamlineTracking - Using exclusion region image" << std::endl; m_ExclusionInterpolator->SetInputImage(m_ExclusionRegions); } if (m_TargetRegions.IsNull()) { m_TargetImageSet = false; m_TargetRegions = ItkFloatImgType::New(); m_TargetRegions->SetSpacing( imageSpacing ); m_TargetRegions->SetOrigin( m_TrackingHandler->GetOrigin() ); m_TargetRegions->SetDirection( m_TrackingHandler->GetDirection() ); m_TargetRegions->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_TargetRegions->Allocate(); m_TargetRegions->FillBuffer(1); } else { m_TargetImageSet = true; m_TargetInterpolator->SetInputImage(m_TargetRegions); std::cout << "StreamlineTracking - Using target region image" << std::endl; } if (m_SeedImage.IsNull()) { m_SeedImageSet = false; m_SeedImage = ItkFloatImgType::New(); m_SeedImage->SetSpacing( imageSpacing ); m_SeedImage->SetOrigin( m_TrackingHandler->GetOrigin() ); m_SeedImage->SetDirection( m_TrackingHandler->GetDirection() ); m_SeedImage->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_SeedImage->Allocate(); m_SeedImage->FillBuffer(1); } else { m_SeedImageSet = true; std::cout << "StreamlineTracking - Using seed image" << std::endl; } m_SeedInterpolator->SetInputImage(m_SeedImage); if (m_MaskImage.IsNull()) { // initialize mask image m_MaskImage = ItkFloatImgType::New(); m_MaskImage->SetSpacing( imageSpacing ); m_MaskImage->SetOrigin( m_TrackingHandler->GetOrigin() ); m_MaskImage->SetDirection( m_TrackingHandler->GetDirection() ); m_MaskImage->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } else std::cout << "StreamlineTracking - Using mask image" << std::endl; m_MaskInterpolator->SetInputImage(m_MaskImage); // Autosettings for endpoint constraints if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE && m_TargetImageSet && m_SeedImageSet) { MITK_INFO << "No endpoint constraint chosen but seed and target image set --> setting constraint to EPS_IN_SEED_AND_TARGET"; m_Parameters->m_EpConstraints = EndpointConstraints::EPS_IN_SEED_AND_TARGET; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE && m_TargetImageSet) { MITK_INFO << "No endpoint constraint chosen but target image set --> setting constraint to EPS_IN_TARGET"; m_Parameters->m_EpConstraints = EndpointConstraints::EPS_IN_TARGET; } // Check if endpoint constraints are valid FiberType test_fib; itk::Point<float> p; p.Fill(0); test_fib.push_back(p); test_fib.push_back(p); IsValidFiber(&test_fib); if (m_SeedPoints.empty()) GetSeedPointsFromSeedImage(); m_BuildFibersReady = 0; m_BuildFibersFinished = false; m_Tractogram.clear(); m_SamplingPointset = mitk::PointSet::New(); m_AlternativePointset = mitk::PointSet::New(); m_StopVotePointset = mitk::PointSet::New(); m_StartTime = std::chrono::system_clock::now(); if (m_DemoMode) omp_set_num_threads(1); if (m_Parameters->m_Mode==mitk::TrackingDataHandler::MODE::DETERMINISTIC) std::cout << "StreamlineTracking - Mode: deterministic" << std::endl; else if(m_Parameters->m_Mode==mitk::TrackingDataHandler::MODE::PROBABILISTIC) { std::cout << "StreamlineTracking - Mode: probabilistic" << std::endl; std::cout << "StreamlineTracking - Trials per seed: " << m_Parameters->m_TrialsPerSeed << std::endl; } else std::cout << "StreamlineTracking - Mode: ???" << std::endl; if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE) std::cout << "StreamlineTracking - Endpoint constraint: NONE" << std::endl; else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_TARGET" << std::endl; else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_TARGET_LABELDIFF" << std::endl; else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_SEED_AND_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_SEED_AND_TARGET" << std::endl; else if (m_Parameters->m_EpConstraints==EndpointConstraints::MIN_ONE_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: MIN_ONE_EP_IN_TARGET" << std::endl; else if (m_Parameters->m_EpConstraints==EndpointConstraints::ONE_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: ONE_EP_IN_TARGET" << std::endl; else if (m_Parameters->m_EpConstraints==EndpointConstraints::NO_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: NO_EP_IN_TARGET" << std::endl; std::cout << "StreamlineTracking - Angular threshold: " << m_Parameters->GetAngularThresholdDeg() << "°" << std::endl; std::cout << "StreamlineTracking - Stepsize: " << m_Parameters->GetStepSizeMm() << "mm (" << m_Parameters->GetStepSizeMm()/m_Parameters->GetMinVoxelSizeMm() << "*vox)" << std::endl; std::cout << "StreamlineTracking - Seeds per voxel: " << m_Parameters->m_SeedsPerVoxel << std::endl; std::cout << "StreamlineTracking - Max. tract length: " << m_Parameters->m_MaxTractLengthMm << "mm" << std::endl; std::cout << "StreamlineTracking - Min. tract length: " << m_Parameters->m_MinTractLengthMm << "mm" << std::endl; std::cout << "StreamlineTracking - Max. num. tracts: " << m_Parameters->m_MaxNumFibers << std::endl; std::cout << "StreamlineTracking - Loop check: " << m_Parameters->GetLoopCheckDeg() << "°" << std::endl; + if (m_Parameters->m_SecondOrder) + std::cout << "StreamlineTracking - Using second order streamline integration" << std::endl; + else + std::cout << "StreamlineTracking - Using first order streamline integration" << std::endl; std::cout << "StreamlineTracking - Num. neighborhood samples: " << m_Parameters->m_NumSamples << std::endl; std::cout << "StreamlineTracking - Max. sampling distance: " << m_Parameters->GetSamplingDistanceMm() << "mm (" << m_Parameters->GetSamplingDistanceMm()/m_Parameters->GetMinVoxelSizeMm() << "*vox)" << std::endl; std::cout << "StreamlineTracking - Deflection modifier: " << m_Parameters->m_DeflectionMod << std::endl; std::cout << "StreamlineTracking - Use stop votes: " << m_Parameters->m_StopVotes << std::endl; std::cout << "StreamlineTracking - Only frontal samples: " << m_Parameters->m_OnlyForwardSamples << std::endl; if (m_TrackingPriorHandler!=nullptr) std::cout << "StreamlineTracking - Using directional prior for tractography (w=" << m_Parameters->m_Weight << ")" << std::endl; if (m_DemoMode) { std::cout << "StreamlineTracking - Running in demo mode"; std::cout << "StreamlineTracking - Starting streamline tracking using 1 thread" << std::endl; } else std::cout << "StreamlineTracking - Starting streamline tracking using " << omp_get_max_threads() << " threads" << std::endl; } void StreamlineTrackingFilter::CalculateNewPosition(itk::Point<float, 3>& pos, vnl_vector_fixed<float, 3>& dir) { pos[0] += dir[0]*m_Parameters->GetStepSizeMm(); pos[1] += dir[1]*m_Parameters->GetStepSizeMm(); pos[2] += dir[2]*m_Parameters->GetStepSizeMm(); } std::vector< vnl_vector_fixed<float,3> > StreamlineTrackingFilter::CreateDirections(unsigned int NPoints) { std::vector< vnl_vector_fixed<float,3> > pointshell; if (NPoints<2) return pointshell; std::vector< double > theta; theta.resize(NPoints); std::vector< double > phi; phi.resize(NPoints); auto C = sqrt(4*itk::Math::pi); phi[0] = 0.0; phi[NPoints-1] = 0.0; for(unsigned int i=0; i<NPoints; i++) { theta[i] = acos(-1.0+2.0*i/(NPoints-1.0)) - itk::Math::pi / 2.0; if( i>0 && i<NPoints-1) { phi[i] = (phi[i-1] + C / sqrt(NPoints*(1-(-1.0+2.0*i/(NPoints-1.0))*(-1.0+2.0*i/(NPoints-1.0))))); // % (2*DIST_POINTSHELL_PI); } } for(unsigned int i=0; i<NPoints; i++) { vnl_vector_fixed<float,3> d; d[0] = static_cast<float>(cos(theta[i]) * cos(phi[i])); d[1] = static_cast<float>(cos(theta[i]) * sin(phi[i])); d[2] = static_cast<float>(sin(theta[i])); pointshell.push_back(d); } return pointshell; } vnl_vector_fixed<float,3> StreamlineTrackingFilter::GetNewDirection(const itk::Point<float, 3> &pos, std::deque<vnl_vector_fixed<float, 3> >& olddirs, itk::Index<3> &oldIndex) { if (m_DemoMode) { m_SamplingPointset->Clear(); m_AlternativePointset->Clear(); m_StopVotePointset->Clear(); } vnl_vector_fixed<float,3> direction; direction.fill(0); if (mitk::imv::IsInsideMask<float>(pos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator) && !mitk::imv::IsInsideMask<float>(pos, m_Parameters->m_InterpolateRoiImages, m_StopInterpolator)) direction = m_TrackingHandler->ProposeDirection(pos, olddirs, oldIndex); // get direction proposal at current streamline position else return direction; int stop_votes = 0; int possible_stop_votes = 0; if (!olddirs.empty()) { vnl_vector_fixed<float,3> olddir = olddirs.back(); std::vector< vnl_vector_fixed<float,3> > probeVecs = CreateDirections(m_Parameters->m_NumSamples); itk::Point<double, 3> sample_pos; unsigned int alternatives = 1; for (unsigned int i=0; i<probeVecs.size(); i++) { vnl_vector_fixed<float,3> d; bool is_stop_voter = false; if (!m_Parameters->m_FixRandomSeed && m_Parameters->m_RandomSampling) { d[0] = static_cast<float>(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); d[1] = static_cast<float>(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); d[2] = static_cast<float>(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); d.normalize(); d *= static_cast<float>(m_TrackingHandler->GetRandDouble(0, static_cast<double>(m_Parameters->GetSamplingDistanceMm()))); } else { d = probeVecs.at(i); float dot = dot_product(d, olddir); if (m_Parameters->m_StopVotes && dot>0.7f) { is_stop_voter = true; possible_stop_votes++; } else if (m_Parameters->m_OnlyForwardSamples && dot<0) continue; d *= m_Parameters->GetSamplingDistanceMm(); } sample_pos[0] = pos[0] + d[0]; sample_pos[1] = pos[1] + d[1]; sample_pos[2] = pos[2] + d[2]; vnl_vector_fixed<float,3> tempDir; tempDir.fill(0.0); if (mitk::imv::IsInsideMask<float>(sample_pos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator)) tempDir = m_TrackingHandler->ProposeDirection(sample_pos, olddirs, oldIndex); // sample neighborhood if (tempDir.magnitude()>static_cast<float>(mitk::eps)) { direction += tempDir; if(m_DemoMode) m_SamplingPointset->InsertPoint(i, sample_pos); } else if (m_Parameters->m_AvoidStop && olddir.magnitude()>0.5f) // out of white matter { if (is_stop_voter) stop_votes++; if (m_DemoMode) m_StopVotePointset->InsertPoint(i, sample_pos); float dot = dot_product(d, olddir); if (dot >= 0.0f) // in front of plane defined by pos and olddir d = -d + 2*dot*olddir; // reflect else d = -d; // invert // look a bit further into the other direction sample_pos[0] = pos[0] + d[0]; sample_pos[1] = pos[1] + d[1]; sample_pos[2] = pos[2] + d[2]; alternatives++; vnl_vector_fixed<float,3> tempDir; tempDir.fill(0.0); if (mitk::imv::IsInsideMask<float>(sample_pos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator)) tempDir = m_TrackingHandler->ProposeDirection(sample_pos, olddirs, oldIndex); // sample neighborhood if (tempDir.magnitude()>static_cast<float>(mitk::eps)) // are we back in the white matter? { direction += d * m_Parameters->m_DeflectionMod; // go into the direction of the white matter direction += tempDir; // go into the direction of the white matter direction at this location if(m_DemoMode) m_AlternativePointset->InsertPoint(alternatives, sample_pos); } else { if (m_DemoMode) m_StopVotePointset->InsertPoint(i, sample_pos); } } else { if (m_DemoMode) m_StopVotePointset->InsertPoint(i, sample_pos); if (is_stop_voter) stop_votes++; } } } bool valid = false; if (direction.magnitude()>0.001f && (possible_stop_votes==0 || static_cast<float>(stop_votes)/possible_stop_votes<0.5f) ) { direction.normalize(); valid = true; } else direction.fill(0); if (m_TrackingPriorHandler!=nullptr && (m_Parameters->m_NewDirectionsFromPrior || valid)) { vnl_vector_fixed<float,3> prior = m_TrackingPriorHandler->ProposeDirection(pos, olddirs, oldIndex); if (prior.magnitude()>0.001f) { prior.normalize(); if (dot_product(prior,direction)<0) prior *= -1; direction = (1.0f-m_Parameters->m_Weight) * direction + m_Parameters->m_Weight * prior; direction.normalize(); } else if (m_Parameters->m_RestrictToPrior) direction.fill(0.0); } return direction; } float StreamlineTrackingFilter::FollowStreamline(itk::Point<float, 3> pos, vnl_vector_fixed<float,3> dir, FiberType* fib, DirectionContainer* container, float tractLength, bool front, bool &exclude) { vnl_vector_fixed<float,3> zero_dir; zero_dir.fill(0.0); std::deque< vnl_vector_fixed<float,3> > last_dirs; for (unsigned int i=0; i<m_Parameters->m_NumPreviousDirections-1; i++) last_dirs.push_back(zero_dir); for (int step=0; step< 5000; step++) { itk::Index<3> oldIndex; m_TrackingHandler->WorldToIndex(pos, oldIndex); // get new position CalculateNewPosition(pos, dir); if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask<float>(pos, m_Parameters->m_InterpolateRoiImages, m_ExclusionInterpolator)) { exclude = true; return tractLength; } if (m_AbortTracking) return tractLength; // if yes, add new point to streamline dir.normalize(); if (front) { fib->push_front(pos); container->push_front(dir); } else { fib->push_back(pos); container->push_back(dir); } tractLength += m_Parameters->GetStepSizeMm(); if (m_Parameters->GetLoopCheckDeg()>=0 && CheckCurvature(container, front)>m_Parameters->GetLoopCheckDeg()) return tractLength; if (tractLength>m_Parameters->m_MaxTractLengthMm) return tractLength; if (m_DemoMode && !m_Parameters->m_OutputProbMap) // CHECK: warum sind die samplingpunkte der streamline in der visualisierung immer einen schritt voras? { #pragma omp critical { m_BuildFibersReady++; m_Tractogram.push_back(*fib); BuildFibers(true); m_Stop = true; while (m_Stop){ } } } last_dirs.push_back(dir); if (last_dirs.size()>m_Parameters->m_NumPreviousDirections) last_dirs.pop_front(); dir = GetNewDirection(pos, last_dirs, oldIndex); + if (m_Parameters->m_SecondOrder) + { + itk::Point<float, 3> dpos = pos; + dpos[0] += dir[0]*m_Parameters->GetStepSizeMm()*0.5; + dpos[1] += dir[1]*m_Parameters->GetStepSizeMm()*0.5; + dpos[2] += dir[2]*m_Parameters->GetStepSizeMm()*0.5; + dir = GetNewDirection(dpos, last_dirs, oldIndex); + } while (m_PauseTracking){} if (dir.magnitude()<0.0001f) return tractLength; } return tractLength; } float StreamlineTrackingFilter::CheckCurvature(DirectionContainer* fib, bool front) { if (fib->size()<8) return 0; float m_Distance = std::max(m_Parameters->GetMinVoxelSizeMm()*4, m_Parameters->GetStepSizeMm()*8); float dist = 0; std::vector< vnl_vector_fixed< float, 3 > > vectors; vnl_vector_fixed< float, 3 > meanV; meanV.fill(0); float dev = 0; if (front) { int c = 0; while(dist<m_Distance && c<static_cast<int>(fib->size())-1) { dist += m_Parameters->GetStepSizeMm(); vnl_vector_fixed< float, 3 > v = fib->at(static_cast<unsigned int>(c)); if (dot_product(v,meanV)<0) v = -v; vectors.push_back(v); meanV += v; c++; } } else { int c = static_cast<int>(fib->size())-1; while(dist<m_Distance && c>=0) { dist += m_Parameters->GetStepSizeMm(); vnl_vector_fixed< float, 3 > v = fib->at(static_cast<unsigned int>(c)); if (dot_product(v,meanV)<0) v = -v; vectors.push_back(v); meanV += v; c--; } } meanV.normalize(); for (unsigned int c=0; c<vectors.size(); c++) { float angle = std::fabs(dot_product(meanV, vectors.at(c))); if (angle>1.0f) angle = 1.0; dev += acos(angle)*180.0f/static_cast<float>(itk::Math::pi); } if (vectors.size()>0) dev /= vectors.size(); return dev; } std::shared_ptr<mitk::StreamlineTractographyParameters> StreamlineTrackingFilter::GetParameters() const { return m_Parameters; } void StreamlineTrackingFilter::SetParameters(std::shared_ptr< mitk::StreamlineTractographyParameters > Parameters) { m_Parameters = Parameters; } void StreamlineTrackingFilter::SetTrackingPriorHandler(mitk::TrackingDataHandler *TrackingPriorHandler) { m_TrackingPriorHandler = TrackingPriorHandler; } void StreamlineTrackingFilter::GetSeedPointsFromSeedImage() { MITK_INFO << "StreamlineTracking - Calculating seed points."; m_SeedPoints.clear(); typedef ImageRegionConstIterator< ItkFloatImgType > MaskIteratorType; MaskIteratorType sit(m_SeedImage, m_SeedImage->GetLargestPossibleRegion()); sit.GoToBegin(); while (!sit.IsAtEnd()) { if (sit.Value()>0) { ItkFloatImgType::IndexType index = sit.GetIndex(); itk::ContinuousIndex<float, 3> start; start[0] = index[0]; start[1] = index[1]; start[2] = index[2]; itk::Point<float> worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint(start, worldPos); if ( mitk::imv::IsInsideMask<float>(worldPos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator) ) { m_SeedPoints.push_back(worldPos); for (unsigned int s = 1; s < m_Parameters->m_SeedsPerVoxel; s++) { start[0] = index[0] + static_cast<float>(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); start[1] = index[1] + static_cast<float>(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); start[2] = index[2] + static_cast<float>(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); itk::Point<float> worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint(start, worldPos); m_SeedPoints.push_back(worldPos); } } } ++sit; } if (m_SeedPoints.empty()) mitkThrow() << "No valid seed point in seed image! Is your seed image registered with the image you are tracking on?"; } void StreamlineTrackingFilter::GenerateData() { this->BeforeTracking(); if (!m_Parameters->m_FixRandomSeed) { std::random_device rd; std::mt19937 g(rd()); std::shuffle(m_SeedPoints.begin(), m_SeedPoints.end(), g); } m_CurrentTracts = 0; int num_seeds = static_cast<int>(m_SeedPoints.size()); itk::Index<3> zeroIndex; zeroIndex.Fill(0); m_Progress = 0; int i = 0; int print_interval = num_seeds/100; if (print_interval<100) m_Verbose=false; unsigned int trials_per_seed = 1; if(m_Parameters->m_Mode==mitk::TrackingDataHandler::MODE::PROBABILISTIC) trials_per_seed = m_Parameters->m_TrialsPerSeed; #pragma omp parallel while (i<num_seeds && !m_StopTracking) { int temp_i = 0; #pragma omp critical { temp_i = i; i++; } if (temp_i>=num_seeds || m_StopTracking) continue; else if (m_Verbose && i%print_interval==0) #pragma omp critical { m_Progress += static_cast<unsigned int>(print_interval); std::cout << " \r"; if (m_Parameters->m_MaxNumFibers>0) std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << "/" << m_Parameters->m_MaxNumFibers << '\r'; else std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << '\r'; cout.flush(); } const itk::Point<float> worldPos = m_SeedPoints.at(static_cast<unsigned int>(temp_i)); for (unsigned int trials=0; trials<trials_per_seed; ++trials) { FiberType fib; DirectionContainer direction_container; float tractLength = 0; unsigned long counter = 0; // get starting direction vnl_vector_fixed<float,3> dir; dir.fill(0.0); std::deque< vnl_vector_fixed<float,3> > olddirs; dir = GetNewDirection(worldPos, olddirs, zeroIndex) * 0.5f; bool exclude = false; if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask<float>(worldPos, m_Parameters->m_InterpolateRoiImages, m_ExclusionInterpolator)) exclude = true; bool success = false; if (dir.magnitude()>0.0001f && !exclude) { // forward tracking tractLength = FollowStreamline(worldPos, dir, &fib, &direction_container, 0, false, exclude); fib.push_front(worldPos); // backward tracking if (!exclude) tractLength = FollowStreamline(worldPos, -dir, &fib, &direction_container, tractLength, true, exclude); counter = fib.size(); if (tractLength>=m_Parameters->m_MinTractLengthMm && counter>=2 && !exclude) { #pragma omp critical if ( IsValidFiber(&fib) ) { if (!m_StopTracking) { if (!m_Parameters->m_OutputProbMap) m_Tractogram.push_back(fib); else FiberToProbmap(&fib); m_CurrentTracts++; success = true; } if (m_Parameters->m_MaxNumFibers > 0 && m_CurrentTracts>=static_cast<unsigned int>(m_Parameters->m_MaxNumFibers)) { if (!m_StopTracking) { std::cout << " \r"; MITK_INFO << "Reconstructed maximum number of tracts (" << m_CurrentTracts << "). Stopping tractography."; } m_StopTracking = true; } } } } if (success || m_Parameters->m_Mode!=MODE::PROBABILISTIC) break; // we only try one seed point multiple times if we use a probabilistic tracker and have not found a valid streamline yet }// trials per seed }// seed points this->AfterTracking(); } bool StreamlineTrackingFilter::IsValidFiber(FiberType* fib) { if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE) { return true; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) && mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint EPS_IN_TARGET chosen!"; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) { if (m_TargetImageSet) { float v1 = mitk::imv::GetImageValue<float>(fib->front(), false, m_TargetInterpolator); float v2 = mitk::imv::GetImageValue<float>(fib->back(), false, m_TargetInterpolator); if ( v1>0.0f && v2>0.0f && v1!=v2 ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint EPS_IN_TARGET_LABELDIFF chosen!"; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_SEED_AND_TARGET) { if (m_TargetImageSet && m_SeedImageSet) { if ( mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_SeedInterpolator) && mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; if ( mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_SeedInterpolator) && mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target or seed image set but endpoint constraint EPS_IN_SEED_AND_TARGET chosen!"; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::MIN_ONE_EP_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) || mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint MIN_ONE_EP_IN_TARGET chosen!"; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::ONE_EP_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) && !mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; if ( !mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) && mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint ONE_EP_IN_TARGET chosen!"; } else if (m_Parameters->m_EpConstraints==EndpointConstraints::NO_EP_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask<float>(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) || mitk::imv::IsInsideMask<float>(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return false; return true; } else mitkThrow() << "No target image set but endpoint constraint NO_EP_IN_TARGET chosen!"; } return true; } void StreamlineTrackingFilter::FiberToProbmap(FiberType* fib) { ItkDoubleImgType::IndexType last_idx; last_idx.Fill(0); for (auto p : *fib) { ItkDoubleImgType::IndexType idx; m_OutputProbabilityMap->TransformPhysicalPointToIndex(p, idx); if (idx != last_idx) { if (m_OutputProbabilityMap->GetLargestPossibleRegion().IsInside(idx)) m_OutputProbabilityMap->SetPixel(idx, m_OutputProbabilityMap->GetPixel(idx)+1); last_idx = idx; } } } void StreamlineTrackingFilter::BuildFibers(bool check) { if (m_BuildFibersReady<omp_get_num_threads() && check) return; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); for (unsigned int i=0; i<m_Tractogram.size(); i++) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); FiberType fib = m_Tractogram.at(i); for (FiberType::iterator it = fib.begin(); it!=fib.end(); ++it) { vtkIdType id = vNewPoints->InsertNextPoint((*it).GetDataPointer()); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } if (check) for (int i=0; i<m_BuildFibersReady; i++) m_Tractogram.pop_back(); m_BuildFibersReady = 0; m_FiberPolyData->SetPoints(vNewPoints); m_FiberPolyData->SetLines(vNewLines); m_BuildFibersFinished = true; } void StreamlineTrackingFilter::AfterTracking() { if (m_Verbose) std::cout << " \r"; if (!m_Parameters->m_OutputProbMap) { MITK_INFO << "Reconstructed " << m_Tractogram.size() << " fibers."; MITK_INFO << "Generating polydata "; BuildFibers(false); } else { itk::RescaleIntensityImageFilter< ItkDoubleImgType, ItkDoubleImgType >::Pointer filter = itk::RescaleIntensityImageFilter< ItkDoubleImgType, ItkDoubleImgType >::New(); filter->SetInput(m_OutputProbabilityMap); filter->SetOutputMaximum(1.0); filter->SetOutputMinimum(0.0); filter->Update(); m_OutputProbabilityMap = filter->GetOutput(); } MITK_INFO << "done"; m_EndTime = std::chrono::system_clock::now(); std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(m_EndTime - m_StartTime); std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(m_EndTime - m_StartTime); std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(m_EndTime - m_StartTime); mm %= 60; ss %= 60; MITK_INFO << "Tracking took " << hh.count() << "h, " << mm.count() << "m and " << ss.count() << "s"; m_SeedPoints.clear(); } void StreamlineTrackingFilter::SetDicomProperties(mitk::FiberBundle::Pointer fib) { std::string model_code_value = "-"; std::string model_code_meaning = "-"; std::string algo_code_value = "-"; std::string algo_code_meaning = "-"; if ( m_Parameters->m_Mode==MODE::DETERMINISTIC && dynamic_cast<mitk::TrackingHandlerTensor*>(m_TrackingHandler)) { algo_code_value = "sup181_ee01"; algo_code_meaning = "Deterministic"; if (m_Parameters->m_F > 0.99 && m_Parameters->m_G < 0.01) { if (m_Parameters->m_InterpolateTractographyData) { algo_code_value = "sup181_ee08"; algo_code_meaning = "Euler"; } else { algo_code_value = "sup181_ee04"; algo_code_meaning = "FACT"; } } else if (m_Parameters->m_G > 0.99 && m_Parameters->m_F < 0.01) { algo_code_value = "sup181_ee06"; algo_code_meaning = "TEND"; } } else if (m_Parameters->m_Mode==MODE::DETERMINISTIC) { algo_code_value = "sup181_ee01"; algo_code_meaning = "Deterministic"; } else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) { algo_code_value = "sup181_ee02"; algo_code_meaning = "Probabilistic"; } if (dynamic_cast<mitk::TrackingHandlerTensor*>(m_TrackingHandler) || (dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler) && dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->GetIsOdfFromTensor() ) ) { if ( dynamic_cast<mitk::TrackingHandlerTensor*>(m_TrackingHandler) && dynamic_cast<mitk::TrackingHandlerTensor*>(m_TrackingHandler)->GetNumTensorImages()>1 ) { model_code_value = "sup181_bb02"; model_code_meaning = "Multi Tensor"; } else { model_code_value = "sup181_bb01"; model_code_meaning = "Single Tensor"; } } else if (dynamic_cast<mitk::TrackingHandlerRandomForest<6, 28>*>(m_TrackingHandler) || dynamic_cast<mitk::TrackingHandlerRandomForest<6, 100>*>(m_TrackingHandler)) { model_code_value = "sup181_bb03"; model_code_meaning = "Model Free"; } else if (dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)) { model_code_value = "-"; model_code_meaning = "ODF"; } else if (dynamic_cast<mitk::TrackingHandlerPeaks*>(m_TrackingHandler)) { model_code_value = "-"; model_code_meaning = "Peaks"; } fib->SetProperty("DICOM.anatomy.value", mitk::StringProperty::New("T-A0095")); fib->SetProperty("DICOM.anatomy.meaning", mitk::StringProperty::New("White matter of brain and spinal cord")); fib->SetProperty("DICOM.algo_family_code.value", mitk::StringProperty::New(algo_code_value)); fib->SetProperty("DICOM.algo_family_code.meaning", mitk::StringProperty::New(algo_code_meaning)); fib->SetProperty("DICOM.model_code.value", mitk::StringProperty::New(model_code_value)); fib->SetProperty("DICOM.model_code.meaning", mitk::StringProperty::New(model_code_meaning)); } } diff --git a/Modules/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp b/Modules/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp index cfb7b10..ae7d042 100644 --- a/Modules/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp +++ b/Modules/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp @@ -1,439 +1,440 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include <mitkImageCast.h> #include <mitkImageToItk.h> #include <mitkTensorImage.h> #include <mitkOdfImage.h> #include <mitkIOUtil.h> #include <mitkFiberBundle.h> #include <itkStreamlineTrackingFilter.h> #include <Algorithms/TrackingHandlers/mitkTrackingDataHandler.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h> #include <Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h> #include <itkDiffusionTensor3D.h> #include <mitkTestingMacros.h> #include <mitkTestFixture.h> #include <mitkFiberBundle.h> #include <omp.h> #include <itksys/SystemTools.hxx> #include <mitkEqual.h> #include <mitkStreamlineTractographyParameters.h> class mitkStreamlineTractographyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkStreamlineTractographyTestSuite); MITK_TEST(Test_Peak1); MITK_TEST(Test_Peak2); MITK_TEST(Test_Tensor1); MITK_TEST(Test_Tensor2); MITK_TEST(Test_Tensor3); MITK_TEST(Test_Odf1); MITK_TEST(Test_Odf2); MITK_TEST(Test_Odf3); MITK_TEST(Test_Odf4); MITK_TEST(Test_Odf5); MITK_TEST(Test_Odf6); CPPUNIT_TEST_SUITE_END(); typedef itk::VectorImage< short, 3> ItkDwiType; private: public: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ typedef itk::Image<float, 3> ItkFloatImgType; mitk::TrackingHandlerOdf::ItkOdfImageType::Pointer itk_odf_image; mitk::TrackingHandlerTensor::ItkTensorImageType::ConstPointer itk_tensor_image; mitk::TrackingHandlerPeaks::PeakImgType::Pointer itk_peak_image; ItkFloatImgType::Pointer itk_seed_image; ItkFloatImgType::Pointer itk_mask_image; ItkFloatImgType::Pointer itk_gfa_image; float gfa_threshold; float odf_threshold; float peak_threshold; std::shared_ptr<mitk::StreamlineTractographyParameters> params; itk::StreamlineTrackingFilter::Pointer tracker; void setUp() override { omp_set_num_threads(1); gfa_threshold = 0.2f; odf_threshold = 0.1f; peak_threshold = 0.1f; mitk::Image::Pointer odf_image = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/qball_image.qbi")); mitk::Image::Pointer tensor_image = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/tensor_image.dti")); mitk::Image::Pointer peak_image = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/qball_peak_image.nii.gz")); mitk::Image::Pointer seed_image = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/seed_image.nii.gz")); mitk::Image::Pointer mask_image = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/mask_image.nii.gz")); mitk::Image::Pointer gfa_image = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/gfa_image.nii.gz")); params = std::make_shared<mitk::StreamlineTractographyParameters>(); params->m_FixRandomSeed = true; + params->m_SecondOrder = false; params->m_InterpolateRoiImages = false; params->SetLoopCheckDeg(-1); // todo: test loop check { typedef mitk::ImageToItk< mitk::TrackingHandlerPeaks::PeakImgType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(peak_image); caster->Update(); itk_peak_image = caster->GetOutput(); } { typedef mitk::ImageToItk< mitk::TrackingHandlerTensor::ItkTensorImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(tensor_image); caster->Update(); itk_tensor_image = caster->GetOutput(); } { typedef mitk::ImageToItk< mitk::TrackingHandlerOdf::ItkOdfImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(odf_image); caster->Update(); itk_odf_image = caster->GetOutput(); } itk_gfa_image = ItkFloatImgType::New(); mitk::CastToItkImage(gfa_image, itk_gfa_image); itk_seed_image = ItkFloatImgType::New(); mitk::CastToItkImage(seed_image, itk_seed_image); itk_mask_image = ItkFloatImgType::New(); mitk::CastToItkImage(mask_image, itk_mask_image); } mitk::FiberBundle::Pointer LoadReferenceFib(std::string filename) { mitk::FiberBundle::Pointer fib = nullptr; if (itksys::SystemTools::FileExists(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename))) { mitk::BaseData::Pointer baseData = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename)).at(0); fib = dynamic_cast<mitk::FiberBundle*>(baseData.GetPointer()); } return fib; } mitk::Image::Pointer LoadReferenceImage(std::string filename) { mitk::Image::Pointer img = nullptr; if (itksys::SystemTools::FileExists(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename))) { img = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename)); } return img; } void SetupTracker(mitk::TrackingDataHandler* handler) { tracker = itk::StreamlineTrackingFilter::New(); // tracker->SetInterpolateMasks(false); // tracker->SetNumberOfSamples(0); // tracker->SetAngularThreshold(-1); tracker->SetMaskImage(itk_mask_image); tracker->SetSeedImage(itk_seed_image); tracker->SetStoppingRegions(nullptr); // tracker->SetSeedsPerVoxel(1); // tracker->SetStepSize(0.5); // tracker->SetSamplingDistance(0.25); // tracker->SetUseStopVotes(true); // tracker->SetOnlyForwardSamples(true); // tracker->SetMinTractLength(20); // tracker->SetMaxNumTracts(-1); tracker->SetTrackingHandler(handler); // tracker->SetUseOutputProbabilityMap(false); tracker->SetParameters(params); } void tearDown() override { } void CheckFibResult(std::string ref_file, mitk::FiberBundle::Pointer test_fib) { mitk::FiberBundle::Pointer ref = LoadReferenceFib(ref_file); if (ref.IsNull()) { mitk::IOUtil::Save(test_fib, mitk::IOUtil::GetTempPath()+ref_file); CPPUNIT_FAIL("Reference file not found. Saving test file to " + mitk::IOUtil::GetTempPath() + ref_file); } else { bool is_equal = ref->Equals(test_fib); if (!is_equal) { mitk::IOUtil::Save(test_fib, mitk::IOUtil::GetTempPath()+ref_file); CPPUNIT_FAIL("Tractograms are not equal! Saving test file to " + mitk::IOUtil::GetTempPath() + ref_file); } } } void CheckImageResult(std::string ref_file, mitk::Image::Pointer test_img) { mitk::Image::Pointer ref = LoadReferenceImage(ref_file); if (ref.IsNull()) { mitk::IOUtil::Save(test_img, mitk::IOUtil::GetTempPath()+ref_file); CPPUNIT_FAIL("Reference file not found. Saving test file to " + mitk::IOUtil::GetTempPath() + ref_file); } else { MITK_ASSERT_EQUAL(test_img, ref, "Images should be equal"); } } void Test_Peak1() { mitk::TrackingHandlerPeaks* handler = new mitk::TrackingHandlerPeaks(); handler->SetPeakImage(itk_peak_image); params->m_Cutoff = peak_threshold; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Peak1.fib", outFib); delete handler; } void Test_Peak2() { mitk::TrackingHandlerPeaks* handler = new mitk::TrackingHandlerPeaks(); handler->SetPeakImage(itk_peak_image); params->m_Cutoff = peak_threshold; params->m_InterpolateTractographyData = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Peak2.fib", outFib); delete handler; } void Test_Tensor1() { mitk::TrackingHandlerTensor* handler = new mitk::TrackingHandlerTensor(); handler->SetTensorImage(itk_tensor_image); params->m_Cutoff = gfa_threshold; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Tensor1.fib", outFib); delete handler; } void Test_Tensor2() { mitk::TrackingHandlerTensor* handler = new mitk::TrackingHandlerTensor(); handler->SetTensorImage(itk_tensor_image); params->m_Cutoff = gfa_threshold; params->m_InterpolateTractographyData = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Tensor2.fib", outFib); delete handler; } void Test_Tensor3() { mitk::TrackingHandlerTensor* handler = new mitk::TrackingHandlerTensor(); handler->SetTensorImage(itk_tensor_image); params->m_Cutoff = gfa_threshold; params->m_InterpolateTractographyData = false; params->m_F = 0; params->m_G = 1; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Tensor3.fib", outFib); delete handler; } void Test_Odf1() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); params->m_Cutoff = gfa_threshold; params->m_OdfCutoff = 0; params->m_SharpenOdfs = 1; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf1.fib", outFib); delete handler; } void Test_Odf2() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); params->m_Cutoff = gfa_threshold; params->m_OdfCutoff = 0; params->m_SharpenOdfs = 8; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf2.fib", outFib); delete handler; } void Test_Odf3() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); params->m_Cutoff = gfa_threshold; params->m_OdfCutoff = 0; params->m_SharpenOdfs = 1; params->m_InterpolateTractographyData = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf3.fib", outFib); delete handler; } void Test_Odf4() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); params->m_Cutoff = gfa_threshold; params->m_OdfCutoff = 0; params->m_SharpenOdfs = 1; params->m_SeedsPerVoxel = 3; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf4.fib", outFib); delete handler; } void Test_Odf5() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); params->m_Cutoff = gfa_threshold; params->m_OdfCutoff = 0; params->m_SeedsPerVoxel = 10; params->m_SharpenOdfs = 8; params->m_Mode = mitk::TrackingDataHandler::MODE::PROBABILISTIC; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf5.fib", outFib); delete handler; } void Test_Odf6() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); params->m_Cutoff = gfa_threshold; params->m_OdfCutoff = 0; params->m_SeedsPerVoxel = 10; params->m_SharpenOdfs = 8; params->m_Mode = mitk::TrackingDataHandler::MODE::PROBABILISTIC; params->m_OutputProbMap = true; SetupTracker(handler); tracker->Update(); itk::StreamlineTrackingFilter::ItkDoubleImgType::Pointer outImg = tracker->GetOutputProbabilityMap(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); mitk::IOUtil::Save(img, mitk::IOUtil::GetTempPath()+"Test_Odf6.nrrd"); CheckImageResult("Test_Odf6.nrrd", img); delete handler; } }; MITK_TEST_SUITE_REGISTRATION(mitkStreamlineTractography) diff --git a/Modules/FiberTracking/mitkStreamlineTractographyParameters.cpp b/Modules/FiberTracking/mitkStreamlineTractographyParameters.cpp index 9d059e0..8199e24 100644 --- a/Modules/FiberTracking/mitkStreamlineTractographyParameters.cpp +++ b/Modules/FiberTracking/mitkStreamlineTractographyParameters.cpp @@ -1,367 +1,369 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include <boost/foreach.hpp> #include <mitkLexicalCast.h> #include <itkImageFileWriter.h> #include <itkImageFileReader.h> #include <mitkLog.h> #include <algorithm> #include <string> #include <mitkStreamlineTractographyParameters.h> #include <mitkDiffusionFunctionCollection.h> mitk::StreamlineTractographyParameters::StreamlineTractographyParameters() { AutoAdjust(); } mitk::StreamlineTractographyParameters::~StreamlineTractographyParameters() { } void mitk::StreamlineTractographyParameters::SaveParameters(std::string filename) { if(filename.empty()) return; if(".stp"!=filename.substr(filename.size()-4, 4)) filename += ".stp"; const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } boost::property_tree::ptree parameters; parameters.put("seeding.seeds_per_voxel", m_SeedsPerVoxel); parameters.put("seeding.trials_per_seed", m_TrialsPerSeed); parameters.put("seeding.max_num_fibers", m_MaxNumFibers); parameters.put("seeding.interactive_radius_mm", m_InteractiveRadiusMm); parameters.put("seeding.num_interactive_seeds", m_NumInteractiveSeeds); parameters.put("seeding.enable_interactive", m_EnableInteractive); parameters.put("roi_constraints.ep_constraints", m_EpConstraints); parameters.put("tractography.mode", m_Mode); parameters.put("tractography.sharpen_odfs", m_SharpenOdfs); parameters.put("tractography.cutoff", m_Cutoff); parameters.put("tractography.odf_cutoff", m_OdfCutoff); parameters.put("tractography.step_size_vox", m_StepSizeVox); parameters.put("tractography.min_tract_length_mm", m_MinTractLengthMm); parameters.put("tractography.max_tract_length_mm", m_MaxTractLengthMm); parameters.put("tractography.angluar_threshold_deg", m_AngularThresholdDeg); parameters.put("tractography.loop_check_deg", m_LoopCheckDeg); parameters.put("tractography.f", m_F); parameters.put("tractography.g", m_G); parameters.put("tractography.fix_seed", m_FixRandomSeed); + parameters.put("tractography.second_order", m_SecondOrder); parameters.put("tractography.peak_jitter", m_PeakJitter); parameters.put("prior.weight", m_Weight); parameters.put("prior.restrict_to_prior", m_RestrictToPrior); parameters.put("prior.new_directions", m_NewDirectionsFromPrior); parameters.put("prior.flip_x", m_PriorFlipX); parameters.put("prior.flip_y", m_PriorFlipY); parameters.put("prior.flip_z", m_PriorFlipZ); parameters.put("nsampling.num_samples", m_NumSamples); parameters.put("nsampling.sampling_distance_vox", m_SamplingDistanceVox); parameters.put("nsampling.only_frontal", m_OnlyForwardSamples); parameters.put("nsampling.stop_votes", m_StopVotes); parameters.put("data_handling.flip_x", m_FlipX); parameters.put("data_handling.flip_y", m_FlipY); parameters.put("data_handling.flip_z", m_FlipZ); parameters.put("data_handling.interpolate_tracto_data", m_InterpolateTractographyData); parameters.put("data_handling.interpolate_roi_images", m_InterpolateRoiImages); parameters.put("output.compress", m_CompressFibers); parameters.put("output.compression", m_Compression); parameters.put("output.prob_map", m_OutputProbMap); boost::property_tree::json_parser::write_json(filename, parameters, std::locale(), true); // try{ // itk::ImageFileWriter<ItkFloatImgType>::Pointer writer = itk::ImageFileWriter<ItkFloatImgType>::New(); // writer->SetFileName(filename+"_FMAP.nii.gz"); // writer->SetInput(m_SignalGen.m_FrequencyMap); // writer->Update(); // } // catch(...) // { // MITK_INFO << "No frequency map saved."; // } setlocale(LC_ALL, currLocale.c_str()); } template< class ParameterType > ParameterType mitk::StreamlineTractographyParameters::ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential) { try { return v.second.get<ParameterType>(tag); } catch (...) { if (essential) { mitkThrow() << "Parameter file corrupted. Essential tag is missing: '" << tag << "'"; } MITK_INFO << "Tag '" << tag << "' not found. Using default value '" << defaultValue << "'."; return defaultValue; } } void mitk::StreamlineTractographyParameters::LoadParameters(std::string filename) { if(filename.empty()) { return; } const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } boost::property_tree::ptree parameterTree; boost::property_tree::json_parser::read_json( filename, parameterTree ); BOOST_FOREACH( boost::property_tree::ptree::value_type const& v1, parameterTree ) { if( v1.first == "seeding" ) { m_SeedsPerVoxel = ReadVal<unsigned int>(v1,"seeds_per_voxel", m_SeedsPerVoxel); m_TrialsPerSeed = ReadVal<unsigned int>(v1,"trials_per_seed", m_TrialsPerSeed); m_MaxNumFibers = ReadVal<int>(v1,"max_num_fibers", m_MaxNumFibers); m_InteractiveRadiusMm = ReadVal<float>(v1,"interactive_radius_mm", m_InteractiveRadiusMm); m_NumInteractiveSeeds = ReadVal<unsigned int>(v1,"num_interactive_seeds", m_NumInteractiveSeeds); m_EnableInteractive = ReadVal<bool>(v1,"enable_interactive", m_EnableInteractive); } else if( v1.first == "roi_constraints" ) { switch( ReadVal<int>(v1,"ep_constraints", 0) ) { default: m_EpConstraints = EndpointConstraints::NONE; break; case 1: m_EpConstraints = EndpointConstraints::EPS_IN_TARGET; break; case 2: m_EpConstraints = EndpointConstraints::EPS_IN_TARGET_LABELDIFF; break; case 3: m_EpConstraints = EndpointConstraints::EPS_IN_SEED_AND_TARGET; break; case 4: m_EpConstraints = EndpointConstraints::MIN_ONE_EP_IN_TARGET; break; case 5: m_EpConstraints = EndpointConstraints::ONE_EP_IN_TARGET; break; case 6: m_EpConstraints = EndpointConstraints::NO_EP_IN_TARGET; break; } } else if( v1.first == "tractography" ) { if(ReadVal<int>(v1,"mode", 0) == 0) m_Mode = MODE::DETERMINISTIC; else m_Mode = MODE::PROBABILISTIC; m_SharpenOdfs = ReadVal<int>(v1,"sharpen_odfs", m_SharpenOdfs); m_Cutoff = ReadVal<float>(v1,"cutoff", m_Cutoff); m_OdfCutoff = ReadVal<float>(v1,"odf_cutoff", m_OdfCutoff); SetStepSizeVox(ReadVal<float>(v1,"step_size_vox", m_StepSizeVox)); m_MinTractLengthMm = ReadVal<float>(v1,"min_tract_length_mm", m_MinTractLengthMm); m_MaxTractLengthMm = ReadVal<float>(v1,"max_tract_length_mm", m_MaxTractLengthMm); SetAngularThresholdDeg(ReadVal<float>(v1,"angluar_threshold_deg", m_AngularThresholdDeg)); SetLoopCheckDeg(ReadVal<float>(v1,"loop_check_deg", m_LoopCheckDeg)); m_F = ReadVal<float>(v1,"f", m_F); m_G = ReadVal<float>(v1,"g", m_G); m_FixRandomSeed = ReadVal<bool>(v1,"fix_seed", m_FixRandomSeed); + m_SecondOrder = ReadVal<bool>(v1,"second_order", m_SecondOrder); m_PeakJitter = ReadVal<float>(v1,"peak_jitter", m_PeakJitter); } else if( v1.first == "prior" ) { m_Weight = ReadVal<float>(v1,"weight", m_Weight); m_RestrictToPrior = ReadVal<bool>(v1,"restrict_to_prior", m_RestrictToPrior); m_NewDirectionsFromPrior = ReadVal<bool>(v1,"new_directions", m_NewDirectionsFromPrior); m_PriorFlipX = ReadVal<bool>(v1,"flip_x", m_PriorFlipX); m_PriorFlipY = ReadVal<bool>(v1,"flip_y", m_PriorFlipY); m_PriorFlipZ = ReadVal<bool>(v1,"flip_z", m_PriorFlipZ); } else if( v1.first == "nsampling" ) { m_NumSamples = ReadVal<unsigned int>(v1,"num_samples", m_NumSamples); m_SamplingDistanceVox = ReadVal<float>(v1,"sampling_distance_vox", m_SamplingDistanceVox); m_OnlyForwardSamples = ReadVal<bool>(v1,"only_frontal", m_OnlyForwardSamples); m_StopVotes = ReadVal<bool>(v1,"stop_votes", m_StopVotes); } else if( v1.first == "data_handling" ) { m_FlipX = ReadVal<bool>(v1,"flip_x", m_FlipX); m_FlipY = ReadVal<bool>(v1,"flip_y", m_FlipY); m_FlipZ = ReadVal<bool>(v1,"flip_z", m_FlipZ); m_InterpolateTractographyData = ReadVal<bool>(v1,"interpolate_tracto_data", m_InterpolateTractographyData); m_InterpolateRoiImages = ReadVal<bool>(v1,"interpolate_roi_images", m_InterpolateRoiImages); } else if( v1.first == "output" ) { m_CompressFibers = ReadVal<bool>(v1,"compress", m_CompressFibers); m_Compression = ReadVal<float>(v1,"compression", m_Compression); m_OutputProbMap = ReadVal<bool>(v1,"prob_map", m_OutputProbMap); } } // try // { // itk::ImageFileReader<ItkFloatImgType>::Pointer reader = itk::ImageFileReader<ItkFloatImgType>::New(); // reader->SetFileName(filename+"_FMAP.nrrd"); // if ( itksys::SystemTools::FileExists(filename+"_FMAP.nii.gz") ) // reader->SetFileName(filename+"_FMAP.nii.gz"); // else if ( itksys::SystemTools::FileExists(filename+"_FMAP.nii") ) // reader->SetFileName(filename+"_FMAP.nii"); // else // reader->SetFileName(filename+"_FMAP.nrrd"); // reader->Update(); // m_SignalGen.m_FrequencyMap = reader->GetOutput(); // MITK_INFO << "Frequency map loaded."; // } // catch(...) // { // MITK_INFO << "No frequency map found."; // } setlocale(LC_ALL, currLocale.c_str()); } float mitk::StreamlineTractographyParameters::GetSamplingDistanceMm() const { return m_SamplingDistanceMm; } void mitk::StreamlineTractographyParameters::SetSamplingDistanceVox(float sampling_distance_vox) { m_SamplingDistanceVox = sampling_distance_vox; AutoAdjust(); } void mitk::StreamlineTractographyParameters::AutoAdjust() { if (m_StepSizeVox<static_cast<float>(mitk::eps)) m_StepSizeVox = 0.5; m_StepSizeMm = m_StepSizeVox*m_MinVoxelSizeMm; if (m_AngularThresholdDeg<0) { if (m_StepSizeMm/m_MinVoxelSizeMm<=0.966f) // minimum 15° for automatic estimation m_AngularThresholdDot = static_cast<float>(std::cos( 0.5 * itk::Math::pi * static_cast<double>(m_StepSizeMm/m_MinVoxelSizeMm) )); else m_AngularThresholdDot = static_cast<float>(std::cos( 0.5 * itk::Math::pi * 0.966 )); m_AngularThresholdDeg = std::acos(m_AngularThresholdDot)*180.0/itk::Math::pi; } else m_AngularThresholdDot = static_cast<float>(std::cos( static_cast<double>(m_AngularThresholdDeg)*itk::Math::pi/180.0 )); if (m_SamplingDistanceVox<static_cast<float>(mitk::eps)) m_SamplingDistanceVox = m_MinVoxelSizeMm*0.25f; m_SamplingDistanceMm = m_SamplingDistanceVox*m_MinVoxelSizeMm; } float mitk::StreamlineTractographyParameters::GetStepSizeVox() const { return m_StepSizeVox; } float mitk::StreamlineTractographyParameters::GetAngularThresholdDeg() const { return m_AngularThresholdDeg; } float mitk::StreamlineTractographyParameters::GetSamplingDistanceVox() const { return m_SamplingDistanceVox; } float mitk::StreamlineTractographyParameters::GetMinVoxelSizeMm() const { return m_MinVoxelSizeMm; } float mitk::StreamlineTractographyParameters::GetStepSizeMm() const { return m_StepSizeMm; } void mitk::StreamlineTractographyParameters::SetMinVoxelSizeMm(float min_voxel_size_mm) { m_MinVoxelSizeMm = min_voxel_size_mm; AutoAdjust(); } float mitk::StreamlineTractographyParameters::GetAngularThresholdDot() const { return m_AngularThresholdDot; } void mitk::StreamlineTractographyParameters::SetStepSizeVox(float step_size_vox) { m_StepSizeVox = step_size_vox; AutoAdjust(); } float mitk::StreamlineTractographyParameters::GetLoopCheckDeg() const { return m_LoopCheckDeg; } void mitk::StreamlineTractographyParameters::SetLoopCheckDeg(float loop_check_deg) { m_LoopCheckDeg = loop_check_deg; } void mitk::StreamlineTractographyParameters::SetAngularThresholdDeg(float angular_threshold_deg) { m_AngularThresholdDeg = angular_threshold_deg; AutoAdjust(); } diff --git a/Modules/FiberTracking/mitkStreamlineTractographyParameters.h b/Modules/FiberTracking/mitkStreamlineTractographyParameters.h index a9b66fd..50985ed 100644 --- a/Modules/FiberTracking/mitkStreamlineTractographyParameters.h +++ b/Modules/FiberTracking/mitkStreamlineTractographyParameters.h @@ -1,163 +1,164 @@ #pragma once /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include <mitkImage.h> #include <mitkDiffusionPropertyHelper.h> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> #include <MitkFiberTrackingExports.h> namespace mitk { /** * \brief Datastructure to manage streamline tractography parameters. * */ class MITKFIBERTRACKING_EXPORT StreamlineTractographyParameters { public: enum EndpointConstraints { NONE, ///< No constraints on endpoint locations EPS_IN_TARGET, ///< Both EPs are required to be located in the target image EPS_IN_TARGET_LABELDIFF, ///< Both EPs are required to be located in the target image and the image values at the respective position needs to be distinct EPS_IN_SEED_AND_TARGET, ///< One EP is required to be located in the seed image and one in the target image MIN_ONE_EP_IN_TARGET, ///< At least one EP is required to be located in the target image ONE_EP_IN_TARGET, ///< Exactly one EP is required to be located in the target image NO_EP_IN_TARGET ///< No EP is allowed to be located in the target image }; enum MODE { DETERMINISTIC, PROBABILISTIC }; typedef itk::Image<float, 3> ItkFloatImgType; typedef itk::Image<double, 3> ItkDoubleImgType; typedef itk::Image<unsigned char, 3> ItkUcharImgType; StreamlineTractographyParameters(); StreamlineTractographyParameters(const StreamlineTractographyParameters ¶ms) = default; ~StreamlineTractographyParameters(); void SaveParameters(std::string filename); ///< Save image generation parameters to .stp file. void LoadParameters(std::string filename); ///< Load image generation parameters from .stp file. template< class ParameterType > ParameterType ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential=false); // seeding unsigned int m_SeedsPerVoxel = 1; unsigned int m_TrialsPerSeed = 10; int m_MaxNumFibers = -1; // - seed image // interactive float m_InteractiveRadiusMm = 2; unsigned int m_NumInteractiveSeeds = 50; bool m_EnableInteractive = false; // ROI constraints EndpointConstraints m_EpConstraints; // - mask image // - stop image // - exclusion image // - target image // tractography MODE m_Mode= MODE::DETERMINISTIC; int m_SharpenOdfs = 8; float m_Cutoff = 0.1; // - fa/gfa image float m_OdfCutoff = 0.00025; float m_MinTractLengthMm = 20; float m_MaxTractLengthMm = 400; float m_F = 1; float m_G = 0; bool m_FixRandomSeed = false; unsigned int m_NumPreviousDirections = 1; float m_PeakJitter = 0.01; // actual jitter is drawn from a normal distribution with m_PeakJitter*fabs(direction_value) as standard deviation + bool m_SecondOrder = true; // prior // - peak image float m_Weight = 0.5; bool m_RestrictToPrior = true; bool m_NewDirectionsFromPrior = true; bool m_PriorFlipX = false; bool m_PriorFlipY = false; bool m_PriorFlipZ = false; // neighborhood sampling unsigned int m_NumSamples = 0; bool m_OnlyForwardSamples = false; bool m_StopVotes = false; bool m_AvoidStop = true; bool m_RandomSampling = false; float m_DeflectionMod = 1.0; // data handling bool m_FlipX = false; bool m_FlipY = false; bool m_FlipZ = false; bool m_InterpolateTractographyData = true; bool m_InterpolateRoiImages; bool m_ApplyDirectionMatrix = false; // output and postprocessing bool m_CompressFibers = true; float m_Compression = 0.1; bool m_OutputProbMap = false; float GetAngularThresholdDot() const; float GetAngularThresholdDeg() const; void SetAngularThresholdDeg(float angular_threshold_deg); float GetLoopCheckDeg() const; void SetLoopCheckDeg(float loop_check_deg); float GetStepSizeMm() const; float GetStepSizeVox() const; void SetStepSizeVox(float step_size_vox); float GetSamplingDistanceMm() const; float GetSamplingDistanceVox() const; void SetSamplingDistanceVox(float sampling_distance_vox); void SetMinVoxelSizeMm(float min_voxel_size_mm); float GetMinVoxelSizeMm() const; private: void AutoAdjust(); float m_SamplingDistanceVox = -1; float m_SamplingDistanceMm; float m_AngularThresholdDeg = -1; float m_AngularThresholdDot; float m_LoopCheckDeg = 30; float m_StepSizeVox = -1; float m_StepSizeMm; float m_MinVoxelSizeMm = 1.0; }; } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp index 4738129..4b85564 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp @@ -1,544 +1,575 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include <berryISelectionService.h> #include <berryIWorkbenchWindow.h> // Qmitk #include "QmitkFiberQuantificationView.h" // Qt #include <QMessageBox> // MITK #include <mitkNodePredicateDataType.h> #include <mitkNodePredicateDimension.h> #include <mitkNodePredicateAnd.h> #include <mitkImageCast.h> #include <mitkPeakImage.h> #include <mitkLabelSetImage.h> #include <mitkDICOMSegmentationConstants.h> #include <mitkDICOMSegmentationPropertyHelper.cpp> #include <mitkDICOMQIPropertyHelper.h> // ITK #include <itkTractDensityImageFilter.h> #include <itkTractsToRgbaImageFilter.h> #include <itkTractsToVectorImageFilter.h> #include <itkTractsToFiberEndingsImageFilter.h> #include <itkSignedMaurerDistanceMapImageFilter.h> #include <itkBinaryThinningImageFilter.h> #include <mitkLexicalCast.h> const std::string QmitkFiberQuantificationView::VIEW_ID = "org.mitk.views.fiberquantification"; using namespace mitk; QmitkFiberQuantificationView::QmitkFiberQuantificationView() : QmitkAbstractView() , m_Controls( 0 ) , m_UpsamplingFactor(5) , m_Visible(false) { } // Destructor QmitkFiberQuantificationView::~QmitkFiberQuantificationView() { } void QmitkFiberQuantificationView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberQuantificationViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) ); connect( m_Controls->m_ExtractFiberPeaks, SIGNAL(clicked()), this, SLOT(CalculateFiberDirections()) ); m_Controls->m_TractBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType<mitk::FiberBundle>::Pointer isFib = mitk::TNodePredicateDataType<mitk::FiberBundle>::New(); m_Controls->m_TractBox->SetPredicate( isFib ); m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_ImageBox->SetZeroEntryText("--"); mitk::TNodePredicateDataType<mitk::Image>::Pointer isImagePredicate = mitk::TNodePredicateDataType<mitk::Image>::New(); mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); m_Controls->m_ImageBox->SetPredicate( mitk::NodePredicateAnd::New(isImagePredicate, is3D) ); connect( (QObject*)(m_Controls->m_TractBox), SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect( (QObject*)(m_Controls->m_ImageBox), SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); } } void QmitkFiberQuantificationView::Activated() { } void QmitkFiberQuantificationView::Deactivated() { } void QmitkFiberQuantificationView::Visible() { m_Visible = true; } void QmitkFiberQuantificationView::Hidden() { m_Visible = false; } void QmitkFiberQuantificationView::SetFocus() { m_Controls->m_ProcessFiberBundleButton->setFocus(); } void QmitkFiberQuantificationView::CalculateFiberDirections() { typedef itk::Image<unsigned char, 3> ItkUcharImgType; // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast<mitk::FiberBundle*>(m_SelectedFB.back()->GetData()); itk::TractsToVectorImageFilter<float>::Pointer fOdfFilter = itk::TractsToVectorImageFilter<float>::New(); if (m_SelectedImage.IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); mitk::CastToItkImage<ItkUcharImgType>(m_SelectedImage, itkMaskImage); fOdfFilter->SetMaskImage(itkMaskImage); } // extract directions from fiber bundle fOdfFilter->SetFiberBundle(inputTractogram); fOdfFilter->SetAngularThreshold(cos(m_Controls->m_AngularThreshold->value()*itk::Math::pi/180)); switch (m_Controls->m_FiberDirNormBox->currentIndex()) { case 0: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter<float>::NormalizationMethods::GLOBAL_MAX); break; case 1: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter<float>::NormalizationMethods::SINGLE_VEC_NORM); break; case 2: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter<float>::NormalizationMethods::MAX_VEC_NORM); break; } fOdfFilter->SetOnlyUseMaskGeometry(true); fOdfFilter->SetSizeThreshold(m_Controls->m_PeakThreshold->value()); fOdfFilter->SetMaxNumDirections(m_Controls->m_MaxNumDirections->value()); fOdfFilter->Update(); QString name = m_SelectedFB.back()->GetName().c_str(); if (m_Controls->m_NumDirectionsBox->isChecked()) { mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( fOdfFilter->GetNumDirectionsImage().GetPointer() ); mitkImage->SetVolume( fOdfFilter->GetNumDirectionsImage()->GetBufferPointer() ); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(mitkImage); node->SetName((name+"_NUM_DIRECTIONS").toStdString().c_str()); GetDataStorage()->Add(node, m_SelectedFB.back()); } Image::Pointer mitkImage = dynamic_cast<Image*>(PeakImage::New().GetPointer()); mitk::CastToMitkImage(fOdfFilter->GetDirectionImage(), mitkImage); mitkImage->SetVolume(fOdfFilter->GetDirectionImage()->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(mitkImage); node->SetName( (name+"_DIRECTIONS").toStdString().c_str()); GetDataStorage()->Add(node, m_SelectedFB.back()); } void QmitkFiberQuantificationView::UpdateGui() { m_SelectedFB.clear(); if (m_Controls->m_TractBox->GetSelectedNode().IsNotNull()) m_SelectedFB.push_back(m_Controls->m_TractBox->GetSelectedNode()); m_SelectedImage = nullptr; if (m_Controls->m_ImageBox->GetSelectedNode().IsNotNull()) m_SelectedImage = dynamic_cast<mitk::Image*>(m_Controls->m_ImageBox->GetSelectedNode()->GetData()); m_Controls->m_ProcessFiberBundleButton->setEnabled(!m_SelectedFB.empty()); m_Controls->m_ExtractFiberPeaks->setEnabled(!m_SelectedFB.empty()); } void QmitkFiberQuantificationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& ) { UpdateGui(); } void QmitkFiberQuantificationView::ProcessSelectedBundles() { if ( m_SelectedFB.empty() ){ QMessageBox::information( nullptr, "Warning", "No fibe bundle selected!"); MITK_WARN("QmitkFiberQuantificationView") << "no fibe bundle selected"; return; } int generationMethod = m_Controls->m_GenerationBox->currentIndex(); for( unsigned int i=0; i<m_SelectedFB.size(); i++ ) { mitk::DataNode::Pointer node = m_SelectedFB[i]; if (node.IsNotNull() && dynamic_cast<mitk::FiberBundle*>(node->GetData())) { mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(node->GetData()); QString name(node->GetName().c_str()); DataNode::Pointer newNode = nullptr; switch(generationMethod){ case 0: newNode = GenerateTractDensityImage(fib, TDI_MODE::DENSITY, true, node->GetName()); name += "_TDI"; break; case 1: newNode = GenerateTractDensityImage(fib, TDI_MODE::DENSITY, false, node->GetName()); name += "_TDI"; break; case 2: newNode = GenerateTractDensityImage(fib, TDI_MODE::BINARY, false, node->GetName()); name += "_envelope"; break; case 3: newNode = GenerateColorHeatmap(fib); break; case 4: newNode = GenerateFiberEndingsImage(fib); name += "_fiber_endings"; break; case 5: newNode = GenerateFiberEndingsPointSet(fib); name += "_fiber_endings"; break; case 6: newNode = GenerateDistanceMap(fib); name += "_distance_map"; break; case 7: newNode = GenerateBinarySkeleton(fib); name += "_skeleton"; break; case 8: newNode = GenerateTractDensityImage(fib, TDI_MODE::VISITATION_COUNT, true, node->GetName()); name += "_visitations"; break; + case 9: + newNode = GenerateFiberPointSet(fib); + name += "_fiber_points"; } if (newNode.IsNotNull()) { newNode->SetName(name.toStdString()); GetDataStorage()->Add(newNode); } } } } // generate pointset displaying the fiber endings mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateFiberEndingsPointSet(mitk::FiberBundle::Pointer fib) { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); vtkSmartPointer<vtkPolyData> fiberPolyData = fib->GetFiberPolyData(); int count = 0; int numFibers = fib->GetNumFibers(); for( int i=0; i<numFibers; i++ ) { vtkCell* cell = fiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (numPoints>0) { double* point = points->GetPoint(0); itk::Point<double,3> itkPoint = mitk::imv::GetItkPoint(point); pointSet->InsertPoint(count, itkPoint); count++; } if (numPoints>2) { double* point = points->GetPoint(numPoints-1); itk::Point<double,3> itkPoint = mitk::imv::GetItkPoint(point); pointSet->InsertPoint(count, itkPoint); count++; } } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( pointSet ); return node; } +// generate pointset displaying the fiber endings +mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateFiberPointSet(mitk::FiberBundle::Pointer fib) +{ + mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); + vtkSmartPointer<vtkPolyData> fiberPolyData = fib->GetFiberPolyData(); + + int count = 0; + int numFibers = fib->GetNumFibers(); + for( int i=0; i<numFibers; i++ ) + { + vtkCell* cell = fiberPolyData->GetCell(i); + int numPoints = cell->GetNumberOfPoints(); + vtkPoints* points = cell->GetPoints(); + + for (int j=0; j<numPoints; ++j) + { + double* point = points->GetPoint(j); + itk::Point<double,3> itkPoint = mitk::imv::GetItkPoint(point); + pointSet->InsertPoint(count, itkPoint); + count++; + } + } + + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData( pointSet ); + return node; +} + // generate image displaying the fiber endings mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateFiberEndingsImage(mitk::FiberBundle::Pointer fib) { typedef unsigned int OutPixType; typedef itk::Image<OutPixType,3> OutImageType; typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image OutImageType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate rgba heatmap from fiber bundle mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateColorHeatmap(mitk::FiberBundle::Pointer fib) { typedef itk::RGBAPixel<unsigned char> OutPixType; typedef itk::Image<OutPixType, 3> OutImageType; typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { itk::Image<unsigned char, 3>::Pointer itkImage = itk::Image<unsigned char, 3>::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image<OutPixType,3> OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateBinarySkeleton(mitk::FiberBundle::Pointer fib) { typedef itk::Image<unsigned char, 3> UcharImageType; itk::TractDensityImageFilter< UcharImageType >::Pointer envelope_generator = itk::TractDensityImageFilter< UcharImageType >::New(); envelope_generator->SetFiberBundle(fib); envelope_generator->SetMode(TDI_MODE::BINARY); envelope_generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { UcharImageType::Pointer itkImage = UcharImageType::New(); CastToItkImage(m_SelectedImage, itkImage); envelope_generator->SetInputImage(itkImage); envelope_generator->SetUseImageGeometry(true); } envelope_generator->Update(); itk::BinaryThinningImageFilter<UcharImageType, UcharImageType>::Pointer map_generator = itk::BinaryThinningImageFilter<UcharImageType, UcharImageType>::New(); map_generator->SetInput(envelope_generator->GetOutput()); map_generator->Update(); UcharImageType::Pointer outImg = map_generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateDistanceMap(mitk::FiberBundle::Pointer fib) { typedef itk::Image<unsigned char, 3> UcharImageType; typedef itk::Image<float, 3> FloatImageType; itk::TractDensityImageFilter< UcharImageType >::Pointer envelope_generator = itk::TractDensityImageFilter< UcharImageType >::New(); envelope_generator->SetFiberBundle(fib); envelope_generator->SetMode(TDI_MODE::BINARY); envelope_generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { UcharImageType::Pointer itkImage = UcharImageType::New(); CastToItkImage(m_SelectedImage, itkImage); envelope_generator->SetInputImage(itkImage); envelope_generator->SetUseImageGeometry(true); } envelope_generator->Update(); itk::SignedMaurerDistanceMapImageFilter<UcharImageType, FloatImageType>::Pointer map_generator = itk::SignedMaurerDistanceMapImageFilter<UcharImageType, FloatImageType>::New(); map_generator->SetInput(envelope_generator->GetOutput()); map_generator->SetUseImageSpacing(true); map_generator->SetSquaredDistance(false); map_generator->SetInsideIsPositive(true); map_generator->Update(); FloatImageType::Pointer outImg = map_generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate tract density image from fiber bundle mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateTractDensityImage(mitk::FiberBundle::Pointer fib, TDI_MODE mode, bool absolute, std::string name) { mitk::DataNode::Pointer node = mitk::DataNode::New(); if (mode==TDI_MODE::BINARY) { typedef unsigned char OutPixType; typedef itk::Image<OutPixType, 3> OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetMode(mode); generator->SetOutputAbsoluteValues(absolute); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image<OutPixType,3> OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); if (m_SelectedImage.IsNotNull()) { mitk::LabelSetImage::Pointer multilabelImage = mitk::LabelSetImage::New(); multilabelImage->InitializeByLabeledImage(img); multilabelImage->GetActiveLabelSet()->SetActiveLabel(1); mitk::Label::Pointer label = multilabelImage->GetActiveLabel(); label->SetName("Tractogram"); // Add Segmented Property Category Code Sequence tags (0062, 0003): Sequence defining the general category of this // segment. // (0008,0100) Code Value label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New("T-D000A")); // (0008,0102) Coding Scheme Designator label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New("SRT")); // (0008,0104) Code Meaning label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New("Anatomical Structure")); //------------------------------------------------------------ // Add Segmented Property Type Code Sequence (0062, 000F): Sequence defining the specific property type of this // segment. // (0008,0100) Code Value label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New("DUMMY")); // (0008,0102) Coding Scheme Designator label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New("SRT")); // (0008,0104) Code Meaning label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(name)); //Error: is undeclared// mitk::DICOMQIPropertyHandler::DeriveDICOMSourceProperties(m_SelectedImage, multilabelImage); // init data node node->SetData(multilabelImage); } else { // init data node node->SetData(img); } } else { typedef float OutPixType; typedef itk::Image<OutPixType, 3> OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetMode(mode); generator->SetOutputAbsoluteValues(absolute); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } //generator->SetDoFiberResampling(false); generator->Update(); // get output image typedef itk::Image<OutPixType,3> OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node node->SetData(img); } return node; } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.h b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.h index 53d0c61..9597ada 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.h @@ -1,90 +1,91 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkFiberQuantificationView_h #define QmitkFiberQuantificationView_h #include <QmitkAbstractView.h> #include "ui_QmitkFiberQuantificationViewControls.h" #include <mitkFiberBundle.h> #include <mitkPointSet.h> #include <itkCastImageFilter.h> #include <mitkILifecycleAwarePart.h> #include <itkTractDensityImageFilter.h> /*! \brief Generation of images from fiber bundles (TDI, envelopes, endpoint distribution) and extraction of principal fiber directions from tractograms. */ class QmitkFiberQuantificationView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: typedef itk::Image< unsigned char, 3 > itkUCharImageType; static const std::string VIEW_ID; QmitkFiberQuantificationView(); virtual ~QmitkFiberQuantificationView(); virtual void CreateQtPartControl(QWidget *parent) override; /// /// Sets the focus to an internal widget. /// virtual void SetFocus() override; virtual void Activated() override; virtual void Deactivated() override; virtual void Visible() override; virtual void Hidden() override; protected slots: void ProcessSelectedBundles(); ///< start selected operation on fiber bundle (e.g. tract density image generation) void CalculateFiberDirections(); ///< Calculate main fiber directions from tractogram void UpdateGui(); ///< update button activity etc. dpending on current datamanager selection protected: /// \brief called by QmitkAbstractView when DataManager's selection has changed virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList<mitk::DataNode::Pointer>& nodes) override; Ui::QmitkFiberQuantificationViewControls* m_Controls; std::vector<mitk::DataNode::Pointer> m_SelectedFB; ///< selected fiber bundle nodes mitk::Image::Pointer m_SelectedImage; float m_UpsamplingFactor; ///< upsampling factor for all image generations mitk::DataNode::Pointer GenerateTractDensityImage(mitk::FiberBundle::Pointer fib, TDI_MODE mode, bool absolute, std::string name); mitk::DataNode::Pointer GenerateColorHeatmap(mitk::FiberBundle::Pointer fib); mitk::DataNode::Pointer GenerateFiberEndingsImage(mitk::FiberBundle::Pointer fib); mitk::DataNode::Pointer GenerateFiberEndingsPointSet(mitk::FiberBundle::Pointer fib); + mitk::DataNode::Pointer GenerateFiberPointSet(mitk::FiberBundle::Pointer fib); mitk::DataNode::Pointer GenerateDistanceMap(mitk::FiberBundle::Pointer fib); mitk::DataNode::Pointer GenerateBinarySkeleton(mitk::FiberBundle::Pointer fib); bool m_Visible; }; #endif // _QMITKFIBERTRACKINGVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationViewControls.ui index c2cbb6e..cc451c9 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationViewControls.ui @@ -1,435 +1,440 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QmitkFiberQuantificationViewControls</class> <widget class="QWidget" name="QmitkFiberQuantificationViewControls"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>365</width> <height>581</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <property name="styleSheet"> <string>QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; }</string> </property> <layout class="QGridLayout" name="gridLayout_10"> <property name="verticalSpacing"> <number>25</number> </property> <item row="1" column="0"> <widget class="QGroupBox" name="groupBox_2"> <property name="title"> <string>Fiber-derived images</string> </property> <layout class="QGridLayout" name="gridLayout_3"> <property name="leftMargin"> <number>6</number> </property> <property name="topMargin"> <number>6</number> </property> <property name="rightMargin"> <number>6</number> </property> <property name="bottomMargin"> <number>6</number> </property> <item row="1" column="0"> <widget class="QCommandLinkButton" name="m_ProcessFiberBundleButton"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="maximumSize"> <size> <width>200</width> <height>16777215</height> </size> </property> <property name="font"> <font> <pointsize>11</pointsize> </font> </property> <property name="toolTip"> <string>Perform selected operation on all selected fiber bundles.</string> </property> <property name="text"> <string>Generate Image</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QDoubleSpinBox" name="m_UpsamplingSpinBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Upsampling factor</string> </property> <property name="decimals"> <number>1</number> </property> <property name="minimum"> <double>0.100000000000000</double> </property> <property name="maximum"> <double>10.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>1.000000000000000</double> </property> </widget> </item> <item row="0" column="0"> <widget class="QComboBox" name="m_GenerationBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <item> <property name="text"> <string>Tract Density Image (TDI)</string> </property> </item> <item> <property name="text"> <string>Normalized TDI</string> </property> </item> <item> <property name="text"> <string>Binary Envelope</string> </property> </item> <item> <property name="text"> <string>Fiber Bundle Image</string> </property> </item> <item> <property name="text"> <string>Fiber Endings Image</string> </property> </item> <item> <property name="text"> <string>Fiber Endings Pointset</string> </property> </item> <item> <property name="text"> <string>Distance Map</string> </property> </item> <item> <property name="text"> <string>Binary Skeleton</string> </property> </item> <item> <property name="text"> <string>Streamline Visitation Count</string> </property> </item> + <item> + <property name="text"> + <string>Fiber Pointset</string> + </property> + </item> </widget> </item> </layout> </widget> </item> <item row="2" column="0"> <widget class="QGroupBox" name="groupBox_4"> <property name="title"> <string>Principal Fiber Directions</string> </property> <layout class="QGridLayout" name="gridLayout_6"> <property name="leftMargin"> <number>6</number> </property> <property name="topMargin"> <number>6</number> </property> <property name="rightMargin"> <number>6</number> </property> <property name="bottomMargin"> <number>6</number> </property> <item row="0" column="0"> <widget class="QFrame" name="frame_2"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_5"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="1" column="1"> <widget class="QDoubleSpinBox" name="m_AngularThreshold"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Fiber directions with an angle smaller than the defined threshold are clustered.</string> </property> <property name="decimals"> <number>2</number> </property> <property name="minimum"> <double>0.000000000000000</double> </property> <property name="maximum"> <double>90.000000000000000</double> </property> <property name="singleStep"> <double>1.000000000000000</double> </property> <property name="value"> <double>30.000000000000000</double> </property> </widget> </item> <item row="2" column="1"> <widget class="QDoubleSpinBox" name="m_PeakThreshold"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string><html><head/><body><p>Directions shorter than the defined threshold are discarded.</p></body></html></string> </property> <property name="decimals"> <number>3</number> </property> <property name="maximum"> <double>1.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>0.300000000000000</double> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label"> <property name="text"> <string>Angular Threshold:</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Max. Peaks:</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Size Threshold:</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QSpinBox" name="m_MaxNumDirections"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Maximum number of fiber directions per voxel.</string> </property> <property name="maximum"> <number>100</number> </property> <property name="value"> <number>3</number> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Normalization:</string> </property> </widget> </item> <item row="3" column="1"> <widget class="QComboBox" name="m_FiberDirNormBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="currentIndex"> <number>0</number> </property> <item> <property name="text"> <string>Global maximum</string> </property> </item> <item> <property name="text"> <string>Single vector</string> </property> </item> <item> <property name="text"> <string>Voxel-wise maximum</string> </property> </item> </widget> </item> </layout> </widget> </item> <item row="1" column="0"> <widget class="QCheckBox" name="m_NumDirectionsBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Image containing the number of distinct fiber clusters per voxel.</string> </property> <property name="text"> <string>Output #Directions per Voxel</string> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QCommandLinkButton" name="m_ExtractFiberPeaks"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Generate Directions</string> </property> </widget> </item> </layout> </widget> </item> <item row="0" column="0"> <widget class="QGroupBox" name="groupBox"> <property name="title"> <string>Input Data</string> </property> <layout class="QGridLayout" name="gridLayout"> <property name="leftMargin"> <number>6</number> </property> <property name="topMargin"> <number>6</number> </property> <property name="rightMargin"> <number>6</number> </property> <property name="bottomMargin"> <number>6</number> </property> <item row="0" column="1"> <widget class="QmitkDataStorageComboBox" name="m_TractBox"/> </item> <item row="1" column="1"> <widget class="QmitkDataStorageComboBoxWithSelectNone" name="m_ImageBox"/> </item> <item row="0" column="0"> <widget class="QLabel" name="label_5"> <property name="text"> <string>Tractogram:</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_6"> <property name="text"> <string>Reference Image:</string> </property> </widget> </item> </layout> </widget> </item> <item row="3" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <customwidgets> <customwidget> <class>QmitkDataStorageComboBox</class> <extends>QComboBox</extends> <header location="global">QmitkDataStorageComboBox.h</header> </customwidget> <customwidget> <class>QmitkDataStorageComboBoxWithSelectNone</class> <extends>QComboBox</extends> <header>QmitkDataStorageComboBoxWithSelectNone.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui> diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp index 741505a..09d0d27 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp @@ -1,1216 +1,1218 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include <berryISelectionService.h> #include <berryIWorkbenchWindow.h> #include <berryIStructuredSelection.h> // Qmitk #include "QmitkStreamlineTrackingView.h" #include "QmitkStdMultiWidget.h" // Qt #include <QMessageBox> #include <QFileDialog> // MITK #include <mitkLookupTable.h> #include <mitkLookupTableProperty.h> #include <mitkImageToItk.h> #include <mitkFiberBundle.h> #include <mitkImageCast.h> #include <mitkImageToItk.h> #include <mitkNodePredicateDataType.h> #include <mitkNodePredicateNot.h> #include <mitkNodePredicateAnd.h> #include <mitkNodePredicateProperty.h> #include <mitkNodePredicateDimension.h> #include <mitkOdfImage.h> #include <mitkShImage.h> #include <mitkSliceNavigationController.h> // VTK #include <vtkRenderWindowInteractor.h> #include <vtkPolyData.h> #include <vtkPoints.h> #include <vtkCellArray.h> #include <vtkSmartPointer.h> #include <vtkPolyLine.h> #include <vtkCellData.h> #include <itkTensorImageToOdfImageFilter.h> #include <omp.h> #include <mitkLexicalCast.h> const std::string QmitkStreamlineTrackingView::VIEW_ID = "org.mitk.views.streamlinetracking"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace berry; QmitkStreamlineTrackingWorker::QmitkStreamlineTrackingWorker(QmitkStreamlineTrackingView* view) : m_View(view) { } void QmitkStreamlineTrackingWorker::run() { m_View->m_Tracker->Update(); m_View->m_TrackingThread.quit(); } QmitkStreamlineTrackingView::QmitkStreamlineTrackingView() : m_TrackingWorker(this) , m_Controls(nullptr) , m_FirstTensorProbRun(true) , m_FirstInteractiveRun(true) , m_TrackingHandler(nullptr) , m_ThreadIsRunning(false) , m_DeleteTrackingHandler(false) , m_Visible(false) , m_LastPrior(nullptr) , m_TrackingPriorHandler(nullptr) { m_TrackingWorker.moveToThread(&m_TrackingThread); connect(&m_TrackingThread, SIGNAL(started()), this, SLOT(BeforeThread())); connect(&m_TrackingThread, SIGNAL(started()), &m_TrackingWorker, SLOT(run())); connect(&m_TrackingThread, SIGNAL(finished()), this, SLOT(AfterThread())); m_TrackingTimer = new QTimer(this); } // Destructor QmitkStreamlineTrackingView::~QmitkStreamlineTrackingView() { if (m_Tracker.IsNull()) return; m_Tracker->SetStopTracking(true); m_TrackingThread.wait(); } void QmitkStreamlineTrackingView::CreateQtPartControl( QWidget *parent ) { if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkStreamlineTrackingViewControls; m_Controls->setupUi( parent ); m_Controls->m_FaImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_SeedImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_MaskImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_TargetImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_PriorImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_StopImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_ForestSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_ExclusionImageSelectionWidget->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType<mitk::PeakImage>::Pointer isPeakImagePredicate = mitk::TNodePredicateDataType<mitk::PeakImage>::New(); mitk::TNodePredicateDataType<mitk::Image>::Pointer isImagePredicate = mitk::TNodePredicateDataType<mitk::Image>::New(); mitk::TNodePredicateDataType<mitk::TractographyForest>::Pointer isTractographyForest = mitk::TNodePredicateDataType<mitk::TractographyForest>::New(); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinaryPredicate = mitk::NodePredicateNot::New( isBinaryPredicate ); mitk::NodePredicateAnd::Pointer isNotABinaryImagePredicate = mitk::NodePredicateAnd::New( isImagePredicate, isNotBinaryPredicate ); mitk::NodePredicateDimension::Pointer dimensionPredicate = mitk::NodePredicateDimension::New(3); m_Controls->m_ForestSelectionWidget->SetNodePredicate(isTractographyForest); m_Controls->m_FaImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isNotABinaryImagePredicate, dimensionPredicate) ); m_Controls->m_FaImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_FaImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_SeedImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_SeedImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_SeedImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_MaskImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_MaskImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_MaskImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_StopImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_StopImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_StopImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_TargetImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_TargetImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_TargetImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_PriorImageSelectionWidget->SetNodePredicate( isPeakImagePredicate ); m_Controls->m_PriorImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_PriorImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_ExclusionImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_ExclusionImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_ExclusionImageSelectionWidget->SetSelectionIsOptional(true); connect( m_TrackingTimer, SIGNAL(timeout()), this, SLOT(TimerUpdate()) ); connect( m_Controls->m_SaveParametersButton, SIGNAL(clicked()), this, SLOT(SaveParameters()) ); connect( m_Controls->m_LoadParametersButton, SIGNAL(clicked()), this, SLOT(LoadParameters()) ); connect( m_Controls->commandLinkButton_2, SIGNAL(clicked()), this, SLOT(StopTractography()) ); connect( m_Controls->commandLinkButton, SIGNAL(clicked()), this, SLOT(DoFiberTracking()) ); connect( m_Controls->m_InteractiveBox, SIGNAL(stateChanged(int)), this, SLOT(ToggleInteractive()) ); connect( m_Controls->m_ModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui()) ); connect( m_Controls->m_FaImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::DeleteTrackingHandler ); connect( m_Controls->m_ModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(DeleteTrackingHandler()) ); connect( m_Controls->m_OutputProbMap, SIGNAL(stateChanged(int)), this, SLOT(OutputStyleSwitched()) ); connect( m_Controls->m_SeedImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_ModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_StopImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_TargetImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_PriorImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_ExclusionImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_MaskImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_FaImageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_ForestSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::ForestSwitched ); connect( m_Controls->m_ForestSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkStreamlineTrackingView::OnParameterChanged ); connect( m_Controls->m_SeedsPerVoxelBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_NumFibersBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_ScalarThresholdBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_OdfCutoffBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_StepSizeBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SamplingDistanceBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_AngularThresholdBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_MinTractLengthBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_MaxTractLengthBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_fBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_gBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_NumSamplesBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SeedRadiusBox, SIGNAL(editingFinished()), this, SLOT(InteractiveSeedChanged()) ); connect( m_Controls->m_NumSeedsBox, SIGNAL(editingFinished()), this, SLOT(InteractiveSeedChanged()) ); connect( m_Controls->m_OutputProbMap, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SharpenOdfsBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_InterpolationBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_MaskInterpolationBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FlipXBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FlipYBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FlipZBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_PriorFlipXBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_PriorFlipYBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_PriorFlipZBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FrontalSamplesBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_StopVotesBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_LoopCheckBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_TrialsPerSeedBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_EpConstraintsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_PeakJitterBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); m_Controls->m_SharpenOdfsBox->editingFinished(); m_Controls->m_SeedsPerVoxelBox->editingFinished(); m_Controls->m_NumFibersBox->editingFinished(); m_Controls->m_ScalarThresholdBox->editingFinished(); m_Controls->m_OdfCutoffBox->editingFinished(); m_Controls->m_StepSizeBox->editingFinished(); m_Controls->m_SamplingDistanceBox->editingFinished(); m_Controls->m_AngularThresholdBox->editingFinished(); m_Controls->m_MinTractLengthBox->editingFinished(); m_Controls->m_MaxTractLengthBox->editingFinished(); m_Controls->m_fBox->editingFinished(); m_Controls->m_gBox->editingFinished(); m_Controls->m_NumSamplesBox->editingFinished(); m_Controls->m_SeedRadiusBox->editingFinished(); m_Controls->m_NumSeedsBox->editingFinished(); m_Controls->m_LoopCheckBox->editingFinished(); m_Controls->m_TrialsPerSeedBox->editingFinished(); m_Controls->m_PeakJitterBox->editingFinished(); StartStopTrackingGui(false); } m_ParameterFile = QDir::currentPath()+"/param.stp"; UpdateGui(); } void QmitkStreamlineTrackingView::ParametersToGui(mitk::StreamlineTractographyParameters& params) { m_Controls->m_SeedRadiusBox->setValue(params.m_InteractiveRadiusMm); m_Controls->m_NumSeedsBox->setValue(params.m_NumInteractiveSeeds); m_Controls->m_InteractiveBox->setChecked(params.m_EnableInteractive); m_Controls->m_ResampleFibersBox->setChecked(params.m_CompressFibers); m_Controls->m_SeedRadiusBox->setValue(params.m_InteractiveRadiusMm); m_Controls->m_NumFibersBox->setValue(params.m_MaxNumFibers); m_Controls->m_ScalarThresholdBox->setValue(params.m_Cutoff); m_Controls->m_fBox->setValue(params.m_F); m_Controls->m_gBox->setValue(params.m_G); m_Controls->m_OdfCutoffBox->setValue(params.m_OdfCutoff); m_Controls->m_SharpenOdfsBox->setValue(params.m_SharpenOdfs); m_Controls->m_PriorWeightBox->setValue(params.m_Weight); m_Controls->m_PriorAsMaskBox->setChecked(params.m_RestrictToPrior); m_Controls->m_NewDirectionsFromPriorBox->setChecked(params.m_NewDirectionsFromPrior); m_Controls->m_PriorFlipXBox->setChecked(params.m_PriorFlipX); m_Controls->m_PriorFlipYBox->setChecked(params.m_PriorFlipY); m_Controls->m_PriorFlipZBox->setChecked(params.m_PriorFlipZ); m_Controls->m_FlipXBox->setChecked(params.m_FlipX); m_Controls->m_FlipYBox->setChecked(params.m_FlipY); m_Controls->m_FlipZBox->setChecked(params.m_FlipZ); m_Controls->m_InterpolationBox->setChecked(params.m_InterpolateTractographyData); m_Controls->m_MaskInterpolationBox->setChecked(params.m_InterpolateRoiImages); m_Controls->m_SeedsPerVoxelBox->setValue(params.m_SeedsPerVoxel); m_Controls->m_StepSizeBox->setValue(params.GetStepSizeVox()); m_Controls->m_SamplingDistanceBox->setValue(params.GetSamplingDistanceVox()); m_Controls->m_StopVotesBox->setChecked(params.m_StopVotes); m_Controls->m_FrontalSamplesBox->setChecked(params.m_OnlyForwardSamples); m_Controls->m_TrialsPerSeedBox->setValue(params.m_TrialsPerSeed); m_Controls->m_NumSamplesBox->setValue(params.m_NumSamples); m_Controls->m_LoopCheckBox->setValue(params.GetLoopCheckDeg()); m_Controls->m_AngularThresholdBox->setValue(params.GetAngularThresholdDeg()); m_Controls->m_MinTractLengthBox->setValue(params.m_MinTractLengthMm); m_Controls->m_MaxTractLengthBox->setValue(params.m_MaxTractLengthMm); m_Controls->m_OutputProbMap->setChecked(params.m_OutputProbMap); m_Controls->m_FixSeedBox->setChecked(params.m_FixRandomSeed); + m_Controls->m_SecondOrderBox->setChecked(params.m_SecondOrder); m_Controls->m_PeakJitterBox->setValue(params.m_PeakJitter); switch (params.m_Mode) { case mitk::TrackingDataHandler::MODE::DETERMINISTIC: m_Controls->m_ModeBox->setCurrentIndex(0); break; case mitk::TrackingDataHandler::MODE::PROBABILISTIC: m_Controls->m_ModeBox->setCurrentIndex(1); break; } switch (params.m_EpConstraints) { case itk::StreamlineTrackingFilter::EndpointConstraints::NONE: m_Controls->m_EpConstraintsBox->setCurrentIndex(0); break; case itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET: m_Controls->m_EpConstraintsBox->setCurrentIndex(1); break; case itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF: m_Controls->m_EpConstraintsBox->setCurrentIndex(2); break; case itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET: m_Controls->m_EpConstraintsBox->setCurrentIndex(3); break; case itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET: m_Controls->m_EpConstraintsBox->setCurrentIndex(4); break; case itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET: m_Controls->m_EpConstraintsBox->setCurrentIndex(5); break; case itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET: m_Controls->m_EpConstraintsBox->setCurrentIndex(6); break; } } std::shared_ptr<mitk::StreamlineTractographyParameters> QmitkStreamlineTrackingView::GetParametersFromGui() { std::shared_ptr<mitk::StreamlineTractographyParameters> params = std::make_shared<mitk::StreamlineTractographyParameters>(); // NOT IN GUI // unsigned int m_NumPreviousDirections = 1; // bool m_AvoidStop = true; // bool m_RandomSampling = false; // float m_DeflectionMod = 1.0; // bool m_ApplyDirectionMatrix = false; // NOT IN GUI BUT AUTOMATICALLY SET if (!m_InputImageNodes.empty()) { float min_sp = 999; auto spacing = dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetSpacing(); if (spacing[0] < min_sp) min_sp = spacing[0]; if (spacing[1] < min_sp) min_sp = spacing[1]; if (spacing[2] < min_sp) min_sp = spacing[2]; params->m_Compression = min_sp/10; } params->m_InteractiveRadiusMm = m_Controls->m_SeedRadiusBox->value(); params->m_NumInteractiveSeeds = m_Controls->m_NumSeedsBox->value(); params->m_EnableInteractive = m_Controls->m_InteractiveBox->isChecked(); params->m_CompressFibers = m_Controls->m_ResampleFibersBox->isChecked(); params->m_InteractiveRadiusMm = m_Controls->m_SeedRadiusBox->value(); params->m_MaxNumFibers = m_Controls->m_NumFibersBox->value(); params->m_Cutoff = static_cast<float>(m_Controls->m_ScalarThresholdBox->value()); params->m_F = static_cast<float>(m_Controls->m_fBox->value()); params->m_G = static_cast<float>(m_Controls->m_gBox->value()); params->m_OdfCutoff = static_cast<float>(m_Controls->m_OdfCutoffBox->value()); params->m_SharpenOdfs = m_Controls->m_SharpenOdfsBox->value(); params->m_Weight = static_cast<float>(m_Controls->m_PriorWeightBox->value()); params->m_RestrictToPrior = m_Controls->m_PriorAsMaskBox->isChecked(); params->m_NewDirectionsFromPrior = m_Controls->m_NewDirectionsFromPriorBox->isChecked(); params->m_PriorFlipX = m_Controls->m_PriorFlipXBox->isChecked(); params->m_PriorFlipY = m_Controls->m_PriorFlipYBox->isChecked(); params->m_PriorFlipZ = m_Controls->m_PriorFlipZBox->isChecked(); params->m_FlipX = m_Controls->m_FlipXBox->isChecked(); params->m_FlipY = m_Controls->m_FlipYBox->isChecked(); params->m_FlipZ = m_Controls->m_FlipZBox->isChecked(); params->m_InterpolateTractographyData = m_Controls->m_InterpolationBox->isChecked(); params->m_InterpolateRoiImages = m_Controls->m_MaskInterpolationBox->isChecked(); params->m_SeedsPerVoxel = m_Controls->m_SeedsPerVoxelBox->value(); params->SetStepSizeVox(m_Controls->m_StepSizeBox->value()); params->SetSamplingDistanceVox(m_Controls->m_SamplingDistanceBox->value()); params->m_StopVotes = m_Controls->m_StopVotesBox->isChecked(); params->m_OnlyForwardSamples = m_Controls->m_FrontalSamplesBox->isChecked(); params->m_TrialsPerSeed = m_Controls->m_TrialsPerSeedBox->value(); params->m_NumSamples = m_Controls->m_NumSamplesBox->value(); params->SetLoopCheckDeg(m_Controls->m_LoopCheckBox->value()); params->SetAngularThresholdDeg(m_Controls->m_AngularThresholdBox->value()); params->m_MinTractLengthMm = m_Controls->m_MinTractLengthBox->value(); params->m_MaxTractLengthMm = m_Controls->m_MaxTractLengthBox->value(); params->m_OutputProbMap = m_Controls->m_OutputProbMap->isChecked(); params->m_FixRandomSeed = m_Controls->m_FixSeedBox->isChecked(); + params->m_SecondOrder = m_Controls->m_SecondOrderBox->isChecked(); params->m_PeakJitter = static_cast<float>(m_Controls->m_PeakJitterBox->value()); switch (m_Controls->m_ModeBox->currentIndex()) { case 0: params->m_Mode = mitk::TrackingDataHandler::MODE::DETERMINISTIC; break; case 1: params->m_Mode = mitk::TrackingDataHandler::MODE::PROBABILISTIC; break; default: params->m_Mode = mitk::TrackingDataHandler::MODE::DETERMINISTIC; } switch (m_Controls->m_EpConstraintsBox->currentIndex()) { case 0: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::NONE; break; case 1: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET; break; case 2: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF; break; case 3: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET; break; case 4: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET; break; case 5: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET; break; case 6: params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET; break; } return params; } void QmitkStreamlineTrackingView::SaveParameters() { QString filename = QFileDialog::getSaveFileName( 0, tr("Save Tractography Parameters"), m_ParameterFile, tr("Streamline Tractography Parameters (*.stp)") ); if(filename.isEmpty() || filename.isNull()) return; m_ParameterFile = filename; auto params = GetParametersFromGui(); params->SaveParameters(m_ParameterFile.toStdString()); } void QmitkStreamlineTrackingView::LoadParameters() { QString filename = QFileDialog::getOpenFileName( 0, tr("Load Tractography Parameters"), m_ParameterFile, tr("Streamline Tractography Parameters (*.stp)") ); if(filename.isEmpty() || filename.isNull()) return; m_ParameterFile = filename; mitk::StreamlineTractographyParameters params; params.LoadParameters(m_ParameterFile.toStdString()); ParametersToGui(params); } void QmitkStreamlineTrackingView::StopTractography() { if (m_Tracker.IsNull()) return; m_Tracker->SetStopTracking(true); } void QmitkStreamlineTrackingView::TimerUpdate() { if (m_Tracker.IsNull()) return; QString status_text(m_Tracker->GetStatusText().c_str()); m_Controls->m_StatusTextBox->setText(status_text); } void QmitkStreamlineTrackingView::BeforeThread() { m_TrackingTimer->start(1000); } void QmitkStreamlineTrackingView::AfterThread() { auto params = m_Tracker->GetParameters(); m_TrackingTimer->stop(); if (!params->m_OutputProbMap) { vtkSmartPointer<vtkPolyData> fiberBundle = m_Tracker->GetFiberPolyData(); if (!m_Controls->m_InteractiveBox->isChecked() && fiberBundle->GetNumberOfLines() == 0) { QMessageBox warnBox; warnBox.setWindowTitle("Warning"); warnBox.setText("No fiberbundle was generated!"); warnBox.setDetailedText("No fibers were generated using the chosen parameters. Typical reasons are:\n\n- Cutoff too high. Some images feature very low FA/GFA/peak size. Try to lower this parameter.\n- Angular threshold too strict. Try to increase this parameter.\n- A small step sizes also means many steps to go wrong. Especially in the case of probabilistic tractography. Try to adjust the angular threshold.\n- In case of probabilistic tractography, try to increase the parameter for ODF sharpening (for ODF and tensor tractography) or decrease the peak jitter (for peak tracking)."); warnBox.setIcon(QMessageBox::Warning); warnBox.exec(); if (m_InteractivePointSetNode.IsNotNull()) m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); StartStopTrackingGui(false); if (m_DeleteTrackingHandler) DeleteTrackingHandler(); UpdateGui(); return; } mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(fiberBundle); fib->SetTrackVisHeader(dynamic_cast<mitk::Image*>(m_ParentNode->GetData())->GetGeometry()); if (params->m_CompressFibers && fiberBundle->GetNumberOfLines()>0) fib->Compress(params->m_Compression); fib->ColorFibersByOrientation(); m_Tracker->SetDicomProperties(fib); mitk::DiffusionPropertyHelper::CopyDICOMProperties(m_ParentNode->GetData(), fib); if (m_Controls->m_InteractiveBox->isChecked()) { if (m_InteractiveNode.IsNull()) { m_InteractiveNode = mitk::DataNode::New(); QString name("Interactive"); m_InteractiveNode->SetName(name.toStdString()); GetDataStorage()->Add(m_InteractiveNode); } m_InteractiveNode->SetData(fib); m_InteractiveNode->SetFloatProperty("Fiber2DSliceThickness", params->GetMinVoxelSizeMm()/2); if (auto renderWindowPart = this->GetRenderWindowPart()) renderWindowPart->RequestUpdate(); } else { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(fib); QString name("FiberBundle_"); name += m_ParentNode->GetName().c_str(); name += "_Streamline"; node->SetName(name.toStdString()); node->SetFloatProperty("Fiber2DSliceThickness", params->GetMinVoxelSizeMm()/2); GetDataStorage()->Add(node, m_ParentNode); } } else { TrackerType::ItkDoubleImgType::Pointer outImg = m_Tracker->GetOutputProbabilityMap(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); mitk::DiffusionPropertyHelper::CopyDICOMProperties(m_ParentNode->GetData(), img); if (m_Controls->m_InteractiveBox->isChecked()) { if (m_InteractiveNode.IsNull()) { m_InteractiveNode = mitk::DataNode::New(); QString name("Interactive"); m_InteractiveNode->SetName(name.toStdString()); GetDataStorage()->Add(m_InteractiveNode); } m_InteractiveNode->SetData(img); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::JET_TRANSPARENT); mitk::LookupTableProperty::Pointer lut_prop = mitk::LookupTableProperty::New(); lut_prop->SetLookupTable(lut); m_InteractiveNode->SetProperty("LookupTable", lut_prop); m_InteractiveNode->SetProperty("opacity", mitk::FloatProperty::New(0.5)); m_InteractiveNode->SetFloatProperty("Fiber2DSliceThickness", params->GetMinVoxelSizeMm()/2); if (auto renderWindowPart = this->GetRenderWindowPart()) renderWindowPart->RequestUpdate(); } else { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); QString name("ProbabilityMap_"); name += m_ParentNode->GetName().c_str(); node->SetName(name.toStdString()); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::JET_TRANSPARENT); mitk::LookupTableProperty::Pointer lut_prop = mitk::LookupTableProperty::New(); lut_prop->SetLookupTable(lut); node->SetProperty("LookupTable", lut_prop); node->SetProperty("opacity", mitk::FloatProperty::New(0.5)); GetDataStorage()->Add(node, m_ParentNode); } } if (m_InteractivePointSetNode.IsNotNull()) m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); StartStopTrackingGui(false); if (m_DeleteTrackingHandler) DeleteTrackingHandler(); UpdateGui(); } void QmitkStreamlineTrackingView::InteractiveSeedChanged(bool posChanged) { if(!CheckAndStoreLastParams(sender()) && !posChanged) return; if (m_ThreadIsRunning || !m_Visible) return; if (!posChanged && (!m_Controls->m_InteractiveBox->isChecked() || !m_Controls->m_ParamUpdateBox->isChecked()) ) return; std::srand(std::time(0)); m_SeedPoints.clear(); itk::Point<double> world_pos = this->GetRenderWindowPart()->GetSelectedPosition(); m_SeedPoints.push_back(world_pos); float radius = m_Controls->m_SeedRadiusBox->value(); int num = m_Controls->m_NumSeedsBox->value(); mitk::PointSet::Pointer pointset = mitk::PointSet::New(); pointset->InsertPoint(0, world_pos); m_InteractivePointSetNode->SetProperty("pointsize", mitk::FloatProperty::New(radius*2)); m_InteractivePointSetNode->SetProperty("point 2D size", mitk::FloatProperty::New(radius*2)); m_InteractivePointSetNode->SetData(pointset); for (int i=1; i<num; i++) { itk::Vector<float> p; p[0] = rand()%1000-500; p[1] = rand()%1000-500; p[2] = rand()%1000-500; p.Normalize(); p *= radius; m_SeedPoints.push_back(world_pos+p); } m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,0,0)); DoFiberTracking(); } bool QmitkStreamlineTrackingView::CheckAndStoreLastParams(QObject* obj) { if (obj!=nullptr) { std::string new_val = ""; if(qobject_cast<QDoubleSpinBox*>(obj)!=nullptr) new_val = boost::lexical_cast<std::string>(qobject_cast<QDoubleSpinBox*>(obj)->value()); else if (qobject_cast<QSpinBox*>(obj)!=nullptr) new_val = boost::lexical_cast<std::string>(qobject_cast<QSpinBox*>(obj)->value()); else return true; if (m_LastTractoParams.find(obj->objectName())==m_LastTractoParams.end()) { m_LastTractoParams[obj->objectName()] = new_val; return false; } else if (m_LastTractoParams.at(obj->objectName()) != new_val) { m_LastTractoParams[obj->objectName()] = new_val; return true; } else if (m_LastTractoParams.at(obj->objectName()) == new_val) return false; } return true; } void QmitkStreamlineTrackingView::OnParameterChanged() { UpdateGui(); if(!CheckAndStoreLastParams(sender())) return; if (m_Controls->m_InteractiveBox->isChecked() && m_Controls->m_ParamUpdateBox->isChecked()) DoFiberTracking(); } void QmitkStreamlineTrackingView::ToggleInteractive() { UpdateGui(); m_Controls->m_SeedsPerVoxelBox->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); m_Controls->m_SeedsPerVoxelLabel->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); m_Controls->m_SeedImageSelectionWidget->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); m_Controls->label_6->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); if ( m_Controls->m_InteractiveBox->isChecked() ) { if (m_FirstInteractiveRun) { QMessageBox::information(nullptr, "Information", "Place and move a spherical seed region anywhere in the image by left-clicking and dragging. If the seed region is colored red, tracking is in progress. If the seed region is colored white, tracking is finished.\nPlacing the seed region for the first time in a newly selected dataset might cause a short delay, since the tracker needs to be initialized."); m_FirstInteractiveRun = false; } QApplication::setOverrideCursor(Qt::PointingHandCursor); QApplication::processEvents(); m_InteractivePointSetNode = mitk::DataNode::New(); m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); m_InteractivePointSetNode->SetName("InteractiveSeedRegion"); mitk::PointSetShapeProperty::Pointer shape_prop = mitk::PointSetShapeProperty::New(); shape_prop->SetValue(mitk::PointSetShapeProperty::PointSetShape::CIRCLE); m_InteractivePointSetNode->SetProperty("Pointset.2D.shape", shape_prop); GetDataStorage()->Add(m_InteractivePointSetNode); m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } else { QApplication::restoreOverrideCursor(); QApplication::processEvents(); m_InteractiveNode = nullptr; m_InteractivePointSetNode = nullptr; m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); disconnect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkStreamlineTrackingView::Activated() { } void QmitkStreamlineTrackingView::Deactivated() { } void QmitkStreamlineTrackingView::Visible() { m_Visible = true; } void QmitkStreamlineTrackingView::Hidden() { m_Visible = false; m_Controls->m_InteractiveBox->setChecked(false); ToggleInteractive(); } void QmitkStreamlineTrackingView::OnSliceChanged() { InteractiveSeedChanged(true); } void QmitkStreamlineTrackingView::SetFocus() { } void QmitkStreamlineTrackingView::DeleteTrackingHandler() { if (!m_ThreadIsRunning && m_TrackingHandler != nullptr) { if (m_TrackingPriorHandler != nullptr) { delete m_TrackingPriorHandler; m_TrackingPriorHandler = nullptr; } delete m_TrackingHandler; m_TrackingHandler = nullptr; m_DeleteTrackingHandler = false; m_LastPrior = nullptr; } else if (m_ThreadIsRunning) { m_DeleteTrackingHandler = true; } } void QmitkStreamlineTrackingView::ForestSwitched() { DeleteTrackingHandler(); } void QmitkStreamlineTrackingView::OutputStyleSwitched() { if (m_InteractiveNode.IsNotNull()) GetDataStorage()->Remove(m_InteractiveNode); m_InteractiveNode = nullptr; } void QmitkStreamlineTrackingView::OnSelectionChanged( berry::IWorkbenchPart::Pointer , const QList<mitk::DataNode::Pointer>& nodes ) { std::vector< mitk::DataNode::Pointer > last_nodes = m_InputImageNodes; m_InputImageNodes.clear(); m_AdditionalInputImages.clear(); bool retrack = false; for( auto node : nodes ) { if( node.IsNotNull() && dynamic_cast<mitk::Image*>(node->GetData()) ) { if( dynamic_cast<mitk::TensorImage*>(node->GetData()) || dynamic_cast<mitk::OdfImage*>(node->GetData()) || dynamic_cast<mitk::ShImage*>(node->GetData()) || dynamic_cast<mitk::PeakImage*>(node->GetData()) || mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast<mitk::Image *>(node->GetData()))) { m_InputImageNodes.push_back(node); retrack = true; } else { mitk::Image* img = dynamic_cast<mitk::Image*>(node->GetData()); if (img!=nullptr && img->GetDimension()==3) m_AdditionalInputImages.push_back(dynamic_cast<mitk::Image*>(node->GetData())); } } } // sometimes the OnSelectionChanged event is sent twice and actually no selection has changed for the first event. We need to catch that. if (last_nodes.size() == m_InputImageNodes.size()) { bool same_nodes = true; for (unsigned int i=0; i<m_InputImageNodes.size(); i++) if (last_nodes.at(i)!=m_InputImageNodes.at(i)) { same_nodes = false; break; } if (same_nodes) return; } DeleteTrackingHandler(); UpdateGui(); if (retrack) OnParameterChanged(); } void QmitkStreamlineTrackingView::UpdateGui() { m_Controls->m_TensorImageLabel->setText("<font color='red'>select in data-manager</font>"); m_Controls->m_fBox->setEnabled(false); m_Controls->m_fLabel->setEnabled(false); m_Controls->m_gBox->setEnabled(false); m_Controls->m_gLabel->setEnabled(false); m_Controls->m_FaImageSelectionWidget->setEnabled(true); m_Controls->mFaImageLabel->setEnabled(true); m_Controls->m_OdfCutoffBox->setEnabled(false); m_Controls->m_OdfCutoffLabel->setEnabled(false); m_Controls->m_SharpenOdfsBox->setEnabled(false); m_Controls->m_ForestSelectionWidget->setVisible(false); m_Controls->m_ForestLabel->setVisible(false); m_Controls->commandLinkButton->setEnabled(false); m_Controls->m_TrialsPerSeedBox->setEnabled(false); m_Controls->m_TrialsPerSeedLabel->setEnabled(false); m_Controls->m_TargetImageSelectionWidget->setEnabled(false); m_Controls->m_TargetImageLabel->setEnabled(false); m_Controls->m_PeakJitterBox->setEnabled(false); if (m_Controls->m_InteractiveBox->isChecked()) { m_Controls->m_InteractiveSeedingFrame->setVisible(true); m_Controls->m_StaticSeedingFrame->setVisible(false); m_Controls->commandLinkButton_2->setVisible(false); m_Controls->commandLinkButton->setVisible(false); } else { m_Controls->m_InteractiveSeedingFrame->setVisible(false); m_Controls->m_StaticSeedingFrame->setVisible(true); m_Controls->commandLinkButton_2->setVisible(m_ThreadIsRunning); m_Controls->commandLinkButton->setVisible(!m_ThreadIsRunning); } if (m_Controls->m_EpConstraintsBox->currentIndex()>0) { m_Controls->m_TargetImageSelectionWidget->setEnabled(true); m_Controls->m_TargetImageLabel->setEnabled(true); } // stuff that is only important for probabilistic tractography if (m_Controls->m_ModeBox->currentIndex()==1) { m_Controls->m_TrialsPerSeedBox->setEnabled(true); m_Controls->m_TrialsPerSeedLabel->setEnabled(true); } if(!m_InputImageNodes.empty()) { if (m_InputImageNodes.size()>1) m_Controls->m_TensorImageLabel->setText( ( std::to_string(m_InputImageNodes.size()) + " images selected").c_str() ); else m_Controls->m_TensorImageLabel->setText(m_InputImageNodes.at(0)->GetName().c_str()); m_Controls->commandLinkButton->setEnabled(!m_Controls->m_InteractiveBox->isChecked() && !m_ThreadIsRunning); m_Controls->m_ScalarThresholdBox->setEnabled(true); m_Controls->m_FaThresholdLabel->setEnabled(true); if ( dynamic_cast<mitk::TensorImage*>(m_InputImageNodes.at(0)->GetData()) ) { if (m_Controls->m_ModeBox->currentIndex()==1) { m_Controls->m_OdfCutoffBox->setEnabled(true); m_Controls->m_OdfCutoffLabel->setEnabled(true); m_Controls->m_SharpenOdfsBox->setEnabled(true); } else { m_Controls->m_fBox->setEnabled(true); m_Controls->m_fLabel->setEnabled(true); m_Controls->m_gBox->setEnabled(true); m_Controls->m_gLabel->setEnabled(true); } } else if ( dynamic_cast<mitk::OdfImage*>(m_InputImageNodes.at(0)->GetData()) || dynamic_cast<mitk::ShImage*>(m_InputImageNodes.at(0)->GetData())) { m_Controls->m_OdfCutoffBox->setEnabled(true); m_Controls->m_OdfCutoffLabel->setEnabled(true); m_Controls->m_SharpenOdfsBox->setEnabled(true); } else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast<mitk::Image *>(m_InputImageNodes.at(0)->GetData())) ) { m_Controls->m_ForestSelectionWidget->setVisible(true); m_Controls->m_ForestLabel->setVisible(true); m_Controls->m_ScalarThresholdBox->setEnabled(false); m_Controls->m_FaThresholdLabel->setEnabled(false); } else if ( dynamic_cast<mitk::PeakImage*>(m_InputImageNodes.at(0)->GetData()) && m_Controls->m_ModeBox->currentIndex()==1) { m_Controls->m_PeakJitterBox->setEnabled(true); } } } void QmitkStreamlineTrackingView::StartStopTrackingGui(bool start) { m_ThreadIsRunning = start; if (!m_Controls->m_InteractiveBox->isChecked()) { m_Controls->commandLinkButton_2->setVisible(start); m_Controls->commandLinkButton->setVisible(!start); m_Controls->m_InteractiveBox->setEnabled(!start); m_Controls->m_StatusTextBox->setVisible(start); } } void QmitkStreamlineTrackingView::DoFiberTracking() { auto params = GetParametersFromGui(); if (m_InputImageNodes.empty()) { QMessageBox::information(nullptr, "Information", "Please select an input image in the datamaneger (tensor, ODF, peak or dMRI image)!"); return; } if (m_ThreadIsRunning || !m_Visible) return; if (m_Controls->m_InteractiveBox->isChecked() && m_SeedPoints.empty()) return; StartStopTrackingGui(true); m_Tracker = TrackerType::New(); if (params->m_EpConstraints == itk::StreamlineTrackingFilter::EndpointConstraints::NONE) m_Tracker->SetTargetRegions(nullptr); if( dynamic_cast<mitk::TensorImage*>(m_InputImageNodes.at(0)->GetData()) ) { if (m_Controls->m_ModeBox->currentIndex()==1) { if (m_InputImageNodes.size()>1) { QMessageBox::information(nullptr, "Information", "Probabilistic tensor tractography is only implemented for single-tensor mode!"); StartStopTrackingGui(false); return; } if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerOdf(); typedef itk::TensorImageToOdfImageFilter< float, float > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( mitk::convert::GetItkTensorFromTensorImage(dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())) ); filter->Update(); dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->SetOdfImage(filter->GetOutput()); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->SetGfaImage(itkImg); } } dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->SetIsOdfFromTensor(true); } else { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerTensor(); for (unsigned int i=0; i<m_InputImageNodes.size(); ++i) dynamic_cast<mitk::TrackingHandlerTensor*>(m_TrackingHandler)->AddTensorImage(mitk::convert::GetItkTensorFromTensorImage(dynamic_cast<mitk::Image*>(m_InputImageNodes.at(i)->GetData())).GetPointer()); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast<mitk::TrackingHandlerTensor*>(m_TrackingHandler)->SetFaImage(itkImg); } } } } else if ( dynamic_cast<mitk::OdfImage*>(m_InputImageNodes.at(0)->GetData()) || dynamic_cast<mitk::ShImage*>(m_InputImageNodes.at(0)->GetData())) { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerOdf(); if (dynamic_cast<mitk::ShImage*>(m_InputImageNodes.at(0)->GetData())) dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->SetOdfImage(mitk::convert::GetItkOdfFromShImage(dynamic_cast<mitk::ShImage*>(m_InputImageNodes.at(0)->GetData()))); else dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->SetOdfImage(mitk::convert::GetItkOdfFromOdfImage(dynamic_cast<mitk::OdfImage*>(m_InputImageNodes.at(0)->GetData()))); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast<mitk::TrackingHandlerOdf*>(m_TrackingHandler)->SetGfaImage(itkImg); } } } else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())) ) { if ( m_Controls->m_ForestSelectionWidget->GetSelectedNode().IsNull() ) { QMessageBox::information(nullptr, "Information", "Not random forest for machine learning based tractography (raw dMRI tractography) selected. Did you accidentally select the raw diffusion-weighted image in the datamanager?"); StartStopTrackingGui(false); return; } if (m_TrackingHandler==nullptr) { mitk::TractographyForest::Pointer forest = dynamic_cast<mitk::TractographyForest*>(m_Controls->m_ForestSelectionWidget->GetSelectedNode()->GetData()); mitk::Image::Pointer dwi = dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData()); std::vector< std::vector< ItkFloatImageType::Pointer > > additionalFeatureImages; additionalFeatureImages.push_back(std::vector< ItkFloatImageType::Pointer >()); for (auto img : m_AdditionalInputImages) { ItkFloatImageType::Pointer itkimg = ItkFloatImageType::New(); mitk::CastToItkImage(img, itkimg); additionalFeatureImages.at(0).push_back(itkimg); } bool forest_valid = false; if (forest->GetNumFeatures()>=100) { params->m_NumPreviousDirections = static_cast<unsigned int>((forest->GetNumFeatures() - (100 + additionalFeatureImages.at(0).size()))/3); m_TrackingHandler = new mitk::TrackingHandlerRandomForest<6, 100>(); dynamic_cast<mitk::TrackingHandlerRandomForest<6, 100>*>(m_TrackingHandler)->AddDwi(dwi); dynamic_cast<mitk::TrackingHandlerRandomForest<6, 100>*>(m_TrackingHandler)->SetAdditionalFeatureImages(additionalFeatureImages); dynamic_cast<mitk::TrackingHandlerRandomForest<6, 100>*>(m_TrackingHandler)->SetForest(forest); forest_valid = dynamic_cast<mitk::TrackingHandlerRandomForest<6, 100>*>(m_TrackingHandler)->IsForestValid(); } else { params->m_NumPreviousDirections = static_cast<unsigned int>((forest->GetNumFeatures() - (28 + additionalFeatureImages.at(0).size()))/3); m_TrackingHandler = new mitk::TrackingHandlerRandomForest<6, 28>(); dynamic_cast<mitk::TrackingHandlerRandomForest<6, 28>*>(m_TrackingHandler)->AddDwi(dwi); dynamic_cast<mitk::TrackingHandlerRandomForest<6, 28>*>(m_TrackingHandler)->SetAdditionalFeatureImages(additionalFeatureImages); dynamic_cast<mitk::TrackingHandlerRandomForest<6, 28>*>(m_TrackingHandler)->SetForest(forest); forest_valid = dynamic_cast<mitk::TrackingHandlerRandomForest<6, 28>*>(m_TrackingHandler)->IsForestValid(); } if (!forest_valid) { QMessageBox::information(nullptr, "Information", "Random forest is invalid. The forest signatue does not match the parameters of TrackingHandlerRandomForest."); StartStopTrackingGui(false); return; } } } else { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerPeaks(); dynamic_cast<mitk::TrackingHandlerPeaks*>(m_TrackingHandler)->SetPeakImage(mitk::convert::GetItkPeakFromPeakImage(dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData()))); } } if (m_Controls->m_InteractiveBox->isChecked()) { m_Tracker->SetSeedPoints(m_SeedPoints); } else if (m_Controls->m_SeedImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_SeedImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetSeedImage(mask); } if (m_Controls->m_MaskImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_MaskImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetMaskImage(mask); } if (m_Controls->m_StopImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_StopImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetStoppingRegions(mask); } if (m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_TargetImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetTargetRegions(mask); } if (m_Controls->m_PriorImageSelectionWidget->GetSelectedNode().IsNotNull()) { auto prior_params = GetParametersFromGui(); if (m_LastPrior!=m_Controls->m_PriorImageSelectionWidget->GetSelectedNode() || m_TrackingPriorHandler==nullptr) { typedef mitk::ImageToItk< mitk::TrackingHandlerPeaks::PeakImgType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(dynamic_cast<mitk::PeakImage*>(m_Controls->m_PriorImageSelectionWidget->GetSelectedNode()->GetData())); caster->SetCopyMemFlag(true); caster->Update(); mitk::TrackingHandlerPeaks::PeakImgType::Pointer itkImg = caster->GetOutput(); m_TrackingPriorHandler = new mitk::TrackingHandlerPeaks(); dynamic_cast<mitk::TrackingHandlerPeaks*>(m_TrackingPriorHandler)->SetPeakImage(itkImg); m_LastPrior = m_Controls->m_PriorImageSelectionWidget->GetSelectedNode(); } prior_params->m_FlipX = m_Controls->m_PriorFlipXBox->isChecked(); prior_params->m_FlipY = m_Controls->m_PriorFlipYBox->isChecked(); prior_params->m_FlipZ = m_Controls->m_PriorFlipZBox->isChecked(); m_TrackingPriorHandler->SetParameters(prior_params); m_Tracker->SetTrackingPriorHandler(m_TrackingPriorHandler); } else if (m_Controls->m_PriorImageSelectionWidget->GetSelectedNode().IsNull()) m_Tracker->SetTrackingPriorHandler(nullptr); if (m_Controls->m_ExclusionImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast<mitk::Image*>(m_Controls->m_ExclusionImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetExclusionRegions(mask); } if (params->m_EpConstraints!=itk::StreamlineTrackingFilter::EndpointConstraints::NONE && m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNull()) { QMessageBox::information(nullptr, "Error", "Endpoint constraints are used but no target image is set!"); StartStopTrackingGui(false); return; } else if (params->m_EpConstraints==itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET && (m_Controls->m_SeedImageSelectionWidget->GetSelectedNode().IsNull()|| m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNull()) ) { QMessageBox::information(nullptr, "Error", "Endpoint constraint EPS_IN_SEED_AND_TARGET is used but no target or no seed image is set!"); StartStopTrackingGui(false); return; } float min_sp = 999; auto spacing = dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetSpacing(); if (spacing[0] < min_sp) min_sp = spacing[0]; if (spacing[1] < min_sp) min_sp = spacing[1]; if (spacing[2] < min_sp) min_sp = spacing[2]; params->m_Compression = min_sp/10; - float max_size = 0; - for (int i=0; i<3; ++i) - if (dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetExtentInMM(i)>max_size) - max_size = dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetExtentInMM(i); - if (params->m_MinTractLengthMm >= max_size) - { - MITK_INFO << "Max. image size: " << max_size << "mm"; - MITK_INFO << "Min. tract length: " << params->m_MinTractLengthMm << "mm"; - QMessageBox::information(nullptr, "Error", "Minimum tract length exceeds the maximum image extent! Recommended value is about 1/10 of the image extent."); - StartStopTrackingGui(false); - return; - } - else if (params->m_MinTractLengthMm > max_size/10) - { - MITK_INFO << "Max. image size: " << max_size << "mm"; - MITK_INFO << "Min. tract length: " << params->m_MinTractLengthMm << "mm"; - MITK_WARN << "Minimum tract length is larger than 1/10 the maximum image extent! Decrease recommended."; - } +// float max_size = 0; +// for (int i=0; i<3; ++i) +// if (dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetExtentInMM(i)>max_size) +// max_size = dynamic_cast<mitk::Image*>(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetExtentInMM(i); +// if (params->m_MinTractLengthMm >= max_size) +// { +// MITK_INFO << "Max. image size: " << max_size << "mm"; +// MITK_INFO << "Min. tract length: " << params->m_MinTractLengthMm << "mm"; +// QMessageBox::information(nullptr, "Error", "Minimum tract length exceeds the maximum image extent! Recommended value is about 1/10 of the image extent."); +// StartStopTrackingGui(false); +// return; +// } +// else if (params->m_MinTractLengthMm > max_size/10) +// { +// MITK_INFO << "Max. image size: " << max_size << "mm"; +// MITK_INFO << "Min. tract length: " << params->m_MinTractLengthMm << "mm"; +// MITK_WARN << "Minimum tract length is larger than 1/10 the maximum image extent! Decrease recommended."; +// } m_Tracker->SetParameters(params); m_Tracker->SetTrackingHandler(m_TrackingHandler); m_Tracker->SetVerbose(!m_Controls->m_InteractiveBox->isChecked()); m_ParentNode = m_InputImageNodes.at(0); m_TrackingThread.start(QThread::LowestPriority); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui index 65ef468..5e8138d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui @@ -1,1655 +1,1674 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QmitkStreamlineTrackingViewControls</class> <widget class="QWidget" name="QmitkStreamlineTrackingViewControls"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>453</width> <height>859</height> </rect> </property> <property name="minimumSize"> <size> <width>0</width> <height>0</height> </size> </property> <property name="windowTitle"> <string>QmitkTemplate</string> </property> <property name="styleSheet"> <string>QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; }</string> </property> <layout class="QGridLayout" name="gridLayout_11"> <property name="topMargin"> <number>3</number> </property> <property name="bottomMargin"> <number>3</number> </property> <property name="horizontalSpacing"> <number>0</number> </property> <property name="verticalSpacing"> <number>40</number> </property> <item row="1" column="0"> <widget class="QFrame" name="frame_6"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_12"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>15</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <property name="horizontalSpacing"> <number>6</number> </property> <property name="verticalSpacing"> <number>15</number> </property> <item row="1" column="0"> <widget class="QTextEdit" name="m_StatusTextBox"> <property name="enabled"> <bool>true</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="readOnly"> <bool>true</bool> </property> </widget> </item> <item row="4" column="0"> <widget class="QFrame" name="frame_11"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_21"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <property name="verticalSpacing"> <number>0</number> </property> <item row="0" column="1"> <widget class="QCommandLinkButton" name="m_SaveParametersButton"> <property name="enabled"> <bool>true</bool> </property> <property name="toolTip"> <string>Save parameters to json file</string> </property> <property name="text"> <string>Save Parameters</string> </property> <property name="icon"> <iconset resource="../../resources/QmitkTractography.qrc"> <normaloff>:/QmitkTractography/download.png</normaloff>:/QmitkTractography/download.png</iconset> </property> </widget> </item> <item row="0" column="0"> <widget class="QCommandLinkButton" name="m_LoadParametersButton"> <property name="enabled"> <bool>true</bool> </property> <property name="toolTip"> <string>Load parameters from json file</string> </property> <property name="text"> <string>Load Parameters</string> </property> <property name="icon"> <iconset resource="../../resources/QmitkTractography.qrc"> <normaloff>:/QmitkTractography/upload.png</normaloff>:/QmitkTractography/upload.png</iconset> </property> </widget> </item> </layout> </widget> </item> <item row="3" column="0"> <widget class="QCommandLinkButton" name="commandLinkButton"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Start Tractography</string> </property> <property name="icon"> <iconset resource="../../resources/QmitkTractography.qrc"> <normaloff>:/QmitkTractography/right.png</normaloff>:/QmitkTractography/right.png</iconset> </property> </widget> </item> <item row="2" column="0"> <widget class="QCommandLinkButton" name="commandLinkButton_2"> <property name="enabled"> <bool>true</bool> </property> <property name="toolTip"> <string>Stop tractography and return all fibers reconstructed until now.</string> </property> <property name="text"> <string>Stop Tractography</string> </property> <property name="icon"> <iconset resource="../../resources/QmitkTractography.qrc"> <normaloff>:/QmitkTractography/stop.png</normaloff>:/QmitkTractography/stop.png</iconset> </property> </widget> </item> <item row="0" column="0"> <widget class="QFrame" name="frame_9"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_19"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="0" column="0"> <widget class="QLabel" name="label_2"> <property name="toolTip"> <string>Input Image. ODF, tensor and peak images are currently supported.</string> </property> <property name="text"> <string>Input Image:</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLabel" name="m_TensorImageLabel"> <property name="toolTip"> <string>Input Image. ODF, tensor, peak, and, in case of ML tractography, raw diffusion-weighted images are currently supported.</string> </property> <property name="text"> <string><html><head/><body><p><span style=" color:#ff0000;">select image in data-manager</span></p></body></html></string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="m_ForestLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Tractography Forest:</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_ForestSelectionWidget" native="true"> <property name="styleSheet"> <string notr="true"/> </property> </widget> </item> </layout> </widget> </item> </layout> </widget> </item> <item row="2" column="0" rowspan="6"> <widget class="QToolBox" name="toolBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>0</width> <height>0</height> </size> </property> <property name="font"> <font> - <weight>75</weight> <bold>true</bold> </font> </property> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="page_seed"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>421</width> <height>254</height> </rect> </property> <attribute name="label"> <string>Seeding</string> </attribute> <attribute name="toolTip"> <string>Specify how, where and how many tractography seed points are placed.</string> </attribute> <layout class="QGridLayout" name="gridLayout_17"> <item row="0" column="0"> <widget class="QFrame" name="m_InteractiveSeedingFrame"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_15"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="1" column="0"> <widget class="QFrame" name="frame_7"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_13"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="2" column="1"> <widget class="QSpinBox" name="m_NumSeedsBox"> <property name="toolTip"> <string>Number of seed points equally distributed around selected position. </string> </property> <property name="minimum"> <number>1</number> </property> <property name="maximum"> <number>9999999</number> </property> <property name="value"> <number>50</number> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="m_FaThresholdLabel_4"> <property name="toolTip"> <string/> </property> <property name="text"> - <string>Radius: </string> + <string>Radius:</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QDoubleSpinBox" name="m_SeedRadiusBox"> <property name="toolTip"> <string>Seedpoints are equally distributed within a sphere centered at the selected position with the specified radius (in mm).</string> </property> <property name="decimals"> <number>2</number> </property> <property name="maximum"> <double>50.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>2.000000000000000</double> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="m_SeedsPerVoxelLabel_4"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Num. Seeds:</string> </property> </widget> </item> </layout> </widget> </item> <item row="0" column="0"> <widget class="QCheckBox" name="m_ParamUpdateBox"> <property name="enabled"> <bool>true</bool> </property> <property name="toolTip"> <string>When checked, parameter changes cause instant retracking while in interactive mode.</string> </property> <property name="text"> <string>Update on Parameter Change </string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> </layout> </widget> </item> <item row="2" column="0"> <widget class="QFrame" name="frame_8"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_16"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="0" column="1"> <widget class="QSpinBox" name="m_TrialsPerSeedBox"> <property name="toolTip"> <string>Try each seed N times until a valid streamline is obtained (only for probabilistic tractography).</string> </property> <property name="statusTip"> <string>Minimum fiber length (in mm)</string> </property> <property name="minimum"> <number>1</number> </property> <property name="maximum"> <number>999</number> </property> <property name="value"> <number>10</number> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="m_TrialsPerSeedLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Trials Per Seed:</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="m_FaThresholdLabel_2"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Max. Num. Fibers:</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QSpinBox" name="m_NumFibersBox"> <property name="toolTip"> <string>Tractography is stopped after the desired number of fibers is reached, even before all seed points are processed (-1 means no limit).</string> </property> <property name="minimum"> <number>-1</number> </property> <property name="maximum"> <number>999999999</number> </property> <property name="value"> <number>-1</number> </property> </widget> </item> </layout> </widget> </item> <item row="1" column="0"> <widget class="QFrame" name="m_StaticSeedingFrame"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_2"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="2" column="0"> <widget class="QLabel" name="m_SeedsPerVoxelLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Seeds per Voxel:</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label_6"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Seed Image:</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QSpinBox" name="m_SeedsPerVoxelBox"> <property name="toolTip"> <string>Number of seed points placed in each voxel.</string> </property> <property name="minimum"> <number>1</number> </property> <property name="maximum"> <number>9999999</number> </property> </widget> </item> <item row="0" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_SeedImageSelectionWidget" native="true"> <property name="styleSheet"> <string notr="true"/> </property> </widget> </item> </layout> </widget> </item> <item row="3" column="0"> <widget class="QCheckBox" name="m_InteractiveBox"> <property name="enabled"> <bool>true</bool> </property> <property name="toolTip"> <string>Dynamically pick a seed location by click into image.</string> </property> <property name="text"> <string>Enable Interactive Tractography</string> </property> </widget> </item> <item row="4" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <widget class="QWidget" name="page_constraints"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>435</width> - <height>184</height> + <width>541</width> + <height>175</height> </rect> </property> <attribute name="label"> <string>ROI Constraints</string> </attribute> <attribute name="toolTip"> <string>Specify various ROI and mask images to constrain the tractography process.</string> </attribute> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <widget class="QLabel" name="label_7"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Mask Image:</string> </property> </widget> </item> <item row="3" column="1"> <widget class="QComboBox" name="m_EpConstraintsBox"> <property name="toolTip"> <string>Select which fibers should be accepted or rejected based on the location of their endpoints.</string> </property> - <property name="sizeAdjustPolicy"> - <enum>QComboBox::AdjustToMinimumContentsLength</enum> - </property> <item> <property name="text"> <string>No Constraints on EP locations</string> </property> </item> <item> <property name="text"> <string>Both EPs in Target Image</string> </property> </item> <item> <property name="text"> <string>Both EPs in Target Image But Different Label</string> </property> </item> <item> <property name="text"> <string>One EP in Seed Image and One EP in Target Image</string> </property> </item> <item> <property name="text"> <string>At Least One EP in Target Image</string> </property> </item> <item> <property name="text"> <string>Exactly One EP in Target Image</string> </property> </item> <item> <property name="text"> <string>No EP in Target Image</string> </property> </item> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="label_10"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Endpoint Constraints:</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_9"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Stop ROI Image:</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_11"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Exclusion ROI Image:</string> </property> </widget> </item> <item row="4" column="0"> <widget class="QLabel" name="m_TargetImageLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Target ROI Image:</string> </property> </widget> </item> <item row="6" column="0"> <spacer name="verticalSpacer_6"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item row="0" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_MaskImageSelectionWidget" native="true"/> </item> <item row="1" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_StopImageSelectionWidget" native="true"/> </item> <item row="2" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_ExclusionImageSelectionWidget" native="true"/> </item> <item row="4" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_TargetImageSelectionWidget" native="true"/> </item> </layout> </widget> <widget class="QWidget" name="page_trackingparam"> <property name="geometry"> <rect> <x>0</x> - <y>-19</y> + <y>-270</y> <width>421</width> - <height>436</height> + <height>459</height> </rect> </property> <attribute name="label"> <string>Tractography Parameters</string> </attribute> <attribute name="toolTip"> <string>Specify the behavior of the tractography at each streamline integration step (step size, deterministic/probabilistic, ...).</string> </attribute> <layout class="QGridLayout" name="gridLayout_14"> - <item row="8" column="1"> - <widget class="QDoubleSpinBox" name="m_MinTractLengthBox"> + <item row="5" column="1"> + <widget class="QDoubleSpinBox" name="m_OdfCutoffBox"> <property name="toolTip"> - <string>Minimum tract length in mm. Shorter fibers are discarded.</string> - </property> - <property name="statusTip"> - <string>Minimum fiber length (in mm)</string> + <string>Additional threshold on the ODF magnitude. This is useful in case of CSD fODF tractography. For fODFs a good default value is 0.1, for normalized dODFs, e.g. Q-ball ODFs, this threshold should be very low (0.00025) or 0.</string> </property> <property name="decimals"> - <number>1</number> + <number>5</number> </property> <property name="maximum"> - <double>999.000000000000000</double> + <double>1.000000000000000</double> </property> <property name="singleStep"> - <double>1.000000000000000</double> + <double>0.100000000000000</double> </property> <property name="value"> - <double>20.000000000000000</double> - </property> - </widget> - </item> - <item row="12" column="0"> - <widget class="QLabel" name="m_fLabel"> - <property name="toolTip"> - <string>f parameter of tensor tractography. f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!).</string> - </property> - <property name="text"> - <string>f:</string> + <double>0.000250000000000</double> </property> </widget> </item> - <item row="13" column="1"> - <widget class="QDoubleSpinBox" name="m_gBox"> + <item row="14" column="1"> + <widget class="QDoubleSpinBox" name="m_PeakJitterBox"> <property name="toolTip"> - <string>f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!).</string> + <string>Important for probabilistic peak tractography and peak prior. Actual jitter is drawn from a normal distribution with peak_jitter*fabs(direction_value) as standard deviation.</string> </property> <property name="decimals"> - <number>2</number> + <number>3</number> </property> <property name="maximum"> <double>1.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> - <double>0.000000000000000</double> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="m_FaThresholdLabel_5"> - <property name="toolTip"> - <string/> - </property> - <property name="text"> - <string>Sharpen ODFs:</string> + <double>0.010000000000000</double> </property> </widget> </item> - <item row="15" column="0"> + <item row="16" column="0"> <widget class="QLabel" name="m_FaThresholdLabel_6"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Fix Random Seed:</string> </property> </widget> </item> - <item row="11" column="1"> - <widget class="QSpinBox" name="m_LoopCheckBox"> - <property name="toolTip"> - <string>Maximum allowed angular SDTEV over 4 voxel lengths. Default: 30°</string> - </property> - <property name="statusTip"> - <string/> - </property> - <property name="minimum"> - <number>-1</number> - </property> - <property name="maximum"> - <number>180</number> - </property> - <property name="value"> - <number>30</number> - </property> - </widget> - </item> - <item row="10" column="0"> - <widget class="QLabel" name="m_AngularThresholdLabel"> - <property name="toolTip"> - <string/> + <item row="18" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <property name="text"> - <string>Angular Threshold:</string> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> </property> - </widget> + </spacer> </item> - <item row="9" column="0"> - <widget class="QLabel" name="m_MinTractLengthLabel_2"> + <item row="1" column="0"> + <widget class="QLabel" name="m_FaThresholdLabel_3"> <property name="toolTip"> <string/> </property> <property name="text"> - <string>Max. Tract Length:</string> + <string>Mode:</string> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="m_FaThresholdLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Cutoff:</string> </property> </widget> </item> - <item row="10" column="1"> - <widget class="QSpinBox" name="m_AngularThresholdBox"> + <item row="8" column="1"> + <widget class="QDoubleSpinBox" name="m_MinTractLengthBox"> <property name="toolTip"> - <string>Angular threshold between two steps (in degree). Default: 90° * step_size</string> + <string>Minimum tract length in mm. Shorter fibers are discarded.</string> </property> - <property name="minimum"> - <number>-1</number> + <property name="statusTip"> + <string>Minimum fiber length (in mm)</string> + </property> + <property name="decimals"> + <number>1</number> </property> <property name="maximum"> - <number>90</number> + <double>999.000000000000000</double> </property> <property name="singleStep"> - <number>1</number> + <double>1.000000000000000</double> </property> <property name="value"> - <number>-1</number> + <double>20.000000000000000</double> </property> </widget> </item> - <item row="16" column="0"> - <spacer name="verticalSpacer_2"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + <item row="10" column="0"> + <widget class="QLabel" name="m_AngularThresholdLabel"> + <property name="toolTip"> + <string/> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> + <property name="text"> + <string>Angular Threshold:</string> </property> - </spacer> + </widget> </item> <item row="12" column="1"> <widget class="QDoubleSpinBox" name="m_fBox"> <property name="toolTip"> <string>f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!).</string> </property> <property name="decimals"> <number>2</number> </property> <property name="maximum"> <double>1.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>1.000000000000000</double> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QComboBox" name="m_ModeBox"> + <item row="11" column="1"> + <widget class="QSpinBox" name="m_LoopCheckBox"> <property name="toolTip"> - <string>Toggle between deterministic and probabilistic tractography. Some modes might not be available for all types of tractography.</string> + <string>Maximum allowed angular SDTEV over 4 voxel lengths. Default: 30°</string> </property> - <item> - <property name="text"> - <string>Deterministic</string> - </property> - </item> - <item> - <property name="text"> - <string>Probabilistic</string> - </property> - </item> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="m_FaThresholdLabel_3"> - <property name="toolTip"> + <property name="statusTip"> <string/> </property> - <property name="text"> - <string>Mode:</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QDoubleSpinBox" name="m_OdfCutoffBox"> - <property name="toolTip"> - <string>Additional threshold on the ODF magnitude. This is useful in case of CSD fODF tractography. For fODFs a good default value is 0.1, for normalized dODFs, e.g. Q-ball ODFs, this threshold should be very low (0.00025) or 0.</string> - </property> - <property name="decimals"> - <number>5</number> + <property name="minimum"> + <number>-1</number> </property> <property name="maximum"> - <double>1.000000000000000</double> - </property> - <property name="singleStep"> - <double>0.100000000000000</double> + <number>180</number> </property> <property name="value"> - <double>0.000250000000000</double> + <number>30</number> </property> </widget> </item> - <item row="8" column="0"> - <widget class="QLabel" name="m_MinTractLengthLabel"> + <item row="13" column="0"> + <widget class="QLabel" name="m_gLabel"> <property name="toolTip"> <string/> </property> <property name="text"> - <string>Min. Tract Length:</string> + <string>g:</string> </property> </widget> </item> - <item row="4" column="0"> - <widget class="QLabel" name="mFaImageLabel"> + <item row="16" column="1"> + <widget class="QCheckBox" name="m_FixSeedBox"> <property name="toolTip"> - <string/> + <string>Always produce the same random numbers.</string> </property> <property name="text"> - <string>FA/GFA Image:</string> - </property> - </widget> - </item> - <item row="11" column="0"> - <widget class="QLabel" name="m_LoopCheckLabel"> - <property name="toolTip"> <string/> </property> - <property name="text"> - <string>Loop Check:</string> - </property> </widget> </item> <item row="9" column="1"> <widget class="QDoubleSpinBox" name="m_MaxTractLengthBox"> <property name="toolTip"> <string>Minimum tract length in mm. Shorter fibers are discarded.</string> </property> <property name="statusTip"> <string>Maximum fiber length (in mm)</string> </property> <property name="decimals"> <number>1</number> </property> <property name="maximum"> <double>999.000000000000000</double> </property> <property name="singleStep"> <double>1.000000000000000</double> </property> <property name="value"> <double>400.000000000000000</double> </property> </widget> </item> - <item row="15" column="1"> - <widget class="QCheckBox" name="m_FixSeedBox"> - <property name="toolTip"> - <string>Always produce the same random numbers.</string> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="m_StepsizeLabel"> - <property name="toolTip"> - <string/> - </property> - <property name="text"> - <string>Step Size:</string> - </property> - </widget> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="m_OdfCutoffLabel"> - <property name="toolTip"> - <string/> - </property> - <property name="text"> - <string>ODF Cutoff:</string> - </property> - </widget> - </item> <item row="6" column="1"> <widget class="QDoubleSpinBox" name="m_StepSizeBox"> <property name="toolTip"> <string>Step size (in voxels)</string> </property> <property name="decimals"> <number>2</number> </property> <property name="minimum"> <double>0.010000000000000</double> </property> <property name="maximum"> <double>10.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>0.500000000000000</double> </property> </widget> </item> - <item row="13" column="0"> - <widget class="QLabel" name="m_gLabel"> + <item row="2" column="0"> + <widget class="QLabel" name="m_FaThresholdLabel_5"> <property name="toolTip"> <string/> </property> <property name="text"> - <string>g:</string> + <string>Sharpen ODFs:</string> </property> </widget> </item> - <item row="4" column="1"> - <widget class="QmitkSingleNodeSelectionWidget" name="m_FaImageSelectionWidget" native="true"/> - </item> <item row="3" column="1"> <widget class="QDoubleSpinBox" name="m_ScalarThresholdBox"> <property name="toolTip"> <string>Threshold on peak magnitude, FA, GFA, ...</string> </property> <property name="decimals"> <number>5</number> </property> <property name="maximum"> <double>1.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>0.100000000000000</double> </property> </widget> </item> + <item row="4" column="1"> + <widget class="QmitkSingleNodeSelectionWidget" name="m_FaImageSelectionWidget" native="true"/> + </item> <item row="14" column="0"> <widget class="QLabel" name="m_PeakJitterLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Peak Jitter:</string> </property> </widget> </item> - <item row="14" column="1"> - <widget class="QDoubleSpinBox" name="m_PeakJitterBox"> + <item row="11" column="0"> + <widget class="QLabel" name="m_LoopCheckLabel"> <property name="toolTip"> - <string>Important for probabilistic peak tractography and peak prior. Actual jitter is drawn from a normal distribution with peak_jitter*fabs(direction_value) as standard deviation.</string> + <string/> + </property> + <property name="text"> + <string>Loop Check:</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="m_StepsizeLabel"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>Step Size:</string> + </property> + </widget> + </item> + <item row="9" column="0"> + <widget class="QLabel" name="m_MinTractLengthLabel_2"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>Max. Tract Length:</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="mFaImageLabel"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>FA/GFA Image:</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="m_OdfCutoffLabel"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>ODF Cutoff:</string> + </property> + </widget> + </item> + <item row="13" column="1"> + <widget class="QDoubleSpinBox" name="m_gBox"> + <property name="toolTip"> + <string>f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!).</string> </property> <property name="decimals"> - <number>3</number> + <number>2</number> </property> <property name="maximum"> <double>1.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> - <double>0.010000000000000</double> + <double>0.000000000000000</double> + </property> + </widget> + </item> + <item row="10" column="1"> + <widget class="QSpinBox" name="m_AngularThresholdBox"> + <property name="toolTip"> + <string>Angular threshold between two steps (in degree). Default: 90° * step_size</string> + </property> + <property name="minimum"> + <number>-1</number> + </property> + <property name="maximum"> + <number>90</number> + </property> + <property name="singleStep"> + <number>1</number> + </property> + <property name="value"> + <number>-1</number> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="m_ModeBox"> + <property name="toolTip"> + <string>Toggle between deterministic and probabilistic tractography. Some modes might not be available for all types of tractography.</string> + </property> + <item> + <property name="text"> + <string>Deterministic</string> + </property> + </item> + <item> + <property name="text"> + <string>Probabilistic</string> + </property> + </item> + </widget> + </item> + <item row="12" column="0"> + <widget class="QLabel" name="m_fLabel"> + <property name="toolTip"> + <string>f parameter of tensor tractography. f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!).</string> + </property> + <property name="text"> + <string>f:</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="m_MinTractLengthLabel"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>Min. Tract Length:</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QSpinBox" name="m_SharpenOdfsBox"> <property name="toolTip"> <string>Rais ODF to the power of X</string> </property> <property name="minimum"> <number>1</number> </property> <property name="value"> <number>8</number> </property> </widget> </item> + <item row="15" column="0"> + <widget class="QLabel" name="m_SecondOrderLabel"> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string>Second Order:</string> + </property> + </widget> + </item> + <item row="15" column="1"> + <widget class="QCheckBox" name="m_SecondOrderBox"> + <property name="toolTip"> + <string>Always produce the same random numbers.</string> + </property> + <property name="text"> + <string/> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> </layout> </widget> <widget class="QWidget" name="page_prior"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>435</width> - <height>184</height> + <height>189</height> </rect> </property> <attribute name="label"> <string>Tractography Prior</string> </attribute> <layout class="QGridLayout" name="gridLayout_9"> <item row="1" column="0"> <widget class="QLabel" name="m_PriorLabel_2"> <property name="text"> <string>Weight:</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QDoubleSpinBox" name="m_PriorWeightBox"> <property name="toolTip"> <string>Weighting factor between prior and data.</string> </property> <property name="maximum"> <double>1.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>0.500000000000000</double> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="m_PriorLabel"> <property name="text"> <string>Peak Image:</string> </property> </widget> </item> <item row="5" column="0"> <spacer name="verticalSpacer_7"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item row="3" column="1"> <widget class="QCheckBox" name="m_NewDirectionsFromPriorBox"> <property name="toolTip"> <string>If unchecked, the prior cannot create directions where there are none in the data.</string> </property> <property name="text"> <string/> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="m_PriorLabel_4"> <property name="text"> <string>New Directions from Prior:</string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="m_PriorLabel_3"> <property name="text"> <string>Restrict to Prior:</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QmitkSingleNodeSelectionWidget" name="m_PriorImageSelectionWidget" native="true"/> </item> <item row="2" column="1"> <widget class="QCheckBox" name="m_PriorAsMaskBox"> <property name="toolTip"> <string>Restrict tractography to regions where the prior is valid.</string> </property> <property name="text"> <string/> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="4" column="1"> <widget class="QFrame" name="frame_10"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_20"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <property name="spacing"> <number>0</number> </property> <item row="0" column="1"> <widget class="QCheckBox" name="m_PriorFlipYBox"> <property name="text"> <string>y</string> </property> </widget> </item> <item row="0" column="0"> <widget class="QCheckBox" name="m_PriorFlipXBox"> <property name="text"> <string>x</string> </property> </widget> </item> <item row="0" column="2"> <widget class="QCheckBox" name="m_PriorFlipZBox"> <property name="text"> <string>z</string> </property> </widget> </item> </layout> </widget> </item> <item row="4" column="0"> <widget class="QLabel" name="m_PriorLabel_5"> <property name="text"> <string>Flip Directions:</string> </property> </widget> </item> </layout> </widget> <widget class="QWidget" name="page_neighborhood"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>435</width> - <height>184</height> + <height>189</height> </rect> </property> <attribute name="label"> <string>Neighborhood Sampling</string> </attribute> <attribute name="toolTip"> <string>Specify if and how information about the current streamline neighborhood should be used.</string> </attribute> <layout class="QGridLayout" name="gridLayout_18"> <item row="1" column="0"> <widget class="QCheckBox" name="m_FrontalSamplesBox"> <property name="toolTip"> <string>Only neighborhood samples in front of the current streamline position are considered.</string> </property> <property name="text"> <string>Use Only Frontal Samples</string> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="m_StopVotesBox"> <property name="toolTip"> <string>If checked, the majority of sampling points has to place a stop-vote for the streamline to terminate. If not checked, all sampling positions have to vote for a streamline termination.</string> </property> <property name="text"> <string>Use Stop-Votes</string> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> <item row="0" column="0"> <widget class="QFrame" name="frame_4"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_10"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="0" column="0"> <widget class="QLabel" name="m_SeedsPerVoxelLabel_2"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Num. Samples:</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QSpinBox" name="m_NumSamplesBox"> <property name="toolTip"> <string>Number of neighborhood samples that are used to determine the next fiber progression direction.</string> </property> <property name="maximum"> <number>50</number> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="m_SamplingDistanceLabel"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Sampling Distance:</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QDoubleSpinBox" name="m_SamplingDistanceBox"> <property name="toolTip"> <string>Sampling distance (in voxels)</string> </property> <property name="decimals"> <number>2</number> </property> <property name="maximum"> <double>10.000000000000000</double> </property> <property name="singleStep"> <double>0.100000000000000</double> </property> <property name="value"> <double>0.250000000000000</double> </property> </widget> </item> </layout> </widget> </item> <item row="3" column="0"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <widget class="QWidget" name="page_data"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>435</width> - <height>184</height> + <height>189</height> </rect> </property> <attribute name="label"> <string>Data Handling</string> </attribute> <attribute name="toolTip"> <string>Specify interpolation and direction flips.</string> </attribute> <layout class="QGridLayout" name="gridLayout_6"> <item row="1" column="0"> <widget class="QFrame" name="frame_3"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_7"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="0" column="0"> <widget class="QCheckBox" name="m_InterpolationBox"> <property name="toolTip"> <string>Trilinearly interpolate the input image used for tractography.</string> </property> <property name="text"> <string>Interpolate Tractography Data</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="1" column="0"> <widget class="QCheckBox" name="m_MaskInterpolationBox"> <property name="toolTip"> <string>Trilinearly interpolate the ROI images used to constrain the tractography.</string> </property> <property name="text"> <string>Interpolate ROI Images</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> </layout> </widget> </item> <item row="0" column="0"> <widget class="QFrame" name="frame_2"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_3"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="7" column="1"> <widget class="QFrame" name="frame"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_4"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item row="0" column="0"> <widget class="QCheckBox" name="m_FlipXBox"> <property name="toolTip"> <string>Internally flips progression directions. This might be necessary depending on the input data.</string> </property> <property name="text"> <string>x</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QCheckBox" name="m_FlipYBox"> <property name="toolTip"> <string>Internally flips progression directions. This might be necessary depending on the input data.</string> </property> <property name="text"> <string>y</string> </property> </widget> </item> <item row="0" column="2"> <widget class="QCheckBox" name="m_FlipZBox"> <property name="toolTip"> <string>Internally flips progression directions. This might be necessary depending on the input data.</string> </property> <property name="text"> <string>z</string> </property> </widget> </item> </layout> </widget> </item> <item row="7" column="0"> <widget class="QLabel" name="m_SeedsPerVoxelLabel_3"> <property name="toolTip"> <string/> </property> <property name="text"> <string>Flip directions:</string> </property> </widget> </item> </layout> </widget> </item> <item row="2" column="0"> <spacer name="verticalSpacer_4"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <widget class="QWidget" name="page_output"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>435</width> - <height>184</height> + <height>189</height> </rect> </property> <attribute name="label"> <string>Output and Postprocessing</string> </attribute> <attribute name="toolTip"> <string>Specify the tractography output (streamlines or probability maps) and postprocessing steps.</string> </attribute> <layout class="QGridLayout" name="gridLayout_5"> <item row="3" column="0"> <spacer name="verticalSpacer_5"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item row="1" column="0"> <widget class="QCheckBox" name="m_ResampleFibersBox"> <property name="toolTip"> <string>Compress fibers using the specified error constraint.</string> </property> <property name="text"> <string>Compress Fibers</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QCheckBox" name="m_OutputProbMap"> <property name="toolTip"> <string>Output map with voxel-wise visitation counts instead of streamlines.</string> </property> <property name="text"> <string>Output Probability Map</string> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> </layout> </widget> </widget> </item> </layout> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> <customwidget> <class>QmitkSingleNodeSelectionWidget</class> <extends>QWidget</extends> <header>QmitkSingleNodeSelectionWidget.h</header> <container>1</container> </customwidget> </customwidgets> <resources> <include location="../../resources/QmitkTractography.qrc"/> </resources> <connections/> </ui> diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp index 9bb2ae0..01cbf65 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp @@ -1,1238 +1,1285 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkControlVisualizationPropertiesView.h" #include "mitkNodePredicateDataType.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkResliceMethodProperty.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkShImage.h" #include "mitkPlanarFigure.h" #include "mitkFiberBundle.h" #include "QmitkDataStorageComboBox.h" #include "mitkPlanarFigureInteractor.h" #include <mitkOdfImage.h> #include <mitkTensorImage.h> #include <mitkImage.h> #include <mitkDiffusionPropertyHelper.h> #include <mitkConnectomicsNetwork.h> #include "usModuleRegistry.h" #include <mitkPeakImage.h> #include <mitkBaseRenderer.h> #include "mitkPlaneGeometry.h" #include <QmitkRenderWindow.h> #include <itkFlipPeaksFilter.h> #include <mitkImageToItk.h> #include <mitkWorkbenchUtil.h> #include "berryIWorkbenchWindow.h" #include "berryIWorkbenchPage.h" #include "berryISelectionService.h" #include "berryConstants.h" #include "berryPlatformUI.h" #include "itkRGBAPixel.h" #include <itkTractDensityImageFilter.h> #include "qwidgetaction.h" #include "qcolordialog.h" #include <QRgb> #include <itkMultiThreaderBase.h> #include <mitkClippingProperty.h> #include <ciso646> #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::string QmitkControlVisualizationPropertiesView::VIEW_ID = "org.mitk.views.controlvisualizationpropertiesview"; using namespace berry; QmitkControlVisualizationPropertiesView::QmitkControlVisualizationPropertiesView() : QmitkAbstractView(), m_Controls(nullptr), m_CurrentSelection(nullptr), m_IconTexOFF(new QIcon(":/QmitkDiffusionImaging/texIntOFFIcon.png")), m_IconTexON(new QIcon(":/QmitkDiffusionImaging/texIntONIcon.png")), m_IconGlyOFF_T(new QIcon(":/QmitkDiffusionImaging/glyphsoff_T.png")), m_IconGlyON_T(new QIcon(":/QmitkDiffusionImaging/glyphson_T.png")), m_IconGlyOFF_C(new QIcon(":/QmitkDiffusionImaging/glyphsoff_C.png")), m_IconGlyON_C(new QIcon(":/QmitkDiffusionImaging/glyphson_C.png")), m_IconGlyOFF_S(new QIcon(":/QmitkDiffusionImaging/glyphsoff_S.png")), m_IconGlyON_S(new QIcon(":/QmitkDiffusionImaging/glyphson_S.png")), m_GlyIsOn_T(false), m_GlyIsOn_C(false), m_GlyIsOn_S(false), m_CurrentThickSlicesMode(1), m_CurrentThickSlicesNum(0), m_CurrentPickingNode(nullptr), m_ColorPropertyObserverTag(0), m_OpacityPropertyObserverTag(0) { m_MyMenu = nullptr; auto numThread = itk::MultiThreaderBase::GetGlobalMaximumNumberOfThreads(); itk::MultiThreaderBase::SetGlobalDefaultNumberOfThreads(numThread); } QmitkControlVisualizationPropertiesView::~QmitkControlVisualizationPropertiesView() { } void QmitkControlVisualizationPropertiesView::SetTs(int currentThickSlicesMode, int num, std::string render_window) { if (auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN)) { mitk::BaseRenderer::Pointer renderer = renderWindowPart->GetQmitkRenderWindow(QString(render_window.c_str()))->GetRenderer(); renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(num)); if(num>0) { renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New(currentThickSlicesMode)); renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.showarea", mitk::BoolProperty::New(true)); } else { renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New(0)); renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.showarea", mitk::BoolProperty::New(false)); } renderer->SendUpdateSlice(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::OnThickSlicesModeSelected( QAction* action ) { m_CurrentThickSlicesMode = action->data().toInt(); switch( m_CurrentThickSlicesMode ) { case 0: return; case 1: this->m_Controls->m_TSMenu->setText("MIP"); break; case 2: this->m_Controls->m_TSMenu->setText("SUM"); break; case 3: this->m_Controls->m_TSMenu->setText("WEIGH"); break; default: return; } SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "axial"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "sagittal"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "coronal"); } void QmitkControlVisualizationPropertiesView::OnTSNumChanged( int num ) { m_CurrentThickSlicesNum = num; SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "axial"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "sagittal"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "coronal"); m_TSLabel->setText(QString::number( num*2 + 1 )); } void QmitkControlVisualizationPropertiesView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkControlVisualizationPropertiesViewControls; m_Controls->setupUi(parent); this->CreateConnections(); // hide warning (ODFs in rotated planes) m_Controls->m_lblRotatedPlanesWarning->hide(); m_MyMenu = new QMenu(parent); m_Controls->m_TSMenu->setMenu( m_MyMenu ); QIcon iconFiberFade(":/QmitkDiffusionImaging/MapperEfx2D.png"); m_Controls->m_FiberFading2D->setIcon(iconFiberFade); m_Controls->m_NormalizationFrame->setVisible(false); m_Controls->m_Crosshair->setVisible(false); mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } connect(m_Controls->m_SetColor1, SIGNAL(clicked()), this, SLOT(SetColor())); connect(m_Controls->m_SetColor2, SIGNAL(clicked()), this, SLOT(SetColor())); } } void QmitkControlVisualizationPropertiesView::SetColor() { - if(m_SelectedNode) + for (auto node : m_SelectedNodes) { QColor c = QColorDialog::getColor(); if (c.isValid()) { float rgb[3]; rgb[0] = static_cast<float>(c.redF()); rgb[1] = static_cast<float>(c.greenF()); rgb[2] = static_cast<float>(c.blueF()); - m_SelectedNode->SetColor(rgb); + node->SetColor(rgb); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } void QmitkControlVisualizationPropertiesView::SetFocus() { m_Controls->m_TSMenu->setFocus(); } void QmitkControlVisualizationPropertiesView::SliceRotation(const itk::EventObject&) { // test if plane rotated if( m_GlyIsOn_T || m_GlyIsOn_C || m_GlyIsOn_S ) { if( this->IsPlaneRotated() ) { // show label m_Controls->m_lblRotatedPlanesWarning->show(); } else { //hide label m_Controls->m_lblRotatedPlanesWarning->hide(); } } } void QmitkControlVisualizationPropertiesView::NodeRemoved(const mitk::DataNode* /*node*/) { } #include <mitkMessage.h> void QmitkControlVisualizationPropertiesView::CreateConnections() { if ( m_Controls ) { connect( static_cast<QObject*>(m_Controls->m_VisibleOdfsON_T), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_T()) ); connect( static_cast<QObject*>(m_Controls->m_VisibleOdfsON_S), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_S()) ); connect( static_cast<QObject*>(m_Controls->m_VisibleOdfsON_C), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_C()) ); connect( static_cast<QObject*>(m_Controls->m_ShowMaxNumber), SIGNAL(editingFinished()), this, SLOT(ShowMaxNumberChanged()) ); connect( static_cast<QObject*>(m_Controls->m_NormalizationDropdown), SIGNAL(currentIndexChanged(int)), this, SLOT(NormalizationDropdownChanged(int)) ); connect( static_cast<QObject*>(m_Controls->m_ScalingFactor), SIGNAL(valueChanged(double)), this, SLOT(ScalingFactorChanged(double)) ); connect( static_cast<QObject*>(m_Controls->m_AdditionalScaling), SIGNAL(currentIndexChanged(int)), this, SLOT(AdditionalScaling(int)) ); connect(static_cast<QObject*>(m_Controls->m_ResetColoring), SIGNAL(clicked()), static_cast<QObject*>(this), SLOT(ResetColoring())); connect(static_cast<QObject*>(m_Controls->m_ResetColoring2), SIGNAL(clicked()), static_cast<QObject*>(this), SLOT(ResetColoring())); connect(static_cast<QObject*>(m_Controls->m_FiberFading2D), SIGNAL(clicked()), static_cast<QObject*>(this), SLOT( Fiber2DfadingEFX() ) ); connect(static_cast<QObject*>(m_Controls->m_FiberClippingBox), SIGNAL(editingFinished()), static_cast<QObject*>(this), SLOT( FiberSlicingThickness2D() ) ); connect(static_cast<QObject*>(m_Controls->m_Crosshair), SIGNAL(clicked()), static_cast<QObject*>(this), SLOT(SetInteractor())); connect(static_cast<QObject*>(m_Controls->m_LineWidth), SIGNAL(editingFinished()), static_cast<QObject*>(this), SLOT(LineWidthChanged())); connect(static_cast<QObject*>(m_Controls->m_TubeWidth), SIGNAL(editingFinished()), static_cast<QObject*>(this), SLOT(TubeRadiusChanged())); connect(static_cast<QObject*>(m_Controls->m_RibbonWidth), SIGNAL(editingFinished()), static_cast<QObject*>(this), SLOT(RibbonWidthChanged())); connect( static_cast<QObject*>(m_Controls->m_OdfColorBox), SIGNAL(currentIndexChanged(int)), static_cast<QObject*>(this), SLOT(OnColourisationModeChanged() ) ); connect(static_cast<QObject*>(m_Controls->m_Clip0), SIGNAL(toggled(bool)), static_cast<QObject*>(this), SLOT(Toggle3DClipping(bool))); connect(static_cast<QObject*>(m_Controls->m_Clip1), SIGNAL(toggled(bool)), static_cast<QObject*>(this), SLOT(Toggle3DClipping(bool))); connect(static_cast<QObject*>(m_Controls->m_Clip2), SIGNAL(toggled(bool)), static_cast<QObject*>(this), SLOT(Toggle3DClipping(bool))); connect(static_cast<QObject*>(m_Controls->m_Clip3), SIGNAL(toggled(bool)), static_cast<QObject*>(this), SLOT(Toggle3DClipping(bool))); connect(static_cast<QObject*>(m_Controls->m_FlipClipBox), SIGNAL(stateChanged(int)), static_cast<QObject*>(this), SLOT(Toggle3DClipping())); connect(static_cast<QObject*>(m_Controls->m_Enable3dPeaks), SIGNAL(stateChanged(int)), static_cast<QObject*>(this), SLOT(Toggle3DPeaks())); connect(static_cast<QObject*>(m_Controls->m_FlipPeaksButton), SIGNAL(clicked()), static_cast<QObject*>(this), SLOT(FlipPeaks())); m_Controls->m_BundleControlsFrame->setVisible(false); m_Controls->m_ImageControlsFrame->setVisible(false); m_Controls->m_PeakImageFrame->setVisible(false); m_Controls->m_lblRotatedPlanesWarning->setVisible(false); m_Controls->m_3DClippingBox->setVisible(false); } } // set diffusion image channel to b0 volume void QmitkControlVisualizationPropertiesView::NodeAdded(const mitk::DataNode *node) { mitk::DataNode* notConst = const_cast<mitk::DataNode*>(node); bool isDiffusionImage( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast<mitk::Image *>(node->GetData())) ); if (isDiffusionImage) { mitk::Image::Pointer dimg = dynamic_cast<mitk::Image*>(notConst->GetData()); // if there is no b0 image in the dataset, the GetB0Indices() returns a vector of size 0 // and hence we cannot set the Property directly to .front() int displayChannelPropertyValue = 0; mitk::DiffusionPropertyHelper::BValueMapType map = mitk::DiffusionPropertyHelper::GetBValueMap(dimg); if( map[0].size() > 0) { displayChannelPropertyValue = map[0].front(); } notConst->SetIntProperty("DisplayChannel", displayChannelPropertyValue ); } } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkControlVisualizationPropertiesView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& nodes) { m_Controls->m_BundleControlsFrame->setVisible(false); m_Controls->m_ImageControlsFrame->setVisible(false); m_Controls->m_PeakImageFrame->setVisible(false); m_Controls->m_3DClippingBox->setVisible(false); m_Controls->m_FlipClipBox->setVisible(false); m_Controls->m_Enable3dPeaks->setVisible(false); - if (nodes.size()>1) // only do stuff if one node is selected - return; +// if (nodes.size()>1) // only do stuff if one node is selected +// return; m_Controls->m_NumberGlyphsFrame->setVisible(false); m_Controls->m_GlyphFrame->setVisible(false); m_Controls->m_TSMenu->setVisible(false); - m_SelectedNode = nullptr; + m_SelectedNodes.clear(); int numOdfImages = 0; for (mitk::DataNode::Pointer node: nodes) { if(node.IsNull()) continue; mitk::BaseData* nodeData = node->GetData(); if(nodeData == nullptr) continue; - m_SelectedNode = node; - if (dynamic_cast<mitk::PeakImage*>(nodeData)) { m_Controls->m_PeakImageFrame->setVisible(true); if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_ColorPropertyObserverTag); itk::ReceptorMemberCommand<QmitkControlVisualizationPropertiesView>::Pointer command = itk::ReceptorMemberCommand<QmitkControlVisualizationPropertiesView>::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetCustomColor ); m_Color = dynamic_cast<mitk::ColorProperty*>(node->GetProperty("color", nullptr)); if (m_Color.IsNotNull()) m_ColorPropertyObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); int ClippingPlaneId = -1; - m_SelectedNode->GetPropertyValue("3DClippingPlaneId",ClippingPlaneId); + node->GetPropertyValue("3DClippingPlaneId",ClippingPlaneId); switch(ClippingPlaneId) { case 0: m_Controls->m_Clip0->setChecked(1); break; case 1: m_Controls->m_Clip1->setChecked(1); break; case 2: m_Controls->m_Clip2->setChecked(1); break; case 3: m_Controls->m_Clip3->setChecked(1); break; default : m_Controls->m_Clip0->setChecked(1); } m_Controls->m_Enable3dPeaks->setVisible(true); m_Controls->m_3DClippingBox->setVisible(true); } else if (dynamic_cast<mitk::FiberBundle*>(nodeData)) { int ClippingPlaneId = -1; - m_SelectedNode->GetPropertyValue("3DClippingPlaneId",ClippingPlaneId); + node->GetPropertyValue("3DClippingPlaneId",ClippingPlaneId); switch(ClippingPlaneId) { case 0: m_Controls->m_Clip0->setChecked(1); break; case 1: m_Controls->m_Clip1->setChecked(1); break; case 2: m_Controls->m_Clip2->setChecked(1); break; case 3: m_Controls->m_Clip3->setChecked(1); break; default : m_Controls->m_Clip0->setChecked(1); } // handle fiber property observers if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_ColorPropertyObserverTag); itk::ReceptorMemberCommand<QmitkControlVisualizationPropertiesView>::Pointer command = itk::ReceptorMemberCommand<QmitkControlVisualizationPropertiesView>::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetCustomColor ); m_Color = dynamic_cast<mitk::ColorProperty*>(node->GetProperty("color", nullptr)); if (m_Color.IsNotNull()) m_ColorPropertyObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); m_Controls->m_FlipClipBox->setVisible(true); m_Controls->m_3DClippingBox->setVisible(true); m_Controls->m_BundleControlsFrame->setVisible(true); if(m_CurrentPickingNode != 0 && node.GetPointer() != m_CurrentPickingNode) { m_Controls->m_Crosshair->setEnabled(false); } else { m_Controls->m_Crosshair->setEnabled(true); } int width; node->GetIntProperty("shape.linewidth", width); m_Controls->m_LineWidth->setValue(width); float radius; node->GetFloatProperty("shape.tuberadius", radius); m_Controls->m_TubeWidth->setValue(radius); float range; node->GetFloatProperty("Fiber2DSliceThickness",range); mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(node->GetData()); mitk::BaseGeometry::Pointer geo = fib->GetGeometry(); mitk::ScalarType max = geo->GetExtentInMM(0); max = std::max(max, geo->GetExtentInMM(1)); max = std::max(max, geo->GetExtentInMM(2)); m_Controls->m_FiberClippingBox->setMaximum(max); m_Controls->m_FiberClippingBox->setValue(range); } else if(dynamic_cast<mitk::OdfImage*>(nodeData) || dynamic_cast<mitk::TensorImage*>(nodeData) || dynamic_cast<mitk::ShImage*>(nodeData)) { m_Controls->m_ImageControlsFrame->setVisible(true); m_Controls->m_NumberGlyphsFrame->setVisible(true); m_Controls->m_GlyphFrame->setVisible(true); m_Controls->m_NormalizationFrame->setVisible(true); if(m_NodeUsedForOdfVisualization.IsNotNull()) { m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", false); } m_NodeUsedForOdfVisualization = node; m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); if (dynamic_cast<mitk::TensorImage*>(nodeData)) { m_Controls->m_NormalizationDropdown->setVisible(false); m_Controls->m_NormalizationLabel->setVisible(false); } else { m_Controls->m_NormalizationDropdown->setVisible(true); m_Controls->m_NormalizationLabel->setVisible(true); } int val; node->GetIntProperty("ShowMaxNumber", val); m_Controls->m_ShowMaxNumber->setValue(val); m_Controls->m_NormalizationDropdown->setCurrentIndex(dynamic_cast<mitk::EnumerationProperty*>(node->GetProperty("Normalization"))->GetValueAsId()); float fval; node->GetFloatProperty("Scaling",fval); m_Controls->m_ScalingFactor->setValue(fval); m_Controls->m_AdditionalScaling->setCurrentIndex(dynamic_cast<mitk::EnumerationProperty*>(node->GetProperty("ScaleBy"))->GetValueAsId()); bool switchTensorViewValue = false; node->GetBoolProperty( "DiffusionCore.Rendering.OdfVtkMapper.SwitchTensorView", switchTensorViewValue ); bool colourisationModeBit = false; node->GetBoolProperty("DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", colourisationModeBit ); m_Controls->m_OdfColorBox->setCurrentIndex(colourisationModeBit); numOdfImages++; } else if(dynamic_cast<mitk::PlanarFigure*>(nodeData)) { PlanarFigureFocus(); } else if( dynamic_cast<mitk::Image*>(nodeData) ) { m_Controls->m_ImageControlsFrame->setVisible(true); m_Controls->m_TSMenu->setVisible(true); } + + m_SelectedNodes.push_back(node); } if( nodes.empty() ) { return; } mitk::DataNode::Pointer node = nodes.at(0); if( node.IsNull() ) { return; } QMenu *myMenu = m_MyMenu; myMenu->clear(); QActionGroup* thickSlicesActionGroup = new QActionGroup(myMenu); thickSlicesActionGroup->setExclusive(true); int currentTSMode = 0; { mitk::ResliceMethodProperty::Pointer m = dynamic_cast<mitk::ResliceMethodProperty*>(node->GetProperty( "reslice.thickslices" )); if( m.IsNotNull() ) currentTSMode = m->GetValueAsId(); } int maxTS = 30; for (auto node: nodes) { mitk::Image* image = dynamic_cast<mitk::Image*>(node->GetData()); if (image) { int size = std::max(image->GetDimension(0), std::max(image->GetDimension(1), image->GetDimension(2))); if (size>maxTS) { maxTS=size; } } } maxTS /= 2; int currentNum = 0; { mitk::IntProperty::Pointer m = dynamic_cast<mitk::IntProperty*>(node->GetProperty( "reslice.thickslices.num" )); if( m.IsNotNull() ) { currentNum = m->GetValue(); if(currentNum < 0) { currentNum = 0; } if(currentNum > maxTS) { currentNum = maxTS; } } } if(currentTSMode==0) { currentNum=0; } QSlider *m_TSSlider = new QSlider(myMenu); m_TSSlider->setMinimum(0); m_TSSlider->setMaximum(maxTS-1); m_TSSlider->setValue(currentNum); m_TSSlider->setOrientation(Qt::Horizontal); connect( m_TSSlider, SIGNAL( valueChanged(int) ), this, SLOT( OnTSNumChanged(int) ) ); QHBoxLayout* _TSLayout = new QHBoxLayout; _TSLayout->setContentsMargins(4,4,4,4); _TSLayout->addWidget(m_TSSlider); _TSLayout->addWidget(m_TSLabel=new QLabel(QString::number(currentNum*2+1),myMenu)); QWidget* _TSWidget = new QWidget; _TSWidget->setLayout(_TSLayout); QActionGroup* thickSliceModeActionGroup = new QActionGroup(myMenu); thickSliceModeActionGroup->setExclusive(true); QWidgetAction *m_TSSliderAction = new QWidgetAction(myMenu); m_TSSliderAction->setDefaultWidget(_TSWidget); myMenu->addAction(m_TSSliderAction); QAction* mipThickSlicesAction = new QAction(myMenu); mipThickSlicesAction->setActionGroup(thickSliceModeActionGroup); mipThickSlicesAction->setText("MIP (max. intensity proj.)"); mipThickSlicesAction->setCheckable(true); mipThickSlicesAction->setChecked(m_CurrentThickSlicesMode==1); mipThickSlicesAction->setData(1); myMenu->addAction( mipThickSlicesAction ); QAction* sumThickSlicesAction = new QAction(myMenu); sumThickSlicesAction->setActionGroup(thickSliceModeActionGroup); sumThickSlicesAction->setText("SUM (sum intensity proj.)"); sumThickSlicesAction->setCheckable(true); sumThickSlicesAction->setChecked(m_CurrentThickSlicesMode==2); sumThickSlicesAction->setData(2); myMenu->addAction( sumThickSlicesAction ); QAction* weightedThickSlicesAction = new QAction(myMenu); weightedThickSlicesAction->setActionGroup(thickSliceModeActionGroup); weightedThickSlicesAction->setText("WEIGHTED (gaussian proj.)"); weightedThickSlicesAction->setCheckable(true); weightedThickSlicesAction->setChecked(m_CurrentThickSlicesMode==3); weightedThickSlicesAction->setData(3); myMenu->addAction( weightedThickSlicesAction ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); connect( thickSliceModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(OnThickSlicesModeSelected(QAction*)) ); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_S() { m_GlyIsOn_S = m_Controls->m_VisibleOdfsON_S->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Visible() { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkControlVisualizationPropertiesView::Hidden() { } void QmitkControlVisualizationPropertiesView::Activated() { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkControlVisualizationPropertiesView::Deactivated() { } void QmitkControlVisualizationPropertiesView::FlipPeaks() { - if (m_SelectedNode.IsNull() || dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData())==nullptr) - return; - std::string name = m_SelectedNode->GetName(); + for (auto node : m_SelectedNodes) + { + if (dynamic_cast<mitk::PeakImage*>(node->GetData())==nullptr) + continue; - mitk::Image::Pointer image = dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData()); + std::string name = node->GetName(); - typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType; - CasterType::Pointer caster = CasterType::New(); - caster->SetInput(image); - caster->Update(); - mitk::PeakImage::ItkPeakImageType::Pointer itkImg = caster->GetOutput(); + mitk::Image::Pointer image = dynamic_cast<mitk::PeakImage*>(node->GetData()); - itk::FlipPeaksFilter< float >::Pointer flipper = itk::FlipPeaksFilter< float >::New(); - flipper->SetInput(itkImg); - flipper->SetFlipX(m_Controls->m_FlipPeaksX->isChecked()); - flipper->SetFlipY(m_Controls->m_FlipPeaksY->isChecked()); - flipper->SetFlipZ(m_Controls->m_FlipPeaksZ->isChecked()); - flipper->Update(); + typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType; + CasterType::Pointer caster = CasterType::New(); + caster->SetInput(image); + caster->Update(); + mitk::PeakImage::ItkPeakImageType::Pointer itkImg = caster->GetOutput(); - mitk::Image::Pointer resultImage = dynamic_cast<mitk::Image*>(mitk::PeakImage::New().GetPointer()); - mitk::CastToMitkImage(flipper->GetOutput(), resultImage); - resultImage->SetVolume(flipper->GetOutput()->GetBufferPointer()); - m_SelectedNode->SetData(resultImage); - m_SelectedNode->SetName(name); + itk::FlipPeaksFilter< float >::Pointer flipper = itk::FlipPeaksFilter< float >::New(); + flipper->SetInput(itkImg); + flipper->SetFlipX(m_Controls->m_FlipPeaksX->isChecked()); + flipper->SetFlipY(m_Controls->m_FlipPeaksY->isChecked()); + flipper->SetFlipZ(m_Controls->m_FlipPeaksZ->isChecked()); + flipper->Update(); + + mitk::Image::Pointer resultImage = dynamic_cast<mitk::Image*>(mitk::PeakImage::New().GetPointer()); + mitk::CastToMitkImage(flipper->GetOutput(), resultImage); + resultImage->SetVolume(flipper->GetOutput()->GetBufferPointer()); + node->SetData(resultImage); + node->SetName(name); + } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Toggle3DPeaks() { - if (m_SelectedNode.IsNull() || dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData())==nullptr) - return; + for (auto node : m_SelectedNodes) + { + if (dynamic_cast<mitk::PeakImage*>(node->GetData())==nullptr) + continue; - bool enabled = false; - m_SelectedNode->GetBoolProperty("Enable3DPeaks", enabled); - m_SelectedNode->SetBoolProperty( "Enable3DPeaks", !enabled ); + bool enabled = false; + node->GetBoolProperty("Enable3DPeaks", enabled); + node->SetBoolProperty( "Enable3DPeaks", !enabled ); + } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Toggle3DClipping(bool enabled) { - if (!enabled || m_SelectedNode.IsNull() || (dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())==nullptr && dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData())==nullptr)) - return; + for (auto node : m_SelectedNodes) + { + if (!enabled || node.IsNull() || (dynamic_cast<mitk::FiberBundle*>(node->GetData())==nullptr && dynamic_cast<mitk::PeakImage*>(node->GetData())==nullptr)) + continue; - m_SelectedNode->SetBoolProperty( "3DClippingPlaneFlip", m_Controls->m_FlipClipBox->isChecked() ); + node->SetBoolProperty( "3DClippingPlaneFlip", m_Controls->m_FlipClipBox->isChecked() ); - if (m_Controls->m_Clip0->isChecked()) - { - m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 0 ); - Set3DClippingPlane(true, m_SelectedNode, ""); - } - else if (m_Controls->m_Clip1->isChecked()) - { - m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 1 ); - Set3DClippingPlane(false, m_SelectedNode, "axial"); - } - else if (m_Controls->m_Clip2->isChecked()) - { - m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 2 ); - Set3DClippingPlane(false, m_SelectedNode, "sagittal"); - } - else if (m_Controls->m_Clip3->isChecked()) - { - m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 3 ); - Set3DClippingPlane(false, m_SelectedNode, "coronal"); + if (m_Controls->m_Clip0->isChecked()) + { + node->SetIntProperty( "3DClippingPlaneId", 0 ); + Set3DClippingPlane(true, node, ""); + } + else if (m_Controls->m_Clip1->isChecked()) + { + node->SetIntProperty( "3DClippingPlaneId", 1 ); + Set3DClippingPlane(false, node, "axial"); + } + else if (m_Controls->m_Clip2->isChecked()) + { + node->SetIntProperty( "3DClippingPlaneId", 2 ); + Set3DClippingPlane(false, node, "sagittal"); + } + else if (m_Controls->m_Clip3->isChecked()) + { + node->SetIntProperty( "3DClippingPlaneId", 3 ); + Set3DClippingPlane(false, node, "coronal"); + } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::OnSliceChanged() { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; i<nodes->Size(); ++i) { mitk::DataNode::Pointer node = nodes->GetElement(i); int plane_id = -1; node->GetIntProperty("3DClippingPlaneId", plane_id); if (plane_id==1) Set3DClippingPlane(false, node, "axial"); else if (plane_id==2) Set3DClippingPlane(false, node, "sagittal"); else if (plane_id==3) Set3DClippingPlane(false, node, "coronal"); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Set3DClippingPlane(bool disable, mitk::DataNode* node, std::string plane) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow && node && (dynamic_cast<mitk::FiberBundle*>(node->GetData()) || dynamic_cast<mitk::PeakImage*>(node->GetData()))) { mitk::Vector3D planeNormal; planeNormal.Fill(0.0); mitk::Point3D planeOrigin; planeOrigin.Fill(0.0); if (!disable) { mitk::SliceNavigationController* slicer = renderWindow->GetQmitkRenderWindow(QString(plane.c_str()))->GetSliceNavigationController(); mitk::PlaneGeometry::ConstPointer planeGeo = slicer->GetCurrentPlaneGeometry(); planeOrigin = this->GetRenderWindowPart()->GetSelectedPosition(); planeNormal = planeGeo->GetNormal(); } node->SetProperty( "3DClipping", mitk::ClippingProperty::New( planeOrigin, planeNormal ) ); if (dynamic_cast<mitk::FiberBundle*>(node->GetData())) dynamic_cast<mitk::FiberBundle*>(node->GetData())->RequestUpdate(); else mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_T() { m_GlyIsOn_T = m_Controls->m_VisibleOdfsON_T->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_C() { m_GlyIsOn_C = m_Controls->m_VisibleOdfsON_C->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } bool QmitkControlVisualizationPropertiesView::IsPlaneRotated() { mitk::Image* currentImage = dynamic_cast<mitk::Image* >( m_NodeUsedForOdfVisualization->GetData() ); if( currentImage == nullptr ) { MITK_ERROR << " Casting problems. Returning false"; return false; } mitk::Vector3D imageNormal0 = currentImage->GetSlicedGeometry()->GetAxisVector(0); mitk::Vector3D imageNormal1 = currentImage->GetSlicedGeometry()->GetAxisVector(1); mitk::Vector3D imageNormal2 = currentImage->GetSlicedGeometry()->GetAxisVector(2); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); auto renderWindowPart = this->GetRenderWindowPart(); double eps = 0.000001; // for all 2D renderwindows of the render window part check alignment { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast<const mitk::PlaneGeometry*> ( renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast<const mitk::PlaneGeometry*> ( renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast<const mitk::PlaneGeometry*> ( renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } return false; } void QmitkControlVisualizationPropertiesView::ShowMaxNumberChanged() { int maxNr = m_Controls->m_ShowMaxNumber->value(); if ( maxNr < 1 ) { m_Controls->m_ShowMaxNumber->setValue( 1 ); maxNr = 1; } - if ( dynamic_cast<mitk::OdfImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::TensorImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::ShImage*>(m_SelectedNode->GetData()) ) + for (auto node : m_SelectedNodes) { - m_SelectedNode->SetIntProperty("ShowMaxNumber", maxNr); + if ( dynamic_cast<mitk::OdfImage*>(node->GetData()) + || dynamic_cast<mitk::TensorImage*>(node->GetData()) + || dynamic_cast<mitk::ShImage*>(node->GetData()) ) + { + node->SetIntProperty("ShowMaxNumber", maxNr); + } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::NormalizationDropdownChanged(int normDropdown) { typedef mitk::OdfNormalizationMethodProperty PropType; PropType::Pointer normMeth = PropType::New(); switch(normDropdown) { case 0: normMeth->SetNormalizationToMinMax(); break; case 1: normMeth->SetNormalizationToMax(); break; case 2: normMeth->SetNormalizationToNone(); break; case 3: normMeth->SetNormalizationToGlobalMax(); break; default: normMeth->SetNormalizationToMinMax(); } - if ( dynamic_cast<mitk::OdfImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::TensorImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::ShImage*>(m_SelectedNode->GetData()) ) + for (auto node : m_SelectedNodes) { - m_SelectedNode->SetProperty("Normalization", normMeth.GetPointer()); + if ( dynamic_cast<mitk::OdfImage*>(node->GetData()) + || dynamic_cast<mitk::TensorImage*>(node->GetData()) + || dynamic_cast<mitk::ShImage*>(node->GetData()) ) + { + node->SetProperty("Normalization", normMeth.GetPointer()); + } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::ScalingFactorChanged(double scalingFactor) { - if ( dynamic_cast<mitk::OdfImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::TensorImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::ShImage*>(m_SelectedNode->GetData()) ) + for (auto node : m_SelectedNodes) { - m_SelectedNode->SetFloatProperty("Scaling", scalingFactor); + if ( dynamic_cast<mitk::OdfImage*>(node->GetData()) + || dynamic_cast<mitk::TensorImage*>(node->GetData()) + || dynamic_cast<mitk::ShImage*>(node->GetData()) ) + { + node->SetFloatProperty("Scaling", scalingFactor); + } } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkControlVisualizationPropertiesView::AdditionalScaling(int additionalScaling) { typedef mitk::OdfScaleByProperty PropType; PropType::Pointer scaleBy = PropType::New(); switch(additionalScaling) { case 0: scaleBy->SetScaleByNothing(); break; case 1: scaleBy->SetScaleByGFA(); //m_Controls->params_frame->setVisible(true); break; default: scaleBy->SetScaleByNothing(); } - if ( dynamic_cast<mitk::OdfImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::TensorImage*>(m_SelectedNode->GetData()) - || dynamic_cast<mitk::ShImage*>(m_SelectedNode->GetData()) ) + for (auto node : m_SelectedNodes) { - m_SelectedNode->SetProperty("ScaleBy", scaleBy.GetPointer()); + if ( dynamic_cast<mitk::OdfImage*>(node->GetData()) + || dynamic_cast<mitk::TensorImage*>(node->GetData()) + || dynamic_cast<mitk::ShImage*>(node->GetData()) ) + { + node->SetProperty("ScaleBy", scaleBy.GetPointer()); + } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Fiber2DfadingEFX() { - if (m_SelectedNode && dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData()) ) + for (auto node : m_SelectedNodes) { - bool currentMode; - m_SelectedNode->GetBoolProperty("Fiber2DfadeEFX", currentMode); - m_SelectedNode->SetProperty("Fiber2DfadeEFX", mitk::BoolProperty::New(!currentMode)); - dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())->RequestUpdate2D(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if (node && dynamic_cast<mitk::FiberBundle*>(node->GetData()) ) + { + bool currentMode; + node->GetBoolProperty("Fiber2DfadeEFX", currentMode); + node->SetProperty("Fiber2DfadeEFX", mitk::BoolProperty::New(!currentMode)); + dynamic_cast<mitk::FiberBundle*>(node->GetData())->RequestUpdate2D(); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::FiberSlicingThickness2D() { - if (m_SelectedNode && dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())) + for (auto node : m_SelectedNodes) { - float fibThickness = m_Controls->m_FiberClippingBox->value(); - float currentThickness = 0; - m_SelectedNode->GetFloatProperty("Fiber2DSliceThickness", currentThickness); - if ( fabs(fibThickness-currentThickness) < 0.001 ) - return; - - m_SelectedNode->SetProperty("Fiber2DSliceThickness", mitk::FloatProperty::New(fibThickness)); - dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())->RequestUpdate2D(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if (node && dynamic_cast<mitk::FiberBundle*>(node->GetData())) + { + float fibThickness = m_Controls->m_FiberClippingBox->value(); + float currentThickness = 0; + node->GetFloatProperty("Fiber2DSliceThickness", currentThickness); + if ( fabs(fibThickness-currentThickness) < 0.001 ) + continue; + + node->SetProperty("Fiber2DSliceThickness", mitk::FloatProperty::New(fibThickness)); + dynamic_cast<mitk::FiberBundle*>(node->GetData())->RequestUpdate2D(); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::SetCustomColor(const itk::EventObject& /*e*/) { - if(m_SelectedNode && dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())) - { - float color[3]; - m_SelectedNode->GetColor(color); - mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData()); - fib->SetFiberColors(color[0]*255, color[1]*255, color[2]*255); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } - else if (m_SelectedNode && dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData())) + for (auto node : m_SelectedNodes) { - float color[3]; - m_SelectedNode->GetColor(color); - mitk::PeakImage::Pointer img = dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData()); - img->SetCustomColor(color[0]*255, color[1]*255, color[2]*255); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if(node && dynamic_cast<mitk::FiberBundle*>(node->GetData())) + { + float color[3]; + node->GetColor(color); + mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(node->GetData()); + fib->SetFiberColors(color[0]*255, color[1]*255, color[2]*255); + } + else if (node && dynamic_cast<mitk::PeakImage*>(node->GetData())) + { + float color[3]; + node->GetColor(color); + mitk::PeakImage::Pointer img = dynamic_cast<mitk::PeakImage*>(node->GetData()); + img->SetCustomColor(color[0]*255, color[1]*255, color[2]*255); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::ResetColoring() { - if(m_SelectedNode && dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())) + for (auto node : m_SelectedNodes) { - mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData()); - fib->ColorFibersByOrientation(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } - else if(m_SelectedNode && dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData())) - { - mitk::PeakImage::Pointer fib = dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData()); - fib->ColorByOrientation(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if(node && dynamic_cast<mitk::FiberBundle*>(node->GetData())) + { + mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(node->GetData()); + fib->ColorFibersByOrientation(); + } + else if(node && dynamic_cast<mitk::PeakImage*>(node->GetData())) + { + mitk::PeakImage::Pointer fib = dynamic_cast<mitk::PeakImage*>(node->GetData()); + fib->ColorByOrientation(); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::PlanarFigureFocus() { - if(m_SelectedNode) + for (auto node : m_SelectedNodes) { - mitk::PlanarFigure* _PlanarFigure = 0; - _PlanarFigure = dynamic_cast<mitk::PlanarFigure*> (m_SelectedNode->GetData()); - - if (_PlanarFigure && _PlanarFigure->GetPlaneGeometry()) + if(node) { + mitk::PlanarFigure* _PlanarFigure = 0; + _PlanarFigure = dynamic_cast<mitk::PlanarFigure*> (node->GetData()); - QmitkRenderWindow* selectedRenderWindow = 0; - bool PlanarFigureInitializedWindow = false; + if (_PlanarFigure && _PlanarFigure->GetPlaneGeometry()) + { - auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); + QmitkRenderWindow* selectedRenderWindow = 0; + bool PlanarFigureInitializedWindow = false; - QmitkRenderWindow* axialRenderWindow = - renderWindowPart->GetQmitkRenderWindow("axial"); + auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); - if (m_SelectedNode->GetBoolProperty("PlanarFigureInitializedWindow", - PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) - { - selectedRenderWindow = axialRenderWindow; - } + QmitkRenderWindow* axialRenderWindow = + renderWindowPart->GetQmitkRenderWindow("axial"); - QmitkRenderWindow* sagittalRenderWindow = - renderWindowPart->GetQmitkRenderWindow("sagittal"); + if (node->GetBoolProperty("PlanarFigureInitializedWindow", + PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) + { + selectedRenderWindow = axialRenderWindow; + } - if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( - "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - sagittalRenderWindow->GetRenderer())) - { - selectedRenderWindow = sagittalRenderWindow; - } + QmitkRenderWindow* sagittalRenderWindow = + renderWindowPart->GetQmitkRenderWindow("sagittal"); - QmitkRenderWindow* coronalRenderWindow = - renderWindowPart->GetQmitkRenderWindow("coronal"); + if (!selectedRenderWindow && node->GetBoolProperty( + "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, + sagittalRenderWindow->GetRenderer())) + { + selectedRenderWindow = sagittalRenderWindow; + } - if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( - "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - coronalRenderWindow->GetRenderer())) - { - selectedRenderWindow = coronalRenderWindow; - } + QmitkRenderWindow* coronalRenderWindow = + renderWindowPart->GetQmitkRenderWindow("coronal"); - QmitkRenderWindow* _3DRenderWindow = - renderWindowPart->GetQmitkRenderWindow("3d"); + if (!selectedRenderWindow && node->GetBoolProperty( + "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, + coronalRenderWindow->GetRenderer())) + { + selectedRenderWindow = coronalRenderWindow; + } - if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( - "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - _3DRenderWindow->GetRenderer())) - { - selectedRenderWindow = _3DRenderWindow; - } + QmitkRenderWindow* _3DRenderWindow = + renderWindowPart->GetQmitkRenderWindow("3d"); - const mitk::PlaneGeometry* _PlaneGeometry = _PlanarFigure->GetPlaneGeometry(); + if (!selectedRenderWindow && node->GetBoolProperty( + "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, + _3DRenderWindow->GetRenderer())) + { + selectedRenderWindow = _3DRenderWindow; + } - mitk::VnlVector normal = _PlaneGeometry->GetNormalVnl(); + const mitk::PlaneGeometry* _PlaneGeometry = _PlanarFigure->GetPlaneGeometry(); - mitk::PlaneGeometry::ConstPointer worldGeometry1 = - axialRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); - mitk::PlaneGeometry::ConstPointer _Plane1 = - dynamic_cast<const mitk::PlaneGeometry*>( worldGeometry1.GetPointer() ); - mitk::VnlVector normal1 = _Plane1->GetNormalVnl(); + mitk::VnlVector normal = _PlaneGeometry->GetNormalVnl(); - mitk::PlaneGeometry::ConstPointer worldGeometry2 = - sagittalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); - mitk::PlaneGeometry::ConstPointer _Plane2 = - dynamic_cast<const mitk::PlaneGeometry*>( worldGeometry2.GetPointer() ); - mitk::VnlVector normal2 = _Plane2->GetNormalVnl(); + mitk::PlaneGeometry::ConstPointer worldGeometry1 = + axialRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); + mitk::PlaneGeometry::ConstPointer _Plane1 = + dynamic_cast<const mitk::PlaneGeometry*>( worldGeometry1.GetPointer() ); + mitk::VnlVector normal1 = _Plane1->GetNormalVnl(); - mitk::PlaneGeometry::ConstPointer worldGeometry3 = - coronalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); - mitk::PlaneGeometry::ConstPointer _Plane3 = - dynamic_cast<const mitk::PlaneGeometry*>( worldGeometry3.GetPointer() ); - mitk::VnlVector normal3 = _Plane3->GetNormalVnl(); + mitk::PlaneGeometry::ConstPointer worldGeometry2 = + sagittalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); + mitk::PlaneGeometry::ConstPointer _Plane2 = + dynamic_cast<const mitk::PlaneGeometry*>( worldGeometry2.GetPointer() ); + mitk::VnlVector normal2 = _Plane2->GetNormalVnl(); - normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); - normal1[0] = fabs(normal1[0]); normal1[1] = fabs(normal1[1]); normal1[2] = fabs(normal1[2]); - normal2[0] = fabs(normal2[0]); normal2[1] = fabs(normal2[1]); normal2[2] = fabs(normal2[2]); - normal3[0] = fabs(normal3[0]); normal3[1] = fabs(normal3[1]); normal3[2] = fabs(normal3[2]); + mitk::PlaneGeometry::ConstPointer worldGeometry3 = + coronalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); + mitk::PlaneGeometry::ConstPointer _Plane3 = + dynamic_cast<const mitk::PlaneGeometry*>( worldGeometry3.GetPointer() ); + mitk::VnlVector normal3 = _Plane3->GetNormalVnl(); - double ang1 = angle(normal, normal1); - double ang2 = angle(normal, normal2); - double ang3 = angle(normal, normal3); + normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); + normal1[0] = fabs(normal1[0]); normal1[1] = fabs(normal1[1]); normal1[2] = fabs(normal1[2]); + normal2[0] = fabs(normal2[0]); normal2[1] = fabs(normal2[1]); normal2[2] = fabs(normal2[2]); + normal3[0] = fabs(normal3[0]); normal3[1] = fabs(normal3[1]); normal3[2] = fabs(normal3[2]); - if(ang1 < ang2 && ang1 < ang3) - { - selectedRenderWindow = axialRenderWindow; - } - else - { - if(ang2 < ang3) + double ang1 = angle(normal, normal1); + double ang2 = angle(normal, normal2); + double ang3 = angle(normal, normal3); + + if(ang1 < ang2 && ang1 < ang3) { - selectedRenderWindow = sagittalRenderWindow; + selectedRenderWindow = axialRenderWindow; } else { - selectedRenderWindow = coronalRenderWindow; + if(ang2 < ang3) + { + selectedRenderWindow = sagittalRenderWindow; + } + else + { + selectedRenderWindow = coronalRenderWindow; + } + } + + // make node visible + if (selectedRenderWindow) + { + const mitk::Point3D& centerP = _PlaneGeometry->GetOrigin(); + selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( + centerP, _PlaneGeometry->GetNormal()); } } - // make node visible - if (selectedRenderWindow) + // set interactor for new node (if not already set) + mitk::PlanarFigureInteractor::Pointer figureInteractor + = dynamic_cast<mitk::PlanarFigureInteractor*>(node->GetDataInteractor().GetPointer()); + + if(figureInteractor.IsNull()) { - const mitk::Point3D& centerP = _PlaneGeometry->GetOrigin(); - selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( - centerP, _PlaneGeometry->GetNormal()); + figureInteractor = mitk::PlanarFigureInteractor::New(); + us::Module* planarFigureModule = us::ModuleRegistry::GetModule( "MitkPlanarFigure" ); + figureInteractor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule ); + figureInteractor->SetEventConfig( "PlanarFigureConfig.xml", planarFigureModule ); + figureInteractor->SetDataNode( node ); } - } - - // set interactor for new node (if not already set) - mitk::PlanarFigureInteractor::Pointer figureInteractor - = dynamic_cast<mitk::PlanarFigureInteractor*>(m_SelectedNode->GetDataInteractor().GetPointer()); - if(figureInteractor.IsNull()) - { - figureInteractor = mitk::PlanarFigureInteractor::New(); - us::Module* planarFigureModule = us::ModuleRegistry::GetModule( "MitkPlanarFigure" ); - figureInteractor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule ); - figureInteractor->SetEventConfig( "PlanarFigureConfig.xml", planarFigureModule ); - figureInteractor->SetDataNode( m_SelectedNode ); + node->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); } - - m_SelectedNode->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); } } void QmitkControlVisualizationPropertiesView::SetInteractor() { // BUG 19179 // typedef std::vector<mitk::DataNode*> Container; // Container _NodeSet = this->GetDataManagerSelection(); // mitk::DataNode* node = 0; // mitk::FiberBundle* bundle = 0; // mitk::FiberBundleInteractor::Pointer bundleInteractor = 0; // // finally add all nodes to the model // for(Container::const_iterator it=_NodeSet.begin(); it!=_NodeSet.end() // ; it++) // { // node = const_cast<mitk::DataNode*>(*it); // bundle = dynamic_cast<mitk::FiberBundle*>(node->GetData()); // if(bundle) // { // bundleInteractor = dynamic_cast<mitk::FiberBundleInteractor*>(node->GetInteractor()); // if(bundleInteractor.IsNotNull()) // mitk::GlobalInteraction::GetInstance()->RemoveInteractor(bundleInteractor); // if(!m_Controls->m_Crosshair->isChecked()) // { // m_Controls->m_Crosshair->setChecked(false); // this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::ArrowCursor); // m_CurrentPickingNode = 0; // } // else // { // m_Controls->m_Crosshair->setChecked(true); // bundleInteractor = mitk::FiberBundleInteractor::New("FiberBundleInteractor", node); // mitk::GlobalInteraction::GetInstance()->AddInteractor(bundleInteractor); // this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::CrossCursor); // m_CurrentPickingNode = node; // } // } // } } void QmitkControlVisualizationPropertiesView::TubeRadiusChanged() { - if(m_SelectedNode && dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())) + for (auto node : m_SelectedNodes) { - float newRadius = m_Controls->m_TubeWidth->value(); - m_SelectedNode->SetFloatProperty("shape.tuberadius", newRadius); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if(node && dynamic_cast<mitk::FiberBundle*>(node->GetData())) + { + float newRadius = m_Controls->m_TubeWidth->value(); + node->SetFloatProperty("shape.tuberadius", newRadius); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::RibbonWidthChanged() { - if(m_SelectedNode && dynamic_cast<mitk::FiberBundle*>(m_SelectedNode->GetData())) + for (auto node : m_SelectedNodes) { - float newWidth = m_Controls->m_RibbonWidth->value(); - m_SelectedNode->SetFloatProperty("shape.ribbonwidth", newWidth); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if(node && dynamic_cast<mitk::FiberBundle*>(node->GetData())) + { + float newWidth = m_Controls->m_RibbonWidth->value(); + node->SetFloatProperty("shape.ribbonwidth", newWidth); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::LineWidthChanged() { - if(m_SelectedNode && dynamic_cast<mitk::PeakImage*>(m_SelectedNode->GetData())) + for (auto node : m_SelectedNodes) { - auto newWidth = m_Controls->m_LineWidth->value(); - float currentWidth = 0; - m_SelectedNode->SetFloatProperty("shape.linewidth", currentWidth); - if (currentWidth==newWidth) - return; - m_SelectedNode->SetFloatProperty("shape.linewidth", newWidth); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if(node && dynamic_cast<mitk::PeakImage*>(node->GetData())) + { + auto newWidth = m_Controls->m_LineWidth->value(); + float currentWidth = 0; + node->SetFloatProperty("shape.linewidth", currentWidth); + if (currentWidth==newWidth) + continue; + node->SetFloatProperty("shape.linewidth", newWidth); + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Welcome() { berry::PlatformUI::GetWorkbench()->GetIntroManager() ->ShowIntro(GetSite()->GetWorkbenchWindow(), false); } void QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() { - if( m_SelectedNode && m_NodeUsedForOdfVisualization.IsNotNull() ) + for (auto node : m_SelectedNodes) { - m_SelectedNode->SetProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", mitk::BoolProperty::New( m_Controls->m_OdfColorBox->currentIndex() ) ); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } - else - { - MITK_DEBUG << "QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() was called but m_NodeUsedForOdfVisualization was Null."; + if( node && m_NodeUsedForOdfVisualization.IsNotNull() ) + { + node->SetProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", mitk::BoolProperty::New( m_Controls->m_OdfColorBox->currentIndex() ) ); + } + else + { + MITK_DEBUG << "QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() was called but m_NodeUsedForOdfVisualization was Null."; + } } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.h index e75ffd3..6c2bcbe 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.h @@ -1,154 +1,154 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _QMITKControlVisualizationPropertiesView_H_INCLUDED #define _QMITKControlVisualizationPropertiesView_H_INCLUDED #include <QmitkAbstractView.h> #include <string> #include "berryISelectionListener.h" #include "berryIStructuredSelection.h" #include "berryISizeProvider.h" #include "ui_QmitkControlVisualizationPropertiesViewControls.h" #include "mitkEnumerationProperty.h" #include <mitkILifecycleAwarePart.h> #include <QmitkSliceNavigationListener.h> /*! * \ingroup org_mitk_gui_qt_diffusionquantification_internal * * \brief QmitkControlVisualizationPropertiesView * * Document your class here. */ class QmitkControlVisualizationPropertiesView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { friend struct CvpSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkControlVisualizationPropertiesView(); virtual ~QmitkControlVisualizationPropertiesView(); virtual void CreateQtPartControl(QWidget *parent) override; /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); protected slots: void VisibleOdfsON_S(); void VisibleOdfsON_T(); void VisibleOdfsON_C(); void ShowMaxNumberChanged(); void NormalizationDropdownChanged(int); void ScalingFactorChanged(double); void AdditionalScaling(int); void OnThickSlicesModeSelected( QAction* action ); void OnTSNumChanged(int num); void ResetColoring(); void PlanarFigureFocus(); void Fiber2DfadingEFX(); void FiberSlicingThickness2D(); void LineWidthChanged(); void TubeRadiusChanged(); void RibbonWidthChanged(); void SetInteractor(); void Toggle3DClipping(bool enabled=true); void FlipPeaks(); void Welcome(); void OnSliceChanged(); void SetColor(); void Toggle3DPeaks(); /// \brief Slot function for switching colourisation mode of glyphs. void OnColourisationModeChanged(); protected: virtual void SetFocus() override; virtual void Activated() override; virtual void Deactivated() override; virtual void Visible() override; virtual void Hidden() override; virtual void NodeRemoved(const mitk::DataNode* node) override; /// \brief called by QmitkAbstractView when DataManager's selection has changed virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList<mitk::DataNode::Pointer>& nodes) override; virtual void NodeAdded(const mitk::DataNode *node) override; void SetCustomColor(const itk::EventObject& /*e*/); bool IsPlaneRotated(); void SliceRotation(const itk::EventObject&); void Set3DClippingPlane(bool disable, mitk::DataNode *node, std::string plane); void SetTs(int m_CurrentThickSlicesMode, int num, std::string render_window); Ui::QmitkControlVisualizationPropertiesViewControls* m_Controls; QScopedPointer<berry::ISelectionListener> m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; mitk::DataNode::Pointer m_NodeUsedForOdfVisualization; QIcon* m_IconTexOFF; QIcon* m_IconTexON; QIcon* m_IconGlyOFF_T; QIcon* m_IconGlyON_T; QIcon* m_IconGlyOFF_C; QIcon* m_IconGlyON_C; QIcon* m_IconGlyOFF_S; QIcon* m_IconGlyON_S; bool m_TexIsOn; bool m_GlyIsOn_T; bool m_GlyIsOn_C; bool m_GlyIsOn_S; int m_CurrentThickSlicesMode; int m_CurrentThickSlicesNum; QLabel* m_TSLabel; QMenu* m_MyMenu; // for planarfigure and bundle handling: - mitk::DataNode::Pointer m_SelectedNode; + std::vector< mitk::DataNode::Pointer > m_SelectedNodes; mitk::DataNode* m_CurrentPickingNode; unsigned long m_ColorPropertyObserverTag; unsigned long m_OpacityPropertyObserverTag; mitk::ColorProperty::Pointer m_Color; mitk::FloatProperty::Pointer m_Opacity; QmitkSliceNavigationListener m_SliceChangeListener; }; #endif // _QMITKControlVisualizationPropertiesView_H_INCLUDED