diff --git a/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp b/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp index cd32b97..ab3e64f 100644 --- a/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp +++ b/Modules/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp @@ -1,307 +1,307 @@ /*=================================================================== 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 #include #include #include #include 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& 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 ref; ref.fill(0); ref[0]=1; for (int i=0; i0) 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 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_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) std::cout << "TrackingHandlerOdf - Sharpening ODfs" << std::endl; } int TrackingHandlerOdf::SampleOdf(vnl_vector< float >& probs, vnl_vector< float >& angles) { boost::random::discrete_distribution 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> 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])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 TrackingHandlerOdf::ProposeDirection(const itk::Point& pos, std::deque >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed 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(pos, m_Parameters->m_InterpolateTractographyData, m_GfaInterpolator); if (gfam_Cutoff) return output_direction; vnl_vector_fixed 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(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 min_odf_val = 999; int max_idx_d = -1; int c = 0; for (int i : m_OdfHemisphereIndices) { if (odf_values[i]<0) odf_values[i] = 0; if (odf_values[i]>max_odf_val) { max_odf_val = odf_values[i]; max_idx_d = c; } if (odf_values[i]m_SharpenOdfs) { // sharpen ODF probs -= min_odf_val; probs /= (max_odf_val-min_odf_val); for (unsigned int i=0; i0) { probs /= odf_sum; 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; im_Mode==MODE::DETERMINISTIC && odf_val>max_prob && odf_val>m_Parameters->m_OdfCutoff) { // use maximum peak of the ODF weighted with the directional prior max_prob = odf_val; vnl_vector_fixed d = m_OdfFloatDirs.get_row(i); if (angle<0) d *= -1; output_direction = odf_val*d; } else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) { // update ODF probabilties with the ODF values pow(abs_angle, m_DirPriorPower) probs[i] = odf_val; probs_sum += probs[i]; } } // do probabilistic sampling if (m_Parameters->m_Mode==MODE::PROBABILISTIC && probs_sum>0.0001) { 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 (aGetAngularThresholdDot()) 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/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp index faad923..1abd408 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp @@ -1,405 +1,408 @@ /*=================================================================== 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. ===================================================================*/ //misc #define _USE_MATH_DEFINES #include // Blueberry #include #include // Qmitk #include "QmitkSimpleRegistrationView.h" // MITK #include #include #include #include #include #include #include #include #include #include #include #include // Qt #include #define _USE_MATH_DEFINES #include const std::string QmitkSimpleRegistrationView::VIEW_ID = "org.mitk.views.simpleregistrationview"; QmitkSimpleRegistrationView::QmitkSimpleRegistrationView() : QmitkAbstractView() , m_Controls( 0 ) , m_RegistrationType(0) { } // Destructor QmitkSimpleRegistrationView::~QmitkSimpleRegistrationView() { } void QmitkSimpleRegistrationView::StartRegistration() { QmitkRegistrationJob* pJob; if (m_Controls->m_RegBox->currentIndex()==0) { mitk::MultiModalRigidDefaultRegistrationAlgorithm< ItkFloatImageType >::Pointer algo = mitk::MultiModalRigidDefaultRegistrationAlgorithm< ItkFloatImageType >::New(); pJob = new QmitkRegistrationJob(algo); m_RegistrationType = 0; } else { mitk::MultiModalAffineDefaultRegistrationAlgorithm< ItkFloatImageType >::Pointer algo = mitk::MultiModalAffineDefaultRegistrationAlgorithm< ItkFloatImageType >::New(); pJob = new QmitkRegistrationJob(algo); m_RegistrationType = 1; } pJob->setAutoDelete(true); m_MovingImageNode = m_Controls->m_MovingImageBox->GetSelectedNode(); mitk::Image::Pointer movingImage = dynamic_cast(m_MovingImageNode->GetData()); if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(movingImage)) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(movingImage, itkVectorImagePointer); itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(m_Controls->m_MovingChannelBox->value()); filter->Update(); mitk::Image::Pointer newImage = mitk::Image::New(); newImage->InitializeByItk( filter->GetOutput() ); newImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); pJob->m_spMovingData = newImage; } else pJob->m_spMovingData = movingImage; mitk::Image::Pointer fixedImage = dynamic_cast(m_Controls->m_FixedImageBox->GetSelectedNode()->GetData()); if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(fixedImage)) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(fixedImage, itkVectorImagePointer); itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(m_Controls->m_MovingChannelBox->value()); filter->Update(); mitk::Image::Pointer newImage = mitk::Image::New(); newImage->InitializeByItk( filter->GetOutput() ); newImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); pJob->m_spTargetData = newImage; } else pJob->m_spTargetData = fixedImage; pJob->m_TargetDataUID = mitk::EnsureUID(m_Controls->m_FixedImageBox->GetSelectedNode()->GetData()); pJob->m_MovingDataUID = mitk::EnsureUID(m_Controls->m_MovingImageBox->GetSelectedNode()->GetData()); connect(pJob, SIGNAL(RegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), this, SLOT(OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), Qt::BlockingQueuedConnection); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); m_Controls->m_RegistrationStartButton->setEnabled(false); m_Controls->m_RegistrationStartButton->setText("Registration in progress ..."); } void QmitkSimpleRegistrationView::OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer spResultRegistration, const QmitkRegistrationJob* job) { mitk::Image::Pointer movingImage = dynamic_cast(m_MovingImageNode->GetData()); mitk::Image::Pointer image; if (m_RegistrationType==0 && !m_Controls->m_ResampleBox->isChecked()) { image = mitk::ImageMappingHelper::refineGeometry(movingImage, spResultRegistration, true); mitk::DiffusionPropertyHelper::CopyProperties(movingImage, image, true); auto reg = spResultRegistration->GetRegistration(); - typedef mitk::DiffusionImageCorrectionFilter CorrectionFilterType; - CorrectionFilterType::Pointer corrector = CorrectionFilterType::New(); - corrector->SetImage( image ); - corrector->CorrectDirections( mitk::MITKRegistrationHelper::getAffineMatrix(reg, false)->GetMatrix().GetVnlMatrix() ); + if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(movingImage)) + { + typedef mitk::DiffusionImageCorrectionFilter CorrectionFilterType; + CorrectionFilterType::Pointer corrector = CorrectionFilterType::New(); + corrector->SetImage( image ); + corrector->CorrectDirections( mitk::MITKRegistrationHelper::getAffineMatrix(reg, false)->GetMatrix().GetVnlMatrix() ); + } } else { if (!mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(movingImage)) { image = mitk::ImageMappingHelper::map(movingImage, spResultRegistration, false, 0, job->m_spTargetData->GetGeometry(), false, 0, mitk::ImageMappingInterpolator::BSpline_3); } else { typedef itk::ComposeImageFilter < ITKDiffusionVolumeType > ComposeFilterType; ComposeFilterType::Pointer composer = ComposeFilterType::New(); ItkDwiType::Pointer itkVectorImagePointer = mitk::DiffusionPropertyHelper::GetItkVectorImage(movingImage); for (unsigned int i=0; iGetVectorLength(); ++i) { itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(i); filter->Update(); mitk::Image::Pointer gradientVolume = mitk::Image::New(); gradientVolume->InitializeByItk( filter->GetOutput() ); gradientVolume->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); mitk::Image::Pointer registered_mitk_image = mitk::ImageMappingHelper::map(gradientVolume, spResultRegistration, false, 0, job->m_spTargetData->GetGeometry(), false, 0, mitk::ImageMappingInterpolator::BSpline_3); ITKDiffusionVolumeType::Pointer registered_itk_image = ITKDiffusionVolumeType::New(); mitk::CastToItkImage(registered_mitk_image, registered_itk_image); composer->SetInput(i, registered_itk_image); } composer->Update(); image = mitk::GrabItkImageMemory( composer->GetOutput() ); mitk::DiffusionPropertyHelper::CopyProperties(movingImage, image, true); auto reg = spResultRegistration->GetRegistration(); typedef mitk::DiffusionImageCorrectionFilter CorrectionFilterType; CorrectionFilterType::Pointer corrector = CorrectionFilterType::New(); corrector->SetImage( image ); corrector->CorrectDirections( mitk::MITKRegistrationHelper::getAffineMatrix(reg, false)->GetMatrix().GetVnlMatrix() ); } } if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image)) mitk::DiffusionPropertyHelper::InitializeImage( image ); mitk::DiffusionPropertyHelper::CopyDICOMProperties(movingImage, image); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); resultNode->SetData(image); if (m_MovingImageNode.IsNotNull()) { m_MovingImageNode->SetVisibility(false); QString name = m_MovingImageNode->GetName().c_str(); if (m_RegistrationType==0) resultNode->SetName((name+"_registered (rigid)").toStdString().c_str()); else resultNode->SetName((name+"_registered (affine)").toStdString().c_str()); } else { if (m_RegistrationType==0) resultNode->SetName("Registered (rigid)"); else resultNode->SetName("Registered (affine)"); } // resultNode->SetOpacity(0.6); // resultNode->SetColor(0.0, 0.0, 1.0); GetDataStorage()->Add(resultNode); mitk::RenderingManager::GetInstance()->InitializeViews( resultNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); if (m_Controls->m_RegOutputBox->isChecked()) { mitk::DataNode::Pointer registration_node = mitk::DataNode::New(); registration_node->SetData(spResultRegistration); if (m_RegistrationType==0) registration_node->SetName("Registration Object (rigid)"); else registration_node->SetName("Registration Object (affine)"); GetDataStorage()->Add(registration_node, resultNode); } this->GetRenderWindowPart()->RequestUpdate(); m_Controls->m_RegistrationStartButton->setEnabled(true); m_Controls->m_RegistrationStartButton->setText("Start Registration"); m_MovingImageNode = nullptr; TractoChanged(); } void QmitkSimpleRegistrationView::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::QmitkSimpleRegistrationViewControls; m_Controls->setupUi( parent ); m_Controls->m_FixedImageBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_MovingImageBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); m_Controls->m_FixedImageBox->SetPredicate(isImagePredicate); m_Controls->m_MovingImageBox->SetPredicate(isImagePredicate); mitk::TNodePredicateDataType::Pointer isFib = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isReg = mitk::TNodePredicateDataType::New(); m_Controls->m_TractoBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_RegObjectBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_TractoBox->SetPredicate(isFib); m_Controls->m_RegObjectBox->SetPredicate(isReg); connect( m_Controls->m_FixedImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(FixedImageChanged()) ); connect( m_Controls->m_MovingImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(MovingImageChanged()) ); connect( m_Controls->m_TractoBox, SIGNAL(currentIndexChanged(int)), this, SLOT(TractoChanged()) ); connect( m_Controls->m_RegObjectBox, SIGNAL(currentIndexChanged(int)), this, SLOT(TractoChanged()) ); connect( m_Controls->m_RegistrationStartButton, SIGNAL(clicked()), this, SLOT(StartRegistration()) ); connect( m_Controls->m_TractoRegistrationStartButton, SIGNAL(clicked()), this, SLOT(StartTractoRegistration()) ); FixedImageChanged(); MovingImageChanged(); TractoChanged(); } } void QmitkSimpleRegistrationView::StartTractoRegistration() { mitk::FiberBundle::Pointer fib = dynamic_cast(m_Controls->m_TractoBox->GetSelectedNode()->GetData()); mitk::MAPRegistrationWrapper::Pointer reg = dynamic_cast(m_Controls->m_RegObjectBox->GetSelectedNode()->GetData()); mitk::MITKRegistrationHelper::Affine3DTransformType::Pointer affine = mitk::MITKRegistrationHelper::getAffineMatrix(reg, false); mitk::FiberBundle::Pointer fib_copy = fib->GetDeepCopy(); fib_copy->TransformFibers(affine); mitk::DiffusionPropertyHelper::CopyProperties(fib, fib_copy); mitk::DataNode::Pointer registration_node = mitk::DataNode::New(); registration_node->SetData(fib_copy); QString name = m_Controls->m_TractoBox->GetSelectedNode()->GetName().c_str(); registration_node->SetName((name+"_registered").toStdString().c_str()); GetDataStorage()->Add(registration_node, m_Controls->m_TractoBox->GetSelectedNode()); } void QmitkSimpleRegistrationView::TractoChanged() { if (m_Controls->m_RegObjectBox->GetSelectedNode().IsNotNull() && m_Controls->m_TractoBox->GetSelectedNode().IsNotNull()) m_Controls->m_TractoRegistrationStartButton->setEnabled(true); else m_Controls->m_TractoRegistrationStartButton->setEnabled(false); } void QmitkSimpleRegistrationView::FixedImageChanged() { if (m_Controls->m_FixedImageBox->GetSelectedNode().IsNotNull()) { mitk::Image::Pointer image = dynamic_cast(m_Controls->m_FixedImageBox->GetSelectedNode()->GetData()); int channels = image->GetNumberOfChannels(); int dims = image->GetDimension(); int fourth_dim_size = image->GetTimeSteps(); bool isdiff = mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image); if (dims==4 || channels>1) { m_Controls->m_FixedChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } if (isdiff) { m_Controls->m_FixedChannelBox->setEnabled(true); if (fourth_dim_size>1) m_Controls->m_FixedChannelBox->setMaximum(fourth_dim_size-1); else if (isdiff) m_Controls->m_FixedChannelBox->setMaximum(mitk::DiffusionPropertyHelper::GetGradientContainer(image)->Size()-1); } else { m_Controls->m_FixedChannelBox->setEnabled(false); } m_Controls->m_RegistrationStartButton->setEnabled(true); } else { m_Controls->m_FixedChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } } void QmitkSimpleRegistrationView::MovingImageChanged() { if (m_Controls->m_MovingImageBox->GetSelectedNode().IsNotNull()) { mitk::Image::Pointer image = dynamic_cast(m_Controls->m_MovingImageBox->GetSelectedNode()->GetData()); int channels = image->GetNumberOfChannels(); int dims = image->GetDimension(); int fourth_dim_size = image->GetTimeSteps(); bool isdiff = mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image); if (dims==4 || channels>1) { m_Controls->m_MovingChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } if (isdiff) { m_Controls->m_MovingChannelBox->setEnabled(true); if (fourth_dim_size>1) m_Controls->m_MovingChannelBox->setMaximum(fourth_dim_size-1); else if (isdiff) m_Controls->m_MovingChannelBox->setMaximum(mitk::DiffusionPropertyHelper::GetGradientContainer(image)->Size()-1); } else { m_Controls->m_MovingChannelBox->setEnabled(false); } m_Controls->m_RegistrationStartButton->setEnabled(true); } else { m_Controls->m_MovingChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } } void QmitkSimpleRegistrationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& ) { FixedImageChanged(); MovingImageChanged(); TractoChanged(); } void QmitkSimpleRegistrationView::SetFocus() { m_Controls->m_RegistrationStartButton->setFocus(); FixedImageChanged(); MovingImageChanged(); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox index 6cc4616..32df94c 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox @@ -1,105 +1,105 @@ /** \page org_mitk_views_streamlinetracking Streamline Tractography This view enables streamline tractography on various input data. The corresponding command line application is named "MitkStreamlineTractography". Available sections: - \ref StrTrackUserManualInputData - \ref StrTrackUserManualSeeding - \ref StrTrackUserManualConstraints - \ref StrTrackUserManualParameters - \ref StrTrackUserManualNeighbourhoodSampling - \ref StrTrackUserManualDataHandling - \ref StrTrackUserManualPostprocessing - \ref StrTrackUserManualReferences \section StrTrackUserManualInputData Input Data Select the data you want to track on in the datamanager. Supported file types are: - One or multiple DTI images selected in the datamanager. - One ODF image, e.g. obtained using MITK Q-ball reconstruction or MRtrix CSD (tractography similar to [6]). - One peak image (4D float image). - One raw diffusion-weighted image for machine learning based tractography [1]. -- Tractography Forest: Needed for machine learning based tractography [1]. \section StrTrackUserManualSeeding Seeding Specify how, where and how many tractography seed points are placed. This can be either done statically using a seed image or in an interactive fashion. Interactive tractography enables the dynamic placement of spherical seed regions simply by clicking into the image (similar to [5]). Image based seeding: - Seed Image: ROI image used to define the seed voxels. If no seed mask is specified, the whole image volume is seeded. - Seeds per voxel: If set to 1, the seed is defined as the voxel center. If > 1 the seeds are distributet randomly inside the voxel. Interactive seeding: - Update on Parameter Change: When "Update on Parameter Change" is checked, each parameter change causes an instant retracking with the new parameters. This enables an intuitive exploration of the effects that the other tractography parameters have on the resulting tractogram. - Radius: Radius of the manually placed spherical seed region. - Num.Seeds: Number of seeds placed randomly inside the spherical seed region. Parameters for both seeding modes: - Trials Per Seed: Try each seed N times until a valid streamline is obtained (only for probabilistic tractography). - Max. Num. Fibers: Tractography is stopped after the desired number of fibers is reached, even before all seed points are processed. \section StrTrackUserManualConstraints ROI Constraints Specify various ROI and mask images to constrain the tractography process. - Mask Image: ROI image used to constrain the generated streamlines, typically a brain mask. Streamlines that leave the regions defined in this image will stop immediately. - Stop ROI Image: ROI image used to define stopping regions. Streamlines that enter the regions defined in this image will stop immediately. - Exclusion ROI Image: Fibers that enter a region defined in this image will be discarded. - Endpoint Constraints: Determines which fibers are accepted based on their endpoint location. Options are: - No constraints on endpoint locations (command line option NONE) - Both EPs are required to be located in the target image (command line option EPS_IN_TARGET) - Both EPs are required to be located in the target image and the image values at the respective position needs to be distinct (command line option EPS_IN_TARGET_LABELDIFF) - One EP is required to be located in the seed image and one in the target image (command line option EPS_IN_SEED_AND_TARGET) - At least one EP is required to be located in the target image (command line option MIN_ONE_EP_IN_TARGET) - Exactly one EP is required to be located in the target image (command line option ONE_EP_IN_TARGET) - No EP is allowed to be located in the target image (command line option NO_EP_IN_TARGET) - Target Image: ROI image needed for endpoint constraints. \section StrTrackUserManualParameters Tractography Parameters -- Mode: Toggle between deterministic and probabilistic tractography (also affects tracking prior proposals). The probabilistic method simply samples the output direction from the discrete probability ditribution provided by the discretized ODF. Probabilistic peak tracking does not derive probabilities from the data but simply adds a normally distributed jitter to the proposed direction. +- Mode: Toggle between deterministic and probabilistic tractography (also affects tracking prior proposals). The probabilistic method simply samples the output direction from the discrete probability distribution provided by the discretized ODF. Probabilistic peak tracking does not derive probabilities from the data but simply adds a normally distributed jitter to the proposed direction. - Sharpen ODFs: If you are using dODF images as input, it is advisable to sharpen the ODFs (min-max normalize and raise to the power of 4). This is not necessary (and not recommended) for CSD fODFs, since they are naturally much sharper. - Cutoff: If the streamline reaches a position with an FA value or peak magnitude lower than the speciefied threshold, tracking is terminated. Typical values are 0.2 for FA/GFA and 0.1 for CSD peaks. - FA/GFA image used to determine streamline termination. If no image is specified, the FA/GFA image is automatically calculated from the input image. If multiple tensor images are used as input, it is recommended to provide such an image since the FA maps calculated from the individual input tensor images can not provide a suitable termination criterion. - ODF Cutoff: 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. - Step Size: The algorithm proceeds along the streamline with a fixed stepsize. Default is 0.5*minSpacing. - Min. Tract Length: Shorter fibers are discarded. - Max. Tract Length: Longer fibers are discarded. - Angular Threshold: Maximum angle between two successive steps (in degree). Default is 90° * step_size. For probabilistic tractography, candidate directions exceeding this threshold have probability 0, i.e. the respective ODF value is set to zero. The probabilities of the valid directions are normalized to sum to 1. - Loop Check: Stop streamline if the threshold on the angular stdev over the last 4 voxel lengths is exceeded. -1 = no loop check. - f and g values to balance between FACT [2] and TEND [3,4] tracking (only for tensor based tractography). For further information please refer to [2,3] - Peak Jitter: Used for probabilistic peak tractography. Since no probability are provided by the data, a gausian jitter is added to the peaks. The value influences the standard deviataion of the gaussian (dir[i] += normal(0, peak_jitter*|dir[i]|)). \section StrTrackUserManualTractographyPrior Tractography Prior It is possible to use a peak image as prior for tractography on arbitrary other input images. The local progression direction is determined as the weighted average between the direction obtained from the prior and the input data. - Weight: Weighting factor between prior and input data directions. A weight of zero means that no prior iformation is used. With a weight of one, tractography is performed directly on the prior directions itself. - Restrict to Prior: The prior image is used as tractography mask. Voxels without prior peaks are excluded. - New Directions from Prior: By default, the prior is used even if there is no valid direction found in the data. If unchecked, the prior cannot create directions where there are none in the data. - Flip directions: Internally flips prior directions. This might be necessary depending on the input data. \section StrTrackUserManualDataHandling Data Handling - Flip directions: Internally flips progression directions. This might be necessary depending on the input data. - Interpolate Tractography Data: Trilinearly interpolate the input image used for tractography. - Interpolate ROI Images: Trilinearly interpolate the ROI images used to constrain the tractography. \section StrTrackUserManualNeighbourhoodSampling Neighbourhood Sampling (for details see [1]) - Neighborhood Samples: Number of neighborhood samples that are used to determine the next fiber progression direction. - Sampling Distance: Distance of the sampling positions from the current streamline position (in voxels). - Use Only Frontal Samples: Only neighborhood samples in front of the current streamline position are considered. - Use Stop-Votes: 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. \section StrTrackUserManualPostprocessing Output and Postprocessing - Compress Fibers: Whole brain tractograms obtained with a small step size can contain billions of points. The tractograms can be compressed by removing points that do not really contribute to the fiber shape, such as many points on a straight line. An error threshold (in mm) can be defined to specify which points should be removed and which not. - Output Probability Map: No streamline are generated. Instead, the tractography outputs a visitation-count map that indicates the probability of a fiber to reach a voxel from the selected seed region. For this measure to be sensible, the number of seeds per voxel needs to be rather large. \section StrTrackUserManualReferences References [1] Neher, Peter F., Marc-Alexandre Côté, Jean-Christophe Houde, Maxime Descoteaux, and Klaus H. Maier-Hein. “Fiber Tractography Using Machine Learning.” NeuroImage. Accessed July 19, 2017. doi:10.1016/j.neuroimage.2017.07.028.\n [2] Mori, Susumu, Walter E. Kaufmann, Godfrey D. Pearlson, Barbara J. Crain, Bram Stieltjes, Meiyappan Solaiyappan, and Peter C. M. Van Zijl. “In Vivo Visualization of Human Neural Pathways by Magnetic Resonance Imaging.” Annals of Neurology 47 (2000): 412–414.\n [3] Weinstein, David, Gordon Kindlmann, and Eric Lundberg. “Tensorlines: Advection-Diffusion Based Propagation through Diffusion Tensor Fields.” In Proceedings of the Conference on Visualization’99: Celebrating Ten Years, 249–253, n.d.\n [4] Lazar, Mariana, David M. Weinstein, Jay S. Tsuruda, Khader M. Hasan, Konstantinos Arfanakis, M. Elizabeth Meyerand, Benham Badie, et al. “White Matter Tractography Using Diffusion Tensor Deflection.” Human Brain Mapping 18, no. 4 (2003): 306–321.\n [5] Chamberland, M., K. Whittingstall, D. Fortin, D. Mathieu, and M. Descoteaux. “Real-Time Multi-Peak Tractography for Instantaneous Connectivity Display.” Front Neuroinform 8 (2014): 59. doi:10.3389/fninf.2014.00059.\n [6] Tournier, J-Donald, Fernando Calamante, and Alan Connelly. “MRtrix: Diffusion Tractography in Crossing Fiber Regions.” International Journal of Imaging Systems and Technology 22, no. 1 (March 2012): 53–66. doi:10.1002/ima.22005. */ 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 f4effe8..5b3afa8 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,1202 +1,1215 @@ /*=================================================================== 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 #include #include // Qmitk #include "QmitkStreamlineTrackingView.h" #include "QmitkStdMultiWidget.h" // Qt #include #include // MITK #include #include #include #include #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include #include #include 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::Pointer isPeakImagePredicate = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isTractographyForest = mitk::TNodePredicateDataType::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(stateChanged(int)), 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_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->setChecked(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_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 QmitkStreamlineTrackingView::GetParametersFromGui() { std::shared_ptr params = std::make_shared(); // 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(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(m_Controls->m_ScalarThresholdBox->value()); params->m_F = static_cast(m_Controls->m_fBox->value()); params->m_G = static_cast(m_Controls->m_gBox->value()); params->m_OdfCutoff = static_cast(m_Controls->m_OdfCutoffBox->value()); params->m_SharpenOdfs = m_Controls->m_SharpenOdfsBox->isChecked(); params->m_Weight = static_cast(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_PeakJitter = static_cast(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 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."); 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(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 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 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(obj)!=nullptr) new_val = boost::lexical_cast(qobject_cast(obj)->value()); else if (qobject_cast(obj)!=nullptr) new_val = boost::lexical_cast(qobject_cast(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& 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(node->GetData()) ) { if( dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(node->GetData()))) { m_InputImageNodes.push_back(node); retrack = true; } else { mitk::Image* img = dynamic_cast(node->GetData()); if (img!=nullptr && img->GetDimension()==3) m_AdditionalInputImages.push_back(dynamic_cast(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; im_TensorImageLabel->setText("select in data-manager"); 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); - m_Controls->m_PeakJitterBox->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(m_InputImageNodes.at(0)->GetData()) ) { - m_Controls->m_fBox->setEnabled(true); - m_Controls->m_fLabel->setEnabled(true); - m_Controls->m_gBox->setEnabled(true); - m_Controls->m_gLabel->setEnabled(true); + 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(m_InputImageNodes.at(0)->GetData()) || dynamic_cast(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(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(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(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(m_InputImageNodes.at(0)->GetData())) ); filter->Update(); dynamic_cast(m_TrackingHandler)->SetOdfImage(filter->GetOutput()); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast(m_TrackingHandler)->SetGfaImage(itkImg); } } dynamic_cast(m_TrackingHandler)->SetIsOdfFromTensor(true); } else { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerTensor(); for (unsigned int i=0; i(m_TrackingHandler)->AddTensorImage(mitk::convert::GetItkTensorFromTensorImage(dynamic_cast(m_InputImageNodes.at(i)->GetData())).GetPointer()); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast(m_TrackingHandler)->SetFaImage(itkImg); } } } } else if ( dynamic_cast(m_InputImageNodes.at(0)->GetData()) || dynamic_cast(m_InputImageNodes.at(0)->GetData())) { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerOdf(); if (dynamic_cast(m_InputImageNodes.at(0)->GetData())) dynamic_cast(m_TrackingHandler)->SetOdfImage(mitk::convert::GetItkOdfFromShImage(dynamic_cast(m_InputImageNodes.at(0)->GetData()))); else dynamic_cast(m_TrackingHandler)->SetOdfImage(mitk::convert::GetItkOdfFromOdfImage(dynamic_cast(m_InputImageNodes.at(0)->GetData()))); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast(m_TrackingHandler)->SetGfaImage(itkImg); } } } else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(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(m_Controls->m_ForestSelectionWidget->GetSelectedNode()->GetData()); mitk::Image::Pointer dwi = dynamic_cast(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((forest->GetNumFeatures() - (100 + additionalFeatureImages.at(0).size()))/3); m_TrackingHandler = new mitk::TrackingHandlerRandomForest<6, 100>(); dynamic_cast*>(m_TrackingHandler)->AddDwi(dwi); dynamic_cast*>(m_TrackingHandler)->SetAdditionalFeatureImages(additionalFeatureImages); dynamic_cast*>(m_TrackingHandler)->SetForest(forest); forest_valid = dynamic_cast*>(m_TrackingHandler)->IsForestValid(); } else { params->m_NumPreviousDirections = static_cast((forest->GetNumFeatures() - (28 + additionalFeatureImages.at(0).size()))/3); m_TrackingHandler = new mitk::TrackingHandlerRandomForest<6, 28>(); dynamic_cast*>(m_TrackingHandler)->AddDwi(dwi); dynamic_cast*>(m_TrackingHandler)->SetAdditionalFeatureImages(additionalFeatureImages); dynamic_cast*>(m_TrackingHandler)->SetForest(forest); forest_valid = dynamic_cast*>(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(m_TrackingHandler)->SetPeakImage(mitk::convert::GetItkPeakFromPeakImage(dynamic_cast(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(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(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(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(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(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(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(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(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(m_InputImageNodes.at(0)->GetData())->GetGeometry()->GetExtentInMM(i)>max_size) max_size = dynamic_cast(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); }