diff --git a/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp b/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp index 6f22880b1b..cb49dd113d 100644 --- a/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp +++ b/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp @@ -1,482 +1,482 @@ #ifndef _mitkCLUtil_HXX #define _mitkCLUtil_HXX #include #include #include #include // itk includes #include #include // Morphologic Operations #include #include #include #include #include #include #include #include // Image Filter #include void mitk::CLUtil::ProbabilityMap(const mitk::Image::Pointer & image , double mean, double stddev, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkProbabilityMap, 3, mean, stddev, outimage); } void mitk::CLUtil::ErodeGrayscale(mitk::Image::Pointer & image , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkErodeGrayscale, 3, outimage, radius, d); } void mitk::CLUtil::DilateGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkDilateGrayscale, 3, outimage, radius, d); } void mitk::CLUtil::FillHoleGrayscale(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_1(image, mitk::CLUtil::itkFillHoleGrayscale, 3, outimage); } void mitk::CLUtil::InsertLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & maskImage, unsigned int label) { AccessByItk_2(image, mitk::CLUtil::itkInsertLabel, maskImage, label); } void mitk::CLUtil::GrabLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage, unsigned int label) { AccessFixedDimensionByItk_2(image, mitk::CLUtil::itkGrabLabel, 3, outimage, label); } void mitk::CLUtil::ConnectedComponentsImage(mitk::Image::Pointer & image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components) { AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkConnectedComponentsImage,3, mask, outimage, num_components); } void mitk::CLUtil::MergeLabels(mitk::Image::Pointer & img, const std::map & map) { AccessByItk_1(img, mitk::CLUtil::itkMergeLabels, map); } void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, std::map & map) { AccessByItk_1(image, mitk::CLUtil::itkCountVoxel, map); } void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, unsigned int label, unsigned int & count) { AccessByItk_2(image, mitk::CLUtil::itkCountVoxel, label, count); } void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, unsigned int & count) { AccessByItk_1(image, mitk::CLUtil::itkCountVoxel, count); } void mitk::CLUtil::CreateCheckerboardMask(mitk::Image::Pointer image, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_1(image, mitk::CLUtil::itkCreateCheckerboardMask,3, outimage); } void mitk::CLUtil::LogicalAndImages(const mitk::Image::Pointer & image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_2(image1,itkLogicalAndImages, 3, image2, outimage); } void mitk::CLUtil::InterpolateCheckerboardPrediction(mitk::Image::Pointer checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage) { AccessFixedDimensionByItk_2(checkerboard_prediction, mitk::CLUtil::itkInterpolateCheckerboardPrediction,3, checkerboard_mask, outimage); } void mitk::CLUtil::GaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed ,double sigma) { AccessFixedDimensionByItk_2(image, mitk::CLUtil::itkGaussianFilter,3, smoothed, sigma); } void mitk::CLUtil::DilateBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor , MorphologicalDimensions d) { AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkDilateBinary, 3, resultImage, factor, d); } void mitk::CLUtil::ErodeBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkErodeBinary, 3, resultImage, factor, d); } void mitk::CLUtil::ClosingBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkClosingBinary, 3, resultImage, factor, d); } template void mitk::CLUtil::itkProbabilityMap(const TImageType * sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage) { itk::Image::Pointer itk_img = itk::Image::New(); itk_img->SetRegions(sourceImage->GetLargestPossibleRegion()); itk_img->SetOrigin(sourceImage->GetOrigin()); itk_img->SetSpacing(sourceImage->GetSpacing()); itk_img->SetDirection(sourceImage->GetDirection()); itk_img->Allocate(); itk::ImageRegionConstIterator it(sourceImage,sourceImage->GetLargestPossibleRegion()); itk::ImageRegionIterator > outit(itk_img,itk_img->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { double x = it.Value(); - double prob = (1.0/(std_dev*std::sqrt(2.0*M_PI))) * std::exp(-(((x-mean)*(x-mean))/(2.0*std_dev*std_dev))); + double prob = (1.0/(std_dev*std::sqrt(2.0*itk::Math::pi))) * std::exp(-(((x-mean)*(x-mean))/(2.0*std_dev*std_dev))); outit.Set(prob); ++it; ++outit; } mitk::CastToMitkImage(itk_img, resultImage); } template< typename TImageType > void mitk::CLUtil::itkInterpolateCheckerboardPrediction(TImageType * checkerboard_prediction, Image::Pointer &checkerboard_mask, mitk::Image::Pointer & outimage) { typename TImageType::Pointer itk_checkerboard_mask; mitk::CastToItkImage(checkerboard_mask,itk_checkerboard_mask); typename TImageType::Pointer itk_outimage = TImageType::New(); itk_outimage->SetRegions(checkerboard_prediction->GetLargestPossibleRegion()); itk_outimage->SetDirection(checkerboard_prediction->GetDirection()); itk_outimage->SetOrigin(checkerboard_prediction->GetOrigin()); itk_outimage->SetSpacing(checkerboard_prediction->GetSpacing()); itk_outimage->Allocate(); itk_outimage->FillBuffer(0); //typedef typename itk::ShapedNeighborhoodIterator::SizeType SizeType; typedef itk::Size<3> SizeType; SizeType size; size.Fill(1); itk::ShapedNeighborhoodIterator iit(size,checkerboard_prediction,checkerboard_prediction->GetLargestPossibleRegion()); itk::ShapedNeighborhoodIterator mit(size,itk_checkerboard_mask,itk_checkerboard_mask->GetLargestPossibleRegion()); itk::ImageRegionIterator oit(itk_outimage,itk_outimage->GetLargestPossibleRegion()); typedef typename itk::ShapedNeighborhoodIterator::OffsetType OffsetType; OffsetType offset; offset.Fill(0); offset[0] = 1; // {1,0,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); offset[0] = -1; // {-1,0,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); offset[0] = 0; offset[1] = 1; //{0,1,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); offset[1] = -1; //{0,-1,0} iit.ActivateOffset(offset); mit.ActivateOffset(offset); // iit.ActivateOffset({{0,0,1}}); // iit.ActivateOffset({{0,0,-1}}); // mit.ActivateOffset({{0,0,1}}); // mit.ActivateOffset({{0,0,-1}}); while(!iit.IsAtEnd()) { if(mit.GetCenterPixel() == 0) { typename TImageType::PixelType mean = 0; for (auto i = iit.Begin(); ! i.IsAtEnd(); i++) { mean += i.Get(); } //std::sort(list.begin(),list.end(),[](const typename TImageType::PixelType x,const typename TImageType::PixelType y){return x<=y;}); oit.Set((mean+0.5)/6.0); } else { oit.Set(iit.GetCenterPixel()); } ++iit; ++mit; ++oit; } mitk::CastToMitkImage(itk_outimage,outimage); } template< typename TImageType > void mitk::CLUtil::itkCreateCheckerboardMask(TImageType * image, mitk::Image::Pointer & outimage) { typename TImageType::Pointer zeroimg = TImageType::New(); zeroimg->SetRegions(image->GetLargestPossibleRegion()); zeroimg->SetDirection(image->GetDirection()); zeroimg->SetOrigin(image->GetOrigin()); zeroimg->SetSpacing(image->GetSpacing()); zeroimg->Allocate(); zeroimg->FillBuffer(0); typedef itk::CheckerBoardImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput1(image); filter->SetInput2(zeroimg); typename FilterType::PatternArrayType pattern; pattern.SetElement(0,(image->GetLargestPossibleRegion().GetSize()[0])); pattern.SetElement(1,(image->GetLargestPossibleRegion().GetSize()[1])); pattern.SetElement(2,(image->GetLargestPossibleRegion().GetSize()[2])); filter->SetCheckerPattern(pattern); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(), outimage); } template void mitk::CLUtil::itkSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source , typename TImageType::PixelType label, double & val ) { itk::Image::Pointer itk_source; mitk::CastToItkImage(source,itk_source); itk::ImageRegionConstIterator inputIter(image, image->GetLargestPossibleRegion()); itk::ImageRegionConstIterator< itk::Image > sourceIter(itk_source, itk_source->GetLargestPossibleRegion()); while(!inputIter.IsAtEnd()) { if(inputIter.Value() == label) val += sourceIter.Value(); ++inputIter; ++sourceIter; } } template void mitk::CLUtil::itkSqSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source, typename TImageType::PixelType label, double & val ) { itk::Image::Pointer itk_source; mitk::CastToItkImage(source,itk_source); itk::ImageRegionConstIterator inputIter(image, image->GetLargestPossibleRegion()); itk::ImageRegionConstIterator< itk::Image > sourceIter(itk_source, itk_source->GetLargestPossibleRegion()); while(!inputIter.IsAtEnd()) { if(inputIter.Value() == label) val += sourceIter.Value() * sourceIter.Value(); ++inputIter; ++sourceIter; } } template void mitk::CLUtil::itkFitStructuringElement(TStructuringElement & se, MorphologicalDimensions d, int factor) { typename TStructuringElement::SizeType size; size.Fill(factor); switch(d) { case(All): case(Axial): size.SetElement(2,0); break; case(Sagital): size.SetElement(0,0); break; case(Coronal): size.SetElement(1,0); break; } se.SetRadius(size); se.CreateStructuringElement(); } template void mitk::CLUtil::itkClosingBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement BallType; typedef itk::BinaryMorphologicalClosingImageFilter FilterType; BallType strElem; itkFitStructuringElement(strElem,d,factor); typename FilterType::Pointer erodeFilter = FilterType::New(); erodeFilter->SetKernel(strElem); erodeFilter->SetInput(sourceImage); erodeFilter->SetForegroundValue(1); erodeFilter->Update(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } template void mitk::CLUtil::itkDilateBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement BallType; typedef typename itk::BinaryDilateImageFilter BallDilateFilterType; BallType strElem; itkFitStructuringElement(strElem,d,factor); typename BallDilateFilterType::Pointer erodeFilter = BallDilateFilterType::New(); erodeFilter->SetKernel(strElem); erodeFilter->SetInput(sourceImage); erodeFilter->SetDilateValue(1); erodeFilter->Update(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } template void mitk::CLUtil::itkErodeBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement BallType; typedef typename itk::BinaryErodeImageFilter BallErodeFilterType; BallType strElem; itkFitStructuringElement(strElem,d,factor); typename BallErodeFilterType::Pointer erodeFilter = BallErodeFilterType::New(); erodeFilter->SetKernel(strElem); erodeFilter->SetInput(sourceImage); erodeFilter->SetErodeValue(1); // erodeFilter->UpdateLargestPossibleRegion(); erodeFilter->Update(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } /// /// \brief itkFillHolesBinary /// \param sourceImage /// \param resultImage /// template void mitk::CLUtil::itkFillHolesBinary(itk::Image* sourceImage, mitk::Image::Pointer& resultImage) { typedef itk::Image ImageType; typedef typename itk::BinaryFillholeImageFilter FillHoleFilterType; typename FillHoleFilterType::Pointer fillHoleFilter = FillHoleFilterType::New(); fillHoleFilter->SetInput(sourceImage); fillHoleFilter->SetForegroundValue(1); fillHoleFilter->Update(); mitk::CastToMitkImage(fillHoleFilter->GetOutput(), resultImage); } /// /// \brief itkLogicalAndImages /// \param image1 keep the values of image 1 /// \param image2 /// template void mitk::CLUtil::itkLogicalAndImages(const TImageType * image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage) { typename TImageType::Pointer itk_outimage = TImageType::New(); itk_outimage->SetRegions(image1->GetLargestPossibleRegion()); itk_outimage->SetDirection(image1->GetDirection()); itk_outimage->SetOrigin(image1->GetOrigin()); itk_outimage->SetSpacing(image1->GetSpacing()); itk_outimage->Allocate(); itk_outimage->FillBuffer(0); typename TImageType::Pointer itk_image2; mitk::CastToItkImage(image2,itk_image2); itk::ImageRegionConstIterator it1(image1, image1->GetLargestPossibleRegion()); itk::ImageRegionConstIterator it2(itk_image2, itk_image2->GetLargestPossibleRegion()); itk::ImageRegionIterator oit(itk_outimage,itk_outimage->GetLargestPossibleRegion()); while(!it1.IsAtEnd()) { if(it1.Value() == 0 || it2.Value() == 0) { oit.Set(0); }else oit.Set(it1.Value()); ++it1; ++it2; ++oit; } mitk::CastToMitkImage(itk_outimage, outimage); } /// /// \brief GaussianFilter /// \param image /// \param smoothed /// \param sigma /// template void mitk::CLUtil::itkGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed ,double sigma) { typedef itk::DiscreteGaussianImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(image); filter->SetVariance(sigma); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),smoothed); } template void mitk::CLUtil::itkErodeGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement StructureElementType; typedef itk::GrayscaleErodeImageFilter FilterType; StructureElementType ball; itkFitStructuringElement(ball,d, radius); typename FilterType::Pointer filter = FilterType::New(); filter->SetKernel(ball); filter->SetInput(image); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),outimage); } template void mitk::CLUtil::itkDilateGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d) { typedef itk::BinaryBallStructuringElement StructureElementType; typedef itk::GrayscaleDilateImageFilter FilterType; StructureElementType ball; itkFitStructuringElement(ball,d, radius); typename FilterType::Pointer filter = FilterType::New(); filter->SetKernel(ball); filter->SetInput(image); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),outimage); } template void mitk::CLUtil::itkFillHoleGrayscale(TImageType * image, mitk::Image::Pointer & outimage) { typedef itk::GrayscaleFillholeImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(image); filter->Update(); mitk::CastToMitkImage(filter->GetOutput(),outimage); } #endif diff --git a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp index 303716e61f..9987d1a2f0 100644 --- a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp +++ b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp @@ -1,802 +1,799 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkTestingMacros.h" -#define _USE_MATH_DEFINES #include #include "mitkClippedSurfaceBoundsCalculator.h" #include "mitkGeometry3D.h" #include "mitkNumericTypes.h" #include "mitkPlaneGeometry.h" -#include - static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); // Check planes which are only on one slice: // Slice 0 mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceZero, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); // Slice 3 origin[2] = 3; mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New(); planeOnSliceThree->InitializePlane(origin, normal); planeOnSliceThree->SetImageGeometry(false); calculator->SetInput(planeOnSliceThree, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3"); // Slice 17 origin[2] = 17; mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceSeventeen, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17"); // Slice 20 origin[2] = 19; mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New(); planeOnSliceTwenty->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceTwenty, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19"); delete calculator; } static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); // Check planes which are only on one slice: // Slice 0 mitk::Point3D origin; origin[0] = 510; // Set to 511.9 so that the intersection point is inside the bounding box origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New(); planeSagittalOne->InitializePlane(origin, normal); calculator->SetInput(planeSagittalOne, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); // Slice 3 origin[0] = 256; MITK_INFO << "Case1 origin: " << origin; mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New(); planeSagittalTwo->InitializePlane(origin, normal); MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal(); MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin(); calculator->SetInput(planeSagittalTwo, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); // Slice 17 origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceSeventeen, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); // Crooked planes: origin[0] = 0; origin[1] = 507; origin[2] = 0; normal[0] = 1; normal[1] = -1; normal[2] = 1; mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New(); planeCrookedOne->InitializePlane(origin, normal); calculator->SetInput(planeCrookedOne, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 5, "Check if plane is from slice 0 to slice 5 with inclined plane"); origin[0] = 512; origin[1] = 0; origin[2] = 16; mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New(); planeCrookedTwo->InitializePlane(origin, normal); calculator->SetInput(planeCrookedTwo, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 16 && minMax.second == 19, "Check if plane is from slice 16 to slice 19 with inclined plane"); origin[0] = 510; origin[1] = 0; origin[2] = 0; normal[1] = 0; normal[2] = 0.04; mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New(); planeCrookedThree->InitializePlane(origin, normal); calculator->SetInput(planeCrookedThree, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19 with inclined plane"); delete calculator; } static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are outside of the bounding box mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); // In front of the bounding box mitk::Point3D origin; origin[0] = 510; origin[1] = 0; origin[2] = -5; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New(); planeInFront->InitializePlane(origin, normal); calculator->SetInput(planeInFront, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Behind the bounding box origin[2] = 515; mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New(); planeBehind->InitializePlane(origin, normal); calculator->SetInput(planeBehind, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Above origin[1] = 515; mitk::FillVector3D(normal, 0, 1, 0); mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New(); planeAbove->InitializePlane(origin, normal); calculator->SetInput(planeAbove, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Below origin[1] = -5; mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New(); planeBelow->InitializePlane(origin, normal); calculator->SetInput(planeBelow, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Left side origin[0] = -5; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New(); planeLeftSide->InitializePlane(origin, normal); calculator->SetInput(planeLeftSide, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Right side origin[1] = 515; mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New(); planeRightSide->InitializePlane(origin, normal); calculator->SetInput(planeRightSide, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); delete calculator; } static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D, mitk::BaseGeometry::Pointer secondGeometry3D) { mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer firstImage = mitk::Image::New(); firstImage->Initialize(mitk::MakePixelType(), *(firstGeometry3D.GetPointer())); calculator->SetInput(secondGeometry3D, firstImage); calculator->Update(); auto minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); delete calculator; } static void CheckIntersectionWithPointCloud(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are inside the bounding box mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); { mitk::Point3D pnt1, pnt2; pnt1[0] = 3; pnt1[1] = 5; pnt1[2] = 3; pnt2[0] = 8; pnt2[1] = 3; pnt2[2] = 8; mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back(pnt1); pointlist.push_back(pnt2); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput(pointlist, image); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8, "Check if points span from slice 3 to slice 8 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5, "Check if points span from slice 3 to slice 5 in sagittal"); } { mitk::Point3D pnt1, pnt2; pnt1.Fill(-3); pnt2.Fill(600); mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back(pnt1); pointlist.push_back(pnt2); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput(pointlist, image); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19, "Check if points are correctly clipped to slice 0 and slice 19 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511, "Check if points are correctly clipped to slice 0 and slice 511 in sagittal"); } } static void CheckIntersectionWithRotatedGeometry() { // Define origin for second Geometry3D; mitk::Point3D origin3; origin3[0] = -45.25; origin3[1] = -113.22; origin3[2] = 0.62; // Define normal: mitk::Vector3D normal3; normal3[0] = -0.02; normal3[1] = -0.18; normal3[2] = 2.99; mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 1.43; spacing[2] = 3.0; // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New(); planeGeometry3->InitializePlane(origin3, normal3); planeGeometry3->SetSpacing(spacing); mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds(); moreBounds[0] = 0; moreBounds[1] = 64; moreBounds[2] = 0; moreBounds[3] = 140; moreBounds[4] = 0; moreBounds[5] = 1; planeGeometry3->SetBounds(moreBounds); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New(); rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry3.GetPointer()), 25); rotatedSlicedGeometry3D->SetImageGeometry(true); mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix; matrix[0][0] = 1.43; matrix[0][1] = -0.01; matrix[0][2] = -0.02; matrix[0][0] = 0.01; matrix[1][1] = 1.43; matrix[2][2] = -0.18; matrix[0][0] = 0.01; matrix[1][1] = 0.08; matrix[2][2] = 2.99; geom->SetMatrix(matrix); mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer())); // Check planes which are only on one slice: { // last Slice mitk::Point3D origin; origin[0] = -47.79; origin[1] = 81.41; origin[2] = 84.34; mitk::Vector3D normal; mitk::FillVector3D(normal, 0.02, 0.18, -2.99); mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 1.43; spacing[2] = 3.0; mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); planeOnSliceZero->SetSpacing(spacing); calculator->SetInput(planeOnSliceZero, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 24 && minMax.second == 24, "Check if plane is on slice 24"); } { // Slice 0-24 mitk::Point3D origin; origin[0] = -45; origin[1] = -105; origin[2] = 35; mitk::Vector3D normal; mitk::FillVector3D(normal, 1.43, 0.01, 0.01); mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 3.0; spacing[2] = 1.43; mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); planeOnSliceZero->SetSpacing(spacing); calculator->SetInput(planeOnSliceZero, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first != minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 24, "Check if plane is on slices 0-24"); } delete calculator; } static void CheckIntersectionWithRotatedGeometry90() { // Define origin for second Geometry3D; mitk::Point3D origin3; origin3[0] = 0.0; origin3[1] = 0.0; origin3[2] = 0.0; // Define normal: mitk::Vector3D normal3; normal3[0] = 0; normal3[1] = 0; normal3[2] = 1; mitk::Vector3D spacing; spacing[0] = 1.0; spacing[1] = 2.0; spacing[2] = 1.0; // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New(); planeGeometry3->InitializePlane(origin3, normal3); planeGeometry3->SetSpacing(spacing); mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds(); moreBounds[0] = 0; moreBounds[1] = 50; moreBounds[2] = 0; moreBounds[3] = 50; moreBounds[4] = 0; moreBounds[5] = 1; planeGeometry3->SetBounds(moreBounds); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New(); rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry3.GetPointer()), 50); rotatedSlicedGeometry3D->SetImageGeometry(true); // Set the rotation to zero (identity times spacing matrix) because the default makes a rotation mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix; matrix[0][0] = spacing[0]; matrix[0][1] = 0.0; matrix[0][2] = 0.0; matrix[1][0] = 0.0; matrix[1][1] = spacing[1]; matrix[1][2] = 0.0; matrix[2][0] = 0.0; matrix[2][1] = 0.0; matrix[2][2] = spacing[2]; geom->SetMatrix(matrix); mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer())); // construct the planes in y direction { // first Slice in Y direction mitk::Point3D originFirstSlice; originFirstSlice[0] = 0.0; originFirstSlice[1] = -1.0; originFirstSlice[2] = 0.0; mitk::Vector3D normalFitrstSlice; mitk::FillVector3D(normalFitrstSlice, 0.0, 1.0, 0.0); mitk::Vector3D spacingFirstSlice; spacingFirstSlice[0] = 1.0; spacingFirstSlice[1] = 1.0; spacingFirstSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New(); planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice); planeOnFirstSlice->SetSpacing(spacingFirstSlice); // last Slice in Y Direction mitk::Point3D originLastSlice; originLastSlice[0] = 0.0; originLastSlice[1] = 99.0; originLastSlice[2] = 0.0; mitk::Vector3D normalLastSlice; mitk::FillVector3D(normalLastSlice, 0.0, 1.0, 0.0); mitk::Vector3D spacingLastSlice; spacingLastSlice[0] = 1.0; spacingLastSlice[1] = 1.0; spacingLastSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New(); planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice); planeOnLastSlice->SetSpacing(spacingLastSlice); // calculate the intersection on the last slice calculator->SetInput(planeOnLastSlice, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; // calculate the intersection on the first slice calculator->SetInput(planeOnFirstSlice, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; } // now we make a rotation of the Image about 270 degrees around Z Axis to get an aligment on the positiv x axis - double angleInDegrees = 270 * M_PI / 180; + double angleInDegrees = 270 * itk::Math::pi / 180; mitk::AffineTransform3D::MatrixType matrix2; matrix2[0][0] = cos(angleInDegrees); matrix2[0][1] = -sin(angleInDegrees); matrix2[0][2] = 0.0; matrix2[1][0] = sin(angleInDegrees); matrix2[1][1] = cos(angleInDegrees); matrix2[1][2] = 0.0; matrix2[2][0] = 0.0; matrix2[2][1] = 0.0; matrix2[2][2] = 1.0; // multiplie the identity with the transformation matrix mitk::AffineTransform3D::MatrixType TransformationMatrix = matrix2 * matrix; // initialize the image with the new rotation Matrix geom->SetMatrix(TransformationMatrix); image->Initialize(mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer())); { // first Slice in X Diection mitk::Point3D originFirstSlice; originFirstSlice[0] = -1.0; originFirstSlice[1] = 0.0; originFirstSlice[2] = 0.0; mitk::Vector3D normalFitrstSlice; mitk::FillVector3D(normalFitrstSlice, 1.0, 0.0, 0.0); mitk::Vector3D spacingFirstSlice; spacingFirstSlice[0] = 1.0; spacingFirstSlice[1] = 1.0; spacingFirstSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New(); planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice); planeOnFirstSlice->SetSpacing(spacingFirstSlice); // last Slice in X Direction mitk::Point3D originLastSlice; originLastSlice[0] = 99.0; originLastSlice[1] = 0.0; originLastSlice[2] = 0.0; mitk::Vector3D normalLastSlice; mitk::FillVector3D(normalLastSlice, 1.0, 0.0, 0.0); mitk::Vector3D spacingLastSlice; spacingLastSlice[0] = 1.0; spacingLastSlice[1] = 1.0; spacingLastSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New(); planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice); planeOnLastSlice->SetSpacing(spacingLastSlice); calculator->SetInput(planeOnLastSlice, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; calculator->SetInput(planeOnFirstSlice, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; } delete calculator; } int mitkClippedSurfaceBoundsCalculatorTest(int, char *[]) { // always start with this! MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator"); /** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a * Geometry3D. * This unit test checks if the correct min and max values for the three spatial directions (x, y, z) * are calculated. To test this we define artificial PlaneGeometries and Geometry3Ds and test different * scenarios: * * 1. planes which are inside the bounding box of a 3D geometry but only on one slice * 2. planes which are outside of the bounding box * 3. planes which are inside the bounding box but over more than one slice * * Note: Currently rotated geometries are not tested! */ /********************* Define Geometry3D ***********************/ // Define origin: mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; // Define normal: mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializePlane(origin, normal); // Set Bounds: mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds(); bounds[0] = 0; bounds[1] = 512; bounds[2] = 0; bounds[3] = 512; bounds[4] = 0; bounds[5] = 1; planeGeometry->SetBounds(bounds); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New(); slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry.GetPointer()), 20); mitk::BaseGeometry::Pointer geometry3D = dynamic_cast(slicedGeometry3D.GetPointer()); geometry3D->SetImageGeometry(true); // Define origin for second Geometry3D; mitk::Point3D origin2; origin2[0] = 511; origin2[1] = 60; origin2[2] = 0; // Define normal: mitk::Vector3D normal2; mitk::FillVector3D(normal2, 0, 1, 0); // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New(); planeGeometry2->InitializePlane(origin2, normal2); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New(); secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry2.GetPointer()), 20); mitk::BaseGeometry::Pointer secondGeometry3D = dynamic_cast(secondSlicedGeometry3D.GetPointer()); secondGeometry3D->SetImageGeometry(true); /***************************************************************/ CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D); CheckPlanesOutsideOfBoundingBox(geometry3D); CheckPlanesInsideBoundingBox(geometry3D); CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D); CheckIntersectionWithPointCloud(geometry3D); CheckIntersectionWithRotatedGeometry(); CheckIntersectionWithRotatedGeometry90(); /** ToDo: * test also rotated 3D geometry! */ MITK_TEST_END(); } diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/FittingFunctions/mitkAbstractFitter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/FittingFunctions/mitkAbstractFitter.h index fe50298bf8..2d090a6309 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/FittingFunctions/mitkAbstractFitter.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/FittingFunctions/mitkAbstractFitter.h @@ -1,125 +1,125 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 _MITK_AbstractFitter_H #define _MITK_AbstractFitter_H #include #include #include #include namespace mitk { class AbstractFitter: public vnl_least_squares_function { public: typedef mitk::DiffusionPropertyHelper::GradientDirectionType GradientDirectionType; typedef mitk::DiffusionPropertyHelper::GradientDirectionsContainerType GradientContainerType; typedef itk::VectorImage< unsigned short, 3 > DiffusionImageType; typedef TensorImage::PixelType TensorType; AbstractFitter(unsigned int number_of_parameters, unsigned int number_of_measurements) : vnl_least_squares_function(number_of_parameters, number_of_measurements, no_gradient) { } GradientContainerType::Pointer gradientDirections; DiffusionImageType::PixelType measurements; std::vector bValues; double S0; std::vector weightedIndices; void set_S0(double val) { S0 = val; } void set_measurements(const DiffusionImageType::PixelType& m) { measurements = m; } void set_bvalues(std::vector& x) { bValues = x; } void set_weightedIndices(std::vector& x) { weightedIndices = x; } void set_gradient_directions(const GradientContainerType::Pointer& directions) { gradientDirections = directions; } static void Sph2Cart(vnl_vector_fixed& dir, const double &theta, const double &phi) { dir[0] = std::sin(theta)*std::cos(phi); dir[1] = std::sin(theta)*std::sin(phi); dir[2] = std::cos(theta); } static void Cart2Sph(const vnl_vector_fixed& dir, double &theta, double &phi) { theta = std::acos( dir[2] ); // phi goes from 0.0 (+x axis) and wraps at 2 * PI // theta goes from 0.0 (+z axis) and wraps at PI // if x and y are 0.0 or very close, return phi == 0 if (fabs(dir[0]) + fabs(dir[1]) < 1E-9) { phi = 0.0; } else { // ie, if ( x == 0 && y == 0 ) == false if (dir[1] == 0.0) { if (dir[0] > 0.0) phi = 0.0; else - phi = M_PI; + phi = itk::Math::pi; } else if (dir[0] == 0.0) { // avoid div by zero if (dir[1] > 0) { - phi = M_PI / 2.0; + phi = itk::Math::pi / 2.0; } else { - phi = 1.5 * M_PI; + phi = 1.5 * itk::Math::pi; } } else if (dir[0] > 0.0 && dir[1] > 0.0) // first quadrant phi = atan(dir[1] / dir[0]); else if (dir[0] < 0.0 && dir[1] > 0.0) // second quadrant - phi = M_PI + atan(dir[1] / dir[0]); + phi = itk::Math::pi + atan(dir[1] / dir[0]); else if (dir[0] < 0.0 && dir[1] < 0.0) // third quadrant - phi = M_PI + atan(dir[1] / dir[0]); + phi = itk::Math::pi + atan(dir[1] / dir[0]); else // fourth quadrant - phi = 2.0 * M_PI + atan(dir[1] / dir[0]); + phi = 2.0 * itk::Math::pi + atan(dir[1] / dir[0]); } } }; } #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp index 651c99fe86..d3eb88f170 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp @@ -1,657 +1,655 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkAnalyticalDiffusionQballReconstructionImageFilter_cpp #define __itkAnalyticalDiffusionQballReconstructionImageFilter_cpp #include #include #include #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include #include "itkPointShell.h" namespace itk { template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> AnalyticalDiffusionQballReconstructionImageFilter::AnalyticalDiffusionQballReconstructionImageFilter() : m_GradientDirectionContainer(nullptr), m_NumberOfGradientDirections(0), m_NumberOfBaselineImages(1), m_Threshold(NumericTraits< ReferencePixelType >::NonpositiveMin()), m_BValue(-1), m_Lambda(0.0), m_DirectionsDuplicated(false), m_Delta1(0.001), m_Delta2(0.001), m_UseMrtrixBasis(false) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int ShOrder, int NrOdfDirections> typename itk::AnalyticalDiffusionQballReconstructionImageFilter< TReferenceImagePixelType,TGradientImagePixelType,TOdfPixelType, ShOrder,NrOdfDirections>::OdfPixelType itk::AnalyticalDiffusionQballReconstructionImageFilter::Normalize( OdfPixelType odf, typename NumericTraits::AccumulateType b0 ) { switch( m_NormalizationMethod ) { case QBAR_STANDARD: { TOdfPixelType sum = 0; for(int i=0; i0) odf /= sum; return odf; break; } case QBAR_B_ZERO_B_VALUE: { for(int i=0; i vnl_vector itk::AnalyticalDiffusionQballReconstructionImageFilter::PreNormalize( vnl_vector vec, typename NumericTraits::AccumulateType b0 ) { switch( m_NormalizationMethod ) { case QBAR_STANDARD: { int n = vec.size(); double b0f = (double)b0; for(int i=0; i=1) vec[i] = 1-m_Delta2/2; else if (vec[i]>=1-m_Delta2) vec[i] = 1-m_Delta2/2-(1-vec[i])*(1-vec[i])/(2*m_Delta2); vec[i] = log(-log(vec[i])); } return vec; break; } } return vec; } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter::BeforeThreadedGenerateData() { // If we have more than 2 inputs, then each input, except the first is a // gradient image. The number of gradient images must match the number of // gradient directions. //const unsigned int numberOfInputs = this->GetNumberOfInputs(); // There need to be at least 6 gradient directions to be able to compute the // tensor basis if( m_NumberOfGradientDirections < (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder ) { itkExceptionMacro( << "Not enough gradient directions supplied (" << m_NumberOfGradientDirections << "). At least " << (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder << " needed for SH-order " << ShOrder); } // Input must be an itk::VectorImage. std::string gradientImageClassName( this->ProcessObject::GetInput(0)->GetNameOfClass()); if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) { itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. " << "But its of type: " << gradientImageClassName ); } this->ComputeReconstructionMatrix(); typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); m_BZeroImage = BZeroImageType::New(); m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_BZeroImage->Allocate(); m_ODFSumImage = BZeroImageType::New(); m_ODFSumImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_ODFSumImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_ODFSumImage->SetDirection( img->GetDirection() ); // Set the image direction m_ODFSumImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_ODFSumImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_ODFSumImage->Allocate(); m_CoefficientImage = CoefficientImageType::New(); m_CoefficientImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_CoefficientImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_CoefficientImage->SetDirection( img->GetDirection() ); // Set the image direction m_CoefficientImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_CoefficientImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_CoefficientImage->Allocate(); if(m_NormalizationMethod == QBAR_SOLID_ANGLE || m_NormalizationMethod == QBAR_NONNEG_SOLID_ANGLE) m_Lambda = 0.0; } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetPrimaryOutput()); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); ImageRegionIterator< BZeroImageType > oit2(m_BZeroImage, outputRegionForThread); oit2.GoToBegin(); ImageRegionIterator< FloatImageType > oit3(m_ODFSumImage, outputRegionForThread); oit3.GoToBegin(); ImageRegionIterator< CoefficientImageType > oit4(m_CoefficientImage, outputRegionForThread); oit4.GoToBegin(); typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; typedef typename GradientImagesType::PixelType GradientVectorType; typename GradientImagesType::Pointer gradientImagePointer = nullptr; // Would have liked a dynamic_cast here, but seems SGI doesn't like it // The enum will ensure that an inappropriate cast is not done gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); // Compute the indicies of the baseline images and gradient images std::vector baselineind; // contains the indicies of // the baseline images std::vector gradientind; // contains the indicies of // the gradient images for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { float bval = gdcit.Value().two_norm(); bval = bval*bval*m_BValue; if(bval < 100) baselineind.push_back(gdcit.Index()); else gradientind.push_back(gdcit.Index()); } if( m_DirectionsDuplicated ) { int gradIndSize = gradientind.size(); for(int i=0; i::AccumulateType b0 = NumericTraits::Zero; // Average the baseline image pixels for(unsigned int i = 0; i < baselineind.size(); ++i) { b0 += b[baselineind[i]]; } b0 /= this->m_NumberOfBaselineImages; OdfPixelType odf(0.0); typename CoefficientImageType::PixelType coeffPixel(0.0); vnl_vector B(m_NumberOfGradientDirections); if( (b0 != 0) && (b0 >= m_Threshold) ) { for( unsigned int i = 0; i< m_NumberOfGradientDirections; i++ ) { B[i] = static_cast(b[gradientind[i]]); } B = PreNormalize(B, b0); if(m_NormalizationMethod == QBAR_SOLID_ANGLE) { vnl_vector coeffs(m_NumberCoefficients); coeffs = ( m_CoeffReconstructionMatrix * B ); - coeffs[0] += 1.0/(2.0*sqrt(M_PI)); + coeffs[0] += 1.0/(2.0*sqrt(itk::Math::pi)); odf = ( m_SphericalHarmonicBasisMatrix * coeffs ).data_block(); coeffPixel = coeffs.data_block(); } else if(m_NormalizationMethod == QBAR_NONNEG_SOLID_ANGLE) { /** this would be the place to implement a non-negative * solver for quadratic programming problem: * min .5*|| Bc-s ||^2 subject to -CLPc <= 4*pi*ones * (refer to MICCAI 2009 Goh et al. "Estimating ODFs with PDF constraints") * .5*|| Bc-s ||^2 == .5*c'B'Bc - x'B's + .5*s's */ itkExceptionMacro( << "Nonnegative Solid Angle not yet implemented"); } else { vnl_vector coeffs(m_NumberCoefficients); coeffs = ( m_CoeffReconstructionMatrix * B ); - coeffs[0] += 1.0/(2.0*sqrt(M_PI)); + coeffs[0] += 1.0/(2.0*sqrt(itk::Math::pi)); coeffPixel = coeffs.data_block(); odf = ( m_ReconstructionMatrix * B ).data_block(); } odf = Normalize(odf, b0); } oit.Set( odf ); oit2.Set( b0 ); float sum = 0; for (unsigned int k=0; k void AnalyticalDiffusionQballReconstructionImageFilter ::tofile2(vnl_matrix *pA, std::string fname) { vnl_matrix A = (*pA); std::ofstream myfile; std::locale C("C"); std::locale originalLocale = myfile.getloc(); myfile.imbue(C); myfile.open (fname.c_str()); myfile << "A1=["; for(unsigned int i=0; i void AnalyticalDiffusionQballReconstructionImageFilter::ComputeReconstructionMatrix() { m_NumberCoefficients = (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder; if( m_NumberOfGradientDirections < m_NumberCoefficients ) { itkExceptionMacro( << "Not enough gradient directions supplied (" << m_NumberOfGradientDirections << "). At least " << (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder << " needed for SH-order " << ShOrder); } // Gradient preprocessing { // check for duplicate diffusion gradients bool warning = false; for(GradientDirectionContainerType::ConstIterator gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { for(GradientDirectionContainerType::ConstIterator gdcit2 = this->m_GradientDirectionContainer->Begin(); gdcit2 != this->m_GradientDirectionContainer->End(); ++gdcit2) { if(gdcit1.Value() == gdcit2.Value() && gdcit1.Index() != gdcit2.Index()) { itkWarningMacro( << "Some of the Diffusion Gradients equal each other. Corresponding image data should be averaged before calling this filter." ); warning = true; break; } } if (warning) break; } // handle acquisition schemes where only half of the spherical // shell is sampled by the gradient directions. In this case, // each gradient direction is duplicated in negative direction. vnl_vector centerMass(3); centerMass.fill(0.0); int count = 0; for(GradientDirectionContainerType::ConstIterator gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { float bval = gdcit1.Value().two_norm(); bval = bval*bval*m_BValue; if(bval > 100) { centerMass += gdcit1.Value(); count ++; } } centerMass /= count; if(centerMass.two_norm() > 0.1) { m_DirectionsDuplicated = true; m_NumberOfGradientDirections *= 2; } } // Create 3xM matrix Q that contains the gradient vectors in polar coordinates vnl_matrix Q(3, m_NumberOfGradientDirections); { int i = 0; for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { float bval = gdcit.Value().two_norm(); bval = bval*bval*m_BValue; if(bval > 100) { double x = gdcit.Value().get(0); double y = gdcit.Value().get(1); double z = gdcit.Value().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i++) = cart[2]; } } if(m_DirectionsDuplicated) { for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { float bval = gdcit.Value().two_norm(); bval = bval*bval*m_BValue; if(bval > 100) { double x = gdcit.Value().get(0); double y = gdcit.Value().get(1); double z = gdcit.Value().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i++) = cart[2]; } } } } // Calcualte SH basis B vnl_matrix L(m_NumberCoefficients,m_NumberCoefficients, 0); vnl_vector lj(m_NumberCoefficients); vnl_matrix B(m_NumberOfGradientDirections,m_NumberCoefficients); for(unsigned int i=0; i P(m_NumberCoefficients,m_NumberCoefficients, 0); for(unsigned int i=0; i B_transpose(B.transpose()); vnl_matrix_inverse* pseudoInverse = new vnl_matrix_inverse( B_transpose * B + L*L*m_Lambda ); m_CoeffReconstructionMatrix = pseudoInverse->pinverse() * B_transpose; switch(m_NormalizationMethod) { case QBAR_ADC_ONLY: case QBAR_RAW_SIGNAL: break; case QBAR_STANDARD: case QBAR_B_ZERO_B_VALUE: case QBAR_B_ZERO: case QBAR_NONE: { m_CoeffReconstructionMatrix = P * m_CoeffReconstructionMatrix; break; } case QBAR_SOLID_ANGLE: { - m_CoeffReconstructionMatrix = (float)(1.0/(8.0*M_PI)) * P * L * m_CoeffReconstructionMatrix; + m_CoeffReconstructionMatrix = (float)(1.0/(8.0*itk::Math::pi)) * P * L * m_CoeffReconstructionMatrix; break; } case QBAR_NONNEG_SOLID_ANGLE: break; } // needed to calculate the ODF values from the SH coefficients vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); m_SphericalHarmonicBasisMatrix = mitk::sh::CalcShBasisForDirections(ShOrder, U->as_matrix()); m_ReconstructionMatrix = m_SphericalHarmonicBasisMatrix * m_CoeffReconstructionMatrix; } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter ::SetGradientImage(const GradientDirectionContainerType *gradientDirection, const GradientImagesType *gradientImage ) { // Copy Gradient Direction Container this->m_GradientDirectionContainer = GradientDirectionContainerType::New(); for(GradientDirectionContainerType::ConstIterator it = gradientDirection->Begin(); it != gradientDirection->End(); it++) { this->m_GradientDirectionContainer->push_back(it.Value()); } if (m_BValue<0) itkExceptionMacro("B-value needs to best before gradient image!"); unsigned int numImages = gradientDirection->Size(); this->m_NumberOfBaselineImages = 0; for(GradientDirectionContainerType::Iterator it = this->m_GradientDirectionContainer->Begin(); it != this->m_GradientDirectionContainer->End(); it++) { float bval = it.Value().two_norm(); bval = bval*bval*m_BValue; if(bval < 100) { this->m_NumberOfBaselineImages++; } else // Normalize non-zero gradient directions { it.Value() = it.Value() / it.Value().two_norm(); } } if (this->m_NumberOfBaselineImages==0) itkExceptionMacro("No baseline image detected (no b-zero image)"); this->m_NumberOfGradientDirections = numImages - this->m_NumberOfBaselineImages; // ensure that the gradient image we received has as many components as // the number of gradient directions if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + m_NumberOfGradientDirections ) { itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages << "baselines = " << m_NumberOfGradientDirections + this->m_NumberOfBaselineImages << " directions specified but image has " << gradientImage->GetVectorLength() << " components."); } this->ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { std::locale C("C"); std::locale originalLocale = os.getloc(); os.imbue(C); Superclass::PrintSelf(os,indent); os << indent << "OdfReconstructionMatrix: " << m_ReconstructionMatrix << std::endl; if ( m_GradientDirectionContainer ) os << indent << "GradientDirectionContainer: " << m_GradientDirectionContainer << std::endl; else os << indent << "GradientDirectionContainer: (Gradient directions not set)" << std::endl; os << indent << "NumberOfGradientDirections: " << m_NumberOfGradientDirections << std::endl; os << indent << "NumberOfBaselineImages: " << m_NumberOfBaselineImages << std::endl; os << indent << "Threshold for reference B0 image: " << m_Threshold << std::endl; os << indent << "BValue: " << m_BValue << std::endl; os.imbue( originalLocale ); } } #endif // __itkAnalyticalDiffusionQballReconstructionImageFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp index 96cfad8def..65cd6c572d 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp @@ -1,1262 +1,1262 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkDiffusionMultiShellQballReconstructionImageFilter_cpp #define __itkDiffusionMultiShellQballReconstructionImageFilter_cpp #include #include #include #include namespace itk { template< class T, class TG, class TO, int L, int NODF> DiffusionMultiShellQballReconstructionImageFilter ::DiffusionMultiShellQballReconstructionImageFilter() : m_ReconstructionType(Mode_Standard1Shell), m_Interpolation_Flag(false), m_Interpolation_SHT1_inv(nullptr), m_Interpolation_SHT2_inv(nullptr), m_Interpolation_SHT3_inv(nullptr), m_TARGET_SH_shell1(nullptr), m_TARGET_SH_shell2(nullptr), m_TARGET_SH_shell3(nullptr), m_MaxDirections(0), m_CoeffReconstructionMatrix(nullptr), m_ODFSphericalHarmonicBasisMatrix(nullptr), m_GradientDirectionContainer(nullptr), m_NumberOfGradientDirections(0), m_NumberOfBaselineImages(0), m_Threshold(0), m_BZeroImage(nullptr), m_CoefficientImage(nullptr), m_BValue(1.0), m_Lambda(0.0), m_IsHemisphericalArrangementOfGradientDirections(false), m_IsArithmeticProgession(false) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::SetGradientImage(const GradientDirectionContainerType *gradientDirection , const GradientImagesType *gradientImage , float bvalue) { m_BValue = bvalue; m_NumberOfBaselineImages = 0; this->m_GradientDirectionContainer = GradientDirectionContainerType::New(); for(GradientDirectionContainerType::ConstIterator it = gradientDirection->Begin(); it != gradientDirection->End(); it++) { this->m_GradientDirectionContainer->push_back(it.Value()); } if(m_BValueMap.size() == 0){ itkWarningMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : no GradientIndexMapAvalible"); GradientDirectionContainerType::ConstIterator gdcit; for( gdcit = m_GradientDirectionContainer->Begin(); gdcit != m_GradientDirectionContainer->End(); ++gdcit) { double bValueKey = int(((m_BValue * gdcit.Value().two_norm() * gdcit.Value().two_norm())+7.5)/10)*10; MITK_INFO << bValueKey; m_BValueMap[bValueKey].push_back(gdcit.Index()); } } if(m_BValueMap.find(0) == m_BValueMap.end()) { itkExceptionMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : GradientIndxMap with no b-Zero indecies found: check input BValueMap"); } m_NumberOfBaselineImages = m_BValueMap[0].size(); m_NumberOfGradientDirections = gradientDirection->Size() - m_NumberOfBaselineImages; // ensure that the gradient image we received has as many components as // the number of gradient directions if( gradientImage->GetVectorLength() != m_NumberOfBaselineImages + m_NumberOfGradientDirections ) { itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << m_NumberOfBaselineImages << "baselines = " << m_NumberOfGradientDirections + m_NumberOfBaselineImages << " directions specified but image has " << gradientImage->GetVectorLength() << " components."); } ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); std::string gradientImageClassName(ProcessObject::GetInput(0)->GetNameOfClass()); if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. But its of type: " << gradientImageClassName ); m_BZeroImage = BZeroImageType::New(); typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( ProcessObject::GetInput(0) ); m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_BZeroImage->Allocate(); m_CoefficientImage = CoefficientImageType::New(); m_CoefficientImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_CoefficientImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_CoefficientImage->SetDirection( img->GetDirection() ); // Set the image direction m_CoefficientImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_CoefficientImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_CoefficientImage->Allocate(); } template void DiffusionMultiShellQballReconstructionImageFilter ::Normalize( OdfPixelType & out) { for(int i=0; i void DiffusionMultiShellQballReconstructionImageFilter ::Projection1(vnl_vector & vec, double delta) { if (delta==0){ //Clip attenuation values. If att<0 => att=0, if att>1 => att=1 for (unsigned int i=0; i=0 && vec[i]<=1)*vec[i]+(vec[i]>1); } else{ //Use function from Aganj et al, MRM, 2010 for (unsigned int i=0; i< vec.size(); i++) vec[i]=CalculateThreashold(vec[i], delta); } } template double DiffusionMultiShellQballReconstructionImageFilter ::CalculateThreashold(const double value, const double delta) { return (value<0)*(0.5*delta) + (value>=0 && value=delta && value<1-delta)*value+(value>=1-delta && value<1)*(1-0.5*delta-0.5*((1-value)*(1-value))/delta) + (value>=1)*(1-0.5*delta); } template void DiffusionMultiShellQballReconstructionImageFilter ::Projection2( vnl_vector & E1,vnl_vector & E2, vnl_vector & E3, double delta ) { const double sF = sqrt(5.0); vnl_vector vOnes(m_MaxDirections); vOnes.fill(1.0); vnl_matrix T0(m_MaxDirections, 3); vnl_matrix C(m_MaxDirections, 7); vnl_matrix A(m_MaxDirections, 7); vnl_matrix B(m_MaxDirections, 7); vnl_vector s0(m_MaxDirections); vnl_vector a0(m_MaxDirections); vnl_vector b0(m_MaxDirections); vnl_vector ta(m_MaxDirections); vnl_vector tb(m_MaxDirections); vnl_vector e(m_MaxDirections); vnl_vector m(m_MaxDirections); vnl_vector a(m_MaxDirections); vnl_vector b(m_MaxDirections); // logarithmierung aller werte in E for(unsigned int i = 0 ; i < m_MaxDirections; i++) { T0(i,0) = -log(E1(i)); T0(i,1) = -log(E2(i)); T0(i,2) = -log(E3(i)); } //T0 = -T0.apply(std::log); // Summeiere Zeilenweise über alle Shells sum = E1+E2+E3 for(unsigned int i = 0 ; i < m_MaxDirections; i++) { s0[i] = T0(i,0) + T0(i,1) + T0(i,2); } for(unsigned int i = 0; i < m_MaxDirections; i ++) { // Alle Signal-Werte auf der Ersten shell E(N,0) normiert auf s0 a0[i] = T0(i,0) / s0[i]; // Alle Signal-Werte auf der Zweiten shell E(N,1) normiert auf s0 b0[i] = T0(i,1) / s0[i]; } ta = a0 * 3.0; tb = b0 * 3.0; e = tb - (ta * 2.0); m = (tb * 2.0 ) + ta; for(unsigned int i = 0; i =1-3*(sF+2)*delta); C(i,2) = (m[i] > 3-3*sF*delta) && (-1+3*(2*sF+5)*delta= 3-3*sF*delta && e[i] >= -3 *sF * delta); C(i,4) = (2.5 + 1.5*(5+sF)*delta < m[i] && m[i] < 3-3*sF*delta && e[i] > -3*sF*delta); C(i,5) = (ta[i] <= 0.5+1.5 *(sF+1)*delta && m[i] <= 2.5 + 1.5 *(5+sF) * delta); C(i,6) = !((bool) C(i,0) ||(bool) C(i,1) ||(bool) C(i,2) ||(bool) C(i,3) ||(bool) C(i,4) ||(bool) C(i,5) ); // ~ANY(C(i,[0-5] ),2) A(i,0)=(bool)C(i,0) * a0(i); A(i,1)=(bool)C(i,1) * (1.0/3.0-(sF+2)*delta); A(i,2)=(bool)C(i,2) * (0.2+0.8*a0(i)-0.4*b0(i)-delta/sF); A(i,3)=(bool)C(i,3) * (0.2+delta/sF); A(i,4)=(bool)C(i,4) * (0.2*a0(i)+0.4*b0(i)+2*delta/sF); A(i,5)=(bool)C(i,5) * (1.0/6.0+0.5*(sF+1)*delta); A(i,6)=(bool)C(i,6) * a0(i); B(i,0)=(bool)C(i,0) * (1.0/3.0+delta); B(i,1)=(bool)C(i,1) * (1.0/3.0+delta); B(i,2)=(bool)C(i,2) * (0.4-0.4*a0(i)+0.2*b0(i)-2*delta/sF); B(i,3)=(bool)C(i,3) * (0.4-3*delta/sF); B(i,4)=(bool)C(i,4) * (0.4*a0(i)+0.8*b0(i)-delta/sF); B(i,5)=(bool)C(i,5) * (1.0/3.0+delta); B(i,6)=(bool)C(i,6) * b0(i); } for(unsigned int i = 0 ; i < m_MaxDirections; i++) { double sumA = 0; double sumB = 0; for(int j = 0 ; j < 7; j++) { sumA += A(i,j); sumB += B(i,j); } a[i] = sumA; b[i] = sumB; } for(unsigned int i = 0; i < m_MaxDirections; i++) { E1(i) = exp(-(a[i]*s0[i])); E2(i) = exp(-(b[i]*s0[i])); E3(i) = exp(-((1-a[i]-b[i])*s0[i])); } } template void DiffusionMultiShellQballReconstructionImageFilter ::Projection3( vnl_vector & A, vnl_vector & a, vnl_vector & b, double delta0) { const double s6 = sqrt(6.0); const double s15 = s6/2.0; vnl_vector delta(a.size()); delta.fill(delta0); vnl_matrix AM(a.size(), 15); vnl_matrix aM(a.size(), 15); vnl_matrix bM(a.size(), 15); vnl_matrix B(a.size(), 15); AM.set_column(0, A); AM.set_column(1, A); AM.set_column(2, A); AM.set_column(3, delta); AM.set_column(4, (A+a-b - (delta*s6))/3.0); AM.set_column(5, delta); AM.set_column(6, delta); AM.set_column(7, delta); AM.set_column(8, A); AM.set_column(9, 0.2*(a*2+A-2*(s6+1)*delta)); AM.set_column(10,0.2*(b*(-2)+A+2-2*(s6+1)*delta)); AM.set_column(11, delta); AM.set_column(12, delta); AM.set_column(13, delta); AM.set_column(14, 0.5-(1+s15)*delta); aM.set_column(0, a); aM.set_column(1, a); aM.set_column(2, -delta + 1); aM.set_column(3, a); aM.set_column(4, (A*2+a*5+b+s6*delta)/6.0); aM.set_column(5, a); aM.set_column(6, -delta + 1); aM.set_column(7, 0.5*(a+b)+(1+s15)*delta); aM.set_column(8, -delta + 1); aM.set_column(9, 0.2*(a*4+A*2+(s6+1)*delta)); aM.set_column(10, -delta + 1); aM.set_column(11, (s6+3)*delta); aM.set_column(12, -delta + 1); aM.set_column(13, -delta + 1); aM.set_column(14, -delta + 1); bM.set_column(0, b); bM.set_column(1, delta); bM.set_column(2, b); bM.set_column(3, b); bM.set_column(4, (A*(-2)+a+b*5-s6*delta)/6.0); bM.set_column(5, delta); bM.set_column(6, b); bM.set_column(7, 0.5*(a+b)-(1+s15)*delta); bM.set_column(8, delta); bM.set_column(9, delta); bM.set_column(10, 0.2*(b*4-A*2+1-(s6+1)*delta)); bM.set_column(11, delta); bM.set_column(12, delta); bM.set_column(13, -delta*(s6+3) + 1); bM.set_column(14, delta); delta0 *= 0.99; vnl_matrix R2(a.size(), 15); std::vector I(a.size()); for (unsigned int i=0; idelta0 && aM(i,j)<1-delta0) R2(i,j) = (AM(i,j)-A(i))*(AM(i,j)-A(i))+ (aM(i,j)-a(i))*(aM(i,j)-a(i))+(bM(i,j)-b(i))*(bM(i,j)-b(i)); else R2(i,j) = 1e20; } unsigned int index = 0; double minvalue = 999; for(int j = 0 ; j < 15 ; j++) { if(R2(i,j) < minvalue){ minvalue = R2(i,j); index = j; } } I[i] = index; } for (unsigned int i=0; i < A.size(); i++){ A(i) = AM(i,(int)I[i]); a(i) = aM(i,(int)I[i]); b(i) = bM(i,(int)I[i]); } } template void DiffusionMultiShellQballReconstructionImageFilter ::S_S0Normalization( vnl_vector & vec, double S0 ) { for(unsigned int i = 0; i < vec.size(); i++) { if (S0==0) S0 = 0.01; vec[i] /= S0; } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::DoubleLogarithm(vnl_vector & vec) { for(unsigned int i = 0; i < vec.size(); i++) { vec[i] = log(-log(vec[i])); } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::BeforeThreadedGenerateData() { m_ReconstructionType = Mode_Standard1Shell; if(m_BValueMap.size() == 4 ){ BValueMapIteraotr it = m_BValueMap.begin(); it++; // skip b0 entry const unsigned int bValue_shell1 = it->first; const unsigned int size_shell1 = it->second.size(); IndiciesVector shell1 = it->second; it++; const unsigned int bValue_shell2 = it->first; const unsigned int size_shell2 = it->second.size(); IndiciesVector shell2 = it->second; it++; const unsigned int bValue_shell3 = it->first; const unsigned int size_shell3 = it->second.size(); IndiciesVector shell3 = it->second; // arithmetic progrssion if(bValue_shell2 - bValue_shell1 == bValue_shell1 && bValue_shell3 - bValue_shell2 == bValue_shell1 ) { // check if Interpolation is needed // if shells with different numbers of directions exist m_Interpolation_Flag = false; if(size_shell1 != size_shell2 || size_shell2 != size_shell3 || size_shell1 != size_shell3) { m_Interpolation_Flag = true; MITK_INFO << "Shell interpolation: shells with different numbers of directions"; } else { // else if each shell holds same numbers of directions, but the gradient direction differ more than one 1 degree m_Interpolation_Flag = CheckForDifferingShellDirections(); if(m_Interpolation_Flag) MITK_INFO << "Shell interpolation: gradient direction differ more than one 1 degree"; } m_ReconstructionType = Mode_Analytical3Shells; if(m_Interpolation_Flag) { unsigned int interp_SHOrder_shell1 = 12; while( ((interp_SHOrder_shell1+1)*(interp_SHOrder_shell1+2)/2) > size_shell1 && interp_SHOrder_shell1 > L ) interp_SHOrder_shell1 -= 2 ; const int number_coeffs_shell1 = (int)(interp_SHOrder_shell1*interp_SHOrder_shell1 + interp_SHOrder_shell1 + 2.0)/2.0 + interp_SHOrder_shell1; unsigned int interp_SHOrder_shell2 = 12; while( ((interp_SHOrder_shell2+1)*(interp_SHOrder_shell2+2)/2) > size_shell2 && interp_SHOrder_shell2 > L ) interp_SHOrder_shell2 -= 2 ; const int number_coeffs_shell2 = (int)(interp_SHOrder_shell2*interp_SHOrder_shell2 + interp_SHOrder_shell2 + 2.0)/2.0 + interp_SHOrder_shell2; unsigned int interp_SHOrder_shell3 = 12; while( ((interp_SHOrder_shell3+1)*(interp_SHOrder_shell3+2)/2) > size_shell3 && interp_SHOrder_shell3 > L ) interp_SHOrder_shell3 -= 2 ; const int number_coeffs_shell3 = (int)(interp_SHOrder_shell3*interp_SHOrder_shell3 + interp_SHOrder_shell3 + 2.0)/2.0 + interp_SHOrder_shell3; // Create direction container for all directions (no duplicates, different directions from all shells) IndiciesVector all_directions_container = GetAllDirections(); m_MaxDirections = all_directions_container.size(); // create target SH-Basis // initialize empty target matrix and set the wanted directions vnl_matrix * Q = new vnl_matrix(3, m_MaxDirections); ComputeSphericalFromCartesian(Q, all_directions_container); // initialize a SH Basis, neede to interpolate from oldDirs -> newDirs m_TARGET_SH_shell1 = new vnl_matrix(m_MaxDirections, number_coeffs_shell1); ComputeSphericalHarmonicsBasis(Q, m_TARGET_SH_shell1, interp_SHOrder_shell1); delete Q; Q = new vnl_matrix(3, m_MaxDirections); ComputeSphericalFromCartesian(Q, all_directions_container); m_TARGET_SH_shell2 = new vnl_matrix(m_MaxDirections, number_coeffs_shell2); ComputeSphericalHarmonicsBasis(Q, m_TARGET_SH_shell2, interp_SHOrder_shell2); delete Q; Q = new vnl_matrix(3, m_MaxDirections); ComputeSphericalFromCartesian(Q, all_directions_container); m_TARGET_SH_shell3 = new vnl_matrix(m_MaxDirections, number_coeffs_shell3); ComputeSphericalHarmonicsBasis(Q, m_TARGET_SH_shell3, interp_SHOrder_shell3); delete Q; // end creat target SH-Basis // create measured-SHBasis // Shell 1 vnl_matrix * tempSHBasis; vnl_matrix_inverse * temp; // initialize empty matrix and set the measured directions of shell1 Q = new vnl_matrix(3, shell1.size()); ComputeSphericalFromCartesian(Q, shell1); // initialize a SH Basis, need to get the coeffs from measuredShell tempSHBasis = new vnl_matrix(shell1.size(), number_coeffs_shell1); ComputeSphericalHarmonicsBasis(Q, tempSHBasis, interp_SHOrder_shell1); // inversion of the SH=Basis // c_s1 = B^-1 * shell1 // interp_Values = targetSHBasis * c_s1 // Values = m_TARGET_SH_shell1 * Interpolation_SHT1_inv * DataShell1; temp = new vnl_matrix_inverse((*tempSHBasis)); m_Interpolation_SHT1_inv = new vnl_matrix(temp->inverse()); delete Q; delete temp; delete tempSHBasis; // Shell 2 Q = new vnl_matrix(3, shell2.size()); ComputeSphericalFromCartesian(Q, shell2); tempSHBasis = new vnl_matrix(shell2.size(), number_coeffs_shell2); ComputeSphericalHarmonicsBasis(Q, tempSHBasis, interp_SHOrder_shell2); temp = new vnl_matrix_inverse((*tempSHBasis)); m_Interpolation_SHT2_inv = new vnl_matrix(temp->inverse()); delete Q; delete temp; delete tempSHBasis; // Shell 3 Q = new vnl_matrix(3, shell3.size()); ComputeSphericalFromCartesian(Q, shell3); tempSHBasis = new vnl_matrix(shell3.size(), number_coeffs_shell3); ComputeSphericalHarmonicsBasis(Q, tempSHBasis, interp_SHOrder_shell3); temp = new vnl_matrix_inverse((*tempSHBasis)); m_Interpolation_SHT3_inv = new vnl_matrix(temp->inverse()); delete Q; delete temp; delete tempSHBasis; ComputeReconstructionMatrix(all_directions_container); MITK_INFO << "Reconstruction information: Multishell Reconstruction filter - Interpolation"; MITK_INFO << "Shell 1"; MITK_INFO << " SHOrder: " << interp_SHOrder_shell1; MITK_INFO << " Number of Coeffs: " << number_coeffs_shell1; MITK_INFO << " Number of Gradientdirections: " << size_shell1; MITK_INFO << "Shell 2"; MITK_INFO << " SHOrder: " << interp_SHOrder_shell2; MITK_INFO << " Number of Coeffs: " << number_coeffs_shell2; MITK_INFO << " Number of Gradientdirections: " << size_shell2; MITK_INFO << "Shell 3"; MITK_INFO << " SHOrder: " << interp_SHOrder_shell3; MITK_INFO << " Number of Coeffs: " << number_coeffs_shell3; MITK_INFO << " Number of Gradientdirections: " << size_shell3; MITK_INFO << "Overall"; MITK_INFO << " SHOrder: " << L; MITK_INFO << " Number of Coeffs: " << (L+1)*(L+2)*0.5; MITK_INFO << " Number of Gradientdirections: " << m_MaxDirections; return; }else { ComputeReconstructionMatrix(shell1); } } } if(m_BValueMap.size() > 2 && m_ReconstructionType != Mode_Analytical3Shells) { m_ReconstructionType = Mode_NumericalNShells; } if(m_BValueMap.size() == 2){ BValueMapIteraotr it = m_BValueMap.begin(); it++; // skip b0 entry IndiciesVector shell = it->second; ComputeReconstructionMatrix(shell); } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType /*NumberOfThreads*/) { itk::TimeProbe clock; clock.Start(); switch(m_ReconstructionType) { case Mode_Standard1Shell: StandardOneShellReconstruction(outputRegionForThread); break; case Mode_Analytical3Shells: AnalyticalThreeShellReconstruction(outputRegionForThread); break; case Mode_NumericalNShells: break; } clock.Stop(); MITK_INFO << "Reconstruction in : " << clock.GetTotal() << " s"; } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::StandardOneShellReconstruction(const OutputImageRegionType& outputRegionForThread) { // Get output image pointer typename OdfImageType::Pointer outputImage = static_cast< OdfImageType * >(ProcessObject::GetPrimaryOutput()); // Get input gradient image pointer typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( ProcessObject::GetInput(0) ); // ImageRegionIterator for the output image ImageRegionIterator< OdfImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); // ImageRegionIterator for the BZero (output) image ImageRegionIterator< BZeroImageType > bzeroIterator(m_BZeroImage, outputRegionForThread); bzeroIterator.GoToBegin(); // Const ImageRegionIterator for input gradient image typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); BValueMapIteraotr it = m_BValueMap.begin(); it++; // skip b0 entry IndiciesVector SignalIndicies = it->second; IndiciesVector BZeroIndicies = m_BValueMap[0]; unsigned int NumbersOfGradientIndicies = SignalIndicies.size(); typedef typename GradientImagesType::PixelType GradientVectorType; // iterate overall voxels of the gradient image region while( ! git.IsAtEnd() ) { GradientVectorType b = git.Get(); // ODF Vector OdfPixelType odf(0.0); double b0average = 0; const unsigned int b0size = BZeroIndicies.size(); for(unsigned int i = 0; i < b0size ; ++i) { b0average += b[BZeroIndicies[i]]; } b0average /= b0size; bzeroIterator.Set(b0average); ++bzeroIterator; // Create the Signal Vector vnl_vector SignalVector(NumbersOfGradientIndicies); if( (b0average != 0) && (b0average >= m_Threshold) ) { for( unsigned int i = 0; i< SignalIndicies.size(); i++ ) { SignalVector[i] = static_cast(b[SignalIndicies[i]]); } // apply threashold an generate ln(-ln(E)) signal // Replace SignalVector with PreNormalized SignalVector S_S0Normalization(SignalVector, b0average); Projection1(SignalVector); DoubleLogarithm(SignalVector); // approximate ODF coeffs vnl_vector coeffs = ( (*m_CoeffReconstructionMatrix) * SignalVector ); - coeffs[0] = 1.0/(2.0*sqrt(M_PI)); + coeffs[0] = 1.0/(2.0*sqrt(itk::Math::pi)); odf = element_cast(( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs )).data_block(); - odf *= (M_PI*4/NODF); + odf *= (itk::Math::pi*4/NODF); } // set ODF to ODF-Image oit.Set( odf ); ++oit; ++git; } MITK_INFO << "One Thread finished reconstruction"; } //#include "itkLevenbergMarquardtOptimizer.h" template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::NumericalNShellReconstruction(const OutputImageRegionType& /*outputRegionForThread*/) { /* itk::LevenbergMarquardtOptimizer::Pointer optimizer = itk::LevenbergMarquardtOptimizer::New(); optimizer->SetUseCostFunctionGradient(false); // Scale the translation components of the Transform in the Optimizer itk::LevenbergMarquardtOptimizer::ScalesType scales(transform->GetNumberOfParameters()); scales.Fill(0.01); unsigned long numberOfIterations = 80000; double gradientTolerance = 1e-10; // convergence criterion double valueTolerance = 1e-10; // convergence criterion double epsilonFunction = 1e-10; // convergence criterion optimizer->SetScales( scales ); optimizer->SetNumberOfIterations( numberOfIterations ); optimizer->SetValueTolerance( valueTolerance ); optimizer->SetGradientTolerance( gradientTolerance ); optimizer->SetEpsilonFunction( epsilonFunction );*/ } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::AnalyticalThreeShellReconstruction(const OutputImageRegionType& outputRegionForThread) { // Input Gradient Image and Output ODF Image typedef typename GradientImagesType::PixelType GradientVectorType; typename OdfImageType::Pointer outputImage = static_cast< OdfImageType * >(ProcessObject::GetPrimaryOutput()); typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( ProcessObject::GetInput(0) ); // Define Image iterators ImageRegionIterator< OdfImageType > odfOutputImageIterator(outputImage, outputRegionForThread); ImageRegionConstIterator< GradientImagesType > gradientInputImageIterator(gradientImagePointer, outputRegionForThread ); ImageRegionIterator< BZeroImageType > bzeroIterator(m_BZeroImage, outputRegionForThread); ImageRegionIterator< CoefficientImageType > coefficientImageIterator(m_CoefficientImage, outputRegionForThread); // All iterators seht to Begin of the specific OutputRegion coefficientImageIterator.GoToBegin(); bzeroIterator.GoToBegin(); odfOutputImageIterator.GoToBegin(); gradientInputImageIterator.GoToBegin(); // Get Shell Indicies for all non-BZero Gradients // it MUST be a arithmetic progression eg.: 1000, 2000, 3000 BValueMapIteraotr it = m_BValueMap.begin(); it++; // it = b-value = 1000 IndiciesVector Shell1Indiecies = it->second; it++; // it = b-value = 2000 IndiciesVector Shell2Indiecies = it->second; it++; // it = b-value = 3000 IndiciesVector Shell3Indiecies = it->second; IndiciesVector BZeroIndicies = m_BValueMap[0]; if(!m_Interpolation_Flag) { m_MaxDirections = Shell1Indiecies.size(); }// else: m_MaxDirection is set in BeforeThreadedGenerateData // Nx3 Signal Matrix with E(0) = Shell 1, E(1) = Shell 2, E(2) = Shell 3 vnl_vector< double > E1(m_MaxDirections); vnl_vector< double > E2(m_MaxDirections); vnl_vector< double > E3(m_MaxDirections); vnl_vector AlphaValues(m_MaxDirections); vnl_vector BetaValues(m_MaxDirections); vnl_vector LAValues(m_MaxDirections); vnl_vector PValues(m_MaxDirections); vnl_vector DataShell1(Shell1Indiecies.size()); vnl_vector DataShell2(Shell2Indiecies.size()); vnl_vector DataShell3(Shell3Indiecies.size()); vnl_matrix tempInterpolationMatrixShell1,tempInterpolationMatrixShell2,tempInterpolationMatrixShell3; if(m_Interpolation_Flag) { tempInterpolationMatrixShell1 = (*m_TARGET_SH_shell1) * (*m_Interpolation_SHT1_inv); tempInterpolationMatrixShell2 = (*m_TARGET_SH_shell2) * (*m_Interpolation_SHT2_inv); tempInterpolationMatrixShell3 = (*m_TARGET_SH_shell3) * (*m_Interpolation_SHT3_inv); } OdfPixelType odf(0.0); typename CoefficientImageType::PixelType coeffPixel(0.0); double P2,A,B2,B,P,alpha,beta,lambda, ER1, ER2; // iterate overall voxels of the gradient image region while( ! gradientInputImageIterator.IsAtEnd() ) { odf = 0.0; coeffPixel = 0.0; GradientVectorType b = gradientInputImageIterator.Get(); // calculate for each shell the corresponding b0-averages double shell1b0Norm =0; double shell2b0Norm =0; double shell3b0Norm =0; double b0average = 0; const unsigned int b0size = BZeroIndicies.size(); if(b0size == 1) { shell1b0Norm = b[BZeroIndicies[0]]; shell2b0Norm = b[BZeroIndicies[0]]; shell3b0Norm = b[BZeroIndicies[0]]; b0average = b[BZeroIndicies[0]]; }else if(b0size % 3 ==0) { for(unsigned int i = 0; i < b0size ; ++i) { if(i < b0size / 3) shell1b0Norm += b[BZeroIndicies[i]]; if(i >= b0size / 3 && i < (b0size / 3)*2) shell2b0Norm += b[BZeroIndicies[i]]; if(i >= (b0size / 3) * 2) shell3b0Norm += b[BZeroIndicies[i]]; } shell1b0Norm /= (b0size/3); shell2b0Norm /= (b0size/3); shell3b0Norm /= (b0size/3); b0average = (shell1b0Norm + shell2b0Norm+ shell3b0Norm)/3; }else { for(unsigned int i = 0; i = m_Threshold) ) { // Get the Signal-Value for each Shell at each direction (specified in the ShellIndicies Vector .. this direction corresponse to this shell...) /*//fsl fix --------------------------------------------------- for(int i = 0 ; i < Shell1Indiecies.size(); i++) DataShell1[i] = static_cast(b[Shell1Indiecies[i]]); for(int i = 0 ; i < Shell2Indiecies.size(); i++) DataShell2[i] = static_cast(b[Shell2Indiecies[i]]); for(int i = 0 ; i < Shell3Indiecies.size(); i++) DataShell3[i] = static_cast(b[Shell2Indiecies[i]]); // Normalize the Signal: Si/S0 S_S0Normalization(DataShell1, shell1b0Norm); S_S0Normalization(DataShell2, shell2b0Norm); S_S0Normalization(DataShell3, shell2b0Norm); *///fsl fix -------------------------------------------ende-- ///correct version for(unsigned int i = 0 ; i < Shell1Indiecies.size(); i++) DataShell1[i] = static_cast(b[Shell1Indiecies[i]]); for(unsigned int i = 0 ; i < Shell2Indiecies.size(); i++) DataShell2[i] = static_cast(b[Shell2Indiecies[i]]); for(unsigned int i = 0 ; i < Shell3Indiecies.size(); i++) DataShell3[i] = static_cast(b[Shell3Indiecies[i]]); // Normalize the Signal: Si/S0 S_S0Normalization(DataShell1, shell1b0Norm); S_S0Normalization(DataShell2, shell2b0Norm); S_S0Normalization(DataShell3, shell3b0Norm); if(m_Interpolation_Flag) { E1 = tempInterpolationMatrixShell1 * DataShell1; E2 = tempInterpolationMatrixShell2 * DataShell2; E3 = tempInterpolationMatrixShell3 * DataShell3; }else{ E1 = (DataShell1); E2 = (DataShell2); E3 = (DataShell3); } //Implements Eq. [19] and Fig. 4. Projection1(E1); Projection1(E2); Projection1(E3); //inqualities [31]. Taking the lograithm of th first tree inqualities //convert the quadratic inqualities to linear ones. Projection2(E1,E2,E3); for( unsigned int i = 0; i< m_MaxDirections; i++ ) { double e1 = E1.get(i); double e2 = E2.get(i); double e3 = E3.get(i); P2 = e2-e1*e1; A = (e3 -e1*e2) / ( 2* P2); B2 = A * A -(e1 * e3 - e2 * e2) /P2; B = 0; if(B2 > 0) B = sqrt(B2); P = 0; if(P2 > 0) P = sqrt(P2); alpha = A + B; beta = A - B; PValues.put(i, P); AlphaValues.put(i, alpha); BetaValues.put(i, beta); } Projection3(PValues, AlphaValues, BetaValues); for(unsigned int i = 0 ; i < m_MaxDirections; i++) { const double fac = (PValues[i] * 2 ) / (AlphaValues[i] - BetaValues[i]); lambda = 0.5 + 0.5 * std::sqrt(1 - fac * fac);; ER1 = std::fabs(lambda * (AlphaValues[i] - BetaValues[i]) + (BetaValues[i] - E1.get(i) )) + std::fabs(lambda * (AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] - E2.get(i) )) + std::fabs(lambda * (AlphaValues[i] * AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] * BetaValues[i] - E3.get(i) )); ER2 = std::fabs((1-lambda) * (AlphaValues[i] - BetaValues[i]) + (BetaValues[i] - E1.get(i) )) + std::fabs((1-lambda) * (AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] - E2.get(i) )) + std::fabs((1-lambda) * (AlphaValues[i] * AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] * BetaValues[i] - E3.get(i))); if(ER1 < ER2) LAValues.put(i, lambda); else LAValues.put(i, 1-lambda); } DoubleLogarithm(AlphaValues); DoubleLogarithm(BetaValues); vnl_vector SignalVector(element_product((LAValues) , (AlphaValues)-(BetaValues)) + (BetaValues)); vnl_vector coeffs((*m_CoeffReconstructionMatrix) *SignalVector ); // the first coeff is a fix value - coeffs[0] = 1.0/(2.0*sqrt(M_PI)); + coeffs[0] = 1.0/(2.0*sqrt(itk::Math::pi)); coeffPixel = element_cast(coeffs).data_block(); // Cast the Signal-Type from double to float for the ODF-Image odf = element_cast( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs ).data_block(); - odf *= ((M_PI*4)/NODF); + odf *= ((itk::Math::pi*4)/NODF); } // set ODF to ODF-Image coefficientImageIterator.Set(coeffPixel); odfOutputImageIterator.Set( odf ); ++odfOutputImageIterator; ++coefficientImageIterator; ++gradientInputImageIterator; } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter:: ComputeSphericalHarmonicsBasis(vnl_matrix * QBallReference, vnl_matrix *SHBasisOutput, int LOrder , vnl_matrix* LaplaciaBaltramiOutput, vnl_vector* SHOrderAssociation, vnl_matrix* SHEigenvalues) { // MITK_INFO << *QBallReference; for(unsigned int i=0; i< (*SHBasisOutput).rows(); i++) { for(int k = 0; k <= LOrder; k += 2) { for(int m =- k; m <= k; m++) { int j = ( k * k + k + 2 ) / 2 + m - 1; // Compute SHBasisFunctions if(QBallReference){ double phi = (*QBallReference)(0,i); double th = (*QBallReference)(1,i); (*SHBasisOutput)(i,j) = mitk::sh::Yj(m,k,th,phi); } // Laplacian Baltrami Order Association if(LaplaciaBaltramiOutput) (*LaplaciaBaltramiOutput)(j,j) = k*k*(k + 1)*(k+1); // SHEigenvalues with order Accosiation kj if(SHEigenvalues) (*SHEigenvalues)(j,j) = -k* (k+1); // Order Association if(SHOrderAssociation) (*SHOrderAssociation)[j] = k; } } } } template< class T, class TG, class TO, int L, int NOdfDirections> void DiffusionMultiShellQballReconstructionImageFilter ::ComputeReconstructionMatrix(IndiciesVector const & refVector) { typedef std::unique_ptr< vnl_matrix< double> > MatrixDoublePtr; typedef std::unique_ptr< vnl_vector< int > > VectorIntPtr; typedef std::unique_ptr< vnl_matrix_inverse< double > > InverseMatrixDoublePtr; int numberOfGradientDirections = refVector.size(); if( numberOfGradientDirections <= (((L+1)*(L+2))/2) || numberOfGradientDirections < 6 ) { itkExceptionMacro( << "At least (L+1)(L+2)/2 gradient directions for each shell are required; current : " << numberOfGradientDirections ); } CheckDuplicateDiffusionGradients(); const int LOrder = L; int NumberOfCoeffs = (int)(LOrder*LOrder + LOrder + 2.0)/2.0 + LOrder; MatrixDoublePtr SHBasisMatrix(new vnl_matrix(numberOfGradientDirections,NumberOfCoeffs)); SHBasisMatrix->fill(0.0); VectorIntPtr SHOrderAssociation(new vnl_vector(NumberOfCoeffs)); SHOrderAssociation->fill(0.0); MatrixDoublePtr LaplacianBaltrami(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); LaplacianBaltrami->fill(0.0); MatrixDoublePtr FRTMatrix(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); FRTMatrix->fill(0.0); MatrixDoublePtr SHEigenvalues(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); SHEigenvalues->fill(0.0); MatrixDoublePtr Q(new vnl_matrix(3, numberOfGradientDirections)); // Convert Cartesian to Spherical Coordinates refVector -> Q ComputeSphericalFromCartesian(Q.get(), refVector); // SHBasis-Matrix + LaplacianBaltrami-Matrix + SHOrderAssociationVector ComputeSphericalHarmonicsBasis(Q.get() ,SHBasisMatrix.get() , LOrder , LaplacianBaltrami.get(), SHOrderAssociation.get(), SHEigenvalues.get()); // Compute FunkRadon Transformation Matrix Associated to SHBasis Order lj for(int i=0; i(((SHBasisMatrix->transpose()) * (*SHBasisMatrix)) + (m_Lambda * (*LaplacianBaltrami)))); InverseMatrixDoublePtr pseudo_inv(new vnl_matrix_inverse((*temp))); MatrixDoublePtr inverse(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); (*inverse) = pseudo_inv->inverse(); - const double factor = (1.0/(16.0*M_PI*M_PI)); + const double factor = (1.0/(16.0*itk::Math::pi*itk::Math::pi)); MatrixDoublePtr SignalReonstructionMatrix (new vnl_matrix((*inverse) * (SHBasisMatrix->transpose()))); m_CoeffReconstructionMatrix = new vnl_matrix(( factor * ((*FRTMatrix) * ((*SHEigenvalues) * (*SignalReonstructionMatrix))) )); // SH Basis for ODF-reconstruction vnl_matrix_fixed* U = PointShell >::DistributePointShell(); for(int i=0; i( U->as_matrix() )); m_ODFSphericalHarmonicBasisMatrix = new vnl_matrix(NOdfDirections,NumberOfCoeffs); ComputeSphericalHarmonicsBasis(tempPtr.get(), m_ODFSphericalHarmonicBasisMatrix, LOrder); } template< class T, class TG, class TO, int L, int NOdfDirections> void DiffusionMultiShellQballReconstructionImageFilter ::ComputeSphericalFromCartesian(vnl_matrix * Q, IndiciesVector const & refShell) { for(unsigned int i = 0; i < refShell.size(); i++) { double x = m_GradientDirectionContainer->ElementAt(refShell[i]).normalize().get(0); double y = m_GradientDirectionContainer->ElementAt(refShell[i]).normalize().get(1); double z = m_GradientDirectionContainer->ElementAt(refShell[i]).normalize().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); (*Q)(0,i) = cart[0]; (*Q)(1,i) = cart[1]; (*Q)(2,i) = cart[2]; } } template< class T, class TG, class TO, int L, int NODF> bool DiffusionMultiShellQballReconstructionImageFilter ::CheckDuplicateDiffusionGradients() { bool value = false; BValueMapIteraotr mapIterator = m_BValueMap.begin(); mapIterator++; while(mapIterator != m_BValueMap.end()) { std::vector::const_iterator it1 = mapIterator->second.begin(); std::vector::const_iterator it2 = mapIterator->second.begin(); for(; it1 != mapIterator->second.end(); ++it1) { for(; it2 != mapIterator->second.end(); ++it2) { if(m_GradientDirectionContainer->ElementAt(*it1) == m_GradientDirectionContainer->ElementAt(*it2) && it1 != it2) { itkWarningMacro( << "Some of the Diffusion Gradients equal each other. Corresponding image data should be averaged before calling this filter." ); value = true; } } } ++mapIterator; } return value; } template< class T, class TG, class TO, int L, int NODF> std::vector DiffusionMultiShellQballReconstructionImageFilter ::GetAllDirections() { IndiciesVector directioncontainer; BValueMapIteraotr mapIterator = m_BValueMap.begin(); mapIterator++; IndiciesVector shell1 = mapIterator->second; mapIterator++; IndiciesVector shell2 = mapIterator->second; mapIterator++; IndiciesVector shell3 = mapIterator->second; while(shell1.size()>0) { unsigned int wntIndex = shell1.back(); shell1.pop_back(); IndiciesVector::iterator containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot(m_GradientDirectionContainer->ElementAt(*containerIt), m_GradientDirectionContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } while(shell2.size()>0) { unsigned int wntIndex = shell2.back(); shell2.pop_back(); IndiciesVector::iterator containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot(m_GradientDirectionContainer->ElementAt(*containerIt), m_GradientDirectionContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } while(shell3.size()>0) { unsigned int wntIndex = shell3.back(); shell3.pop_back(); IndiciesVector::iterator containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot(m_GradientDirectionContainer->ElementAt(*containerIt), m_GradientDirectionContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } return directioncontainer; } // corresponding directions between shells (e.g. dir1_shell1 vs dir1_shell2) differ more than 1 degree. template< class T, class TG, class TO, int L, int NODF> bool DiffusionMultiShellQballReconstructionImageFilter ::CheckForDifferingShellDirections() { bool interp_flag = false; BValueMapIteraotr mapIterator = m_BValueMap.begin(); mapIterator++; IndiciesVector shell1 = mapIterator->second; mapIterator++; IndiciesVector shell2 = mapIterator->second; mapIterator++; IndiciesVector shell3 = mapIterator->second; for (unsigned int i=0; i< shell1.size(); i++) if (fabs(dot(m_GradientDirectionContainer->ElementAt(shell1[i]), m_GradientDirectionContainer->ElementAt(shell2[i]))) <= 0.9998) {interp_flag=true; break;} for (unsigned int i=0; i< shell1.size(); i++) if (fabs(dot(m_GradientDirectionContainer->ElementAt(shell1[i]), m_GradientDirectionContainer->ElementAt(shell3[i]))) <= 0.9998) {interp_flag=true; break;} for (unsigned int i=0; i< shell1.size(); i++) if (fabs(dot(m_GradientDirectionContainer->ElementAt(shell2[i]), m_GradientDirectionContainer->ElementAt(shell3[i]))) <= 0.9998) {interp_flag=true; break;} return interp_flag; } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { std::locale C("C"); std::locale originalLocale = os.getloc(); os.imbue(C); Superclass::PrintSelf(os,indent); //os << indent << "OdfReconstructionMatrix: " << m_ReconstructionMatrix << std::endl; if ( m_GradientDirectionContainer ) { os << indent << "GradientDirectionContainer: " << m_GradientDirectionContainer << std::endl; } else { os << indent << "GradientDirectionContainer: (Gradient directions not set)" << std::endl; } os << indent << "NumberOfGradientDirections: " << m_NumberOfGradientDirections << std::endl; os << indent << "NumberOfBaselineImages: " << m_NumberOfBaselineImages << std::endl; os << indent << "Threshold for reference B0 image: " << m_Threshold << std::endl; os << indent << "BValue: " << m_BValue << std::endl; os.imbue( originalLocale ); } } #endif // __itkDiffusionMultiShellQballReconstructionImageFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionQballReconstructionImageFilter.txx b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionQballReconstructionImageFilter.txx index 2d826213b5..3f2e16ffb3 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionQballReconstructionImageFilter.txx +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkDiffusionQballReconstructionImageFilter.txx @@ -1,856 +1,851 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkDiffusionQballReconstructionImageFilter_txx #define __itkDiffusionQballReconstructionImageFilter_txx #include "itkDiffusionQballReconstructionImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkArray.h" #include "vnl/vnl_vector.h" #include "itkPointShell.h" -#define _USE_MATH_DEFINES -#include - namespace itk { -#define QBALL_RECON_PI M_PI - template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NrOdfDirections, int NrBasisFunctionCenters> DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::DiffusionQballReconstructionImageFilter() : m_GradientDirectionContainer(nullptr), m_NumberOfGradientDirections(0), m_NumberOfEquatorSamplingPoints(0), m_NumberOfBaselineImages(1), m_Threshold(NumericTraits< ReferencePixelType >::NonpositiveMin()), m_BValue(1.0), m_GradientImageTypeEnumeration(Else), m_DirectionsDuplicated(false) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NrOdfDirections, int NrBasisFunctionCenters> void DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::BeforeThreadedGenerateData() { // If we have more than 2 inputs, then each input, except the first is a // gradient image. The number of gradient images must match the number of // gradient directions. const unsigned int numberOfInputs = this->GetNumberOfInputs(); // There need to be at least 6 gradient directions to be able to compute the // tensor basis if( m_NumberOfGradientDirections < 1 ) { itkExceptionMacro( << "Your image contains no diffusion gradients!" ); } // If there is only 1 gradient image, it must be an itk::VectorImage. Otherwise // we must have a container of (numberOfInputs-1) itk::Image. Check to make sure if ( numberOfInputs == 1 && m_GradientImageTypeEnumeration != GradientIsInASingleImage ) { std::string gradientImageClassName( this->ProcessObject::GetInput(0)->GetNameOfClass()); if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) { itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. " << "But its of type: " << gradientImageClassName ); } } // Compute reconstruction matrix that is multiplied to the data-vector // each voxel in order to reconstruct the ODFs this->ComputeReconstructionMatrix(); // Allocate the b-zero image m_BZeroImage = BZeroImageType::New(); if( m_GradientImageTypeEnumeration == GradientIsInManyImages ) { typename ReferenceImageType::Pointer img = static_cast< ReferenceImageType * >(this->ProcessObject::GetInput(0)); m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); } // The gradients are specified in a single multi-component image else if( m_GradientImageTypeEnumeration == GradientIsInASingleImage ) { typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); } m_BZeroImage->Allocate(); } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NrOdfDirections, int NrBasisFunctionCenters> typename itk::DiffusionQballReconstructionImageFilter::OdfPixelType itk::DiffusionQballReconstructionImageFilter::Normalize( OdfPixelType odf, typename NumericTraits::AccumulateType b0 ) { switch( m_NormalizationMethod ) { // divide by sum to retreive a PDF case QBR_STANDARD: { odf.Normalize(); return odf; break; } // ADC style case QBR_B_ZERO_B_VALUE: { for(int i=0; i vnl_vector itk::DiffusionQballReconstructionImageFilter::PreNormalize( vnl_vector vec ) { switch( m_NormalizationMethod ) { // standard: no normalization before reconstruction case QBR_STANDARD: { return vec; break; } // log of signal case QBR_B_ZERO_B_VALUE: { int n = vec.size(); for(int i=0; i void DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { // init output and b-zero iterators typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetPrimaryOutput()); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); ImageRegionIterator< BZeroImageType > oit2(m_BZeroImage, outputRegionForThread); oit2.GoToBegin(); vnl_vector B(m_NumberOfGradientDirections); // Two cases here: // 1. Gradients specified in multiple images // 'n' iterators for each of the gradient images // 2. Gradients specified in a single multi-component image // one iterator for all gradient directions if( m_GradientImageTypeEnumeration == GradientIsInManyImages ) { // b-zero reference image iterator ImageRegionConstIterator< ReferenceImageType > it(static_cast< ReferenceImageType * >(this->ProcessObject::GetInput(0)), outputRegionForThread); it.GoToBegin(); // fill vector with gradient iterators typedef ImageRegionConstIterator< GradientImageType > GradientIteratorType; std::vector< GradientIteratorType * > gradientItContainer; for( unsigned int i = 1; i<= m_NumberOfGradientDirections; i++ ) { // adapt index in case we have a duplicated shell int index = i; if(m_DirectionsDuplicated) index = i % (m_NumberOfGradientDirections/2); // init and pushback current input image iterator typename GradientImageType::Pointer gradientImagePointer = nullptr; // dynamic_cast would be nice, static because of SGI gradientImagePointer = static_cast< GradientImageType * >( this->ProcessObject::GetInput(index) ); GradientIteratorType *git = new GradientIteratorType( gradientImagePointer, outputRegionForThread ); git->GoToBegin(); gradientItContainer.push_back(git); } // Following loop does the actual reconstruction work in each voxel // (Tuch, Q-Ball Reconstruction [1]) while( !it.IsAtEnd() ) { // b-zero reference value ReferencePixelType b0 = it.Get(); // init ODF OdfPixelType odf(0.0); // threshold on reference value to suppress noisy regions if( (b0 != 0) && (b0 >= m_Threshold) ) { // fill array of diffusion measurements for( unsigned int i = 0; i< m_NumberOfGradientDirections; i++ ) { GradientPixelType b = gradientItContainer[i]->Get(); B[i] = static_cast(b); ++(*gradientItContainer[i]); } // pre-normalization according to m_NormalizationMethod B = PreNormalize(B); // actual reconstruction odf = ( (*m_ReconstructionMatrix) * B ).data_block(); // post-normalization according to m_NormalizationMethod odf.Normalize(); } else { // in case we fall below threshold, we just increment to next voxel for( unsigned int i = 0; i< m_NumberOfGradientDirections; i++ ) { ++(*gradientItContainer[i]); } } for (unsigned int i=0; i GradientIteratorType; typedef typename GradientImagesType::PixelType GradientVectorType; typename GradientImagesType::Pointer gradientImagePointer = nullptr; // dynamic_cast would be nice, static because of SGI gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); // set of indicies each for the baseline images and gradient images std::vector baselineind; // contains baseline indicies std::vector gradientind; // contains gradient indicies for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { if(gdcit.Value().one_norm() <= 0.0) { baselineind.push_back(gdcit.Index()); } else { gradientind.push_back(gdcit.Index()); } } // in case we have a duplicated shell, we also duplicate or indices if( m_DirectionsDuplicated ) { int gradIndSize = gradientind.size(); for(int i=0; i::AccumulateType b0 = NumericTraits::Zero; for(unsigned int i = 0; i < baselineind.size(); ++i) { b0 += b[baselineind[i]]; } b0 /= this->m_NumberOfBaselineImages; // init resulting ODF OdfPixelType odf(0.0); // threshold on reference value to suppress noisy regions if( (b0 != 0) && (b0 >= m_Threshold) ) { for( unsigned int i = 0; i< m_NumberOfGradientDirections; i++ ) { B[i] = static_cast(b[gradientind[i]]); } // pre-normalization according to m_NormalizationMethod B = PreNormalize(B); // actual reconstruction odf = ( (*m_ReconstructionMatrix) * B ).data_block(); // post-normalization according to m_NormalizationMethod odf = Normalize(odf, b0); } for (unsigned int i=0; i void DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::ComputeReconstructionMatrix() { if( m_NumberOfGradientDirections < 1 ) { itkExceptionMacro( << "Your image contains no diffusion gradients!" ); } { // check for duplicate diffusion gradients bool warning = false; for(GradientDirectionContainerType::ConstIterator gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { for(GradientDirectionContainerType::ConstIterator gdcit2 = this->m_GradientDirectionContainer->Begin(); gdcit2 != this->m_GradientDirectionContainer->End(); ++gdcit2) { if(gdcit1.Value() == gdcit2.Value() && gdcit1.Index() != gdcit2.Index()) { itkWarningMacro( << "Some of the Diffusion Gradients equal each other. Corresponding image data should be averaged before calling this filter." ); warning = true; break; } } if (warning) break; } // handle acquisition schemes where only half of the spherical // shell is sampled by the gradient directions. In this case, // each gradient direction is duplicated in negative direction. vnl_vector centerMass(3); centerMass.fill(0.0); int count = 0; for(GradientDirectionContainerType::ConstIterator gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { if(gdcit1.Value().one_norm() > 0.0) { centerMass += gdcit1.Value(); count ++; } } centerMass /= count; if(centerMass.two_norm() > 0.1) { m_DirectionsDuplicated = true; m_NumberOfGradientDirections *= 2; } } // set default number of equator sampling points if needed if(!this->m_NumberOfEquatorSamplingPoints) this->m_NumberOfEquatorSamplingPoints - = (int) ceil((double)sqrt(8*QBALL_RECON_PI*this->m_NumberOfGradientDirections)); + = (int) ceil((double)sqrt(8*itk::Math::pi*this->m_NumberOfGradientDirections)); vnl_matrix* Q = new vnl_matrix(3, m_NumberOfGradientDirections); { // Fill matrix Q with gradient directions int i = 0; for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { if(gdcit.Value().one_norm() > 0.0) { (*Q)(0,i) = gdcit.Value().get(0); (*Q)(1,i) = gdcit.Value().get(1); (*Q)(2,i++) = gdcit.Value().get(2); } } if(m_DirectionsDuplicated) { for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { if(gdcit.Value().one_norm() > 0.0) { (*Q)(0,i) = -gdcit.Value().get(0); (*Q)(1,i) = -gdcit.Value().get(1); (*Q)(2,i++) = -gdcit.Value().get(2); } } } } vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); vnl_matrix_fixed* V = itk::PointShell >::DistributePointShell(); // calculate sampling points on the equator perpendicular to z-axis vnl_matrix *C = new vnl_matrix(3, m_NumberOfEquatorSamplingPoints); for(unsigned int i=0; i::Zero; } // rotate the sampling points to each directions of interest vnl_matrix *S = new vnl_matrix(3,m_NumberOfEquatorSamplingPoints*NOdfDirections); { vnl_vector_fixed z(NumericTraits::Zero); z.put(2,NumericTraits::One); vnl_matrix_fixed eye(NumericTraits::Zero); eye.fill_diagonal(NumericTraits::One); for(int i=0; i ui = (*U).get_column(i); vnl_matrix *RC = new vnl_matrix(3,m_NumberOfEquatorSamplingPoints); if( (z(0)*ui(0)+z(1)*ui(1)+z(2)*ui(2)+1) != 0 ) { vnl_matrix_fixed R; R.set_column(0, (z+ui)*(z(0)+ui(0))); R.set_column(1, (z+ui)*(z(1)+ui(1))); R.set_column(2, (z+ui)*(z(2)+ui(2))); R /= (z(0)*ui(0)+z(1)*ui(1)+z(2)*ui(2)+1); R -= eye; (*RC) = R*(*C); } else { RC = C; } (*S).set_columns(i*m_NumberOfEquatorSamplingPoints, *RC); } } // determine interpolation kernel width first // use to determine diffusion measurement contribution to each of the kernels vnl_matrix *H_plus = new vnl_matrix(NBasisFunctionCenters,m_NumberOfGradientDirections); - double maxSigma = QBALL_RECON_PI/6; + double maxSigma = itk::Math::pi/6; double bestSigma = maxSigma; { double stepsize = 0.01; double start = 0.01; double minCondition = NumericTraits::max(); vnl_matrix *H = new vnl_matrix(m_NumberOfGradientDirections,NBasisFunctionCenters,(double)0); { int increasing = 0; for( double sigma=start; sigma *tmpH = new vnl_matrix(m_NumberOfGradientDirections,NBasisFunctionCenters); for(unsigned int r=0; r1.0) ? 1.0 : qtv); double x = acos(qtv); - (*tmpH)(r,c) = (1.0/(sigma*sqrt(2.0*QBALL_RECON_PI))) + (*tmpH)(r,c) = (1.0/(sigma*sqrt(2.0*itk::Math::pi))) *exp((-x*x)/(2*sigma*sigma)); } } vnl_svd *solver; if(m_NumberOfGradientDirections>NBasisFunctionCenters) { solver = new vnl_svd(*tmpH); } else { solver = new vnl_svd(tmpH->transpose()); } double condition = solver->sigma_max() / solver->sigma_min(); std::cout << sigma << ": " << condition << std::endl; if( condition < minCondition ) { minCondition = condition; bestSigma = sigma; H->update(*tmpH); } else { // optimum assumed to be hit after condition increased 3 times if (++increasing>3) break; } } } vnl_matrix_inverse *pseudoInverse = new vnl_matrix_inverse(*H); (*H_plus) = pseudoInverse->pinverse(); std::cout << "choosing sigma = " << bestSigma << std::endl; } // this is the contribution of each kernel to each sampling point on the // equator vnl_matrix *G = new vnl_matrix(m_NumberOfEquatorSamplingPoints*NOdfDirections,NBasisFunctionCenters); { for(unsigned int r=0; r1.0) ? 1.0 : stv); double x = acos(stv); - (*G)(r,c) = (1.0/(bestSigma*sqrt(2.0*QBALL_RECON_PI))) + (*G)(r,c) = (1.0/(bestSigma*sqrt(2.0*itk::Math::pi))) *exp((-x*x)/(2*bestSigma*bestSigma)); } } } vnl_matrix *GH_plus = new vnl_matrix(m_NumberOfEquatorSamplingPoints*NOdfDirections,m_NumberOfGradientDirections); // simple matrix multiplication, manual cause of stack overflow using operator for (unsigned i = 0; i < m_NumberOfEquatorSamplingPoints*NOdfDirections; ++i) { for (unsigned j = 0; j < m_NumberOfGradientDirections; ++j) { double accum = (*G)(i,0) * (*H_plus)(0,j); for (unsigned k = 1; k < NOdfDirections; ++k) accum += (*G)(i,k) * (*H_plus)(k,j); (*GH_plus)(i,j) = accum; } } typename vnl_matrix::iterator it3; for( it3 = (*GH_plus).begin(); it3 != (*GH_plus).end(); it3++) { if(*it3<0.0) *it3 = 0; } // this is an addition to the original tuch algorithm for(unsigned int i=0; i r = GH_plus->get_row(i); r /= r.sum(); GH_plus->set_row(i,r); } m_ReconstructionMatrix = new vnl_matrix(NOdfDirections,m_NumberOfGradientDirections,0.0); for(int i=0; i r = m_ReconstructionMatrix->get_row(i); r /= r.sum(); m_ReconstructionMatrix->set_row(i,r); } std::cout << "Reconstruction Matrix computed." << std::endl; } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NrOdfDirections, int NrBasisFunctionCenters> void DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::AddGradientImage( const GradientDirectionType &gradientDirection, const GradientImageType *gradientImage ) { // Make sure crazy users did not call both AddGradientImage and // SetGradientImage if( m_GradientImageTypeEnumeration == GradientIsInASingleImage) { itkExceptionMacro( << "Cannot call both methods:" << "AddGradientImage and SetGradientImage. Please call only one of them."); } // If the container to hold the gradient directions hasn't been allocated // yet, allocate it. if( !this->m_GradientDirectionContainer ) { this->m_GradientDirectionContainer = GradientDirectionContainerType::New(); } this->m_NumberOfGradientDirections = m_GradientDirectionContainer->Size(); m_GradientDirectionContainer->InsertElement( this->m_NumberOfGradientDirections, gradientDirection / gradientDirection.two_norm() ); this->ProcessObject::SetNthInput( this->m_NumberOfGradientDirections, const_cast< GradientImageType* >(gradientImage) ); m_GradientImageTypeEnumeration = GradientIsInManyImages; } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NrOdfDirections, int NrBasisFunctionCenters> void DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::SetGradientImage(const GradientDirectionContainerType *gradientDirection, const GradientImagesType *gradientImage ) { // Make sure crazy users did not call both AddGradientImage and // SetGradientImage if( m_GradientImageTypeEnumeration == GradientIsInManyImages ) { itkExceptionMacro( << "Cannot call both methods:" << "AddGradientImage and SetGradientImage. Please call only one of them."); } this->m_GradientDirectionContainer = GradientDirectionContainerType::New(); for(GradientDirectionContainerType::ConstIterator it = gradientDirection->Begin(); it != gradientDirection->End(); it++) { this->m_GradientDirectionContainer->push_back(it.Value()); } unsigned int numImages = gradientDirection->Size(); this->m_NumberOfBaselineImages = 0; for(GradientDirectionContainerType::Iterator it = this->m_GradientDirectionContainer->Begin(); it != this->m_GradientDirectionContainer->End(); it++) { if(it.Value().one_norm() <= 0.0) { this->m_NumberOfBaselineImages++; } else // Normalize non-zero gradient directions { it.Value() = it.Value() / it.Value().two_norm(); } } this->m_NumberOfGradientDirections = numImages - this->m_NumberOfBaselineImages; // ensure that the gradient image we received has as many components as // the number of gradient directions if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + m_NumberOfGradientDirections ) { itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages << "baselines = " << m_NumberOfGradientDirections + this->m_NumberOfBaselineImages << " directions specified but image has " << gradientImage->GetVectorLength() << " components."); } this->ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); m_GradientImageTypeEnumeration = GradientIsInASingleImage; } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NrOdfDirections, int NrBasisFunctionCenters> void DiffusionQballReconstructionImageFilter< TReferenceImagePixelType, TGradientImagePixelType, TOdfPixelType, NrOdfDirections, NrBasisFunctionCenters> ::PrintSelf(std::ostream& os, Indent indent) const { std::locale C("C"); std::locale originalLocale = os.getloc(); os.imbue(C); Superclass::PrintSelf(os,indent); os << indent << "OdfReconstructionMatrix: " << m_ReconstructionMatrix << std::endl; if ( m_GradientDirectionContainer ) { os << indent << "GradientDirectionContainer: " << m_GradientDirectionContainer << std::endl; } else { os << indent << "GradientDirectionContainer: (Gradient directions not set)" << std::endl; } os << indent << "NumberOfGradientDirections: " << m_NumberOfGradientDirections << std::endl; os << indent << "NumberOfBaselineImages: " << m_NumberOfBaselineImages << std::endl; os << indent << "Threshold for reference B0 image: " << m_Threshold << std::endl; os << indent << "BValue: " << m_BValue << std::endl; if ( this->m_GradientImageTypeEnumeration == GradientIsInManyImages ) { os << indent << "Gradient images haven been supplied " << std::endl; } else if ( this->m_GradientImageTypeEnumeration == GradientIsInManyImages ) { os << indent << "A multicomponent gradient image has been supplied" << std::endl; } os.imbue( originalLocale ); } } #endif // __itkDiffusionQballReconstructionImageFilter_txx diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkOrientationDistributionFunction.txx b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkOrientationDistributionFunction.txx index 4e4777c2e0..dc9107f3b9 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkOrientationDistributionFunction.txx +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkOrientationDistributionFunction.txx @@ -1,1372 +1,1367 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 _itkOrientationDistributionFunction_txx #define _itkOrientationDistributionFunction_txx #include #include #include #include #include #include #include "itkPointShell.h" -#define _USE_MATH_DEFINES -#include #ifdef _MSC_VER #if _MSC_VER <= 1700 #define fmin(a,b) ((a<=b)?(a):(b)) #define fmax(a,b) ((a>=b)?(a):(b)) #define isnan(c) (c!=c) #endif #endif #include #include #include #include #include #include namespace itk { template vtkPolyData* itk::OrientationDistributionFunction::m_BaseMesh = nullptr; template double itk::OrientationDistributionFunction::m_MaxChordLength = -1.0; template vnl_matrix_fixed* itk::OrientationDistributionFunction::m_Directions = itk::PointShell >::DistributePointShell(); template std::vector< std::vector* >* itk::OrientationDistributionFunction::m_NeighborIdxs = nullptr; template std::vector< std::vector* >* itk::OrientationDistributionFunction::m_AngularRangeIdxs = nullptr; template std::vector* itk::OrientationDistributionFunction::m_HalfSphereIdxs = nullptr; template itk::SimpleFastMutexLock itk::OrientationDistributionFunction::m_MutexBaseMesh; template itk::SimpleFastMutexLock itk::OrientationDistributionFunction::m_MutexHalfSphereIdxs; template itk::SimpleFastMutexLock itk::OrientationDistributionFunction::m_MutexNeighbors; template itk::SimpleFastMutexLock itk::OrientationDistributionFunction::m_MutexAngularRange; - -#define ODF_PI M_PI - /** * Assignment Operator */ template OrientationDistributionFunction& OrientationDistributionFunction ::operator= (const Self& r) { BaseArray::operator=(r); return *this; } /** * Assignment Operator from a scalar constant */ template OrientationDistributionFunction& OrientationDistributionFunction ::operator= (const ComponentType & r) { BaseArray::operator=(&r); return *this; } /** * Assigment from a plain array */ template OrientationDistributionFunction& OrientationDistributionFunction ::operator= (const ComponentArrayType r ) { BaseArray::operator=(r); return *this; } /** * Returns a temporary copy of a vector */ template OrientationDistributionFunction OrientationDistributionFunction ::operator+(const Self & r) const { Self result; for( unsigned int i=0; i OrientationDistributionFunction OrientationDistributionFunction ::operator-(const Self & r) const { Self result; for( unsigned int i=0; i const OrientationDistributionFunction & OrientationDistributionFunction ::operator+=(const Self & r) { for( unsigned int i=0; i const OrientationDistributionFunction & OrientationDistributionFunction ::operator-=(const Self & r) { for( unsigned int i=0; i const OrientationDistributionFunction & OrientationDistributionFunction ::operator*=(const RealValueType & r) { for( unsigned int i=0; i const OrientationDistributionFunction & OrientationDistributionFunction ::operator/=(const RealValueType & r) { for( unsigned int i=0; i OrientationDistributionFunction OrientationDistributionFunction ::operator*(const RealValueType & r) const { Self result; for( unsigned int i=0; i OrientationDistributionFunction OrientationDistributionFunction ::operator/(const RealValueType & r) const { Self result; for( unsigned int i=0; i const typename OrientationDistributionFunction::ValueType & OrientationDistributionFunction ::operator()(unsigned int row, unsigned int col) const { unsigned int k; if( row < col ) { k = row * InternalDimension + col - row * ( row + 1 ) / 2; } else { k = col * InternalDimension + row - col * ( col + 1 ) / 2; } if( k >= InternalDimension ) { k = 0; } return (*this)[k]; } /** * Matrix notation access to elements */ template typename OrientationDistributionFunction::ValueType & OrientationDistributionFunction ::operator()(unsigned int row, unsigned int col) { unsigned int k; if( row < col ) { k = row * InternalDimension + col - row * ( row + 1 ) / 2; } else { k = col * InternalDimension + row - col * ( col + 1 ) / 2; } if( k >= InternalDimension ) { k = 0; } return (*this)[k]; } /** * Set the Tensor to an Identity. * Set ones in the diagonal and zeroes every where else. */ template void OrientationDistributionFunction ::SetIsotropic() { this->Fill(NumericTraits< T >::One / NOdfDirections); } /** * InitFromTensor() */ template void OrientationDistributionFunction ::InitFromTensor(itk::DiffusionTensor3D tensor) { m_EigenAnalysisCalculated = false; for(unsigned int i=0; i void OrientationDistributionFunction:: InitFromEllipsoid( itk::DiffusionTensor3D tensor ) { m_EigenAnalysisCalculated = false; FixedArray nulltensor; nulltensor.Fill(0.0); if( tensor == nulltensor ) { for ( unsigned int it=0; it < NOdfDirections; ++it ){ (*this)[it] = (T)0; } MITK_DEBUG << "OrientationDistributionFunction<" << typeid(T).name() << ", " << NOdfDirections << ">::InitFromEllipsoid(" << typeid(tensor).name() << ") encountered a nulltensor as dti input point and ignorend it."; return; } tensor.ComputeEigenAnalysis( m_EigenValues, this->m_EigenVectors ); // gives normalized eigenvectors as lines i.e. rows. m_EigenAnalysisCalculated = true; double a = m_EigenValues[0]; // those eigenvalues are the 3 |axes of the ellipsoid|, double b = m_EigenValues[1]; // ComputeEigenAnalysis gives eigenValues in ascending < order per default, double c = m_EigenValues[2]; // therefor the third eigenVector is the main direction of diffusion. if( a <= 0.0 || b <= 0.0 || c <= 0.0 ) { for ( unsigned int it=0; it < NOdfDirections; ++it ){ (*this)[it] = (T)0; } MITK_DEBUG << "OrientationDistributionFunction<" << typeid(T).name() << ", " << NOdfDirections << ">::InitFromEllipsoid(" << typeid(tensor).name() << ") encountered an eigenvalue <= 0 and ignored this input point."; return; } // check magnitude and scale towards 1 to minimize numerical condition kappa: #ifdef _MSC_VER #if _MSC_VER <= 1700 int exponent_a = floor(std::log(a)/std::log(2)); int exponent_b = floor(std::log(b)/std::log(2)); int exponent_c = floor(std::log(c)/std::log(2)); #else int exponent_a = std::ilogb(a); int exponent_b = std::ilogb(b); int exponent_c = std::ilogb(c); #endif #else int exponent_a = std::ilogb(a); int exponent_b = std::ilogb(b); int exponent_c = std::ilogb(c); #endif T min_exponent= fmin(exponent_a, fmin(exponent_b, exponent_c) ); T max_exponent= fmax(exponent_c, fmax(exponent_b, exponent_a) ); int scale_exponent = floor(0.5 * (min_exponent + max_exponent)); double scaling = pow(2, scale_exponent); a= a/scaling; b= b/scaling; c= c/scaling; vnl_matrix_fixed eigenBase; // for change of base system. for (int row = 0 ; row < 3; ++row) // Transposing because ComputeEigenAnalysis(,) gave us _row_ vectors. { for (int col = 0; col < 3; ++col) { eigenBase(row, col) = this->m_EigenVectors(col, row); } } eigenBase= vnl_inverse(eigenBase); // Assuming canonical orthonormal system x=[1;0;0];y=[0;1;0];z=[0;0;1] for original DT. eigenBase.assert_finite(); #ifndef NDEBUG double kappa=1.0; { // calculate numerical condition kappa= ||f(x)-f(x~)|| approximately: double gxaa = pow( a, -2.0); double gybb = pow( b, -2.0); double gzcc = pow( c, -2.0); kappa = sqrt( pow( a, 2.0)+ pow( b, 2.0)+ pow( c, 2.0) + 1 ) / (gxaa + gybb + gzcc) * sqrt( pow( a, -6.0)+ pow( b, -6.0)+ pow( c, -6.0) + pow( a, -4.0)+ pow( b, -4.0)+ pow( c, -4.0) ); MITK_DEBUG <<"kappa= "<< kappa << ", eigenvalues= [" << a <<", "<< b <<", "<< c <<"], eigenbase= [" < g( (*m_Directions)(0,i), (*m_Directions)(1,i), (*m_Directions)(2,i) ); g = eigenBase*g; // passive change of base system of g. g = g.normalize(); // unit vectors necessary. (*this)[i] = scaling / sqrt( (g[0]/a)*(g[0]/a) + (g[1]/b)*(g[1]/b) + (g[2]/c)*(g[2]/c) ); #ifndef NDEBUG { // boundary check for numerical stability, assuming sigma=6, ||f(x~)-f~(x~)|| <= eps*kappa*sigma. T min_ev= fmin(a, fmin(b, c) ); T max_ev= fmax(c, fmax(b, a) ); double eps= std::numeric_limits::epsilon(); assert( scaling*min_ev <= ((*this)[i] + eps*kappa*6.0) ); // we should be between smallest and assert( (*this)[i] <= (scaling*max_ev + eps*kappa*6.0) ); // biggest eigenvalue. } #endif if ( (*this)[i] < T(0) || (*this)[i] > T(1) || std::isnan((*this)[i]) ) // P∈[0;1] sanity check. { // C: NaN != NaN, C++11: isnan((*this)[i]). MITK_DEBUG << "OrientationDistributionFunction<" << typeid(T).name() << ", " << NOdfDirections << ">::InitFromEllipsoid(" << typeid(tensor).name() << ") encountered a probability value out of range [0;1] and set it to zero: (*this)[" << i <<"]= " << (*this)[i]; (*this)[i] = T(0); } } } /** * L2-Normalization */ template void OrientationDistributionFunction ::L2Normalize() { T sum = 0; for( unsigned int i=0; i void OrientationDistributionFunction ::Normalize() { T sum = 0; for( unsigned int i=0; i0) { for( unsigned int i=0; i OrientationDistributionFunction OrientationDistributionFunction ::MinMaxNormalize() const { T max = NumericTraits::NonpositiveMin(); T min = NumericTraits::max(); for( unsigned int i=0; i max ? (*this)[i] : max; min = (*this)[i] < min ? (*this)[i] : min; } Self retval; for( unsigned int i=0; i OrientationDistributionFunction OrientationDistributionFunction ::MaxNormalize() const { T max = NumericTraits::NonpositiveMin(); for( unsigned int i=0; i max ? (*this)[i] : max; } Self retval; for( unsigned int i=0; i T OrientationDistributionFunction ::GetMaxValue() const { T max = NumericTraits::NonpositiveMin(); for( unsigned int i=0; i= max ) { max = (*this)[i]; } } return max; } template T OrientationDistributionFunction ::GetMinValue() const { T min = NumericTraits::max(); for( unsigned int i=0; i= min ) { min = (*this)[i]; } } return min; } template T OrientationDistributionFunction ::GetMeanValue() const { T sum = 0; for( unsigned int i=0; i double OrientationDistributionFunction ::GetMaxChordLength() { if(m_MaxChordLength<0.0) { ComputeBaseMesh(); double max_dist = -1; vtkPoints* points = m_BaseMesh->GetPoints(); for(int i=0; iGetPoint(i,p); std::vector neighbors = GetNeighbors(i); for(std::size_t j=0; jGetPoint(neighbors[j],n); double d = sqrt( (p[0]-n[0])*(p[0]-n[0]) + (p[1]-n[1])*(p[1]-n[1]) + (p[2]-n[2])*(p[2]-n[2])); max_dist = d>max_dist ? d : max_dist; } } m_MaxChordLength = max_dist; } return m_MaxChordLength; } template void OrientationDistributionFunction ::ComputeBaseMesh() { m_MutexBaseMesh.Lock(); if(m_BaseMesh == nullptr) { vtkPoints* points = vtkPoints::New(); for(unsigned int j=0; jInsertNextPoint(az,elev,r); } vtkPolyData* polydata = vtkPolyData::New(); polydata->SetPoints( points ); vtkDelaunay2D *delaunay = vtkDelaunay2D::New(); delaunay->SetInputData( polydata ); delaunay->Update(); vtkCellArray* vtkpolys = delaunay->GetOutput()->GetPolys(); vtkCellArray* vtknewpolys = vtkCellArray::New(); vtkIdType npts; vtkIdType *pts; while(vtkpolys->GetNextCell(npts,pts)) { bool insert = true; for(int i=0; iGetPoint(pts[i]); double az = tmpPoint[0]; double elev = tmpPoint[1]; - if((std::abs(az)>ODF_PI-0.5) || (std::abs(elev)>ODF_PI/2-0.5)) + if((std::abs(az)>itk::Math::pi-0.5) || (std::abs(elev)>itk::Math::pi/2-0.5)) insert = false; } if(insert) vtknewpolys->InsertNextCell(npts, pts); } vtkPoints* points2 = vtkPoints::New(); for(unsigned int j=0; jInsertNextPoint(az,elev,r); } vtkPolyData* polydata2 = vtkPolyData::New(); polydata2->SetPoints( points2 ); vtkDelaunay2D *delaunay2 = vtkDelaunay2D::New(); delaunay2->SetInputData( polydata2 ); delaunay2->Update(); vtkpolys = delaunay2->GetOutput()->GetPolys(); while(vtkpolys->GetNextCell(npts,pts)) { bool insert = true; for(int i=0; iGetPoint(pts[i]); double az = tmpPoint[0]; double elev = tmpPoint[1]; - if((std::abs(az)>ODF_PI-0.5) || (std::abs(elev)>ODF_PI/2-0.5)) + if((std::abs(az)>itk::Math::pi-0.5) || (std::abs(elev)>itk::Math::pi/2-0.5)) insert = false; } if(insert) vtknewpolys->InsertNextCell(npts, pts); } polydata->SetPolys(vtknewpolys); for (unsigned int p = 0; p < NOdfDirections; p++) { points->SetPoint(p,m_Directions->get_column(p).data_block()); } polydata->SetPoints( points ); m_BaseMesh = polydata; } m_MutexBaseMesh.Unlock(); } /** * Extract the index of the principal diffusion direction */ template int OrientationDistributionFunction::GetPrincipalDiffusionDirectionIndex() const { T max = NumericTraits::NonpositiveMin(); int maxidx = -1; for( unsigned int i=0; i= max ) { max = (*this)[i]; maxidx = i; } } return maxidx; } /** * Extract the principal diffusion direction */ template vnl_vector_fixed OrientationDistributionFunction::GetPrincipalDiffusionDirection() const { if (m_EigenAnalysisCalculated) { vnl_vector_fixed vec; vec[0] = this->m_EigenVectors(2,0); vec[1] = this->m_EigenVectors(2,1); vec[2] = this->m_EigenVectors(2,2); vec.normalize(); return vec; } else { int idx = GetPrincipalDiffusionDirectionIndex(); if (idx>0 && idx<(int)NOdfDirections) return OrientationDistributionFunction::GetDirection(idx); vnl_vector_fixed vec; vec.fill(0); return vec; } } template std::vector OrientationDistributionFunction ::GetNeighbors(int idx) { ComputeBaseMesh(); m_MutexNeighbors.Lock(); if(m_NeighborIdxs == nullptr) { m_NeighborIdxs = new std::vector< std::vector* >(); vtkCellArray* polys = m_BaseMesh->GetPolys(); for(unsigned int i=0; i(); polys->InitTraversal(); vtkIdType npts; vtkIdType *pts; while(polys->GetNextCell(npts,pts)) { if( pts[0] == i ) { idxs->push_back(pts[1]); idxs->push_back(pts[2]); } else if( pts[1] == i ) { idxs->push_back(pts[0]); idxs->push_back(pts[2]); } else if( pts[2] == i ) { idxs->push_back(pts[0]); idxs->push_back(pts[1]); } } std::sort(idxs->begin(), idxs->end()); std::vector< int >::iterator endLocation; endLocation = std::unique( idxs->begin(), idxs->end() ); idxs->erase(endLocation, idxs->end()); m_NeighborIdxs->push_back(idxs); } } m_MutexNeighbors.Unlock(); return *m_NeighborIdxs->at(idx); } /** * Extract the n-th diffusion direction */ template int OrientationDistributionFunction ::GetNthDiffusionDirection(int n, vnl_vector_fixed rndVec) const { if( n == 0 ) return GetPrincipalDiffusionDirectionIndex(); m_MutexHalfSphereIdxs.Lock(); if( !m_HalfSphereIdxs ) { m_HalfSphereIdxs = new std::vector(); for( unsigned int i=0; iget_column(i),rndVec) > 0.0) { m_HalfSphereIdxs->push_back(i); } } } m_MutexHalfSphereIdxs.Unlock(); // collect indices of directions // that are local maxima std::vector localMaxima; std::vector::iterator it; for( it=m_HalfSphereIdxs->begin(); it!=m_HalfSphereIdxs->end(); it++) { std::vector nbs = GetNeighbors(*it); std::vector::iterator it2; bool max = true; for(it2 = nbs.begin(); it2 != nbs.end(); it2++) { if((*this)[*it2] > (*this)[*it]) { max = false; break; } } if(max) localMaxima.push_back(*it); } // delete n highest local maxima from list // and return remaining highest int maxidx = -1; std::vector::iterator itMax; for( int i=0; i<=n; i++ ) { maxidx = -1; T max = NumericTraits::NonpositiveMin(); for(it = localMaxima.begin(); it != localMaxima.end(); it++) { if((*this)[*it]>max) { max = (*this)[*it]; maxidx = *it; } } it = find(localMaxima.begin(), localMaxima.end(), maxidx); if(it!=localMaxima.end()) localMaxima.erase(it); } return maxidx; } template < typename TComponent, unsigned int NOdfDirections > vnl_vector_fixed itk::OrientationDistributionFunction ::GetDirection( int i ) { return m_Directions->get_column(i); } /** * Interpolate a position between sampled directions */ template T OrientationDistributionFunction ::GetInterpolatedComponent(vnl_vector_fixed dir, InterpolationMethods method) const { ComputeBaseMesh(); double retval = -1.0; switch(method) { case ODF_NEAREST_NEIGHBOR_INTERP: { vtkPoints* points = m_BaseMesh->GetPoints(); double current_min = NumericTraits::max(); int current_min_idx = -1; for(int i=0; i P(points->GetPoint(i)); double dist = (dir-P).two_norm(); current_min_idx = distGetNthComponent(current_min_idx); break; } case ODF_TRILINEAR_BARYCENTRIC_INTERP: { double maxChordLength = GetMaxChordLength(); vtkCellArray* polys = m_BaseMesh->GetPolys(); vtkPoints* points = m_BaseMesh->GetPoints(); vtkIdType npts; vtkIdType *pts; double current_min = NumericTraits::max(); polys->InitTraversal(); while(polys->GetNextCell(npts,pts)) { vnl_vector_fixed A(points->GetPoint(pts[0])); vnl_vector_fixed B(points->GetPoint(pts[1])); vnl_vector_fixed C(points->GetPoint(pts[2])); vnl_vector_fixed d1; d1.put(0,(dir-A).two_norm()); d1.put(1,(dir-B).two_norm()); d1.put(2,(dir-C).two_norm()); double maxval = d1.max_value(); if(maxval>maxChordLength) { continue; } // Compute vectors vnl_vector_fixed v0 = C - A; vnl_vector_fixed v1 = B - A; // Project direction to plane ABC vnl_vector_fixed v6 = dir; vnl_vector_fixed cross = vnl_cross_3d(v0, v1); cross = cross.normalize(); vtkPlane::ProjectPoint(v6.data_block(),A.data_block(),cross.data_block(),v6.data_block()); v6 = v6-A; // Calculate barycentric coords vnl_matrix_fixed mat; mat.set_column(0, v0); mat.set_column(1, v1); vnl_matrix_inverse inv(mat); vnl_matrix_fixed inver = inv.pinverse(); vnl_vector uv = inv.pinverse()*v6; // Check if point is in triangle double eps = 0.01; if( (uv(0) >= 0-eps) && (uv(1) >= 0-eps) && (uv(0) + uv(1) <= 1+eps) ) { // check if minimum angle is the max so far if(d1.two_norm() < current_min) { current_min = d1.two_norm(); vnl_vector barycentricCoords(3); barycentricCoords[2] = uv[0]<0 ? 0 : (uv[0]>1?1:uv[0]); barycentricCoords[1] = uv[1]<0 ? 0 : (uv[1]>1?1:uv[1]); barycentricCoords[0] = 1-(barycentricCoords[1]+barycentricCoords[2]); retval = barycentricCoords[0]*this->GetNthComponent(pts[0]) + barycentricCoords[1]*this->GetNthComponent(pts[1]) + barycentricCoords[2]*this->GetNthComponent(pts[2]); } } } break; } case ODF_SPHERICAL_GAUSSIAN_BASIS_FUNCTIONS: { double maxChordLength = GetMaxChordLength(); double sigma = asin(maxChordLength/2); // this is the contribution of each kernel to each sampling point on the // equator vnl_vector contrib; contrib.set_size(NOdfDirections); vtkPoints* points = m_BaseMesh->GetPoints(); double sum = 0; for(int i=0; i P(points->GetPoint(i)); double stv = dir[0]*P[0] + dir[1]*P[1] + dir[2]*P[2]; stv = (stv<-1.0) ? -1.0 : ( (stv>1.0) ? 1.0 : stv); double x = acos(stv); - contrib[i] = (1.0/(sigma*sqrt(2.0*ODF_PI))) + contrib[i] = (1.0/(sigma*sqrt(2.0*itk::Math::pi))) *exp((-x*x)/(2*sigma*sigma)); sum += contrib[i]; } retval = 0; for(int i=0; iGetNthComponent(i); } break; } } if(retval==-1) { std::cout << "Interpolation failed" << std::endl; return 0; } return retval; } /** * Calculate Generalized Fractional Anisotropy */ template T OrientationDistributionFunction ::GetGeneralizedFractionalAnisotropy() const { double mean = 0; double std = 0; double rms = 0; for( unsigned int i=0; i T itk::OrientationDistributionFunction ::GetGeneralizedGFA( int k, int p ) const { double mean = 0; double std = 0; double rms = 0; double max = NumericTraits::NonpositiveMin(); for( unsigned int i=0; i max ? val : max; } max = pow(max,(double)p); mean /= N; for( unsigned int i=0; i0) { rms += pow(val,(double)(p*k)); } } std /= N - 1; std = sqrt(std); if(k>0) { rms /= N; rms = pow(rms,(double)(1.0/k)); } else if(k<0) // lim k->inf gives us the maximum { rms = max; } else // k==0 undefined, we define zeros root from 1 as 1 { rms = 1; } if(rms == 0) { return 0; } else { return (T)(std/rms); } } /** * Calculate Nematic Order Parameter */ template < typename T, unsigned int N > T itk::OrientationDistributionFunction ::GetNematicOrderParameter() const { // not yet implemented return 0; } /** * Calculate StdDev by MaxValue */ template < typename T, unsigned int N > T itk::OrientationDistributionFunction ::GetStdDevByMaxValue() const { double mean = 0; double std = 0; T max = NumericTraits::NonpositiveMin(); for( unsigned int i=0; i max ? (*this)[i] : max; } mean /= InternalDimension; for( unsigned int i=0; i T itk::OrientationDistributionFunction ::GetPrincipleCurvature(double alphaMinDegree, double alphaMaxDegree, int invert) const { // following loop only performed once // (computing indices of each angular range) m_MutexAngularRange.Lock(); if(m_AngularRangeIdxs == nullptr) { m_AngularRangeIdxs = new std::vector< std::vector* >(); for(unsigned int i=0; i pDir = GetDirection(i); auto idxs = new std::vector(); for(unsigned int j=0; j cDir = GetDirection(j); - double angle = ( 180 / ODF_PI ) * acos( dot_product(pDir, cDir) ); + double angle = ( 180 / itk::Math::pi ) * acos( dot_product(pDir, cDir) ); if( (angle < alphaMaxDegree) && (angle > alphaMinDegree) ) { idxs->push_back(j); } } m_AngularRangeIdxs->push_back(idxs); } } m_MutexAngularRange.Unlock(); // find the maximum (or minimum) direction (remember index and value) T mode; int pIdx = -1; if(invert == 0) { pIdx = GetPrincipalDiffusionDirectionIndex(); mode = (*this)[pIdx]; } else { mode = NumericTraits::max(); for( unsigned int i=0; i nbs = GetNeighbors(pIdx); //////std::vector modeAndNeighborVals; //////modeAndNeighborVals.push_back(mode); //////int numNeighbors = nbs.size(); //////for(int i=0; i odfValuesInAngularRange; int numInRange = m_AngularRangeIdxs->at(pIdx)->size(); for(int i=0; iat(pIdx))[i] ]); } // sort them by value std::sort( odfValuesInAngularRange.begin(), odfValuesInAngularRange.end() ); // median of angular range T median = odfValuesInAngularRange[floor(quantile*(double)numInRange+0.5)]; // compute and return final value if(mode > median) { return mode/median - 1.0; } else { return median/mode - 1.0; } } /** * Calculate Normalized Entropy */ template < typename T, unsigned int N > T itk::OrientationDistributionFunction ::GetNormalizedEntropy() const { double mean = 0; for( unsigned int i=0; i OrientationDistributionFunction OrientationDistributionFunction ::PreMultiply( const MatrixType & m ) const { Self result; typedef typename NumericTraits::AccumulateType AccumulateType; for(unsigned int r=0; r::ZeroValue(); for(unsigned int t=0; t( sum ); } } return result; } /** * Post-multiply the Tensor by a Matrix */ template OrientationDistributionFunction OrientationDistributionFunction ::PostMultiply( const MatrixType & m ) const { Self result; typedef typename NumericTraits::AccumulateType AccumulateType; for(unsigned int r=0; r::ZeroValue(); for(unsigned int t=0; t( sum ); } } return result; } /** * Print content to an ostream */ template std::ostream & operator<<(std::ostream& os,const OrientationDistributionFunction & c ) { for(unsigned int i=0; i::PrintType>(c[i]) << " "; } return os; } /** * Read content from an istream */ template std::istream & operator>>(std::istream& is, OrientationDistributionFunction & dt ) { for(unsigned int i=0; i < dt.GetNumberOfComponents(); i++) { is >> dt[i]; } return is; } } // end namespace itk #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkPointShell.txx b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkPointShell.txx index b1f3a8b3b3..6f9efcb9aa 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkPointShell.txx +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkPointShell.txx @@ -1,5247 +1,5242 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkPointShell_txx__ #define __itkPointShell_txx__ #include "vnl/vnl_vector_fixed.h" #include "vnl/vnl_matrix_fixed.h" -#define _USE_MATH_DEFINES -#include - namespace itk { -#define DIST_POINTSHELL_PI M_PI - template TMatrixType * PointShell ::DistributePointShell() { auto theta = new vnl_vector_fixed(); auto phi = new vnl_vector_fixed(); - double C = sqrt(4*DIST_POINTSHELL_PI); + double C = sqrt(4*itk::Math::pi); (*phi)(0) = 0.0; (*phi)(NPoints-1) = 0.0; for(int i=0; i0 && i(); for(int i=0; i class PointShell<12, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*12] = { 0.250254,0.320072,0.437594, 0.550563,0.623196,0.975676, -0.250253,-0.320073,-0.437593, -0.550563,-0.6231950000000001,-0.975676, -0.916162,-0.078054,-0.598061, 0.758025,0.436642,-0.177816, 0.916161,0.078054,0.598061, -0.758024,-0.436643,0.177817, -0.313083,-0.944171,0.671442, -0.349682,0.648821,-0.128204, 0.313082,0.944172,-0.671442, 0.349683,-0.648822,0.128204 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<42, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*42] = { 0.9756767549555488,0.9397937974509784,0.6231965669156312, 0.8971014254962764,0.6899186612252972,0.5505632701289798, 0.4375940376503738,0.06907711216819172,-0.3200730131503601, 0.6235168560971455,0.1781713819192711,0.3200724178002418, 0.7616224484252742,0.5117469206751248,0.2502538949373057, 0.4043068999720243,0.7205836671095894,0.8306996157029517, -0.4375932291383154,-0.06907715072573903,0.06640211724156461, -0.6231952788307174,-0.2192092047974508,-0.1781713502362079, 0.3352293490184233,-0.2502532651867688,-0.4043060780204903, 0.1765178736510398,-0.975676581463901,-0.9397935597648752, -0.8306992108488227,-0.6235158879179115,0.21921067448898, -0.7616223457476226,-0.3352294641675141,-0.7205834943870382, -0.5117470172320778,-0.06640153973221416,-0.5505634949474448, -0.176517304179968,-0.6899183532182501,-0.8971016370404784, -0.1778161375899129,0.1521341729788544,0.4366423972091847, 0.3410385404813853,0.7022080829625521,0.7580253719184177, -0.5980610514568759,-0.3056524524744078,0.07805400320688782, -0.09487972868743588,0.3025309778153461,-0.07805410188638827, -0.1503967869358663,0.3996773221825281,-0.9161616153729887, -0.8900379255247358,-0.6430242175142256,-0.4560492515462431, 0.5980613131647216,0.3056526230838691,0.797087559189558, -0.4366431953633789,-0.7951587738972527,-0.3025315118038733, -0.5843857415998933,0.9161619708326186,0.8900381161951118, 0.9840623116657442,0.1778171059718252,-0.1521342296531137, 0.4560499397602033,0.09487917246041,0.7951586652134927, 0.1503972383762518,0.584385354546882,0.6430245879023526, -0.399676350745083,-0.7970870947583816,-0.7580246814516085, -0.9840624137666333,-0.7022083677250226,-0.3410374453295617, -0.1282040992012935,0.3060111300013609,0.6488215902264565, -0.2808945465452747,0.1758296024995542,-0.3496821715684524, 0.6714420577705413,0.9496341036793501,0.9441720387917811, 0.7760312926982584,0.9363385958760152,-0.9441722324566724, -0.6303269409870232,-0.7605084662990487,-0.3130828685601688, 0.2106381298878163,-0.2593820240207756,0.3193074202631062, -0.671442351589465,-0.9496340459616871,-0.6002017842451572, -0.648822290296783,-0.5653935344751363,-0.9363384293729856, -0.7389956620788968,0.3130823317650696,-0.2106389019045293, -0.02151295053653907,0.1282040764044601,-0.3060118317848469, -0.3193074905789999,-0.7760321386045594,0.5653931175085022, 0.6303269573377657,0.7389959159190775,0.2593815856416469, 0.7605089118538748,0.6002024649156598,0.3496833143594963, 0.02151295276860444,-0.175829673802738,0.2808952005684321 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<92, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*92] = { 0.9756767549555487,0.9881046683022421,0.8528238339140162, 0.6231965669156312,0.9602286065935876,0.9016229543668698, 0.68966672282401,0.7970712198937139,0.6617902410567804, 0.5505632701289798,0.4375940376503739,0.2130515669413184, -0.07773888316805401,-0.3200730131503601,0.575075565563677, 0.3107083252171692,-0.006505365777837463,0.6463093264667951, 0.3555189519163455,0.3200724178002418,0.6201479032959075, 0.8717669667532965,0.4569904905821254,0.7744719807282636, 0.5454521871592555,0.2502538949373057,0.3600408236839538, 0.4319414682718342,0.5665551499276381,0.697797754038698, 0.7103565350090897,0.8449704612165639,0.9168712118355732, -0.4375932291383153,-0.213051272338593,0.07773856031405026, -0.1245894223683107,0.181648017057727,0.2546621526726748, -0.6231952788307172,-0.3823142165271753,-0.04708691012811009, -0.3555183995893311,-0.02217694060430831,0.3149362814931866, 0.006504983859799645,0.3417324928044827,-0.2502532651867688, -0.3600400745299253,-0.4319406005716204,0.01921174674137865, -0.05758606025521316,0.3265627679363224,-0.9756765814639009, -0.988104613901425,-0.8528232434185344,-0.9168708930751739, -0.854235225343551,-0.6463082287051728,-0.7103560109842373, -0.5750746905699001,0.3823157740702657,0.0470881327099307, 0.3873829237054168,-0.6201479767677171,-0.8717667933239607, -0.3417327749428254,-0.6485010946606566,-0.8449702640289916, -0.3149361858566655,-0.5665549223826651,0.854235631268382, -0.4569906864326022,-0.1816479322770386,0.1245901828483064, -0.5454522916908328,-0.2546618495309371,-0.5505634949474448, 0.6485010946606566,0.05758689641252078,-0.01921097131735245, -0.3265625234832059,-0.3107079508004592,-0.3873820250532686, -0.6896660583043277,-0.6617902107749167,-0.6977972953381247, -0.9016230372124288,-0.9602286916122059,-0.7970714969272577, 0.02217737847260294,-0.774471897882643,-0.1778161375899129, 0.03109142233089637,0.2669187829195654,0.4366423972091846, 0.1544374088220477,0.4265382311030135,0.6260922126282031, 0.5136107375104821,0.7494380454402638,0.7580253719184178, -0.5980610514568761,-0.4291113361140411,-0.1696203080333493, 0.07805400320688781,-0.2914862882507431,-0.03496909012717316, 0.2274959035287995,0.1056298518860434,0.3651211825280409, -0.07805410188638827,-0.1281592432697842,-0.1664476243728032, 0.2310143008479173,0.2106389321639752,0.5518992169467314, -0.9161616153729886,-0.9327747060225072,-0.8106886470978539, -0.771485918473268,-0.7097589298055721,-0.5273136421678301, -0.4881111164997092,-0.3660248731570757,0.5980613131647216, 0.4291115157626469,0.1696204410073287,0.7499962909367706, 0.5360957563846484,0.8113900261915854,-0.4366431953633788, -0.6867851479477911,-0.8708228680516066,-0.365121777041269, -0.6002019152270697,-0.7331976694123558,-0.2274963530521035, -0.4115345048612673,0.9161619708326186,0.9327749805257903, 0.8106887957474585,0.9941684586829005,0.9531387910864955, 0.9334760373935599,0.1778171059718252,-0.03109104889813258, -0.266919222145987,0.3660256667153609,0.1422988506889488, -0.1056306282458797,0.5273142067726671,0.2914860445503094, 0.6867847261588661,0.8708230332223915,0.8854287054759334, 0.1281594836202007,0.1664482855753907,0.4115339986401247, 0.4916319868779721,0.4881116080953447,0.7331975726799166, 0.7714862700692517,-0.1422987785572308,-0.2310136280273065, -0.5360950867028047,-0.749995864312011,-0.551898166785698, -0.8113895155772442,-0.7580246814516086,-0.4916319868779721, -0.9531387450388693,-0.9941684373181742,-0.9334760519103793, 0.03496867572798409,-0.885428988462601,-0.6260926899993671, -0.7494380619781833,0.7097593165624652,-0.4265380492676017, -0.154436271103469,-0.513609764630649,0.6002015742230613, -0.2106379566061765,-0.1282040992012935,0.1506070646973712, 0.448827218017063,0.6488215902264565,-0.232615798766268, 0.07170206110196441,0.3638246180723771,-0.3176184514965804, -0.01939827024983318,-0.3496821715684524,0.6714420577705416, 0.8777656242085763,0.9824386073167399,0.9441720387917811, 0.7644107780855932,0.9498618317294465,0.9737573075945113, 0.7557291769626011,0.8604026946131842,-0.9441722324566724, -0.7739455965390631,-0.4607792790679794,-0.8589482430980673, -0.5965101770495287,-0.6307845637432553,-0.3130828685601688, 0.01737679733452725,0.3952347219739472,-0.2895250242862085, 0.09654302677976184,0.4661908578614318,-0.2185691140597737, 0.1592889611288683,-0.671442351589465,-0.8777656078901646, -0.982438609905361,-0.649602216294724,-0.8243817913354027, -0.5261115978506249,-0.6488222902967828,-0.6181925269683732, -0.4893366483035592,-0.8604026705465684,-0.7995410210009178, -0.6026908960383706,-0.9737572051250643,-0.8449013283633929, 0.3130823317650696,-0.01737758434298078,-0.3952353653550799, 0.1061130743462423,-0.2970021727032599,-0.1482539922220807, 0.1282040764044601,-0.1506074987015245,-0.4488280788164578, -0.159288972427813,-0.5000332157715695,-0.7557300072673856, -0.4661910177108468,-0.7644115292814133,0.6181920323103048, 0.4893362367199527,0.2568082513095248,0.7739454978673308, 0.4607793683387657,0.8449014608184471,0.5811577408091089, 0.2185687785328751,0.6026910636921159,0.2895245326714229, 0.5000325428335932,0.8589483198537152,0.8243822455102904, 0.6496025629973337,0.6307853921801013,0.5261125320735169, 0.3496833143594963,-0.5811577408091089,0.2970021583546204, -0.1061134148983228,0.1482544392795429,-0.9498619694604108, -0.2568086311939767,-0.3638250562458945,0.01939866440958929, -0.0965434988571485,-0.07170210104888378,0.2326162031595532, 0.3176193294845107,0.7995412648414353,0.5965106290979496 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<162, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*162] = { 0.9756767549555488,0.9968737649581846,0.9397937974509784, 0.7989268505692991,0.6231965669156312,0.9764792677814802, 0.9657129396229355,0.8567905682573015,0.679558714000995, 0.8971014254962764,0.8343457228904876,0.6899186612252972, 0.7377425905504456,0.6387690340353133,0.5505632701289798, 0.4375940376503738,0.2787444080680243,0.06907711216819172, -0.1467487371786262,-0.3200730131503601,0.5436064763029955, 0.3641181930198064,0.1299861910861632,-0.09463328683104604, 0.6235168560971455,0.421472539809791,0.1781713819192711, 0.6478378002177454,0.4350913076613168,0.3200724178002418, 0.543583002477908,0.7616224484252742,0.9117594243665534, 0.4242146396073646,0.6694499319211135,0.8720427891122669, 0.5117469206751248,0.7406754453578036,0.5536544075493044, 0.2502538949373057,0.3336803609057232,0.4043068999720243, 0.438887490788423,0.4847692553550146,0.5913899314506929, 0.6492812914734455,0.6425800233106177,0.7205836671095894, 0.8155579470101821,0.8306996157029517,0.8921547074343706, 0.9447583574027534,-0.4375932291383154,-0.278743955138478, -0.06907715072573903,0.1467483093137361,-0.2140239599986307, -0.001406336898152031,0.2327251986251803,0.06640211724156461, 0.3039508137298482,0.3409081262151013,-0.6231952788307174, -0.4546947222788382,-0.2192092047974508,0.03581973661208818, -0.4350905112236564,-0.2089153242611869,0.0609953219830862, 0.3006812043050254,-0.1781713502362079,0.08257031568712175, 0.3352293490184233,0.09463277722176672,0.3398901915912972, -0.2502532651867688,-0.3336796480610522,-0.4043060780204903, -0.4388866188864613,-0.05621340666038893,-0.1197552832780665, -0.1776465335448071,0.1765178736510398,0.1277105869872963, 0.3935116760630417,-0.975676581463901,-0.9968737272214637, -0.9397935597648752,-0.7989260596435309,-0.9447580743829742, -0.9308031701048347,-0.8218804228775519,-0.6478366382006043, -0.8306992108488227,-0.7645261844287355,-0.6235158879179115, -0.6425794288509028,-0.543605629081954,0.4546962564744617, 0.21921067448898,-0.03581866880653414,0.4779576265881325, 0.2080468003445843,0.4555126388719097,-0.543583197615373, -0.7616223457476226,-0.9117592443356049,-0.3398905505309818, -0.5766491384642831,-0.7792417673233771,-0.8921544952781482, -0.3352294641675141,-0.5550737397870624,-0.7205834943870382, -0.3006809838192767,-0.4847689570296102,0.8218810203164033, 0.9308033931946534,0.7645267056644409,-0.4242149221746557, -0.2327253235991419,0.00140664545831139,0.2140247669633664, -0.5117470172320778,-0.3039507303469009,-0.06640153973221416, -0.5536545418185335,-0.3409079709201472,-0.5505634949474448, 0.7792418677670352,0.5766490604842006,0.5550737528785307, 0.1776474313398276,0.1197561650892531,0.05621421006516834, -0.1277100030888262,-0.176517304179968,-0.3935115910448358, -0.1299862309792015,-0.3641176806190365,-0.4214718858755578, -0.2080457563809749,-0.477956563666828,-0.6795578679273953, -0.4555121490743896,-0.6899183532182501,-0.6387691005409376, -0.6492806993353901,-0.5913894852863821,-0.8155575864896008, -0.8567903368462096,-0.9657130735429578,-0.9764792804081987, -0.834345897568434,-0.8971016370404784,-0.7377428728490714, -0.06099477310985241,0.2089161290281741,-0.08257033948198818, -0.8720426859708773,-0.6694498734011487,-0.740675476302381, -0.1778161375899129,-0.02718233037986957,0.1521341729788544, 0.3178872248051531,0.4366423972091847,0.06305939500089614, 0.2592762165236786,0.4491542846588709,0.5806638189836385, 0.3410385404813853,0.5484671467389891,0.7022080829625521, 0.5886123721388867,0.761147081998915,0.7580253719184177, -0.5980610514568759,-0.4818747107897384,-0.3056524524744078, -0.102179801407443,0.07805400320688782,-0.3811861908796716, -0.2105722545208477,-0.001641189844283069,0.1883562467232593, -0.09487972868743588,0.1091686599118738,0.3025309778153461, 0.1998858588754617,0.3897335832229792,-0.07805410188638827, -0.1156801299839266,-0.1503967869358663,-0.1717048170733874, 0.1470965334761953,0.1310545667952879,0.100226360425432, 0.3996773221825281,0.3894173756029716,0.6166247865338805, -0.9161616153729887,-0.93968222548459,-0.8900379255247358, -0.7610423024299814,-0.8216809647863927,-0.8059784419089511, -0.7076799521032789,-0.5537209611836944,-0.6430242175142256, -0.577817181345379,-0.4560492515462431,-0.4070385900414059, -0.3177184789930138,0.5980613131647216,0.4818748986235024, 0.3056526230838691,0.1021798945147061,0.7166389355513205, 0.5797448495418049,0.3708138719979267,0.797087559189558, 0.6291765311790336,0.8064720598658887,-0.4366431953633789, -0.6250679008877252,-0.7951587738972527,-0.894357332660672, -0.3897342152274581,-0.5770900179397438,-0.7252694054566711, -0.7936687210308288,-0.3025315118038733,-0.4662800296194353, -0.5843857415998933,-0.1883566309643827,-0.3230016809603438, 0.9161619708326186,0.9396825386256142,0.8900381161951118, 0.7610424573388179,0.98459889389352,0.985272860446651, 0.8869743499570832,0.9840623116657442,0.9364058649128151, 0.8957919700911449,0.1778171059718252,0.02718289859111821, -0.1521342296531137,-0.3178878068603045,0.31771932061924, 0.1597779786826127,-0.03010074909289372,-0.1998866892639353, 0.4560499397602033,0.2896405921772683,0.09487917246041, 0.5537214556082512,0.3811861035860583,0.6250673427588886, 0.7951586652134927,0.8943575917970892,0.7872122199392447, 0.9353918603084618,0.8865248509843126,0.1156802730367007, 0.1503972383762518,0.1717055726654157,0.3230012075937738, 0.3862979863651042,0.4171265008378126,0.4070391829394841, 0.584385354546882,0.6452876111171104,0.6430245879023526, 0.7936687790069655,0.8216813253645509,0.03010042800399446, -0.159777601870924,-0.2896404384341761,-0.1470960585711635, -0.3708132697350703,-0.5797443521912047,-0.7166385361913998, -0.399676350745083,-0.6291757405426562,-0.7970870947583816, -0.6166237809711579,-0.8064715143730887,-0.7580246814516085, -0.417126245587897,-0.3862981411371962,-0.6452875761129986, -0.8869742038831786,-0.9852727972566903,-0.9845987728990332, -0.9364058061885231,-0.9840624137666333,-0.8957918541675716, 0.001640881330767951,0.2105720065923513,-0.109169268647464, -0.9353919526972201,-0.7872126141915534,-0.5806643774075225, -0.8865251005609038,-0.7022083677250226,-0.7611469342493191, 0.707680317140174,0.8059787882713181,0.577817672809353, -0.4491544873380491,-0.2592757174096171,-0.063058272648828, -0.5484668466671915,-0.3410374453295617,-0.5886114742048517, 0.7252693007743299,0.5770896067011005,0.4662795459560942, -0.1002253860034311,-0.1310538163159151,-0.3894163023521484, -0.1282040992012935,0.07418771901881034,0.3060111300013609, 0.5105522497698926,0.6488215902264565,-0.2061837828129686, 0.01320461245153456,0.2533186742384155,0.4483631157317138, -0.2808945465452747,-0.05523589088936585,0.1758296024995542, -0.3305621657917769,-0.1123798946526014,-0.3496821715684524, 0.6714420577705413,0.8307215647087252,0.9496341036793501, 0.983882155708098,0.9441720387917811,0.7477894668983293, 0.9072360592139173,0.9915144459980433,0.9775308002021622, 0.7760312926982584,0.9002461340540874,0.9363385958760152, 0.7350862725086435,0.8116669810370153,-0.9441722324566724, -0.8313456723553714,-0.6303269409870232,-0.3731115220313701, -0.8935348618728204,-0.7312055040636086,-0.4790574606800003, -0.7605084662990487,-0.5474979380988695,-0.5596790773588694, -0.3130828685601688,-0.07519795113003093,0.2106381298878163, 0.4776948653078811,-0.299731815412124,-0.02562616156907596, 0.2786088475509629,0.5296074119458033,-0.2593820240207756, 0.03150460300683353,0.3193074202631062,-0.1959070295313489, 0.08053951964108649,-0.671442351589465,-0.8307216077305004, -0.9496340459616871,-0.9838822098556939,-0.6637939293172054, -0.8147968652653733,-0.8990751104656249,-0.6002017842451572, -0.7153675946298389,-0.4830988161196208,-0.648822290296783, -0.6344626299567083,-0.5653935344751363,-0.4459169294668335, -0.8116671044980045,-0.7895071237697285,-0.6857578728711079, -0.5288485365725882,-0.9363384293729856,-0.8807752703984302, -0.7389956620788968,-0.9775307754986758,-0.8832579304805722, 0.3130823317650696,0.07519720122433592,-0.2106389019045293, -0.4776954195825031,0.1655447705464922,-0.1220494268493898, -0.4262841911656161,-0.02151295053653907,-0.3268548028226648, -0.2066526242811376,0.1282040764044601,-0.07418801789892716, -0.3060118317848469,-0.51055312502423,-0.08053951945115613, -0.3287498381001709,-0.5688642328312289,-0.7350870708018485, -0.3193074905789999,-0.5758541835274278,-0.7760321386045594, -0.5296076162752261,-0.7477901272839138,0.6344620803203709, 0.5653931175085022,0.4459164955009389,0.389683753785766, 0.2859346018498913,0.08112906023855893,0.831345524857385, 0.6303269573377657,0.3731116142445962,0.8832579654623612, 0.7198955735651289,0.467747528435525,0.1959067638111305, 0.7389959159190775,0.5248781214125594,0.2593815856416469, 0.5288485749239231,0.2997313094215869,0.5688633866559212, 0.3287493895928871,0.5758535688432742,0.8935348059013593, 0.8990753265127792,0.8147972186077681,0.6637941002839092, 0.7605089118538748,0.7153683254339585,0.6002024649156598, 0.5596800524090022,0.4830998363360157,0.3496833143594963, -0.4677475887278177,-0.7198955529773995,-0.5248781506021705, 0.4262841209617862,0.1220490717256491,-0.165545217365127, 0.326855199205205,0.02151295276860444,0.2066532886754674, -0.9915144412787341,-0.9072363224101446,-0.9002463663906001, -0.2859350592025221,-0.3896842610441149,-0.4483636748755489, -0.0811290830782055,-0.175829673802738,0.1123805173372347, -0.2786093002807504,0.02562556437243592,-0.03150492197167008, -0.253319097564816,-0.01320461850800903,0.2061840662709727, 0.05523623192861417,0.2808952005684321,0.3305631346577989, 0.6857580324047194,0.78950721141067,0.8807755242176112, 0.4790578522946484,0.7312056921497674,0.5474986596025796 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<252, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*252] = { 0.9756767549555488,0.9977154378498742,0.9738192119472443, 0.8915721200771204,0.7646073555341725,0.6231965669156312, 0.9817040172417226,0.9870396762453547,0.9325589150767597, 0.8173592116492303,0.6708930871960926,0.9399233672993689, 0.9144882783890762,0.8267930935417315,0.6931818659696647, 0.8407280774774689,0.782394344826989,0.6762337155773353, 0.7005607434301688,0.6228579759074076,0.5505632701289798, 0.4375940376503738,0.3153040621970065,0.1569517536476641, -0.01984099037382634,-0.1857690950088067,-0.3200730131503601, 0.5232435944036425,0.3889403678268736,0.2135250052622625, 0.02420694871807206,-0.1448539951504302,0.5971534158422009, 0.4482053228282282,0.2597018771197477,0.06677517278138323, 0.6404616222418184,0.4782876117785159,0.2868761951248767, 0.6459894362878276,0.4789651252338281,0.3200724178002418, 0.4973180497018747,0.6793811951363423,0.8323587928990375, 0.9308933612987835,0.4036036036586492,0.5984781165037405, 0.7817280923310203,0.9140795130247613,0.4809905907165384, 0.6759621154318279,0.8390728924802671,0.5347729120192694, 0.7094340284155564,0.5560356639783846,0.2502538949373057, 0.3171352000240629,0.3793963897789465,0.4231100429674418, 0.4410301813437042,0.4357529867703999,0.5208717223808415, 0.5850086433327374,0.611055499882272,0.6009463532173235, 0.6305067000562991,0.7188806066405239,0.7654898954879897, 0.7616477696596397,0.7997756996573342,0.8700831379830764, 0.8872031228985237,0.9155019734809123,0.9568003701205341, -0.4375932291383153,-0.3153035222278598,-0.1569515927579475, 0.0198407706589918,0.1857686171195431,-0.2644927501381796, -0.1064219080255857,0.07849995612144045,0.2583107784678281, -0.04938676750055992,0.1358448755096817,0.3243479900672576, 0.1811879481039926,0.3692668145365748,0.3890115016151001, -0.6231952788307174,-0.4943551945928708,-0.319458133528771, -0.1156489798772063,0.08328895892415776,-0.4789641985801549, -0.3127252940830145,-0.1059392282183739,0.1077444781964869, 0.2912280153186658,-0.2868758523956744,-0.08856892011805101, 0.1287405357080231,0.3245517154572714,-0.06677541204276306, 0.1413542883070481,0.3408430926744944,0.1448534358763926, 0.3374016489097037,-0.2502532651867688,-0.3171345072414974, -0.3793956104585266,-0.4231091882680272,-0.4410293135613324, -0.09929959410007272,-0.1535127609134815,-0.2052877394623771, -0.2436963810571767,0.08175409117371149,0.04056025153798869, -0.006048944565669369,0.2686152102237028,0.2319923070602857, 0.430309819720559,-0.975676581463901,-0.9977153903038788, -0.9738191090293654,-0.8915716840571059,-0.7646064477130079, -0.9568001079664734,-0.9598482725023617,-0.9044523389503778, -0.7901672201648241,-0.6459882395962464,-0.8872027729137049, -0.8582754834679532,-0.7705800268610806,-0.6404605781008121, -0.7616472974254324,-0.7008201753656432,-0.5971525097007422, -0.6009457148226922,-0.5232427588825813,0.4943566966479628, 0.3194596781650836,0.1156503154178581,-0.0832879858164388, 0.5222841738261358,0.3225497922064885,0.1018140973507329, 0.5217885230992481,0.3044789836562512,0.4873191346491355, -0.4973183240635209,-0.6793811856410323,-0.8323586364840968, -0.9308931819742911,-0.3374020539278631,-0.5261951664998159, -0.7070125356849136,-0.8417962075837926,-0.9155017573317124, -0.3408433114184408,-0.5265312606271311,-0.6896418460594331, -0.7997755164970677,-0.3245517106425898,-0.4925847482169691, -0.6305065080228541,-0.2912277152063287,-0.4357526334612896, 0.7901679726328494,0.9044526665335126,0.9598484396937114, 0.7705806468939737,0.858275831469383,0.7008207681995118, -0.4036039458806759,-0.2583110138480089,-0.0784999126587471, 0.1064223584250461,0.264493571710179,-0.4809907334514471, -0.3243480295764106,-0.1358446002697818,0.04938746901646566, -0.5347730026038946,-0.3692667658371347,-0.1811875286592425, -0.5560358190148772,-0.3890114324926668,-0.5505634949474449, 0.8417963565884857,0.7070125813068046,0.5261950179989611, 0.6896418985458221,0.5265311900255359,0.4925848265160583, 0.2436972866599269,0.2052886581368649,0.153513629451971, 0.09930039009433847,0.006049691633511915,-0.04055950638179381, -0.08175337578691833,-0.2319919155781195,-0.2686148310916902, -0.430309819678344,-0.02420720081803753,-0.2135248270679241, -0.3889397838050994,-0.2597016312374675,-0.4482046405142344, -0.4782867918076852,-0.1018130528605821,-0.322548598821141, -0.5222830294256716,-0.6708921376896406,-0.304478224282928, -0.5217878437313506,-0.6931813485878851,-0.4873188675145023, -0.6762335873429084,-0.6228580878699612,-0.6110548409057, -0.5850080622199078,-0.5208712693637837,-0.7654894328832393, -0.7188802647693375,-0.8700828159137221,-0.8173587433845655, -0.9325588839421305,-0.9870397834787261,-0.9817039872478999, -0.8267930492778305,-0.9144884914916022,-0.9399235077793813, -0.7823945479956939,-0.8407283372889187,-0.7005610213599369, -0.1077438933887955,0.1059400956623477,0.3127262866621893, -0.1287403742204129,0.08856921814263634,-0.1413545191115968, -0.9140794058749131,-0.7817279594934516,-0.5984781448346268, -0.8390728949381593,-0.6759620794963979,-0.709434131000089, -0.1778161375899129,-0.06053925384414331,0.07929679392711581, 0.222673458561735,0.3458247516791153,0.4366423972091846, 0.01030826616734189,0.1591522280204451,0.3173816763430465, 0.4549463955350546,0.5521270265729551,0.2292788658415479, 0.3973400932411465,0.5502139834879405,0.6594089221868847, 0.4476465561008348,0.6096570464011057,0.7343998566036512, 0.629214796874201,0.7646693979379596,0.7580253719184178, -0.5980610514568761,-0.5101530988159087,-0.382225667160838, -0.2244621267538426,-0.06301328229424107,0.07805400320688782, -0.4311039309963852,-0.3079662136138592,-0.1501157132113724, 0.01750888497279251,0.1650825345160538,-0.2148810450151756, -0.06090095222676627,0.1073128739652992,0.2584097661066967, 0.02655484252908358,0.1901297170957776,0.3420822257932489, 0.2531835106264871,0.4022303494272352,-0.07805410188638827, -0.1080255529483224,-0.1376217050758367,-0.1609000070073124, -0.1740018618448228,0.09827676798573926,0.083291898217249, 0.06127443921955168,0.03526739273256396,0.2991139104294396, 0.2941068360088736,0.2692865316145088,0.4942032775296958, 0.4857723178878524,0.6512069539966677,-0.9161616153729886, -0.9396953110011561,-0.9204280785344878,-0.8462030522374957, -0.7293237120999879,-0.8470541513588044,-0.8482966176587544, -0.7977006542517769,-0.6951661565374421,-0.566558592627622, -0.7243096319272092,-0.6931460376496088,-0.6140043047773551, -0.5016343691560573,-0.5520254073275178,-0.4928644880867128, -0.403575153350467,-0.3587591578566765,-0.2886351685087218, 0.5980613131647216,0.5101532951859686,0.382225843595672, 0.2244622808787926,0.06301334452030186,0.6944632949786616, 0.5955168212825119,0.4473425940100297,0.2700417838303327, 0.7724043956082883,0.6553545192922715,0.4871408620353512, 0.8097301284690857,0.6725220182496192,0.8002534097038426, -0.4366431953633789,-0.5869882376922511,-0.7332080507197046, -0.8450980113065225,-0.9041113586460733,-0.4022310083998925, -0.554596445154436,-0.6925605687496104,-0.7854318984598006, -0.8250621271173465,-0.3420827953668352,-0.4840440064641756, -0.6033456975789954,-0.6777531805937266,-0.2584102557043402, -0.3819753792546441,-0.4821906665520286,-0.1650828712784331, -0.270790845781693,0.9161619708326184,0.9396956424389374, 0.9204283182965946,0.8462032095340455,0.7293238793541417, 0.9749588444840027,0.9879501207294071,0.942053498973333, 0.8348196077814718,0.9950795014807369,0.9818515654328379, 0.9027098746674149,0.9581801446138297,0.9118246030313639, 0.8703772282258925,0.1778171059718252,0.06053992567271226, -0.07929659020903117,-0.2226737578340799,-0.345825401239635, 0.2886360377097776,0.1672516508448342,0.02000533874392893, -0.1285435155191929,-0.2531843553864728,0.403575906447316, 0.2774342678683828,0.1245598363284875,-0.02655554762561945, 0.5016349858535857,0.3695530582277636,0.2148806720954671, 0.5665590425344393,0.431103930292903,0.5869876102086139, 0.7332077514676827,0.845098078457225,0.9041116580482536, 0.7182616282077119,0.8617334421407644,0.9490975365686583, 0.8223898048944452,0.9416915744235097,0.8729720010540123, 0.1080256414522809,0.1376220280275969,0.1609005865750696, 0.1740026689030255,0.2707904196202965,0.3196768235430837, 0.3552546724685221,0.3677018240803483,0.3587598208776521, 0.4821901792282771,0.5389508449256169,0.5637713635689835, 0.5520258363563475,0.6777529577987501,0.7231337276202411, 0.724309982145211,0.8250622687013296,0.8470545173149734, 0.1285429999155006,-0.02000532948058562,-0.1672511147059996, -0.1245600244829796,-0.2774338902981233,-0.3695528631494325, -0.09827641615811868,-0.2700412859530667,-0.4473420975374328, -0.5955164071695848,-0.6944629164413806,-0.2991130971968019, -0.4871400501186961,-0.6553538941234454,-0.7724039524031648, -0.4942022299541438,-0.6725212074710563,-0.8097296395344389, -0.6512059956089504,-0.8002528392148971,-0.7580246814516085, -0.3677014077761052,-0.3552545716101517,-0.3196770257819652, -0.5637712030900536,-0.5389510214534028,-0.7231336172569296, -0.8348194119106425,-0.9420533966954356,-0.9879499956150448, -0.9749586635216289,-0.9027097279159257,-0.9818515951566739, -0.9950795477220543,-0.9118244750171576,-0.9581802235578871, -0.8703770126934449,-0.0175091339170676,0.1501155140512474, 0.3079660822386824,-0.1073133727582037,0.06090046334304851, -0.1901304002696938,-0.9490974969653682,-0.8617336589899791, -0.7182621005240754,-0.5521276321758419,-0.941691783045487, -0.8223901593137167,-0.6594093292610237,-0.872972144171723, -0.7343999908188845,-0.7646691446910742,0.6951665021597787, 0.7977009700656229,0.8482969664746548,0.6140047811934269, 0.6931464276818936,0.4928650597255946,-0.4549467775084718, -0.3173815862988101,-0.1591515620353438,-0.01030716362341688, -0.5502140363721867,-0.3973395475484636,-0.2292777334167206, -0.609656670182737,-0.4476455277450017,-0.6292139442700462, 0.7854319364049284,0.6925603649758249,0.5545959620739339, 0.6033453603619342,0.4840435291285519,0.3819748711371402, -0.03526641653115874,-0.06127364342066123,-0.0832913202753871, -0.2692854573778917,-0.2941058574917593,-0.4857712605383084, -0.1282040992012934,0.02998172476739921,0.2130449739264662, 0.394354771181159,0.5438573645627299,0.6488215902264565, -0.1901340637026595,-0.02057293935230464,0.1720544722828635, 0.3534794142829396,0.4950335464190314,-0.252933321812349, -0.07636778766011496,0.1169519253626288,0.2909961752861106, -0.304612640171253,-0.1271903099934383,0.0580070042064605, -0.3366056805211806,-0.1653138037361849,-0.3496821715684524, 0.6714420577705413,0.8002044514563711,0.9106424580428781, 0.9742808059046055,0.9805708386415104,0.9441720387917811, 0.7350956003099328,0.8682639008659977,0.9653353535299492, 0.9995536316680411,0.9755844796257857,0.7728091190204586, 0.8918537226509272,0.9597077065970592,0.9637247890765801, 0.767530944505584,0.857374860312736,0.8948082473172733, 0.7201359303293944,0.7802583897718675,-0.9441722324566724, -0.8608166107545396,-0.7207644955095487,-0.5303678229575245, -0.3211867088850157,-0.9096404828216634,-0.7967975927156801, -0.620601831095295,-0.4039985827676406,-0.8241231220089414, -0.6757043639889994,-0.4726959329165278,-0.6854057579633669, -0.5106159168102177,-0.5164821811548767,-0.3130828685601688, -0.128054626578418,0.09418349997750548,0.3239109228229815, 0.523048087763098,-0.3043330399192493,-0.09531787499064681, 0.146419102006115,0.3786227553496849,0.5638039035645359, -0.2789925774668332,-0.05252850546893189,0.1924160430438771, 0.4101897544477446,-0.2358533016570041,-0.006318969895916147, 0.2236016867495729,-0.1820659309330632,0.03496843200875603, -0.6714423515894649,-0.8002045390283683,-0.9106424117173109, -0.9742807748705438,-0.9805709251787237,-0.6691519386893557, -0.79626257796142,-0.8909109722488041,-0.9275521423149625, -0.6332080201962388,-0.7430051304270751,-0.8108589038018792, -0.5581290590099237,-0.6413705283620924,-0.4563600901355626, -0.648822290296783,-0.6411378559950974,-0.600293640880965, -0.5219527418637842,-0.4191009430775375,-0.7802586188950914, -0.7711197529973893,-0.713538182957094,-0.6094980396194888, -0.4842093859996422,-0.8948081394501657,-0.8705497953564927, -0.7870195954857328,-0.6597854273844109,-0.9637246412193355, -0.9132982945459158,-0.8070428410352181,-0.975584505681221, -0.9015722073987464,0.3130823317650696,0.1280539101236855, -0.09418429615654819,-0.3239116283455282,-0.523048586255087, 0.1989845274738189,-0.01970764286946627,-0.2653151882168217, -0.4936479477555078,0.05597369301027853,-0.1852629737758222, -0.4302072668465533,-0.09867461327158224,-0.3387557568094476, -0.2393260112020272,0.1282040764044601,-0.0299819504088954, -0.2130455201840074,-0.3943555879655132,-0.5438582278251758, -0.03496843048443223,-0.2252069693946209,-0.4261053308619027, -0.5992598174372795,-0.720136706807033,-0.2236017161594696, -0.4317330442416767,-0.6250530132529536,-0.7675317913865697, -0.4101898771205939,-0.6101488498350025,-0.7728099228904255, -0.5638041319099237,-0.7350961954485662,0.6411372723067146, 0.6002931843810706,0.5219523372221417,0.4191004905746099, 0.4596949806286311,0.3916338931244087,0.2980734064957148, 0.226741584116328,0.1432114770939381,-0.02097489882147943, 0.8608164411414747,0.7207644427956729,0.5303678926086439, 0.3211867913977836,0.9015721838250796,0.7879881821713033, 0.6114960278478284,0.3951892122332402,0.1820657113417612, 0.8070430398170311,0.6574928275930984,0.4544842943197335, 0.2358529185889448,0.6597856586149884,0.4841878538357612, 0.2789921022280572,0.4842093252521232,0.3043325272261384, 0.5992589358516202,0.4261046359672609,0.2252066549797059, 0.6250522113657903,0.4317325950511361,0.6101482870567641, 0.9096403689902206,0.9275522217134882,0.8909112253661301, 0.796262827475376,0.6691520068054228,0.8241233338640371, 0.810859375773786,0.7430057321681839,0.6332085061147845, 0.6854064426268304,0.6413714065577412,0.5581299045184589, 0.5164832226272315,0.4563611494403301,0.3496833143594963, -0.3951892821849063,-0.6114960336943951,-0.787988199289983, -0.4544844137443082,-0.657492739431111,-0.484187939006181, 0.4936478319326018,0.2653148405479006,0.01970714938077021, -0.1989850169013517,0.4302075642722875,0.1852629793843341, -0.0559739158243807,0.3387563694841473,0.09867487876932232, 0.2393267951217032,-0.999553621201999,-0.9653354239158236, -0.8682642090770526,-0.9597077173597477,-0.8918540989344099, -0.8573751662344773,-0.2980738893651726,-0.3916343988495664, -0.4596955428592778,-0.4950341577852201,-0.1432117197792371, -0.2267418620329016,-0.2909964852939082,0.02097514873862574, -0.05800679989935065,0.1653145532988453,-0.3786231842883476, -0.1464197032303796,0.09531724619007391,-0.1924163631703616, 0.05252803743712917,0.006318730357784829,-0.3534800054422614, -0.1720548071373146,0.02057294660420643,0.190134278339324, -0.1169519894866824,0.07636807502743861,0.2529338262925594, 0.1271908635410245,0.3046134343217798,0.3366066958443542, 0.6094980941008995,0.7135382519498201,0.7711196978950583, 0.7870198804193677,0.8705500304441893,0.9132984713369965, 0.403998910419839,0.62060207699311,0.7967976318501995, 0.4726965405256068,0.6757048258462731,0.5106167801856609 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<362, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*362] = { 0.9756767549555487,0.9966345256373878,0.9881046683022421, 0.9397937974509782,0.8528238339140162,0.7412214378486838, 0.6231965669156312,0.9834769262997924,0.9946239689739521, 0.9675363014408187,0.8953435882073797,0.7874413200090197, 0.6642104349774659,0.9602286065935876,0.9526601679743949, 0.9016229543668698,0.8082746868815032,0.68966672282401, 0.8971014254962764,0.865591182877164,0.7933984257386667, 0.6899186612252972,0.7970712198937139,0.7447486127431006, 0.6617902410567804,0.6754324093484999,0.6115790799976598, 0.5505632701289798,0.4375940376503739,0.3383775196739809, 0.2130515669413184,0.06907711216819173,-0.07773888316805401, -0.2106409255960856,-0.3200730131503601,0.5092547181142852, 0.4023388431564537,0.265404014577164,0.1102235177567159, -0.04300669133613336,-0.1770183828831563,0.575075565563677, 0.4585983658816588,0.3107083252171692,0.1482373789939068, -0.006505365777837463,0.6235168560971455,0.4966123011343984, 0.3414318272800055,0.178171381919271,0.6463093264667951, 0.5114333072914884,0.3555189519163455,0.6437455132939696, 0.5064909697503934,0.3200724178002418,0.4666605048128079, 0.6201479032959075,0.7616224484252742,0.8717669667532965, 0.9417225609211535,0.3896493566867455,0.5498790773446007, 0.7090922337204692,0.8433688828589941,0.9352336066902967, 0.4569904905821254,0.6220231833138386,0.7744719807282636, 0.8905764752647638,0.5117469206751248,0.6692307929647209, 0.8035074327650433,0.5454521871592555,0.6853580997057199, 0.5566669166856135,0.2502538949373057,0.3059448167491349, 0.3600408236839538,0.4043068999720242,0.4319414682718342, 0.441694708443188,0.4034206272561209,0.4730271557866856, 0.532848297654098,0.5712181400125941,0.5831430197408345, 0.573108039637807,0.5665551499276381,0.6430548267994817, 0.697797754038698,0.7197945422057391,0.7103565350090897, 0.7205836671095895,0.7916313033238946,0.830001177910469, 0.8306996157029517,0.8449704612165639,0.8994200667307424, 0.9168712118355732,0.9290744770112713,0.9630120083465428, -0.4375932291383153,-0.3383769257894378,-0.213051272338593, -0.069077150725739,0.07773856031405026,0.210640419502912, -0.2966224671809508,-0.1723655417522876,-0.02500201334267453, 0.1301781584808292,0.2729791005675229,-0.1245894223683107, 0.02220568982766716,0.181648017057727,0.3325661017840546, 0.06640211724156465,0.224593525159963,0.3797737583307059, 0.2546621526726748,0.4084582460890006,0.4194124862841989, -0.6231952788307172,-0.5191377313825043,-0.3823142165271753, -0.2192092047974507,-0.04708691012811009,0.1137784201343049, -0.5064899646320268,-0.3766634899185586,-0.2148504892229182, -0.03595624973804722,0.1367370476543766,0.2846552253706529, -0.3555183995893311,-0.2005508680670486,-0.02217694060430831, 0.1572376520619428,0.3149362814931866,-0.1781713502362079, -0.007356907961155389,0.1715373930872541,0.3352293490184234, 0.006504983859799645,0.1777752170237141,0.3417324928044827, 0.1770178009419758,0.3352469806136632,-0.2502532651867688, -0.305944136422603,-0.3600400745299253,-0.4043060780204903, -0.4319406005716204,-0.4416938460694254,-0.1269352612818317, -0.1739928009393344,-0.2202529758737042,-0.2586227459701909, -0.284108486127532,0.01921174674137865,-0.0178650781598001, -0.05758606025521316,-0.0946046496972474,0.1765178736510398, 0.1461529703905971,0.1077831731751904,0.3265627679363224, 0.2967152689909496,0.4533499532981318,-0.9756765814639009, -0.9966344649740785,-0.988104613901425,-0.9397935597648753, -0.8528232434185344,-0.7412204556286736,-0.9630117602017364, -0.9724898559386537,-0.9443983719046766,-0.872205391539895, -0.7653064930405823,-0.6437442957669286,-0.9168708930751739, -0.9063844772960285,-0.854235225343551,-0.7619985358162277, -0.6463082287051728,-0.8306992108488227,-0.7961776618828416, -0.7239846967363567,-0.6235158879179115,-0.7103560109842373, -0.6562122899574033,-0.5750746905699001,-0.57310737178428, -0.5092538887995088,0.5191392058051657,0.3823157740702657, 0.21921067448898,0.0470881327099307,-0.1137775092906953, 0.5472470006444878,0.3931697400830757,0.2142753542688524, 0.03384607877300972,0.5571877569557013,0.3873829237054168, 0.1993990574966068,0.5423114310655514,0.3634170963876792, 0.5045541233354154,-0.4666608333315146,-0.6201479767677171, -0.7616223457476226,-0.8717667933239607,-0.9417223829869568, -0.3352474166348553,-0.4910400536375052,-0.6475846838452103, -0.7818611891844719,-0.8763941477411082,-0.9290742611527086, -0.3417327749428254,-0.4990082491682611,-0.6485010946606566, -0.7675612257958812,-0.8449702640289916,-0.3352294641675141, -0.4847082676062016,-0.6189847486203324,-0.7205834943870382, -0.3149361858566655,-0.4500011545486986,-0.5665549223826651, -0.2846548708446658,-0.4034202330037945,0.7653073532861547, 0.872205859792842,0.9443985741044771,0.9724900050827093, 0.761999264311147,0.854235631268382,0.9063847463761845, 0.7239853440497003,0.7961780994118357,0.656212928522132, -0.3896497406159066,-0.2729794041072619,-0.1301782703739063, 0.02500220716165068,0.1723660797382231,0.2966232936614225, -0.4569906864326022,-0.3325662253633467,-0.1816479322770386, -0.02220527620210722,0.1245901828483064,-0.5117470172320778, -0.379773775950072,-0.2245933008307449,-0.0664015397322142, -0.5454522916908328,-0.4084582325525932,-0.2546618495309371, -0.5566670856360056,-0.4194124724547443,-0.5505634949474448, 0.8763943198737287,0.7818612997205676,0.6475846717443501, 0.4910398461057182,0.7675613256844861,0.6485010946606566, 0.4990081234589473,0.6189848075516286,0.4847082388524531, 0.450001299655162,0.2841093866952762,0.2586236728290179, 0.2202538772848093,0.1739936456875063,0.1269360432434686, 0.09460546886511037,0.05758689641252078,0.01786589383219402, -0.01921097131735245,-0.1077825867383485,-0.1461523912078012, -0.176517304179968,-0.2967150069434516,-0.3265625234832059, -0.4533500040307881,0.04300632519905166,-0.1102235647973928, -0.2654036978528873,-0.4023382153059064,-0.148237402516883, -0.3107079508004592,-0.4585976694770509,-0.341431363569028, -0.4966114955495211,-0.5114323842282982,-0.0338450669354788, -0.2142741518944354,-0.3931684703578905,-0.5472458119265531, -0.6642094200020999,-0.1993981773021475,-0.3873820250532686, -0.5571869441797634,-0.6896660583043277,-0.3634165630969035, -0.5423109973324143,-0.6899183532182501,-0.504553984174398, -0.6617902107749167,-0.6115792176515682,-0.5831423229868272, -0.5712174808640838,-0.5328477301676857,-0.4730266887482617, -0.7197940112087894,-0.6977972953381247,-0.643054488612089, -0.8300007800598662,-0.7916309980553373,-0.8994197666839766, -0.787440689562144,-0.8953433656420341,-0.9675363415293142, -0.9946240407011416,-0.9834768689460589,-0.8082744359221968, -0.9016230372124288,-0.9526603585193832,-0.9602286916122059, -0.7933984756354565,-0.8655914258905276,-0.8971016370404785, -0.7447488265146829,-0.7970714969272577,-0.6754326817805886, -0.1367364480221615,0.03595711502889169,0.2148515425621942, 0.3766645834143537,-0.1572373865760062,0.02217737847260294, 0.2005514111744157,-0.1715374471229988,0.007356909834687842, -0.1777755545457471,-0.9352334959045954,-0.8433687324667886, -0.7090921450939843,-0.5498791863597017,-0.8905764592918793, -0.774471897882643,-0.6220231689320465,-0.8035075122026962, -0.6692308066909014,-0.6853582437416091,-0.1778161375899129, -0.08196112014272428,0.03109142233089637,0.1521341729788544, 0.2669187829195654,0.3632859224072412,0.4366423972091846, -0.02374119024204515,0.09471244259515242,0.2248569419991813, 0.350706344617783,0.4558820271112225,0.5328176919569597, 0.1544374088220477,0.290680604004569,0.4265382311030135, 0.5423794137964371,0.6260922126282031,0.3410385404813853, 0.4823536424782295,0.6082030290912097,0.702208082962552, 0.5136107375104821,0.6447862719396159,0.7494380454402638, 0.6543855332308085,0.7656972306146125,0.7580253719184178, -0.5980610514568761,-0.5275667073977156,-0.4291113361140411, -0.3056524524744078,-0.1696203080333493,-0.0376422258299667, 0.07805400320688781,-0.4626069507186271,-0.367865039277211, -0.2460698157210856,-0.1075922772772709,0.02954542570643959, 0.1497986786591435,-0.2914862882507431,-0.1726261297241735, -0.03496909012717316,0.1043290137555473,0.2274959035287995, -0.09487972868743588,0.03929513959897087,0.1777727700059916, 0.3025309778153461,0.1056298518860434,0.2403183339972744, 0.3651211825280409,0.2871566925727233,0.4096380159360284, -0.07805410188638827,-0.1029113333575652,-0.1281592432697842, -0.1503967869358663,-0.1664476243728032,-0.1752005995696255, 0.06662046905832243,0.05250750966401235,0.0344560317246887, 0.01402339491018058,-0.006131193542750579,0.2310143008479173, 0.2261291767278549,0.2106389321639752,0.1852638684253743, 0.399677322182528,0.3973696146518023,0.3769369479489949, 0.5518992169467314,0.5439427436094942,0.6724578932085956, -0.9161616153729886,-0.9381742633287281,-0.9327747060225072, -0.8900379255247358,-0.8106886470978539,-0.7076732091095549, -0.8620450993411092,-0.8700248897395222,-0.8443283587296551, -0.7791770527382307,-0.6830500709829336,-0.5739187050780551, -0.771485918473268,-0.7582565243728648,-0.7097589298055721, -0.6279538602818827,-0.5273136421678301,-0.6430242175142256, -0.6070333936049677,-0.5418820315598687,-0.456049251546243, -0.4881111164997092,-0.4360363777707778,-0.3660248731570757, -0.327027482762229,-0.2694021154175107,0.5980613131647216, 0.5275669112241526,0.4291115157626469,0.305652623083869, 0.1696204410073287,0.03764226488103771,0.6790262210552894, 0.60193444501539,0.4907540154202648,0.3522765038549025, 0.2045239960649213,0.7499962909367706,0.6619943399357987, 0.5360957563846484,0.3850393813839648,0.7970875591895581, 0.6947571795971019,0.5562797487334057,0.8113900261915854, 0.6959590446635378,0.7949388873021738,-0.4366431953633788, -0.5614655245500564,-0.6867851479477911,-0.7951587738972525, -0.8708228680516066,-0.908932652052906,-0.4096386948450996, -0.5369984265979351,-0.6595626659529629,-0.757774542842855, -0.8188523117857327,-0.8439728473440037,-0.365121777041269, -0.4879068833210152,-0.6002019152270697,-0.6843307714263435, -0.7331976694123558,-0.3025315118038733,-0.4144630434096209, -0.5126750515159538,-0.5843857415998934,-0.2274963530521035, -0.3262252144152584,-0.4115345048612673,-0.1497989805512191, -0.2366659840339851,0.9161619708326186,0.938174604388516, 0.9327749805257903,0.890038116195112,0.8106887957474585, 0.7076733877280226,0.9671526283488294,0.9837044593162763, 0.9631630917506131,0.8980117463611794,0.7967295682829974, 0.9941684586829005,0.9959257775132996,0.9531387910864955, 0.8656231437836883,0.9840623116657442,0.9635372003220445, 0.8983859076909864,0.9334760373935599,0.8907539689815491, 0.8525641076255658,0.1778171059718252,0.08196185466921782, -0.03109104889813258,-0.1521342296531137,-0.266919222145987, -0.363286609556101,0.2694030024488408,0.1709841140913856, 0.0528882307987244,-0.07296161244603722,-0.1901866620094973, -0.2871575400870702,0.3660256667153609,0.2648098022646842, 0.1422988506889488,0.01311012528268693,-0.1056306282458797, 0.4560499397602033,0.3508815236279322,0.2250316837343615, 0.09487917246041001,0.5273142067726671,0.4179974732249477, 0.2914860445503094,0.5739191244872464,0.4626070035784272, 0.5614648567136933,0.6867847261588661,0.7951586652134927, 0.8708230332223915,0.9089329717882446,0.6702235465196067, 0.7988296386378929,0.8970417421055762,0.9520780267590907, 0.7664410264477003,0.8854287054759334,0.9628651295567006, 0.8322645377129818,0.9304765454032002,0.8591275181397264, 0.1029113872119272,0.1281594836202007,0.1503972383762519, 0.1664482855753907,0.175201438535584,0.2366655991467124, 0.2755134432217821,0.308440535504026,0.3288733874144261, 0.3341527600467577,0.3270281951640686,0.4115339986401247, 0.4596635416815941,0.4916319868779721,0.5005292304537458, 0.4881116080953447,0.584385354546882,0.6313194611701424, 0.6517522812978667,0.6430245879023526,0.7331975726799166, 0.7681409935232665,0.7714862700692517,0.8439730388551088, 0.8620454677591289,0.1901860445525563,0.0729613782704526, -0.05288800289873952,-0.1709834836318114,-0.01311052982462483, -0.1422987785572308,-0.2648092917366038,-0.2250317714436444, -0.3508811505850218,-0.4179972559636759,-0.06662019868190015, -0.2045235871714226,-0.3522760475882291,-0.490753594798392, -0.6019340751777634,-0.6790258583886756,-0.2310136280273065, -0.385038640575524,-0.5360950867028047,-0.6619938111098648, -0.749995864312011,-0.399676350745083,-0.5562788600968236, -0.6947565047867294,-0.7970870947583818,-0.551898166785698, -0.6959582378852542,-0.8113895155772442,-0.6724569726942554, -0.794938298671674,-0.7580246814516086,-0.3341522378920435, -0.3288731104321892,-0.3084405200297279,-0.2755136565752797, -0.5005289572181015,-0.4916319868779721,-0.4596638193626508, -0.651752116850578,-0.6313195665821806,-0.7681408256666581, -0.7967293465331413,-0.8980116136367456,-0.9631629650010809, -0.9837042844506774,-0.9671524096287046,-0.8656229381767455, -0.9531387450388693,-0.9959257826316365,-0.9941684373181742, -0.8983857314821276,-0.9635372279543751,-0.9840624137666333, -0.890753770890823,-0.9334760519103793,-0.8525638190917326, -0.0295456393046314,0.1075921183812993,0.24606969704266, 0.3678649858572621,-0.1043294399383607,0.03496867572798409, 0.1726257663402015,-0.1777733562873886,-0.03929575512161578, -0.2403190522322838,-0.9520779048267128,-0.8970418148986564, -0.7988299587745943,-0.6702240741753134,-0.5328183289734231, -0.9628652449155908,-0.885428988462601,-0.7664414449873368, -0.6260926899993671,-0.9304767531770004,-0.8322648150388383, -0.7022083677250226,-0.8591275565599557,-0.7494380619781833, -0.7656969051450071,0.6830504023375936,0.7791773456982055, 0.8443286725827212,0.8700252447721738,0.6279543165738012, 0.7097593165624652,0.7582568868087332,0.5418825825925072, 0.6070338354963091,0.4360370100657923,-0.4558825077011868, -0.3507064981157044,-0.2248566368863532,-0.0947116843681468, 0.02374227578387531,-0.5423796577050666,-0.4265380492676017, -0.2906799053833736,-0.154436271103469,-0.6082029717256227, -0.4823530727762694,-0.3410374453295618,-0.6447858429341417, -0.513609764630649,-0.654384710247139,0.8188524345231695, 0.757774488968496,0.6595623604908243,0.5369978886010214, 0.6843305795053973,0.6002015742230613,0.4879063887999532, 0.512674598571755,0.4144625345167917,0.3262247228510809, 0.006132171174653799,-0.01402256634368312,-0.03445538312060447, -0.05250705338074756,-0.1852627984273255,-0.2106379566061765, -0.226128333686081,-0.376935865064754,-0.397368560322806, -0.5439417227815399,-0.1282040992012935,0.001413185228105039, 0.1506070646973712,0.3060111300013609,0.448827218017063, 0.5644591381617006,0.6488215902264565,-0.1794700290349442, -0.04186542201104336,0.1153813721057172,0.2745267180089526, 0.4148587047410423,0.5243374917023425,-0.232615798766268, -0.08912570230038545,0.07170206110196441,0.2291650104977662, 0.3638246180723771,-0.2808945465452747,-0.1344874258557153, 0.02465792853592251,0.1758296024995542,-0.3176184514965804, -0.1720469916489061,-0.01939827024983318,-0.3399568124040331, -0.1991953311157892,-0.3496821715684524,0.6714420577705416, 0.7792137212760166,0.8777656242085763,0.9496341036793504, 0.9824386073167399,0.9768385042055774,0.9441720387917811, 0.7257096121901333,0.8383309419108317,0.9322072488654631, 0.9880661303797563,0.9986378183907035,0.9727408945826257, 0.7644107780855932,0.871715411213502,0.9498618317294465, 0.9834333410851046,0.9737573075945113,0.7760312926982584, 0.8670825303083295,0.9229415742963546,0.9363385958760152, 0.7557291769626011,0.8250351935145946,0.8604026946131842, 0.7093185095742097,0.7587249788041119,-0.9441722324566724, -0.8784288421462625,-0.7739455965390631,-0.6303269409870232, -0.4607792790679794,-0.2871643574027738,-0.9185505385856864, -0.8335922034949309,-0.7042733744470981,-0.5371520006668183, -0.3539781199209927,-0.8589482430980673,-0.7496350811244772, -0.5965101770495287,-0.4153923937207508,-0.7605084662990487, -0.6278754136763828,-0.4607541022738985,-0.6307845637432553, -0.4841596501592634,-0.4877728218435191,-0.3130828685601688, -0.1619469688877697,0.01737679733452725,0.2106381298878163, 0.3952347219739472,0.5514566289762698,-0.3068061997519851, -0.1389316419038192,0.05641200521180441,0.2580561121281895, 0.4397576822043592,0.5849482839226374,-0.2895250242862085, -0.1073663493645557,0.09654302677976184,0.2959218923430848, 0.4661908578614318,-0.2593820240207756,-0.06950063773914905, 0.1321435149365393,0.3193074202631061,-0.2185691140597737, -0.03026253166221194,0.1592889611288683,-0.1728399308158635, 0.005511078744538343,-0.671442351589465,-0.7792138411724322, -0.8777656078901646,-0.949634045961687,-0.982438609905361, -0.9768386118322019,-0.6715195478039778,-0.7797211321494723, -0.8709393754318948,-0.9267982045118057,-0.9400278430381637, -0.649602216294724,-0.7491798190236064,-0.8243817913354027, -0.8608974751546269,-0.6002017842451572,-0.6832790131816587, -0.7391378312821431,-0.5261115978506249,-0.5905953516181053, -0.4383666636546263,-0.6488222902967828,-0.6444008694878965, -0.6181925269683732,-0.5653935344751363,-0.4893366483035592, -0.4011183181354504,-0.7587252832301369,-0.7548226978527568, -0.7202890787466467,-0.6515250496520278,-0.557479929039536, -0.4546220799908838,-0.8604026705465684,-0.8495447148476215, -0.7995410210009178,-0.7120166543368723,-0.6026908960383706, -0.9363384293729855,-0.9100364067183718,-0.8412723782021747, -0.7389956620788968,-0.9737572051250643,-0.9284250544292145, -0.8449013283633929,-0.9727409539931392,-0.9119203221721993, 0.3130823317650696,0.1619462783427865,-0.01737758434298078, -0.2106389019045294,-0.3952353653550799,-0.5514570904847834, 0.220234495302766,0.04529946956154991,-0.1542902631671154, -0.3559342336221437,-0.5333895041467447,0.1061130743462423, -0.08838939227573586,-0.2970021727032599,-0.4916772652886296, -0.02151295053653907,-0.2241324894823635,-0.4257764066297352, -0.1482539922220807,-0.3442635849044243,-0.2600158884245429, 0.1282040764044601,-0.001413366553346585,-0.1506074987015245, -0.3060118317848468,-0.4488280788164578,-0.564459985716308, -0.005511078361168723,-0.1582021264863434,-0.3245220334380661, -0.4836676111525484,-0.6149268292273564,-0.7093192714417703, -0.159288972427813,-0.3291548996161899,-0.5000332157715695, -0.6474460255720145,-0.7557300072673856,-0.319307490579, -0.4929333495466833,-0.6520789064271625,-0.7760321386045594, -0.4661910177108468,-0.6282225265671552,-0.7644115292814133, -0.5849485267562503,-0.7257101604516547,0.6444002635563192, 0.6181920323103048,0.5653931175085022,0.4893362367199527, 0.4011178519784379,0.5013193772200437,0.4552897581943395, 0.3865257888849735,0.3039767654183579,0.319546485626849, 0.2568082513095248,0.1820185654081009,0.1150393454476789, 0.04627539851448641,-0.08558647206366617,0.8784286613131731, 0.7739454978673308,0.6303269573377658,0.4607793683387657, 0.2871644290583872,0.9119202617664459,0.8264212051537363, 0.6967772336314446,0.5296559032972297,0.3468071953669221, 0.1728397432053184,0.8449014608184471,0.7346429035325667, 0.5811577408091089,0.4004001175275866,0.2185687785328751, 0.7389959159190774,0.6053871763277086,0.4382657239603054, 0.2593815856416469,0.6026910636921159,0.4554759872637936, 0.2895245326714229,0.4546219464457031,0.3068056829967603, 0.6149259495773405,0.4836668020692608,0.3245214821530512, 0.1582018910739125,0.6474451599919397,0.5000325428335932, 0.329154569384261,0.6520781574644046,0.4929329083973684, 0.6282220041094108,0.9185503953325266,0.9400278438555837, 0.9267983622223734,0.8709396068783997,0.7797212987315195, 0.6715195494525621,0.8589483198537152,0.8608977587445145, 0.8243822455102904,0.7491802985670569,0.6496025629973337, 0.7605089118538747,0.7391384910218132,0.6832797730640632, 0.6002024649156599,0.6307853921801013,0.5905963116881646, 0.5261125320735169,0.4877738980761407,0.4383677443133593, 0.3496833143594963,-0.3468072634833415,-0.5296559121107985, -0.6967772517279487,-0.8264212573361066,-0.4004002676068469, -0.5811577408091089,-0.734642815176818,-0.4382658852809146, -0.6053870894221652,-0.4554761269845107,0.5333893556910446, 0.3559338950225972,0.1542897676170885,-0.04530002222683179, -0.2202350051083672,0.491677469651672,0.2970021583546204, 0.08838916973894111,-0.1061134148983228,0.4257769268822734, 0.2241327483663198,0.02151295276860445,0.3442643233016264, 0.1482544392795429,0.2600167460395648,-0.998637827838979, -0.988066142434594,-0.9322073703652781,-0.8383312666748716, -0.9834332923270373,-0.9498619694604108,-0.8717158495436891, -0.9229416329138649,-0.8670829638030551,-0.8250355565045274, -0.3039772599799364,-0.3865262864971092,-0.4552902929786847, -0.5013199694054605,-0.5243381301130434,-0.1820189194073548, -0.2568086311939767,-0.319546898969995,-0.3638250562458945, -0.04627540884770265,-0.1150393837809054,-0.1758296738027379, 0.08558690678521951,0.01939866440958929,0.1991961595715477, -0.4397580914639075,-0.2580566866135989,-0.05641266799084353, 0.1389310087452558,-0.295922215665352,-0.0965434988571485, 0.107365815243832,-0.1321437542333658,0.06950025525528913, 0.03026233882278601,-0.4148593732748149,-0.2745272477919344, -0.1153816305493603,0.04186543327713237,0.1794701997231261, -0.2291653183673072,-0.07170210104888378,0.08912594410177796, 0.2326162031595532,-0.02465773800362392,0.134487905062478, 0.2808952005684321,0.1720476740810845,0.3176193294845107, 0.3399578552959914,0.5574798958328108,0.6515250645580214, 0.7202890442609949,0.754822534930785,0.7120168974234773, 0.7995412648414353,0.8495448706478895,0.841272643210163, 0.9100366384710021,0.9284251625234463,0.3539783956892765, 0.5371522584244614,0.7042734954121332,0.8335921603240184, 0.4153929051801631,0.5965106290979496,0.7496353473630305, 0.4607548496345096,0.6278760663083329,0.4841605931432954 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<492, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*492] = { 0.9756767549555487,0.995129146382046,0.9942990656144199, 0.9656349505028294,0.906669314667086,0.8226336999568686, 0.7243638464104109,0.6231965669156312,0.9839703767589784, 0.997142038124067,0.984428692964357,0.9391662760580684, 0.862670051560969,0.7648456045484533,0.6590519021583431, 0.9707167715611407,0.9718838371878742,0.9426229117731289, 0.8796306184740903,0.7892458129328142,0.6846076300785651, 0.929183358966584,0.9136522029381524,0.8666502644050813, 0.7898345918065129,0.693320589737796,0.8580670030245058, 0.8250351905308677,0.7641558269670571,0.6811699046825979, 0.7636773150775941,0.7169778531093051,0.6492336193335421, 0.6574101272386674,0.6032570204852985,0.5505632701289798, 0.4375940376503738,0.3542026794336651,0.2512745383622963, 0.1321836977435469,0.005435183516526082,-0.1177256014717769, -0.2278158095071639,-0.3200730131503601,0.499121216713679, 0.4104288008617515,0.2993922246212043,0.1713800199472592, 0.03766821044034947,-0.08889959511906904,-0.1993010113619456, 0.5575360887326264,0.462310883286662,0.3429526003657482, 0.2075487278198905,0.06972486181048379,-0.0574641852590465, 0.6055762148233742,0.5027272059054465,0.3761219220443625, 0.2365775369888062,0.09858215717061569,0.6366252883681098, 0.5264243511029411,0.3955623729759449,0.2563797979489069, 0.647928555739847,0.5327472238507116,0.4019285426857001, 0.6416958910015911,0.5252922545125212,0.3200724178002418, 0.4449423089720693,0.5765874507842912,0.7038338496229432, 0.8135085046154491,0.8958808454008315,0.9485590875281112, 0.3796302423021757,0.5151344445922483,0.6532516657507655, 0.7794467577011209,0.879719713309462,0.9471999912418547, 0.4385611209938041,0.579827292674625,0.7171156368030697, 0.8342797026222076,0.9195293587283735,0.4904847582024935, 0.6301148467885526,0.7583069705424822,0.8604123883748889, 0.529043067100619,0.6594466179023598,0.7726806309784415, 0.5508150816257275,0.6670356654438421,0.5566867904077125, 0.2502538949373057,0.2979150767790733,0.345210193321538, 0.3870703453412754,0.4184100968812649,0.4364489070347424, 0.4418245296642042,0.3805829325031832,0.4390055479929592, 0.492568207092815,0.5338014037295256,0.5572819316169835, 0.5624691657697151,0.5532743164301828,0.5199151685430116, 0.5855041443032848,0.6393053209231115,0.6727852191382864, 0.6825747626046904,0.6719797747984303,0.6571141852151331, 0.7228163905998002,0.7689467902788032,0.7886244561063042, 0.78247330591778,0.7784686816166657,0.8360899632174316, 0.8684468597631155,0.8724880558103147,0.8732117959842211, 0.9170839626929869,0.9340377000206754,0.9378324283560583, 0.9666143691402809,-0.4375932291383153,-0.354202049210883, -0.2512741515648044,-0.1321835946181268,-0.005435354322950186, 0.1177252152663303,0.2278152856398797,-0.318790682468547, -0.2170414107297249,-0.09666371763400751,0.03477626912480049, 0.1650597498899593,0.28228599025285,-0.17643804815428, -0.05685397486912016,0.07657633376040601,0.2119799408472924, 0.3357313012364012,-0.01650847491202961,0.1157420722211241, 0.2531713417578513,0.3818912918445931,0.1487980970433173, 0.2844888970254793,0.4153506930815835,0.3048153313129787, 0.4341873267571942,0.4402832445658493,-0.6231952788307174, -0.5360175915304849,-0.4245963103234196,-0.291418513965459, -0.1453010498480997,0.0007922214273259519,0.13494147117507, -0.5252911977287607,-0.4194174780522606,-0.28881747505953, -0.1402907332818909,0.01290161533377887,0.1562152529527386, 0.2798596737299269,-0.401927847344762,-0.2767587723714138, -0.1302687984978551,0.02582668527333206,0.1758198972364496, 0.3070530822616677,-0.2563795458672579,-0.1157654558096325, 0.03830398324407951,0.191055688005941,0.3280904342412125, -0.09858232519641157,0.04907792605008903,0.199937541973602, 0.3397702575045667,0.05746372336207268,0.2022279185194857, 0.3410562736291031,0.1993004179530413,0.3334923558881231, -0.2502532651867688,-0.2979144048694595,-0.34520946527015, -0.3870695532799813,-0.4184092501028168,-0.4364480352337543, -0.4418236723862358,-0.1460995017681994,-0.1875439456120633, -0.2289599148396085,-0.2657355106897256,-0.2936735229963843, -0.3110073744636102,-0.02437365804953178,-0.05828840766257726, -0.0937912788361675,-0.1272711110222873,-0.1553588546931559, 0.1088504388442424,0.08137944753513068,0.04932387614619547, 0.01557150971513331,0.2428173001617129,0.2183408313117627, 0.1859840000400707,0.3656411198910443,0.3402213332299013, 0.4690651309969549,-0.975676581463901,-0.995129073697949, -0.9942990262685066,-0.9656348165534084,-0.9066689404406876, -0.8226329948202892,-0.7243628133079894,-0.9666141311314619, -0.9785291857720615,-0.9649167587949544,-0.9193242448617983, -0.8431576679717276,-0.7462319572506099,-0.6416946598622301, -0.9340374019521771,-0.9328600498682232,-0.9022446524809692, -0.8392521444630035,-0.7502214356485383,-0.6479274205124774, -0.8724876912825506,-0.8541267298323965,-0.8060828752150751, -0.7303087484697733,-0.6366242668463477,-0.7824728542655722, -0.7469876674129576,-0.6861081367635697,-0.6055752927616058, -0.6719792111143933,-0.6239138376203725,-0.557535231009602, -0.5532736276163508,-0.4991203912134258,0.5360190436422174, 0.4245978595344713,0.2914200439268497,0.1453024287888095, -0.0007910862586512385,-0.1349406039583337,0.5628633831872754, 0.4391923979565908,0.2932083572735901,0.1374730553820866, -0.01276978562268766,0.5775069821113988,0.4414551280568763, 0.2853595486273291,0.1249280534487668,0.5745152767417482, 0.4284746793321108,0.2676940340983546,0.5524169045150343, 0.4015572805868465,0.5149954726706612,-0.4449426764484256, -0.576587591800914,-0.7038338094577924,-0.8135083597704743, -0.8958806666660788,-0.9485589107573548,-0.3334928142377668, -0.4656561029153702,-0.6013830911884229,-0.7267009436209915, -0.8278508961406972,-0.8977211598944568,-0.9378322143189233, -0.3410565997728103,-0.4760902226557944,-0.6097779474907786, -0.7269418806406593,-0.8157918899746622,-0.8732115893121841, -0.3397704479470293,-0.4718778429932776,-0.5973004074862646, -0.7021751105216565,-0.7784685034185206,-0.3280904656572098, -0.4519721780114144,-0.5652060578754561,-0.6571140026135045, -0.3070529166634529,-0.4196426265222307,-0.5199149039850575, -0.2798592799108112,-0.3805825072580722,0.7462328929439237, 0.843158260373911,0.9193245436545445,0.9649169034529495, 0.9785293294285902,0.7502222547730358,0.8392526460505537, 0.9022449400962008,0.9328602810500073,0.7303094694378339, 0.8060833454941833,0.8541270795356295,0.6861088083158182, 0.7469881692208658,0.6239145070358707,-0.3796306565856781, -0.2822863401756764,-0.1650599593464782,-0.03477624931230538, 0.09666402587967657,0.2170420055376572,0.3187915103535204, -0.4385613640703605,-0.3357314877557171,-0.2119799800663603, -0.07657612650429085,0.05685448230997714,0.1764388387711721, -0.4904848834824611,-0.3818913632934865,-0.2531712491023975, -0.1157417111569205,0.01650914517247642,-0.5290431556806968, -0.415350713896117,-0.2844887227712293,-0.1487976298306422, -0.5508152020150771,-0.4341873428629178,-0.3048151129115739, -0.5566869690240907,-0.4402832689070376,-0.5505634949474448, 0.8977213434708383,0.8278510409608931,0.7267010116232644, 0.601383022760614,0.4656558477896065,0.8157920246508805, 0.7269419391910862,0.6097779075806529,0.4760900512385949, 0.7021751903445496,0.5973004014138014,0.4718777509809839, 0.5652061482232076,0.4519722029963607,0.4196428289231057, 0.3110082673965054,0.2936744473798271,0.2657364265527945, 0.2289607907051673,0.1875447674114613,0.1461002696873639, 0.1553597055562715,0.1272719901065957,0.09379215148049876, 0.05828924862952556,0.02437445585198169,-0.01557080948102478, -0.04932316861007474,-0.08137874963697227,-0.1088497589109511, -0.1859835450151495,-0.2183403902096033,-0.2428168640462141, -0.3402211599784607,-0.3656409687130195,-0.4690652148440055, 0.08889916251124211,-0.03766840286064398,-0.1713799135166342, -0.2993918156335824,-0.4104281434194711,-0.06972505514639399, -0.2075485942610162,-0.3429521440427875,-0.4623101760624211, -0.2365773504687261,-0.3761213898309075,-0.5027264164274929, -0.3955617526602613,-0.5264234573415938,-0.5327462324847574, 0.0127707607707569,-0.1374718849546943,-0.293207061528236, -0.4391910893180505,-0.5628621680946149,-0.6590508424134647, -0.1249271271017331,-0.2853585453187341,-0.4414541323690568, -0.5775060764024141,-0.684606860838456,-0.2676933323589697, -0.4284740156087684,-0.5745147048698985,-0.6933201342296018, -0.4015569080051182,-0.5524166274339118,-0.6811697288917756, -0.5149954132531107,-0.6492336477442618,-0.6032571744726157, -0.562468445445845,-0.557281226121636,-0.5338007588723378, -0.4925676465433573,-0.4390050662086606,-0.6825741836716002, -0.6727846814032147,-0.6393048713560345,-0.5855037965960378, -0.7886240006308879,-0.7689463943189565,-0.7228160987485833, -0.8684464995603698,-0.8360896738847699,-0.9170836780332227, -0.7648448608123986,-0.8626696659610523,-0.939166188119448, -0.9844287505675849,-0.9971420785412131,-0.9839703005564542, -0.789245396544295,-0.8796305387421426,-0.9426230395503291, -0.9718839912389975,-0.9707168152563411,-0.7898344765948023, -0.8666504070362878,-0.9136524365050784,-0.9291835215938483, -0.7641559277074473,-0.8250354441736533,-0.8580672517338752, -0.7169780714779912,-0.7636775971947915,-0.6574103946704876, -0.1562146458746545,-0.01290076927461595,0.1402917844716378, 0.2888186351042548,0.4194186302321976,-0.1758195663775944, -0.02582617528460154,0.1302694498290071,0.2767594860031721, -0.1910556254438784,-0.03830382499862148,0.11576568242264, -0.1999377238417288,-0.04907809969461731,-0.2022283181311396, -0.9471998764213244,-0.8797195604896335,-0.7794466205193276, -0.653251640029333,-0.5151346189117816,-0.9195293275646416, -0.834279604589424,-0.717115548266178,-0.579827326676834, -0.8604124463046277,-0.7583069490956061,-0.6301148416214684, -0.7726807603671683,-0.659446675397405,-0.6670358344072718, -0.1778161375899129,-0.09682873617143042,-0.002564331870712152, 0.1001489444293402,0.20294037487074,0.2966901028193483, 0.375180936757441,0.4366423972091846,-0.0474532387078642, 0.05034809081619229,0.1589075235585833,0.2695165933494903, 0.3711623656745905,0.4552971059046723,0.5189584406200277, 0.1017827048364344,0.2144158936234644,0.3316684820340907, 0.4414792935657015,0.5327981708248342,0.6005401860053867, 0.2614395442328129,0.3824105897655243,0.4989141911429241, 0.5982545824011208,0.6726053169812281,0.4179945008346566, 0.5376874271465311,0.6438148252422833,0.7263687759217851, 0.5575576702299878,0.6670994939031171,0.7570605631322944, 0.6714338802491097,0.7658357564785362,0.7580253719184177, -0.5980610514568761,-0.539304606284799,-0.4601049879074139, -0.3610209854418669,-0.247915131130675,-0.1308224218325103, -0.01993206276454632,0.07805400320688782,-0.4842131513641215, -0.4078776799069966,-0.3108032946618485,-0.1973076501762547, -0.07725012597615898,0.03770515913931162,0.1390339189204285, -0.3436782750638498,-0.2488690439489935,-0.1366770376390136, -0.015847433302958,0.1014607675543575,0.2051261004110744, -0.1810587170687898,-0.07134455272126199,0.04823642170967721, 0.1661580441221543,0.2713649127044082,-0.007965415159176724, 0.1085527127599039,0.2253294012222397,0.3313524432512411, 0.1602444452922388,0.2740270626018594,0.3797664442036205, 0.3106168108763466,0.4144914982680714,-0.07805410188638828, -0.09926944144321027,-0.1210902608980977,-0.1414695137250296, -0.1581584914998737,-0.1696765129666563,-0.1759038243562996, 0.0445080871572717,0.0312941062385701,0.01557512261039578, -0.001683488527681644,-0.01888610824676229,-0.03445251803458384, 0.1827599102095679,0.1772109890337862,0.1655328468312471, 0.1477041886655562,0.1255191042072879,0.3281955954096453, 0.327054627020386,0.3149499526725762,0.2920106184972662, 0.4680615585920147,0.4660213421080895,0.4487906967476448, 0.5899485877914471,0.5822989875106862,0.6867607929057539, -0.9161616153729887,-0.9364062548880398,-0.9378298619231508, -0.9131962255692416,-0.8599818711884049,-0.7829080825969388, -0.6920503887795537,-0.8718421840682521,-0.8825595832157751, -0.8702495449678662,-0.8290948069847427,-0.7603667866829383, -0.6729204873538853,-0.5786150802505966,-0.8013843264777428, -0.7976661081468692,-0.768502718877635,-0.711654457383761, -0.632841917808473,-0.5431812243043028,-0.7022906139785883, -0.681473218889239,-0.6365518584536976,-0.5697323001067327, -0.4894330041487118,-0.5787744062647625,-0.5426164975113815, -0.4876750583595674,-0.4191311379161569,-0.4417943029188641, -0.3959648506658267,-0.3385129506001341,-0.3046660637665696, -0.2557948010777292,0.5980613131647217,0.5393048163248674, 0.4601051714264852,0.3610211606830505,0.2479152916167419, 0.1308225338647039,0.01993208413931726,0.6677553214177145, 0.6047094356064554,0.5171428148173597,0.40713632090741, 0.2835896681332588,0.1591265698738985,0.7315641787700664, 0.661547900814459,0.5636788840886751,0.4428493496799737, 0.3112182413099586,0.7806188517072566,0.7008301889050352, 0.5922663576988024,0.4633278474811566,0.8073788931114407, 0.7168049773549268,0.6000284935406993,0.8094700983598709, 0.7101312760741833,0.7906351929054215,-0.4366431953633788, -0.5432543873236343,-0.6518847469150993,-0.7519687641278953, -0.8321863440327827,-0.8854204339944604,-0.9116068551607237, -0.4144921923404347,-0.5235089742164267,-0.6316176685916446, -0.7265196152590857,-0.7972596530206874,-0.8395279581409318, -0.8565153620209197,-0.3797670590739394,-0.4868622160190148, -0.5894554430684077,-0.6751509712448159,-0.7353253350307362, -0.7689936060115737,-0.3313530028134794,-0.4321132275126733, -0.5253715109567975,-0.600556355906943,-0.6522238398741106, -0.271365416599326,-0.3629934344088176,-0.4458145633979653, -0.5120186635901344,-0.2051265147794646,-0.2871867138062492, -0.3608173514139882,-0.1390341944715653,-0.2127048766307628, 0.9161619708326185,0.9364066014623644,0.937830159535626, 0.9131964504670906,0.8599820357483486,0.7829082325289186, 0.6920505768643884,0.9609824530756677,0.9781541365013002, 0.9704616345084118,0.9310013912281646,0.86057880605657, 0.7685149559210446,0.989767208703788,0.9980900358901049, 0.9758827628822744,0.91903449699185,0.833265855013368, 0.9934762859646802,0.9871926244298611,0.9476218597261858, 0.8754517847749737,0.9670219266787334,0.9434641845143519, 0.8885228243698201,0.9127512333378954,0.8739366103094172, 0.8395063271314147,0.1778171059718252,0.09682951222050769, 0.002564819628494808,-0.1001488152988667,-0.2029406081721617, -0.2966906282753529,-0.3751816472846526,0.2557957005565683, 0.173079435577695,0.07531211221520523,-0.03133676316818776, -0.1369434620566336,-0.2318708914823758,-0.3106176573163182, 0.3385137719481263,0.2540232415826708,0.1530285137711279, 0.04321731529158302,-0.06436010707835081,-0.160245257281258, 0.4191318730425461,0.3321294046295875,0.2281310249483762, 0.1162846541659367,0.007964747877728702,0.489433642057663, 0.3991899134973828,0.2930621280324591,0.1810582896385999, 0.5431817494666105,0.4500346846996958,0.3436781221558973, 0.5786154775493708,0.4842132402051962,0.5432536936373913, 0.6518842443443043,0.751968519322874,0.8321863638644724, 0.8854206569589087,0.9116071863986105,0.6355394193271248, 0.7490598502163163,0.8459479265844492,0.9147022337905197, 0.9515590961968758,0.7217468924595103,0.832492462220627, 0.9181881173363158,0.9702103552386692,0.7903983024315012, 0.8899272054685274,0.9588415071652456,0.8327634000595739, 0.9155844937451473,0.847341552439817,0.09926947140308087, 0.1210904447270627,0.141469873017204,0.1581590349215481, 0.16967722932441,0.1759046848916014,0.2127045253884476, 0.2445425491615283,0.273585758469152,0.2957341763491218, 0.308047355491858,0.310289867067379,0.304666812316337, 0.360816856533433,0.40111044364496,0.4328607304643095, 0.4506895618367096,0.4528028309124678,0.441794849646433, 0.5120181966189056,0.5550964104131831,0.5826403298719719, 0.5901407115935625,0.5787748141912227,0.6522235657335231, 0.6906215449122578,0.7078523050511999,0.7022909666912099, 0.7689935992922883,0.7968842810746374,0.8013846828119295, 0.8565155859292075,0.8718425534235449,0.2318702160237238, 0.1369430725775421,0.03133674940539507,-0.07531173762971051, -0.1730787436687962,0.06435957029576898,-0.0432174738186679, -0.1530282625973764,-0.2540226452878776,-0.1162849524413907, -0.2281309051554154,-0.3321289161485715,-0.2930621449512896, -0.3991895474466183,-0.4500344545042632,-0.04450787402063819, -0.1591262308672862,-0.2835892617928241,-0.4071359133189035, -0.5171424431454641,-0.6047090919043497,-0.667754971050827, -0.182759348567292,-0.311217588457912,-0.4428487008416941, -0.5636783165490287,-0.6615474307561048,-0.7315637666610734, -0.3281947296279775,-0.4633269810702052,-0.5922656048068207, -0.7008296027194382,-0.7806184022682794,-0.4680605237949423, -0.6000275832292742,-0.7168042826134902,-0.8073784119772466, -0.5899475572826254,-0.7101304786227719,-0.8094695688943716, -0.6867599019180602,-0.7906345906969057,-0.7580246814516085, -0.3102892714845059,-0.3080469554975349,-0.2957339957005241, -0.2735857903437153,-0.2445427612771611,-0.4528024643637317, -0.4506894194251309,-0.4328608289948512,-0.4011107629731985, -0.5901404834522559,-0.5826403283348299,-0.5550966534493702, -0.7078521147132451,-0.6906215734113883,-0.7968840729809553, -0.7685147196433009,-0.8605786508071993,-0.9310012591522379, -0.9704614726381049,-0.9781539258256538,-0.9609822090426239, -0.833265614060389,-0.9190343924750819,-0.9758827239891746, -0.9980899940222691,-0.9897671312659951,-0.8754515637483313, -0.9476218214663272,-0.9871926808705815,-0.9934763548505058, -0.8885226077999228,-0.9434641720148271,-0.9670220212347258, -0.8739363526795478,-0.912751179432779,-0.8395059839899685, -0.03770535005341785,0.07724999366628089,0.1973075542157886, 0.3108032352691305,0.4078776804650779,-0.1014611429922413, 0.0158470793272554,0.1366767120232094,0.2488687826864697, -0.1661585633922329,-0.04823695834758815,0.07134403839749669, -0.2253300338935004,-0.1085533974058879,-0.2740278002009307, -0.951558921251018,-0.9147022049495029,-0.8459481068009407, -0.7490602493222091,-0.6355399873284351,-0.5189591001755303, -0.9702103859734443,-0.9181883003327364,-0.8324928057608664, -0.7217473579669872,-0.6005407102760519,-0.9588416857098183, -0.8899274903619968,-0.7903986578533415,-0.6726056915299506, -0.9155846529394258,-0.8327635907539088,-0.7263689563436138, -0.8473415025692198,-0.7570604878450786,-0.7658353788323373, 0.6729208082532415,0.7603670653263657,0.8290950909162599, 0.8702498673585751,0.8825599429670654,0.6328423544382739, 0.7116548337644016,0.7685030738837739,0.7976664655237659, 0.569732829878993,0.6365523011473486,0.6814736060981027, 0.4876756652068531,0.542616991447444,0.3959655290681676, -0.4552976478369847,-0.3711626697331859,-0.2695165436861755, -0.158907070465767,-0.05034727738276638,0.04745431047424936, -0.5327985288361234,-0.4414793439189789,-0.3316681166200877, -0.2144151021287253,-0.1017815730335519,-0.5982547186038516, -0.498913942879685,-0.3824098828497664,-0.261438418974126, -0.6438146830008175,-0.5376868414254443,-0.4179934509733508, -0.6670990265838406,-0.5575567398179423,-0.6714330779441176, 0.8395281342551513,0.797259699808284,0.7265194573448021, 0.631617280616129,0.5235083955500395,0.7353252547527041, 0.6751507612001276,0.5894550707609513,0.4868616963131356, 0.6005560087932349,0.5253710768222695,0.4321127152346745, 0.4458140656510558,0.3629929231163813,0.287186249855225, 0.03445349641709906,0.01888696113562014,0.001684189644027077, -0.01557458737398696,-0.03129373726645494,-0.1255180400739348, -0.1477032144715439,-0.1655319877909157,-0.1772102701265833, -0.2920095247225022,-0.3149489017570188,-0.3270536482990907, -0.4487896352785713,-0.466020275040162,-0.5822980026295906, -0.1282040992012934,-0.01849805050225584,0.1065963991903489, 0.2398318813191588,0.3698188179209382,0.48502451338496, 0.5783911156884447,0.6488215902264565,-0.1719025532007262, -0.05632783998118585,0.07515681888535472,0.2129025876416392, 0.343567868763417,0.4557584300457211,0.5443645168181878, -0.2175987830988393,-0.0973017552494557,0.03818460718077296, 0.1770480397960358,0.3053148472212455,0.4131148482333044, -0.2612807879100861,-0.1378469909045712,-0.001396095557054295, 0.1351028209250271,0.2586670590048458,-0.2983313855304687, -0.1738078395074255,-0.03960230940919204,0.09162948390440753, -0.3254633663003954,-0.2022894544581522,-0.07317794255704746, -0.3420357716053446,-0.2226579469368842,-0.3496821715684524, 0.6714420577705413,0.7639967300466739,0.8515659143445876, 0.9231420898872643,0.968766507749211,0.9843912721595947, 0.9735002156201846,0.9441720387917811,0.7186206475410257, 0.8155881299141562,0.9020895786249314,0.9652453470205394, 0.9962999166711242,0.9953266714812502,0.9700250905310301, 0.7556710613817772,0.8510774619026286,0.9293561756854016, 0.9780963063206267,0.9923931460327553,0.9770471587096806, 0.7749162464509299,0.8614959728515891,0.9253137561812753, 0.9572996257002351,0.9574145614312103,0.7711321510425843, 0.8432637257218606,0.8903690639450959,0.9080060338759378, 0.7446544865975486,0.8006806881904648,0.8332051934736653, 0.701244308549226,0.7431452383031477,-0.9441722324566724, -0.8900404033987204,-0.8080124134651047,-0.6961352518091236, -0.5596336341615111,-0.4106183042575765,-0.2632365894868439, -0.9240670480583062,-0.8565379051211715,-0.7569806316871794, -0.6264662143927534,-0.4751173969978469,-0.3187870772043735, -0.8799221319939898,-0.7952336612815645,-0.6770111077910543, -0.5311881497579278,-0.3724388713810448,-0.8072870326747171, -0.7042659659540528,-0.5707688373923591,-0.4176365891727677, -0.7078360053862648,-0.5898764843905154,-0.4489449331750177, -0.5903925893983768,-0.4647486526822006,-0.4673964384898752, -0.3130828685601689,-0.185445228664084,-0.03612578735626812, 0.1274723552911619,0.2921714771415931,0.4433590934587491, 0.5708391580621228,-0.3082982931616353,-0.1684123240900115, -0.006503141132676928,0.1663065314632002,0.3335853689877698, 0.4804232043289857,0.5992429554740794,-0.2957556065313156, -0.1446156178201561,0.0263111709560856,0.2022572179054991, 0.3655171685702938,0.5033858756710694,-0.2738409047359961, -0.1145893425106214,0.05935120233887466,0.2312500236673017, 0.3849556597532595,-0.2429129440661442,-0.08075215189426678, 0.08929215655126845,0.2511845570441078,-0.2057157098095482, -0.04657083216475545,0.1139410251668723,-0.1662796617532138, -0.01502268661531953,-0.671442351589465,-0.7639968739619373, -0.8515659293219712,-0.9231420361206036,-0.9687664657212128, -0.984391303457987,-0.973500337776358,-0.6726628661443731, -0.7663025019644482,-0.850423091629073,-0.9127051151953394, -0.9446333569667822,-0.9460408841414493,-0.6585464808986428, -0.7477446091210315,-0.8224367335791617,-0.8711767663146228, -0.8890600090241656,-0.624789307379154,-0.7038755707064229, -0.7649345287347953,-0.7996789024113027,-0.570962738954507, -0.6365977473321981,-0.6837028657947305,-0.5018423792963483, -0.5542516901414858,-0.4254957770577467,-0.648822290296783, -0.6461886815965452,-0.6283026738755576,-0.5912852336175236, -0.5351200741127089,-0.4647903048152701,-0.3882830681102161, -0.7431455981686927,-0.7416382763983708,-0.7194745213161431, -0.6726721034791168,-0.603498627991077,-0.5203744826993632, -0.4336588493758105,-0.8332052486459366,-0.828474359608384, -0.7972278976396602,-0.7372273383122565,-0.6545105158801499, -0.5606846159713372,-0.9080059008549388,-0.8943581597155409, -0.8500103413150977,-0.7764211405289977,-0.6833452492402967, -0.957414401308411,-0.9304983201224565,-0.8725103749365023, -0.7889189186796404,-0.9770470988810096,-0.9362839635412132, -0.8680388569279747,-0.9700251729575815,-0.918444056010861, 0.3130823317650696,0.1854445580462805,0.03612501838448451, -0.1274731492430557,-0.2921722054174978,-0.4433596869123763, -0.5708395935644333,0.2348779693026915,0.08967582567032931, -0.07603665789539318,-0.2502419786720302,-0.4161249577436337, -0.5591593471944528,0.1405944428773656,-0.02046318130327168, -0.1971193271182073,-0.3730652190141688,-0.5305955532581022, 0.03400663448859465,-0.1372155522896737,-0.3155629354201782, -0.4830545524260225,-0.07686580556220674,-0.2494045186454276, -0.4194486169997139,-0.1821860518397986,-0.3471084060868513, -0.2742390008535104,0.1282040764044601,0.01849789837253189, -0.1065967544617238,-0.2398324745610959,-0.3698196073694229, -0.4850253879196206,-0.5783919486276667,0.0150226853040112, -0.1119113111904829,-0.2515228306741237,-0.3922511186515919, -0.5199342604026214,-0.6239982016489247,-0.7012450602090677, -0.1139410284132941,-0.2554296773980814,-0.4031586053186698, -0.542022233559877,-0.6580467871742169,-0.7446552996299098, -0.251184596582457,-0.4001969364777949,-0.5460646790820674, -0.673147094708149,-0.7711330012738366,-0.384955766757247, -0.5316548106571598,-0.6658605062472989,-0.7749170668851217, -0.5033860614642302,-0.6389055531086461,-0.7556717637535928, -0.5992432078251344,-0.718621161033365,0.6461880602442403, 0.6283021483764009,0.5912847908960939,0.5351196688462319, 0.4647898820036706,0.3882825918191143,0.5284642450992455, 0.4960034056042855,0.4454222319691665,0.3800276076057463, 0.3072006168941325,0.3815325789989955,0.3347740585202575, 0.2747735598444634,0.2075688995229241,0.2126093137747108, 0.156329837645353,0.09467242600265936,0.03661534005366113, -0.02137253427038529,-0.1295837823093738,0.890040216351073, 0.808012285288426,0.696135219402544,0.5596336911375484, 0.4106183982029223,0.263236651429863,0.9184439709263976, 0.8505076927737054,0.7506591839147216,0.6200379306780087, 0.468796032975448,0.3127569623094897,0.1662794974092329, 0.868038934491271,0.7825908968870643,0.6639295465452925, 0.5181065730349851,0.3597960374562562,0.2057154129295492, 0.7889191397304282,0.6849812234203291,0.5511465043193342, 0.3983516721236944,0.2429125431986682,0.6833454958120994, 0.5645910307529023,0.4236593282043838,0.2738404383453708, 0.5606847158751149,0.4345981000668111,0.2957551060736421, 0.4336586612857497,0.3082977736038213,0.6239973336558853, 0.5199334023086634,0.3922504194657625,0.2515223878822021, 0.1119111251755824,0.6580459058109899,0.5420214442749262, 0.4031580569915867,0.2554294261048449,0.6731462609902346, 0.5460640349170377,0.4001965955151434,0.6658598068272309, 0.5316543804516458,0.6389050615461086,0.9240668881257947, 0.9460408367508209,0.9446334423554673,0.9127052977657962, 0.8504232826059466,0.7663026047198382,0.672662821600414, 0.8799221274953846,0.8890601671224364,0.8711770865982926, 0.8224371418546537,0.7477449864099799,0.6585467268780121, 0.8072873085337188,0.7996793702817474,0.7649351423426319, 0.7038762137269466,0.6247898512026207,0.7078366234465509, 0.6837036520525703,0.6365986074780202,0.5709635410688519, 0.5903935068116462,0.5542526992524547,0.5018433659765416, 0.4673975349060661,0.4254968708628413,0.3496833143594963, -0.3127570262656423,-0.4687960400735756,-0.6200379371399192, -0.7506592271179455,-0.8505077714673068,-0.3597961933958567, -0.5181066147650901,-0.6639295189615018,-0.7825908374998334, -0.3983518694010773,-0.5511465125252933,-0.684981089854631, -0.4236595256885799,-0.5645909758908611,-0.4345982861939252, 0.5591591752824973,0.4161246264414231,0.250241497477734, 0.07603608646389884,-0.08967640497055197,-0.2348784900769719, 0.5305956825248687,0.3730651765875013,0.1971191044524291, 0.02046282792852898,-0.1405948497186229,0.4830549755697658, 0.3155631609030148,0.1372155601346925,-0.03400679840825552, 0.4194492775199674,0.2494049520909613,0.07686599366344003, 0.3471092245505432,0.1821866253122765,0.2742399078697009, -0.9953267028881266,-0.996299919654964,-0.9652453855328264, -0.9020897348256817,-0.8155884604797163,-0.9923930940648169, -0.9780963403965233,-0.9293563919654181,-0.851077922468494, -0.9572995816653358,-0.9253139445404677,-0.8614964761454963, -0.8903691794182322,-0.843264195535517,-0.8006810953746416, -0.3072011182546949,-0.3800281004182731,-0.4454227426500373, -0.49600396162629,-0.5284648561961354,-0.5443651710562558, -0.2075693133952311,-0.2747739903004954,-0.3347745172062594, -0.3815330693216025,-0.4131153608788349,-0.09467260192740248, -0.156330035009575,-0.2126095377726251,-0.2586673060001281, 0.02137271473539107,-0.03661518331511127,-0.09162936047901911, 0.1295843445482322,0.07317846937762097,0.2226588286505751, -0.4804235981897006,-0.3335859124413625,-0.1663071857914592, 0.006502456545087328,0.1684116947040793,-0.3655174937182495, -0.2022576822989345,-0.02631172536408047,0.1446150543700356, -0.2312502717555327,-0.05935158437557636,0.1145888807063153, -0.08929234550841479,0.08075182856000082,0.04657066968406794, -0.4557591367849598,-0.3435685084916551,-0.2129030384298065, -0.07515702237270272,0.05632785157141965,0.171902693522397, -0.3053152988366306,-0.1770483103705765,-0.03818462683571519, 0.09730196068160382,0.2175991175749613,-0.1351028913474505, 0.001396275074369462,0.1378474039188301,0.2612813355040192, 0.03960267796661452,0.1738084474791471,0.2983321411535529, 0.2022902215891641,0.3254642982361922,0.3420368325512359, 0.5203743808147369,0.603498584268205,0.6726720548002406, 0.719474396239089,0.741638033276861,0.6545106949482685, 0.7372275485364457,0.7972280664875324,0.8284744266242357, 0.7764214244136933,0.8500106167743341,0.8943583778920716, 0.8725105875877179,0.9304985104221231,0.9362840195367765, 0.3187873126271887,0.4751176460530464,0.6264663831892676, 0.7569806648964799,0.8565378137633573,0.3724393069541885, 0.5311885746144091,0.6770114116120104,0.7952337966915513, 0.4176372345893317,0.5707694457794332,0.7042664250854263, 0.4489457715859918,0.5898772631309003,0.4647496441635906 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<642, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*642] = { 0.9756767549555488,0.9936273045965159,0.9968737649581846, 0.9799612120178374,0.9397937974509784,0.8775847359538656, 0.7989268505692991,0.7116707502187636,0.6231965669156312, 0.9839439883131955,0.997606269193783,0.9928389431247303, 0.9642927845075879,0.9105363959854476,0.8355633282696746, 0.7474316403407526,0.6549945735140008,0.9764792677814802, 0.98203616585628,0.9657129396229355,0.9235205478703636, 0.8567905682573015,0.7723352820545265,0.679558714000995, 0.9483175267842808,0.9421385440112533,0.9121470670895898, 0.8569526200134567,0.7808692882316954,0.6923761132322002, 0.8971014254962764,0.8773049187483365,0.8343457228904876, 0.7697920647699087,0.6899186612252972,0.8248449061833976, 0.7923517869921962,0.7399265426789414,0.6712800260218211, 0.7377425905504456,0.6958798370509692,0.6387690340353133, 0.6438863846345996,0.5968935863094567,0.5505632701289798, 0.4375940376503738,0.3657089664269339,0.2787444080680243, 0.1782978006253255,0.06907711216819172,-0.04176369281672855, -0.1467487371786262,-0.2403654598146251,-0.3200730131503601, 0.4914658799359538,0.415730180051685,0.322900981562039, 0.2155490838668689,0.09999803429967301,-0.01516787889370763, -0.1220279512352428,-0.2156209889399338,0.5436064763029955, 0.4631956410318647,0.3641181930198064,0.2503784412304452, 0.1299861910861632,0.01243715490308354,-0.09463328683104604, 0.5892511755529848,0.5032632034506475,0.398083833846048, 0.2794416963056139,0.1566100659016669,0.03909742267488438, 0.6235168560971455,0.5315692770219257,0.421472539809791, 0.300467209121473,0.1781713819192711,0.6431587521468424, 0.5460109940101698,0.4333214055893837,0.3130665794035795, 0.6478378002177454,0.547462575328819,0.4350913076613168, 0.6399334332955604,0.5389210845670883,0.3200724178002418, 0.4287826750526064,0.543583002477908,0.6574820925009807, 0.7616224484252742,0.84789985457486,0.9117594243665534, 0.9532148993238304,0.3721063927495636,0.4892591428502506, 0.6101418466998186,0.7256202393614284,0.8256057937005235, 0.9026705854615993,0.9545776898360481,0.4242146396073646, 0.5469136809977159,0.6694499319211135,0.7811234805255704, 0.8720427891122669,0.9369519951934821,0.4722731360977363, 0.595952912761318,0.7145554586697968,0.8172157093145741, 0.8959095653312069,0.5117469206751248,0.6311048134383331, 0.7406754453578036,0.8310759079383635,0.5392182936767531, 0.6497579584113991,0.7472675269245613,0.5536544075493044, 0.6528511281517397,0.5564810307882812,0.2502538949373057, 0.2918876561725356,0.3336803609057232,0.3723595102048779, 0.4043068999720243,0.4267717639621923,0.438887490788423, 0.4417451579814044,0.3636248847448722,0.4137579266740147, 0.4613876866690337,0.5016743933949674,0.5302454776050456, 0.5449783839335098,0.5467236437808158,0.5384586718996058, 0.4847692553550146,0.5414178984405463,0.5913899314506929, 0.6286876371986284,0.6492812914734455,0.6528721916210272, 0.6425800233106177,0.6067853073481849,0.6657991451456106, 0.7129452846304364,0.742280692335758,0.7515124484581964, 0.7428160470273413,0.7205836671095894,0.7764326916323942, 0.8155579470101821,0.8335749126408774,0.8306996157029517, 0.8174816717930142,0.8650995515806045,0.8929631510323212, 0.8991001659430972,0.8921547074343706,0.9286306144549926, 0.9447583574027534,0.943906557149077,0.9688828514077716, -0.4375932291383154,-0.3657083101776577,-0.278743955138478, -0.1782975892372044,-0.06907715072573903,0.04176343253822806, 0.1467483093137361,0.2403649238452659,-0.334979155028856, -0.249113234922861,-0.1483246071802934,-0.03654010765740752, 0.07901069585299947,0.1897435516910367,0.2886438308249422, -0.2140239599986307,-0.114043112737167,-0.001406336898152031, 0.1172166109444462,0.2327251986251803,0.3367145075141528, -0.07787968046152194,0.03376382657255556,0.153308912419789, 0.2719508349268641,0.3804163499145621,0.06640211724156461, 0.1844665658852621,0.3039508137298482,0.4155682770935452, 0.2091265361205084,0.3268694132891478,0.4395588586722926, 0.3409081262151013,0.4522359229770948,0.4554687573578106, -0.6231952788307174,-0.5482281060337162,-0.4546947222788382, -0.3434835709283097,-0.2192092047974508,-0.08979359660521438, 0.03581973661208818,0.1504629769958484,-0.538919991494099, -0.4498208454617149,-0.3413996198032652,-0.2168595871258214, -0.08365072681681855,0.04833087340999961,0.1701142201456619, 0.2762195992594308,-0.4350905112236564,-0.3310154747857772, -0.2089153242611869,-0.07495576041016457,0.0609953219830862, 0.1886252092359959,0.3006812043050254,-0.3130661573786132, -0.1955639168315415,-0.06402306061993944,0.07274927837670857, 0.2040627320686719,0.3211588705998562,-0.1781713502362079, -0.05170714345574682,0.08257031568712175,0.2147106704082434, 0.3352293490184233,-0.03909771946676634,0.08986774257262679, 0.2197779802638865,0.3414374432933526,0.09463277722176672, 0.2196681717134536,0.3398901915912972,0.2156203893390849, 0.3320690152456851,-0.2502532651867688,-0.2918869903084482, -0.3336796480610522,-0.3723587421891308,-0.4043060780204903, -0.4267709034446925,-0.4388866188864613,-0.4417443051644412, -0.1601455684487161,-0.1971059987993399,-0.2343857490409392, -0.2689083414095461,-0.2974793723400317,-0.3179763028669095, -0.3300715220338927,-0.05621340666038893,-0.08741483463411388, -0.1197552832780665,-0.1507029759524935,-0.1776465335448071, -0.1988689416472687,0.05815084571042724,0.03249724572817093, 0.004031222699520391,-0.02530412585141796,-0.053215898827698, 0.1765178736510398,0.1546289422059015,0.1277105869872963, 0.09748683381429475,0.2907448784422404,0.269907109295262, 0.2420435658170933,0.3935116760630417,0.371277608971055, 0.4804450046073554,-0.975676581463901,-0.9936272216648101, -0.9968737272214637,-0.9799611311880234,-0.9397935597648752, -0.8775842433730664,-0.7989260596435309,-0.7116696803542213, -0.9688826210954523,-0.9815699711830299,-0.9760365766523995, -0.9470636842643547,-0.893307090661199,-0.8187603857237982, -0.7313944919484914,-0.6399321926850757,-0.9447580743829742, -0.9484314744303296,-0.9308031701048347,-0.8881406289392687, -0.8218804228775519,-0.7387299092709896,-0.6478366382006043, -0.8990998280625567,-0.8904516044143657,-0.8590773924437412, -0.8038827695098977,-0.7291818553952708,-0.6431576879244336, -0.8306992108488227,-0.808389020899373,-0.7645261844287355, -0.700875860490056,-0.6235158879179115,-0.7428155559569007, -0.7083401623912103,-0.6559147788974127,-0.5892502820769048, -0.6425794288509028,-0.599662429956479,-0.543605629081954, -0.5384579674699886,-0.4914650570197842,0.5482295401850756, 0.4546962564744617,0.3434851238806179,0.21921067448898, 0.08979489147188233,-0.03581866880653414,-0.1504621418996611, 0.5734097649981191,0.4708926749454921,0.3496406892388488, 0.2164317110563474,0.08116185373706966,-0.04652577585176784, 0.5899999525047667,0.4779576265881325,0.3476203004228133, 0.2080468003445843,0.07035894736836774,0.5939040461937424, 0.4730191144791596,0.3362467154220247,0.1942772308316476, 0.5828267863796578,0.4555126388719097,0.3164089231485616, 0.5575911119751512,0.4276808797198417,0.5218578151430378, -0.4287830717591774,-0.543583197615373,-0.6574821123382336, -0.7616223457476226,-0.8478996902559745,-0.9117592443356049, -0.953214723425574,-0.332069490448325,-0.446630150856391, -0.5654761604524486,-0.6798202351943792,-0.7798056854765759, -0.8580045844734643,-0.9119481688022204,-0.943906345398722, -0.3398905505309818,-0.4575823132651822,-0.5766491384642831, -0.6870731075595852,-0.7792417673233771,-0.8476201697382868, -0.8921544952781482,-0.3414376861695784,-0.4585532096219049, -0.5734800429475671,-0.6761401742385725,-0.7585095042345053, -0.8174814835779161,-0.3352294641675141,-0.4479051019866874, -0.5550737397870624,-0.6478759583634458,-0.7205834943870382, -0.3211588327497382,-0.4264290282814993,-0.5239384830804104, -0.6067851042454853,-0.3006809838192767,-0.3970753303611698, -0.4847689570296102,-0.2762191758252563,-0.3636244354414296, 0.7313954822136002,0.8187610780655567,0.8933074970084837, 0.9470638862390964,0.9760366948920106,0.9815701139230523, 0.7387307994522452,0.8218810203164033,0.8881409840733967, 0.9308033931946534,0.9484316859524741,0.7291826478779969, 0.8038833043893007,0.8590777515266519,0.8904519038272285, 0.7008765828222415,0.7645267056644409,0.808389434193021, 0.6559154701692661,0.7083407113717404,0.5996631212693852, -0.3721068299668963,-0.2886442143937944,-0.1897438268706112, -0.07901079510230862,0.0365402424927288,0.1483250002713656, 0.249113868623751,0.3349799829238154,-0.4242149221746557, -0.3367147430533525,-0.2327253235991419,-0.1172165489233448, 0.00140664545831139,0.1140436844250324,0.2140247669633664, -0.4722732967958943,-0.3804164687039511,-0.2719508356433764, -0.1533087105593531,-0.03376336567881106,0.07788040640138937, -0.5117470172320778,-0.415568329019231,-0.3039507303469009, -0.1844662592618953,-0.06640153973221416,-0.5392183881102406, -0.4395588911842847,-0.3268692828205596,-0.2091261596739739, -0.5536545418185335,-0.4522359631907089,-0.3409079709201472, -0.5564812164049491,-0.4554688095204276,-0.5505634949474448, 0.911948358176072,0.8580047480914766,0.779805799941989, 0.6798202570778734,0.5654760412600151,0.4466298574551351, 0.847620327622159,0.7792418677670352,0.6870731334410928, 0.5766490604842006,0.457582102461728,0.7585096116294638, 0.6761402059852791,0.5734799960054555,0.458553071765391, 0.6478760427254714,0.5550737528785307,0.4479050525999325, 0.523938612103898,0.42642910652745,0.3970755800133287, 0.330072407434952,0.3179772211416992,0.2974802928098944, 0.268909235983653,0.2343866000755754,0.1971068007343541, 0.1601463238235379,0.1988698061348988,0.1776474313398276, 0.1507038783156887,0.1197561650892531,0.08741568024186514, 0.05621421006516834,0.05321666456243916,0.02530491044308729, -0.004030439768117324,-0.03249647940113914,-0.05815010397008495, -0.09748624755051107,-0.1277100030888262,-0.1546283656290311, -0.176517304179968,-0.2420432126719758,-0.2699067748969105, -0.2907445498638314,-0.3712774990883263,-0.3935115910448358, -0.4804451115014433,0.1220274770108985,0.01516759063021488, -0.09999808166532918,-0.2155488634876903,-0.3229005078918639, -0.4157295012949034,-0.01243745747761582,-0.1299862309792015, -0.2503781938406115,-0.3641176806190365,-0.4631949245146199, -0.1566100780036334,-0.2794414004304853,-0.3980832592651996, -0.5032624248731086,-0.3004668503638805,-0.4214718858755578, -0.531568413027702,-0.4333206720101446,-0.5460100361038957, -0.5474615360697464,0.04652671746835695,-0.08116072547370122, -0.2164304342992144,-0.349639340464782,-0.4708913462876021, -0.5734085330236464,-0.6549934813681663,-0.07035800928464173, -0.2080457563809749,-0.3476192098203675,-0.477956563666828, -0.5899989806009898,-0.6795578679273953,-0.1942764317499744, -0.3362459041917389,-0.4730183457316648,-0.5939033648561503, -0.6923755409289452,-0.3164083741856072,-0.4555121490743896, -0.5828263835645211,-0.6899183532182501,-0.4276806208272582, -0.5575909387515523,-0.6712799366842756,-0.5218578089727968, -0.6387691005409376,-0.5968937515401853,-0.5467229076134115, -0.5449776495480215,-0.5302447815086285,-0.5016737622437388, -0.4613871281827766,-0.4137574314122305,-0.6528715777859069, -0.6492806993353901,-0.6286871066630142,-0.5913894852863821, -0.5414175353843733,-0.751511947518686,-0.7422802285333175, -0.7129448986517994,-0.6657988547064399,-0.8335745044290338, -0.8155575864896008,-0.7764324183665692,-0.8929628157948554, -0.865099271542177,-0.9286303415753032,-0.7474308144934858, -0.8355628131811949,-0.910536173478481,-0.964292764650283, -0.9928389973443023,-0.9976062839252843,-0.9839438984088483, -0.7723347377989339,-0.8567903368462096,-0.9235205630598494, -0.9657130735429578,-0.9820362841720375,-0.9764792804081987, -0.7808690244142814,-0.8569526309007678,-0.9121472452874007, -0.9421387497441781,-0.94831764697765,-0.7697920349927029, -0.834345897568434,-0.877305173541843,-0.8971016370404784, -0.7399266733270721,-0.7923520436827605,-0.8248451738745377, -0.6958800576642888,-0.7377428728490714,-0.6438866478040335, -0.1701136087322269,-0.04833004892467691,0.08365175264713676, 0.2168607561552602,0.3414008427548977,0.4498220342255287, -0.1886248334720497,-0.06099477310985241,0.07495646481991973, 0.2089161290281741,0.3310163070583758,-0.2040625875734068, -0.07274901933354507,0.06402341598693255,0.1955643282951042, -0.2147107402706368,-0.08257033948198818,0.05170715566660035, -0.2197782438454324,-0.0898680254606548,-0.2196686109085476, -0.9545775709750491,-0.9026704336737921,-0.8256056375543351, -0.7256201416180276,-0.6101418826154756,-0.4892593689607926, -0.9369519509430696,-0.8720426859708773,-0.7811233602703923, -0.6694498734011487,-0.5469137667342426,-0.895909603120296, -0.8172156710096443,-0.7145554011223244,-0.595952924378129, -0.8310760190279151,-0.740675476302381,-0.631104833094877, -0.747267688498931,-0.6497580495472918,-0.6528513129029779, -0.1778161375899129,-0.107732211558371,-0.02718233037986957, 0.06111658446407255,0.1521341729788544,0.2395836558807668, 0.3178872248051531,0.3837865324362482,0.4366423972091847, -0.06488531255731433,0.01813723762238458,0.1103935391881494, 0.2069070831033206,0.3006175096647589,0.3845630878227619, 0.4542522939636228,0.5085532875085977,0.06305939500089614, 0.1581936380649512,0.2592762165236786,0.3589842883205384, 0.4491542846588709,0.5237530566427051,0.5806638189836385, 0.2011334080862844,0.3049347792866978,0.4093092940034627, 0.5055265527628903,0.5860660664928973,0.6473011578337731, 0.3410385404813853,0.4476590365909358,0.5484671467389891, 0.6350798717696985,0.7022080829625521,0.4729450152996533, 0.5757634189631973,0.6671532416651529,0.7406455836915592, 0.5886123721388867,0.682355586840312,0.761147081998915, 0.6837147822732754,0.765634519258382,0.7580253719184177, -0.5980610514568759,-0.5477295977070786,-0.4818747107897384, -0.4002556178808948,-0.3056524524744078,-0.2038805884680659, -0.102179801407443,-0.006890393767211274,0.07805400320688782, -0.4999225208961594,-0.4363106684051463,-0.3565940723019476, -0.2625351309487317,-0.1594214993217877,-0.0549134894376971, 0.04356549235716502,0.1310565626039044,-0.3811861908796716, -0.3032604317093944,-0.2105722545208477,-0.107535321253739, -0.001641189844283069,0.09898039401487412,0.1883562467232593, -0.2440298576445608,-0.1531593370054226,-0.05138446179122957, 0.05448758952315854,0.1561816678501543,0.2469078854412431, -0.09487972868743588,0.004642223416678918,0.1091686599118738, 0.2108696280700075,0.3025309778153461,0.05649572496138416, 0.1584211910021343,0.2589814921571267,0.3510585742974243, 0.1998858588754617,0.298075413103841,0.3897335832229792, 0.3277592945902335,0.4178993211010856,-0.07805410188638827, -0.09654989477487182,-0.1156801299839266,-0.1341453490447077, -0.1503967869358663,-0.1631208476237052,-0.1717048170733874, -0.1763517237002561,0.02821687967586119,0.015882211907606, 0.001803084777968777,-0.01336575229369984,-0.02858037077889998, -0.04271047578209198,-0.0549244526833743,0.1470965334761953, 0.140993103021656,0.1310545667952879,0.117197412734933, 0.100226360425432,0.0816416498682333,0.273572290929994, 0.2720829158426912,0.2637397329328206,0.2481180890825449, 0.2264390031249597,0.3996773221825281,0.3995925724178936, 0.3894173756029716,0.3691632841122737,0.5164083739773968, 0.514049378265622,0.4992114966670869,0.6166247865338805, 0.609293992020038,0.6970151178651957,-0.9161616153729887, -0.9347343784055889,-0.93968222548459,-0.9257960724039628, -0.8900379255247358,-0.8334050053153379,-0.7610423024299814, -0.6802788134766146,-0.8787071877689653,-0.8904371961034525, -0.8856636768163644,-0.8596394026846126,-0.8111262103564944, -0.743728138896018,-0.6646636793709766,-0.581842306375343, -0.8216809647863927,-0.8231596650234216,-0.8059784419089511, -0.7670189419545808,-0.7076799521032789,-0.6339122309388853, -0.5537209611836944,-0.7427083018941957,-0.7314571731747196, -0.7012133398208896,-0.6514023484259069,-0.5859175101354265, -0.511730458968065,-0.6430242175142256,-0.6188528748169413, -0.577817181345379,-0.5218263992188138,-0.4560492515462431, -0.5282587532766855,-0.4937121240962341,-0.4464002188664107, -0.3896719563417937,-0.4070385900414059,-0.3663927387120716, -0.3177184789930138,-0.2880885834764299,-0.2456792441590282, 0.5980613131647216,0.5477298128889157,0.4818748986235024, 0.4002557953563768,0.3056526230838691,0.2038807357406828, 0.1021798945147061,0.006890401333784563,0.6591961567279382, 0.6058950950047038,0.5342799125746878,0.4447327915277979, 0.3416191699708766,0.2325993338657758,0.1260188711040966, 0.7166389355513205,0.658631931443462,0.5797448495418049, 0.481678385980196,0.3708138719979267,0.256391213430893, 0.7645097528661115,0.6997519467043368,0.6125989595293794, 0.5067270065580872,0.3904111886835731,0.797087559189558, 0.724147921463701,0.6291765311790336,0.5179208049695498, 0.8109706900869452,0.7300074788244177,0.6294473765659205, 0.8064720598658887,0.7194304887783171,0.7871548899782018, -0.4366431953633789,-0.5296368489080719,-0.6250679008877252, -0.7161969218969775,-0.7951587738972527,-0.8554711457309036, -0.894357332660672,-0.9132140384692056,-0.4179000270237177, -0.5130217272422377,-0.6088499338150114,-0.697440817161931, -0.770571681316741,-0.8228096498409901,-0.8533624251889405, -0.8654069300758386,-0.3897342152274581,-0.4841961940355134, -0.5770900179397438,-0.659947012990183,-0.7252694054566711, -0.769475954957455,-0.7936687210308288,-0.3510591528670133, -0.441802940329269,-0.528708833131731,-0.6037960859260583, -0.6611957546281005,-0.6992451996033126,-0.3025315118038733, -0.3871149210682015,-0.4662800296194353,-0.5333768673612823, -0.5843857415998933,-0.2469083611836815,-0.3241948947822677, -0.3955149325344624,-0.4558201674611825,-0.1883566309643827, -0.2585114083922621,-0.3230016809603438,-0.131056817682024, -0.1949865131648375,0.9161619708326186,0.934734728353398, 0.9396825386256142,0.9257963253168833,0.8900381161951118, 0.8334051579656884,0.7610424573388179,0.6802790093210535, 0.95606107640987,0.9727986783919703,0.9719596947667498, 0.9481265951474337,0.8996133652092656,0.830024065837643, 0.7470250693356176,0.98459889389352,0.9957514224668438, 0.985272860446651,0.9487272931457502,0.8869743499570832, 0.8065039787728745,0.9954874564049441,0.9969183323524673, 0.9737757915263076,0.9239648191561672,0.851378736753008, 0.9840623116657442,0.9728010399724041,0.9364058649128151, 0.8757746689964236,0.9495572492185336,0.9251912324727678, 0.8778794078487933,0.8957919700911449,0.8605602916932662, 0.8295641154807277,0.1778171059718252,0.1077330169440829, 0.02718289859111821,-0.06111631614771156,-0.1521342296531137, -0.2395840100830868,-0.3178878068603045,-0.3837872586817543, 0.2456801528075706,0.1743614211692323,0.0913009251307558, -9.150547636147518e-005,-0.09380226130703311,-0.1828695547719357, -0.2617550281067236,-0.3277601386896922,0.31771932061924, 0.2451950112624202,0.1597779786826127,0.06571158217888019, -0.03010074909289372,-0.1203656141836725,-0.1998866892639353, 0.3896727248396884,0.31551207359359,0.2277347108654265, 0.1315171099293629,0.03437982180295363,-0.05649645964725484, 0.4560499397602033,0.379603102905924,0.2896405921772683, 0.1921815993207149,0.09487917246041,0.5117310565618495, 0.432706773459909,0.3413166137711556,0.2440295330914618, 0.5537214556082512,0.4726319336973814,0.3811861035860583, 0.5818426869442771,0.4999226354150897,0.5296361374668247, 0.6250673427588886,0.7161965757306298,0.7951586652134927, 0.8554712518134584,0.8943575917970892,0.9132143767653008, 0.6095437192932451,0.7099831823616649,0.80114225201565, 0.874273305605062,0.9239434255089938,0.9498851810027883, 0.6864630907143527,0.7872122199392447,0.8728983750907815, 0.9353918603084618,0.9717433027190985,0.7529076094482211, 0.8481358928542101,0.9232232087908666,0.9723005987504904, 0.8019213565969565,0.8865248509843126,0.9481833022791925, 0.8298631139894471,0.9011831013407439,0.8376467778090524, 0.09654990728233075,0.1156802730367007,0.1341456406128616, 0.1503972383762518,0.163121458312337,0.1717055726654157, 0.1763525997352119,0.1949861891736041,0.2217704348994384, 0.2472029328538624,0.2686947191514402,0.2839095008331465, 0.2917169688378368,0.2925778501087149,0.2880893594612404, 0.3230012075937738,0.3570186650821726,0.3862979863651042, 0.4071207791101573,0.4171265008378126,0.4163707105097114, 0.4070391829394841,0.4558196682853914,0.4939031399298617, 0.5227372695226704,0.5383590521019039,0.5395474601226077, 0.5282592036287527,0.584385354546882,0.6217221861558012, 0.6452876111171104,0.652151703940053,0.6430245879023526, 0.6992450234541641,0.7309800986736478,0.7458180696675939, 0.7427086516187589,0.7936687790069655,0.8166219571778315, 0.8216813253645509,0.8654071763861386,0.8787075572966508, 0.2617543171369139,0.1828690638929441,0.09380207139517749, 9.165794599607253e-005,-0.09130044825545007,-0.1743606863382632, 0.1203649946594492,0.03010042800399446,-0.06571155052616354, -0.159777601870924,-0.2451943556628112,-0.03438026636045293, -0.1315172003159161,-0.2277344419143756,-0.3155115060038017, -0.1921818096757193,-0.2896404384341761,-0.3796026338863787, -0.3413165804505149,-0.4327064149448353,-0.4726316954005857, -0.02821670874193007,-0.1260185867435537,-0.2325989754896013, -0.3416187871825309,-0.444732423910872,-0.5342795725490167, -0.6058947680736913,-0.6591958160975573,-0.1470960585711635, -0.2563906423085273,-0.3708132697350703,-0.4816778179392841, -0.5797443521912047,-0.6586314997511467,-0.7166385361913998, -0.2735715277331494,-0.3904103835191622,-0.5067262463544535, -0.6125983088644244,-0.6997514194040853,-0.7645093154710443, -0.399676350745083,-0.5179198795056302,-0.6291757405426562, -0.7241473051258307,-0.7970870947583816,-0.516407321060813, -0.6294464664763966,-0.7300067759514388,-0.8109701936315704, -0.6166237809711579,-0.7194297010446339,-0.8064715143730887, -0.697014250125597,-0.7871542772926711,-0.7580246814516085, -0.2925772011093052,-0.2917164801053564,-0.2839091960301776, -0.2686946036173254,-0.2472029920942969,-0.2217706416368722, -0.4163702688077253,-0.417126245587897,-0.407120730642943, -0.3862981411371962,-0.3570189974757899,-0.5395471670390704, -0.5383589526996108,-0.5227373833229371,-0.493903460730213, -0.6521514808212688,-0.6452875761129986,-0.6217223634380962, -0.7458178526549705,-0.730980062865163,-0.8166217206889562, -0.7470248243274916,-0.8300238938854384,-0.8996132253994521, -0.9481264427436803,-0.9719595014156921,-0.9727984419199607, -0.9560608144734745,-0.8065037168656155,-0.8869742038831786, -0.9487272157391016,-0.9852727972566903,-0.9957513357128307, -0.9845987728990332,-0.8513784827612589,-0.9239647227267276, -0.9737757945994621,-0.9969183688503204,-0.9954874793720167, -0.8757744250303561,-0.9364058061885231,-0.9728010979637555, -0.9840624137666333,-0.8778791504147609,-0.9251911695017921, -0.9495573085262712,-0.8605599852861939,-0.8957918541675716, -0.8295637303264062,-0.04356566771769104,0.05491337401809196, 0.1594214223252114,0.2625350815284993,0.3565940574819209, 0.4363107079656693,-0.09898073214385947,0.001640881330767951, 0.107535036331357,0.2105720065923513,0.3032602493938258, -0.1561821374463407,-0.0544880626634626,0.05138399870775862, 0.1531589210125483,-0.2108702003917341,-0.109169268647464, -0.004642833898907788,-0.2589821529532703,-0.158421915656623, -0.2980761626942871,-0.9498849702325406,-0.9239433259414138, -0.8742733756226419,-0.8011425227232617,-0.7099836412348224, -0.6095443176654687,-0.508553964054817,-0.9717432666228709, -0.9353919526972201,-0.8728986221002711,-0.7872126141915534, -0.6864635931358892,-0.5806643774075225,-0.9723007209152063, -0.9232234443044223,-0.8481362314873311,-0.7529080174160984, -0.647301592079592,-0.948183485298304,-0.8865251005609038, -0.8019216417165289,-0.7022083677250226,-0.9011831985532854, -0.829863222791688,-0.7406456753648975,-0.8376466557989706, -0.7611469342493191,-0.7656341022868733,0.6646639924076627, 0.7437284087719479,0.8111264735787829,0.8596396929680696, 0.8856640083476979,0.8904375589329932,0.6339126501414293, 0.707680317140174,0.7670192843558462,0.8059787882713181, 0.8231600236208387,0.5859180191150533,0.651402785083716, 0.7012137302542939,0.7314575393289862,0.5218269830903716, 0.577817672809353,0.6188532957478536,0.446400869266141, 0.4937126651205253,0.3663934528166599,-0.4542528774843695, -0.3845634904875965,-0.3006176376093279,-0.2069068719953542, -0.11039298336186,-0.01813638876408994,0.06488637319187028, -0.5237534893339546,-0.4491544873380491,-0.358984163651417, -0.2592757174096171,-0.1581927886880057,-0.063058272648828, -0.5860663263409258,-0.5055265231286077,-0.4093088881435918, -0.3049339801812209,-0.2011322717929374,-0.6350799196461491, -0.5484668466671915,-0.4476583293687135,-0.3410374453295617, -0.6671530327423928,-0.5757628211692016,-0.4729440070010416, -0.6823550906513535,-0.5886114742048517,-0.6837139951578899, 0.8533626369422408,0.8228097648480324,0.7705716341879386, 0.6974405664026642,0.6088494821992544,0.513021117997808, 0.7694759566268259,0.7252693007743299,0.6599467606818161, 0.5770896067011005,0.4841956488153404,0.6611955122025592, 0.6037957522821978,0.5287083985893539,0.441802418256347, 0.5333764308745213,0.4662795459560942,0.387114402450423, 0.3955144253601533,0.3241943959991313,0.2585109731392027, 0.05492543133462732,0.04271134692005163,0.02858111208036886, 0.01336634929270618,-0.001802636489293655,-0.01588190769759986, -0.08164059192530317,-0.1002253860034311,-0.1171965412658538, -0.1310538163159151,-0.1409924872128895,-0.2264379065659517, -0.2481170428345225,-0.2637387553592617,-0.2720820325977006, -0.3691621981499088,-0.3894163023521484,-0.3995915326289882, -0.4992104641187212,-0.5140483226358935,-0.6092930383066186, -0.1282040992012935,-0.03314438343075717,0.07418771901881034, 0.1895805529161077,0.3060111300013609,0.4152646181118619, 0.5105522497698926,0.5884153641804594,0.6488215902264565, -0.1662652220900749,-0.06672909617768248,0.04565193884691998, 0.1653145024136672,0.2838178720038453,0.392358453375388, 0.4847688072125908,0.5588878800183537,-0.2061837828129686, -0.1028578719693947,0.01320461245153456,0.135055834380525, 0.2533186742384155,0.3594175117480762,0.4483631157317138, -0.2454367954185786,-0.1392470619788042,-0.0212985877993455, 0.1003748549375823,0.2162630814588294,0.3187734758296685, -0.2808945465452747,-0.1730244679184136,-0.05523589088936585, 0.0639822904424641,0.1758296024995542,-0.309764254307227, -0.2016807651564305,-0.08611192469610081,0.02876188488837943, -0.3305621657917769,-0.2239243298387429,-0.1123798946526014, -0.3434303716667939,-0.2398370895892076,-0.3496821715684524, 0.6714420577705413,0.7524953419593941,0.8307215647087252, 0.8988911695232938,0.9496341036793501,0.9781045443142695, 0.983882155708098,0.9706580078492304,0.9441720387917811, 0.7131190377206373,0.7979983822227008,0.8766844493347566, 0.9405391525406509,0.982132974036711,0.9983759032186432, 0.9915700817355307,0.967642375326243,0.7477894668983293, 0.8327562120383802,0.9072360592139173,0.9621573628309809, 0.9915144459980433,0.9950116576093856,0.9775308002021622, 0.7702158662916526,0.8504518596256061,0.9159087827490561, 0.958615376965025,0.9752335996497555,0.9682499097067426, 0.7760312926982584,0.8470020976881563,0.9002461340540874, 0.9301900108044431,0.9363385958760152,0.7636458947692996, 0.8226631878609201,0.863227169508762,0.8824665400301602, 0.7350862725086435,0.7819435892168385,0.8116669810370153, 0.6950244929211268,0.7313851393296726,-0.9441722324566724, -0.8982335639429714,-0.8313456723553714,-0.7414326155290212, -0.6303269409870232,-0.504437732215241,-0.3731115220313701, -0.2455227591385536,-0.9277610900314076,-0.8719938339703784, -0.7922901462160301,-0.6879655695561273,-0.5635230570387244, -0.4282074607020305,-0.2928561738545139,-0.8935348618728204, -0.8252311012303484,-0.7312055040636086,-0.6132787902902372, -0.4790574606800003,-0.3397875802758924,-0.8379237951960332, -0.7555203588771385,-0.6479597593660587,-0.5201883296648473, -0.3821929206720374,-0.7605084662990487,-0.6648552402767216, -0.5474979380988695,-0.4159703173399101,-0.6652563588950895, -0.5599712780007364,-0.4386104476633646,-0.5596790773588694, -0.4500513701337736,-0.452215421939787,-0.3130828685601688, -0.2026653349757201,-0.07519795113003093,0.06519223482424771, 0.2106381298878163,0.3511437862186094,0.4776948653078811, 0.5848777234045024,-0.3092743464263315,-0.189541493899996, -0.05216564154993419,0.09665867971286488,0.2468076262033025, 0.3871266155400391,0.5092302530840672,0.6095258724352348, -0.299731815412124,-0.1710988752936458,-0.02562616156907596, 0.1281943739657924,0.2786088475509629,0.4146241489277275, 0.5296074119458033,-0.2831889282507517,-0.1472477576556685, 0.002978788421451789,0.1571443739074394,0.3031991278501129, 0.4316900017871718,-0.2593820240207756,-0.1190520671608967, 0.03150460300683353,0.1812458940061291,0.3193074202631062, -0.229491624834242,-0.08860645786522432,0.05782435035907074, 0.1994358494400003,-0.1959070295313489,-0.05832274784344905, 0.08053951964108649,-0.1613858092941266,-0.03012854522533278, -0.671442351589465,-0.7524955042654247,-0.8307216077305004, -0.8988911324269672,-0.9496340459616871,-0.9781045247295723, -0.9838822098556939,-0.9706581405182357,-0.6732379910932457, -0.7555353929734253,-0.8321927576733623,-0.8949176301047361, -0.9365114268652753,-0.9538841305299445,-0.949106939734586, -0.6637939293172054,-0.7437729138119192,-0.8147968652653733, -0.8684734875620626,-0.8990751104656249,-0.906028082404147, -0.6398902977415805,-0.7135875679260879,-0.7753830615613055, -0.8180895331241328,-0.8383689548572043,-0.6002017842451572, -0.6645162706136697,-0.7153675946298389,-0.747703849697991, -0.5464363016035276,-0.6002045214061834,-0.6407682965760649, -0.4830988161196208,-0.5271645300902241,-0.4158550110968205, -0.648822290296783,-0.6472486014147985,-0.6344626299567083, -0.6075203746111165,-0.5653935344751363,-0.5100061066600627, -0.4459169294668335,-0.3786832614419487,-0.7313855414086173, -0.7310744930343959,-0.7160642832118092,-0.6830433558919348, -0.6318400429346884,-0.5662582509809229,-0.492781618346183, -0.418059300054038,-0.8116671044980045,-0.8099276517898805, -0.7895071237697285,-0.74756375917153,-0.6857578728711079, -0.6101862708902825,-0.5288485365725882,-0.882466459585, -0.8755369303175729,-0.8463852653947486,-0.7938122127531423, -0.7219269876119373,-0.6386807736786417,-0.9363384293729856, -0.920580474049937,-0.8807752703984302,-0.8181738478934094, -0.7389956620788968,-0.9682497764059059,-0.9417119830617079, -0.8917766410561483,-0.8219783739569782,-0.9775307754986758, -0.9407006676234038,-0.8832579304805722,-0.9676424743883053, -0.9228815898031446,0.3130823317650696,0.2026646799488628, 0.07519720122433592,-0.06519302988114704,-0.2106389019045293, -0.3511444697687643,-0.4776954195825031,-0.5848781397282268, 0.2455618355531619,0.1217043818280624,-0.01891222872758066, -0.1695413327023869,-0.3196901877897432,-0.4582042349710876, -0.5770670118160844,0.1655447705464922,0.0289439344448441, -0.1220494268493898,-0.2778581225722938,-0.4262841911656161, -0.5567786600361208,0.07498802093385774,-0.07139865292607843, -0.2274740800198117,-0.3816395081442781,-0.5218451060581218, -0.02151295053653907,-0.1724761631672423,-0.3268548028226648, -0.4727737792825113,-0.1175093448034605,-0.2667795638863601, -0.413210185645996,-0.2066526242811376,-0.3486960301430706, -0.2845979196243639,0.1282040764044601,0.03314425179116901, -0.07418801789892716,-0.1895810572320515,-0.3060118317848469, -0.4152654547344749,-0.51055312502423,-0.5884161844630592, 0.03012854220925541,-0.07822075478791662,-0.1975265605100549, -0.3210457437412529,-0.4395492890997685,-0.5442335497807274, -0.6297193044589443,-0.6950252371327137,-0.08053951945115613, -0.2008908777469321,-0.3287498381001709,-0.454849657793549, -0.5688642328312289,-0.6631668267266261,-0.7350870708018485, -0.1994358711312821,-0.3279452265433291,-0.4583916832350944, -0.5800652917395807,-0.683455813944916,-0.7636467367251844, -0.3193074905789999,-0.4498985165051642,-0.5758541835274278, -0.6869055692486817,-0.7760321386045594,-0.4316901383846216, -0.5576908306091364,-0.6732598101682218,-0.7702166526708257, -0.5296076162752261,-0.6457739738857279,-0.7477901272839138, -0.6095261314476621,-0.7131195245736661,0.6472479688324123, 0.6344620803203709,0.6075199046816795,0.5653931175085022, 0.5100057007389891,0.4459164955009389,0.3786827774326808, 0.5474099886510654,0.5236257914257873,0.4857185197867951, 0.4345152489333596,0.3738198763531287,0.3091172837171857, 0.4250511511939136,0.389683753785766,0.3423573476615138, 0.2859346018498913,0.2253099468416643,0.2835632478824897, 0.2385758256608239,0.1860028309402729,0.1299534657091715, 0.1312824242313411,0.08112906023855893,0.02887591783637235, -0.02047837605993849,-0.07041365620347247,-0.1612832799773341, 0.8982333732257657,0.831345524857385,0.7414325451852105, 0.6303269573377657,0.5044378109359756,0.3731116142445962, 0.2455228128124526,0.9228814872695982,0.8667983517234625, 0.7868465046935174,0.6823837598597929,0.5579412946135831, 0.4227639330815517,0.2876608055402039,0.1613856625640601, 0.8832579654623612,0.8143439687072217,0.7198955735651289, 0.6018165967179313,0.467747528435525,0.3289004093680582, 0.1959067638111305,0.8219785498827571,0.7387750958937861, 0.6307665078242817,0.5029950256220953,0.3654474931658011, 0.2294912586325793,0.7389959159190775,0.6425282428469168, 0.5248781214125594,0.3936431094566906,0.2593815856416469, 0.6386809855643202,0.5327535820451708,0.4113926019080829, 0.2831884462269615,0.5288485749239231,0.4188791724037085, 0.2997313094215869,0.418059069948,0.3092738247890606, 0.6297184498311313,0.5442326731425132,0.4395485037972626, 0.3210451478850294,0.1975261966743493,0.07822060158766293, 0.6631659475585863,0.5688633866559212,0.4548489689298922, 0.3287493895928871,0.2008906793070462,0.6834549460780991, 0.5800645299836926,0.4583911438911752,0.3279449596345045, 0.6869047733719595,0.5758535688432742,0.449898169624651, 0.6732591535970365,0.5576904114999332,0.6457735063412144, 0.9277609198709332,0.949106860839256,0.9538841631800392, 0.9365115581247451,0.8949178072878723,0.8321929059126493, 0.755535446210326,0.6732379126875105,0.8935348059013593, 0.9060281564869469,0.8990753265127792,0.8684738109832391, 0.8147972186077681,0.7437732084300421,0.6637941002839092, 0.8379239537972928,0.8383692759036656,0.8180900037577743, 0.7753836155369005,0.7135881068095284,0.639890731966599, 0.7605089118538748,0.747704461889213,0.7153683254339585, 0.664517027376214,0.6002024649156598,0.6652570996829634, 0.6407691682823238,0.6002054473372921,0.5464371824658241, 0.5596800524090022,0.5271655706244485,0.4830998363360157, 0.4522165310007144,0.4158561136906575,0.3496833143594963, -0.2876608652754351,-0.4227639382532795,-0.5579412897306785, -0.6823837835510913,-0.7868465717409772,-0.8667984500087526, -0.3289005616515215,-0.4677475887278177,-0.6018165999573247, -0.7198955529773995,-0.8143439414326642,-0.3654477029697049, -0.5029950893382186,-0.6307664561929963,-0.7387749669914671, -0.3936433402523272,-0.5248781506021705,-0.6425281057326593, -0.4113928310116375,-0.5327535685472626,-0.4188793967911134, 0.577066822550539,0.4582039092088336,0.3196897246958904, 0.169540766109225,0.01891161845986538,-0.1217049732030892, -0.2455623627393322,0.5567787306370593,0.4262841209617862, 0.2778578974521733,0.1220490717256491,-0.02894436515050694, -0.165545217365127,0.5218454423524129,0.3816396895816639, 0.2274740807377008,0.07139849210836301,-0.07498829123147716, 0.4727743520975624,0.326855199205205,0.1724763529977552, 0.02151295276860444,0.4132109394322543,0.2667801205874505, 0.1175096785325448,0.3486969033343195,0.2066532886754674, 0.2845988618411943,-0.9915701323913838,-0.9983759139464571, -0.9821329817122836,-0.9405392168410572,-0.8766846298252051, -0.7979987142017131,-0.9950116201913212,-0.9915144412787341, -0.9621574590524813,-0.9072363224101446,-0.8327566769720253, -0.9752335225013484,-0.9586154363208594,-0.9159090584602802, -0.8504523952727223,-0.9301899969464084,-0.9002463663906001, -0.8470026365747616,-0.863227339500068,-0.8226636840857314, -0.7819440310902068,-0.3091177896659217,-0.3738203674077975, -0.4345157440029583,-0.4857190441849699,-0.5236263640902669, -0.5474106128468904,-0.5588885443532919,-0.2253103954606045, -0.2859350592025221,-0.3423578252345214,-0.3896842610441149, -0.4250516888435538,-0.4483636748755489,-0.1299537462879293, -0.1860031284708927,-0.2385761459990237,-0.2835635916740772, -0.3187738370923436,-0.02887592341002545,-0.0811290830782055, -0.1312824709082232,-0.175829673802738,0.07041398450649188, 0.02047868355036482,-0.02876160928094873,0.1612839336168132, 0.1123805173372347,0.2398380094723457,-0.5092306348673886, -0.3871271308997349,-0.2468082566335953,-0.09665937383397012, 0.05216495245602301,0.189540870507144,-0.4146244745670597, -0.2786093002807504,-0.1281949271302634,0.02562556437243592, 0.1710982989127918,-0.3031993859046618,-0.1571447546490307, -0.00297925984664287,0.147247252029971,-0.1812460903983632, -0.03150492197167008,0.119051661271318,-0.05782450627681404, 0.08860617741886109,0.05832260658459812,-0.4847695337402862, -0.3923591556376936,-0.2838184503258238,-0.1653148824643805, -0.04565210375030839,0.06672910665806144,0.1662653402185898, -0.3594180507425287,-0.253319097564816,-0.1350560618895416, -0.01320461850800903,0.1028580486723768,0.2061840662709727, -0.2162633298539993,-0.1003749112365929,0.02129875586186771, 0.139247419944916,0.2454372621958313,-0.06398217348632655, 0.05523623192861417,0.17302500577526,0.2808952005684321, 0.08611242071710691,0.2016814632806757,0.3097650809527805, 0.2239251562628219,0.3305631346577989,0.3434314452768145, 0.4927814627145429,0.5662581542385627,0.6318399645986254, 0.683043240780333,0.7160640841376876,0.7310741891309572, 0.6101863849438733,0.6857580324047194,0.7475639112793486, 0.78950721141067,0.8099276375885235,0.7219272504872226, 0.7938124902720932,0.8463855099577528,0.8755371018531016, 0.8181741141101887,0.8807755242176112,0.9205806914487843, 0.8917768010350154,0.9417121277771469,0.9407006846750625, 0.2928563777418555,0.4282076937835696,0.5635232482078705, 0.6879656610506216,0.7922901195775008,0.8719937126447483, 0.3397879564876633,0.4790578522946484,0.6132791099941464, 0.7312056921497674,0.8252311496217807,0.3821934817691987, 0.520188888877212,0.6479602207299933,0.7555206677931847, 0.415971059154168,0.5474986596025796,0.6648558465528478, 0.4386113475958364,0.5599721413103722,0.4500523932965443 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<812, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*812] = { 0.9756767549555487,0.9922508097125681,0.9977223655964853, 0.9881046683022422,0.9605519470607568,0.9146195529015363, 0.8528238339140162,0.7799959774035472,0.7017828679367573, 0.6231965669156313,0.9837002757017411,0.9971982264747434, 0.9970195212545997,0.9790578176231792,0.9411837637692063, 0.8844913295435498,0.8132898830804115,0.733693935416079, 0.6517368229295557,0.9797764734635961,0.9875546637606186, 0.9791028733313679,0.9508425500024201,0.9020818068674743, 0.8358206809420552,0.757892552927626,0.6749593629473696, 0.9602286065935876,0.9595713376770644,0.9407948278222147, 0.9016229543668696,0.8432733179563544,0.7704382634077368, 0.6896667228240099,0.9226923908191266,0.9116587851060941, 0.8819863001926875,0.833225536765196,0.7683765355083914, 0.693030195174853,0.8672949635738141,0.8455181303237055, 0.8062955359163166,0.7509515682311381,0.6835651897016059, 0.7970712198937139,0.7659650471206068,0.7200326020746473, 0.6617902410567805,0.7171844959174759,0.6793953685069648, 0.6300938559406523,0.6333774065241421,0.5918819525326566, 0.5505632701289798,0.4375940376503738,0.3744427273176402, 0.2993381563954681,0.2130515669413183,0.1182813882301629, 0.01954824200640591,-0.07773888316805401,-0.1686715778074173, -0.2499271804752164,-0.32007301315036,0.4854882574576871, 0.4194229010537701,0.3399338752288831,0.2482942679442456, 0.1481384200976902,0.04502062388977755,-0.05499873367858239, -0.1469877513574829,-0.2280773868570659,0.5324004751734927, 0.4628532125512143,0.3786136701220479,0.2817283713716202, 0.1769156386035929,0.07062416136199023,-0.03081255771979229, -0.1228131994141411,0.5750755655636769,0.5013629074790578, 0.4122166091807719,0.3107083252171691,0.2025911492637849, 0.09481562164394035,-0.006505365777837463,0.6099587706972414, 0.5315747218240751,0.4378921540503243,0.3330794318970117, 0.2235852429888137,0.1162930447896056,0.6341450324413492, 0.5511580300049011,0.4540605894555947,0.3478844246654598, 0.2392124945153528,0.6463093264667951,0.5595982440953389, 0.46086512448968,0.3555189519163455,0.647046909800018, 0.5581741842011717,0.4598430939447178,0.6384375755240163, 0.5492419252084272,0.3200724178002418,0.4163027723501219, 0.5178612432225475,0.6201479032959074,0.7171164359379398, 0.8025495773806206,0.8717669667532965,0.9228269911622408, 0.9565659576168886,0.3662566345969678,0.4693191708546967, 0.5762858482292442,0.6811797034275579,0.7768921407658641, 0.8570710368648269,0.9180184145424319,0.9594305591455454, 0.4128244293574089,0.5208884118196857,0.6304558785383466, 0.7343501607073976,0.8250439179808262,0.8969571451990621, 0.9480541165102998,0.4569904905821253,0.5671264332790738, 0.6755415775491402,0.7744719807282637,0.8569290887080647, 0.9189090911818834,0.4955267041670984,0.6040846468052238, 0.7074267562469234,0.7981205092043148,0.8705859012982401, 0.5255624261223592,0.6289645166670731,0.7241496615411406, 0.804855834362468,0.5454521871592556,0.6410312785579098, 0.7264644091774437,0.5551982228864044,0.641627582268501, 0.5561969667017818,0.2502538949373057,0.2872027754172267, 0.3245344712904296,0.3600408236839537,0.3911506315330521, 0.4155633223527059,0.4319414682718341,0.4402542183539166, 0.4415840008368697,0.3505479885894233,0.3943500788462172, 0.43685649380825,0.4748347886538213,0.5050141859852789, 0.5250961060909611,0.5345072780254662,0.5344003893389805, 0.5269837056592495,0.4574833953114167,0.5069750581222974, 0.5525393550046852,0.5900243847437713,0.6159403270481748, 0.6286926742607352,0.6290385665487288,0.6194911060068102, 0.5665551499276381,0.6191962274832306,0.6644606027057701, 0.6977977540386979,0.7162925017359711,0.7197189132272425, 0.7103565350090897,0.6716249671619864,0.7237430894816727, 0.7648128117488485,0.7907287689487973,0.7998964507309693, 0.7936885378432519,0.7661563418365048,0.813819159923423, 0.8474217166041815,0.864080531689031,0.8638072256614686, 0.8449704612165639,0.8851005033495888,0.9095132315221558, 0.9168712118355733,0.9055760485508874,0.9366559154796388, 0.9518640240021961,0.9483465315553231,0.9704010313875515, -0.4375932291383154,-0.3744420515634606,-0.2993376542433732, -0.213051272338593,-0.1182813170567518,-0.01954838456202594, 0.07773856031405028,0.1686711209781245,0.249926635788548, -0.3473076758598704,-0.2731773531779083,-0.1869784560548725, -0.09084077595186728,0.01090483422160223,0.1124324367516528, 0.2079533392573423,0.2932321828182562,-0.2423880400933935, -0.156942680061891,-0.06052677114529338,0.04301840739087177, 0.1478309256372904,0.2474621200563187,0.336722136779245, -0.1245894223683107,-0.02900257303954696,0.07490364945070915, 0.181648017057727,0.2845287164429474,0.3775439714321544, 0.00186176882165046,0.104598795853549,0.2116013903531214, 0.3164139369537128,0.4125877669907546,0.1306304412032901, 0.2361087796289779,0.3411560209375105,0.4393820957485916, 0.2546621526726749,0.358132074959863,0.4568650785571312, 0.3679945821324933,0.465540692618106,0.46700138061564, -0.6231952788307172,-0.5574600324007678,-0.4770927236000206, -0.3823142165271753,-0.2756046525485073,-0.1617836973833182, -0.0470869101281101,0.06243545366421368,0.1623223370722767, -0.5492408053824432,-0.4724705019699099,-0.3803275558898422, -0.2743443251752258,-0.1587631541423281,-0.04000763487086941, 0.07495625978661888,0.180495590370777,0.2733676095225912, -0.4598422233986804,-0.3712294758837851,-0.2676544688598244, -0.1524290150215411,-0.03159943316739083,0.08740013657156358, 0.1978753137579477,0.2954972412516565,-0.3555183995893311, -0.2556130138286056,-0.1427706895826486,-0.02217694060430834, 0.09888849629266881,0.2130604246988013,0.3149362814931866, -0.2392122924568965,-0.1303824105317197,-0.01228273317409482, 0.1085468771341984,0.2246722751170698,0.3298926161068582, -0.116293170907997,-0.002544855969777311,0.1157810363722986, 0.2317919381153067,0.3389908350437634,0.006504983859799631, 0.1204470178133221,0.2342680443317885,0.3417324928044827, 0.122812659812205,0.23268986296373,0.338624103921372, 0.2280767837862029,0.330902937710777,-0.2502532651867688, -0.2872021140968067,-0.3245337697854004,-0.3600400745299252, -0.3911498325365141,-0.4155624805143979,-0.4319406005716203, -0.4402533481355587,-0.4415831518511793,-0.1708721571930995, -0.2041871265271164,-0.2379683866812855,-0.2700974862342881, -0.2982093646817494,-0.3203587126138262,-0.3356190104303782, -0.3442372407872382,-0.08038054194912378,-0.1091996275570321, -0.1389306574098392,-0.1677554863057991,-0.1936713782240709, -0.2150838382608105,-0.2312629415986541,0.01921174674137868, -0.004986017519443271,-0.03105772275158658,-0.05758606025521312, -0.08288952042427381,-0.1055085254918716,0.123925135248786, 0.1034734912606135,0.07972411299125522,0.05380820853884998, 0.02732027507363822,0.2282811487692264,0.2098640891542395, 0.1865989201629623,0.1596028170589836,0.3265627679363224, 0.3082244742822114,0.2838117948729975,0.4142824694706087, 0.3944807806873461,0.4890558390601624,-0.975676581463901, -0.9922507181739645,-0.9977223240044052,-0.988104613901425, -0.9605517932270823,-0.9146192114249708,-0.8528232434185343, -0.7799951208906594,-0.7017817703686649,-0.9704008071255719, -0.9831225967982262,-0.9822981052334858,-0.9639034131304861, -0.9258761899238439,-0.8693365652843744,-0.798567800167315, -0.7196174154484367,-0.6384363279989543,-0.951863752574358, -0.9581118567763953,-0.948488121885417,-0.9195867065770826, -0.8708257888235816,-0.8052054331206489,-0.7284489979298084, -0.6470457280542714,-0.9168708930751738,-0.9141083375203286, -0.8939111910264987,-0.854235225343551,-0.7963893637954717, -0.7249746765437297,-0.6463082287051728,-0.8638068526880216, -0.8504293560497204,-0.8194747846295519,-0.7707138739098191, -0.7071466871144221,-0.6341440232385527,-0.7936880971710701, -0.7697465167330589,-0.7297586716006348,-0.675179694541184, -0.609957839140293,-0.7103560109842373,-0.6776367931781411, -0.631704229014305,-0.5750746905699001,-0.6194904872158699, -0.5808664875389803,-0.53239963484432,-0.5269829891706682, -0.4854874364134862,0.5574614519339011,0.4770942414262169, 0.3823157740702657,0.2756061719864941,0.1617850988203112, 0.0470881327099307,-0.06243443855232068,-0.1623215265517549, 0.5809489294799253,0.4937832424572646,0.3911366273799454, 0.276734760526323,0.1567997280124966,0.03849904347704674, -0.07201766541821289,0.5981392304579348,0.5035962293740359, 0.3933108814734469,0.2724812113143264,0.1485413749948833, 0.02903407175199371,0.6059866911555354,0.5040926955212129, 0.3873829237054168,0.2624333853186085,0.1373130258988978, 0.6022642769367629,0.4940448560674484,0.3732152095431146, 0.2472094891883707,0.5864999399564288,0.4740709200953988, 0.352163126792074,0.5602791768912673,0.4464581629930486, 0.5266502279007443,-0.4163031917414612,-0.5178614819829612, -0.6201479767677171,-0.7171163801074496,-0.8025494402189062, -0.8717667933239608,-0.9228268112486158,-0.9565657823609164, -0.3309034260843112,-0.4319023018909033,-0.5371520139920846, -0.6408948133012854,-0.7362003328755568,-0.8167859635709049, -0.8788842105927732,-0.9220131313955344,-0.9483463221383831, -0.338624488411881,-0.4426206857782847,-0.5490725801393385, -0.6512627192307555,-0.7419563768480812,-0.8155735468033498, -0.8697858892475217,-0.9055758335701712,-0.3417327749428255, -0.4462719473348893,-0.5509105300296661,-0.6485010946606566, -0.7322978310246682,-0.7980541850077586,-0.8449702640289916, -0.3389910093648414,-0.4413180005751814,-0.5412519758066311, -0.6319456221810869,-0.7078189355434928,-0.7661561658452967, -0.3298926669562023,-0.427540138135818,-0.5206911591272092, -0.6034312473993601,-0.6716247883748602,-0.3149361858566656, -0.4062273552356623,-0.49166038715387,-0.566554922382665, -0.2954969768757202,-0.37970711664501,-0.4574830674709519, -0.2733671630067541,-0.3505475201547475,0.7196184465472099, 0.7985685715325342,0.8693370709616777,0.9258764738945257, 0.9639035601847371,0.9822982118031284,0.9831227403545156, 0.7284499433836592,0.8052061147659351,0.8708262265695167, 0.9195869704437927,0.9484883092395549,0.9581120573066315, 0.7249755313208718,0.796389971071354,0.8542356312683819, 0.8939114816111898,0.9141086068192968,0.7071474645768509, 0.7707144394707161,0.8194752023181837,0.8504297125393642, 0.675180421967678,0.7297592328398797,0.7697469803916117, 0.6317049359907486,0.6776373777282386,0.5808671950649753, -0.366257089693115,-0.2932325919216836,-0.2079536613378413, -0.1124326200188124,-0.01090482797043428,0.09084100501255125, 0.1869789126919778,0.2731780147607579,0.3473085031912251, -0.4128247445431558,-0.3367224114404735,-0.2474623076665533, -0.1478309677445388,-0.04301824976707492,0.06052715939621055, 0.1569432970144201,0.2423888563057751,-0.4569906864326022, -0.377544132020254,-0.2845287871856594,-0.1816479322770386, -0.07490335342398057,0.02900310611676332,0.1245901828483064, -0.4955268212110568,-0.4125878505007722,-0.3164139219124497, -0.2116012050517462,-0.1045983865863539,-0.001861117423562203, -0.5255625148012287,-0.4393821442371415,-0.3411559538741524, -0.2361085232789399,-0.1306299482392742,-0.5454522916908328, -0.4568651248612463,-0.3581319814379551,-0.2546618495309371, -0.5551983685993169,-0.4655407524301317,-0.3679944751189687, -0.5561971575770934,-0.4670014539093825,-0.5505634949474449, 0.9220133236751157,0.8788843849049742,0.8167861054780646, 0.7362004119831233,0.6408947895214253,0.5371518506414063, 0.4319019774051972,0.8697860623176014,0.8155736760665241, 0.7419564499373366,0.6512627126170119,0.5490724656572447, 0.4426204410195734,0.7980543166386451,0.73229789903684, 0.6485010946606566,0.5509104500833818,0.446271772176749, 0.7078190327552072,0.6319456504394643,0.5412519405164845, 0.4413178995840423,0.6034313501235972,0.5206912029562619, 0.4275401340932171,0.4916605546162393,0.4062274818767118, 0.379707405130632,0.3442381194341675,0.3356199216704505, 0.3203596326360576,0.2982102696268391,0.2700983579505985, 0.2379692158357015,0.2041879117102824,0.1708729017037819, 0.2312638113027555,0.2150847428759935,0.1936722946488835, 0.1677563920072661,0.1389315354365512,0.1092004686440709, 0.08038134335000687,0.1055093293250425,0.08289035011594331, 0.05758689641252075,0.03105854840479935,0.004986821056754707, -0.01921097131735246,-0.02731960194972041,-0.05380752683403425, -0.0797234336008201,-0.103472821018762,-0.1239244773230862, -0.1596023291970502,-0.1865984417823142,-0.209863618928254, -0.2282806816594251,-0.2838115205510092,-0.3082242217152255, -0.3265625234832059,-0.39448071777243,-0.41428243288548, -0.4890559628033717,0.1469872495241008,0.05499837995147388, -0.04502078247419547,-0.1481383500660343,-0.2482939617934105, -0.3399333541527281,-0.4194222062209155,0.03081218235410985, -0.07062432464607897,-0.1769155567176647,-0.281728039903757, -0.3786131170062194,-0.462852487954783,-0.09481577398373708, -0.2025910390450985,-0.3107079508004592,-0.4122160055133201, -0.5013621351348974,-0.2235850902780548,-0.3330789997683635, -0.4378914824575998,-0.5315738812374622,-0.3478839295034498, -0.4540598420421491,-0.5511571070104784,-0.4608643070543677, -0.5595972383031447,-0.5581731097866064,0.07201857797974105, -0.03849795696968416,-0.1567984874655333,-0.2767334157256128, -0.3911352491971175,-0.4937819036034042,-0.5809476860617484, -0.6517357063102128,-0.02903313746554977,-0.1485403228794575, -0.2724800804665481,-0.3933097299991974,-0.503595119108857, -0.598138209964731,-0.6749584587129783,-0.1373121735202105, -0.2624324872189829,-0.3873820250532686,-0.5040918441619855, -0.6059859240149241,-0.6896660583043277,-0.2472088184389502, -0.3732145606963823,-0.4940442637379011,-0.6022637655195766, -0.6930297734586861,-0.3521626999463403,-0.4740705579739117, -0.5864996568405977,-0.6835649861113746,-0.4464579863091031, -0.5602790756561028,-0.6617902107749168,-0.5266502592949665, -0.6300939487796368,-0.5918821259544012,-0.53439964190375, -0.5345065247441787,-0.525095375735572,-0.5050135034697202, -0.4748341677209202,-0.4368559346659283,-0.3943495717901673, -0.6290379264397734,-0.6286920435345361,-0.6159397381647077, -0.5900238619718436,-0.552538907016351,-0.5069746781213621, -0.7197183758293895,-0.7162919864381756,-0.6977972953381246, -0.6644602226954861,-0.6191959297333463,-0.7998960023058911, -0.7907283519716943,-0.7648124613084771,-0.7237428249931476, -0.8640801553848605,-0.8474213786178793,-0.8138188945925402, -0.9095129144421652,-0.8851002301776055,-0.936655652047691, -0.7336930479121847,-0.8132892661843129,-0.8844909854341375, -0.9411836430482039,-0.9790578295745921,-0.9970195644563975, -0.9971982203541059,-0.9837001754183022,-0.7578919095374027, -0.8358203200836417,-0.9020816987419813,-0.9508426122816337, -0.9791029969688041,-0.9875547507725127,-0.9797764622248338, -0.7704378741700656,-0.8432731964302291,-0.9016230372124288, -0.9407950105701793,-0.959571511273145,-0.960228691612206, -0.768376378413949,-0.8332256052212422,-0.8819865069139035, -0.9116590219002929,-0.9226925651801682,-0.7509515941742244, -0.8062957285440923,-0.8455183951051963,-0.8672952051616537, -0.7200327517213486,-0.7659653038152823,-0.7970714969272577, -0.6793955903243286,-0.717184776407749,-0.633377666107338, -0.1804949761426579,-0.07495545509988207,0.04000862921312944, 0.1587643045074381,0.2743455685619207,0.380328817183559, 0.472471714864844,-0.1978749051329674,-0.08739956539043256, 0.0316001612578226,0.152429864698119,0.2676553828144042, 0.3712303926692266,-0.213060219387871,-0.0988881700848137, 0.02217737847260294,0.1427712082619221,0.2556135701396871, -0.2246722609068409,-0.1085467937789958,0.01228287704184508, 0.130382595337822,-0.2317920983902931,-0.1157811822543603, 0.002544721985696509,-0.2342683636555594,-0.1204473722877317, -0.23269032918574,-0.9594304364836954,-0.9180182641419954, -0.8570708740276961,-0.7768920058412835,-0.6811796536024748, -0.5762859389154773,-0.4693194383350184,-0.9480540609614943, -0.8969570397650206,-0.8250437852464129,-0.7343500529080861, -0.6304558624488807,-0.5208885452010862,-0.9189091107086019, -0.8569290407920915,-0.774471897882643,-0.6755415222905497, -0.5671264763802179,-0.8705859926593716,-0.798120523611733, -0.7074267366705677,-0.6040846614078772,-0.8048559824957469, -0.724149733213467,-0.6289645629177542,-0.7264645919207081, -0.641031394884048,-0.6416277774997261,-0.1778161375899129, -0.1160626578000681,-0.04588612344347009,0.03109142233089637, 0.1117410720994994,0.1918123956592439,0.2669187829195654, 0.3336638559940376,0.3902934987610119,0.4366423972091847, -0.07822810590231034,-0.006235377192768587,0.07354985884216768, 0.1581390650418906,0.2429944925392092,0.3229913994840846, 0.3938351936689208,0.4531165220472979,0.5004647043037878, 0.03352103754179751,0.1154299733980676,0.2032831078056842, 0.292541229210957,0.3775430167942871,0.4530586788103156, 0.5157866661887469,0.5648910982101242,0.1544374088220477, 0.2443626306102227,0.3370001516124326,0.4265382311030134, 0.5070037257166236,0.5740673187258024,0.6260922126282031, 0.2792615471857886,0.3736358874527184,0.4664608463431823, 0.5514626265288498,0.6234114376019896,0.6796182090584544, 0.4012129841179684,0.495438498091003,0.5837000128597972, 0.660290805551725,0.7214982694759079,0.513610737510482, 0.6032356918969084,0.6833069969058528,0.7494380454402638, 0.6115888872401081,0.6933734762473292,0.7634088105496576, 0.6929698693584984,0.7653064136814648,0.7580253719184178, -0.5980610514568759,-0.5540609356997265,-0.4979043682596813, -0.4291113361140411,-0.3489872892412604,-0.26088135031786, -0.1696203080333493,-0.08026917121509158,0.003104503401481844, 0.07805400320688781,-0.5118464583535911,-0.4574805454176465, -0.3903649047262822,-0.3111481867341126,-0.2226772699131629, -0.1297540437661504,-0.03794114332033659,0.04796410149079763, 0.1249140518997611,-0.4093046623238462,-0.3436365672482747, -0.2657018883722788,-0.177734023626004,-0.08420287536469411, 0.009136975046692321,0.09689317432239272,0.1753846695482919, -0.2914862882507431,-0.214943022039254,-0.1281283439422551, -0.03496909012717313,0.05893398726743186,0.1478453618978381, 0.2274959035287995,-0.16207395039147,-0.07691226494625918, 0.01500850865584403,0.108539699421764,0.1979267183009546, 0.2784559688242085,-0.02723965791063066,0.06265634702765752, 0.1549020715540746,0.2440506450225836,0.3251843877273637, 0.1056298518860434,0.1957006847953421,0.2838067303057802, 0.3651211825280409,0.2298299119587134,0.3160346825162738, 0.3968842416588859,0.3408204777877171,0.4204156838417109, -0.07805410188638827,-0.09444407992200232,-0.1114341761737927, -0.1281592432697841,-0.1435433307961642,-0.1565435248513201, -0.1664476243728032,-0.1730570234043899,-0.1766548755042983, 0.0157271412771055,0.00421518985213093,-0.008591614873188899, -0.02222682113317407,-0.03596894129854906,-0.0489918802882604, -0.0605924265125066,-0.07036414931943043,0.1197931219386519, 0.1133598979509488,0.1043544464570979,0.09273871068686244, 0.07893799958180921,0.06380141477309535,0.04835884529075852, 0.2310143008479173,0.2288491935457802,0.2221994711972146, 0.2106389321639752,0.1945980336985477,0.1753190140959641, 0.3442626575810957,0.344448149200232,0.3378594888639801, 0.3240587639448084,0.3038950785717152,0.4532138881334763, 0.4531600279320766,0.4442184199518987,0.4263949183927991, 0.5518992169467315,0.5491648599857102,0.536164631289481, 0.636238087652084,0.6292447926211703,0.7047142955122331, -0.9161616153729886,-0.9332409689531019,-0.9400448551193769, -0.9327747060225071,-0.9086729333885725,-0.8672206083002895, -0.8106886470978539,-0.7435546757108702,-0.671104187615456, -0.8837679892439161,-0.8957169226742606,-0.8953622468139258, -0.879022816612988,-0.8447970322719913,-0.7936799886395396, -0.7295529133114078,-0.6579138707033989,-0.5841830474046977, -0.8362110030564622,-0.8405992754866302,-0.8309565568491305, -0.8043503638067647,-0.7603455612152211,-0.7016495768401146, -0.6333375620907219,-0.5611246469661415,-0.771485918473268, -0.766275808658604,-0.7462153282219795,-0.709758929805572, -0.6582056983470901,-0.595590067087498,-0.5273136421678301, -0.6896210625953493,-0.6739687249276908,-0.6440754938657285, -0.6000706653666524,-0.544661671880628,-0.4823592409207763, -0.5934057828726733,-0.5681859910978439,-0.5308212768379395, -0.4828430686319266,-0.4275962761242672,-0.4881111164997092, -0.4557380987684553,-0.4142857087129486,-0.3660248731570757, -0.3801362714084162,-0.3437506203025199,-0.3015400644213536, -0.2753204578199928,-0.2378722763394139,0.5980613131647216, 0.5540611551609296,0.4979045604542198,0.4291115157626469, 0.3489874637290572,0.2608815137134187,0.1696204410073288, 0.08026924777144263,-0.003104506871877828,0.6524878671692068, 0.6063306836302526,0.5460446037762663,0.4714063018804604, 0.3845537031663028,0.2900121666992721,0.1936208245443922, 0.1008859431994594,0.7044824728062356,0.6549957846833561, 0.589454559810383,0.5082654506345897,0.4147343369684993, 0.3146157758763655,0.2144661074447615,0.7499962909367706, 0.6957170016368869,0.6239253864871747,0.5360957563846484, 0.4368631914867457,0.3329288310787005,0.7847921789285285, 0.7244174460120194,0.6460542235032618,0.5525231471367963, 0.4495787672324474,0.805637417939517,0.7386336021807628, 0.6544795745347518,0.5572396030423862,0.8113900261915854, 0.7383768115126219,0.6502709550316551,0.8032920019996154, 0.725915395913506,0.7843092736443379,-0.4366431953633788, -0.5190819485847166,-0.6039640604128714,-0.686785147947791, -0.7620470421725769,-0.8245340389659808,-0.8708228680516067, -0.9001617337332903,-0.9142381355016868,-0.4204163992491611, -0.5047005830710198,-0.5903445107000899,-0.6720308549719619, -0.7437914180445663,-0.8006802874293815,-0.8402926072696949, -0.8631751150784488,-0.8720236313262308,-0.396884888208038, -0.4811289714781835,-0.5652543720979706,-0.6434244315426998, -0.7097591671683677,-0.7601772892083588,-0.7935642249312238, -0.8115619523188943,-0.3651217770412689,-0.4471759195096291, -0.5274838962988779,-0.6002019152270698,-0.6601534291553377, -0.7044750126107846,-0.7331976694123559,-0.3251849417564136, -0.4031031327086847,-0.4778781266280965,-0.5442129229008882, -0.5980262207943586,-0.6376204486797759,-0.2784564800311357, -0.3509705292257924,-0.4194890855987771,-0.4796201828812963, -0.5284049821532861,-0.2274963530521034,-0.2942151694937769, -0.3567023183694048,-0.4115345048612673,-0.1753850281553566, -0.2366295712717834,-0.2938643950650254,-0.1249142906614369, -0.1813653192898883,0.9161619708326186,0.9332413210693342, 0.9400451789678279,0.9327749805257902,0.9086731502239969, 0.8672207777405768,0.8106887957474585,0.7435548367414557, 0.6711043898508244,0.9520728550100875,0.9680084878195204, 0.9709706641436524,0.9568547607732569,0.923414887485804, 0.8715118657296428,0.8051612260268248,0.7302053387950368, 0.9795688039876239,0.9918158125836303,0.9881921235557868, 0.9648780850920352,0.9208732655385489,0.8588851060453266, 0.7845540741828551,0.9941684586829005,0.9997711640715929, 0.9870068177874959,0.9531387910864955,0.8989972043871354, 0.829085472566247,0.9920538154161512,0.9884396081113127, 0.9651307683693848,0.9211259732003716,0.8591326593451197, 0.9714466929952836,0.9573448137030386,0.9239099284934242, 0.8720020075968424,0.9334760373935599,0.9093872922332051, 0.8679349808976941,0.881888023912356,0.8497898935937275, 0.8217573530818993,0.1778171059718252,0.116063484932519, 0.04588675059291181,-0.03109104889813256,-0.111740984528746, -0.1918125910017186,-0.266919222145987,-0.3336644772286424, -0.3902942361971635,0.2378731920177505,0.1751982200024925, 0.103165135794194,0.0237726939459323,-0.05924605897326656, -0.1410802144925054,-0.217121271652078,-0.2841551297870086, -0.3408213192163667,0.3015409214864257,0.2379996307470747, 0.1642142712273472,0.08265050914794721,-0.002351577711465992, -0.08556215486494216,-0.1623583634318567,-0.2298307519128458, 0.3660256667153609,0.3013724622732556,0.225787563312025, 0.1422988506889489,0.05578339303902819,-0.02833333511696591, -0.1056306282458797,0.42759700108315,0.3613584700754575, 0.2839225264985345,0.1989204411880135,0.1115820483497766, 0.0272390133777002,0.4823598903354967,0.4141193140607006, 0.3350423572261403,0.2492664112715316,0.1620734932467959, 0.5273142067726671,0.4570511845454862,0.376979581956603, 0.2914860445503094,0.5611251168568853,0.4893610105925254, 0.409304623993076,0.5841834148888068,0.5118465920502688, 0.5190812242961226,0.6039634624796284,0.686784726158866, 0.7620468277893128,0.8245340313583351,0.8708230332223915, 0.9001620167170649,0.9142384783990968,0.5894211704148156, 0.6789524307604198,0.7632448457303311,0.8359266838325403, 0.891894617970833,0.9289011503005559,0.9478965192118636, 0.6583452758445511,0.749524790850048,0.8315532424890266, 0.8978881068207238,0.944448070806156,0.9707810672151929, 0.7208184417027227,0.809677174255781,0.8854287054759336, 0.9423468561715556,0.9781178066356491,0.7716444357268979, 0.854135980832467,0.9204708008966339,0.9665675908847857, 0.8070418390872103,0.8801658592323851,0.9356914498708749, 0.8258654425765057,0.8883525351377126,0.8296779096104938, 0.09444407913201958,0.1114342885457886,0.1281594836202007, 0.1435437104478973,0.1565440479837881,0.1664482855753907, 0.1730578081089095,0.176655763191492,0.1813650175735936, 0.2043807284477892,0.226758533434972,0.2468100045265798, 0.262820145857554,0.2735753518716192,0.2787599021207723, 0.2789608606213531,0.2753212552695638,0.2938639456031617, 0.3229737187957749,0.3493473093663221,0.3704627992219873, 0.3842636510528081,0.3899007538969458,0.3879754312808241, 0.380136902581416,0.4115339986401249,0.4448996490238289, 0.4726025527270443,0.4916319868779722,0.5002042468552221, 0.4984303277487671,0.4881116080953447,0.5284045295040382, 0.5629551752469137,0.5885431838636429,0.6023440210047828, 0.6035085783646522,0.5934061805946154,0.6376201472701202, 0.6697547537752425,0.6900358414869175,0.6965200475426346, 0.6896214177247305,0.7331975726799168,0.7598360876893205, 0.7728363883884936,0.7714862700692517,0.8115620579167974, 0.8309267868599184,0.8362113667997926,0.8720238939031298, 0.8837683585624158,0.2841543957726532,0.2171207121163834, 0.1410798976495498,0.0592460337919352,-0.02377241605014863, -0.1031645846803213,-0.175197453605406,0.1623576902420928, 0.08556172001198789,0.002351438316001383,-0.08265033236073645, -0.1642138033298737,-0.2379989318886596,0.02833279000440243, -0.05578364441956794,-0.1422987785572308,-0.2257871853292439, -0.301371837576038,-0.1115824064790156,-0.1989204755639271, -0.2839222453034268,-0.3613579271786332,-0.2492665519223285, -0.3350421794323328,-0.4141188622036356,-0.376979512041707, -0.4570508332511692,-0.489360766920074,-0.01572700265409594, -0.1008857021547371,-0.1936205087916404,-0.2900118132468701, -0.3845533472228392,-0.4714059641664385,-0.5460442847763476, -0.6063303681777003,-0.6524875344052207,-0.1197927153790677, -0.214465606765001,-0.3146152265921756,-0.4147337903295822, -0.5082649460772711,-0.5894541111166113,-0.65499537962445, -0.7044820845747036,-0.2310136280273066,-0.3329280970872507, -0.43686246029571,-0.5360950867028047,-0.6239248102620224, -0.695716515229827,-0.749995864312011,-0.3442617649825689, -0.4495778732913127,-0.5525223259640154,-0.6460535267660109, -0.7244168848320439,-0.7847917261432807,-0.4532128635509181, -0.5572386535616057,-0.654478768383796,-0.7386329688076397, -0.805636940817698,-0.5518981667856981,-0.6502700535877015, -0.7383761054919119,-0.811389515577244,-0.6362371067112403, -0.7259146170102994,-0.8032914429700343,-0.7047134464025514, -0.7843086526571425,-0.7580246814516085,-0.2789601712242363, -0.2787593471791244,-0.2735749526850533,-0.2628199127188, -0.2468099337172354,-0.2267586092205901,-0.2043809286570809, -0.3879749291048343,-0.3899004091962853,-0.3842634838788619, -0.3704628145053439,-0.349347495161076,-0.3229740508586004, -0.498429974108997,-0.5002040604834036,-0.4916319868779722, -0.4726027411534949,-0.4449000101574501,-0.6035083146581218, -0.602343922563195,-0.588543271739299,-0.5629554529176811, -0.6965198150492191,-0.690035767365203,-0.6697548637373957, -0.7728361486877732,-0.7598360011487315,-0.8309265297968929, -0.7302050882445748,-0.8051610414501039,-0.8715117174305623, -0.9234147395568944,-0.9568545837269061,-0.9709704452151936, -0.968008232338148,-0.9520725798532584,-0.7845537997567457, -0.8588849308275506,-0.9208731569645157,-0.9648780014111388, -0.9881920288336453,-0.9918156880829223,-0.9795686493343514, -0.8290851961543591,-0.8989970625213397,-0.9531387450388693, -0.987006817440241,-0.9997711657617464,-0.9941684373181741, -0.8591323903215224,-0.9211258673075536,-0.9651307788218259, -0.9884396774035085,-0.9920538947738091,-0.8720017371830424, -0.9239098369784071,-0.9573448480949935,-0.9714467930697923, -0.8679346859597882,-0.9093871792307242,-0.9334760519103793, -0.8497895472965013,-0.8818878542954445,-0.8217569347483581, -0.04796426561714673,0.03794103867724843,0.1297539800598756, 0.2226772328227925,0.3111481732455522,0.3903649242566535, 0.4574806142481337,-0.09689348391076305,-0.00913724963965745, 0.08420262555973922,0.177733799656962,0.265701704660737, 0.3436364458954788,-0.1478457927031966,-0.05893441045550545, 0.0349686757279841,0.1281279562907054,0.214942691058949, -0.1979272450610679,-0.1085402471877293,-0.01500906268915838, 0.07691173856262062,-0.2440512506935033,-0.154902725197284, -0.06265701957101971,-0.2838074100306656,-0.1957014339320081, -0.3160354401730464,-0.9478962831266258,-0.9289010005915933, -0.8918946048405405,-0.8359268445086131,-0.7632451895269284, -0.6789529354540513,-0.5894217923304083,-0.5004653941257039, -0.970780979775399,-0.9444480894583087,-0.8978882630448816, -0.8315535470087182,-0.7495252275742876,-0.6583458081952406, -0.5648916828872914,-0.9781178706298981,-0.942347026709516, -0.885428988462601,-0.8096775549851075,-0.7208188890120822, -0.6260926899993671,-0.9665677527611446,-0.9204710448766127, -0.8541362909342279,-0.7716447840125762,-0.6796185647672793, -0.9356916072835824,-0.8801660576917285,-0.8070420537147268, -0.7214984744500784,-0.8883525705162149,-0.8258654786670103, -0.7494380619781833,-0.8296777286633932,-0.7634086046890135, -0.7653059660649493,0.6579141776573929,0.7295531777521024, 0.7936802384922247,0.8447972973738501,0.8790231171992372, 0.8953625859168114,0.8957172874110191,0.6333379663382935, 0.7016499314834187,0.7603458913147364,0.8043506945733514, 0.8309569030556652,0.8405996366357998,0.5955905567158142, 0.6582061258811204,0.7097593165624652,0.7462156952614298, 0.7662761679440523,0.5446622343557217,0.6000711505073423, 0.6440759195726991,0.6739691095309485,0.4828436947701487, 0.5308218111264984,0.5681864487641957,0.4142863939154359, 0.455738680653885,0.3437513626484632,-0.4531171355531167, -0.3938356644953573,-0.3229916528986729,-0.2429944658048455, -0.1581387295403868,-0.07354922969450257,0.00623625010153865, 0.07822915744532465,-0.5157871513702247,-0.4530589859436308, -0.3775430647931653,-0.2925409610470896,-0.2032825122447724, -0.1154290866147241,-0.0335199252992554,-0.5740676626100093, -0.5070038470041614,-0.4265380492676018,-0.3369996244991067, -0.244361769966899,-0.154436271103469,-0.6234116143878676, -0.5514625314967724,-0.4664604103168303,-0.3736350915397741, -0.2792604271482146,-0.6602907797777987,-0.5836996709958147, -0.4954377928330375,-0.4012119230675125,-0.6833067346898886, -0.6032350844987688,-0.5136097646306489,-0.6933729576191783, -0.6115880145737621,-0.6929690938467825,0.8631753518491425, 0.8402927698803195,0.8006803216023103,0.7437912797965405, 0.6720305274074914,0.5903440100883541,0.504699950277095, 0.7935642870226529,0.7601772654056832,0.7097590166001092, 0.6434241301081504,0.5652539229757697,0.4811284030348103, 0.7044748595212692,0.66015319194957,0.6002015742230613, 0.5274834493042921,0.4471753838390926,0.5980258661419307, 0.5442125068737823,0.4778776501574548,0.4031026076549349, 0.4796197003718685,0.4194885812207204,0.3509700147775985, 0.356701818393901,0.2942146894919281,0.2366291626721505, 0.07036512794891495,0.06059331156828225,0.04899265284320812, 0.0359695874535567,0.02222733419962072,0.008591995504757999, -0.004214935230486583,-0.048357793313131,-0.06380043950324864, -0.07893711715811484,-0.09273793551230918,-0.1043537896571689, -0.113359365885021,-0.1753179190717106,-0.1945969916350631, -0.2106379566061765,-0.2221985793343545,-0.2288484037077831, -0.3038939791712426,-0.3240576891045761,-0.3378584512937831, -0.3444471709858402,-0.4263938526473902,-0.4442173491846191, -0.4531589664405656,-0.5361636274888475,-0.5491638241821553, -0.6292438651304321,-0.1282040992012934,-0.04435978008441287, 0.04944233877845754,0.1506070646973712,0.2546642688007949, 0.3559200448438602,0.4488272180170631,0.5294097717623608, 0.5959629108387098,0.6488215902264566,-0.1619077855794933, -0.07454406204984147,0.02329146842828509,0.1282100848553425, 0.234790969615194,0.3366773586411712,0.4283146113627389, 0.5063384506251003,0.5698896326340842,-0.1972673366002622, -0.1068255930288844,-0.005791503701260298,0.1015779223878756, 0.2090686111987675,0.3100671263842886,0.3994533679902369, 0.4746871659657975,-0.232615798766268,-0.1396773162324554, -0.03655119363532085,0.07170206110196441,0.1784301917623365, 0.2772572737517244,0.3638246180723772,-0.2658039506626713, -0.1710978759257405,-0.06718962048721892,0.04030107224694054, 0.1447607652202159,0.2404750267534745,-0.2946652126306246, -0.1990974281683128,-0.09582172900178909,0.009369859926838612, 0.110357956539064,-0.3176184514965803,-0.222270660707132, -0.1210148748258565,-0.01939827024983316,-0.3340739915348134, -0.2401148801885883,-0.1420870179889694,-0.3444209939391287, -0.2529463331304582,-0.3496821715684524,0.6714420577705413, 0.7435113472506464,0.8139336018335218,0.8777656242085762, 0.9296329303259336,0.9651729312878542,0.9824386073167399, 0.9823984726130219,0.9682596586238317,0.9441720387917811, 0.7087413879135209,0.7840892682826439,0.8556051669023013, 0.9173531285160066,0.9635713995106854,0.9905236148375803, 0.9977653075434614,0.9879747192712225,0.9655968026264566, 0.7409584518952473,0.8171173803566061,0.8865969745674289, 0.942889039924638,0.9806175261526792,0.9974611488769433, 0.9948177215231417,0.976809672217213,0.7644107780855932, 0.8381137943506111,0.9020313711806934,0.9498618317294466, 0.9774884200770568,0.984455050704485,0.9737573075945112, 0.7756818501512159,0.8435121923354123,0.8989022227634631, 0.9366307840854883,0.95437657834853,0.9533825051676448, 0.7727341579527024,0.8320450757851201,0.8774020340365609, 0.9052158359967212,0.9148948007536834,0.7557291769626011, 0.805326671094437,0.8408668603662851,0.8604026946131843, 0.7269859063883545,0.7671790270453136,0.7943722223707224, 0.690100618808278,0.7222076989157948,-0.9441722324566724, -0.904307645384738,-0.8481757819865325,-0.773945596539063, -0.6820112385376135,-0.5756807280718292,-0.4607792790679794, -0.3441534701740746,-0.2318935611202809,-0.9303809621015218, -0.8830185434308674,-0.8172030379805079,-0.7317787780884728, -0.6286054699715032,-0.5128635622007778,-0.3918810385950385, -0.2730236045989769,-0.9028984430498929,-0.8460642977744163, -0.7691784804069041,-0.6724056610478215,-0.5595277701998983, -0.4374897246213904,-0.3144118544998256,-0.8589482430980673, -0.7911988721488439,-0.703044075433466,-0.5965101770495288, -0.4772883218837695,-0.3533685405348139,-0.7974562734423089, -0.7186356601273992,-0.6208045991538126,-0.507926737136379, -0.3869469856202812,-0.7199870192241779,-0.6317037484916127, -0.5275199172214167,-0.4127886377572592,-0.6307845637432554, -0.5361873333707976,-0.4298568952019761,-0.5356827691130239, -0.4385943873688064,-0.4404800743875191,-0.3130828685601688, -0.2158144102235012,-0.1048477339184343,0.01737679733452727, 0.145995491636905,0.2742909798999176,0.3952347219739472, 0.503291831296798,0.595502090315309,-0.3099520106140219, -0.2053757769283784,-0.08650347275041102,0.04302105714473008, 0.1768577004771411,0.3071581921441303,0.4266784695958356, 0.5306275177659158,0.6172668313578871,-0.302423711672288, -0.1907069702272193,-0.06489577644534418,0.0699408153488305, 0.2061362196609274,0.3353108895502397,0.4507704673548409, 0.5489716751760349,-0.2895250242862085,-0.1715150632893158, -0.04067912706104116,0.09654302677976182,0.2317116972020255, 0.3567592436499814,0.4661908578614318,-0.2708185619737357, -0.1481961478813345,-0.01510368122142407,0.1210917442402347, 0.2520105777387078,0.3706589640013763,-0.2467266437111619, -0.1219141274081689,0.01025701144397722,0.142223084729963, 0.2664554400639934,-0.2185691140597738,-0.09433919811625824, 0.03395634318261122,0.1592889611288683,-0.1882244284110535, -0.06716551971534636,0.055031530590314,-0.1575991801780021, -0.0416967436508152,-0.671442351589465,-0.7435115240281642, -0.8139336689380561,-0.8777656078901646,-0.9296328738783285, -0.9651728842355707,-0.9824386099053611,-0.982398544792382, -0.9682597992070626,-0.673525768984227,-0.7468180740978238, -0.8166237491391959,-0.8772252002603724,-0.9230382082945876, -0.9503956493652942,-0.9587838051377746,-0.9507033782544355, -0.6670475871543912,-0.7391545962936478,-0.8055307765029669, -0.860125367790516,-0.8978538005513599,-0.9163947908554175, -0.9168546729772998,-0.649602216294724,-0.7177302448625567, -0.7778859526921634,-0.8243817913354027,-0.853342815897189, -0.8640711446818524,-0.6197562179675709,-0.6813798175903147, -0.733374932687383,-0.7711033603736286,-0.7922438176365504, -0.5778269971598609,-0.6314056112449312,-0.6747362861851252, -0.7045760418412901,-0.5261115978506249,-0.571437749107807, -0.6069777467397017,-0.4682968578205298,-0.5062792722326205, -0.4083732040408349,-0.6488222902967828,-0.6479137619538795, -0.6384433779259612,-0.6181925269683732,-0.5859405951200543, -0.5421896843793962,-0.4893366483035593,-0.4310576147663105, -0.3712412833748336,-0.722208134089623,-0.7225295469502088, -0.7119299887749709,-0.6878297181787101,-0.6492831334094801, -0.5977545202455957,-0.5369263387902474,-0.4715400964562942, -0.4060110053588667,-0.7943724032793758,-0.7941684890748284, -0.780287498377037,-0.7501802425240992,-0.7037353198781235, -0.6438103021069973,-0.5754139215508255,-0.5040323183695714, -0.8604026705465683,-0.8571439110045493,-0.8374826979355753, -0.7995410210009178,-0.744592852017412,-0.6769935095953296, -0.6026908960383707,-0.9148946566640305,-0.9058191273231, -0.8783402703714518,-0.831895347992814,-0.7693419318067468, -0.6961400902573952,-0.9533823404746037,-0.9363830473278872, -0.9003464103777271,-0.846307850369865,-0.7783786922776838, -0.9737572051250643,-0.9481191644195498,-0.904368254349328, -0.8449013283633929,-0.9768096756732072,-0.9433249035584995, -0.8938553761971309,-0.9655969141863968,-0.9260722794540685, 0.3130823317650696,0.2158137676495382,0.1048470017130739, -0.01737758434298081,-0.1459962827220426,-0.2742917196047844, -0.3952353653550799,-0.5032923546149073,-0.5955024919545937, 0.2536926972719504,0.1458327290767235,0.02422841952626041, -0.1071275628186857,-0.241611507146674,-0.3712645460380625, -0.4889532492383237,-0.5901702171834956,0.1843472992213911, 0.06615765451161945,-0.06461113970496464,-0.2021592879969449, -0.338354586304306,-0.4648175094943233,-0.575319351774491, 0.1061130743462423,-0.02080286326993523,-0.1576482143840719, -0.2970021727032601,-0.4300387818650189,-0.549076707052971, 0.0217206853443193,-0.1108168656978456,-0.2493324402368818, -0.3855277139058982,-0.5110231660272195,-0.06464549315404491, -0.1986151347691201,-0.3340228839844541,-0.4627520281251212, -0.1482539922220807,-0.2793070464170456,-0.4076024215152396, -0.2250412157123533,-0.3496313063989884,-0.2924709198142461, 0.1282040764044601,0.04435966353085222,-0.04944259604098935, -0.1506074987015245,-0.2546648874595455,-0.3559208170725224, -0.4488280788164576,-0.5294106421507812,-0.5959637203467131, 0.04169673907303984,-0.0526834259769666,-0.1563566027108192, -0.2651887425550946,-0.3731530321841731,-0.4736563198547184, -0.5613802828138158,-0.6335666007603626,-0.690101357380803, -0.05503152918262894,-0.1593042550239766,-0.2709316440867437, -0.3840952777948459,-0.4915861222634885,-0.5867907021484375, -0.6655838183420531,-0.7269866926442972,-0.159288972427813, -0.2712574169662021,-0.3872244295124012,-0.5000332157715695, -0.6022061061584294,-0.6881925170266414,-0.7557300072673857, -0.2664554858018714,-0.3823479651751564,-0.4978444298202386, -0.6053352646595813,-0.6982069960901036,-0.7727350088749903, -0.3706590624880138,-0.4857936740008229,-0.5959856542636139, -0.6942612161795336,-0.7756826781990721,-0.4661910177108468, -0.5761185565808596,-0.6773744620468557,-0.7644115292814133, -0.5489718931615636,-0.6504766905631515,-0.7409590768696484, -0.6172670952623341,-0.7087418537741278,0.6479131208665134, 0.6384428093318625,0.6181920323103047,0.5859401592476801, 0.5421892777726929,0.4893362367199527,0.4310571708515276, 0.3712407933301676,0.5613207863628051,0.5433246784685077, 0.5142659275004758,0.4739667199004282,0.4241908010905809, 0.3683211596170611,0.3103315078005186,0.4569583774922522, 0.4296550077229934,0.3922062664218491,0.345761387535644, 0.293177934819726,0.2382039928591278,0.3364534206151339, 0.3005219256748686,0.2568082513095248,0.2076321770193636, 0.1563031965788156,0.2045546517133144,0.1623927536580789, 0.1159478851139529,0.06807760796624844,0.0685659565224499, 0.02376600432590625,-0.02150913216682495,-0.06350995748665327, -0.1072608130272718,-0.1851326652875042,0.9043074523981195, 0.8481756214456443,0.7739454978673308,0.6820112173362239, 0.5756807770328707,0.4607793683387657,0.3441535580133543, 0.2318936078193602,0.926072164038092,0.8784582627879883, 0.8124335550546075,0.7268690803364112,0.6236462465254911, 0.5079539512218285,0.3871116910406285,0.2684634496083129, 0.1575990472160092,0.8938553783036348,0.8365254960181361, 0.7592600076242926,0.6622795368519036,0.5494016575639665, 0.4275712710943025,0.3048730417540333,0.1882241880032878, 0.8449014608184471,0.7764699938313133,0.6878549375110379, 0.5811577408091089,0.4620991215146014,0.3386395224598848, 0.2185687785328751,0.7783789236415784,0.6987988931237948, 0.6005524285298199,0.4876744928391774,0.3671099975843117, 0.2467262336464146,0.6961403422326237,0.6071556637949365, 0.5027238349931351,0.3882403289629487,0.2708181010489455, 0.602691063692116,0.5075711336386517,0.4012405518966602, 0.2895245326714229,0.5040323033369615,0.4066733092394736, 0.3024232018417092,0.4060107420397416,0.3099514873609258, 0.6335657588222857,0.5613794019471703,0.4736554861193561, 0.3731523315866652,0.2651882329560032,0.1563562968227162, 0.05268329572084096,0.665582947801096,0.5867898301880646, 0.4915853474795021,0.3840946840958189,0.2709312717864595, 0.159304093053227,0.6881916390051439,0.6022052797777839, 0.500032542833593,0.387223979093218,0.2712572035078997, 0.6982061514387302,0.6053345332894541,0.4978439026516569, 0.3823476853537574,0.694260458246256,0.5959850669993192, 0.4857933245168793,0.6773738416456014,0.5761181477179906, 0.6504762420694124,0.9303807852901133,0.9507032776506229, 0.9587837990451511,0.9503957355401843,0.9230383566605702, 0.8772253580218827,0.8166238578878361,0.7468180882094819, 0.6735256647351061,0.90289835288087,0.9168546892221431, 0.9163949287729053,0.8978540461202875,0.860125673826823, 0.8055310756657073,0.7391548242370243,0.6670477005822436, 0.8589483198537152,0.8640713573235344,0.8533431666375708, 0.8243822455102904,0.7778864433741867,0.7177306948097215, 0.6496025629973337,0.7974565860481383,0.7922442814347184, 0.7711039549446601,0.7333755999307914,0.6813804770418425, 0.6197567932818368,0.7199875994402858,0.704576762533665, 0.6747371020412667,0.6314064480391776,0.5778277738330908, 0.6307853921801013,0.6069786776259655,0.5714387199953477, 0.5261125320735169,0.5356837831672402,0.5062803340418997, 0.4682979008420118,0.4404811918375181,0.4083743128719785, 0.3496833143594963,-0.2684635055945434,-0.3871116949027418, -0.5079539380311434,-0.6236462513912529,-0.7268691253470475, -0.8124336419036672,-0.8784583757439789,-0.3048731870534707, -0.4275713388616055,-0.5494016757834509,-0.6622795348064975, -0.7592600049270052,-0.8365254973182974,-0.3386397327605397, -0.4620992154744972,-0.5811577408091089,-0.6878548720792258, -0.7764698875810475,-0.3671102436707482,-0.487674577809656, -0.6005523742168883,-0.6987987332107705,-0.3882405864046344, -0.5027238913366621,-0.6071555453420947,-0.4012408083880579, -0.5075711618348809,-0.4066735651215305,0.590170014682346, 0.4889529277025601,0.3712641002824293,0.2416109555838913, 0.1071269463449765,-0.02422904936538466,-0.1458333255362707, -0.2536932284372023,0.575319376406684,0.4648174146687293, 0.3383543572464268,0.2021589358241457,0.06461070043643219, -0.06615813268612159,-0.1843477715691807,0.5490769699623794, 0.4300389185128004,0.2970021583546204,0.157648053896879, 0.02080258943368662,-0.1061134148983228,0.5110236542957048, 0.3855280620563327,0.2493326170112359,0.110816873466397, -0.02172081454080515,0.4627527059506328,0.3340234043580559, 0.1986154658537707,0.06464563879740347,0.4076032405543511, 0.2793076930534321,0.1482544392795429,0.3496322190693472, 0.2250419477534466,0.2924718882891986,-0.9879747859642711, -0.9977653310206788,-0.990523615974921,-0.9635714188487002, -0.9173532159549495,-0.85560536501624,-0.7840895998008963, -0.994817702995987,-0.997461134800403,-0.9806175623759372, -0.9428891811827549,-0.886597265826606,-0.8171178418357974, -0.9844549713337977,-0.9774884174060016,-0.9498619694604108, -0.9020317021121872,-0.8381143412526206,-0.9543765048804849, -0.9366308742795815,-0.8989025406729466,-0.8435127700618501, -0.9052158630007405,-0.8774023054284241,-0.8320456365430426, -0.8408670789696687,-0.805327187943167,-0.7671794966406347, -0.3103320171372975,-0.3683216507471385,-0.4241912872571255, -0.4739672217049487,-0.5142664654627133,-0.5433252645626975, -0.5613214202090539,-0.5698903038330552,-0.2382044630883786, -0.2931784077953506,-0.3457618730212876,-0.3922067754962655, -0.4296555471996706,-0.4569589463091218,-0.4746877559159965, -0.1563035449296971,-0.2076325381624787,-0.2568086311939766, -0.300522327960348,-0.336453843995229,-0.3638250562458944, -0.06807774532319075,-0.1159480367576914,-0.1623929246512774, -0.2045548436199081,-0.2404752368196474,0.02150927301351948, -0.02376587784419006,-0.06856585200941642,-0.1103578775111495, 0.1072612554376189,0.06351038126206604,0.0193986644095893, 0.1851333868984673,0.142087712340153,0.2529472816242414, -0.5306278899290351,-0.4266789610910172,-0.3071587951012373, -0.1768583830726642,-0.0430217688399398,0.08650278658817297, 0.2053751597977648,-0.4507707926372612,-0.3353113300288001, -0.2061367616665949,-0.06994142151255993,0.06489515773394869, 0.1907063885440197,-0.3567595103770218,-0.231712075682342, -0.09654349885714854,0.04067860126609572,0.171514533036228, -0.2520107854074638,-0.1210920629807046,0.01510327295162653, 0.1481956904535304,-0.1422232452547296,-0.01025728491155416, 0.1219137656087612,-0.03395647625215152,0.09433895004400768, 0.06716539409874363,-0.5063391876138633,-0.4283153498076878, -0.3366780195432001,-0.2347914812056201,-0.1282104074092338, -0.0232916058381637,0.07454407090624685,0.1619078867980062, -0.3994539622273614,-0.310067650345868,-0.2090689910564566, -0.101578111712534,0.005791506035575475,0.1068257468493955, 0.1972675814170221,-0.2772576433388097,-0.1784304214682155, -0.07170210104888376,0.0365513498207285,0.1396776293169983, 0.2326162031595532,-0.1447608377351292,-0.04030095729466294, 0.06718993398467658,0.1710983522944223,0.2658045221403088, -0.009369596982657442,0.09582219059763358,0.1990980586833061, 0.2946659462714916,0.1210154650283388,0.2222714245712332, 0.3176193294845107,0.2401157501920485,0.3340749869695284, 0.3444220768905359,0.4715398981500615,0.5369261966393798, 0.5977544079195963,0.6492830104914913,0.6878295422870739, 0.7119297300805439,0.7225291958412279,0.5754139764385705, 0.6438104077520811,0.703735439041779,0.7501803284162304, 0.7802875102236209,0.7941684049065568,0.6769937335137454, 0.7445931056463463,0.7995412648414352,0.8374828910500066, 0.8571440245657689,0.7693422116354997,0.8318956310273866, 0.8783405275925528,0.9058193343789728,0.8463080799206776, 0.900346626617332,0.936383240514816,0.9043683688324304, 0.948119268339247,0.9433248910510675,0.2730237834281818, 0.3918812540743772,0.512863760525918,0.6286055997510109, 0.7317788088844818,0.817202970027369,0.883018402482024, 0.3144121837974857,0.4374900830135051,0.559528090412838, 0.6724058856901879,0.7691785827023693,0.8460642869454081, 0.3533690330364157,0.4772888327771027,0.5965106290979496, 0.7030444104060292,0.7911990697093608,0.3869476434981415, 0.5079274002472546,0.6208051861357136,0.718636116718038, 0.4127894497989522,0.5275207205127158,0.631704463913464, 0.4298578384111184,0.536188255170367,0.4385954324163403 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; template<> class PointShell<1002, vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell() { double coords[3*1002] = { 0.9756767549555488,0.9910244201339067,0.9977154378498742, 0.9927800993607139,0.9738192119472443,0.9397937974509784, 0.8915721200771204,0.8318778673129345,0.7646073555341725, 0.693869346809198,0.6231965669156312,0.9833704607135893, 0.9964241736681655,0.9989802553190876,0.987902775761947, 0.9610864540427623,0.9183225885811028,0.8616492668438125, 0.7948923552800862,0.7226196323725681,0.6490709211350905, 0.9817040172417226,0.9905693687720445,0.9870396762453547, 0.9681598062324672,0.9325589150767597,0.881205326354676, 0.8173592116492303,0.7456638277324369,0.6708930871960926, 0.9679134019101711,0.9705588332310378,0.959200825309353, 0.9315264271335991,0.8872988802116832,0.8287690756632178, 0.7601362175694236,0.6863342915062906,0.9399233672993689, 0.934650496070032,0.9144882783890762,0.8781852416959071, 0.8267930935417315,0.7635949206829796,0.6931818659696647, 0.8971014254962764,0.8830745457598012,0.8543282347786828, 0.8108509720355672,0.754782849204541,0.6899186612252973, 0.8407280774774689,0.8182890571891552,0.782394344826989, 0.7341199823031558,0.6762337155773353,0.7738549713477869, 0.7444263912611687,0.7036087793266443,0.6531781708866408, 0.7005607434301688,0.6661985543964391,0.6228579759074076, 0.6249824933683414,0.5878380796842572,0.5505632701289798, 0.4375940376503738,0.3812939184075813,0.3153040621970065, 0.2399906033000567,0.1569517536476641,0.06907711216819173, -0.01984099037382634,-0.1058738447453135,-0.1857690950088067, -0.2574501777461976,-0.3200730131503601,0.4806956770236153, 0.4221182950629059,0.3527542025586833,0.2732412963568581, 0.1857299193159225,0.09380763985613347,0.001855404825389049, -0.08593912303271299,-0.1664330628904374,-0.2378914640002027, 0.5232435944036425,0.4619857831524665,0.3889403678268736, 0.305177254463085,0.2135250052622625,0.1182658918935892, 0.02420694871807206,-0.06444621903648747,-0.1448539951504302, 0.5629325029626137,0.4984853033494325,0.4215263456365218, 0.3337322884960374,0.2386638182445053,0.1411593054699449, 0.04617547921974115,-0.04233031957129997,0.5971534158422009, 0.5290500940108692,0.4482053228282282,0.3570208135159443, 0.2597018771197477,0.1613609902081595,0.06677517278138323, 0.6235168560971456,0.5515679242489461,0.4673132571654228, 0.3738575901923395,0.2758011201251887,0.1781713819192711, 0.6404616222418184,0.5649656062523414,0.4782876117785159, 0.3840417218310146,0.2868761951248767,0.6476575150457713, 0.5694506961858633,0.481712063543859,0.388259258354853, 0.6459894362878276,0.566293293292668,0.4789651252338281, 0.6371661773752719,0.5573232120043787,0.3200724178002418, 0.4063802566221164,0.4973180497018747,0.5897273522875156, 0.6793811951363423,0.7616224484252742,0.8323587928990375, 0.889001584581435,0.9308933612987835,0.9590813334864968, 0.3615817483188572,0.4535168742898277,0.5491779429163389, 0.64449992308253,0.7344437631005388,0.8139835008962493, 0.8793283338489147,0.9287763706314454,0.9627864725173401, 0.4036036036586492,0.499949320334643,0.5984781165037405, 0.6942590478190055,0.7817280923310203,0.8559922020830166, 0.9140795130247613,0.9554674311422222,0.4441835036049274, 0.5429866860840018,0.6418227066741695,0.7351609508563529, 0.8174230627001258,0.8844224346157006,0.9343673632346869, 0.4809905907165384,0.5797158369815773,0.6759621154318279, 0.7640818148143264,0.8390728924802671,0.897874775015538, 0.5117469206751248,0.6076796078041039,0.6986831899155831, 0.7795497616193484,0.8462988042859838,0.5347729120192694, 0.6255452713743446,0.7094340284155564,0.7820975288082114, 0.5493993272212266,0.633404689923654,0.7093243648499448, 0.5560356639783846,0.6325607438661257,0.5558948726075613, 0.2502538949373057,0.2834596649308375,0.3171352000240629, 0.3497321770767574,0.3793963897789465,0.4043068999720242, 0.4231100429674418,0.4352503773152589,0.4410301813437042, 0.4413950384816501,0.3401627625801983,0.3789979847088327, 0.4171897539368579,0.4525063773118708,0.4825397797856335, 0.5052684266816663,0.5196090830694199,0.5256607773912181, 0.5245227828309349,0.5178400854028183,0.4357529867703999, 0.479500221024681,0.5208717223808415,0.5569598687552583, 0.5850086433327374,0.6031754713474787,0.611055499882272, 0.6096654752354952,0.6009463532173235,0.5339523560360242, 0.5809954188619362,0.6233304915365264,0.6575940739282847, 0.6811006607903708,0.6926539121832439,0.6928333109300957, 0.6836092937706954,0.6305067000562991,0.6783846661286849, 0.7188806066405239,0.7486166997908829,0.7654898954879897, 0.7692993175005894,0.7616477696596397,0.7205836671095895, 0.7663950141411601,0.8022874547238388,0.8253952737866894, 0.8345810205360885,0.8306996157029517,0.7997756996573342, 0.8408319454787708,0.8700831379830764,0.8855671363619817, 0.8872031228985237,0.8650978792558638,0.8995240516802058, 0.921218286608597,0.9292366279325103,0.9155019734809123, 0.9425022788739567,0.9568003701205341,0.951723763021908, 0.9714657198391669,-0.4375932291383153,-0.3812932275173999, -0.3153035222278598,-0.2399902437364576,-0.1569515927579475, -0.06907715072573904,0.0198407706589918,0.1058734766109667, 0.1857686171195431,0.257449626507278,-0.3570040493097855, -0.2918648787974354,-0.2168311251285635,-0.1330995445648626, -0.04332608784719146,0.04859599442048472,0.1382857743932946, 0.2218613031927873,0.2966853245585511,-0.2644927501381796, -0.1901399993818987,-0.1064219080255857,-0.01561852722247663, 0.07849995612144045,0.1712924506084217,0.2583107784678281, 0.3362909838861173,-0.1610782390926968,-0.0780603807507026, 0.01281177251559359,0.1081011253085713,0.2031694108248999, 0.2931782765634202,0.3742486038435638,-0.04938676750055992, 0.04056537812597379,0.1358448755096817,0.232090324110116, 0.3243479900672576,0.4082538491780348,0.06640211724156458, 0.1604512568352431,0.2565833049862117,0.3500388266409877, 0.436217636937124,0.1811879481039926,0.2758835926122972, 0.3692668145365748,0.4568072375603044,0.2900013554556395, 0.3820078910424991,0.4697464271137876,0.3890115016151001, 0.4757292848583335,0.4760519631420977,-0.6231952788307174, -0.5646794654796425,-0.4943551945928708,-0.4121616924667699, -0.319458133528771,-0.2192092047974507,-0.1156489798772063, -0.01344431006605927,0.08328895892415776,0.1716736688754058, -0.5573220717056252,-0.4899613221250736,-0.4101426439397438, -0.3185871044669488,-0.2177607050610849,-0.1117914195951733, -0.005729665044898873,0.09558913699521315,0.1885288856066103, 0.2710751964644532,-0.4789641985801549,-0.402057686424166, -0.3127252940830145,-0.2127814923512051,-0.1059392282183739, 0.002692533596869102,0.1077444781964869,0.2048204657664428, 0.2912280153186658,-0.3882586050506625,-0.3019152874380811, -0.2041697091828281,-0.09809683500613878,0.01149943312218195, 0.1190413522149791,0.2195138260022026,0.3094968646292108, -0.2868758523956744,-0.1923492526670043,-0.08856892011805101, 0.02025987059937257,0.1287405357080231,0.2315279747463358, 0.3245517154572714,-0.1781713502362079,-0.07790941981152795, 0.02852799618806018,0.136265045008317,0.2399985475821452, 0.3352293490184233,-0.06677541204276306,0.035950103618558, 0.1413542883070481,0.2445218382684434,0.3408430926744944, 0.04232987969953258,0.1440992946503647,0.2452457212250337, 0.3413681152295799,0.1448534358763926,0.2427634183836315, 0.3374016489097037,0.2378908589603879,0.3299350928268919, -0.2502532651867688,-0.2834590071461157,-0.3171345072414974, -0.3497314427767591,-0.3793956104585266,-0.4043060780204903, -0.4231091882680272,-0.43524950627169,-0.4410293135613324, -0.4413941927799012,-0.1793269230918291,-0.2096296374650262, -0.2404488793458187,-0.2702796532141552,-0.2973713527283103, -0.3200999575733405,-0.3373822412281449,-0.3489197313348205, -0.3551542383465527,-0.09929959410007272,-0.1260192341590947, -0.1535127609134815,-0.1804461251889914,-0.2052877394623771, -0.2266616403246165,-0.2436963810571767,-0.2561842894548281, -0.01142151773467507,-0.034316887225212,-0.05856032689489409, -0.08307746147223684,-0.1065840014642066,-0.1278836167145054, -0.1461545898453977,0.08175409117371149,0.06228642306456245, 0.04056025153798869,0.01740518175433122,-0.006048944565669369, -0.02862806146519492,0.1765178736510397,0.1594436875323896, 0.1389956691289133,0.1158878972138483,0.09125781311736192, 0.2686152102237028,0.2525244737680689,0.2319923070602857, 0.2077893717181683,0.3541399826172473,0.3376577748776239, 0.3159635830269856,0.430309819720559,0.4124399032037845, 0.4957938830064738,-0.975676581463901,-0.9910243213530433, -0.9977153903038788,-0.9927800571163499,-0.9738191090293654, -0.9397935597648752,-0.8915716840571059,-0.8318771966689975, -0.7646064477130079,-0.6938682276631534,-0.9714655004610564, -0.983887723902267,-0.9858981447798461,-0.9744145976009966, -0.9473804480134012,-0.9046164166427493,-0.8481606115014303, -0.7818095106057968,-0.7100822624036758,-0.6371649246085965, -0.9568001079664734,-0.9644051676944579,-0.9598482725023617, -0.9402907381732713,-0.9044523389503778,-0.8533359449908623, -0.7901672201648241,-0.7194988284538555,-0.6459882395962464, -0.9292363237529666,-0.9300944286029187,-0.9173973041924147, -0.8890014167596085,-0.844773720173384,-0.7869651249304563, -0.7196711542084638,-0.6476563908917461,-0.8872027729137049, -0.8798270414245473,-0.8582754834679532,-0.8214852287823353, -0.7705800268610806,-0.7087709559526547,-0.6404605781008121, -0.8306992108488226,-0.8145452502402749,-0.7846557072154475, -0.7411783184941589,-0.6862531911113832,-0.6235158879179116, -0.7616472974254324,-0.73736034048619,-0.7008201753656432, -0.6531910402117445,-0.5971525097007422,-0.6836087421746132, -0.652851924507533,-0.612034208511092,-0.5629316404694558, -0.6009457148226922,-0.5659076360456838,-0.5232427588825813, -0.5178393593330568,-0.4806948574024101,0.5646808729466086, 0.4943566966479628,0.4121632456350594,0.3194596781650836, 0.21921067448898,0.1156503154178581,0.013445471844618, -0.0832879858164388,-0.1716728777368211,0.5865775784632071, 0.5109645672417243,0.4225383974740667,0.323390011818691, 0.2174206286797427,0.1096806787773226,0.005232361633134523, -0.09191315247018395,0.6036999320472409,0.5222841738261358, 0.4275626351684279,0.3225497922064885,0.2120884454656291, 0.1018140973507329,-0.003178626653090429,0.6137659240175044, 0.5263406106031267,0.4258274919194899,0.3162311593161161, 0.2031293654582096,0.09233653282973062,0.6148617703985678, 0.5217885230992481,0.4167137837086616,0.3044789836562512, 0.190984387847104,0.6060496757760786,0.5084224141934828, 0.4006853449688581,0.2881416501332511,0.5877496225758772, 0.4873191346491355,0.3791778875849738,0.5616447830131567, 0.460498372897877,0.5301563765120954,-0.4063806941036804, -0.4973183240635209,-0.5897274724251101,-0.6793811856410323, -0.7616223457476226,-0.8323586364840968,-0.8890014069516364, -0.9308931819742911,-0.9590811587055613,-0.3299355917757612, -0.4201916721510849,-0.5144019383498233,-0.6086443620415749, -0.6980092714824531,-0.7775489279696631,-0.8434725272922868, -0.8939999530932097,-0.9294606883512702,-0.9517235558231533, -0.3374020539278631,-0.4303972015599547,-0.5261951664998159, -0.6201746406216091,-0.7070125356849136,-0.7819076179156583, -0.8417962075837926,-0.8859147779324957,-0.9155017573317124, -0.3413684282280401,-0.4354200733480757,-0.5306961815651272, -0.6221165659419365,-0.7043785843697545,-0.7732956289121359, -0.8268002829198581,-0.8650976752328781,-0.3408433114184408, -0.4339783095259441,-0.5265312606271311,-0.6133559560669121, -0.6896418460594331,-0.752136864699832,-0.7997755164970677, -0.3352294641675141,-0.4255076064827776,-0.5134721858345982, -0.594338662260662,-0.6641265172984932,-0.7205834943870382, -0.3245517106425898,-0.4104116666038574,-0.4925847482169691, -0.5669637398252093,-0.6305065080228541,-0.3094967204309826, -0.3899712787954628,-0.4658908667926167,-0.5339521035735905, -0.2912277152063287,-0.3659561925975448,-0.4357526334612896, -0.2710747314715586,-0.3401622786069621,0.7100833249889532, 0.78181034476627,0.8481612032420389,0.9046167870637368, 0.9473806530146282,0.9744147135590244,0.9858982469324079, 0.9838878689052019,0.7194998173208004,0.7901679726328494, 0.8533364642620689,0.9044526665335126,0.9402909454666769, 0.9598484396937114,0.9644053616541416,0.7196720611840924, 0.7869658012318539,0.8447741887331299,0.8890017361264828, 0.9173975522215362,0.930094678876582,0.7087717858204172, 0.7705806468939737,0.8214856791906156,0.858275831469383, 0.8798273607029399,0.6862539610403643,0.741178910976439, 0.7846561726417634,0.8145456545188131,0.6531917738872118, 0.7008207681995118,0.7373608444418822,0.6120349280184139, 0.6528525365910808,0.5659083559960189,-0.3615822177172413, -0.2966857537063793,-0.22186166029524,-0.1382860190443388, -0.04859608414266816,0.04332618727116257,0.1330998494814693, 0.2168316303130393,0.2918655611474421,0.3570048758530226, -0.4036039458806759,-0.3362912903737734,-0.2583110138480089, -0.1712925696560921,-0.0784999126587471,0.01561876770079114, 0.1064223584250461,0.1901406492265948,0.264493571710179, -0.4441837318029271,-0.3742488010631976,-0.2931784021968726, -0.2031694140087586,-0.1081009568659323,-0.01281139938873339, 0.07806096659640296,0.1610790216932629,-0.4809907334514471, -0.4082539641847306,-0.3243480295764106,-0.2320902314782855, -0.1358446002697818,-0.04056489081311081,0.04938746901646566, -0.5117470172320779,-0.4362177044221099,-0.3500388090292672, -0.2565831412886956,-0.1604508981073868,-0.06640153973221416, -0.5347730026038946,-0.4568072901222747,-0.3692667658371347, -0.2758833806070636,-0.1811875286592425,-0.5493994424693911, -0.4697464869291438,-0.3820078284652705,-0.2900011116348868, -0.5560358190148772,-0.4757293607590583,-0.3890114324926668, -0.5558950675560154,-0.4760520530085997,-0.5505634949474449, 0.9294608819831129,0.8940001336574592,0.8434726858300888, 0.7775490434174117,0.6980093127768466,0.6086442955222793, 0.5144017370522948,0.4201913220833389,0.8859149609435977, 0.8417963565884857,0.7819077242323556,0.7070125813068046, 0.6201746011540189,0.5261950179989611,0.4303969275523253, 0.8268004336113636,0.7732957269571541,0.7043786253344242, 0.622116538987203,0.5306960716781189,0.4354198662954997, 0.7521369798040806,0.6896418985458221,0.6133559483764451, 0.5265311900255359,0.4339781689134173,0.664126617868866, 0.5943387021482599,0.5134721735844615,0.4255075445403705, 0.5669638671854947,0.4925848265160583,0.4104117066697837, 0.4658910697532281,0.3899714480143811,0.3659565132385014, 0.3551551111068512,0.3489206356503204,0.3373831583083507, 0.3201008675109025,0.2973722384562898,0.2702805035644438, 0.2404496897688697,0.2096304085552757,0.1793276582557128, 0.2561851603269178,0.2436972866599269,0.2266625621335284, 0.2052886581368649,0.1804470242976833,0.153513629451971, 0.1260200668693211,0.09930039009433847,0.1461554161525869, 0.1278844724735265,0.1065848698133528,0.08307832599156778, 0.05856117448713494,0.03431770949468276,0.01142231063250637, 0.02862879196054897,0.006049691633511915,-0.01740443052828812, -0.04055950638179381,-0.06228569088648633,-0.08175337578691833, -0.09125722755264572,-0.1158873114023829,-0.1389950879223139, -0.1594431124986218,-0.176517304179968,-0.2077889659882155, -0.2319919155781195,-0.2525240921328624,-0.2686148310916902, -0.3159633704917529,-0.3376575857943411,-0.3541398039589991, -0.4124398762523879,-0.430309819678344,-0.4957940195469321, 0.1664325418685087,0.08593872296651928,-0.001855644740443331, -0.09380768650859528,-0.1857297549973136,-0.2732409243006708, -0.3527536454451026,-0.4221175876781037,0.06444579340302205, -0.02420720081803753,-0.1182659371646478,-0.2135248270679241, -0.3051768592760364,-0.3889397838050994,-0.4619850515450297, -0.04617573152762199,-0.1411593366285305,-0.238663612601281, -0.3337318558644699,-0.4215257202593218,-0.4984845341024346, -0.1613609945604779,-0.2597016312374675,-0.3570203295626905, -0.4482046405142344,-0.529049270280373,-0.2758008261967574, -0.3738570466287534,-0.467312506887279,-0.5515670310282831, -0.3840411190982298,-0.4782867918076852,-0.5649646363950814, -0.4817111823705308,-0.5694496536902212,-0.5662921921093249, 0.0919140401962618,-0.005231313174239269,-0.1096794790522038, -0.2174193094193431,-0.3233886246408304,-0.4225370035538252, -0.510963223483816,-0.5865763269261466,-0.6490697854071827, 0.003179550365269548,-0.1018130528605821,-0.2120873052250725, -0.322548598821141,-0.4275614411441423,-0.5222830294256716, -0.6036988748207749,-0.6708921376896406,-0.09233565318963641, -0.2031284196976215,-0.316230181653878,-0.4258265246770868, -0.5263396945379846,-0.6137650892643368,-0.6863335534851791, -0.190983636619622,-0.304478224282928,-0.4167130490396238, -0.5217878437313506,-0.6148611676086653,-0.6931813485878851, -0.2881410938833685,-0.4006848272509508,-0.5084219568812576, -0.6060492919686921,-0.68991835321825,-0.3791775567551729, -0.4873188675145023,-0.5877494261961844,-0.6762335873429084, -0.4604982573084285,-0.5616447337445059,-0.653178182658568, -0.5301564355851682,-0.6228580878699612,-0.5878382593304642, -0.5245220270255621,-0.5256600112160642,-0.5196083293421272, -0.5052677071807448,-0.4825391100490625,-0.4525057633827704, -0.4171891927640138,-0.3789974674618629,-0.6096648147710031, -0.6110548409057,-0.6031748400009704,-0.5850080622199078, -0.5569593510286511,-0.5208712693637837,-0.4794998244238688, -0.6928327439316474,-0.6926533571029836,-0.6811001461047985, -0.6575936218265264,-0.6233301120657577,-0.5809951090033914, -0.769298834842142,-0.7654894328832393,-0.7486162870521085, -0.7188802647693375,-0.6783844022430058,-0.8345806089171193, -0.8253948883356401,-0.8022871256496356,-0.766394760848657, -0.8855667831280848,-0.8700828159137221,-0.840831684134326, -0.9212179835682319,-0.8995237842791114,-0.9425020232333647, -0.7226186971374691,-0.794891657666899,-0.8616488184692596, -0.9183223647655301,-0.9610863946187903,-0.9879028002301763, -0.9989802852434637,-0.9964241505078985,-0.9833703523103508, -0.7456631063614969,-0.8173587433845655,-0.8812051016459226, -0.9325588839421305,-0.9681598882374777,-0.9870397834787261, -0.99056942924076,-0.9817039872478999,-0.7601357246335175, -0.82876883364816,-0.887298854659372,-0.931526545874374, -0.9592009976026995,-0.9705589758896634,-0.9679134580585838, -0.7635946484712315,-0.8267930492778305,-0.8781853684611858, -0.9144884914916022,-0.934650707238976,-0.9399235077793813, -0.7547827674388909,-0.8108510779697197,-0.8543284576557796, -0.8830748007745595,-0.8971016370404786,-0.7341200461470681, -0.7823945479956939,-0.8182893261768063,-0.8407283372889187, -0.7036089418396923,-0.7444266467538491,-0.7738552525426188, -0.6661987769113784,-0.7005610213599369,-0.6249827499272808, -0.1885282693989691,-0.09558834947432154,0.005730628355370741, 0.111792540109273,0.2177619400763412,0.3185883951684442, 0.4101439293014277,0.4899625516770138,-0.2048200319759604, -0.1077438933887955,-0.002691796830558471,0.1059400956623477, 0.2127824486290439,0.3127262866621893,0.4020586651367561, -0.2195135736243867,-0.1190409791973846,-0.01149894225597868, 0.09809742254017301,0.2041703576935574,0.3019159562443602, -0.2315278949190421,-0.1287403742204129,-0.02025963236651961, 0.08856921814263634,0.1923495858173033,-0.2399986269164587, -0.136265086596977,-0.02852800385427971,0.07790943731694504, -0.2445220631569563,-0.1413545191115968,-0.03595033950187966, -0.2452460806309568,-0.1440996982422414,-0.2427639040507477, -0.9627863463864214,-0.9287762212458259,-0.8793281693046819, -0.8139833471872829,-0.734443661783315,-0.6444999213728085, -0.5491780807749006,-0.4535171752887245,-0.9554673658442766, -0.9140794058749131,-0.8559920650689382,-0.7817279594934516, -0.6942589693680463,-0.5984781448346268,-0.4999494955348047, -0.934367366569149,-0.8844223798763841,-0.8174229679769948, -0.7351608585502457,-0.641822673191966,-0.542986764989374, -0.8978748469678216,-0.8390728949381593,-0.7640817734701692, -0.6759620794963979,-0.5797158650472573,-0.8462989355647894, -0.7795498187240726,-0.6986832060861172,-0.6076796358995511, -0.7820977027354027,-0.709434131000089,-0.6255453415851245, -0.7093245618847172,-0.6334048252011675,-0.6325609463354179, -0.1778161375899129,-0.1226310892230631,-0.06053925384414331, 0.007442324388539504,0.07929679392711581,0.1521341729788544, 0.222673458561735,0.2879340035685283,0.3458247516791153, 0.3953823593491522,0.4366423972091846,-0.08876371160394406, -0.02528588772613339,0.04476837629156977,0.1195214252181962, 0.1959986327009231,0.2705464283542062,0.3396119290589926, 0.4005431057992871,0.4520218529859129,0.4940014731008501, 0.01030826616734189,0.08198490466663314,0.1591522280204451, 0.2389097207973864,0.3173816763430465,0.3904924510986985, 0.4549463955350546,0.5089146064888007,0.5521270265729551, 0.117472560686524,0.1962648015037478,0.2785512690899858, 0.3604576080817683,0.4375569230040101,0.505925367029105, 0.5630823357891753,0.6083330877322986,0.2292788658415479, 0.3129719884702788,0.3973400932411465,0.4778825649301736, 0.5502139834879405,0.6111631805503013,0.6594089221868847, 0.3410385404813853,0.4265108902934403,0.5094170629259336, 0.5852084170909949,0.6501542610793901,0.7022080829625521, 0.4476465561008348,0.5314703253828574,0.6096570464011057, 0.6781972947322877,0.7343998566036512,0.5446711990330303, 0.6238421950486808,0.6949971116211809,0.7550398975145011, 0.629214796874201,0.7016703235997708,0.7646693979379596, 0.7001887122370751,0.7649403475532479,0.7580253719184178, -0.5980610514568761,-0.5589881370818129,-0.5101530988159087, -0.4510385045651341,-0.382225667160838,-0.3056524524744078, -0.2244621267538426,-0.1424014047662905,-0.06301328229424107, 0.01100431499766755,0.07805400320688782,-0.5212000928328268, -0.473821384591564,-0.4161517087436187,-0.3483435180237136, -0.2719386249037586,-0.1899104644827079,-0.106168437105403, -0.0246774460389087,0.05138083746707222,0.120041421580981, -0.4311039309963852,-0.3746268308434739,-0.3079662136138592, -0.2322443529582202,-0.1501157132113724,-0.06545131715451497, 0.01750888497279251,0.09514231706859014,0.1650825345160538, -0.3282706848689272,-0.2627160067380389,-0.1880137321428399, -0.1064226243048281,-0.0215868970434456,0.06217585792644167, 0.1409092129286223,0.2118443492085483,-0.2148810450151756, -0.1414239428971924,-0.06090095222676627,0.0234070437450894, 0.1073128739652992,0.1866888371989906,0.2584097661066967, -0.0948797286874359,-0.01589086440904075,0.06724059985094104, 0.1506371707417032,0.2301937869064764,0.3025309778153461, 0.02655484252908358,0.1079004271192526,0.1901297170957776, 0.269350641258243,0.3420822257932489,0.1440569199487702, 0.2244719783102178,0.3027669323023652,0.3755350385655634, 0.2531835106264871,0.3299301683341083,0.4022303494272352, 0.3510968726936461,0.4223460594339758,-0.07805410188638827, -0.09276641502761757,-0.1080255529483224,-0.123231540300325, -0.1376217050758367,-0.1503967869358663,-0.1609000070073124, -0.1687715154704865,-0.1740018618448228,-0.1768698604837748, 0.005852711686888685,-0.004907458921365185,-0.01667363978400245, -0.02910230703937302,-0.04167551826680154,-0.05377893240309974, -0.06483570957325965,-0.07443642513334246,-0.08240206216970784, 0.09827676798573926,0.09169789424305551,0.083291898217249, 0.07306224170584404,0.06127443921955168,0.04845159286147566, 0.03526739273256396,0.0223825152995957,0.1971676395871742, 0.1943681810797974,0.1884952036420767,0.1792304305890973, 0.1667127428266929,0.1515792114414989,0.134812448209863, 0.2991139104294396,0.2989413652639448,0.2941068360088736, 0.2841377396140656,0.2692865316145088,0.2505276366498019, 0.399677322182528,0.4003768622965933,0.3947504029769399, 0.382445062756354,0.3640665537863402,0.4942032775296958, 0.4938403319910914,0.4857723178878524,0.470018015874229, 0.5788262679961029,0.575792452883696,0.5642398722628517, 0.6512069539966677,0.6445542311848552,0.7107016666079528, -0.9161616153729886,-0.9319303956122561,-0.9396953110011561, -0.9366313083669698,-0.9204280785344878,-0.8900379255247357, -0.8462030522374957,-0.7914228667351423,-0.7293237120999879, -0.663758722300958,-0.8876449455755545,-0.8994356085395798, -0.9017511736022054,-0.8917607021504116,-0.8675634598042835, -0.8289706205471806,-0.7778214867506247,-0.7175692482251322, -0.6523369554154551,-0.5859517695425136,-0.8470541513588044, -0.8530863623730024,-0.8482966176587544,-0.8301999149225319, -0.7977006542517769,-0.7517266504248017,-0.6951661565374421, -0.6320680091083589,-0.566558592627622,-0.7927543534178244, -0.7914101005217242,-0.7783641365944748,-0.7518828443693624, -0.7119690859200297,-0.660654211393362,-0.6015113260745434, -0.5386394371453509,-0.7243096319272092,-0.7146076494872069, -0.6931460376496088,-0.6592387683821411,-0.6140043047773551, -0.5602361841518015,-0.5016343691560573,-0.6430242175142256, -0.6250295478554301,-0.596219325041805,-0.5569826617529877, -0.5092509168566325,-0.4560492515462431,-0.5520254073275178, -0.5269450103103401,-0.4928644880867128,-0.4509854395387125, -0.403575153350467,-0.4557100264506188,-0.425580431270515, -0.3887439926860071,-0.3468035231429711,-0.3587591578566765, -0.3258932157449829,-0.2886351685087218,-0.2651897825092151, -0.2316682604407079,0.5980613131647216,0.5589883601394342, 0.5101532951859686,0.451038686721687,0.382225843595672, 0.3056526230838691,0.2244622808787926,0.1424015236007947, 0.06301334452030186,-0.01100432747032814,0.6470944588058076, 0.6063945120633992,0.5544956308560397,0.4909814474854842, 0.4168791394819704,0.3348509827825754,0.248806364862682, 0.163021328012756,0.08119217116689301,0.6944632949786616, 0.6513144930113939,0.5955168212825119,0.5269608417915713, 0.4473425940100297,0.3601678525102263,0.2700417838303327, 0.1815453704759045,0.7372825381163968,0.6906294333400347, 0.6300883699674214,0.5561262995663098,0.4712906267013536, 0.3798989213097872,0.2870043862751919,0.7724043956082883, 0.7211854504544825,0.6553545192922715,0.576197864204669, 0.4871408620353512,0.3930729578844198,0.797087559189558, 0.7405927447841627,0.6695504697315396,0.5861540209201834, 0.4945084290202527,0.8097301284690857,0.7479263796731634, 0.6725220182496192,0.5864764631401966,0.8103038862615974, 0.7439345925004668,0.6656398165194758,0.8002534097038426, 0.7306537225524881,0.7819506470598,-0.4366431953633789, -0.5106666754492736,-0.5869882376922511,-0.6624740500734984, -0.7332080507197046,-0.7951587738972527,-0.8450980113065225, -0.8813674368391481,-0.9041113586460733,-0.9149201193897882, -0.4223467825896815,-0.4979651901691212,-0.5751701148400569, -0.6502749910785526,-0.7189488051634834,-0.7771253015026822, -0.8220318406135049,-0.8528138420803982,-0.8704523073313828, -0.877132051866122,-0.4022310083998925,-0.4781163288807728, -0.554596445154436,-0.6275642260210416,-0.6925605687496104, -0.7458582025116769,-0.7854318984598006,-0.8112889263329699, -0.8250621271173465,-0.3755356470489176,-0.4501425958887488, -0.5241864933378524,-0.5934002650897944,-0.6535679922027033, -0.7016275314245141,-0.7364042366101387,-0.758599491836005, -0.3420827953668352,-0.4139041914442195,-0.4840440064641756, -0.5484062706939985,-0.6033456975789954,-0.6466104559972755, -0.6777531805937266,-0.3025315118038733,-0.3703991963484116, -0.435725075678865,-0.4948721352375582,-0.5449289606848956, -0.5843857415998934,-0.2584102557043402,-0.321701018732575, -0.3819753792546441,-0.436205800680254,-0.4821906665520286, -0.2118447747227839,-0.2704914294051139,-0.3260202911877148, -0.3760152101544279,-0.1650828712784331,-0.2194154020550029, -0.270790845781693,-0.1200416470582943,-0.1705734571487906, 0.9161619708326184,0.9319307491762596,0.9396956424389374, 0.9366315992939006,0.9204283182965946,0.8900381161951118, 0.8462032095340455,0.7914230156292639,0.7293238793541417, 0.6637589298557965,0.9487876867424746,0.9638219991954514, 0.9689402187057976,0.9610351558002814,0.937956150415152, 0.8993632776707052,0.8470958517094176,0.7847581799295993, 0.716723246034345,0.9749588444840027,0.9874641413480602, 0.9879501207294071,0.9733335913695014,0.942053498973333, 0.8948602911895638,0.8348196077814718,0.7664457510261931, 0.9913973712326389,0.9992329380155185,0.9930645438165779, 0.9702883664051513,0.9303746073596573,0.8753546246993716, 0.8093341962935008,0.9950795014807369,0.9961777933609237, 0.9818515654328379,0.9504460903479695,0.9027098746674149, 0.841806420960272,0.9840623116657442,0.9769921932350635, 0.9540532376241087,0.9148166158751114,0.8612136882276913, 0.9581801446138297,0.9425905156295621,0.9118246030313639, 0.8666310652479987,0.9192101668893,0.895902096029785, 0.8590657328509213,0.8703772282258925,0.8409823115598557, 0.8154720767160899,0.1778171059718252,0.1226319330808614, 0.06053992567271226,-0.007441870151939188,-0.07929659020903117, -0.1521342296531137,-0.2226737578340799,-0.2879345044307138, -0.345825401239635,-0.3953831051073846,0.2316691816765674, 0.1757724390286309,0.1122685379972423,0.04238946123155791, -0.03147431095673338,-0.1060223690705309,-0.1777018008392572, -0.2435073681639464,-0.3015367950655638,-0.3510977114791443, 0.2886360377097776,0.2320884584878828,0.1672516508448342, 0.09562800512668546,0.02000533874392893,-0.05595525429524186, -0.1285435155191929,-0.1948426136371608,-0.2531843553864728, 0.3468043362453595,0.289467434229511,0.2232553503091939, 0.1500085463918315,0.07290895867983387,-0.004119533631953385, -0.07735131409521774,-0.1440577225948253,0.403575906447316, 0.3451254769459608,0.2774342678683828,0.2027388030014913, 0.1245598363284875,0.04693326425220518,-0.02655554762561945, 0.4560499397602033,0.396110678421097,0.3269267724177788, 0.2511351438644352,0.1724665161635576,0.09487917246041, 0.5016349858535857,0.4399931207714222,0.3695530582277636, 0.2932656162238703,0.2148806720954671,0.5386399743309223, 0.4754121349188742,0.4042569522642996,0.3282705050396187, 0.5665590425344393,0.5022156935301549,0.431103930292903, 0.5859521265200787,0.5212002413653404,0.5106659414837471, 0.5869876102086139,0.6624735709935894,0.7332077514676827, 0.7951586652134928,0.845098078457225,0.8813676435997579, 0.9041116580482536,0.9149204654298684,0.5734213362282777, 0.6539109564930851,0.7314600073324019,0.8014445481530106, 0.8596212046135118,0.9032173146992399,0.9315553799266565, 0.9459093111069366,0.6355985255876813,0.7182616282077119, 0.7953081408089798,0.8617334421407644,0.9136023622650965, 0.9490975365686583,0.9687717311497821,0.6936983436880695, 0.7758024684863688,0.8493585337677274,0.9095263441824443, 0.9532437439744202,0.9799603432594377,0.7438876747925507, 0.8223898048944452,0.8896840618492347,0.9416915744235097, 0.9765940848309838,0.782878718417177,0.8550853688846947, 0.9142324281909144,0.9574084820329141,0.808813197110931, 0.8729720010540123,0.9233179132328828,0.8216808702627202, 0.8772096742129835,0.8230696091572214,0.09276640377310574, 0.1080256414522809,0.1232317408385954,0.1376220280275969, 0.1503972383762519,0.1609005865750696,0.1687722156036723, 0.1740026689030255,0.1768707572388351,0.1705731738763045, 0.1906930126322413,0.2105464412580206,0.2289927972013699, 0.2447929403327111,0.2568964854662051,0.2647265853183172, 0.2683098464673112,0.2681884415185128,0.2651905971794372, 0.2707904196202965,0.2960475246517403,0.3196768235430837, 0.3399487493807457,0.3552546724685221,0.3645596525653644, 0.3677018240803483,0.3653636154505258,0.3587598208776521, 0.3760147097386288,0.4053026883769021,0.4310210766569808, 0.4509771335793986,0.4634949414172432,0.4679374232163439, 0.464858993637328,0.4557105558116591,0.4821901792282771, 0.5135270414103618,0.5389508449256169,0.5561389504462149, 0.5637713635689835,0.5619411906656432,0.5520258363563475, 0.584385354546882,0.6152086674144305,0.6377766977965403, 0.6500821302058097,0.651519250667168,0.6430245879023526, 0.6777529577987501,0.7055014818762047,0.7231337276202411, 0.7293239492942762,0.724309982145211,0.7585994584260806, 0.7813166545183711,0.7928692947394784,0.7927547081501097, 0.8250622687013296,0.8417304368393459,0.8470545173149734, 0.8771323267159262,0.887645314498864,0.3015360453845148, 0.2435067604258691,0.1777013915256129,0.1060222058462941, 0.0314744189369969,-0.04238908731366174,-0.1122679310637554, -0.1757716484888774,0.1948419043847709,0.1285429999155006, 0.05595498503480459,-0.02000532948058562,-0.0956277167931003, -0.1672511147059996,-0.2320877267540867,0.07735069910586773, 0.004119161966720001,-0.07290904878978868,-0.1500083477710563, -0.2232548905422917,-0.2894667665085293,-0.04693373219192027, -0.1245600244829796,-0.2027386973931088,-0.2774338902981233, -0.3451248789428226,-0.1724667992141356,-0.2511351336125937, -0.3269264832443289,-0.3961101570586181,-0.2932657019670711, -0.3695528631494325,-0.4399926839835791,-0.4042568549159931, -0.475411790223567,-0.5022154461218424,-0.005852598553161901, -0.08119196505793361,-0.163021049159767,-0.2488060411927053, -0.3348506437156618,-0.4168788074510346,-0.4909811315875961, -0.554495326274857,-0.6063942048702905,-0.6470941325142202, -0.09827641615811868,-0.1815449292413942,-0.2700412859530667, -0.3601673374231551,-0.4473420975374328,-0.5269603854089682, -0.5955164071695848,-0.6513141073946994,-0.6944629164413806, -0.1971670439169432,-0.2870037215224481,-0.3798982345693028, -0.4712899665970914,-0.5561257020948373,-0.6300878481113522, -0.6906289766699522,-0.7372821213716372,-0.2991130971968019, -0.3930721160778319,-0.4871400501186961,-0.5761971323316351, -0.6553538941234454,-0.7211849292226609,-0.7724039524031648, -0.399676350745083,-0.4945074863044202,-0.5861531681464745, -0.6695497477032463,-0.7405921623591333,-0.7970870947583817, -0.4942022299541438,-0.5864755076757882,-0.6725212074710563, -0.74792573591698,-0.8097296395344389,-0.5788252298638613, -0.665638926578608,-0.7439338855424372,-0.810303362756081, -0.6512059956089504,-0.7306529513600663,-0.8002528392148971, -0.7107008326980349,-0.7819500193454378,-0.7580246814516085, -0.2681877205946842,-0.2683092406063016,-0.2647261130437045, -0.2568961585702784,-0.2447927610385159,-0.2289927579868598, -0.2105465274520093,-0.1906932062912961,-0.3653630641630742, -0.3677014077761052,-0.3645593896043551,-0.3552545716101517, -0.339948807237406,-0.3196770257819652,-0.2960478502452225, -0.464858585918057,-0.4679371612123701,-0.4634948428974661, -0.4509772049977542,-0.4310213111152231,-0.4053030678481073, -0.5619408834098191,-0.5637712030900536,-0.5561389551781383, -0.5389510214534028,-0.5135273825534448,-0.651518994572369, -0.6500820157815153,-0.6377767462064369,-0.6152088877366557, -0.7293237033806105,-0.7231336172569296,-0.7055015320014856, -0.7928690367045447,-0.7813165283080149,-0.841730164442513, -0.7167229918086124,-0.7847579855913325,-0.8470956951046914, -0.8993631303768833,-0.9379559853910195,-0.9610349555582707, -0.968939979478583,-0.9638217293378932,-0.9487874014792429, -0.7664454690716618,-0.8348194119106425,-0.8948601580568658, -0.9420533966954356,-0.9733334885435185,-0.9879499956150448, -0.9874639861492262,-0.9749586635216289,-0.8093339051128812, -0.8753544488679451,-0.9303745202617085,-0.9702883306150087, -0.9930645219898977,-0.9992329031534651,-0.9913973109989458, -0.8418061322197662,-0.9027097279159257,-0.9504460536566606, -0.9818515951566739,-0.996177846854893,-0.9950795477220543, -0.8612134015838815,-0.9148164899330201,-0.9540532347842757, -0.9769922654221862,-0.9840624137666333,-0.8666307679449418, -0.9118244750171576,-0.9425905155806792,-0.9581802235578871, -0.8590654047339893,-0.8959019370925372,-0.9192101353217356, -0.8409819323578077,-0.8703770126934449,-0.8154716315810106, -0.05138099314083437,0.02467734836797703,0.1061683820305035, 0.1899104376062115,0.2719386182644327,0.3483435331583406, 0.4161517553821363,0.4738214760951626,-0.09514260413767517, -0.0175091339170676,0.06545109551523358,0.1501155140512474, 0.2322441809962577,0.3079660822386824,0.3746267572069504, -0.1409096123632751,-0.06217624169087836,0.02158652471546835, 0.1064222699680223,0.1880134132638135,0.262715746535069, -0.1866893275259466,-0.1073133727582037,-0.02340754682308935, 0.06090046334304851,0.1414234968618731,-0.2301943507183196, -0.1506377672576039,-0.06724121511703012,0.01589026002363016, -0.2693512698554454,-0.1901304002696938,-0.1079011406857555, -0.3027676257747358,-0.224472743157967,-0.3299309317589397, -0.9459090564395943,-0.9315551937272754,-0.9032172391195878, -0.8596212762960382,-0.8014447869733361,-0.7314604096467703, -0.6539114969940219,-0.5734219768623257,-0.4940021735847102, -0.9687716041275991,-0.9490974969653682,-0.913602441262072, -0.8617336589899791,-0.7953084953825064,-0.7182621005240754, -0.6355990826652251,-0.5521276321758419,-0.9799603552305356, -0.9532438509855897,-0.909526560381993,-0.8493588583184724, -0.77580288428704,-0.6936988224175965,-0.6083335981356588, -0.9765942083818028,-0.941691783045487,-0.8896843516207826, -0.8223901593137167,-0.7438880688474475,-0.6594093292610237, -0.9574086493790075,-0.9142326541123561,-0.8550856370062082, -0.782879006233477,-0.7022083677250226,-0.9233180317911697, -0.872972144171723,-0.8088133452877488,-0.7343999908188845, -0.877209652594128,-0.8216808432264592,-0.7550398503916647, -0.8230693796089513,-0.7646691446910742,-0.7649398754101678, 0.6523372575426411,0.7175695092343044,0.7778217282247372, 0.828970867566289,0.867563733535706,0.8917610129438806, 0.9017515185532632,0.8994359744121508,0.6320684006265465, 0.6951665021597787,0.7517269700406279,0.7977009700656229, 0.8302002440404257,0.8482969664746548,0.8530867259579973, 0.6015117982089927,0.6606546288416104,0.7119694660426552, 0.7518832062338756,0.7783644942261146,0.7914104587099922, 0.5602367267427266,0.6140047811934269,0.6592391933644046, 0.6931464276818936,0.7146080169943935,0.509251521131838, 0.5569831878642361,0.5962197855090862,0.6250299573281404, 0.4509860998418013,0.4928650597255946,0.5269455040116824, 0.3887447062136717,0.4255810481267848,0.3258939808872747, -0.4520224891160809,-0.4005436259417036,-0.3396122734793814, -0.2705465411303475,-0.1959984769042034,-0.1195209939316481, -0.04476769320458044,0.02528677771990007,0.08876475562044144, -0.5089151305978357,-0.4549467775084718,-0.3904926248184883, -0.3173815862988101,-0.2389093389601502,-0.1591515620353438, -0.08198399284624955,-0.01030716362341688,-0.5630827400946593, -0.5059255957776372,-0.4375569069570671,-0.3604572977868911, -0.2785506506829446,-0.1962638993317844,-0.1174714262849324, -0.6111634481983498,-0.5502140363721867,-0.477882336850574, -0.3973395475484636,-0.3129711292234176,-0.2292777334167206, -0.6501543660224874,-0.5852082672248219,-0.509416602839924, -0.4265101014752001,-0.3410374453295618,-0.6781972070318585, -0.609656670182737,-0.531469622469496,-0.4476455277450017, -0.6949968061057114,-0.6238415797897515,-0.5446702557332809, -0.701669787076208,-0.6292139442700462,-0.7001879458668289, 0.8704525623995767,0.8528140389888319,0.8220319347625164, 0.7771252518482044,0.7189485862927419,0.6502746013906258, 0.5751695760111849,0.497964538802267,0.8112890338044526, 0.7854319364049284,0.7458581338540565,0.6925603649758249, 0.6275638768849461,0.5545959620739339,0.4781157400838166, 0.7364041563355311,0.7016273774526178,0.6535677410698937, 0.5933999047634313,0.5241860281758185,0.4501420452269545, 0.6466101827787859,0.6033453603619342,0.5484058620979402, 0.4840435291285519,0.4139036582576368,0.5449285342030792, 0.4948716695409166,0.4357245760359055,0.3703986729586991, 0.4362052982975687,0.3819748711371402,0.3217005151313638, 0.3260198057983744,0.270490970101615,0.2194150172838141, 0.08240304061283914,0.07443732100834362,0.06483650679882089, 0.05377961812205672,0.04167608470977979,0.02910275219613371, 0.01667396676186753,0.004907674513220617,-0.0223814688467947, -0.03526641653115874,-0.04845070073398356,-0.06127364342066123, -0.07306155181298434,-0.0832913202753871,-0.09169743029697446, -0.1348113569049385,-0.1515781730059853,-0.1667117684090611, -0.1792295328710818,-0.1884943959488113,-0.1943674753097082, -0.2505265309683513,-0.2692854573778917,-0.2841367059314813, -0.2941058574917593,-0.2989404603106648,-0.3640654661632947, -0.3824439829710035,-0.394749339616747,-0.4003758331308906, -0.4700169740867185,-0.4857712605383084,-0.4938392688789908, -0.5642388946704648,-0.5757914389111062,-0.6445533256821902, -0.1282040992012934,-0.05321855554427612,0.02998172476739921, 0.1197175263736552,0.2130449739264662,0.3060111300013609, 0.394354771181159,0.4744188270546212,0.5438573645627299, 0.6018455943795508,0.6488215902264565,-0.1584409684968982, -0.08061941460724659,0.005851663594817331,0.09880655117780221, 0.1946724526862442,0.2889433394395604,0.3771263430050333, 0.4557536219427536,0.5229695128140037,0.5785062522661573, -0.1901340637026595,-0.1097761406561385,-0.02057293935230464, 0.07475784175875169,0.1720544722828635,0.2664447755897635, 0.3534794142829396,0.4301062418911859,0.4950335464190314, -0.2221347426415571,-0.1396269276604669,-0.04840420657561294, 0.04825793538561177,0.1457553988926505,0.2391262056337631, 0.3242394390900903,0.3985926425219686,-0.252933321812349, -0.168751250747966,-0.07636778766011496,0.02046791155100532, 0.1169519253626288,0.2083323398966038,0.2909961752861106, -0.2808945465452747,-0.1956215915798094,-0.1030413667410095, -0.007197896555794293,0.08719109671857094,0.1758296024995542, -0.304612640171253,-0.218957329454675,-0.1271903099934383, -0.03341079168503205,0.0580070042064605,-0.3232363968742333, -0.2380131586083366,-0.1480327683071742,-0.05721040323642156, -0.3366056805211806,-0.2526623104055841,-0.1653138037361849, -0.3451559795786212,-0.2632729700486575,-0.3496821715684524, 0.6714420577705413,0.7363064649907652,0.8002044514563711, 0.8596329319699905,0.9106424580428781,0.9496341036793503, 0.9742808059046055,0.9841303617506333,0.9805708386415104, 0.9662289123338648,0.9441720387917811,0.7051823376417343, 0.772857969148872,0.8380824708147486,0.8966582333398196, 0.9442212565687949,0.9773096449869836,0.994346428784018, 0.9959947242778775,0.9847131791126237,0.9638452720539006, 0.7350956003099328,0.8038805096388253,0.8682639008659977, 0.9235417715931376,0.9653353535299492,0.9908225390539687, 0.9995536316680411,0.9933753693114809,0.9755844796257857, 0.7585152302781616,0.8261311652202954,0.8871112537106268, 0.9366413319148751,0.9708622908203767,0.988032496009293, 0.9889451040531339,0.9763862533615466,0.7728091190204586, 0.8367229328771276,0.8918537226509272,0.9338031104143576, 0.9597077065970592,0.9690768333338338,0.9637247890765801, 0.7760312926982586,0.8339786000659982,0.8815310666159923, 0.9151715385907231,0.933244106655567,0.9363385958760152, 0.767530944505584,0.8180289491084085,0.857374860312736, 0.8831546795136415,0.8948082473172733,0.7481893791144126, 0.790783305063361,0.8223658994270935,0.8415629406712848, 0.7201359303293944,0.755286693908195,0.7802583897718675, 0.6861124167319267,0.7148528823763073,-0.9441722324566724, -0.9089826616996974,-0.8608166107545396,-0.798145123044153, -0.7207644955095487,-0.6303269409870232,-0.5303678229575245, -0.4256669568776663,-0.3211867088850157,-0.221088326716299, -0.9323220393450906,-0.8912341788673274,-0.8355390934902637, -0.7640503287556382,-0.6773887436471858,-0.5783931938554159, -0.4717817419682783,-0.3630890689211784,-0.2573948493808621, -0.9096404828216634,-0.8611864915849053,-0.7967975927156801, -0.7160071810801653,-0.620601831095295,-0.5147133115844718, -0.4039985827676406,-0.2942465140581043,-0.873971359384776, -0.8169372490707259,-0.7433257505302604,-0.6537697064615564, -0.5513858884167668,-0.4413849791363659,-0.329823034867975, -0.8241231220089414,-0.7579997417459179,-0.6757043639889994, -0.5791759017751473,-0.4726959329165278,-0.3620176676150795, -0.7605084662990487,-0.6858745237990584,-0.5966690200428781, -0.4960221196000292,-0.388888002229758,-0.6854057579633669, -0.603999039701328,-0.5106159168102177,-0.4091534188855591, -0.6025948313141819,-0.5169734519149861,-0.4224835049786242, -0.5164821811548767,-0.4294377118759299,-0.4311427045530306, -0.3130828685601688,-0.2261777090945995,-0.128054626578418, -0.02022860607974414,0.09418349997750548,0.2106381298878162, 0.3239109228229815,0.4291933772271586,0.523048087763098, 0.6038168419091856,-0.3104444323030748,-0.2176605469020037, -0.1131261690200332,0.0009102704499983739,0.1203702792920805, 0.2398156109769667,0.3535535822768655,0.4569191625595045, 0.5471127369113119,0.6232994222041273,-0.3043330399192493, -0.2057260225894636,-0.09531787499064681,0.02374459641152675, 0.146419102006115,0.2666203176913846,0.3786227553496849, 0.4783283811020137,0.5638039035645359,-0.2939990078566014, -0.1900378279544306,-0.07488904582360796,0.04735210953525339, 0.1708856651832371,0.2894245513177179,0.3977010533927281, 0.4924894823466497,-0.2789925774668332,-0.1706755754519138, -0.05252850546893189,0.07054986219834002,0.1924160430438771, 0.307105809225674,0.4101897544477446,-0.2593820240207756, -0.1482455618465796,-0.02928064957337935,0.0921572380074013, 0.2100905134498461,0.3193074202631062,-0.2358533016570041, -0.1238167822688868,-0.006318969895916147,0.1112788403952597, 0.2236016867495729,-0.2096044634529125,-0.09868017515402502, 0.01532894544522089,0.127462173385832,-0.1820659309330632, -0.07404772953164979,0.03496843200875603,-0.1545841458628095, -0.05083475466396539,-0.6714423515894649,-0.7363066534250458, -0.8002045390283683,-0.859632936776908,-0.9106424117173109, -0.9496340459616872,-0.9742807748705438,-0.9841303841597815, -0.9805709251787237,-0.9662290590683913,-0.6736593131243975, -0.739662482666216,-0.8034418824887359,-0.8609423496737078, -0.9079287599677793,-0.9410171351552678,-0.9586305009774437, -0.9613540496396505,-0.9515175508275152,-0.6691519386893557, -0.7345993546337776,-0.79626257796142,-0.8497460401941979, -0.8909109722488041,-0.9170267250100911,-0.9275521423149625, -0.9240939630879936,-0.656100800090535,-0.7189837013183803, -0.7764177384094252,-0.8240374297552895,-0.8582583152457706, -0.8773387645254044,-0.8817972923410141,-0.6332080201962388, -0.6915533212632716,-0.7430051304270751,-0.7836645345684842, -0.8108589038018792,-0.8239068177963524,-0.6002017842451572, -0.6525164982995073,-0.6970417319534228,-0.7306820673883421, -0.7517816084827516,-0.5581290590099237,-0.6037336945345413, -0.6413705283620924,-0.6688591076565297,-0.5092217844350768, -0.5482985439178426,-0.5798809609547027,-0.4563600901355626, -0.4897211300815704,-0.4024024278645879,-0.648822290296783, -0.648349171242789,-0.6411378559950974,-0.6255004973956005, -0.600293640880965,-0.5653935344751363,-0.5219527418637842, -0.4722400785685584,-0.4191009430775375,-0.3653071673956498, -0.7148533441384284,-0.7155197916209453,-0.7077869387160182, -0.6896699883604869,-0.6600703681315339,-0.6193375043296356, -0.5694127009086171,-0.5133919240157367,-0.4547192979777763, -0.3964310803290843,-0.7802586188950914,-0.7808677178913338, -0.7711197529973893,-0.7489240139903413,-0.713538182957094, -0.6660993109205865,-0.6094980396194888,-0.5475936950081823, -0.4842093859996422,-0.841562970549242,-0.8403682541467874, -0.8267667446471012,-0.798907464199165,-0.7567804454437204, -0.7025296887747676,-0.6399394350221852,-0.5733572200389935, -0.8948081394501657,-0.8897668713226807,-0.8705497953564927, -0.8359665662613534,-0.7870195954857328,-0.7268354112898293, -0.6597854273844109,-0.9363384293729855,-0.9255995665778384, -0.8996275962075316,-0.8582152453052212,-0.8034009739634004, -0.7389956620788967,-0.9637246412193355,-0.9461585726484955, -0.9132982945459158,-0.8659870495929599,-0.8070428410352181, -0.9763861801087089,-0.9518769772925728,-0.91299797697185, -0.8614408694954784,-0.975584505681221,-0.9449459265141765, -0.9015722073987464,-0.963845393304663,-0.9284651475621579, 0.3130823317650696,0.2261770766623218,0.1280539101236855, 0.02022783076448256,-0.09418429615654819,-0.2106389019045293, -0.3239116283455282,-0.4291939860046181,-0.523048586255087, -0.6038172319641657,0.2600841789503569,0.1646285788165745, 0.05778571619095332,-0.05796842566746966,-0.1783494840893115, -0.297794748004312,-0.4106115455276073,-0.5122593289719761, -0.6001443622816693,0.1989845274738189,0.09504589509123174, -0.01970764286946627,-0.1416365624266124,-0.2653151882168217, -0.3845121065161455,-0.4936479477555078,-0.5890080819228373, 0.1303871207046509,0.01886496318699474,-0.1019485160673513, -0.2272413285785661,-0.3507747719153151,-0.4662617951252705, -0.5688734433747777,0.05597369301027853,-0.06123892158082981, -0.1852629737758222,-0.3104018186002749,-0.4302072668465533, -0.5390198361162625,-0.02151295053653907,-0.1416473256500602, -0.2654479680509194,-0.3868857125795179,-0.4999829944651056, -0.09867461327158224,-0.2185280531038441,-0.3387557568094476, -0.4536233831584169,-0.1721555744064095,-0.2886982185503227, -0.4027071899557529,-0.2393260112020272,-0.3502029095948441, -0.2986531728773539,0.1282040764044601,0.05321845051851246, -0.0299819504088954,-0.1197179049294584,-0.2130455201840074, -0.3060118317848468,-0.3943555879655132,-0.4744196990222574, -0.5438582278251758,-0.6018463947181518,0.0508347486986919, -0.03269551088976293,-0.1240992485699228,-0.2207245467966487, -0.3185587143239474,-0.4128297421420895,-0.4990447345439135, -0.5740017863876323,-0.6362851104978535,-0.6861131509034271, -0.03496843048443223,-0.126718664611582,-0.2252069693946209, -0.3266628419962321,-0.4261053308619027,-0.5183500501623459, -0.5992598174372795,-0.6666016739887244,-0.720136706807033, -0.1274621786236507,-0.2261259790523726,-0.329452932630458, -0.4326360098383147,-0.5301336109403396,-0.6169837287853274, -0.6899929014185586,-0.7481901976756111,-0.2236017161594696, -0.3267916497417753,-0.4317330442416767,-0.5329718535250777, -0.6250530132529536,-0.703875699754291,-0.7675317913865697, -0.3193074905789999,-0.4238069911549194,-0.5267203305462205, -0.6225639241960304,-0.7066200241231836,-0.7760321386045594, -0.4101898771205939,-0.5125483215775021,-0.6101488498350025, -0.6980950818677443,-0.7728099228904255,-0.4924896604754524, -0.589718294305585,-0.679698789286976,-0.7585159482037284, -0.5638041319099237,-0.6538562109800681,-0.7350961954485662, -0.623299689837984,-0.7051827865650066,0.6483485235093655, 0.6411372723067146,0.6254999813609712,0.6002931843810706, 0.5653931175085023,0.5219523372221417,0.4722396596033476, 0.4191004905746099,0.3653066725193389,0.5719394335088436, 0.5579566936615924,0.5351892752413209,0.5030959515755655, 0.4623631200726549,0.4149320801413646,0.3635618193945052, 0.3111391128807077,0.4812077579558775,0.4596949806286311, 0.4297384718300117,0.3916338931244087,0.3469138437185931, 0.2980734064957148,0.2479339211516947,0.3769270466275512, 0.34798863705642,0.3118670040922781,0.2697400287604772, 0.2237517049369116,0.1764984145646341,0.2618704462020703, 0.226741584116328,0.1865794537419862,0.1432114770939381, 0.09893915843486868,0.1407291822657564,0.1016644513337693, 0.06025214993119726,0.01853073096018258,0.0192767564986509, -0.02097489882147943,-0.06089466864900071,-0.09693134249768307, -0.1358102942547418,-0.2036925009060082,0.9089824672626068, 0.8608164411414747,0.7981450033151747,0.7207644427956729, 0.6303269573377657,0.5303678926086439,0.4256670502613963, 0.3211867913977836,0.2210883675134422,0.9284650222993554, 0.8871725500645941,0.8313006928275277,0.7596804192454199, 0.6729483436994259,0.5739528381046123,0.4674119497069649, 0.3588508188064044,0.2533333547826946,0.1545840239447369, 0.9015721838250796,0.8527098639261403,0.7879881821713033, 0.7069782619885319,0.6114960278478284,0.5056844240896506, 0.3951892122332402,0.285769898249863,0.1820657113417612, 0.8614409638909055,0.8038277741655933,0.7297824294607609, 0.6399926588400225,0.5376088254129453,0.4278416041703199, 0.3167134480853839,0.2096041546069603,0.8070430398170311, 0.7402383431039794,0.6574928275930984,0.560806507588693, 0.4544842943197335,0.3442560602121421,0.2358529185889448, 0.7389959159190774,0.6636727901334047,0.5740968543042024, 0.4734498690772693,0.3666860169560138,0.2593815856416469, 0.6597856586149884,0.5777801683909922,0.4841878538357612, 0.3829343216652333,0.2789921022280572,0.5733573420809426, 0.4873055377141242,0.3928154550148792,0.2939985098519586, 0.4842093252521232,0.3969457605237173,0.3043325272261384, 0.3964307901608145,0.3104439077532887,0.6362842799460792, 0.5740009080509757,0.4990438745890764,0.4128289723722984, 0.3185580939893934,0.2207241066952111,0.1240989860968612, 0.03269539736236529,0.6666008139591869,0.5992589358516202, 0.5183492243752909,0.4261046359672609,0.3266623297142192, 0.2252066549797059,0.1267185286485934,0.689992024373379, 0.6169828686407737,0.530132851891722,0.4326354224553247, 0.3294525535291586,0.2261258043934048,0.7038748329124103, 0.6250522113657903,0.5329711994696135,0.4317325950511361, 0.3267914216946357,0.7066192072999724,0.622563222965479, 0.5267198166849954,0.423806701432994,0.6980943593640034, 0.6101482870567641,0.5125479715352314,0.6796981993053423, 0.5897178945758851,0.6538557778979411,0.9323218580087335, 0.9515174346052251,0.9613540145138462,0.9586305496920505, 0.9410172511751344,0.9079289076765579,0.8609424826856753, 0.8034419563572277,0.7396624652608896,0.6736591885240169, 0.9096403689902206,0.9240939382365192,0.9275522217134882, 0.9170269050764695,0.8909112253661301,0.849746318794698, 0.796262827475376,0.7345995283284186,0.6691520068054228, 0.8739713777891581,0.8817974249990892,0.8773390199098727, 0.8582586769711391,0.8240378550735361,0.7764181680693131, 0.7189840763729405,0.656101076264991,0.8241233338640371, 0.8239071624215608,0.810859375773786,0.7836651001194045, 0.7430057321681839,0.6915538934135402,0.6332085061147845, 0.7605089118538748,0.7517821894256266,0.730682759920626, 0.6970424857617678,0.6525172475488723,0.6002024649156599, 0.6854064426268304,0.6688599095382908,0.6413714065577412, 0.6037345889195533,0.5581299045184589,0.6025957234227359, 0.5798819340529643,0.5482995467196561,0.5092227563225322, 0.5164832226272315,0.4897222069506372,0.4563611494403301, 0.4311438278222397,0.4024035413263246,0.3496833143594963, -0.2533334075588959,-0.3588508219660573,-0.4674119310964882, -0.5739528280203016,-0.6729483660875734,-0.7596804843602077, -0.8313007955580714,-0.8871726742411541,-0.2857700357309581, -0.3951892821849063,-0.5056844492735352,-0.6114960336943951, -0.7069782687900093,-0.787988199289983,-0.8527098891878181, -0.3167136531290147,-0.4278417135183467,-0.5376088566787567, -0.639992634716211,-0.7297823708955478,-0.8038276949870052, -0.3442563102744645,-0.4544844137443082,-0.5608065113072425, -0.657492739431111,-0.7402381888786644,-0.3666862898302455, -0.4734499761180304,-0.5740968114811118,-0.6636726256137523, -0.3829346014572981,-0.484187939006181,-0.5777800787254197, -0.3928157351219527,-0.4873056046530535,-0.3969460425371419, 0.600144149407117,0.5122590107240774,0.4106111150772491, 0.2977942147481387,0.1783488751426739,0.05796778060322072, -0.05778635529341983,-0.1646291768340628,-0.260084712697206, 0.5890080700362191,0.4936478319326018,0.3845118729629886, 0.2653148405479006,0.1416361235766341,0.01970714938077021, -0.09504640342765641,-0.1989850169013517,0.568873645341387, 0.4662618905156885,0.3507747390771843,0.2272411653349327, 0.1019482418076114,-0.01886531395275474,-0.1303875092325712, 0.5390202482542535,0.4302075642722875,0.3104019730711553, 0.1852629793843341,0.061238796088284,-0.0559739158243807, 0.4999835950826037,0.3868861858512856,0.2654482825921611, 0.1416474750285941,0.02151295276860444,0.4536241369952758, 0.3387563694841473,0.2185284943201295,0.09867487876932232, 0.4027080566573236,0.2886989329203987,0.1721561104744125, 0.3502038519557472,0.2393267951217032,0.298654161645374, -0.984713259051105,-0.9959947612173518,-0.9943464342167785, -0.9773096457316496,-0.9442212908026748,-0.8966583408378461, -0.8380826821486993,-0.772858299407996,-0.9933753694301851, -0.999553621201999,-0.9908225482912788,-0.9653354239158236, -0.9235419454230499,-0.8682642090770526,-0.8038809644055149, -0.9889450353591552,-0.9880324674077218,-0.9708623496514911, -0.9366415263248317,-0.8871116184521261,-0.8261317121277938, -0.9690767381494427,-0.9597077173597477,-0.9338032828335274, -0.8918540989344099,-0.8367235291009597,-0.9332440544501512, -0.9151716624555581,-0.881531417418751,-0.8339792023300259, -0.8831547498989281,-0.8573751662344773,-0.8180295248104962, -0.8223661602728524,-0.7907838386634013,-0.7552871860588483, -0.3111396248637522,-0.3635623115811258,-0.4149325617880891, -0.4623636071651684,-0.503096462804278,-0.5351898259002444, -0.5579572907903124,-0.5719400747815568,-0.5785069283639182, -0.2479344056308305,-0.2980738893651726,-0.3469143327715778, -0.3916343988495664,-0.4297390036046975,-0.4596955428592778, -0.4812083484887336,-0.4950341577852201,-0.1764988082863052, -0.2237521076309895,-0.2697404459306319,-0.3118674408741246, -0.3479890956417611,-0.3769275248330646,-0.3985931343359159, -0.09893938901501213,-0.1432117197792371,-0.186579712833973, -0.2267418620329016,-0.2618707421483436,-0.2909964852939082, -0.01853073419918189,-0.06025216482046628,-0.1016644832117871, -0.1407292340032114,-0.1758296738027379,0.06089493100791283, 0.02097514873862574,-0.01927652693035482,-0.05800679989935065, 0.1358108258261833,0.09693185715603557,0.0572108907410362, 0.2036932746994002,0.1653145532988453,0.2632739407438701, -0.5471131012749936,-0.4569196340994406,-0.3535541587646317, -0.2398162730231354,-0.1203709912173413,-0.0009109877496780188, 0.1131254888510733,0.2176599356582619,-0.478328705556794, -0.3786231842883476,-0.2666208448402719,-0.1464197032303796, -0.02374523314186032,0.09531724619007391,0.2057254392916539, -0.3977013270678494,-0.2894249268534429,-0.1708861328457008, -0.04735264212838212,0.07488848723411734,0.1900372836013431, -0.3071060284643041,-0.1924163631703616,-0.07055027067676212, 0.05252803743712917,0.1706750855887899,-0.2100906838592314, -0.0921575105240425,0.02928029003178066,0.1482451448989572, -0.1112789754160983,0.006318730357784829,0.123816455924515, -0.01532906198457091,0.09867995232819112,0.07404761591340303, -0.5229702552542058,-0.455754381535759,-0.3771270572800299, -0.2889439451759633,-0.194672902916372,-0.09880682824059986, -0.005851780927923981,0.08061942171187554,0.1584410564110822, -0.4301068723700084,-0.3534800054422614,-0.2664452641642683, -0.1720548071373146,-0.07475800001082551,0.02057294660420643, 0.1097762759917693,0.190134278339324,-0.324239892584746, -0.2391265604455334,-0.1457556026172962,-0.04825796103784657, 0.04840435106572794,0.1396272041512353,0.2221350978923813, -0.2083325524549613,-0.1169519894866824,-0.02046779780351893, 0.07636807502743861,0.1687516747481361,0.2529338262925594, -0.08719102201021951,0.007198147474544054,0.1030417934166896, 0.1956221602390349,0.2808952005684322,0.03341116907881747, 0.1271908635410245,0.2189580303556818,0.3046134343217798, 0.1480334302314487,0.2380139721282078,0.323237313179716, 0.2526632136782334,0.3366066958443542,0.3451570696887505, 0.4547190651923899,0.5133917435529424,0.5694125552964621, 0.6193373643788831,0.6600701990863211,0.6896697595590643, 0.7077866317530298,0.7155194029861072,0.5475936980371527, 0.6094980941008995,0.6660993907770576,0.7135382519498201, 0.748924034855715,0.7711196978950583,0.7808675744788959, 0.6399396139686974,0.7025299057553986,0.7567806697847073, 0.7989076596939885,0.8267668794191896,0.8403683088289017, 0.7268356797799225,0.7870198804193677,0.8359668400797895, 0.8705500304441893,0.8897670473314109,0.803401239536944, 0.858215507236093,0.8996278379610205,0.9255997745502057, 0.8659872391476647,0.9132984713369965,0.9461587349139229, 0.9129980537560551,0.9518770467134829,0.9449458910860361, 0.2573950079345842,0.363089267383967,0.4717819390919956, 0.5783933464143868,0.6773888186481814,0.7640503132419058, 0.835538996354342,0.8912340245126464,0.2942468056901541, 0.403998910419839,0.5147136234231619,0.62060207699311, 0.7160073275453937,0.7967976318501995,0.8611864392751584, 0.3298234714814622,0.4413854454351217,0.5513863234589741, 0.6537700563672823,0.7433259842576552,0.8169373645440049, 0.3620182543235557,0.4726965405256068,0.5791764634315589, 0.6757048258462731,0.7580000771779366,0.3888887347419664, 0.4960228623929952,0.5966697046150125,0.685875099677583, 0.4091542831822379,0.5106167801856609,0.6039998362037971, 0.422484479771868,0.5169744155067602,0.4294387727293252 }; auto pointshell = new vnl_matrix_fixed(coords); return pointshell; } }; // ThreeLayerPointShell using a staggered-distribution #include template TMatrixTypeshell1 * ThreeLayerPointShell< NpointsShell1, NpointsShell2, NpointsShell3, TMatrixTypeshell1, TMatrixTypeshell2, TMatrixTypeshell3> ::DistributePointShell1() { itkGenericExceptionMacro(<<"This type of PointShell is not Supported and should not be used!"); return 0; } template TMatrixTypeshell2 * ThreeLayerPointShell< NpointsShell1, NpointsShell2, NpointsShell3, TMatrixTypeshell1, TMatrixTypeshell2, TMatrixTypeshell3> ::DistributePointShell2() { itkGenericExceptionMacro(<<"This type of PointShell is not Supported and should not be used!"); return 0; } template TMatrixTypeshell3 * ThreeLayerPointShell< NpointsShell1, NpointsShell2, NpointsShell3, TMatrixTypeshell1, TMatrixTypeshell2, TMatrixTypeshell3> ::DistributePointShell3() { itkGenericExceptionMacro(<<"This type of PointShell is not Supported and should not be used!"); return 0; } //Distributed Pointshell 7 - 16 - 22 template<> class ThreeLayerPointShell<7,16,22, vnl_matrix_fixed,vnl_matrix_fixed,vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell1() { double coords[3*7] = { 0.8134386897688890,-0.4341253640420510,0.3871080808797820, -0.4587290424636170,-0.8386718051774130,-0.2935937138306440, 0.9664216428893120,0.2069240694656700,-0.1523536597226610, 0.2351246687899070,-0.9681943247699460,0.0855344352275928, 0.3639220075873960,0.5047564346894090,-0.7828037519284090, 0.5319989685716170,-0.4881652317616310,-0.6918611160759410, 0.3581867553682760,0.1086899080235520,0.9273018668009770 }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 7; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell2() { double coords[3*16] = { -0.5452474978820280,0.0761974966412968,0.8348048320170320, 0.2927980757498900,-0.6773379396203400,0.6748945120446840, -0.1417004162163040,-0.9633970491958960,0.2275678308653590, -0.6140671213404080,0.1662941114081500,-0.7715360257302810, -0.7535977994952050,0.5644302557976330,0.3369107343736630, 0.6046201505721580,-0.7556729189381400,0.2517794930204040, -0.5761191698601790,-0.6394532899960390,0.5091033215692650, -0.8771756234198420,-0.0996725024517006,0.4697108876032420, 0.1777092608486050,-0.9423569168215250,-0.2835187117762620, -0.9768429239415920,0.1642087829444060,-0.1371618662353210, -0.1487223091187880,0.6561163072920720,0.7398601665691900, 0.8263922468011810,0.2709995685589460,0.4935940520992520, 0.0308686015665059,0.1530129849183750,0.9877419480227090, 0.4225700117384330,0.6462523317724860,0.6354467002495210, 0.7837589636894810,0.6201599634850410,0.0335187488821074, 0.0539036789368123,0.3663544585938050,-0.9289126999161520 }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 16; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell3() { double coords[3*22] = { -0.8173635684245140,-0.4833486694613540,0.3135137329404920, -0.2868452120883920,-0.8216012153929990,0.4926370541957950, -0.7664861123034300,0.2467518351336330,0.5929692837779680, -0.6473847770633690,-0.2749696442612980,0.7108337676007140, -0.8434413337089970,0.5359848183688930,-0.0364278886191929, -0.6982955004575730,-0.6008181795242910,-0.3891026974897790, -0.1840600867015900,-0.4748774851820570,-0.8605889021772050, -0.5838330320870200,0.5227863025561340,-0.6211549504783510, 0.2399013062530760,-0.8837050331449780,0.4018865233524250, 0.2572284397529420,0.1068009420516400,-0.9604306786849050, 0.0228576846453402,0.6541700548957510,-0.7560020274644400, 0.2865351008629760,-0.2558878809381770,0.9232654159895800, -0.9589554732993380,0.1841748711215180,0.2156015238272050, -0.1416546797498710,-0.9826949020935590,-0.1193510833813020, 0.5319624136210260,0.8404508833828820,-0.1032390580905500, -0.9539866018255820,-0.2464051857069770,-0.1708626582784740, -0.5967074337720730,-0.2735711735415940,-0.7543865398376490, -0.8579620414436700,0.0965216321209641,-0.5045638809650520, 0.4190874972849550,-0.7580966160880540,-0.4996550713194790, -0.1112620377459570,-0.8350080959462270,-0.5388712635322760, 0.2732684357685670,-0.3457098692283190,-0.8976686740277500, -0.5333383595673660,0.8405849409324210,0.0946950436486956 }; auto pointshell = new vnl_matrix_fixed(); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 22; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } }; //Distributed Pointshell 15 - 30 - 45 template<> class ThreeLayerPointShell<15,30,45, vnl_matrix_fixed,vnl_matrix_fixed,vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell1() { double coords[3*15] = { -0.8262066830854160,-0.4726354124122490,0.3065913954417130, -0.1648544111105980,-0.8594278487393920,-0.4839491666988980, -0.0358642514967783,-0.3860637871945480,-0.9217746512470260, -0.7684991573547760,0.6362727605963740,-0.0675723261999100, 0.1470231744209000,-0.2445782513554300,0.9584183142798970, -0.4269215415769730,0.7783400701208900,0.4603528348796690, 0.3354375712062210,0.5671194195694440,-0.7522348036155240, 0.4782763791345030,0.8758625019989820,-0.0641606012602034, 0.9898549006984430,0.0293790674196844,0.1390113159452260, -0.6942246846812260,0.1045517495672360,-0.7121243001342390, -0.5002034766634700,0.0396563572397201,0.8649993383027830, -0.3370551539814700,0.7499893159794020,-0.5691307838197430, -0.8609008763099680,0.2402687527531810,0.4484647228257290, -0.6557865116475140,-0.4999608118385060,-0.5656706088944040, -0.0693103556967315,0.9875634181798020,-0.1411189911607090 }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 15; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell2() { double coords[3*30] = { 0.2837014644823930,-0.2206420544708420,-0.9331830275189480, 0.6952056236193340,-0.1366087970901870,-0.7057104062188850, -0.3982427056422320,-0.8946622247526970,-0.2024407345454680, 0.6912813662578750,0.2350369054844600,0.6832918305709010, 0.3004304980870820,0.2618613453462440,0.9171532868788080, -0.8122424463478870,0.2721420733854360,-0.5159466060013130, 0.9419343376574850,0.3217426374876110,-0.0961320901898529, 0.7896221636664980,0.5657937790715650,0.2374325971945600, 0.4752045378048260,-0.5551457462701800,0.6826374203395420, 0.7035350948936570,-0.6486284012073840,0.2903783177169470, -0.9657370911439440,0.2516454388878180,-0.0634542660172643, 0.4335627021602730,0.7069616605804370,-0.5587741885277380, -0.8057237269560320,0.4981857033689590,0.3203440038128640, 0.9405564452144110,-0.0095010729465114,-0.3395044962566700, 0.1927525893653990,0.9455549503304530,-0.2622446857393060, 0.0977862568275677,-0.8610667582542970,-0.4990008875794600, -0.9244603975979140,-0.1428771862175540,-0.3534955203841470, -0.0587838438498267,0.9919184790835770,0.1124374962135020, 0.0413372764424745,-0.4921846796379190,0.8695087525183610, -0.0416085831127836,0.5985263605884460,0.8000218256348350, -0.0355639978656969,-0.0871145642116132,0.9955632851597270, 0.4117279464778380,0.2997527998928850,-0.8605976743203040, -0.3489182251383090,0.9011702877335440,-0.2571928938998360, -0.4466362970172200,0.1524928751735680,-0.8816246033363880, 0.7474002986676580,0.3366743112988240,-0.5727505579769920, 0.0459516120636567,-0.8054328299457180,0.5909030426342220, -0.6709586581555420,-0.7177574233281400,0.1861149115559070, 0.4102749262480260,0.6499693688374480,0.6396986043952450, 0.4840608825017460,-0.5520432393197750,-0.6789207052027200, 0.4932109057185150,-0.8496377203461870,-0.1867049722028180 }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 30; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell3() { double coords[3*45] = { -0.5319244251302650,-0.0924062555950406,-0.8417348097095310, -0.1568078023044440,-0.3505682049930730,0.9233164391390330, -0.0268033601770698,0.1671912039337100,0.9855600850330810, -0.8635644949706830,0.4302558439150240,-0.2629377717312540, -0.1232991287092530,-0.6582015219317060,0.7426762965022680, 0.2376788616301610,0.5073218686335690,0.8283316246168160, -0.2566320540992200,0.7153437906464810,0.6499409588511380, -0.2043674419674510,0.6455892128571940,-0.7358318536908470, 0.4526173742466210,0.8360898783655450,-0.3099858510073980, 0.6836009409422150,-0.5146343588192300,0.5175337962545400, -0.2416560443524480,-0.0050416195905518,-0.9703488745290690, 0.6217071536966980,0.1629149642421430,-0.7661193963530300, 0.3925487362456630,0.8090879125984540,0.4373582505898420, 0.9937673576390050,0.0621775579881003,-0.0925223766114162, -0.6145262933579910,0.6346672434383510,0.4685668841030610, 0.8259615790179220,0.5617603544414460,-0.0470401335461508, -0.9322660404149650,-0.3254138676429420,-0.1580691134746520, 0.1164156506796110,-0.9181409097759250,0.3787672980507660, 0.3168138266596370,-0.3901334055493560,0.8645374052704360, 0.2176339314862630,0.9758048396839600,-0.0209853929005879, -0.8962471871878890,-0.2522993236544900,0.3648095814822890, -0.6612497786305060,-0.6092040683197140,0.4377432277075390, 0.6864998437314790,-0.7035766895855040,-0.1835696228371040, 0.5089165208574290,0.4070575405636090,0.7584906943850230, -0.8813116916999880,0.4680173236355270,0.0651880882519394, -0.2012114505332470,-0.8431942495440670,0.4985352642593330, 0.5022346836643460,-0.7613661632799880,0.4099779115231480, 0.2920837705654530,0.0626526547841476,-0.9543383654767130, 0.9371632886212690,-0.1374669688206610,0.3206677454060930, 0.5505816317240350,0.4677412077906900,-0.6914318689087190, -0.7167807102053720,0.3777968874482810,0.5860844011845660, -0.2932320783275170,0.9560381526252190,-0.0024492784000661, 0.6321152281530360,0.6671359233891630,0.3941573265343710, 0.8208025699405450,0.0279728493062268,-0.5705266522088930, -0.2446598581934500,0.4624912733458210,0.8521991409686590, 0.5981132830055560,-0.3201976866112960,0.7346658711149490, 0.5635872282435520,-0.8205568040328310,0.0951628473521853, 0.8170510623081950,0.3573123713720030,0.4524990948560730, -0.1077825148871900,-0.7347609483441290,-0.6697083531456270, -0.9500761846368700,0.2288974134306240,0.2120406034480560, -0.2525195159846320,0.9165943360942510,0.3099818012187580, -0.1132939165725980,-0.9559551317814730,-0.2707660881430790, 0.6286490080714330,0.7713217806218580,0.0993133192936744, 0.8227143106676380,0.0339170261757165,0.5674423304249230, -0.5076617466917020,0.3007946007639900,0.8073426528415060 }; auto pointshell = new vnl_matrix_fixed(); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 45; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } }; //Distributed Pointshell 22 - 46 - 67 template<> class ThreeLayerPointShell<22,46,67, vnl_matrix_fixed,vnl_matrix_fixed,vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell1() { double coords[3*22] = { 0.25642497332897700,0.69733994966702500,-0.66930055106179500, -0.63587617425647900,0.60181880252846700,-0.48319314971980300, 0.02750479511080110,-0.49812287900990700,-0.86667011235694100, -0.35667911834084000,-0.89655924253456900,0.26260527634729500, -0.97064232445283100,-0.10849397816068500,0.21466842963899700, 0.13668042906880200,-0.37888572303413600,0.91529452592620000, 0.51081097493717800,0.82410825259274000,0.24478099577432200, 0.56937377655062700,-0.82148556938178300,-0.03122437948083380, -0.70712711556887700,0.17758768051548200,-0.68442228058149100, 0.77448769645207600,-0.17248893945546000,-0.60861841395893200, -0.10740168736203100,0.99190238197584200,0.06778305232459120, 0.33092898900280500,0.01295617559776370,-0.94356671293102500, 0.96250467033690100,-0.16189196397475400,0.21765971510605600, -0.45033217736158200,0.66660174976094400,0.59400592358024700, -0.85650276118059000,-0.36171210207403600,-0.36819475186809100, 0.27465477100258400,0.05835151655784800,0.95977073162340000, 0.11563788475332800,0.85791228608919200,0.50061401197628500, -0.54693056940441200,-0.44061696250608900,-0.71184523922189100, -0.84737370301991400,0.46653338404151400,0.25358314022247300, 0.81343236730821000,0.56190972107245100,-0.15028389527703900, -0.18653146496488500,0.84209830314271900,-0.50603997907498000, -0.63212021995023600,-0.37635030041830100,0.67733631152118100, }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 22; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell2() { double coords[3*46] = { -0.23824007219091700,-0.86075578881685600,0.44982345428038400, -0.51769500181405200,-0.84172584112854800,0.15326282482449900, 0.44541900890312100,0.24566021345088800,0.86096049040302900, -0.07063199679231950,-0.74435681728880700,-0.66403618093053200, 0.28462319785348100,0.44431638677441600,-0.84945428581377100, -0.64536739671188300,-0.00105803610891677,-0.76387158856768400, 0.99957798463419400,-0.02741827295649930,0.00959640259292167, 0.25010658086046200,-0.76013925657241700,-0.59969576355667200, 0.39532812932947400,-0.74907082575728600,0.53160941315984400, -0.34325878056226200,-0.92866133182164700,-0.14057574593845400, -0.26920408631492800,0.17274923523146300,0.94746338274272500, -0.54467024543405600,0.38808975702695900,-0.74345185737178700, 0.52496025794034400,0.62807129953803100,-0.57440679860166600, -0.78050768202914100,-0.24385280423360000,-0.57562450274541400, -0.71694800106757700,-0.61231043102019300,0.33325890810159900, -0.79056173292908700,0.40623831712611600,-0.45823855809687000, -0.01131173053347560,-0.71410951594821100,0.69994260049274700, 0.51711028616473900,-0.41981678757221700,-0.74588927919307400, 0.09293538971980670,0.99313719503650500,-0.07100369830262170, 0.06305806394427750,0.10620573522794300,0.99234269402071500, -0.33630459418638300,0.16690245213556600,-0.92684561357340800, -0.91315571223437600,0.04840489638883500,-0.40472658822878600, 0.93648363925049100,-0.29927660444831100,-0.18284394287491200, -0.05612562081659480,-0.20305248576816200,0.97755797920702400, -0.37275429556215800,0.89636189167834400,0.23997832045161700, -0.67013076748237900,0.05267398640468540,0.74037166722512800, -0.71746069173368600,-0.55944913994429100,-0.41505037722258700, 0.32124017988313500,-0.93693212076753000,0.13770601984920100, 0.28299823115899100,-0.54968353921141900,0.78597710391644900, 0.15427673771645000,0.42717011000338400,0.89091210863881800, 0.73177570493927800,-0.66613283838403200,-0.14412272300735500, -0.75153823442269900,-0.65477491803088400,-0.08037467834125450, 0.48366214065856300,0.15108779957940700,-0.86211565959088600, 0.93951136227144300,0.29289842100589400,0.17756383397277200, -0.87960856796377400,0.45546435899697500,-0.13726246700461500, -0.64569564311752200,0.72655108088168600,-0.23494821414239600, -0.78338372249755100,-0.28544299520279600,0.55211614703387900, -0.21774715968345700,0.47658876151730000,0.85173313123606500, -0.02304329061227880,-0.94618817341094000,-0.32279551926107200, -0.57374791604915700,0.69666566365718000,0.43066260797792600, -0.07108119393689040,0.93003463387864700,-0.36053161255944900, 0.76657473659095900,-0.39315791656506800,-0.50773026880688800, -0.92597907090953600,-0.32929755574671300,0.18473191390972800, 0.91121096468807600,-0.03396316106949220,-0.41053755190286000, -0.43706646286549800,-0.78947199903364200,-0.43093719934587400, -0.40196779675371200,-0.59965623521593900,-0.69197853286038600, }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 46; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell3() { double coords[3*67] = { 0.56913096982886400,-0.78778656243185400,-0.23554674957098500, -0.83141868259485000,-0.50643545944750500,-0.22861780255797400, 0.66520728747741500,0.16406099937578400,-0.72841145870363500, 0.89843444153226500,-0.32337141111972700,0.29706309891846700, 0.10682918210921600,-0.98310908722825000,0.14860702695065500, 0.47695410914339100,0.34004755151800200,-0.81048284403671700, 0.73195863370355600,-0.57860748500094100,-0.35979151858778900, -0.34693446874899500,0.36681507780719100,-0.86318200461267500, 0.69155604307511300,0.71307629015228000,-0.11520609232567700, 0.46165051573275700,0.03834019742989470,0.88623294374826500, -0.97397281635692200,0.09418099894164960,0.20617199721620300, -0.29851857051594800,0.88978611904209300,-0.34520620709530000, -0.35435497259052800,0.57535527165268900,0.73715592976101400, 0.81483656941218300,0.03423671864623110,-0.57867885069779600, 0.09093817184312330,-0.56161837808076500,0.82238375853597700, -0.38426863203906100,-0.76843844817080100,0.51170300937524000, -0.73642722235150800,0.67410491319471600,-0.05707461945896040, -0.54419758869133900,-0.83725684681843000,-0.05338498776051840, -0.21793670722020900,0.72064410859729600,-0.65816081651069200, -0.86994783943189000,0.48926427520412000,0.06173512514583880, 0.10775852899571900,-0.66193658502334100,-0.74177358866187400, 0.64544044927716800,-0.27486614918135400,-0.71263961893168700, -0.11695347642120100,0.01614429356247260,0.99300616621366600, -0.76804280354387200,0.56311186828017900,-0.30498405815136400, -0.89852079402656700,-0.17264968995044600,-0.40354983243941700, 0.15476743176759600,0.59389894762264400,-0.78951319309861900, -0.08940325110166170,0.85346959988929000,0.51341669310343500, -0.70606292132486000,-0.42769174553909800,-0.56440669904592900, -0.07667441970353810,-0.38529704221355400,0.91960166519238700, 0.97761562873568900,0.05518196801914950,0.20303357568948200, 0.27057965262888700,0.90009485795734400,0.34149070011052800, -0.07518696164793450,0.30024488370383200,0.95089427940640300, 0.13690225805945900,0.94909731680809600,-0.28367596825584700, -0.50251469982969000,0.85107263931481100,-0.15216549893059300, 0.21156984762048800,0.24711360232026500,0.94560724781811200, 0.54655302079371500,0.64091673556772900,0.53898565243470900, -0.55776925354389300,-0.75378730237748100,0.34741641091860700, 0.33541827007282600,-0.94032512320547200,-0.05729962277332840, 0.67560875362133500,-0.41311684917543700,0.61064497128657700, 0.89999152407210300,0.19323788395138500,-0.39073568662762600, -0.98068938405211500,-0.19462539941972600,0.01921681316436500, -0.83571217382768900,-0.43205465164452500,0.33898958761077700, 0.17146282600716700,-0.94464169246048600,-0.27973661212473500, -0.54059787517480200,0.19148492705680600,-0.81919927982545100, -0.11323347366714700,-0.98395720927995300,-0.13786366724876500, -0.96778324010342800,0.25052610173143600,-0.02514502985004660, -0.37464301489130600,0.81614231968582300,0.43994809399632100, 0.87643936365442000,-0.25855236763220800,-0.40620772398960400, 0.52679822663262800,-0.75204791987234000,0.39611558241554000, -0.83302160989998000,0.19922527352743200,-0.51612429494024400, -0.36881247628488900,-0.43365274990848600,-0.82214515131661200, -0.68494199482737100,-0.50458868526765800,0.52558988044078300, -0.47018702426243700,0.58505538166360200,-0.66078314340008000, -0.37416836671127000,0.32358269311843500,0.86907552840178400, -0.27722979680714700,-0.21344561523946000,0.93679485966636000, -0.42391010237203500,-0.55511199435901700,0.71564718879185400, 0.14056187290120700,-0.12797911064355400,0.98176560701900200, 0.65560180621904000,0.70152916060800800,0.27936196680821600, 0.31456644451092300,0.94702007926960300,-0.06481451571947000, 0.17066405874813200,0.60471657508100000,0.77794064225615400, 0.79129808311628700,0.03500830070119940,0.61042752439458600, 0.89399321972683600,0.44808044136416500,-0.00020285300027376, 0.62673064388188000,0.22888439553392000,0.74486282864731200, 0.04867785807210680,0.84066847580244300,-0.53935793303288600, -0.61746764544761500,0.52381468179607700,0.58681503556082400, 0.28885209645735200,0.75743720323072100,0.58553680459405300, -0.49988083572550200,0.08893492748180820,0.86151594805214000, }; auto pointshell = new vnl_matrix_fixed(); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 67; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } }; // Distributed Pointshell 30-60-90 template<> class ThreeLayerPointShell<30,60,90, vnl_matrix_fixed,vnl_matrix_fixed,vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell1() { double coords[3*30] = { -0.0384531292653293 ,0.0948025501623591 ,-0.9947531519590260, 0.5110816729083550,0.5146956026128700,0.6883923011395250, -0.1446942037238470,0.9515456918526300,0.2713381354056610, -0.4478957210732550,0.8874427193875220,-0.1087880639148770, 0.0234550198951957,-0.3332336016908820,-0.9425525071558810, 0.9229881949625620,0.3842551764058190,0.0209940792863651, -0.6911807120917000,-0.6043864949901590,0.3962148254494260, -0.0316674103891205,-0.6829866700288160,-0.7297440535435660, 0.8018580068378580,-0.1378458982057450,0.5813968053041460, 0.3973142766999960,0.4262411104325310,-0.8126868285555020, -0.8315080441618440,0.3951380066705710,0.3904616859290430, -0.8752414233521170,-0.2348057149272050,-0.4228696336769560, -0.2818275522720080,-0.8725715270267310,-0.3989889234084150, 0.2217711399465410,0.7995959805053020,-0.5580894457401740, -0.3678809968698430,-0.0344850464711680,0.9292332073876450, 0.6642059344014850,0.6635911630050780,0.3442052368680880, -0.8248962619923230,0.5292990045320030,-0.1984659183651500, -0.1835415051538410,0.8857989535243100,-0.4262308410017950, -0.1166555399998580,-0.9878457938197680,0.1027237685247790, -0.5153844228415700,-0.8501138123201320,0.1080990415997830, 0.5451858248749490,-0.3086757610614890,-0.7794175330902750, -0.5671903126002800,0.6003393592309330,-0.5638153980254410, -0.4890503721480340,-0.1375661511749660,-0.8613392406902020, 0.3976996460860260,-0.6740608343992310,-0.6224764919513510, 0.4349956952359570,-0.3316293950165250,0.8371383932702870, 0.6340600901853650,-0.7334511285536860,-0.2449841710344760, 0.9845956419603380,-0.1475029048111290,0.0938845828929696, 0.9610090220995200,0.0558414999946514,-0.2708198410782880, -0.7632149908465080,-0.1335295186020360,0.6321967616249480, 0.0826215834627986,-0.6217188595585800,0.7788705499730220, }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 30; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell2() { double coords[3*60] = { -0.9757323531821020,0.1141645636029930,0.1868497454401630, -0.3081645385608390,-0.9413175847225490,0.1376801433239040, 0.2268128430936140,0.3605118823065700,-0.9047580433041550, 0.0053723381344127,-0.9527141369992010,0.3038205245615180, 0.4997098468405750,0.8591889901243150,0.1099288325223530, 0.2826871445408290,-0.5384236997311110,0.7938437490332800, -0.9169656502561230,0.3927426788378230,0.0701939062145264, -0.4122096064130160,-0.6082501787985420,0.6783177429290540, 0.8689901348066610,0.0966091330875035,-0.4853069348492570, 0.9016117108528340,-0.0311940714365313,0.4314200421401830, 0.0602430803117106,-0.3322121265937110,0.9412788504044060, -0.7401273323624760,0.6637816179074090,-0.1077288058887970, 0.2609246136270950,0.8706803367668190,-0.4169341640729010, -0.3416095796360020,0.1763315450422400,-0.9231522525152230, 0.2417187698627780,-0.7721496224459510,-0.5876708235505650, 0.0613208945746505,0.1158091933659950,0.9913768096039370, 0.6752331524032520,0.5192825815894980,0.5238375610371130, 0.1859160712558880,0.0545608595018432,-0.9810496047902950, -0.8705635614786970,-0.3824245858905910,-0.3096296522168640, -0.7511998187032470,-0.2098339818012360,-0.6258342691652840, -0.3238272471878560,0.9093048344891080,0.2613438959560160, 0.0312467158098265,-0.9020995865833540,-0.4303951424415010, 0.1997844244448850,-0.2949296496204980,-0.9343996390859500, 0.0993378318350216,0.6282103369083360,-0.7716759473819500, 0.7029961904247910,-0.5712633600352550,0.4236207380776580, 0.6081885222938300,-0.0369636906278732,0.7929315272614730, -0.8386908835040670,-0.4071869261928720,0.3616578618871240, -0.6722483496885470,-0.0353283318457414,0.7394822954675930, 0.0999489360975153,-0.5914665380137460,-0.8001109576696270, 0.1502553918254090,0.7709976382621730,0.6188585937203620, -0.2288419561759350,-0.9457701920389410,-0.2305430609326490, 0.2000892573988740,0.4465173368776440,0.8721161373012560, 0.6810481798116100,0.7159072717862530,-0.1537860688711920, -0.9675337290891160,-0.1992515487286990,0.1554905251265250, -0.6915973408017530,0.2529246583829880,0.6765517240963850, 0.4051983586769120,-0.6954173613750950,0.5934719737474010, 0.3828531024787670,0.6364092429998630,0.6696318222327270, 0.5431852686098420,0.7191354072485380,-0.4333520854994610, -0.4766435443833670,-0.7756897459394560,-0.4136621201440160, -0.2634864894153490,0.9592084977706880,-0.1024398735848210, -0.7706589629193440,0.6046810772525520,0.2011108094677480, -0.5074055034405280,0.8063323635715170,-0.3039206714513410, -0.5457935890943380,0.8354998025106060,0.0636351955151979, 0.3190257966006890,0.1393790695388820,0.9374412067312650, -0.4883735140357740,-0.2538251441534990,0.8349036513178050, 0.7814334110960810,-0.3206898482390990,0.5352754853896490, 0.5674042808667500,-0.3889648144620360,0.7257814789346710, 0.7365968935987790,0.6532051494950900,0.1753512161740710, 0.5454313517376770,-0.7269270773618580,-0.4172309489236830, 0.9213613541912860,-0.2944856448350440,0.2537153128782090, 0.8605683113688990,0.5039629381095560,-0.0737803393843139, 0.9884254663736010,0.0653918314621231,0.1368904883553190, -0.6908004715469640,0.4998012720691420,0.5224877002844740, 0.6727955231552780,0.3943331152249490,-0.6259772985174470, -0.4586010376503450,0.1473697927994620,0.8763373964611290, -0.0145240600324916,0.9964844905654110,0.0825088585714514, 0.5144395480682960,0.3368539248460700,0.7885945629403520, 0.4119578382931460,-0.5109788900305280,-0.7544476876576700, -0.1166393819714400,0.8099047244599660,-0.5748474509561170, 0.8653655945438620,-0.2118053715810700,-0.4541815411805620, }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 60; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell3() { double coords[3*90] = { 0.7830306271444820,0.5716685391117410,-0.2450675791359720, -0.3870553382696590,0.9200004222080310,0.0615417602445897, 0.9237963183374240,-0.2584997104179900,-0.2824504592668160, -0.5842983723039450,-0.7712899205183550,-0.2524029925134570, 0.3359998847225240,0.7641041306124860,0.5506804473080420, -0.9783788169663750,-0.2066075720365220,-0.0093915754081947, -0.0445468908163855,-0.8801917861335100,0.4725230091135180, 0.8775085322085220,-0.2202272889989130,0.4260031890507770, -0.5808389302765810,0.4437425367947590,0.6824358564100420, 0.5331596759297140,0.4366496520639920,-0.7246225509290440, 0.6151687893470670,-0.1325993633208080,-0.7771645703840200, 0.4011147552907920,0.8712537751236570,-0.2828848041531790, 0.7304993029031530,-0.0076263316203289,0.6828708571348050, -0.5475051350669790,0.6162612183405430,0.5660920754123990, 0.8023122911715880,0.2709359026259910,-0.5318728458036140, 0.2336323077450560,-0.6929349377840680,0.6820975859625270, 0.1689148924284850,-0.9486250397707260,0.2675411987635920, -0.8314637780782760,0.5554909857699080,0.0098868838455222, -0.1011517806960660,-0.2148730146317450,0.9713896771353290, 0.3877486434569820,-0.3420676320990350,-0.8559443466531150, -0.3648694025982520,0.8893842897839550,-0.2754376592499350, 0.1726950806932710,0.9829215274686680,0.0635742081586933, -0.3959729941386110,-0.7643798871495520,0.5088504456460080, -0.9617290799445310,0.1062410103220330,-0.2525668713723110, -0.0327316740125812,-0.4982367497870410,-0.8664229790800670, 0.1706515883535990,-0.2008209194778680,0.9646496740747160, 0.2501225023053060,0.6910620770273610,-0.6781385843138260, 0.3654852701417630,0.4920313286800810,0.7901428281688790, 0.9153163381057960,-0.3852897569037580,0.1172510316442380, -0.6881847644097410,0.7218125899083820,0.0734051434441835, -0.6873457829778960,0.2458189841721520,-0.6834682155324220, -0.3219850147738870,0.8095000604196670,-0.4909534626027350, -0.5632523312637280,-0.5973559275989310,0.5708876483936170, -0.9084685052242310,-0.3740631899713040,0.1864449112315510, -0.7845198100635990,-0.3605738415896990,-0.5044949676448910, -0.2000039873810240,-0.6330205581036100,-0.7478525108936150, -0.9148801969792540,0.0534543198667285,0.4001710395103400, 0.7664323664233560,-0.3341914416639150,-0.5485412546174510, 0.4872976306941970,-0.8343714235123850,-0.2576147254054740, -0.6413753405832620,-0.2298206308844890,0.7319973702900570, 0.7054813742936480,-0.6006428992790810,-0.3761969405382820, -0.1929691654472430,0.9760562116682670,0.1003851226546300, -0.9439691512808960,-0.1047955385010510,-0.3129538888403490, -0.4276451629474120,0.0186575235064224,-0.9037541211105420, -0.7344812627593790,-0.4734265259198780,0.4862145608790640, 0.6377710213710160,0.3906960864871740,0.6637806055489840, 0.0474387462958210,0.9621772979130640,0.2682618361425920, 0.7913486901745450,-0.0748105169885527,-0.6067706626946760, -0.0982434620561768,0.1155119940049920,0.9884357345848060, -0.2883901942644810,0.3703318191063880,-0.8829979839215070, 0.3295292140825860,0.2147146585289090,-0.9194063913628870, -0.5210574847572160,-0.0609880081835259,0.8513398618861260, 0.1729068266771900,0.9459249066560850,-0.2744618375222680, -0.2062545956858610,0.4593595720405990,0.8639721206920620, 0.6720182971610780,-0.4607369705129640,0.5797524060176540, -0.2554108625029320,0.6389014951165470,0.7256515491979250, 0.4500266667406290,-0.5361016246739610,0.7141925841425810, -0.9983559681192810,-0.0042300087224308,0.0571617699762653, -0.8406688842019840,-0.0901045876756096,-0.5340009273535010, -0.3909575963129450,0.7939675140678560,0.4655832304648770, -0.9713179118089640,0.2320449947116890,0.0519291308258172, -0.0612195458720222,0.7850603547539780,0.6163865723689800, -0.6242925102191230,-0.1774216962215860,-0.7607761848232110, -0.0802679405745239,-0.4570324492769940,0.8858207482463910, -0.3609460670444700,-0.2996972708558150,-0.8831191779860200, 0.1285945459353310,0.8707004556055770,0.4747042862287270, 0.5951730348219600,-0.7933110389121110,0.1281665095140040, 0.2033751954557950,0.0131770863036256,0.9790122033304090, -0.2052893840274300,0.8803381043882250,0.4276226032002800, -0.3731942266334910,-0.9275730588324440,-0.0182835919789316, 0.2685955896736170,0.5262799019911600,-0.8067749834793190, 0.9390948774123210,0.2816262184669510,0.1969453840280110, -0.9130820847807490,-0.2162790214867180,0.3456942164936930, -0.7722185508796150,-0.6352920195758570,0.0090862280741177, 0.1460103850487270,0.2737759341802480,0.9506459410956610, -0.7668676175868570,-0.5322707367226740,-0.3586110984414160, 0.5327000875787680,-0.1961334068751380,0.8232632042069670, -0.8210993253048930,0.4385794796816200,-0.3652997919326650, -0.6255762780977020,-0.7798597751409450,-0.0217497447925055, 0.2991048825976350,-0.1334972919760040,-0.9448358281952090, 0.0606298744362150,-0.9937831824740320,0.0933756100790349, 0.8564420120030050,-0.4610622959465800,-0.2322254063032560, -0.5455890340730230,-0.6468991032576630,-0.5327796506096080, -0.8522969529983180,-0.5007967009121140,-0.1509720777671300, -0.0228411934161264,-0.7512799112567760,0.6595883373933590, 0.5854881600260220,0.7616691389056020,-0.2776035614111240, -0.1213541141003080,0.4896966102871260,-0.8634062826179960, 0.5356517245344040,-0.7170045054372250,0.4460737261776780, -0.3912157018163170,-0.8815613582567070,-0.2641966053547140, -0.6919685152992150,0.6644155090244570,-0.2823678544069240, }; auto pointshell = new vnl_matrix_fixed(coords); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 90; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } }; // Optimal PointDistribution For Multishell Scheme 50 - 100 - 150 // Generated by Emmanuel Caruyer template<> class ThreeLayerPointShell<50,100,150, vnl_matrix_fixed,vnl_matrix_fixed,vnl_matrix_fixed > { public: static vnl_matrix_fixed* DistributePointShell1() { double coords[3*50] = { 0.913310030405731,-0.305436511654557,-0.269394368368340, 0.274520611983463,-0.931497424367645,-0.238644048726690, 0.724295307031846,-0.291296816682609,-0.624933974754310, 0.179546279617146,0.559467539325302,-0.809171926055848, 0.333306860790720,0.932650091766363,0.138095412226576, -0.423224047088266,0.854335111704236,-0.301650332132319, -0.057337449880758,-0.311676405763001,-0.948456764924911, -0.167285210491163,0.516195511070583,0.839974911947675, 0.763325168891534,0.512093536284904,-0.393820894102804, 0.457619183693223,-0.672231259407514,0.581970632069466, -0.246793802164352,-0.050216662420050,-0.967766038889902, 0.927994874653326,0.242210812920042,-0.283124415622830, 0.613792300977422,0.763214386431347,0.201898022786412, 0.720951430921191,-0.550579081664088,-0.420822657525251, -0.566617724460163,-0.603447657535153,-0.561066198360694, 0.575968738378302,-0.783648913166458,-0.232710965156210, -0.532150073708023,-0.619733926203769,0.576841537828723, -0.911227494753416,-0.409216343893830,-0.046972722888692, 0.243129633174557,0.820980315213879,-0.516603623200345, 0.429059425848317,0.332137825141317,-0.839995520345857, -0.862659530206915,0.023610632091113,0.505233681572638, 0.837291268298631,-0.163164674031590,0.521843483411485, 0.803588479073049,0.456397841392963,0.382029536388100, 0.303630079812031,0.840160692576492,0.449375995445604, 0.558311152322438,-0.044011396373360,-0.828463429598227, 0.238930415896487,-0.057120162540996,-0.969355220438215, 0.246985899213630,-0.326331770359038,0.912417416122670, 0.837050631211904,-0.547095262685976,0.005745810152941, -0.554571420346131,0.053288087401785,-0.830428154313384, 0.751372177673996,-0.530499886791399,0.392440722570616, 0.204389904673926,0.615196807070163,0.761418186963024, 0.106700970850229,-0.867025439314806,0.486705034287282, 0.717231391359855,0.239339763160166,-0.654442976139587, 0.069544882069961,0.220289919002015,-0.972952137036535, 0.648780744604385,-0.754349357811800,0.100202753452702, -0.727376323458570,-0.175112683983955,-0.663520332755394, 0.726060483848279,0.676802250037960,-0.121551997669954, 0.580156273414571,-0.371836855429485,0.724676514978945, -0.060890718603115,0.804268669172008,0.591138078773718, -0.386606299473394,0.736073310034347,0.555636258232458, 0.171832268210408,-0.620770257189966,0.764930035617617, 0.030029178348917,0.973024956515624,0.228737147060675, 0.197806050466708,-0.977284832169067,0.076073143822506, 0.941717594278340,-0.292439497987513,0.166274209195007, 0.103620708501302,0.975442422157789,-0.194357479466658, -0.999930437744010,0.003907201094968,0.011128946611211, -0.453284936431437,-0.867544703290094,0.204692340349242, -0.939310554852217,-0.109134901287686,-0.325246452500478, 0.453912788215515,0.373549749748161,0.808964625405526, 0.446853673336740,-0.414239447718517,-0.792923372451158 }; auto pointshell = new vnl_matrix_fixed(); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 50; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell2() { double coords[3*100] = { -0.6783406728914600,-0.6757825732021540,-0.2883952934037510, -0.7868810762760640,0.3417432912343670,0.5138381989448860, 0.1812675472397420,0.9341812435603770,0.3073230881298610, 0.4392152776431420,0.8556891913232610,0.2736529695387940, -0.6296241987663400,0.2128741188301860,0.7471666332620290, 0.6170474427091470,0.0980582575477010,-0.7807925662894150, 0.3935013484893500,0.9192019551005550,0.0149818048438275, -0.3269299998796330,-0.4401580515926610,0.8362880274145110, -0.2869929508839660,0.8309767335503890,0.4765634421992920, 0.5088992607891730,-0.7072946217461350,-0.4906687889169470, 0.5806843974003410,0.3583450335309360,-0.7310228912691540, 0.9491724956627390,-0.1834420594707910,0.2557744793650880, 0.0153560310858227,0.1752309003794950,0.9844076004691760, -0.0479773456177447,0.9905464575577180,-0.1285141615828030, 0.0693843127112566,-0.5942844228507240,-0.8012564145806050, -0.3165721030432440,-0.4164032785066600,-0.8522854059666170, 0.4896581521634270,0.7211168510820520,0.4901279232051600, 0.7776461071669160,-0.0320557656217032,-0.6278845115931320, 0.2300492326525960,0.8027977937856420,0.5500845869944510, -0.3766021271868230,-0.7564724591162180,0.5347151170455350, -0.1790056555604940,-0.2983809560973960,0.9375104161105410, -0.5841988205464850,-0.8089448753434100,-0.0657261494972090, -0.8551587253793000,0.4448943753338720,0.2660311057074570, -0.7752975795230930,0.6190258715864510,0.1253819504245550, -0.6764680634440770,-0.5064314530534920,0.5347131403830920, 0.7501893689724760,0.6048830482657770,-0.2670812771449510, -0.5138286280086930,-0.4945630265573110,-0.7009904092076970, 0.5804145400895790,0.7956021922609330,-0.1735975613947330, -0.1755232810907650,0.3977545839146920,0.9005458726627870, 0.0397560432054666,0.4921914390519250,-0.8695786590945290, 0.5538585716719580,-0.8081611673456340,0.2002903147434720, 0.8992305597521990,0.1140934067118160,-0.4223352873637700, 0.7698402757004470,0.6378303091348330,0.0227694237612964, -0.4412960658520300,0.8971893864056890,0.0175780313033856, -0.0811198993955311,0.2753962213738170,-0.9579021260938310, -0.8572077886102190,-0.3790625619618600,0.3485776545690900, 0.6857756541322800,0.5415157578159610,0.4862843162558790, 0.8549241931498690,-0.2861863354061220,0.4326684705335710, -0.4182775751481730,0.7970464185340600,-0.4356155149098700, 0.3499190445152290,0.8866592889390710,-0.3023110444949170, -0.0132033510852791,-0.8749575867614390,-0.4840195170535130, 0.9116379197252770,-0.0091996476760664,0.4108913114214080, -0.6490542475593910,-0.0530061128719349,-0.7588932307797450, 0.2036777677589340,-0.7982257583158190,0.5668782988277740, 0.3791318665509280,0.1705982429677800,-0.9094807679450480, -0.1470179365181320,0.9220511216311460,-0.3580606868125770, -0.3344979114418210,-0.6343147268032640,-0.6969619606560740, -0.6076879381877640,0.5109764367904180,0.6079625406438060, -0.5844013508588990,-0.7109052658706510,0.3912655927521240, 0.9862110505381910,-0.1637207304543020,0.0241513191330477, -0.2051748270057740,0.7136488133907960,0.6697825479281010, 0.9069081158736650,-0.4209955960690930,0.0167444752950723, -0.1319432828456750,0.9392959075033450,0.3167241201098720, 0.7549548594926640,-0.1430122887152190,0.6399926916808070, 0.2084074643614400,-0.1965342739298880,-0.9580921709153480, -0.4579812060622150,-0.5603838600979070,0.6900892291838540, -0.5044451924143650,-0.2283097986021220,-0.8327122454439420, -0.1632507891313170,0.0595367888203028,-0.9847865507941140, 0.6304967444231040,-0.7732039721298140,-0.0680402289424677, -0.4189905487906070,0.2950674433427220,0.8587095690065030, -0.1064870135377420,-0.0503578524476393,0.9930380670672570, -0.9594694024759230,-0.2706424081220130,-0.0785566842376365, 0.6572248348109010,-0.6577047684772120,0.3680760166461810, 0.8938413333977800,-0.1605095289995250,-0.4186697526810690, 0.9676575561375490,0.2066292219530930,-0.1447177207012450, 0.8845432302365500,0.4553528053106210,-0.1011785379338700, 0.3127824838603270,-0.9329319353982300,0.1783399049687880, 0.0538977574368990,0.9943600559842480,0.0913406306430770, -0.8270410405970430,-0.3169506321051900,-0.4642686872666290, -0.3836295436457290,0.5570493630789800,0.7365625433970090, -0.4341709114900190,0.8573912849439030,0.2763617269416430, -0.6794088494430530,-0.3329865853053170,-0.6538528498868610, -0.3373613680825300,0.6537103259033100,-0.6773847629913670, 0.3577142489860180,-0.2395141136991300,0.9025926575212580, 0.0475306676831556,-0.6921666442661880,0.7201709326228570, -0.9238533114270850,-0.2442810637733990,-0.2946554273162900, -0.7702042751707540,0.6239784781621260,-0.1320463301238260, -0.1058068553327490,-0.9342834914303710,0.3404691865723970, -0.6712641876558270,0.6601081560713440,0.3371373795040410, -0.9902933383503500,-0.0392378960032633,-0.1333397597723740, 0.2680611017833540,0.2096519795061000,0.9403134015847410, -0.2193780563994170,0.9713963546921800,0.0908976922763060, 0.4259546539557210,-0.0086224931160314,0.9047034239937000, -0.7126278602910880,0.4094408405402170,-0.5696663328955520, -0.2175209712545230,0.4840278946263750,-0.8475857622022950, 0.5687366838665980,-0.5871979617261380,0.5759662647838790, 0.0944273658455115,0.8302981465345910,-0.5492617412861550, 0.2132739207069970,0.6628352636620030,-0.7177490146230690, 0.9572113632369580,-0.2441349912405500,-0.1553850447823740, -0.8451592214984760,-0.4858715283504990,-0.2227885729889170, -0.8457073503037060,0.4544256488253640,-0.2797792117580030, -0.4568229734634650,0.4521433068383050,-0.7660804141846460, 0.2161513748810380,0.9712225012944180,-0.1000271768893380, -0.9727568289959310,0.0233660996838811,0.2306473000651490, 0.0955550598977780,0.4497094877292240,0.8880487639618950, -0.5803054956345510,0.2185728470394220,-0.7845198801008280, 0.0802960854964209,0.7085380275275620,0.7010894395163160, -0.8170365471147090,-0.1054124160886850,-0.5668681532889450, -0.7903491223400260,-0.2661789011279690,0.5518125201648250, -0.4240807031724980,0.0532021972358502,0.9040603317290260 }; auto pointshell = new vnl_matrix_fixed(); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 100; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } static vnl_matrix_fixed* DistributePointShell3() { double coords[3*150] = { -0.5818137157755120,-0.5030550053652260,0.6390840803153230, 0.9670851419246870,0.1662861674810110,0.1926012429165080, 0.0578846316476645,-0.7976809936025330,0.6002952622370840, 0.4660628327321890,-0.1511747542980830,0.8717405747173640, 0.6902368719591830,0.3748091607024440,-0.6189435787222700, -0.1997955747625600,-0.1526408912355860,0.9678752433181160, 0.9511193914166630,0.1151598714233260,-0.2865486124289540, 0.0822444520102396,-0.5510970245321980,0.8303781787025070, 0.7452339673590670,0.4163359900238880,0.5208557163985940, 0.7753251365196900,0.6018341048080190,0.1914853596770620, 0.1116030335825260,-0.9761251513556840,-0.1863449805764000, -0.3170679847245510,0.9480932422722550,-0.0242300850265728, -0.1215812147248450,0.4013080511364970,-0.9078380121580370, 0.8680201958339400,-0.2937180126790240,-0.4003381928473620, 0.7117815464094280,-0.4555489837210150,-0.5346420799205320, -0.4221584476961910,-0.8100472693071080,-0.4069467612929700, 0.0186640877956507,-0.9603335901627470,0.2782284087434000, -0.5396677502633030,0.3145543091567380,0.7809060800868890, -0.4898321413919820,-0.0679885797782780,0.8691616801718060, -0.6750918694436950,0.2756900693390990,-0.6842849943399450, -0.4765551297163720,-0.8680896906246820,-0.1389802049651460, -0.8007926338965610,0.4138072508780090,-0.4330066011249610, 0.6252172011926140,0.2326499546399870,-0.7449680865237690, 0.6824097373557610,-0.7075614150231750,-0.1835042079420600, -0.7485107920414520,-0.0179476036655747,-0.6628796857048340, 0.6619059872141820,-0.0443716919725870,-0.7482724216764290, -0.0389254961022405,0.4513344326841560,0.8915054882763560, 0.6705154952625320,-0.7056948744571640,0.2289185767423150, -0.0421968777730212,-0.3582833631874890,0.9326588096240070, -0.0859685420644288,0.2858229400217130,0.9544184914033570, 0.9471108604678190,0.3162982815144110,-0.0541886989596281, -0.5550008908723820,-0.7690560878042730,-0.3170595290194690, -0.9104090909480310,-0.3561208782301570,0.2105545231234920, -0.8278006128591360,-0.4943059810149400,0.2653445731174760, 0.9608962244735380,-0.0205957379787835,0.2761417414474080, 0.2202282819057040,-0.6075951351306100,-0.7631039612100540, 0.6244544517901810,0.4795352369175020,0.6165246095606390, -0.1883106968238980,-0.4391326823901580,0.8784654624505860, -0.2302051549830810,-0.9409736043275990,0.2481416179884810, -0.0791847103092564,-0.9956294659870780,0.0495151301272986, 0.3432876609136320,-0.1606450217698800,-0.9253900576756810, 0.6910132782044240,0.7150303514681560,0.1059822901455680, -0.3743211784491260,-0.1177211051182680,-0.9197963887590500, -0.0638117734968980,-0.6201179359188230,0.7819090759896310, -0.6081942748353220,0.4670488067500740,-0.6418451029421870, -0.9237110100984830,0.0253860825460826,0.3822479779355320, -0.9551685142832210,0.2944878574922000,-0.0304960835458829, 0.2047274908347290,-0.2027142615186940,0.9575978188537430, 0.4664286040672840,0.4413500991724570,-0.7665862295060520, 0.2436221916378790,0.7468678862924580,-0.6187378994102360, 0.7739427622698460,-0.5762620863418250,0.2625540107769950, -0.8824775950813450,-0.1623522085699230,-0.4414465477857010, -0.5674249283656180,-0.3593537766554100,-0.7408736828048120, 0.6114933730446530,0.6602715260117740,0.4360245023626030, 0.6580967800369840,-0.1033246068935280,0.7458100654424360, -0.0651725243592964,-0.5903861242505850,-0.8044854046911040, 0.6288524174560490,0.2026776272049530,0.7506440011684980, -0.5594322362469380,0.7479956050065880,0.3571248352307300, 0.5065956414163710,-0.4948790783785480,-0.7060138482219270, -0.7796248885518470,0.2823581769849720,-0.5589802259832120, 0.1069521912958730,-0.0945188164394620,-0.9897612955232660, -0.1677987609066020,-0.9702091453514290,-0.1747506512567480, 0.6497726089471500,0.7581321700271420,-0.0550560571776502, -0.7436099555683260,-0.5675313008756160,-0.3534861475448520, 0.8705569853960000,-0.4718161643221160,0.1397141448193180, -0.7367938401263080,0.6760868762135980,-0.0064321818744716, -0.8709771462534060,-0.3550310615284370,-0.3396347391746950, -0.5568793244548770,0.8296552833726470,-0.0394655389742952, 0.8255863288934740,-0.1743226461918030,-0.5366738567965490, 0.5123909885019620,-0.8475608044158260,-0.1381888480304350, 0.3892383734256480,0.6628221159035160,-0.6396564166192190, -0.3850734793456500,-0.5125887952201850,-0.7674445533843810, -0.0303214011502939,0.1349348899639910,-0.9903904220572250, -0.1951010042782680,-0.3408158110301630,-0.9196630801991900, -0.7299481699543050,0.1633700135838910,0.6636911238233930, -0.3764204298254850,-0.8272654006443230,0.4170606873187450, -0.0207934145254701,-0.9354136997804840,-0.3529431174783800, 0.0530508631426718,0.0286917551417508,0.9981795375115150, -0.9893103145653900,-0.1443535615447680,-0.0206676259852998, 0.3520785503091250,-0.7515903016454040,0.5578106424986840, 0.4646561319602880,-0.5645009731359150,0.6822267440963500, -0.3638080873049480,-0.7397671945737120,-0.5660284210567990, 0.8993857269815550,-0.3263912150460360,0.2908162458368350, -0.1700781261443220,0.9625883335514640,-0.2109434310848530, -0.3098321331013300,-0.2959672665364290,0.9035526694315240, 0.3772438334691890,0.2858348804818840,0.8809003980072750, 0.4170539029542560,-0.8070208930488820,-0.4180709511712160, 0.3535382771055950,0.9267047409094850,-0.1273931308865980, 0.0162686253194627,0.8954558957620720,-0.4448528639619620, -0.4409570938666000,0.8843580437434560,-0.1531916833087520, 0.1659388260557630,0.8826574008153690,0.4397501777067190, -0.6722877318211110,0.5534153014066750,-0.4916916816581130, -0.5022690191279730,-0.7127108731518800,0.4896621730492420, -0.2994694405516180,0.9013048000637180,-0.3129979417788570, -0.3172838107693270,-0.5624673796012650,0.7635191093274480, 0.7315837873258040,0.0963804929381182,-0.6749044100485910, -0.2068469360516820,0.7944607239234560,0.5710048188842610, 0.5378470897916880,-0.7741067897070790,0.3338849893780950, 0.1257284809109600,-0.8835082232405290,-0.4512267374105670, 0.3130995324769140,-0.4640943924714670,-0.8286042949679160, 0.9894576229982930,0.0690358063959267,-0.1273093465768750, -0.0845800025595276,-0.7421395656655050,0.6648872748375060, 0.5451534912742490,-0.6927392692238080,0.4721440202170950, -0.5063313485711320,0.1671271448910190,0.8459888196038490, 0.9140701463180970,0.3632958280951350,0.1802551216988890, 0.8112330574743470,0.5738372376167550,-0.1123020533436160, 0.8691548124260960,0.2553703834185790,-0.4235042848770290, 0.2587208365075750,-0.8528680460626400,0.4535191558932370, -0.7810165842314370,-0.4032429479626940,0.4768733794979690, 0.6386218421663880,-0.3771596505315350,-0.6707553508686590, 0.6209847697030420,-0.6181456007191380,-0.4819480595545900, 0.1495129959273720,0.1671684875068860,0.9745258133233210, 0.8500517134843810,0.5236094820204760,0.0569666107515227, 0.2831566624639810,-0.3165192184973970,-0.9053385492864280, -0.9842250831135330,0.1106121936986040,-0.1380794277774220, 0.5302801835192860,0.0861689235342689,0.8434321807849740, 0.8470084161413430,-0.0276085553364133,0.5308620448458990, -0.2172067203511410,-0.5051072452535100,-0.8352771464769700, 0.3204407112933310,0.0455278779726987,-0.9461738544649850, -0.4935635772769070,0.6256485875038120,0.6041182335769260, 0.4980559918394660,0.2263222843527210,-0.8370892739715230, 0.2021779827202400,-0.7110486153559470,0.6734492778996520, 0.6602958653226490,0.7044162570524200,-0.2603979781758510, 0.4825507527225460,-0.3146110298685950,0.8174134027112130, -0.8074813189441350,0.4434122850213180,0.3890494377947510, 0.7706009639641810,-0.5764170340352260,-0.2718778350868380, 0.9877213210351460,-0.1170482811531720,-0.1034712126713200, 0.9471546743930560,-0.1633313871864270,-0.2760812937073770, 0.9072494495203930,-0.1579003412593720,0.3898280628368310, -0.3139240581363140,0.0802169066474812,-0.9460533460704720, 0.2464642940724080,0.9686222614596370,0.0320353921812441, -0.3117655950297000,-0.9062547169400450,-0.2854901080242450, 0.0608632767228376,-0.7075111154985250,-0.7040764752444090, -0.4607335862628370,-0.6188008149745030,-0.6362468969482480, 0.2190580158109430,0.7137403609578260,0.6652730889257150, -0.0823847846664380,-0.8044875371365980,-0.5882283143876660, -0.8295547427688960,-0.1162092353369240,0.5461999106298320, -0.0852410700430401,0.9961766497246490,0.0191322377491922, 0.3131894593955590,-0.5541595443573950,0.7712454615238390, -0.2053809775103630,-0.8866791538596230,0.4142689128907330, -0.2803794157477670,0.8920364641234400,0.3544831870471280, 0.3607197436800900,-0.9212300461377630,-0.1456587402540090, 0.5099563266151200,0.8025819147395540,-0.3095267598741700, 0.3434662076553160,-0.3991341139236100,0.8501311212402990, -0.8525024407450160,0.5081350695533460,0.1226309080689130, -0.7808884875619300,-0.2500741633167730,-0.5724299807266000, -0.5053813675827790,-0.8611316666645480,0.0551536577907309, -0.9210719657807660,0.3672285791438610,0.1294975077471940, 0.6652607437466560,0.6063117536090580,-0.4356767153121150, 0.3394557649489950,-0.6705912128459400,-0.6596038272301810 }; auto pointshell = new vnl_matrix_fixed(); // List sorted for each row [x,y,z] // pointshell need [x...x // y...y // z...z] int i=0; for(int c =0; c < 150; c++) { for(int r=0; r<3; r++){ pointshell->put(r,c,coords[i++]); } } return pointshell; } }; } #endif //__itkPointShell_txx__ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkCartesianToPolarVectorImageFilter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkCartesianToPolarVectorImageFilter.h index 3af042ccd8..16f7205ee4 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkCartesianToPolarVectorImageFilter.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkCartesianToPolarVectorImageFilter.h @@ -1,135 +1,130 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkCartesianToPolarVectorImageFilter_h #define __itkCartesianToPolarVectorImageFilter_h #include "itkUnaryFunctorImageFilter.h" -#define _USE_MATH_DEFINES -#include - -#define C2P_PI M_PI - namespace itk { namespace Functor { template< typename TInput, typename TOutput, bool symmetric > class CartesianToPolarFunction { public: CartesianToPolarFunction() {} ~CartesianToPolarFunction() {} bool operator!=( const CartesianToPolarFunction & ) const { return false; } bool operator==( const CartesianToPolarFunction & other ) const { return !(*this != other); } inline TOutput operator()( const TInput & x ) { TOutput opoint; if(x[0] || x[1] || x[2]) { opoint[0] = sqrt( x[0] * x[0] + x[1] * x[1] + x[2] * x[2] ); opoint[1] = atan2( x[1], x[0] ); - opoint[2] = 0.5*C2P_PI - atan( x[2] / sqrt( x[0] * x[0] + x[1] * x[1] ) ); + opoint[2] = 0.5*itk::Math::pi - atan( x[2] / sqrt( x[0] * x[0] + x[1] * x[1] ) ); - if(symmetric && opoint[1]>C2P_PI) + if(symmetric && opoint[1]>itk::Math::pi) { - opoint[1] = opoint[1] - C2P_PI; + opoint[1] = opoint[1] - itk::Math::pi; } } else { opoint[0] = 0; opoint[1] = 0; opoint[2] = 0; } return opoint; } }; } // end namespace functor /** \class CartesianToPolarVectorImageFilter * */ template class CartesianToPolarVectorImageFilter : public UnaryFunctorImageFilter > { public: /** Standard class typedefs. */ typedef CartesianToPolarVectorImageFilter Self; typedef UnaryFunctorImageFilter< TInputImage,TOutputImage, Functor::CartesianToPolarFunction< typename TInputImage::PixelType, typename TOutputImage::PixelType, symmetric > > Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename TInputImage::PixelType InputPixelType; typedef typename InputPixelType::ValueType InputValueType; /** Run-time type information (and related methods). */ itkTypeMacro( CartesianToPolarVectorImageFilter, UnaryFunctorImageFilter ); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Print internal ivars */ void PrintSelf(std::ostream& os, Indent indent) const { this->Superclass::PrintSelf( os, indent ); } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(InputHasNumericTraitsCheck, (Concept::HasNumericTraits)); /** End concept checking */ #endif protected: CartesianToPolarVectorImageFilter() {}; virtual ~CartesianToPolarVectorImageFilter() {}; private: CartesianToPolarVectorImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; } // end namespace itk #endif // __itkCartesianToPolarVectorImageFilter_h diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkDiffusionTensorPrincipalDirectionImageFilter.txx b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkDiffusionTensorPrincipalDirectionImageFilter.txx index 37e4344f4f..0e9c2b634a 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkDiffusionTensorPrincipalDirectionImageFilter.txx +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkDiffusionTensorPrincipalDirectionImageFilter.txx @@ -1,218 +1,215 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkDiffusionTensorPrincipalDirectionImageFilter_txx #define __itkDiffusionTensorPrincipalDirectionImageFilter_txx #include #include #include #include "itkDiffusionTensorPrincipalDirectionImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkArray.h" #include "vnl/vnl_vector.h" #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - namespace itk { template< class TTensorPixelType> DiffusionTensorPrincipalDirectionImageFilter< TTensorPixelType>::DiffusionTensorPrincipalDirectionImageFilter() : m_NormalizeVectors(true) , m_UsePolarCoordinates(false) , m_FaThreshold(0.2) { this->SetNumberOfRequiredInputs( 1 ); } template< class TTensorPixelType> void DiffusionTensorPrincipalDirectionImageFilter< TTensorPixelType>::BeforeThreadedGenerateData() { typename InputImageType::Pointer inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); Vector spacing3 = inputImagePointer->GetSpacing(); mitk::Point3D origin3 = inputImagePointer->GetOrigin(); itk::Matrix direction3 = inputImagePointer->GetDirection(); ImageRegion<3> imageRegion3 = inputImagePointer->GetLargestPossibleRegion(); if (m_MaskImage.IsNull()) { m_MaskImage = ItkUcharImgType::New(); m_MaskImage->SetSpacing( spacing3 ); m_MaskImage->SetOrigin( origin3 ); m_MaskImage->SetDirection( direction3 ); m_MaskImage->SetRegions( imageRegion3 ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } typename OutputImageType::Pointer outputImage = OutputImageType::New(); outputImage->SetSpacing( spacing3 ); outputImage->SetOrigin( origin3 ); outputImage->SetDirection( direction3 ); outputImage->SetRegions( imageRegion3 ); outputImage->Allocate(); outputImage->FillBuffer(0); this->SetNthOutput(0, outputImage); itk::Vector spacing4; itk::Point origin4; itk::Matrix direction4; itk::ImageRegion<4> imageRegion4; spacing4[0] = spacing3[0]; spacing4[1] = spacing3[1]; spacing4[2] = spacing3[2]; spacing4[3] = 1; origin4[0] = origin3[0]; origin4[1] = origin3[1]; origin4[2] = origin3[2]; origin3[3] = 0; for (int r=0; r<3; r++) for (int c=0; c<3; c++) direction4[r][c] = direction3[r][c]; direction4[3][3] = 1; imageRegion4.SetSize(0, imageRegion3.GetSize()[0]); imageRegion4.SetSize(1, imageRegion3.GetSize()[1]); imageRegion4.SetSize(2, imageRegion3.GetSize()[2]); imageRegion4.SetSize(3, 3); m_PeakImage = PeakImageType::New(); m_PeakImage->SetSpacing( spacing4 ); m_PeakImage->SetOrigin( origin4 ); m_PeakImage->SetDirection( direction4 ); m_PeakImage->SetRegions( imageRegion4 ); m_PeakImage->Allocate(); m_PeakImage->FillBuffer(0.0); } template< class TTensorPixelType> void DiffusionTensorPrincipalDirectionImageFilter< TTensorPixelType> ::AfterThreadedGenerateData() { } template< class TTensorPixelType> void DiffusionTensorPrincipalDirectionImageFilter< TTensorPixelType> ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { typedef itk::DiffusionTensor3D TensorType; typedef ImageRegionConstIterator< InputImageType > InputIteratorType; typename InputImageType::Pointer inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetPrimaryOutput()); ImageRegionIterator< OutputImageType > numDirectionsIterator(outputImage, outputRegionForThread); InputIteratorType tensorIterator(inputImagePointer, outputRegionForThread ); while( !tensorIterator.IsAtEnd() ) { typename InputImageType::IndexType index = tensorIterator.GetIndex(); if (m_MaskImage->GetPixel(index)==0) { ++tensorIterator; ++numDirectionsIterator; continue; } typename InputImageType::PixelType b = tensorIterator.Get(); TensorType tensor = b.GetDataPointer(); typename PeakImageType::IndexType peakIndex; peakIndex[0] = tensorIterator.GetIndex()[0]; peakIndex[1] = tensorIterator.GetIndex()[1]; peakIndex[2] = tensorIterator.GetIndex()[2]; typename TensorType::EigenValuesArrayType eigenvalues; typename TensorType::EigenVectorsMatrixType eigenvectors; if(tensor.GetTrace()!=0) { tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); vnl_vector_fixed vec; vec[0] = eigenvectors(2,0); vec[1] = eigenvectors(2,1); vec[2] = eigenvectors(2,2); vec.normalize(); vnl_vector_fixed out; out.fill(0); float fa = tensor.GetFractionalAnisotropy(); if (faM_PI) + if(out[1]>itk::Math::pi) { - out[1] = out[1] - M_PI; + out[1] = out[1] - itk::Math::pi; } } else { out[0] = 0; out[1] = 0; out[2] = 0; } } else { out = vec; } } peakIndex[3] = 0; m_PeakImage->SetPixel(peakIndex, out[0]); peakIndex[3] = 1; m_PeakImage->SetPixel(peakIndex, out[1]); peakIndex[3] = 2; m_PeakImage->SetPixel(peakIndex, out[2]); numDirectionsIterator.Set( 1 ); } ++numDirectionsIterator; ++tensorIterator; } std::cout << "One Thread finished extraction" << std::endl; } template< class TTensorPixelType> void DiffusionTensorPrincipalDirectionImageFilter< TTensorPixelType> ::PrintSelf(std::ostream& , Indent ) const { } } #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkElectrostaticRepulsionDiffusionGradientReductionFilter.txx b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkElectrostaticRepulsionDiffusionGradientReductionFilter.txx index 04afd2ec9e..36b4b026fb 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkElectrostaticRepulsionDiffusionGradientReductionFilter.txx +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkElectrostaticRepulsionDiffusionGradientReductionFilter.txx @@ -1,261 +1,258 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ /*========================================================================= Program: Tensor ToolKit - TTK Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkElectrostaticRepulsionDiffusionGradientReductionFilter.txx $ Language: C++ Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ Version: $Revision: 68 $ Copyright (c) INRIA 2010. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _itk_ElectrostaticRepulsionDiffusionGradientReductionFilter_txx_ #define _itk_ElectrostaticRepulsionDiffusionGradientReductionFilter_txx_ #endif -#define _USE_MATH_DEFINES - #include "itkElectrostaticRepulsionDiffusionGradientReductionFilter.h" -#include #include #include #include namespace itk { template ElectrostaticRepulsionDiffusionGradientReductionFilter ::ElectrostaticRepulsionDiffusionGradientReductionFilter() { this->SetNumberOfRequiredInputs( 1 ); } template double ElectrostaticRepulsionDiffusionGradientReductionFilter ::Costs() { - double costs = 2*M_PI; + double costs = 2*itk::Math::pi; for (IndicesVector::iterator it = m_UsedGradientIndices.begin(); it!=m_UsedGradientIndices.end(); ++it) { for (IndicesVector::iterator it2 = m_UsedGradientIndices.begin(); it2!=m_UsedGradientIndices.end(); ++it2) if (it != it2) { vnl_vector_fixed v1 = m_OriginalGradientDirections->at(*it); vnl_vector_fixed v2 = m_OriginalGradientDirections->at(*it2); v1.normalize(); v2.normalize(); double temp = dot_product(v1,v2); if (temp>1) temp = 1; else if (temp<-1) temp = -1; double angle = acos(temp); if (angle1) temp = 1; else if (temp<-1) temp = -1; angle = acos(temp); if (angle void ElectrostaticRepulsionDiffusionGradientReductionFilter ::GenerateData() { unsigned int randSeed = time(nullptr); if(m_InputBValueMap.empty() || m_NumGradientDirections.size()!=m_InputBValueMap.size()) mitkThrow() << "Vector of the number of desired gradient directions contains more elements than the specified target b-value map."; BValueMap manipulatedMap = m_InputBValueMap; int shellCounter = 0; for(BValueMap::iterator it = m_InputBValueMap.begin(); it != m_InputBValueMap.end(); it++ ) { srand(randSeed); // initialize index vectors m_UsedGradientIndices.clear(); m_UnusedGradientIndices.clear(); if ( it->second.size() <= m_NumGradientDirections[shellCounter] ) { itkWarningMacro( << "current directions: " << it->second.size() << " wanted directions: " << m_NumGradientDirections[shellCounter]); m_NumGradientDirections[shellCounter] = it->second.size(); shellCounter++; continue; } MITK_INFO << "Shell number: " << shellCounter; unsigned int c=0; for (unsigned int i=0; isecond.size(); i++) { if (csecond.at(i)); else m_UnusedGradientIndices.push_back(it->second.at(i)); c++; } double minAngle = Costs(); double newMinAngle = 0; - MITK_INFO << "minimum angle: " << 180*minAngle/M_PI; + MITK_INFO << "minimum angle: " << 180*minAngle/itk::Math::pi; int stagnationCount = 0; int rejectionCount = 0; int maxRejections = m_NumGradientDirections[shellCounter] * 1000; if (maxRejections<10000) maxRejections = 10000; int iUsed = 0; if (m_UsedGradientIndices.size()>0) while ( stagnationCount<1000 && rejectionCount minAngle) // accept or reject proposal { - MITK_INFO << "minimum angle: " << 180*newMinAngle/M_PI; + MITK_INFO << "minimum angle: " << 180*newMinAngle/itk::Math::pi; if ( (newMinAngle-minAngle)<0.01 ) stagnationCount++; else stagnationCount = 0; minAngle = newMinAngle; rejectionCount = 0; } else { rejectionCount++; m_UsedGradientIndices.at(iUsed) = vUsed; m_UnusedGradientIndices.at(iUnUsed) = vUnUsed; } iUsed++; iUsed = iUsed % m_UsedGradientIndices.size(); } manipulatedMap[it->first] = m_UsedGradientIndices; shellCounter++; } int vecLength = 0 ; for(BValueMap::iterator it = manipulatedMap.begin(); it != manipulatedMap.end(); it++) vecLength += it->second.size(); // initialize output image typename OutputImageType::Pointer outImage = OutputImageType::New(); outImage->SetSpacing( this->GetInput()->GetSpacing() ); // Set the image spacing outImage->SetOrigin( this->GetInput()->GetOrigin() ); // Set the image origin outImage->SetDirection( this->GetInput()->GetDirection() ); // Set the image direction outImage->SetLargestPossibleRegion( this->GetInput()->GetLargestPossibleRegion()); outImage->SetBufferedRegion( this->GetInput()->GetLargestPossibleRegion() ); outImage->SetRequestedRegion( this->GetInput()->GetLargestPossibleRegion() ); outImage->SetVectorLength( vecLength ); // Set the vector length outImage->Allocate(); itk::ImageRegionIterator< OutputImageType > newIt(outImage, outImage->GetLargestPossibleRegion()); newIt.GoToBegin(); typename InputImageType::Pointer inImage = const_cast(this->GetInput(0)); itk::ImageRegionIterator< InputImageType > oldIt(inImage, inImage->GetLargestPossibleRegion()); oldIt.GoToBegin(); // initial new value of voxel OutputPixelType newVec; newVec.SetSize( vecLength ); newVec.AllocateElements( vecLength ); // generate new pixel values while(!newIt.IsAtEnd()) { // init new vector with zeros newVec.Fill(0.0); InputPixelType oldVec = oldIt.Get(); int index = 0; for(BValueMap::iterator it=manipulatedMap.begin(); it!=manipulatedMap.end(); it++) for(unsigned int j=0; jsecond.size(); j++) { newVec[index] = oldVec[it->second.at(j)]; index++; } newIt.Set(newVec); ++newIt; ++oldIt; } // set new gradient directions m_GradientDirections = GradientDirectionContainerType::New(); int index = 0; for(BValueMap::iterator it = manipulatedMap.begin(); it != manipulatedMap.end(); it++) for(unsigned int j = 0; j < it->second.size(); j++) { m_GradientDirections->InsertElement(index, m_OriginalGradientDirections->at(it->second.at(j))); index++; } this->SetNumberOfRequiredOutputs (1); this->SetNthOutput (0, outImage); MITK_INFO << "...done"; } template void ElectrostaticRepulsionDiffusionGradientReductionFilter ::UpdateOutputInformation() { Superclass::UpdateOutputInformation(); int vecLength = 0 ; for(unsigned int index = 0; index < m_NumGradientDirections.size(); index++) { vecLength += m_NumGradientDirections[index]; } this->GetOutput()->SetVectorLength( vecLength ); } } // end of namespace diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkOdfMaximaExtractionFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkOdfMaximaExtractionFilter.cpp index c5944c3f54..0a8bc8f26a 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkOdfMaximaExtractionFilter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkOdfMaximaExtractionFilter.cpp @@ -1,636 +1,636 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkOdfMaximaExtractionFilter_cpp #define __itkOdfMaximaExtractionFilter_cpp #include "itkOdfMaximaExtractionFilter.h" #include #include #include #include #include #include #include #include #include #include #include using namespace boost::math; namespace itk { template< class TOdfPixelType > OdfMaximaExtractionFilter< TOdfPixelType >::OdfMaximaExtractionFilter(): m_NormalizationMethod(MAX_VEC_NORM), m_PeakThreshold(0.2), m_MaxNumPeaks(10), m_ShCoeffImage(nullptr), m_OutputFiberBundle(nullptr), m_NumDirectionsImage(nullptr), m_DirectionImageContainer(nullptr) { } // solve ax? + bx? + cx + d = 0 using cardanos method template< class TOdfPixelType > bool OdfMaximaExtractionFilter::ReconstructQballImage() { if (m_ShCoeffImage.IsNotNull()) { cout << "Using preset coefficient image\n"; return true; } cout << "Starting qball reconstruction\n"; try { QballReconstructionFilterType::Pointer filter = QballReconstructionFilterType::New(); filter->SetBValue(m_Bvalue); filter->SetGradientImage( m_DiffusionGradients, m_DiffusionImage ); filter->SetLambda(0.006); filter->SetNormalizationMethod(QballReconstructionFilterType::QBAR_SOLID_ANGLE); filter->Update(); m_ShCoeffImage = filter->GetCoefficientImage(); if (m_ShCoeffImage.IsNull()) return false; return true; } catch (...) { return false; } } // solve ax³ + bx² + cx + d = 0 using cardanos method template< class TOdfPixelType > std::vector OdfMaximaExtractionFilter< TOdfPixelType > ::SolveCubic(const double& a, const double& b, const double& c, const double& d) { double A, B, p, q, r, D, offset, ee, tmp, root; std::vector roots; double inv3 = 1.0/3.0; if (a!=0) // solve ax³ + bx² + cx + d = 0 { p = b/a; q = c/a; r = d/a; // x³ + px² + qx + r = 0 A = q-p*p*inv3; B = (2.0*p*p*p-9.0*p*q+27.0*r)/27.0; A = A*inv3; B = B*0.5; D = B*B+A*A*A; offset = p*inv3; if (D>0.0) // one real root { ee = sqrt(D); tmp = -B+ee; root = cbrt(tmp); tmp = -B-ee; root += cbrt(tmp); root -= offset; roots.push_back(root); } else if (D<0.0) // three real roots { ee = sqrt(-D); double tmp2 = -B; double angle = 2.0*inv3*atan(ee/(sqrt(tmp2*tmp2+ee*ee)+tmp2)); double sqrt3 = sqrt(3.0); tmp = cos(angle); tmp2 = sin(angle); ee = sqrt(-A); root = 2*ee*tmp-offset; roots.push_back(root); root = -ee*(tmp+sqrt3*tmp2)-offset; roots.push_back(root); root = -ee*(tmp-sqrt3*tmp2)-offset; roots.push_back(root); } else // one or two real roots { tmp=-B; tmp=cbrt(tmp); root=2*tmp-offset; roots.push_back(root); // TODO: check if this is correct (see history) if (A!=0 || B!=0) { root=-tmp-offset; roots.push_back(root); } } } else if (b!=0) // solve bx² + cx + d = 0 { D = c*c-4*b*d; if (D>0) { tmp = sqrt(D); root = (-c+tmp)/(2.0*b); roots.push_back(root); root = (-c-tmp)/(2.0*b); roots.push_back(root); } // TODO: check if this is correct (see history) else if (D==0) { root = -c/(2.0*b); roots.push_back(root); } } // TODO: check if this is correct (see history) else if (c!=0) // solve cx + d = 0 { root = -d/c; roots.push_back(root); } return roots; } template< class TOdfPixelType > double OdfMaximaExtractionFilter< TOdfPixelType > ::ODF_dtheta(const double& sn, const double& cs, const double& A, const double& B, const double& C, const double& D, const double& E, const double& F, const double& G, const double& H) { double dtheta=(G-7*E)*sn*sn + (7*F-35*D-H)*sn*cs + (H+C-F-3*A-5*D)*sn + (0.5*E+B+0.5*G)*cs -0.5*G+3.5*E; return dtheta; } template< class TOdfPixelType > double OdfMaximaExtractionFilter< TOdfPixelType > ::ODF_dtheta2(const double& sn, const double& cs, const double& A, const double& B, const double& C, const double& D, const double& E, const double& F, const double& G, const double& H) { double dtheta2=4*(G-7*E)*sn*cs + 2*(7*F-35*D-H)*(2*cs*cs-1) + 2*(H+C-F-3*A-5*D)*cs -(E+2*B+G)*sn; return dtheta2; } template< class TOdfPixelType > double OdfMaximaExtractionFilter< TOdfPixelType > ::ODF_dphi2(const double& sn, const double& cs, const double& A, const double& B, const double& C, const double& D, const double& E, const double& F, const double& G, const double& H) { double dphi2=35*D*((1+cs)*(1+cs)/4)+(3*A-30*D)*(1+cs)/2.0+3*D-A + 0.5*(7*E*(1+cs)/2.0-3*E+B)*sn + (7*F*(1+cs)/2+C-F)*(1-cs)/2.0 + G*sn*(1-cs)/4.0 + H*((1-cs)*(1-cs)/4); return dphi2; } template< class TOdfPixelType > void OdfMaximaExtractionFilter< TOdfPixelType > ::FindCandidatePeaks(const CoefficientPixelType& SHcoeff) { const double thr = 0.03; // threshold on the derivative of the ODF with respect to theta const double phi_step = 0.005; // step size for 1D exhaustive search on phi bool highRes; // when close to maxima increase resolution double mag, Y, Yp, sn, cs; double phi, dPhi; double A, B, C, D, E, F, G, H, Bp, Cp, Ep, Fp, Gp, Hp, Bs, Cs, Es, Fs, Gs, Hs; CoefficientPixelType a, ap; a = SHcoeff; ap = SHcoeff; m_CandidatePeaks.clear(); // clear peaks of last voxel for (int adaptiveStepwidth=0; adaptiveStepwidth<=1; adaptiveStepwidth++) { phi=0; - while (phi<(2*M_PI)) // phi exhaustive search 0..pi + while (phi<(2*itk::Math::pi)) // phi exhaustive search 0..pi { // calculate 4th order SH representtaion of ODF and according derivative for (int l=0; l<=4; l=l+2) { for (int m=-l; m<=l; m++) { int j=l*(l+1)/2+m; if (m<0) { - mag = sqrt(((2*l+1)/(2*M_PI))*factorial(l+m)/factorial(l-m)); + mag = sqrt(((2*l+1)/(2*itk::Math::pi))*factorial(l+m)/factorial(l-m)); Y = mag*cos(m*phi); Yp = -m*mag*sin(m*phi); } else if (m==0) { - Y = sqrt((2*l+1)/(4*M_PI)); + Y = sqrt((2*l+1)/(4*itk::Math::pi)); Yp = 0; } else { - mag = pow(-1.0,m)*sqrt(((2*l+1)/(2*M_PI))*factorial(l-m)/factorial(l+m)); + mag = pow(-1.0,m)*sqrt(((2*l+1)/(2*itk::Math::pi))*factorial(l-m)/factorial(l+m)); Y = mag*sin(m*phi); Yp = m*mag*cos(m*phi); } a[j] = SHcoeff[j]*Y; ap[j] = SHcoeff[j]*Yp; } } // ODF A=0.5*a[3]; B=-3*(a[2]+a[4]); C=3*(a[1]+a[5]); D=0.125*a[10]; E=-2.5*(a[9]+a[11]); F=7.5*(a[8]+a[12]); G=-105*(a[7]+a[13]); H=105*(a[6]+a[14]); // phi derivative Bp=-3*(ap[2]+ap[4]); Cp=3*(ap[1]+ap[5]); Ep=-2.5*(ap[9]+ap[11]); Fp=7.5*(ap[8]+ap[12]); Gp=-105*(ap[7]+ap[13]); Hp=105*(ap[6]+ap[14]); // 2phi derivative Bs=-B; Cs=-4*C; Es=-E; Fs=-4*F; Gs=-9*G; Hs=-16*H; // solve cubic for tan(theta) std::vector tanTheta = SolveCubic(Hp+Cp-Fp, Gp+Bp-3*Ep, 6*Fp+Cp, Bp+4*Ep); highRes = false; dPhi = phi_step; //for each real cubic solution for tan(theta) for (int n=0; n hessian; hessian(0,0) = ODF_dtheta2(sn, cs, A, B, C, D, E, F, G, H); hessian(0,1) = ODF_dtheta(sn, cs, 0, Bp, Cp, 0, Ep, Fp, Gp, Hp); hessian(1,0) = hessian(0,1); hessian(1,1) = ODF_dphi2(sn, cs, 0, Bs, Cs, 0, Es, Fs, Gs, Hs); double det = vnl_det(hessian); // determinant double tr = vnl_trace(hessian); // trace highRes = true; // we are close to a maximum, so turn on high resolution 1D exhaustive search if (det>=0 && tr<=0) // check if we really have a local maximum { vnl_vector_fixed< double, 2 > peak; peak[0] = theta; peak[1] = phi; m_CandidatePeaks.push_back(peak); } } if (adaptiveStepwidth) // calculate adaptive step width { double t2=tanTheta[n]*tanTheta[n]; double t3=t2*tanTheta[n]; double t4=t3*tanTheta[n]; double const_step=phi_step*(1+t2)/sqrt(t2+t4+pow((((Hs+Cs-Fs)*t3+(Gs+Bs-3*Es)*t2+(6*Fs+Cs)*tanTheta[n]+(Bs+4*Es))/(3*(Hp+Cp-Fp)*t2+2*(Gp+Bp-3*Ep)*tanTheta[n]+(6*Fp+Cp))),2.0)); if (const_step std::vector< vnl_vector_fixed< double, 3 > > OdfMaximaExtractionFilter< TOdfPixelType > ::ClusterPeaks(const CoefficientPixelType& shCoeff) { const double distThres = 0.4; int npeaks = 0, nMin = 0; double dMin, dPos, dNeg, d; Vector3D u; std::vector< Vector3D > v; // initialize container for vector clusters std::vector < std::vector< Vector3D > > clusters; clusters.resize(m_CandidatePeaks.size()); for (int i=0; i::max(); for (int n=0; n shBasis, sphCoords; Cart2Sph(v, sphCoords); // convert candidate peaks to spherical angles shBasis = CalcShBasis(sphCoords, 4); // evaluate spherical harmonics at each peak vnl_vector odfVals(npeaks); odfVals.fill(0.0); double maxVal = itk::NumericTraits::NonpositiveMin(); int maxPos; for (int i=0; imaxVal ) { maxVal = odfVals(i); maxPos = i; } } v.clear(); std::vector< double > restVals; for (int i=0; i=m_PeakThreshold*maxVal ) { u[0] = odfVals(i)*cos(sphCoords(i,1))*sin(sphCoords(i,0)); u[1] = odfVals(i)*sin(sphCoords(i,1))*sin(sphCoords(i,0)); u[2] = odfVals(i)*cos(sphCoords(i,0)); restVals.push_back(odfVals(i)); v.push_back(u); } npeaks = v.size(); if (npeaks>m_MaxNumPeaks) // if still too many peaks, keep only the m_MaxNumPeaks with maximum value { std::vector< Vector3D > v2; for (int i=0; i::NonpositiveMin(); //Get the maximum ODF peak value and the corresponding peak index for (int i=0; imaxVal ) { maxVal = restVals[i]; maxPos = i; } v2.push_back(v[maxPos]); restVals[maxPos] = 0; //zero that entry in order to find the next maximum } return v2; } } return v; } // convert cartesian to spherical coordinates template< class TOdfPixelType > void OdfMaximaExtractionFilter< TOdfPixelType > ::Cart2Sph(const std::vector< Vector3D >& dir, vnl_matrix& sphCoords) { sphCoords.set_size(dir.size(), 2); for (int i=0; i vnl_matrix OdfMaximaExtractionFilter< TOdfPixelType > ::CalcShBasis(vnl_matrix& sphCoords, const int& shOrder) { int R = (shOrder+1)*(shOrder+2)/2; int M = sphCoords.rows(); int j, m; double mag, plm; vnl_matrix shBasis; shBasis.set_size(M,R); for (int p=0; p(l,abs(m),cos(sphCoords(p,0))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; + mag = sqrt((double)(2*l+1)/(4.0*itk::Math::pi)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; if (m<0) shBasis(p,j) = sqrt(2.0)*mag*cos(fabs((double)m)*sphCoords(p,1)); else if (m==0) shBasis(p,j) = mag; else shBasis(p,j) = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(p,1)); j++; } } return shBasis; } template< class TOdfPixelType > void OdfMaximaExtractionFilter< TOdfPixelType > ::GenerateData() { if (!ReconstructQballImage()) return; std::cout << "Starting maxima extraction\n"; switch (m_NormalizationMethod) { case NO_NORM: std::cout << "NO_NORM\n"; break; case SINGLE_VEC_NORM: std::cout << "SINGLE_VEC_NORM\n"; break; case MAX_VEC_NORM: std::cout << "MAX_VEC_NORM\n"; break; } typedef ImageRegionConstIterator< CoefficientImageType > InputIteratorType; InputIteratorType git(m_ShCoeffImage, m_ShCoeffImage->GetLargestPossibleRegion() ); itk::Vector spacing = m_ShCoeffImage->GetSpacing(); double minSpacing = spacing[0]; if (spacing[1]GetOrigin(); itk::Matrix direction = m_ShCoeffImage->GetDirection(); ImageRegion<3> imageRegion = m_ShCoeffImage->GetLargestPossibleRegion(); // initialize num directions image m_NumDirectionsImage = ItkUcharImgType::New(); m_NumDirectionsImage->SetSpacing( spacing ); m_NumDirectionsImage->SetOrigin( origin ); m_NumDirectionsImage->SetDirection( direction ); m_NumDirectionsImage->SetRegions( imageRegion ); m_NumDirectionsImage->Allocate(); m_NumDirectionsImage->FillBuffer(0); vtkSmartPointer m_VtkCellArray = vtkSmartPointer::New(); vtkSmartPointer m_VtkPoints = vtkSmartPointer::New(); m_DirectionImageContainer = ItkDirectionImageContainer::New(); for (int i=0; i nullVec; nullVec.Fill(0.0); ItkDirectionImage::Pointer img = ItkDirectionImage::New(); img->SetSpacing( spacing ); img->SetOrigin( origin ); img->SetDirection( direction ); img->SetRegions( imageRegion ); img->Allocate(); img->FillBuffer(nullVec); m_DirectionImageContainer->InsertElement(m_DirectionImageContainer->Size(), img); } if (m_MaskImage.IsNull()) { m_MaskImage = ItkUcharImgType::New(); m_MaskImage->SetSpacing( spacing ); m_MaskImage->SetOrigin( origin ); m_MaskImage->SetDirection( direction ); m_MaskImage->SetRegions( imageRegion ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } itk::ImageRegionIterator dirIt(m_NumDirectionsImage, m_NumDirectionsImage->GetLargestPossibleRegion()); itk::ImageRegionIterator maskIt(m_MaskImage, m_MaskImage->GetLargestPossibleRegion()); int maxProgress = m_MaskImage->GetLargestPossibleRegion().GetSize()[0]*m_MaskImage->GetLargestPossibleRegion().GetSize()[1]*m_MaskImage->GetLargestPossibleRegion().GetSize()[2]; boost::progress_display disp(maxProgress); git.GoToBegin(); while( !git.IsAtEnd() ) { ++disp; if (maskIt.Value()<=0) { ++git; ++dirIt; ++maskIt; continue; } CoefficientPixelType c = git.Get(); FindCandidatePeaks(c); std::vector< Vector3D > directions = ClusterPeaks(c); typename CoefficientImageType::IndexType index = git.GetIndex(); float max = 0.0; for (int i=0; imax) max = directions.at(i).magnitude(); if (max<0.0001) max = 1.0; for (int i=0; iGetElement(i); itk::Vector< float, 3 > pixel; vnl_vector dir = directions.at(i); vtkSmartPointer container = vtkSmartPointer::New(); itk::ContinuousIndex center; center[0] = index[0]; center[1] = index[1]; center[2] = index[2]; itk::Point worldCenter; m_ShCoeffImage->TransformContinuousIndexToPhysicalPoint( center, worldCenter ); switch (m_NormalizationMethod) { case NO_NORM: break; case SINGLE_VEC_NORM: dir.normalize(); break; case MAX_VEC_NORM: dir /= max; break; } dir = m_MaskImage->GetDirection()*dir; pixel.SetElement(0, dir[0]); pixel.SetElement(1, dir[1]); pixel.SetElement(2, dir[2]); img->SetPixel(index, pixel); itk::Point worldStart; worldStart[0] = worldCenter[0]-dir[0]/2 * minSpacing; worldStart[1] = worldCenter[1]-dir[1]/2 * minSpacing; worldStart[2] = worldCenter[2]-dir[2]/2 * minSpacing; vtkIdType id = m_VtkPoints->InsertNextPoint(worldStart.GetDataPointer()); container->GetPointIds()->InsertNextId(id); itk::Point worldEnd; worldEnd[0] = worldCenter[0]+dir[0]/2 * minSpacing; worldEnd[1] = worldCenter[1]+dir[1]/2 * minSpacing; worldEnd[2] = worldCenter[2]+dir[2]/2 * minSpacing; id = m_VtkPoints->InsertNextPoint(worldEnd.GetDataPointer()); container->GetPointIds()->InsertNextId(id); m_VtkCellArray->InsertNextCell(container); } dirIt.Set(directions.size()); ++git; ++dirIt; ++maskIt; } vtkSmartPointer directionsPolyData = vtkSmartPointer::New(); directionsPolyData->SetPoints(m_VtkPoints); directionsPolyData->SetLines(m_VtkCellArray); m_OutputFiberBundle = mitk::FiberBundle::New(directionsPolyData); std::cout << "Maxima extraction finished\n"; } } #endif // __itkOdfMaximaExtractionFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkPolarToCartesianVectorImageFilter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkPolarToCartesianVectorImageFilter.h index b9d4cad247..4a88210532 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkPolarToCartesianVectorImageFilter.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkPolarToCartesianVectorImageFilter.h @@ -1,126 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkPolarToCartesianVectorImageFilter_h #define __itkPolarToCartesianVectorImageFilter_h #include "itkUnaryFunctorImageFilter.h" -#define _USE_MATH_DEFINES -#include - -#define P2C_PI M_PI - namespace itk { namespace Functor { template< typename TInput, typename TOutput, bool symmetric > class PolarToCartesianFunction { public: PolarToCartesianFunction() {} ~PolarToCartesianFunction() {} bool operator!=( const PolarToCartesianFunction & ) const { return false; } bool operator==( const PolarToCartesianFunction & other ) const { return !(*this != other); } inline TOutput operator()( const TInput & x ) { TOutput opoint; opoint[0] = x[0] * cos( x[1] ) * sin( x[2] ); opoint[1] = x[0] * sin( x[1] ) * sin( x[2] ); opoint[2] = x[0] * cos( x[2] ); if(symmetric && opoint[2]<0) { opoint[2] = -opoint[2]; } return opoint; ; } }; } // end namespace functor /** \class PolarToCartesianVectorImageFilter * */ template class PolarToCartesianVectorImageFilter : public UnaryFunctorImageFilter > { public: /** Standard class typedefs. */ typedef PolarToCartesianVectorImageFilter Self; typedef UnaryFunctorImageFilter< TInputImage,TOutputImage, Functor::PolarToCartesianFunction< typename TInputImage::PixelType, typename TOutputImage::PixelType, symmetric > > Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename TInputImage::PixelType InputPixelType; typedef typename InputPixelType::ValueType InputValueType; /** Run-time type information (and related methods). */ itkTypeMacro( PolarToCartesianVectorImageFilter, UnaryFunctorImageFilter ); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Print internal ivars */ void PrintSelf(std::ostream& os, Indent indent) const { this->Superclass::PrintSelf( os, indent ); } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(InputHasNumericTraitsCheck, (Concept::HasNumericTraits)); /** End concept checking */ #endif protected: PolarToCartesianVectorImageFilter() {}; virtual ~PolarToCartesianVectorImageFilter() {}; private: PolarToCartesianVectorImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; } // end namespace itk #endif // __itkPolarToCartesianVectorImageFilter_h diff --git a/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/itkDiffusionOdfPrepareVisualizationImageFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/itkDiffusionOdfPrepareVisualizationImageFilter.cpp index 2b2867f3b4..a75c90d1f4 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/itkDiffusionOdfPrepareVisualizationImageFilter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/itkDiffusionOdfPrepareVisualizationImageFilter.cpp @@ -1,186 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkDiffusionOdfPrepareVisualizationImageFilter_cpp #define __itkDiffusionOdfPrepareVisualizationImageFilter_cpp #include #include #include #include "itkDiffusionOdfPrepareVisualizationImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkArray.h" #include "vnl/vnl_vector.h" #include "itkOrientationDistributionFunction.h" -#define _USE_MATH_DEFINES -#include - namespace itk { - //#define Odf_RECON_PI M_PI - template< class TOdfPixelType, int NrOdfDirections> DiffusionOdfPrepareVisualizationImageFilter< TOdfPixelType, NrOdfDirections> ::DiffusionOdfPrepareVisualizationImageFilter() : m_Threshold(0), m_ScaleByGfaType(GfaFilterType::GFA_STANDARD), m_DoScaleGfa(false), m_GfaParam1(2), m_GfaParam2(1) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template< class TOdfPixelType, int NrOdfDirections> void DiffusionOdfPrepareVisualizationImageFilter< TOdfPixelType, NrOdfDirections> ::BeforeThreadedGenerateData() { if( m_NormalizationMethod == PV_GLOBAL_MAX ) { typename InputImageType::Pointer inputImagePointer = nullptr; inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); typename GfaFilterType::Pointer filter = GfaFilterType::New(); filter->SetInput(inputImagePointer); filter->SetNumberOfThreads(4); filter->SetComputationMethod(GfaFilterType::GFA_MAX_ODF_VALUE); filter->Update(); typedef typename itk::MinimumMaximumImageCalculator< typename GfaFilterType::OutputImageType > MaxFilterType; typename MaxFilterType::Pointer maxFilter = MaxFilterType::New(); maxFilter->SetImage(filter->GetOutput()); maxFilter->ComputeMaximum(); m_GlobalInputMaximum = maxFilter->GetMaximum(); } //if(m_DoScaleGfa) { typename InputImageType::Pointer inputImagePointer = nullptr; inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); typename GfaFilterType::Pointer filter = GfaFilterType::New(); filter->SetInput(inputImagePointer); filter->SetNumberOfThreads(4); filter->SetComputationMethod(m_ScaleByGfaType); filter->SetParam1(m_GfaParam1); filter->SetParam2(m_GfaParam2); filter->Update(); m_GfaImage = filter->GetOutput(); } } template< class TOdfPixelType, int NrOdfDirections> void DiffusionOdfPrepareVisualizationImageFilter< TOdfPixelType, NrOdfDirections> ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); typedef itk::OrientationDistributionFunction OdfType; typedef ImageRegionConstIterator< InputImageType > InputIteratorType; typedef typename InputImageType::PixelType OdfVectorType; typename InputImageType::Pointer inputImagePointer = nullptr; inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); InputIteratorType git(inputImagePointer, outputRegionForThread ); git.GoToBegin(); typedef ImageRegionConstIterator< GfaImageType > GfaIteratorType; GfaIteratorType gfaIt(m_GfaImage, outputRegionForThread); while( !git.IsAtEnd() ) { OdfVectorType b = git.Get(); OdfType odf = b.GetDataPointer(); switch( m_NormalizationMethod ) { case PV_NONE: { break; } case PV_MAX: { odf = odf.MaxNormalize(); break; } case PV_MIN_MAX: { odf = odf.MinMaxNormalize(); break; } case PV_GLOBAL_MAX: { odf *= 1.0/m_GlobalInputMaximum; break; } case PV_MIN_MAX_INVERT: { odf = odf.MinMaxNormalize(); for(int i=0; i void DiffusionOdfPrepareVisualizationImageFilter< TOdfPixelType, NrOdfDirections> ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os,indent); os << indent << "m_Threshold: " << m_Threshold << std::endl; } } #endif // __itkDiffusionOdfPrepareVisualizationImageFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp b/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp index 66bd1eaa78..72ad985147 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp @@ -1,992 +1,987 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkPartialVolumeAnalysisClusteringCalculator.h" #include "mitkImageAccessByItk.h" #include "mitkImageToItk.h" #include "itkScalarImageToHistogramGenerator.h" #include "itkListSample.h" -#define _USE_MATH_DEFINES -#include - -#define PVA_PI M_PI - namespace mitk { PartialVolumeAnalysisClusteringCalculator::PartialVolumeAnalysisClusteringCalculator() : m_MaxIt(100), m_StepsNumIntegration(100) { } PartialVolumeAnalysisClusteringCalculator::~PartialVolumeAnalysisClusteringCalculator() { } PartialVolumeAnalysisClusteringCalculator::ParamsType* PartialVolumeAnalysisClusteringCalculator::InitialGuess(HistType h) const { int s = h.xVals.size(); int s30 = 0.3 * s; int s70 = 0.7 * s; auto params = new ParamsType(); params->means[0] = h.xVals(s30); params->means[1] = h.xVals(s70); params->sigmas[0] = (h.xVals(s-1) - h.xVals(0)) / 5.0; params->sigmas[1] = (h.xVals(s-1) - h.xVals(0)) / 5.0; params->ps[0] = 0.4; params->ps[1] = 0.4; return params; } PartialVolumeAnalysisClusteringCalculator::ParamsType* PartialVolumeAnalysisClusteringCalculator::Cluster(HistType h, ParamsType *initialGuess) const { auto params = new ParamsType(); params->Initialize(initialGuess); double ll = 9999999999999999999.9; double oll; double sml = (h.xVals[1] - h.xVals[0]) / 1000; int arraysz = h.xVals.size(); for (unsigned int it = 0; it < m_MaxIt; it++) { // wie sehen basisfunktionen zu aktuellen params aus? ClusterResultType curves = CalculateCurves(*params,h.xVals); // summe der basisfunktionen for(int i=0; i2 && oll-ll < arraysz/2*1e-15 ) { break; } for(int j=0; j<2; j++) { VecType array, p(arraysz); array = curves.vals[j]; for(int i=0; ips[j] = 0; params->means[j] = 0; for(int i=0; ips[j] += p(i); params->means[j] += h.xVals(i)*p(i); } params->means[j] /= params->ps[j]; VecType vr = h.xVals; for(int i=0; imeans[j]; } params->sigmas[j] = 0; for(int i=0; isigmas[j] += vr(i)*vr(i)*p(i); } params->sigmas[j] /= params->ps[j]; params->sigmas[j] += sml; } double p3 = 0; for(int i=0; ips[j] = params->ps[j] + 1e-3; sum += params->ps[j]; } sum += p3; for(int j=0; j<2; j++) { params->ps[j] = params->ps[j] / sum; } p3 /= sum; } return params; } void PartialVolumeAnalysisClusteringCalculator::Normalize(ParamsType params, ClusterResultType* curves) const { double sum1=0, sum2=0, sum3=0; int arraysz = curves->vals[0].size(); for(int i=0; ivals[0](i); sum2 += curves->vals[1](i); sum3 += curves->mixedVals[0](i); } sum1 /= params.ps[0]; sum2 /= params.ps[1]; sum3 /= 1.0 -params.ps[0] -params.ps[1]; for(int i=0; ivals[0](i) /= sum1; curves->vals[1](i) /= sum2; curves->mixedVals[0](i) /= sum3; } for(int i=0; icombiVals(i) = curves->mixedVals[0](i) + curves->vals[0](i) + curves->vals[1](i); } } PartialVolumeAnalysisClusteringCalculator::ClusterResultType PartialVolumeAnalysisClusteringCalculator::CalculateCurves(ParamsType params, VecType xVals) const { int arraysz = xVals.size(); ClusterResultType result(arraysz); for( int j=0; j<2; j++) { for(int i=0; i 0.0000000000000001) { itprob.Set( aposteriori ); maxp = aposteriori > maxp ? aposteriori : maxp; } else { itprob.Set(0.0f); } } ++itimage; ++itprob; } itprob.GoToBegin(); itdisp.GoToBegin(); while( !itprob.IsAtEnd() ) { if(itprob.Get()) { typename DisplayImageType::PixelType rgba; rgba.Set(255.0f, 0.0f, 0.0f, 255.0f*(itprob.Get()/maxp)); itdisp.Set( rgba ); } ++itprob; ++itdisp; } outImage1->InitializeByItk(probimage.GetPointer()); outImage1->SetVolume(probimage->GetBufferPointer()); outImage2->InitializeByItk(displayimage.GetPointer()); outImage2->SetVolume(displayimage->GetBufferPointer()); } double* PartialVolumeAnalysisClusteringCalculator::PerformQuantification( mitk::Image::ConstPointer image, mitk::Image::Pointer clusteredImage, mitk::Image::Pointer mask) const { auto retval = new double[2]; AccessFixedDimensionByItk_3( image.GetPointer(), InternalQuantify, 3, clusteredImage.GetPointer(), retval, mask ); return retval; } template < typename TPixel, unsigned int VImageDimension > void PartialVolumeAnalysisClusteringCalculator::InternalQuantify( const itk::Image< TPixel, VImageDimension > *image, mitk::Image::Pointer clusteredImage, double* retval, mitk::Image::Pointer mask ) const { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< float, VImageDimension > ProbImageType; typedef itk::Image< unsigned char, VImageDimension > MaskImageType; typedef mitk::ImageToItk CastFilterType; typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput( clusteredImage ); castFilter->Update(); typename ProbImageType::Pointer clusterImage = castFilter->GetOutput(); typename MaskImageType::Pointer itkmask = nullptr; if(mask.IsNotNull()) { typedef mitk::ImageToItk CastFilterType2; typename CastFilterType2::Pointer castFilter2 = CastFilterType2::New(); castFilter2->SetInput( mask ); castFilter2->Update(); itkmask = castFilter2->GetOutput(); } else { itkmask = MaskImageType::New(); itkmask->SetSpacing( clusterImage->GetSpacing() ); // Set the image spacing itkmask->SetOrigin( clusterImage->GetOrigin() ); // Set the image origin itkmask->SetDirection( clusterImage->GetDirection() ); // Set the image direction itkmask->SetRegions( clusterImage->GetLargestPossibleRegion() ); itkmask->Allocate(); itkmask->FillBuffer(1); } itk::ImageRegionConstIterator itimage(image, image->GetLargestPossibleRegion()); itk::ImageRegionConstIterator itprob(clusterImage, clusterImage->GetLargestPossibleRegion()); itk::ImageRegionConstIterator itmask(itkmask, itkmask->GetLargestPossibleRegion()); itimage.GoToBegin(); itprob.GoToBegin(); itmask.GoToBegin(); double totalProb = 0; double measurement = 0; double error = 0; while( !itimage.IsAtEnd() && !itprob.IsAtEnd() && !itmask.IsAtEnd() ) { double valImag = itimage.Get(); double valProb = itprob.Get(); double valMask = itmask.Get(); typename ProbImageType::PixelType prop = valProb * valMask; totalProb += prop; measurement += valImag * prop; error += valImag * valImag * prop; ++itimage; ++itprob; ++itmask; } measurement = measurement / totalProb; error = error / totalProb; retval[0] = measurement; retval[1] = sqrt( error - measurement*measurement ); } mitk::Image::Pointer PartialVolumeAnalysisClusteringCalculator::CaculateAngularErrorImage( mitk::Image::Pointer comp1, mitk::Image::Pointer comp2, mitk::Image::Pointer probImg) const { // cast input images to itk typedef itk::Image ImageType; typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(comp1); caster->Update(); ImageType::Pointer comp1Image = caster->GetOutput(); caster = CastType::New(); caster->SetInput(comp2); caster->Update(); ImageType::Pointer comp2Image = caster->GetOutput(); caster = CastType::New(); caster->SetInput(probImg); caster->Update(); ImageType::Pointer probImage = caster->GetOutput(); // figure out maximum probability for fiber class float maxProb = 0; itk::ImageRegionConstIterator itprob(probImage, probImage->GetLargestPossibleRegion()); itprob.GoToBegin(); while( !itprob.IsAtEnd() ) { maxProb = itprob.Get() > maxProb ? itprob.Get() : maxProb; ++itprob; } // generate a list sample of angles at positions // where the fiber-prob is higher than .2*maxprob typedef float MeasurementType; const unsigned int MeasurementVectorLength = 2; typedef itk::Vector< MeasurementType , MeasurementVectorLength > MeasurementVectorType; typedef itk::Statistics::ListSample< MeasurementVectorType > ListSampleType; ListSampleType::Pointer listSample = ListSampleType::New(); listSample->SetMeasurementVectorSize( MeasurementVectorLength ); itk::ImageRegionIterator it1(comp1Image, comp1Image->GetLargestPossibleRegion()); itk::ImageRegionIterator it2(comp2Image, comp2Image->GetLargestPossibleRegion()); it1.GoToBegin(); it2.GoToBegin(); itprob.GoToBegin(); while( !itprob.IsAtEnd() ) { if(itprob.Get() > 0.2 * maxProb) { MeasurementVectorType mv; mv[0] = ( MeasurementType ) it1.Get(); mv[1] = ( MeasurementType ) it2.Get(); listSample->PushBack(mv); } ++it1; ++it2; ++itprob; } // generate a histogram from the list sample typedef float HistogramMeasurementType; typedef itk::Statistics::Histogram< HistogramMeasurementType, itk::Statistics::DenseFrequencyContainer2 > HistogramType; typedef itk::Statistics::SampleToHistogramFilter< ListSampleType, HistogramType > GeneratorType; GeneratorType::Pointer generator = GeneratorType::New(); GeneratorType::HistogramType::SizeType size(2); size.Fill(30); generator->SetHistogramSize( size ); generator->SetInput( listSample ); generator->SetMarginalScale( 10.0 ); generator->Update(); // look for frequency mode in the histogram GeneratorType::HistogramType::ConstPointer histogram = generator->GetOutput(); GeneratorType::HistogramType::ConstIterator iter = histogram->Begin(); float maxFreq = 0; MeasurementVectorType maxValue; maxValue.Fill(0); while ( iter != histogram->End() ) { if(iter.GetFrequency() > maxFreq) { maxFreq = iter.GetFrequency(); maxValue[0] = iter.GetMeasurementVector()[0]; maxValue[1] = iter.GetMeasurementVector()[1]; } ++iter; } // generate return image that contains the angular // error of the voxels to the histogram max measurement ImageType::Pointer returnImage = ImageType::New(); returnImage->SetSpacing( comp1Image->GetSpacing() ); // Set the image spacing returnImage->SetOrigin( comp1Image->GetOrigin() ); // Set the image origin returnImage->SetDirection( comp1Image->GetDirection() ); // Set the image direction returnImage->SetRegions( comp1Image->GetLargestPossibleRegion() ); returnImage->Allocate(); itk::ImageRegionConstIterator cit1(comp1Image, comp1Image->GetLargestPossibleRegion()); itk::ImageRegionConstIterator cit2(comp2Image, comp2Image->GetLargestPossibleRegion()); itk::ImageRegionIterator itout(returnImage, returnImage->GetLargestPossibleRegion()); cit1.GoToBegin(); cit2.GoToBegin(); itout.GoToBegin(); vnl_vector v(3); v[0] = cos( maxValue[0] ) * sin( maxValue[1] ); v[1] = sin( maxValue[0] ) * sin( maxValue[1] ); v[2] = cos( maxValue[1] ); // MITK_INFO << "max vector: " << v; while( !cit1.IsAtEnd() ) { vnl_vector v1(3); v1[0] = cos( cit1.Get() ) * sin( cit2.Get() ); v1[1] = sin( cit1.Get() ) * sin( cit2.Get() ); v1[2] = cos( cit2.Get() ); itout.Set(fabs(angle(v,v1))); // MITK_INFO << "ang_error " << v1 << ": " << fabs(angle(v,v1)); ++cit1; ++cit2; ++itout; } mitk::Image::Pointer retval = mitk::Image::New(); retval->InitializeByItk(returnImage.GetPointer()); retval->SetVolume(returnImage->GetBufferPointer()); return retval; } PartialVolumeAnalysisClusteringCalculator::HelperStructPerformRGBClusteringRetval* PartialVolumeAnalysisClusteringCalculator::PerformRGBQuantiles(mitk::Image::ConstPointer image, const MitkHistType *histogram, double p1, double p2) const { auto rgbChannels = new HelperStructRGBChannels(); HelperStructPerformClusteringRetval *resultr = PerformQuantiles(image, histogram, p2, 999999999.0 ); rgbChannels->r = resultr->clusteredImage; HelperStructPerformClusteringRetval *resultg = PerformQuantiles(image, histogram, -999999999.0, p1 ); rgbChannels->g = resultg->clusteredImage; HelperStructPerformClusteringRetval *resultb = PerformQuantiles(image, histogram, p1, p2 ); rgbChannels->b = resultb->clusteredImage; mitk::Image::Pointer outImage = mitk::Image::New(); switch(rgbChannels->r->GetDimension()) { case 2: InternalGenerateRGB<2>(rgbChannels, outImage); break; case 3: InternalGenerateRGB<3>(rgbChannels, outImage); break; case 4: InternalGenerateRGB<4>(rgbChannels, outImage); break; default: InternalGenerateRGB<3>(rgbChannels, outImage); } auto retval = new HelperStructPerformRGBClusteringRetval(); retval->rgbChannels = rgbChannels; retval->rgb = outImage; retval->params = resultr->params; retval->result = resultr->result; retval->hist = resultr->hist; delete resultr; delete resultg; return retval; } PartialVolumeAnalysisClusteringCalculator::HelperStructPerformClusteringRetval* PartialVolumeAnalysisClusteringCalculator::PerformQuantiles(mitk::Image::ConstPointer image, const MitkHistType *histogram, double p1, double p2 ) const { auto retval = new HelperStructPerformClusteringRetval(); retval->hist = new HistType(); retval->hist->InitByMitkHistogram(histogram); auto q = new double[2]; q[0] = histogram->Quantile(0, p1); q[1] = histogram->Quantile(0, p2); mitk::Image::Pointer outImage1 = mitk::Image::New(); mitk::Image::Pointer outImage2 = mitk::Image::New(); AccessFixedDimensionByItk_3( image.GetPointer(), InternalGenerateQuantileImage, 3, q, outImage1, outImage2); retval->clusteredImage = outImage1; retval->displayImage = outImage2; delete[] q; return retval; } template < typename TPixel, unsigned int VImageDimension > void PartialVolumeAnalysisClusteringCalculator::InternalGenerateQuantileImage( const itk::Image< TPixel, VImageDimension > *image, double* q, mitk::Image::Pointer outImage1, mitk::Image::Pointer outImage2 ) const { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< itk::RGBAPixel, VImageDimension > DisplayImageType; typedef itk::Image< float, VImageDimension > ProbImageType; typename ProbImageType::Pointer probimage = ProbImageType::New(); probimage->SetSpacing( image->GetSpacing() ); // Set the image spacing probimage->SetOrigin( image->GetOrigin() ); // Set the image origin probimage->SetDirection( image->GetDirection() ); // Set the image direction probimage->SetRegions( image->GetLargestPossibleRegion() ); probimage->Allocate(); probimage->FillBuffer(0); typename DisplayImageType::Pointer displayimage = DisplayImageType::New(); displayimage->SetSpacing( image->GetSpacing() ); // Set the image spacing displayimage->SetOrigin( image->GetOrigin() ); // Set the image origin displayimage->SetDirection( image->GetDirection() ); // Set the image direction displayimage->SetRegions( image->GetLargestPossibleRegion() ); displayimage->Allocate(); typename DisplayImageType::PixelType rgba; rgba.Set(0.0f, 0.0f, 0.0f, 0.0f); displayimage->FillBuffer(rgba); itk::ImageRegionConstIterator itimage(image, image->GetLargestPossibleRegion()); itk::ImageRegionIterator itprob(probimage, probimage->GetLargestPossibleRegion()); itk::ImageRegionIterator itdisp(displayimage, displayimage->GetLargestPossibleRegion()); itimage.GoToBegin(); itprob.GoToBegin(); while( !itimage.IsAtEnd() ) { if(itimage.Get() > q[0] && itimage.Get() < q[1]) { itprob.Set(1.0f); } ++itimage; ++itprob; } itprob.GoToBegin(); itdisp.GoToBegin(); while( !itprob.IsAtEnd() ) { if(itprob.Get()) { typename DisplayImageType::PixelType rgba; rgba.Set(255.0f, 0.0f, 0.0f, 255.0f); itdisp.Set( rgba ); } ++itprob; ++itdisp; } outImage1->InitializeByItk(probimage.GetPointer()); outImage1->SetVolume(probimage->GetBufferPointer()); outImage2->InitializeByItk(displayimage.GetPointer()); outImage2->SetVolume(displayimage->GetBufferPointer()); } } diff --git a/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp b/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp index 9e19442bdc..20e2350431 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp @@ -1,288 +1,282 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkDiffusionFunctionCollection.h" -#include #include "mitkNumericTypes.h" -// for Windows -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - // Namespace ::SH #include #include #include #include // Namespace ::Gradients #include "itkVectorContainer.h" #include "vnl/vnl_vector.h" //------------------------- SH-function ------------------------------------ double mitk::sh::factorial(int number) { if(number <= 1) return 1; double result = 1.0; for(int i=1; i<=number; i++) result *= i; return result; } void mitk::sh::Cart2Sph(double x, double y, double z, double *spherical) { double phi, th, rad; rad = sqrt(x*x+y*y+z*z); if( rad < mitk::eps ) { - th = M_PI/2; - phi = M_PI/2; + th = itk::Math::pi/2; + phi = itk::Math::pi/2; } else { th = acos(z/rad); phi = atan2(y, x); } spherical[0] = phi; spherical[1] = th; spherical[2] = rad; } double mitk::sh::legendre0(int l) { if( l%2 != 0 ) { return 0; } else { double prod1 = 1.0; for(int i=1;i(l,abs(m),-cos(theta)); - double mag = sqrt((double)(2*l+1)/(4.0*M_PI)*::boost::math::factorial(l-abs(m))/::boost::math::factorial(l+abs(m)))*plm; + double mag = sqrt((double)(2*l+1)/(4.0*itk::Math::pi)*::boost::math::factorial(l-abs(m))/::boost::math::factorial(l+abs(m)))*plm; if (m>0) return mag*cos(m*phi); else if (m==0) return mag; else return mag*sin(-m*phi); } return 0; } vnl_matrix mitk::sh::CalcShBasisForDirections(int sh_order, vnl_matrix U, bool mrtrix) { vnl_matrix sh_basis = vnl_matrix(U.cols(), (sh_order*sh_order + sh_order + 2)/2 + sh_order ); for(unsigned int i=0; i mitk::gradients::GetAllUniqueDirections(const BValueMap & refBValueMap, GradientDirectionContainerType *refGradientsContainer ) { IndiciesVector directioncontainer; auto mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); auto containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot_product(refGradientsContainer->ElementAt(*containerIt), refGradientsContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } } return directioncontainer; } bool mitk::gradients::CheckForDifferingShellDirections(const BValueMap & refBValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer) { auto mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ auto mapIterator_2 = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator_2++; //skip bzero Values for( ; mapIterator_2 != refBValueMap.end(); mapIterator_2++){ if(mapIterator_2 == mapIterator) continue; IndiciesVector currentShell = mapIterator->second; IndiciesVector testShell = mapIterator_2->second; for (unsigned int i = 0; i< currentShell.size(); i++) if (fabs(dot_product(refGradientsContainer->ElementAt(currentShell[i]), refGradientsContainer->ElementAt(testShell[i]))) <= 0.9998) { return true; } } } return false; } vnl_matrix mitk::gradients::ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer) { vnl_matrix Q(3, refShell.size()); Q.fill(0.0); for(unsigned int i = 0; i < refShell.size(); i++) { GradientDirectionType dir = refGradientsContainer->ElementAt(refShell[i]); double x = dir.normalize().get(0); double y = dir.normalize().get(1); double z = dir.normalize().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i) = cart[2]; } return Q; } vnl_matrix mitk::gradients::ComputeSphericalHarmonicsBasis(const vnl_matrix & QBallReference, const unsigned int & LOrder) { vnl_matrix SHBasisOutput(QBallReference.cols(), (LOrder+1)*(LOrder+2)*0.5); SHBasisOutput.fill(0.0); for(int i=0; i< (int)SHBasisOutput.rows(); i++) for(int k = 0; k <= (int)LOrder; k += 2) for(int m =- k; m <= k; m++) { int j = ( k * k + k + 2 ) / 2.0 + m - 1; double phi = QBallReference(0,i); double th = QBallReference(1,i); double val = mitk::sh::Yj(m,k,th,phi); SHBasisOutput(i,j) = val; } return SHBasisOutput; } mitk::gradients::GradientDirectionContainerType::Pointer mitk::gradients::CreateNormalizedUniqueGradientDirectionContainer(const mitk::gradients::BValueMap & bValueMap, const GradientDirectionContainerType *origninalGradentcontainer) { mitk::gradients::GradientDirectionContainerType::Pointer directioncontainer = mitk::gradients::GradientDirectionContainerType::New(); auto mapIterator = bValueMap.begin(); if(bValueMap.find(0) != bValueMap.end() && bValueMap.size() > 1){ mapIterator++; //skip bzero Values vnl_vector_fixed vec; vec.fill(0.0); directioncontainer->push_back(vec); } for( ; mapIterator != bValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); mitk::gradients::GradientDirectionContainerType::Iterator containerIt = directioncontainer->Begin(); bool directionExist = false; while(containerIt != directioncontainer->End()) { if (fabs(dot_product(containerIt.Value(), origninalGradentcontainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { GradientDirectionType dir(origninalGradentcontainer->ElementAt(wntIndex)); directioncontainer->push_back(dir.normalize()); } } } return directioncontainer; } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/ClusteringMetrics/mitkClusteringMetricInnerAngles.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/ClusteringMetrics/mitkClusteringMetricInnerAngles.h index 7f243a8509..5581cb9aa3 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/ClusteringMetrics/mitkClusteringMetricInnerAngles.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/ClusteringMetrics/mitkClusteringMetricInnerAngles.h @@ -1,125 +1,125 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 _ClusteringMetricInnerAngles #define _ClusteringMetricInnerAngles #include namespace mitk { /** * \brief Fiber clustering metric based on the angles between certain parts of the streamline */ class ClusteringMetricInnerAngles : public ClusteringMetric { public: ClusteringMetricInnerAngles(){} virtual ~ClusteringMetricInnerAngles(){} float CalculateDistance(vnl_matrix& s, vnl_matrix& t, bool &flipped) { int p1 = 0; int p2 = s.cols()/4; int p3 = s.cols()/2; int p4 = 3*s.cols()/4; int p5 = s.cols()-1; float a1_s = 0; float a2_s = 0; float a3_s = 0; float a1_t = 0; float a2_t = 0; float a3_t = 0; { vnl_vector v1 = s.get_column(p1)-s.get_column(p2); v1.normalize(); vnl_vector v2 = s.get_column(p3)-s.get_column(p2); v2.normalize(); a1_s = dot_product(v1,v2); - a1_s = std::acos( a1_s ) * 180.0/M_PI; + a1_s = std::acos( a1_s ) * 180.0/itk::Math::pi; } { vnl_vector v1 = s.get_column(p1)-s.get_column(p3); v1.normalize(); vnl_vector v2 = s.get_column(p5)-s.get_column(p3); v2.normalize(); a2_s = dot_product(v1,v2); - a2_s = std::acos( a2_s ) * 180.0/M_PI; + a2_s = std::acos( a2_s ) * 180.0/itk::Math::pi; } { vnl_vector v1 = s.get_column(p3)-s.get_column(p4); v1.normalize(); vnl_vector v2 = s.get_column(p5)-s.get_column(p4); v2.normalize(); a3_s = dot_product(v1,v2); - a3_s = std::acos( a3_s ) * 180.0/M_PI; + a3_s = std::acos( a3_s ) * 180.0/itk::Math::pi; } // { vnl_vector v1 = t.get_column(p1)-t.get_column(p2); v1.normalize(); vnl_vector v2 = t.get_column(p3)-t.get_column(p2); v2.normalize(); a1_t = dot_product(v1,v2); - a1_t = std::acos( a1_t ) * 180.0/M_PI; + a1_t = std::acos( a1_t ) * 180.0/itk::Math::pi; } { vnl_vector v1 = t.get_column(p1)-t.get_column(p3); v1.normalize(); vnl_vector v2 = t.get_column(p5)-t.get_column(p3); v2.normalize(); a2_t = dot_product(v1,v2); - a2_t = std::acos( a2_t ) * 180.0/M_PI; + a2_t = std::acos( a2_t ) * 180.0/itk::Math::pi; } { vnl_vector v1 = t.get_column(p3)-t.get_column(p4); v1.normalize(); vnl_vector v2 = t.get_column(p5)-t.get_column(p4); v2.normalize(); a3_t = dot_product(v1,v2); - a3_t = std::acos( a3_t ) * 180.0/M_PI; + a3_t = std::acos( a3_t ) * 180.0/itk::Math::pi; } float d_direct = 0; float d_flipped = 0; int inc = s.cols()/4; for (unsigned int i=0; id_flipped) { flipped = true; float d1 = std::fabs(a1_s-a3_t); float d2 = std::fabs(a2_s-a2_t); float d3 = std::fabs(a3_s-a1_t); return m_Scale * std::max( d1, std::max(d2,d3) ); } flipped = false; float d1 = std::fabs(a1_s-a1_t); float d2 = std::fabs(a2_s-a2_t); float d3 = std::fabs(a3_s-a3_t); return m_Scale * std::max( d1, std::max(d2,d3) ); } protected: }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.cpp index e98c5ebd28..721392252c 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.cpp @@ -1,499 +1,499 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkMetropolisHastingsSampler.h" using namespace mitk; MetropolisHastingsSampler::MetropolisHastingsSampler(ParticleGrid* grid, EnergyComputer* enComp, ItkRandGenType* randGen, float curvThres) : m_ExTemp(0.01) , m_BirthProb(0.25) , m_DeathProb(0.05) , m_ShiftProb(0.15) , m_OptShiftProb(0.1) , m_ConnectionProb(0.45) , m_TractProb(0.5) , m_DelProb(0.1) , m_ChempotParticle(0.0) , m_AcceptedProposals(0) { m_RandGen = randGen; m_ParticleGrid = grid; m_EnergyComputer = enComp; m_ParticleLength = m_ParticleGrid->m_ParticleLength; m_DistanceThreshold = m_ParticleLength*m_ParticleLength; m_Sigma = m_ParticleLength/8.0; m_Gamma = 1/(m_Sigma*m_Sigma*2); - m_Z = pow(2*M_PI*m_Sigma,3.0/2.0)*(M_PI*m_Sigma/m_ParticleLength); + m_Z = pow(2*itk::Math::pi*m_Sigma,3.0/2.0)*(itk::Math::pi*m_Sigma/m_ParticleLength); m_CurvatureThreshold = curvThres; m_StopProb = exp(-1/m_TractProb); } void MetropolisHastingsSampler::SetProbabilities(float birth, float death, float shift, float optShift, float connect) { m_BirthProb = birth; m_DeathProb = death; m_ShiftProb = shift; m_OptShiftProb = optShift; m_ConnectionProb = connect; float sum = m_BirthProb+m_DeathProb+m_ShiftProb+m_OptShiftProb+m_ConnectionProb; if (sum!=1 && sum>mitk::eps) { m_BirthProb /= sum; m_DeathProb /= sum; m_ShiftProb /= sum; m_OptShiftProb /= sum; m_ConnectionProb /= sum; } std::cout << "Update proposal probabilities" << std::endl; std::cout << "Birth: " << m_BirthProb << std::endl; std::cout << "Death: " << m_DeathProb << std::endl; std::cout << "Shift: " << m_ShiftProb << std::endl; std::cout << "Optimal shift: " << m_OptShiftProb << std::endl; std::cout << "Connection: " << m_ConnectionProb << std::endl; } // print proposal times void MetropolisHastingsSampler::PrintProposalTimes() { double sum = m_BirthTime.GetTotal()+m_DeathTime.GetTotal()+m_ShiftTime.GetTotal()+m_OptShiftTime.GetTotal()+m_ConnectionTime.GetTotal(); std::cout << "Proposal time probes (toal%/mean)" << std::endl; std::cout << "Birth: " << 100*m_BirthTime.GetTotal()/sum << "/" << m_BirthTime.GetMean()*1000 << std::endl; std::cout << "Death: " << 100*m_DeathTime.GetTotal()/sum << "/" << m_DeathTime.GetMean()*1000 << std::endl; std::cout << "Shift: " << 100*m_ShiftTime.GetTotal()/sum << "/" << m_ShiftTime.GetMean()*1000 << std::endl; std::cout << "Optimal shift: " << 100*m_OptShiftTime.GetTotal()/sum << " - " << m_OptShiftTime.GetMean()*1000 << std::endl; std::cout << "Connection: " << 100*m_ConnectionTime.GetTotal()/sum << "/" << m_ConnectionTime.GetMean()*1000 << std::endl; } // update temperature of simulated annealing process void MetropolisHastingsSampler::SetTemperature(float val) { m_InTemp = val; m_Density = exp(-m_ChempotParticle/m_InTemp); } // add small random number drawn from gaussian to each vector element void MetropolisHastingsSampler::DistortVector(float sigma, vnl_vector_fixed& vec) { vec[0] += m_RandGen->GetNormalVariate(0.0, sigma); vec[1] += m_RandGen->GetNormalVariate(0.0, sigma); vec[2] += m_RandGen->GetNormalVariate(0.0, sigma); } // generate normalized random vector vnl_vector_fixed MetropolisHastingsSampler::GetRandomDirection() { vnl_vector_fixed vec; vec[0] = m_RandGen->GetNormalVariate(); vec[1] = m_RandGen->GetNormalVariate(); vec[2] = m_RandGen->GetNormalVariate(); vec.normalize(); return vec; } // generate actual proposal (birth, death, shift and connection of particle) void MetropolisHastingsSampler::MakeProposal() { float randnum = m_RandGen->GetVariate(); // Birth Proposal if (randnum < m_BirthProb) { m_BirthTime.Start(); vnl_vector_fixed R; m_EnergyComputer->DrawRandomPosition(R); vnl_vector_fixed N = GetRandomDirection(); Particle prop; prop.GetPos() = R; prop.GetDir() = N; float prob = m_Density * m_DeathProb /((m_BirthProb)*(m_ParticleGrid->m_NumParticles+1)); float ex_energy = m_EnergyComputer->ComputeExternalEnergy(R,N,nullptr); float in_energy = m_EnergyComputer->ComputeInternalEnergy(&prop); prob *= exp((in_energy/m_InTemp+ex_energy/m_ExTemp)) ; if (prob > 1 || m_RandGen->GetVariate() < prob) { Particle *p = m_ParticleGrid->NewParticle(R); if (p!=nullptr) { p->GetPos() = R; p->GetDir() = N; m_AcceptedProposals++; } } m_BirthTime.Stop(); } // Death Proposal else if (randnum < m_BirthProb+m_DeathProb) { m_DeathTime.Start(); if (m_ParticleGrid->m_NumParticles > 0) { int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; Particle *dp = m_ParticleGrid->GetParticle(pnum); if (dp->pID == -1 && dp->mID == -1) { float ex_energy = m_EnergyComputer->ComputeExternalEnergy(dp->GetPos(),dp->GetDir(),dp); float in_energy = m_EnergyComputer->ComputeInternalEnergy(dp); float prob = m_ParticleGrid->m_NumParticles * (m_BirthProb) /(m_Density*m_DeathProb); //*SpatProb(dp->R); prob *= exp(-(in_energy/m_InTemp+ex_energy/m_ExTemp)) ; if (prob > 1 || m_RandGen->GetVariate() < prob) { m_ParticleGrid->RemoveParticle(pnum); m_AcceptedProposals++; } } } m_DeathTime.Stop(); } // Shift Proposal else if (randnum < m_BirthProb+m_DeathProb+m_ShiftProb) { if (m_ParticleGrid->m_NumParticles > 0) { m_ShiftTime.Start(); int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; Particle *p = m_ParticleGrid->GetParticle(pnum); Particle prop_p = *p; DistortVector(m_Sigma, prop_p.GetPos()); DistortVector(m_Sigma/(2*m_ParticleLength), prop_p.GetDir()); prop_p.GetDir().normalize(); float ex_energy = m_EnergyComputer->ComputeExternalEnergy(prop_p.GetPos(),prop_p.GetDir(),p) - m_EnergyComputer->ComputeExternalEnergy(p->GetPos(),p->GetDir(),p); float in_energy = m_EnergyComputer->ComputeInternalEnergy(&prop_p) - m_EnergyComputer->ComputeInternalEnergy(p); float prob = exp(ex_energy/m_ExTemp+in_energy/m_InTemp); if (m_RandGen->GetVariate() < prob) { vnl_vector_fixed Rtmp = p->GetPos(); vnl_vector_fixed Ntmp = p->GetDir(); p->GetPos() = prop_p.GetPos(); p->GetDir() = prop_p.GetDir(); if (!m_ParticleGrid->TryUpdateGrid(pnum)) { p->GetPos() = Rtmp; p->GetDir() = Ntmp; } m_AcceptedProposals++; } m_ShiftTime.Stop(); } } // Optimal Shift Proposal else if (randnum < m_BirthProb+m_DeathProb+m_ShiftProb+m_OptShiftProb) { if (m_ParticleGrid->m_NumParticles > 0) { m_OptShiftTime.Start(); int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; Particle *p = m_ParticleGrid->GetParticle(pnum); bool no_proposal = false; Particle prop_p = *p; if (p->pID != -1 && p->mID != -1) { Particle *plus = m_ParticleGrid->GetParticle(p->pID); int ep_plus = (plus->pID == p->ID)? 1 : -1; Particle *minus = m_ParticleGrid->GetParticle(p->mID); int ep_minus = (minus->pID == p->ID)? 1 : -1; prop_p.GetPos() = (plus->GetPos() + plus->GetDir() * (m_ParticleLength * ep_plus) + minus->GetPos() + minus->GetDir() * (m_ParticleLength * ep_minus)); prop_p.GetPos() *= 0.5; prop_p.GetDir() = plus->GetPos() - minus->GetPos(); prop_p.GetDir().normalize(); } else if (p->pID != -1) { Particle *plus = m_ParticleGrid->GetParticle(p->pID); int ep_plus = (plus->pID == p->ID)? 1 : -1; prop_p.GetPos() = plus->GetPos() + plus->GetDir() * (m_ParticleLength * ep_plus * 2); prop_p.GetDir() = plus->GetDir(); } else if (p->mID != -1) { Particle *minus = m_ParticleGrid->GetParticle(p->mID); int ep_minus = (minus->pID == p->ID)? 1 : -1; prop_p.GetPos() = minus->GetPos() + minus->GetDir() * (m_ParticleLength * ep_minus * 2); prop_p.GetDir() = minus->GetDir(); } else no_proposal = true; if (!no_proposal) { float cos = dot_product(prop_p.GetDir(), p->GetDir()); float p_rev = exp(-((prop_p.GetPos()-p->GetPos()).squared_magnitude() + (1-cos*cos))*m_Gamma)/m_Z; float ex_energy = m_EnergyComputer->ComputeExternalEnergy(prop_p.GetPos(),prop_p.GetDir(),p) - m_EnergyComputer->ComputeExternalEnergy(p->GetPos(),p->GetDir(),p); float in_energy = m_EnergyComputer->ComputeInternalEnergy(&prop_p) - m_EnergyComputer->ComputeInternalEnergy(p); float prob = exp(ex_energy/m_ExTemp+in_energy/m_InTemp)*m_ShiftProb*p_rev/(m_OptShiftProb+m_ShiftProb*p_rev); if (m_RandGen->GetVariate() < prob) { vnl_vector_fixed Rtmp = p->GetPos(); vnl_vector_fixed Ntmp = p->GetDir(); p->GetPos() = prop_p.GetPos(); p->GetDir() = prop_p.GetDir(); if (!m_ParticleGrid->TryUpdateGrid(pnum)) { p->GetPos() = Rtmp; p->GetDir() = Ntmp; } m_AcceptedProposals++; } } m_OptShiftTime.Stop(); } } // Connection Proposal else { if (m_ParticleGrid->m_NumParticles > 0) { m_ConnectionTime.Start(); int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; Particle *p = m_ParticleGrid->GetParticle(pnum); EndPoint P; P.p = p; P.ep = (m_RandGen->GetVariate() > 0.5)? 1 : -1; // direction of the new tract RemoveAndSaveTrack(P); // remove old tract and save it for later if (m_BackupTrack.m_Probability != 0) { MakeTrackProposal(P); // propose new tract starting from P float prob = (m_ProposalTrack.m_Energy-m_BackupTrack.m_Energy)/m_InTemp ; prob = exp(prob)*(m_BackupTrack.m_Probability * pow(m_DelProb,m_ProposalTrack.m_Length)) /(m_ProposalTrack.m_Probability * pow(m_DelProb,m_BackupTrack.m_Length)); if (m_RandGen->GetVariate() < prob) { ImplementTrack(m_ProposalTrack); // accept proposed tract m_AcceptedProposals++; } else { ImplementTrack(m_BackupTrack); // reject proposed tract and restore old one } } else ImplementTrack(m_BackupTrack); m_ConnectionTime.Stop(); } } } // establish connections between particles stored in input Track void MetropolisHastingsSampler::ImplementTrack(Track &T) { for (int k = 1; k < T.m_Length;k++) m_ParticleGrid->CreateConnection(T.track[k-1].p,T.track[k-1].ep,T.track[k].p,-T.track[k].ep); } // remove pending track from random particle, save it in m_BackupTrack and calculate its probability void MetropolisHastingsSampler::RemoveAndSaveTrack(EndPoint P) { EndPoint Current = P; int cnt = 0; float energy = 0; float AccumProb = 1.0; m_BackupTrack.track[cnt] = Current; EndPoint Next; for (;;) { Next.p = nullptr; if (Current.ep == 1) { if (Current.p->pID != -1) { Next.p = m_ParticleGrid->GetParticle(Current.p->pID); Current.p->pID = -1; m_ParticleGrid->m_NumConnections--; } } else if (Current.ep == -1) { if (Current.p->mID != -1) { Next.p = m_ParticleGrid->GetParticle(Current.p->mID); Current.p->mID = -1; m_ParticleGrid->m_NumConnections--; } } else { fprintf(stderr,"MetropolisHastingsSampler_randshift: Connection inconsistent 3\n"); break; } if (Next.p == nullptr) // no successor { Next.ep = 0; // mark as empty successor break; } else { if (Next.p->pID == Current.p->ID) { Next.p->pID = -1; Next.ep = 1; } else if (Next.p->mID == Current.p->ID) { Next.p->mID = -1; Next.ep = -1; } else { fprintf(stderr,"MetropolisHastingsSampler_randshift: Connection inconsistent 4\n"); break; } } ComputeEndPointProposalDistribution(Current); AccumProb *= (m_SimpSamp.probFor(Next)); if (Next.p == nullptr) // no successor -> break break; energy += m_EnergyComputer->ComputeInternalEnergyConnection(Current.p,Current.ep,Next.p,Next.ep); Current = Next; Current.ep *= -1; cnt++; m_BackupTrack.track[cnt] = Current; if (m_RandGen->GetVariate() > m_DelProb) break; } m_BackupTrack.m_Energy = energy; m_BackupTrack.m_Probability = AccumProb; m_BackupTrack.m_Length = cnt+1; } // generate new track using kind of a local tracking starting from P in the given direction, store it in m_ProposalTrack and calculate its probability void MetropolisHastingsSampler::MakeTrackProposal(EndPoint P) { EndPoint Current = P; int cnt = 0; float energy = 0; float AccumProb = 1.0; m_ProposalTrack.track[cnt++] = Current; Current.p->label = 1; for (;;) { // next candidate is already connected if ((Current.ep == 1 && Current.p->pID != -1) || (Current.ep == -1 && Current.p->mID != -1)) break; // track too long // if (cnt > 250) // break; ComputeEndPointProposalDistribution(Current); int k = m_SimpSamp.draw(m_RandGen->GetVariate()); // stop tracking proposed if (k==0) break; EndPoint Next = m_SimpSamp.objs[k]; float probability = m_SimpSamp.probFor(k); // accumulate energy and proposal distribution energy += m_EnergyComputer->ComputeInternalEnergyConnection(Current.p,Current.ep,Next.p,Next.ep); AccumProb *= probability; // track to next endpoint Current = Next; Current.ep *= -1; Current.p->label = 1; // put label to avoid loops m_ProposalTrack.track[cnt++] = Current; } m_ProposalTrack.m_Energy = energy; m_ProposalTrack.m_Probability = AccumProb; m_ProposalTrack.m_Length = cnt; // clear labels for (int j = 0; j < m_ProposalTrack.m_Length;j++) m_ProposalTrack.track[j].p->label = 0; } // get neigbouring particles of P and calculate the according connection probabilities void MetropolisHastingsSampler::ComputeEndPointProposalDistribution(EndPoint P) { Particle *p = P.p; int ep = P.ep; float dist,dot; vnl_vector_fixed R = p->GetPos() + (p->GetDir() * (ep*m_ParticleLength) ); m_ParticleGrid->ComputeNeighbors(R); m_SimpSamp.clear(); m_SimpSamp.add(m_StopProb,EndPoint(nullptr,0)); for (;;) { Particle *p2 = m_ParticleGrid->GetNextNeighbor(); if (p2 == nullptr) break; if (p!=p2 && p2->label == 0) { if (p2->mID == -1) { dist = (p2->GetPos() - p2->GetDir() * m_ParticleLength - R).squared_magnitude(); if (dist < m_DistanceThreshold) { dot = dot_product(p2->GetDir(),p->GetDir()) * ep; if (dot > m_CurvatureThreshold) { float en = m_EnergyComputer->ComputeInternalEnergyConnection(p,ep,p2,-1); m_SimpSamp.add(exp(en/m_TractProb),EndPoint(p2,-1)); } } } if (p2->pID == -1) { dist = (p2->GetPos() + p2->GetDir() * m_ParticleLength - R).squared_magnitude(); if (dist < m_DistanceThreshold) { dot = dot_product(p2->GetDir(),p->GetDir()) * (-ep); if (dot > m_CurvatureThreshold) { float en = m_EnergyComputer->ComputeInternalEnergyConnection(p,ep,p2,+1); m_SimpSamp.add(exp(en/m_TractProb),EndPoint(p2,+1)); } } } } } } // return number of accepted proposals int MetropolisHastingsSampler::GetNumAcceptedProposals() { return m_AcceptedProposals; } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateDirectionImagesFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateDirectionImagesFilter.cpp index d948ae2439..ff1c0e8c53 100755 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateDirectionImagesFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateDirectionImagesFilter.cpp @@ -1,372 +1,369 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkEvaluateDirectionImagesFilter_cpp #define __itkEvaluateDirectionImagesFilter_cpp #include "itkEvaluateDirectionImagesFilter.h" #include #include #include #include -#define _USE_MATH_DEFINES -#include - namespace itk { template< class PixelType > EvaluateDirectionImagesFilter< PixelType > ::EvaluateDirectionImagesFilter(): m_ImageSet(nullptr), m_ReferenceImageSet(nullptr), m_IgnoreMissingDirections(false), m_IgnoreEmptyVoxels(false), m_Eps(0.0001) { this->SetNumberOfIndexedOutputs(2); } template< class PixelType > void EvaluateDirectionImagesFilter< PixelType >::GenerateData() { if (m_ImageSet.IsNull() || m_ReferenceImageSet.IsNull()) return; DirectionImageContainerType::Pointer set1 = DirectionImageContainerType::New(); DirectionImageContainerType::Pointer set2 = DirectionImageContainerType::New(); for (unsigned int i=0; iSize(); i++) { typename itk::ImageDuplicator< DirectionImageType >::Pointer duplicator = itk::ImageDuplicator< DirectionImageType >::New(); duplicator->SetInputImage( m_ImageSet->GetElement(i) ); duplicator->Update(); set1->InsertElement(i, dynamic_cast(duplicator->GetOutput())); } for (unsigned int i=0; iSize(); i++) { typename itk::ImageDuplicator< DirectionImageType >::Pointer duplicator = itk::ImageDuplicator< DirectionImageType >::New(); duplicator->SetInputImage( m_ReferenceImageSet->GetElement(i) ); duplicator->Update(); set2->InsertElement(i, dynamic_cast(duplicator->GetOutput())); } m_ImageSet = set1; m_ReferenceImageSet = set2; // angular error image typename OutputImageType::Pointer outputImage = OutputImageType::New(); outputImage->SetOrigin( m_ReferenceImageSet->GetElement(0)->GetOrigin() ); outputImage->SetRegions( m_ReferenceImageSet->GetElement(0)->GetLargestPossibleRegion() ); outputImage->SetSpacing( m_ReferenceImageSet->GetElement(0)->GetSpacing() ); outputImage->SetDirection( m_ReferenceImageSet->GetElement(0)->GetDirection() ); outputImage->Allocate(); outputImage->FillBuffer(0.0); this->SetNthOutput(0, outputImage); // length error image outputImage = OutputImageType::New(); outputImage->SetOrigin( m_ReferenceImageSet->GetElement(0)->GetOrigin() ); outputImage->SetRegions( m_ReferenceImageSet->GetElement(0)->GetLargestPossibleRegion() ); outputImage->SetSpacing( m_ReferenceImageSet->GetElement(0)->GetSpacing() ); outputImage->SetDirection( m_ReferenceImageSet->GetElement(0)->GetDirection() ); outputImage->Allocate(); outputImage->FillBuffer(0.0); this->SetNthOutput(1, outputImage); if (m_MaskImage.IsNull()) { m_MaskImage = UCharImageType::New(); m_MaskImage->SetOrigin( outputImage->GetOrigin() ); m_MaskImage->SetRegions( outputImage->GetLargestPossibleRegion() ); m_MaskImage->SetSpacing( outputImage->GetSpacing() ); m_MaskImage->SetDirection( outputImage->GetDirection() ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } m_MeanAngularError = 0.0; m_MedianAngularError = 0; m_MaxAngularError = 0.0; m_MinAngularError = itk::NumericTraits::max(); m_VarAngularError = 0.0; m_AngularErrorVector.clear(); m_MeanLengthError = 0.0; m_MedianLengthError = 0; m_MaxLengthError = 0.0; m_MinLengthError = itk::NumericTraits::max(); m_VarLengthError = 0.0; m_LengthErrorVector.clear(); if (m_ImageSet.IsNull() || m_ReferenceImageSet.IsNull()) return; outputImage = static_cast< OutputImageType* >(this->ProcessObject::GetOutput(0)); typename OutputImageType::Pointer outputImage2 = static_cast< OutputImageType* >(this->ProcessObject::GetOutput(1)); ImageRegionIterator< OutputImageType > oit(outputImage, outputImage->GetLargestPossibleRegion()); ImageRegionIterator< OutputImageType > oit2(outputImage2, outputImage2->GetLargestPossibleRegion()); ImageRegionIterator< UCharImageType > mit(m_MaskImage, m_MaskImage->GetLargestPossibleRegion()); int numTestImages = m_ImageSet->Size(); int numReferenceImages = m_ReferenceImageSet->Size(); int maxNumDirections = std::max(numReferenceImages, numTestImages); // matrix containing the angular error between the directions vnl_matrix< float > diffM; diffM.set_size(maxNumDirections, maxNumDirections); boost::progress_display disp(outputImage->GetLargestPossibleRegion().GetSize()[0]*outputImage->GetLargestPossibleRegion().GetSize()[1]*outputImage->GetLargestPossibleRegion().GetSize()[2]); while( !oit.IsAtEnd() ) { ++disp; if( mit.Get()!=1 ) { ++oit; ++oit2; ++mit; continue; } typename OutputImageType::IndexType index = oit.GetIndex(); float maxAngularError = 1.0; diffM.fill(10); // initialize with invalid error value // get number of valid directions (length > 0) int numRefDirs = 0; int numTestDirs = 0; std::vector< vnl_vector_fixed< PixelType, 3 > > testDirs; std::vector< vnl_vector_fixed< PixelType, 3 > > refDirs; for (int i=0; i refDir = m_ReferenceImageSet->GetElement(i)->GetPixel(index).GetVnlVector(); if (refDir.magnitude() > m_Eps ) { refDir.normalize(); refDirs.push_back(refDir); numRefDirs++; } } for (int i=0; i testDir = m_ImageSet->GetElement(i)->GetPixel(index).GetVnlVector(); if (testDir.magnitude() > m_Eps ) { testDir.normalize(); testDirs.push_back(testDir); numTestDirs++; } } if (m_IgnoreEmptyVoxels && (numRefDirs==0 || numTestDirs==0) ) { ++oit; ++oit2; ++mit; continue; } // i: index of reference direction // j: index of test direction for (int i=0; i refDir; if (i testDir; if (j1.0) diffM[i][j] = 1.0; } } float angularError = 0.0; float lengthError = 0.0; int counter = 0; vnl_matrix< float > diffM_copy = diffM; for (int k=0; k small error) for (int i=0; ierror && diffM[i][j]<2) // found valid error entry { error = diffM[i][j]; a = i; b = j; missingDir = false; } else if (diffM[i][j]<0 && error<0) // found missing direction { a = i; b = j; missingDir = true; } } if (a<0 || b<0 || (m_IgnoreMissingDirections && missingDir)) continue; // no more directions found if (a>=numRefDirs && b>=numTestDirs) { MITK_INFO << "ERROR: missing test and reference direction. should not be possible. check code."; continue; } // remove processed directions from error matrix diffM.set_row(a, 10.0); diffM.set_column(b, 10.0); if (a>=numRefDirs) // missing reference direction (find next closest) { for (int i=0; ierror) { error = diffM_copy[i][b]; a = i; } } else if (b>=numTestDirs) // missing test direction (find next closest) { for (int i=0; ierror) { error = diffM_copy[a][i]; b = i; } } float refLength = 0; float testLength = 1; if (a>=numRefDirs || b>=numTestDirs || error<0) error = 0; else { refLength = m_ReferenceImageSet->GetElement(a)->GetPixel(index).GetVnlVector().magnitude(); testLength = m_ImageSet->GetElement(b)->GetPixel(index).GetVnlVector().magnitude(); } m_LengthErrorVector.push_back( fabs(refLength-testLength) ); - m_AngularErrorVector.push_back( acos(error)*180.0/M_PI ); + m_AngularErrorVector.push_back( acos(error)*180.0/itk::Math::pi ); m_MeanAngularError += m_AngularErrorVector.back(); m_MeanLengthError += m_LengthErrorVector.back(); angularError += m_AngularErrorVector.back(); lengthError += m_LengthErrorVector.back(); counter++; } if (counter>0) { lengthError /= counter; angularError /= counter; } oit2.Set(lengthError); oit.Set(angularError); ++oit; ++oit2; ++mit; } std::sort( m_AngularErrorVector.begin(), m_AngularErrorVector.end() ); m_MeanAngularError /= m_AngularErrorVector.size(); // mean for (unsigned int i=0; im_MaxAngularError ) m_MaxAngularError = m_AngularErrorVector.at(i); if ( m_AngularErrorVector.at(i)1) { m_VarAngularError /= (m_AngularErrorVector.size()-1); // variance // median if (m_AngularErrorVector.size()%2 == 0) m_MedianAngularError = 0.5*( m_AngularErrorVector.at( m_AngularErrorVector.size()/2 ) + m_AngularErrorVector.at( m_AngularErrorVector.size()/2+1 ) ); else m_MedianAngularError = m_AngularErrorVector.at( (m_AngularErrorVector.size()+1)/2 ) ; } std::sort( m_LengthErrorVector.begin(), m_LengthErrorVector.end() ); m_MeanLengthError /= m_LengthErrorVector.size(); // mean for (unsigned int i=0; im_MaxLengthError ) m_MaxLengthError = m_LengthErrorVector.at(i); if ( m_LengthErrorVector.at(i)1) { m_VarLengthError /= (m_LengthErrorVector.size()-1); // variance // median if (m_LengthErrorVector.size()%2 == 0) m_MedianLengthError = 0.5*( m_LengthErrorVector.at( m_LengthErrorVector.size()/2 ) + m_LengthErrorVector.at( m_LengthErrorVector.size()/2+1 ) ); else m_MedianLengthError = m_LengthErrorVector.at( (m_LengthErrorVector.size()+1)/2 ) ; } } } #endif // __itkEvaluateDirectionImagesFilter_cpp diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateTractogramDirectionsFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateTractogramDirectionsFilter.cpp index e16a46d6df..5a6e0ea9d7 100755 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateTractogramDirectionsFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkEvaluateTractogramDirectionsFilter.cpp @@ -1,351 +1,348 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkEvaluateTractogramDirectionsFilter_cpp #define __itkEvaluateTractogramDirectionsFilter_cpp #include "itkEvaluateTractogramDirectionsFilter.h" #include #include #include #include -#define _USE_MATH_DEFINES -#include - namespace itk { template< class PixelType > EvaluateTractogramDirectionsFilter< PixelType > ::EvaluateTractogramDirectionsFilter(): m_ReferenceImageSet(nullptr), m_IgnoreMissingDirections(false), m_Eps(0.0001), m_UseInterpolation(false) { this->SetNumberOfOutputs(1); } template< class PixelType > itk::Vector EvaluateTractogramDirectionsFilter< PixelType >::GetItkVector(double point[3]) { itk::Vector itkVector; itkVector[0] = point[0]; itkVector[1] = point[1]; itkVector[2] = point[2]; return itkVector; } template< class PixelType > vnl_vector_fixed EvaluateTractogramDirectionsFilter< PixelType >::GetVnlVector(double point[3]) { vnl_vector_fixed vnlVector; vnlVector[0] = point[0]; vnlVector[1] = point[1]; vnlVector[2] = point[2]; return vnlVector; } template< class PixelType > vnl_vector_fixed EvaluateTractogramDirectionsFilter< PixelType >::GetVnlVector(Vector& vector) { vnl_vector_fixed vnlVector; vnlVector[0] = vector[0]; vnlVector[1] = vector[1]; vnlVector[2] = vector[2]; return vnlVector; } template< class PixelType > itk::Point EvaluateTractogramDirectionsFilter< PixelType >::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } template< class PixelType > void EvaluateTractogramDirectionsFilter< PixelType >::GenerateData() { if (m_Tractogram.IsNull() || m_ReferenceImageSet.IsNull()) return; if (m_UseInterpolation) MITK_INFO << "Using trilinear interpolation"; else MITK_INFO << "Using nearest neighbor interpolation"; if (m_IgnoreMissingDirections) MITK_INFO << "Ignoring missing directions"; else MITK_INFO << "Penalizing missing directions"; // angular error image typename OutputImageType::Pointer outputImage = OutputImageType::New(); outputImage->SetOrigin( m_ReferenceImageSet->GetElement(0)->GetOrigin() ); outputImage->SetRegions( m_ReferenceImageSet->GetElement(0)->GetLargestPossibleRegion() ); outputImage->SetSpacing( m_ReferenceImageSet->GetElement(0)->GetSpacing() ); outputImage->SetDirection( m_ReferenceImageSet->GetElement(0)->GetDirection() ); outputImage->Allocate(); outputImage->FillBuffer(0.0); DoubleImageType::Pointer counterImage = DoubleImageType::New(); counterImage->SetOrigin( m_ReferenceImageSet->GetElement(0)->GetOrigin() ); counterImage->SetRegions( m_ReferenceImageSet->GetElement(0)->GetLargestPossibleRegion() ); counterImage->SetSpacing( m_ReferenceImageSet->GetElement(0)->GetSpacing() ); counterImage->SetDirection( m_ReferenceImageSet->GetElement(0)->GetDirection() ); counterImage->Allocate(); counterImage->FillBuffer(0.0); std::vector< DoubleImageType::Pointer > directionFound; for (int i=0; iSize(); i++) { DoubleImageType::Pointer check = DoubleImageType::New(); check->SetOrigin( m_ReferenceImageSet->GetElement(0)->GetOrigin() ); check->SetRegions( m_ReferenceImageSet->GetElement(0)->GetLargestPossibleRegion() ); check->SetSpacing( m_ReferenceImageSet->GetElement(0)->GetSpacing() ); check->SetDirection( m_ReferenceImageSet->GetElement(0)->GetDirection() ); check->Allocate(); check->FillBuffer(90); directionFound.push_back(check); } if (m_MaskImage.IsNull()) { m_MaskImage = UCharImageType::New(); m_MaskImage->SetOrigin( outputImage->GetOrigin() ); m_MaskImage->SetRegions( outputImage->GetLargestPossibleRegion() ); m_MaskImage->SetSpacing( outputImage->GetSpacing() ); m_MaskImage->SetDirection( outputImage->GetDirection() ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } m_MeanAngularError = 0.0; m_MedianAngularError = 0; m_MaxAngularError = 0.0; m_MinAngularError = itk::NumericTraits::max(); m_VarAngularError = 0.0; m_AngularErrorVector.clear(); float minSpacing = 1; if(outputImage->GetSpacing()[0]GetSpacing()[1] && outputImage->GetSpacing()[0]GetSpacing()[2]) minSpacing = outputImage->GetSpacing()[0]; else if (outputImage->GetSpacing()[1] < outputImage->GetSpacing()[2]) minSpacing = outputImage->GetSpacing()[1]; else minSpacing = outputImage->GetSpacing()[2]; FiberBundleType::Pointer fiberBundle = m_Tractogram->GetDeepCopy(); fiberBundle->ResampleFibers(minSpacing/10); MITK_INFO << "Evaluating tractogram"; vtkSmartPointer fiberPolyData = fiberBundle->GetFiberPolyData(); boost::progress_display disp( fiberBundle->GetNumFibers() ); for( int i=0; iGetNumFibers(); i++ ) { vtkCell* cell = fiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (numPoints<2) continue; for( int j=0; jGetPoint(j); itk::Point vertex = GetItkPoint(temp); itk::Vector v = GetItkVector(temp); itk::Vector dir(3); if (jGetPoint(j+1))-v; else dir = v-GetItkVector(points->GetPoint(j-1)); vnl_vector_fixed< PixelType, 3 > fiberDir = GetVnlVector(dir); fiberDir.normalize(); itk::Index<3> idx; itk::ContinuousIndex contIndex; m_MaskImage->TransformPhysicalPointToIndex(vertex, idx); m_MaskImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); if (!m_UseInterpolation) // use nearest neighbour interpolation { if (!m_MaskImage->GetLargestPossibleRegion().IsInside(idx) || m_MaskImage->GetPixel(idx)<=0) continue; double angle = 90; int usedIndex = -1; for (int k=0; kSize(); k++) // and each test direction { vnl_vector_fixed< PixelType, 3 > refDir = m_ReferenceImageSet->GetElement(k)->GetPixel(idx).GetVnlVector(); if (refDir.magnitude()>m_Eps) // normalize if not null refDir.normalize(); else continue; // calculate angle between directions - double tempAngle = acos(fabs(dot_product(refDir, fiberDir)))*180.0/M_PI; + double tempAngle = acos(fabs(dot_product(refDir, fiberDir)))*180.0/itk::Math::pi; directionFound.at(k)->SetPixel(idx, tempAngle); if (tempAngle < angle) { angle = tempAngle; usedIndex = k; } } if (usedIndex>=0) directionFound.at(usedIndex)->SetPixel(idx, -1); else if (m_IgnoreMissingDirections) angle = 0; counterImage->SetPixel(idx, counterImage->GetPixel(idx)+1); outputImage->SetPixel(idx, outputImage->GetPixel(idx)+angle); continue; } double frac_x = contIndex[0] - idx[0]; double frac_y = contIndex[1] - idx[1]; double frac_z = contIndex[2] - idx[2]; if (frac_x<0) { idx[0] -= 1; frac_x += 1; } if (frac_y<0) { idx[1] -= 1; frac_y += 1; } if (frac_z<0) { idx[2] -= 1; frac_z += 1; } // use trilinear interpolation itk::Index<3> newIdx; for (int x=0; x<2; x++) { frac_x = 1-frac_x; for (int y=0; y<2; y++) { frac_y = 1-frac_y; for (int z=0; z<2; z++) { frac_z = 1-frac_z; newIdx[0] = idx[0]+x; newIdx[1] = idx[1]+y; newIdx[2] = idx[2]+z; double frac = frac_x*frac_y*frac_z; // is position valid? if (!m_MaskImage->GetLargestPossibleRegion().IsInside(newIdx) || m_MaskImage->GetPixel(newIdx)<=0) continue; double angle = 90; int usedIndex = -1; for (int k=0; kSize(); k++) // and each test direction { vnl_vector_fixed< PixelType, 3 > refDir = m_ReferenceImageSet->GetElement(k)->GetPixel(newIdx).GetVnlVector(); if (refDir.magnitude()>m_Eps) // normalize if not null refDir.normalize(); else continue; // calculate angle between directions - double tempAngle = acos(fabs(dot_product(refDir, fiberDir)))*180.0/M_PI; + double tempAngle = acos(fabs(dot_product(refDir, fiberDir)))*180.0/itk::Math::pi; directionFound.at(k)->SetPixel(newIdx, tempAngle); if (tempAngle < angle) { angle = tempAngle; usedIndex = k; } } if (usedIndex>=0) directionFound.at(usedIndex)->SetPixel(newIdx, -1); else if (m_IgnoreMissingDirections) angle = 0; counterImage->SetPixel(newIdx, counterImage->GetPixel(newIdx)+1); outputImage->SetPixel(newIdx, outputImage->GetPixel(newIdx)+frac*angle); } } } } ++disp; } ImageRegionIterator< OutputImageType > oit(outputImage, outputImage->GetLargestPossibleRegion()); ImageRegionIterator< UCharImageType > mit(m_MaskImage, m_MaskImage->GetLargestPossibleRegion()); ImageRegionIterator< DoubleImageType > cit(counterImage, counterImage->GetLargestPossibleRegion()); if (!m_IgnoreMissingDirections) MITK_INFO << "Creatings statistics and accounting for missing directions"; else MITK_INFO << "Creatings statistics"; boost::progress_display disp2(outputImage->GetLargestPossibleRegion().GetNumberOfPixels()); while( !oit.IsAtEnd() ) { if ( mit.Get()>0) { if ( cit.Get()>m_Eps ) oit.Set(oit.Get()/cit.Get()); if (!m_IgnoreMissingDirections) { int missingCount = 0; if ( cit.Get()>m_Eps ) missingCount++; for (int i=0; iGetPixel(oit.GetIndex())>0 && m_ReferenceImageSet->GetElement(i)->GetPixel(oit.GetIndex()).GetVnlVector().magnitude()>m_Eps ) { oit.Set(oit.Get()+directionFound.at(i)->GetPixel(oit.GetIndex())); missingCount++; } } if (missingCount>1) oit.Set(oit.Get()/missingCount); } if (oit.Get()>m_MaxAngularError) m_MaxAngularError = oit.Get(); if (oit.Get()SetNthOutput(0, outputImage); } } #endif // __itkEvaluateTractogramDirectionsFilter_cpp diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberCurvatureFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberCurvatureFilter.cpp index d27f7b1b08..1437b8eddb 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberCurvatureFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberCurvatureFilter.cpp @@ -1,168 +1,166 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "itkFiberCurvatureFilter.h" -#define _USE_MATH_DEFINES -#include #include #include #include namespace itk{ FiberCurvatureFilter::FiberCurvatureFilter() : m_AngularDeviation(30) , m_Distance(10.0) , m_RemoveFibers(false) , m_UseMedian(false) { } FiberCurvatureFilter::~FiberCurvatureFilter() { } void FiberCurvatureFilter::GenerateData() { vtkSmartPointer inputPoly = m_InputFiberBundle->GetFiberPolyData(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); MITK_INFO << "Applying curvature threshold"; boost::progress_display disp(inputPoly->GetNumberOfCells()); #pragma omp parallel for for (int i=0; iGetNumberOfCells(); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; #pragma omp critical { ++disp; vtkCell* cell = inputPoly->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(j, p); vnl_vector_fixed< double, 3 > p_vec; p_vec[0]=p[0]; p_vec[1]=p[1]; p_vec[2]=p[2]; vertices.push_back(p_vec); } } // calculate curvatures int numPoints = vertices.size(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; j > vectors; vnl_vector_fixed< double, 3 > meanV; meanV.fill(0.0); while(dist1) { vnl_vector_fixed< double, 3 > p1 = vertices.at(c-1); vnl_vector_fixed< double, 3 > p2 = vertices.at(c); vnl_vector_fixed< double, 3 > v = p2-p1; dist += v.magnitude(); v.normalize(); vectors.push_back(v); if (m_UseMedian && c==j) meanV += v; else if (!m_UseMedian) meanV += v; c--; } c = j; dist = 0; while(dist p1 = vertices.at(c); vnl_vector_fixed< double, 3 > p2 = vertices.at(c+1); vnl_vector_fixed< double, 3 > v = p2-p1; dist += v.magnitude(); v.normalize(); vectors.push_back(v); if (m_UseMedian && c==j) meanV += v; else if (!m_UseMedian) meanV += v; c++; } meanV.normalize(); double dev = 0; for (auto vec : vectors) { double angle = dot_product(meanV, vec); if (angle>1.0) angle = 1.0; if (angle<-1.0) angle = -1.0; - dev += acos(angle)*180/M_PI; + dev += acos(angle)*180/itk::Math::pi; } if (vectors.size()>0) dev /= vectors.size(); if (devInsertNextPoint(vertices.at(j).data_block()); container->GetPointIds()->InsertNextId(id); } } else { if (m_RemoveFibers) { container = vtkSmartPointer::New(); break; } if (container->GetNumberOfPoints()>0) { #pragma omp critical vtkNewCells->InsertNextCell(container); } container = vtkSmartPointer::New(); } } #pragma omp critical { if (container->GetNumberOfPoints()>0) vtkNewCells->InsertNextCell(container); } } vtkSmartPointer outputPoly = vtkSmartPointer::New(); outputPoly->SetPoints(vtkNewPoints); outputPoly->SetLines(vtkNewCells); m_OutputFiberBundle = FiberBundle::New(outputPoly); } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkGibbsTrackingFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkGibbsTrackingFilter.cpp index ecb21bc38e..d31b7d25c2 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkGibbsTrackingFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkGibbsTrackingFilter.cpp @@ -1,516 +1,515 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "itkGibbsTrackingFilter.h" // MITK #include #include #include #include #include //#include #include #include // ITK #include #include #include // MISC #include // #include #include -#include #include #include #include namespace itk{ template< class ItkOdfImageType > GibbsTrackingFilter< ItkOdfImageType >::GibbsTrackingFilter(): m_StartTemperature(0.1), m_EndTemperature(0.001), m_Iterations(1e9), m_CurrentIteration(0.0), m_CurrentStep(0), m_ParticleWeight(0), m_ParticleWidth(0), m_ParticleLength(0), m_ConnectionPotential(10), m_InexBalance(0), m_ParticlePotential(0.2), m_MinFiberLength(10), m_AbortTracking(false), m_NumAcceptedFibers(0), m_BuildFibers(false), m_ProposalAcceptance(0), m_CurvatureThreshold(0.7), m_DuplicateImage(true), m_NumParticles(0), m_NumConnections(0), m_RandomSeed(-1), m_LoadParameterFile(""), m_LutPath(""), m_IsInValidState(true) { } template< class ItkOdfImageType > GibbsTrackingFilter< ItkOdfImageType >::~GibbsTrackingFilter() { } // fill output fiber bundle datastructure template< class ItkOdfImageType > typename GibbsTrackingFilter< ItkOdfImageType >::FiberPolyDataType GibbsTrackingFilter< ItkOdfImageType >::GetFiberBundle() { if (!m_AbortTracking) { m_BuildFibers = true; while (m_BuildFibers){} } return m_FiberPolyData; } template< class ItkOdfImageType > void GibbsTrackingFilter< ItkOdfImageType > ::EstimateParticleWeight() { MITK_INFO << "GibbsTrackingFilter: estimating particle weight"; float minSpacing; if(m_OdfImage->GetSpacing()[0]GetSpacing()[1] && m_OdfImage->GetSpacing()[0]GetSpacing()[2]) minSpacing = m_OdfImage->GetSpacing()[0]; else if (m_OdfImage->GetSpacing()[1] < m_OdfImage->GetSpacing()[2]) minSpacing = m_OdfImage->GetSpacing()[1]; else minSpacing = m_OdfImage->GetSpacing()[2]; float m_ParticleLength = 1.5*minSpacing; float m_ParticleWidth = 0.5*minSpacing; // seed random generators Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New(); if (m_RandomSeed>-1) randGen->SetSeed(m_RandomSeed); else randGen->SetSeed(); // instantiate all necessary components SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath); // handle lookup table not found cases if( !interpolator->IsInValidState() ) { m_IsInValidState = false; m_AbortTracking = true; m_BuildFibers = false; mitkThrow() << "Unable to load lookup tables."; } ParticleGrid* particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength, m_ParticleGridCellCapacity); GibbsEnergyComputer* encomp = new GibbsEnergyComputer(m_OdfImage, m_MaskImage, particleGrid, interpolator, randGen); // EnergyComputer* encomp = new EnergyComputer(m_OdfImage, m_MaskImage, particleGrid, interpolator, randGen); MetropolisHastingsSampler* sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold); float alpha = log(m_EndTemperature/m_StartTemperature); m_ParticleWeight = 0.01; int ppv = 0; // main loop int neededParts = 3000; while (ppvSetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential); for( int step = 0; step < 10; step++ ) { // update temperatur for simulated annealing process float temperature = m_StartTemperature * exp(alpha*(((1.0)*step)/((1.0)*10))); sampler->SetTemperature(temperature); for (unsigned long i=0; i<10000; i++) sampler->MakeProposal(); } ppv = particleGrid->m_NumParticles; particleGrid->ResetGrid(); } delete sampler; delete encomp; delete particleGrid; delete interpolator; MITK_INFO << "GibbsTrackingFilter: finished estimating particle weight"; } // perform global tracking template< class ItkOdfImageType > void GibbsTrackingFilter< ItkOdfImageType >::GenerateData() { TimeProbe preClock; preClock.Start(); // check if input is Odf or tensor image and generate Odf if necessary if (m_OdfImage.IsNull() && m_TensorImage.IsNotNull()) { TensorImageToOdfImageFilter::Pointer filter = TensorImageToOdfImageFilter::New(); filter->SetInput( m_TensorImage ); filter->Update(); m_OdfImage = filter->GetOutput(); } else if (m_DuplicateImage) // generate local working copy of Odf image (if not disabled) { typedef itk::ImageDuplicator< ItkOdfImageType > DuplicateFilterType; typename DuplicateFilterType::Pointer duplicator = DuplicateFilterType::New(); duplicator->SetInputImage( m_OdfImage ); duplicator->Update(); m_OdfImage = duplicator->GetOutput(); } // perform mean subtraction on odfs typedef ImageRegionIterator< ItkOdfImageType > InputIteratorType; InputIteratorType it(m_OdfImage, m_OdfImage->GetLargestPossibleRegion() ); it.GoToBegin(); while (!it.IsAtEnd()) { itk::OrientationDistributionFunction odf(it.Get().GetDataPointer()); float mean = odf.GetMeanValue(); odf -= mean; it.Set(odf.GetDataPointer()); ++it; } // check if mask image is given if it needs resampling PrepareMaskImage(); // load parameter file LoadParameters(); // prepare parameters float minSpacing; if(m_OdfImage->GetSpacing()[0]GetSpacing()[1] && m_OdfImage->GetSpacing()[0]GetSpacing()[2]) minSpacing = m_OdfImage->GetSpacing()[0]; else if (m_OdfImage->GetSpacing()[1] < m_OdfImage->GetSpacing()[2]) minSpacing = m_OdfImage->GetSpacing()[1]; else minSpacing = m_OdfImage->GetSpacing()[2]; if(m_ParticleLength == 0) m_ParticleLength = 1.5*minSpacing; if(m_ParticleWidth == 0) m_ParticleWidth = 0.5*minSpacing; if(m_ParticleWeight == 0) EstimateParticleWeight(); float alpha = log(m_EndTemperature/m_StartTemperature); if (m_CurvatureThreshold < mitk::eps) m_CurvatureThreshold = 0; // seed random generators Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New(); if (m_RandomSeed>-1) randGen->SetSeed(m_RandomSeed); else randGen->SetSeed(); // load sphere interpolator to evaluate the ODFs SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath); // handle lookup table not found cases if( !interpolator->IsInValidState() ) { m_IsInValidState = false; m_AbortTracking = true; m_BuildFibers = false; mitkThrow() << "Unable to load lookup tables."; } // initialize the actual tracking components (ParticleGrid, Metropolis Hastings Sampler and Energy Computer) ParticleGrid* particleGrid; GibbsEnergyComputer* encomp; MetropolisHastingsSampler* sampler; try{ particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength, m_ParticleGridCellCapacity); encomp = new GibbsEnergyComputer(m_OdfImage, m_MaskImage, particleGrid, interpolator, randGen); encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential); sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold); } catch(...) { MITK_ERROR << "Particle grid allocation failed. Not enough memory? Try to increase the particle length."; m_IsInValidState = false; m_AbortTracking = true; m_BuildFibers = false; return; } MITK_INFO << "----------------------------------------"; MITK_INFO << "Iterations: " << m_Iterations; MITK_INFO << "Particle length: " << m_ParticleLength; MITK_INFO << "Particle width: " << m_ParticleWidth; MITK_INFO << "Particle weight: " << m_ParticleWeight; MITK_INFO << "Start temperature: " << m_StartTemperature; MITK_INFO << "End temperature: " << m_EndTemperature; MITK_INFO << "In/Ex balance: " << m_InexBalance; MITK_INFO << "Min. fiber length: " << m_MinFiberLength; MITK_INFO << "Curvature threshold: " << m_CurvatureThreshold; MITK_INFO << "Random seed: " << m_RandomSeed; MITK_INFO << "----------------------------------------"; // main loop preClock.Stop(); TimeProbe clock; clock.Start(); m_NumAcceptedFibers = 0; m_CurrentIteration = 0; bool just_built_fibers = false; boost::progress_display disp(m_Iterations); if (!m_AbortTracking) while (m_CurrentIterationSetTemperature(temperature); sampler->MakeProposal(); m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/m_CurrentIteration; m_NumParticles = particleGrid->m_NumParticles; m_NumConnections = particleGrid->m_NumConnections; if (m_AbortTracking) break; if (m_BuildFibers) { FiberBuilder fiberBuilder(particleGrid, m_MaskImage); m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength); m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines(); m_BuildFibers = false; just_built_fibers = true; } } if (!just_built_fibers) { FiberBuilder fiberBuilder(particleGrid, m_MaskImage); m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength); m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines(); } clock.Stop(); delete sampler; delete encomp; delete interpolator; delete particleGrid; m_AbortTracking = true; m_BuildFibers = false; int h = clock.GetTotal()/3600; int m = ((int)clock.GetTotal()%3600)/60; int s = (int)clock.GetTotal()%60; MITK_INFO << "GibbsTrackingFilter: finished gibbs tracking in " << h << "h, " << m << "m and " << s << "s"; m = (int)preClock.GetTotal()/60; s = (int)preClock.GetTotal()%60; MITK_INFO << "GibbsTrackingFilter: preparation of the data took " << m << "m and " << s << "s"; MITK_INFO << "GibbsTrackingFilter: " << m_NumAcceptedFibers << " fibers accepted"; // sampler->PrintProposalTimes(); SaveParameters(); } template< class ItkOdfImageType > void GibbsTrackingFilter< ItkOdfImageType >::PrepareMaskImage() { if(m_MaskImage.IsNull()) { MITK_INFO << "GibbsTrackingFilter: generating default mask image"; m_MaskImage = ItkFloatImageType::New(); m_MaskImage->SetSpacing( m_OdfImage->GetSpacing() ); m_MaskImage->SetOrigin( m_OdfImage->GetOrigin() ); m_MaskImage->SetDirection( m_OdfImage->GetDirection() ); m_MaskImage->SetRegions( m_OdfImage->GetLargestPossibleRegion() ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1.0); } else if ( m_MaskImage->GetLargestPossibleRegion().GetSize()[0]!=m_OdfImage->GetLargestPossibleRegion().GetSize()[0] || m_MaskImage->GetLargestPossibleRegion().GetSize()[1]!=m_OdfImage->GetLargestPossibleRegion().GetSize()[1] || m_MaskImage->GetLargestPossibleRegion().GetSize()[2]!=m_OdfImage->GetLargestPossibleRegion().GetSize()[2] || m_MaskImage->GetSpacing()[0]!=m_OdfImage->GetSpacing()[0] || m_MaskImage->GetSpacing()[1]!=m_OdfImage->GetSpacing()[1] || m_MaskImage->GetSpacing()[2]!=m_OdfImage->GetSpacing()[2] ) { MITK_INFO << "GibbsTrackingFilter: resampling mask image"; typedef itk::ResampleImageFilter< ItkFloatImageType, ItkFloatImageType, float > ResamplerType; ResamplerType::Pointer resampler = ResamplerType::New(); resampler->SetOutputSpacing( m_OdfImage->GetSpacing() ); resampler->SetOutputOrigin( m_OdfImage->GetOrigin() ); resampler->SetOutputDirection( m_OdfImage->GetDirection() ); resampler->SetSize( m_OdfImage->GetLargestPossibleRegion().GetSize() ); resampler->SetInput( m_MaskImage ); resampler->SetDefaultPixelValue(0.0); resampler->Update(); m_MaskImage = resampler->GetOutput(); MITK_INFO << "GibbsTrackingFilter: resampling finished"; } } // load tracking paramters from xml file (.gtp) template< class ItkOdfImageType > bool GibbsTrackingFilter< ItkOdfImageType >::LoadParameters() { m_AbortTracking = true; try { if( m_LoadParameterFile.length()==0 ) { m_AbortTracking = false; return true; } MITK_INFO << "GibbsTrackingFilter: loading parameter file " << m_LoadParameterFile; TiXmlDocument doc( m_LoadParameterFile ); doc.LoadFile(); TiXmlHandle hDoc(&doc); TiXmlElement* pElem; TiXmlHandle hRoot(nullptr); pElem = hDoc.FirstChildElement().Element(); hRoot = TiXmlHandle(pElem); pElem = hRoot.FirstChildElement("parameter_set").Element(); std::string iterations(pElem->Attribute("iterations")); m_Iterations = boost::lexical_cast(iterations); std::string particleLength(pElem->Attribute("particle_length")); m_ParticleLength = boost::lexical_cast(particleLength); std::string particleWidth(pElem->Attribute("particle_width")); m_ParticleWidth = boost::lexical_cast(particleWidth); std::string partWeight(pElem->Attribute("particle_weight")); m_ParticleWeight = boost::lexical_cast(partWeight); std::string startTemp(pElem->Attribute("temp_start")); m_StartTemperature = boost::lexical_cast(startTemp); std::string endTemp(pElem->Attribute("temp_end")); m_EndTemperature = boost::lexical_cast(endTemp); std::string inExBalance(pElem->Attribute("inexbalance")); m_InexBalance = boost::lexical_cast(inExBalance); std::string fiberLength(pElem->Attribute("fiber_length")); m_MinFiberLength = boost::lexical_cast(fiberLength); std::string curvThres(pElem->Attribute("curvature_threshold")); - m_CurvatureThreshold = cos(boost::lexical_cast(curvThres)*M_PI/180); + m_CurvatureThreshold = cos(boost::lexical_cast(curvThres)*itk::Math::pi/180); m_AbortTracking = false; MITK_INFO << "GibbsTrackingFilter: parameter file loaded successfully"; return true; } catch(...) { MITK_INFO << "GibbsTrackingFilter: could not load parameter file"; return false; } } // save current tracking paramters to xml file (.gtp) template< class ItkOdfImageType > bool GibbsTrackingFilter< ItkOdfImageType >::SaveParameters() { try { if( m_SaveParameterFile.length()==0 ) { MITK_INFO << "GibbsTrackingFilter: no filename specified to save parameters"; return true; } MITK_INFO << "GibbsTrackingFilter: saving parameter file " << m_SaveParameterFile; TiXmlDocument documentXML; TiXmlDeclaration* declXML = new TiXmlDeclaration( "1.0", "", "" ); documentXML.LinkEndChild( declXML ); TiXmlElement* mainXML = new TiXmlElement("global_tracking_parameter_file"); mainXML->SetAttribute("file_version", "0.1"); documentXML.LinkEndChild(mainXML); TiXmlElement* paramXML = new TiXmlElement("parameter_set"); paramXML->SetAttribute("iterations", boost::lexical_cast(m_Iterations)); paramXML->SetAttribute("particle_length", boost::lexical_cast(m_ParticleLength)); paramXML->SetAttribute("particle_width", boost::lexical_cast(m_ParticleWidth)); paramXML->SetAttribute("particle_weight", boost::lexical_cast(m_ParticleWeight)); paramXML->SetAttribute("temp_start", boost::lexical_cast(m_StartTemperature)); paramXML->SetAttribute("temp_end", boost::lexical_cast(m_EndTemperature)); paramXML->SetAttribute("inexbalance", boost::lexical_cast(m_InexBalance)); paramXML->SetAttribute("fiber_length", boost::lexical_cast(m_MinFiberLength)); paramXML->SetAttribute("curvature_threshold", boost::lexical_cast(m_CurvatureThreshold)); mainXML->LinkEndChild(paramXML); if(!boost::algorithm::ends_with(m_SaveParameterFile, ".gtp")) m_SaveParameterFile.append(".gtp"); documentXML.SaveFile( m_SaveParameterFile ); MITK_INFO << "GibbsTrackingFilter: parameter file saved successfully"; return true; } catch(...) { MITK_INFO << "GibbsTrackingFilter: could not save parameter file"; return false; } } template< class ItkOdfImageType > void GibbsTrackingFilter< ItkOdfImageType >::SetDicomProperties(mitk::FiberBundle::Pointer fib) { std::string model_code_value = "-"; std::string model_code_meaning = "-"; std::string algo_code_value = "sup181_ee03"; std::string algo_code_meaning = "Global"; 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_code.value", mitk::StringProperty::New(algo_code_value)); fib->SetProperty("DICOM.algo_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/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp index 92df514819..8b5dbf5ee1 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp @@ -1,1026 +1,1023 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include #include #include #include "itkStreamlineTrackingFilter.h" #include #include #include #include #include "itkPointShell.h" #include #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - 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_MinVoxelSize(-1) , m_AngularThresholdDeg(-1) , m_StepSizeVox(-1) , m_SamplingDistanceVox(-1) , m_AngularThreshold(-1) , m_StepSize(0) , m_MaxLength(10000) , m_MinTractLength(20.0) , m_MaxTractLength(400.0) , m_SeedsPerVoxel(1) , m_AvoidStop(true) , m_RandomSampling(false) , m_SamplingDistance(-1) , m_DeflectionMod(1.0) , m_OnlyForwardSamples(true) , m_UseStopVotes(true) , m_NumberOfSamples(30) , m_NumPreviousDirections(1) , m_MaxNumTracts(-1) , m_Verbose(true) , m_LoopCheck(-1) , m_DemoMode(false) , m_Random(true) , m_UseOutputProbabilityMap(false) , m_CurrentTracts(0) , m_Progress(0) , m_StopTracking(false) , m_InterpolateMasks(true) , m_TrialsPerSeed(10) , m_EndpointConstraint(EndpointConstraints::NONE) , m_IntroduceDirectionsFromPrior(true) , m_TrackingPriorAsMask(true) , m_TrackingPriorWeight(1.0) , m_TrackingPriorHandler(nullptr) { this->SetNumberOfRequiredInputs(0); } std::string StreamlineTrackingFilter::GetStatusText() { std::string status = "Seedpoints processed: " + boost::lexical_cast(m_Progress) + "/" + boost::lexical_cast(m_SeedPoints.size()); if (m_SeedPoints.size()>0) status += " (" + boost::lexical_cast(100*m_Progress/m_SeedPoints.size()) + "%)"; if (m_MaxNumTracts>0) status += "\nFibers accepted: " + boost::lexical_cast(m_CurrentTracts) + "/" + boost::lexical_cast(m_MaxNumTracts); else status += "\nFibers accepted: " + boost::lexical_cast(m_CurrentTracts); return status; } void StreamlineTrackingFilter::BeforeTracking() { m_StopTracking = false; m_TrackingHandler->SetRandom(m_Random); m_TrackingHandler->InitForTracking(); m_FiberPolyData = PolyDataType::New(); m_Points = vtkSmartPointer< vtkPoints >::New(); m_Cells = vtkSmartPointer< vtkCellArray >::New(); itk::Vector< double, 3 > imageSpacing = m_TrackingHandler->GetSpacing(); if(imageSpacing[0]SetAngularThreshold(m_AngularThreshold); if (m_TrackingPriorHandler!=nullptr) { m_TrackingPriorHandler->SetRandom(m_Random); m_TrackingPriorHandler->InitForTracking(); m_TrackingPriorHandler->SetAngularThreshold(m_AngularThreshold); } if (m_SamplingDistanceVoxGetNumberOfThreads(); i++) { PolyDataType poly = PolyDataType::New(); m_PolyDataContainer.push_back(poly); } if (m_UseOutputProbabilityMap) { 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_EndpointConstraint==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_EndpointConstraint = EndpointConstraints::EPS_IN_SEED_AND_TARGET; } else if (m_EndpointConstraint==EndpointConstraints::NONE && m_TargetImageSet) { MITK_INFO << "No endpoint constraint chosen but target image set --> setting constraint to EPS_IN_TARGET"; m_EndpointConstraint = EndpointConstraints::EPS_IN_TARGET; } // Check if endpoint constraints are valid FiberType test_fib; itk::Point 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_TrackingHandler->GetMode()==mitk::TrackingDataHandler::MODE::DETERMINISTIC) std::cout << "StreamlineTracking - Mode: deterministic" << std::endl; else if(m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::MODE::PROBABILISTIC) { std::cout << "StreamlineTracking - Mode: probabilistic" << std::endl; std::cout << "StreamlineTracking - Trials per seed: " << m_TrialsPerSeed << std::endl; } else std::cout << "StreamlineTracking - Mode: ???" << std::endl; if (m_EndpointConstraint==EndpointConstraints::NONE) std::cout << "StreamlineTracking - Endpoint constraint: NONE" << std::endl; else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_TARGET" << std::endl; else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_TARGET_LABELDIFF" << std::endl; else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_SEED_AND_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_SEED_AND_TARGET" << std::endl; else if (m_EndpointConstraint==EndpointConstraints::MIN_ONE_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: MIN_ONE_EP_IN_TARGET" << std::endl; else if (m_EndpointConstraint==EndpointConstraints::ONE_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: ONE_EP_IN_TARGET" << std::endl; else if (m_EndpointConstraint==EndpointConstraints::NO_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: NO_EP_IN_TARGET" << std::endl; - std::cout << "StreamlineTracking - Angular threshold: " << m_AngularThreshold << " (" << 180*std::acos( m_AngularThreshold )/M_PI << "°)" << std::endl; + std::cout << "StreamlineTracking - Angular threshold: " << m_AngularThreshold << " (" << 180*std::acos( m_AngularThreshold )/itk::Math::pi << "°)" << std::endl; std::cout << "StreamlineTracking - Stepsize: " << m_StepSize << "mm (" << m_StepSize/m_MinVoxelSize << "*vox)" << std::endl; std::cout << "StreamlineTracking - Seeds per voxel: " << m_SeedsPerVoxel << std::endl; std::cout << "StreamlineTracking - Max. tract length: " << m_MaxTractLength << "mm" << std::endl; std::cout << "StreamlineTracking - Min. tract length: " << m_MinTractLength << "mm" << std::endl; std::cout << "StreamlineTracking - Max. num. tracts: " << m_MaxNumTracts << std::endl; std::cout << "StreamlineTracking - Loop check: " << m_LoopCheck << "°" << std::endl; std::cout << "StreamlineTracking - Num. neighborhood samples: " << m_NumberOfSamples << std::endl; std::cout << "StreamlineTracking - Max. sampling distance: " << m_SamplingDistance << "mm (" << m_SamplingDistance/m_MinVoxelSize << "*vox)" << std::endl; std::cout << "StreamlineTracking - Deflection modifier: " << m_DeflectionMod << std::endl; std::cout << "StreamlineTracking - Use stop votes: " << m_UseStopVotes << std::endl; std::cout << "StreamlineTracking - Only frontal samples: " << m_OnlyForwardSamples << std::endl; if (m_TrackingPriorHandler!=nullptr) std::cout << "StreamlineTracking - Using directional prior for tractography" << 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& pos, vnl_vector_fixed& dir) { pos[0] += dir[0]*m_StepSize; pos[1] += dir[1]*m_StepSize; pos[2] += dir[2]*m_StepSize; } std::vector< vnl_vector_fixed > StreamlineTrackingFilter::CreateDirections(int NPoints) { std::vector< vnl_vector_fixed > pointshell; if (NPoints<2) return pointshell; std::vector< float > theta; theta.resize(NPoints); std::vector< float > phi; phi.resize(NPoints); - float C = sqrt(4*M_PI); + float C = sqrt(4*itk::Math::pi); phi[0] = 0.0; phi[NPoints-1] = 0.0; for(int i=0; i0 && i d; d[0] = cos(theta[i]) * cos(phi[i]); d[1] = cos(theta[i]) * sin(phi[i]); d[2] = sin(theta[i]); pointshell.push_back(d); } return pointshell; } vnl_vector_fixed StreamlineTrackingFilter::GetNewDirection(const itk::Point &pos, std::deque >& olddirs, itk::Index<3> &oldIndex) { if (m_DemoMode) { m_SamplingPointset->Clear(); m_AlternativePointset->Clear(); m_StopVotePointset->Clear(); } vnl_vector_fixed direction; direction.fill(0); if (mitk::imv::IsInsideMask(pos, m_InterpolateMasks, m_MaskInterpolator) && !mitk::imv::IsInsideMask(pos, m_InterpolateMasks, 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 olddir = olddirs.back(); std::vector< vnl_vector_fixed > probeVecs = CreateDirections(m_NumberOfSamples); itk::Point sample_pos; int alternatives = 1; for (unsigned int i=0; i d; bool is_stop_voter = false; if (m_Random && m_RandomSampling) { d[0] = m_TrackingHandler->GetRandDouble(-0.5, 0.5); d[1] = m_TrackingHandler->GetRandDouble(-0.5, 0.5); d[2] = m_TrackingHandler->GetRandDouble(-0.5, 0.5); d.normalize(); d *= m_TrackingHandler->GetRandDouble(0,m_SamplingDistance); } else { d = probeVecs.at(i); float dot = dot_product(d, olddir); if (m_UseStopVotes && dot>0.7) { is_stop_voter = true; possible_stop_votes++; } else if (m_OnlyForwardSamples && dot<0) continue; d *= m_SamplingDistance; } sample_pos[0] = pos[0] + d[0]; sample_pos[1] = pos[1] + d[1]; sample_pos[2] = pos[2] + d[2]; vnl_vector_fixed tempDir; tempDir.fill(0.0); if (mitk::imv::IsInsideMask(sample_pos, m_InterpolateMasks, m_MaskInterpolator)) tempDir = m_TrackingHandler->ProposeDirection(sample_pos, olddirs, oldIndex); // sample neighborhood if (tempDir.magnitude()>mitk::eps) { direction += tempDir; if(m_DemoMode) m_SamplingPointset->InsertPoint(i, sample_pos); } else if (m_AvoidStop && olddir.magnitude()>0.5) // 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.0) // 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 tempDir; tempDir.fill(0.0); if (mitk::imv::IsInsideMask(sample_pos, m_InterpolateMasks, m_MaskInterpolator)) tempDir = m_TrackingHandler->ProposeDirection(sample_pos, olddirs, oldIndex); // sample neighborhood if (tempDir.magnitude()>mitk::eps) // are we back in the white matter? { direction += d * 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.001 && (possible_stop_votes==0 || (float)stop_votes/possible_stop_votes<0.5) ) { direction.normalize(); valid = true; } else direction.fill(0); if (m_TrackingPriorHandler!=nullptr && (m_IntroduceDirectionsFromPrior || valid)) { vnl_vector_fixed prior = m_TrackingPriorHandler->ProposeDirection(pos, olddirs, oldIndex); if (prior.magnitude()>0.001) { prior.normalize(); if (dot_product(prior,direction)<0) prior *= -1; direction = (1.0f-m_TrackingPriorWeight) * direction + m_TrackingPriorWeight * prior; direction.normalize(); } else if (m_TrackingPriorAsMask) direction.fill(0.0); } return direction; } float StreamlineTrackingFilter::FollowStreamline(itk::Point pos, vnl_vector_fixed dir, FiberType* fib, DirectionContainer* container, float tractLength, bool front, bool &exclude) { vnl_vector_fixed zero_dir; zero_dir.fill(0.0); std::deque< vnl_vector_fixed > last_dirs; for (unsigned int i=0; i oldIndex; m_TrackingHandler->WorldToIndex(pos, oldIndex); // get new position CalculateNewPosition(pos, dir); if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask(pos, m_InterpolateMasks, 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_StepSize; if (m_LoopCheck>=0 && CheckCurvature(container, front)>m_LoopCheck) return tractLength; if (tractLength>m_MaxTractLength) return tractLength; if (m_DemoMode && !m_UseOutputProbabilityMap) // 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_NumPreviousDirections) last_dirs.pop_front(); dir = GetNewDirection(pos, last_dirs, oldIndex); while (m_PauseTracking){} if (dir.magnitude()<0.0001) return tractLength; } return tractLength; } float StreamlineTrackingFilter::CheckCurvature(DirectionContainer* fib, bool front) { if (fib->size()<8) return 0; float m_Distance = std::max(m_MinVoxelSize*4, m_StepSize*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(distsize()-1) { dist += m_StepSize; vnl_vector_fixed< float, 3 > v = fib->at(c); vectors.push_back(v); meanV += v; c++; } } else { int c = fib->size()-1; while(dist=0) { dist += m_StepSize; vnl_vector_fixed< float, 3 > v = fib->at(c); vectors.push_back(v); meanV += v; c--; } } meanV.normalize(); for (unsigned int c=0; c1.0) angle = 1.0; - dev += acos(angle)*180/M_PI; + dev += acos(angle)*180/itk::Math::pi; } if (vectors.size()>0) dev /= vectors.size(); return dev; } 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 start; start[0] = index[0]; start[1] = index[1]; start[2] = index[2]; itk::Point worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint(start, worldPos); if ( mitk::imv::IsInsideMask(worldPos, m_InterpolateMasks, m_MaskInterpolator) ) { m_SeedPoints.push_back(worldPos); for (int s = 1; s < m_SeedsPerVoxel; s++) { start[0] = index[0] + m_TrackingHandler->GetRandDouble(-0.5, 0.5); start[1] = index[1] + m_TrackingHandler->GetRandDouble(-0.5, 0.5); start[2] = index[2] + m_TrackingHandler->GetRandDouble(-0.5, 0.5); itk::Point worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint(start, worldPos); m_SeedPoints.push_back(worldPos); } } } ++sit; } } void StreamlineTrackingFilter::GenerateData() { this->BeforeTracking(); if (m_Random) std::random_shuffle(m_SeedPoints.begin(), m_SeedPoints.end()); m_CurrentTracts = 0; int num_seeds = 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; #pragma omp parallel while (i=num_seeds || m_StopTracking) continue; else if (m_Verbose && i%print_interval==0) #pragma omp critical { m_Progress += print_interval; std::cout << " \r"; if (m_MaxNumTracts>0) std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << "/" << m_MaxNumTracts << '\r'; else std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << '\r'; cout.flush(); } const itk::Point worldPos = m_SeedPoints.at(temp_i); for (unsigned int trials=0; trials dir; dir.fill(0.0); std::deque< vnl_vector_fixed > olddirs; dir = GetNewDirection(worldPos, olddirs, zeroIndex) * 0.5f; bool exclude = false; if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask(worldPos, m_InterpolateMasks, m_ExclusionInterpolator)) exclude = true; bool success = false; if (dir.magnitude()>0.0001 && !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_MinTractLength && counter>=2 && !exclude) { #pragma omp critical if ( IsValidFiber(&fib) ) { if (!m_StopTracking) { if (!m_UseOutputProbabilityMap) m_Tractogram.push_back(fib); else FiberToProbmap(&fib); m_CurrentTracts++; success = true; } if (m_MaxNumTracts > 0 && m_CurrentTracts>=static_cast(m_MaxNumTracts)) { if (!m_StopTracking) { std::cout << " \r"; MITK_INFO << "Reconstructed maximum number of tracts (" << m_CurrentTracts << "). Stopping tractography."; } m_StopTracking = true; } } } } if (success || m_TrackingHandler->GetMode()!=mitk::TrackingDataHandler::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_EndpointConstraint==EndpointConstraints::NONE) { return true; } else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) && mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint EPS_IN_TARGET chosen!"; } else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) { if (m_TargetImageSet) { float v1 = mitk::imv::GetImageValue(fib->front(), false, m_TargetInterpolator); float v2 = mitk::imv::GetImageValue(fib->back(), false, m_TargetInterpolator); if ( v1>0.0 && v2>0.0 && v1!=v2 ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint EPS_IN_TARGET_LABELDIFF chosen!"; } else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_SEED_AND_TARGET) { if (m_TargetImageSet && m_SeedImageSet) { if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_SeedInterpolator) && mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) return true; if ( mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_SeedInterpolator) && mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, 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_EndpointConstraint==EndpointConstraints::MIN_ONE_EP_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) || mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint MIN_ONE_EP_IN_TARGET chosen!"; } else if (m_EndpointConstraint==EndpointConstraints::ONE_EP_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) && !mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) return true; if ( !mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) && mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint ONE_EP_IN_TARGET chosen!"; } else if (m_EndpointConstraint==EndpointConstraints::NO_EP_IN_TARGET) { if (m_TargetImageSet) { if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) || mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, 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::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); for (unsigned int i=0; i container = vtkSmartPointer::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; iSetPoints(vNewPoints); m_FiberPolyData->SetLines(vNewLines); m_BuildFibersFinished = true; } void StreamlineTrackingFilter::AfterTracking() { if (m_Verbose) std::cout << " \r"; if (!m_UseOutputProbabilityMap) { 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(m_EndTime - m_StartTime); std::chrono::minutes mm = std::chrono::duration_cast(m_EndTime - m_StartTime); std::chrono::seconds ss = std::chrono::duration_cast(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(); if (m_TrackingPriorHandler!=nullptr) delete m_TrackingPriorHandler; } 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_TrackingHandler->GetMode()==mitk::TrackingDataHandler::DETERMINISTIC && dynamic_cast(m_TrackingHandler) && !m_TrackingHandler->GetInterpolate()) { algo_code_value = "sup181_ee04"; algo_code_meaning = "FACT"; } else if (m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::DETERMINISTIC) { algo_code_value = "sup181_ee01"; algo_code_meaning = "Deterministic"; } else if (m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::PROBABILISTIC) { algo_code_value = "sup181_ee02"; algo_code_meaning = "Probabilistic"; } if (dynamic_cast(m_TrackingHandler) || (dynamic_cast(m_TrackingHandler) && dynamic_cast(m_TrackingHandler)->GetIsOdfFromTensor() ) ) { if ( dynamic_cast(m_TrackingHandler) && dynamic_cast(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*>(m_TrackingHandler) || dynamic_cast*>(m_TrackingHandler)) { model_code_value = "sup181_bb03"; model_code_meaning = "Model Free"; } else if (dynamic_cast(m_TrackingHandler)) { model_code_value = "-"; model_code_meaning = "ODF"; } else if (dynamic_cast(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_code.value", mitk::StringProperty::New(algo_code_value)); fib->SetProperty("DICOM.algo_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/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp index a32bb2b460..fbdcdeea30 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp @@ -1,394 +1,394 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include #include #include #include #include #include #include #include #include #include #include using namespace mitk; using namespace boost::math; template< class ScalarType > RawShModel< ScalarType >::RawShModel() : m_ShOrder(0) , m_ModelIndex(-1) , m_MaxNumKernels(1000) { this->m_RandGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); this->m_RandGen->SetSeed(); m_AdcRange.first = 0; m_AdcRange.second = 0.004; m_FaRange.first = 0; m_FaRange.second = 1; } template< class ScalarType > RawShModel< ScalarType >::~RawShModel() { } template< class ScalarType > void RawShModel< ScalarType >::Clear() { m_ShCoefficients.clear(); m_PrototypeMaxDirection.clear(); m_B0Signal.clear(); } template< class ScalarType > void RawShModel< ScalarType >::RandomModel() { m_ModelIndex = this->m_RandGen->GetIntegerVariate(m_B0Signal.size()-1); } template< class ScalarType > unsigned int RawShModel< ScalarType >::GetNumberOfKernels() { return m_B0Signal.size(); } template< class ScalarType > bool RawShModel< ScalarType >::SampleKernels(Image::Pointer diffImg, ItkUcharImageType::Pointer maskImage, TensorImageType::Pointer tensorImage, QballFilterType::CoefficientImageType::Pointer itkFeatureImage, ItkDoubleImageType::Pointer adcImage) { if (diffImg.IsNull()) return false; typedef itk::VectorImage ITKDiffusionImageType; ITKDiffusionImageType::Pointer itkVectorImagePointer = ITKDiffusionImageType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); const int shOrder = 2; if (tensorImage.IsNull()) { typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); filter->SetBValue(static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); filter->SetGradientImage( static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); } const int NumCoeffs = (shOrder*shOrder + shOrder + 2)/2 + shOrder; if (itkFeatureImage.IsNull()) { QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetBValue(static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); qballfilter->SetGradientImage( static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer ); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); } if (adcImage.IsNull()) { itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput(itkVectorImagePointer); adcFilter->SetGradientDirections(static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()); adcFilter->SetB_value(static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); adcFilter->Update(); adcImage = adcFilter->GetOutput(); } int b0Index = 0; for (unsigned int i=0; i( diffImg->GetProperty(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()->Size(); i++) if ( static_cast( diffImg->GetProperty(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()->GetElement(i).magnitude()<0.001 ) { b0Index = i; break; } double max = 0; { itk::ImageRegionIterator< itk::VectorImage< short, 3 > > it(itkVectorImagePointer, itkVectorImagePointer->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if (maskImage.IsNotNull() && maskImage->GetPixel(it.GetIndex())<=0) { ++it; continue; } if (it.Get()[b0Index]>max) max = it.Get()[b0Index]; ++it; } } MITK_INFO << "Sampling signal kernels."; unsigned int count = 0; itk::ImageRegionIterator< itk::Image< itk::DiffusionTensor3D< double >, 3 > > it(tensorImage, tensorImage->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { bool skipPixel = false; if (maskImage.IsNotNull() && maskImage->GetPixel(it.GetIndex())<=0) { ++it; continue; } for (unsigned int i=0; i( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()->Size(); i++) { if (itkVectorImagePointer->GetPixel(it.GetIndex())[i]!=itkVectorImagePointer->GetPixel(it.GetIndex())[i] || itkVectorImagePointer->GetPixel(it.GetIndex())[i]<=0 || itkVectorImagePointer->GetPixel(it.GetIndex())[i]>itkVectorImagePointer->GetPixel(it.GetIndex())[b0Index]) { skipPixel = true; break; } } if (skipPixel) { ++it; continue; } typedef itk::DiffusionTensor3D TensorType; TensorType::EigenValuesArrayType eigenvalues; TensorType::EigenVectorsMatrixType eigenvectors; TensorType tensor = it.Get(); double FA = tensor.GetFractionalAnisotropy(); double ADC = adcImage->GetPixel(it.GetIndex()); QballFilterType::CoefficientImageType::PixelType itkv = itkFeatureImage->GetPixel(it.GetIndex()); vnl_vector_fixed< double, NumCoeffs > coeffs; for (unsigned int c=0; cGetMaxNumKernels()>this->GetNumberOfKernels() && FA>m_FaRange.first && FAm_AdcRange.first && ADCSetShCoefficients( coeffs, (double)itkVectorImagePointer->GetPixel(it.GetIndex())[b0Index]/max )) { tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); itk::Vector dir; dir[0] = eigenvectors(2, 0); dir[1] = eigenvectors(2, 1); dir[2] = eigenvectors(2, 2); m_PrototypeMaxDirection.push_back(dir); count++; MITK_INFO << "KERNEL: " << it.GetIndex() << " (" << count << ")"; } } ++it; } if (m_ShCoefficients.size()>0) return true; return false; } // convert cartesian to spherical coordinates template< class ScalarType > vnl_matrix RawShModel::Cart2Sph(GradientListType &gradients ) { vnl_matrix sphCoords; sphCoords.set_size(gradients.size(), 2); sphCoords.fill(0.0); for (unsigned int i=0; i 0.0001 ) { gradients[i].Normalize(); sphCoords(i,0) = acos(gradients[i][2]); // theta sphCoords(i,1) = atan2(gradients[i][1], gradients[i][0]); // phi } } return sphCoords; } template< class ScalarType > vnl_matrix RawShModel< ScalarType >::SetFiberDirection(GradientType& fiberDirection) { RandomModel(); GradientType axis = itk::CrossProduct(fiberDirection, m_PrototypeMaxDirection.at(m_ModelIndex)); axis.Normalize(); vnl_quaternion rotation(axis.GetVnlVector(), acos(dot_product(fiberDirection.GetVnlVector(), m_PrototypeMaxDirection.at(m_ModelIndex).GetVnlVector()))); rotation.normalize(); GradientListType gradients; for (unsigned int i=0; im_GradientList.size(); i++) { GradientType dir = this->m_GradientList.at(i); if( dir.GetNorm() > 0.0001 ) { dir.Normalize(); vnl_vector_fixed< double, 3 > vnlDir = rotation.rotate(dir.GetVnlVector()); dir[0] = vnlDir[0]; dir[1] = vnlDir[1]; dir[2] = vnlDir[2]; dir.Normalize(); } gradients.push_back(dir); } return Cart2Sph( gradients ); } template< class ScalarType > bool RawShModel< ScalarType >::SetShCoefficients(vnl_vector< double > shCoefficients, double b0 ) { m_ShOrder = 2; while ( (m_ShOrder*m_ShOrder + m_ShOrder + 2)/2 + m_ShOrder <= shCoefficients.size() ) m_ShOrder += 2; m_ShOrder -= 2; m_ModelIndex = m_B0Signal.size(); m_B0Signal.push_back(b0); m_ShCoefficients.push_back(shCoefficients); // itk::OrientationDistributionFunction odf; // GradientListType gradients; // for (unsigned int i=0; im_GradientList ); // PixelType signal = SimulateMeasurement(); // int minDirIdx = 0; // double min = itk::NumericTraits::max(); // for (unsigned int i=0; ib0 || signal[i]<0) // { // MITK_INFO << "Corrupted signal value detected. Kernel rejected."; // m_B0Signal.pop_back(); // m_ShCoefficients.pop_back(); // return false; // } // if (signal[i]m_GradientList.at(minDirIdx); // maxDir.Normalize(); // m_PrototypeMaxDirection.push_back(maxDir); Cart2Sph( this->m_GradientList ); m_ModelIndex = -1; return true; } template< class ScalarType > ScalarType RawShModel< ScalarType >::SimulateMeasurement(unsigned int dir, GradientType& fiberDirection) { ScalarType signal = 0; if (m_ModelIndex==-1) RandomModel(); vnl_matrix sphCoords = this->SetFiberDirection(fiberDirection); if (dir>=this->m_GradientList.size()) return signal; int j, m; double mag, plm; if (this->m_GradientList[dir].GetNorm()>0.001) { j=0; for (int l=0; l<=static_cast(m_ShOrder); l=l+2) for (m=-l; m<=l; m++) { plm = legendre_p(l,abs(m),cos(sphCoords(dir,0))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; + mag = sqrt((double)(2*l+1)/(4.0*itk::Math::pi)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; double basis; if (m<0) basis = sqrt(2.0)*mag*cos(fabs((double)m)*sphCoords(dir,1)); else if (m==0) basis = mag; else basis = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(dir,1)); signal += m_ShCoefficients.at(m_ModelIndex)[j]*basis; j++; } } else signal = m_B0Signal.at(m_ModelIndex); m_ModelIndex = -1; return signal; } template< class ScalarType > typename RawShModel< ScalarType >::PixelType RawShModel< ScalarType >::SimulateMeasurement(GradientType& fiberDirection) { if (m_ModelIndex==-1) RandomModel(); PixelType signal; signal.SetSize(this->m_GradientList.size()); int M = this->m_GradientList.size(); int j, m; double mag, plm; vnl_matrix< double > shBasis; shBasis.set_size(M, m_ShCoefficients.at(m_ModelIndex).size()); shBasis.fill(0.0); vnl_matrix sphCoords = this->SetFiberDirection(fiberDirection); for (int p=0; pm_GradientList[p].GetNorm()>0.001) { j=0; for (int l=0; l<=static_cast(m_ShOrder); l=l+2) for (m=-l; m<=l; m++) { plm = legendre_p(l,abs(m),cos(sphCoords(p,0))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; + mag = sqrt((double)(2*l+1)/(4.0*itk::Math::pi)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; if (m<0) shBasis(p,j) = sqrt(2.0)*mag*cos(fabs((double)m)*sphCoords(p,1)); else if (m==0) shBasis(p,j) = mag; else shBasis(p,j) = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(p,1)); j++; } double val = 0; for (unsigned int cidx=0; cidx #include #include #include "itkDftImageFilter.h" #include #include #include -#define _USE_MATH_DEFINES -#include - namespace itk { template< class TPixelType > DftImageFilter< TPixelType > ::DftImageFilter() { this->SetNumberOfRequiredInputs( 1 ); } template< class TPixelType > void DftImageFilter< TPixelType > ::BeforeThreadedGenerateData() { } template< class TPixelType > void DftImageFilter< TPixelType > ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType) { typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); typedef ImageRegionConstIterator< InputImageType > InputIteratorType; typename InputImageType::Pointer inputImage = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); int szx = outputImage->GetLargestPossibleRegion().GetSize(0); int szy = outputImage->GetLargestPossibleRegion().GetSize(1); while( !oit.IsAtEnd() ) { float kx = oit.GetIndex()[0]; float ky = oit.GetIndex()[1]; if ((int)szx%2==1) kx -= (szx-1)/2; else kx -= szx/2; if ((int)szy%2==1) ky -= (szy-1)/2; else ky -= szy/2; vcl_complex s(0,0); InputIteratorType it(inputImage, inputImage->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { float x = it.GetIndex()[0]; float y = it.GetIndex()[1]; if ((int)szx%2==1) x -= (szx-1)/2; else x -= szx/2; if ((int)szy%2==1) y -= (szy-1)/2; else y -= szy/2; - s += it.Get() * exp( std::complex(0, -2 * M_PI * (kx*x/szx + ky*y/szy) ) ); + s += it.Get() * exp( std::complex(0, -2 * itk::Math::pi * (kx*x/szx + ky*y/szy) ) ); ++it; } oit.Set(s); ++oit; } } } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp index fd1e7cb3ad..4b71879e04 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp @@ -1,241 +1,238 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "itkFibersFromPlanarFiguresFilter.h" -#define _USE_MATH_DEFINES -#include - // MITK #include #include #include #include #include #include #include #include #include // ITK #include #include #include #include // MISC #include namespace itk{ FibersFromPlanarFiguresFilter::FibersFromPlanarFiguresFilter() { } FibersFromPlanarFiguresFilter::~FibersFromPlanarFiguresFilter() { } void FibersFromPlanarFiguresFilter::GeneratePoints() { Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New(); randGen->SetSeed((unsigned int)0); m_2DPoints.clear(); unsigned int count = 0; while (count < m_Parameters.m_Density) { mitk::Vector2D p; switch (m_Parameters.m_Distribution) { case FiberGenerationParameters::DISTRIBUTE_GAUSSIAN: p[0] = randGen->GetNormalVariate(0, m_Parameters.m_Variance); p[1] = randGen->GetNormalVariate(0, m_Parameters.m_Variance); break; default: p[0] = randGen->GetUniformVariate(-1, 1); p[1] = randGen->GetUniformVariate(-1, 1); } if (sqrt(p[0]*p[0]+p[1]*p[1]) <= 1) { m_2DPoints.push_back(p); count++; } } } void FibersFromPlanarFiguresFilter::GenerateData() { // check if enough fiducials are available for (unsigned int i=0; i m_VtkCellArray = vtkSmartPointer::New(); vtkSmartPointer m_VtkPoints = vtkSmartPointer::New(); std::vector< mitk::PlanarEllipse::Pointer > bundle = m_Parameters.m_Fiducials.at(i); std::vector< unsigned int > fliplist; if (i container = vtkSmartPointer::New(); mitk::PlanarEllipse::Pointer figure = bundle.at(0); mitk::Point2D p0 = figure->GetControlPoint(0); mitk::Point2D p1 = figure->GetControlPoint(1); mitk::Point2D p2 = figure->GetControlPoint(2); mitk::Point2D p3 = figure->GetControlPoint(3); double r1 = p0.EuclideanDistanceTo(p1); double r2 = p0.EuclideanDistanceTo(p2); mitk::Vector2D eDir = p1-p0; eDir.Normalize(); mitk::Vector2D tDir = p3-p0; tDir.Normalize(); // apply twist vnl_matrix_fixed tRot; tRot[0][0] = tDir[0]; tRot[1][1] = tRot[0][0]; tRot[1][0] = sin(acos(tRot[0][0])); tRot[0][1] = -tRot[1][0]; if (tDir[1]<0) tRot.inplace_transpose(); m_2DPoints[j].SetVnlVector(tRot*m_2DPoints[j].GetVnlVector()); // apply new ellipse shape vnl_vector_fixed< double, 2 > newP; newP[0] = m_2DPoints.at(j)[0]; newP[1] = m_2DPoints.at(j)[1]; double alpha = acos(eDir[0]); if (eDir[1]>0) - alpha = 2*M_PI-alpha; + alpha = 2*itk::Math::pi-alpha; vnl_matrix_fixed eRot; eRot[0][0] = cos(alpha); eRot[1][1] = eRot[0][0]; eRot[1][0] = sin(alpha); eRot[0][1] = -eRot[1][0]; newP = eRot*newP; newP[0] *= r1; newP[1] *= r2; newP = eRot.transpose()*newP; p0[0] += newP[0]; p0[1] += newP[1]; const mitk::PlaneGeometry* planeGeo = figure->GetPlaneGeometry(); mitk::Point3D w, wc; planeGeo->Map(p0, w); wc = figure->GetWorldControlPoint(0); vtkIdType id = m_VtkPoints->InsertNextPoint(w.GetDataPointer()); container->GetPointIds()->InsertNextId(id); vnl_vector_fixed< double, 3 > n = planeGeo->GetNormalVnl(); for (unsigned int k=1; kGetControlPoint(0); p1 = figure->GetControlPoint(1); p2 = figure->GetControlPoint(2); p3 = figure->GetControlPoint(3); r1 = p0.EuclideanDistanceTo(p1); r2 = p0.EuclideanDistanceTo(p2); eDir = p1-p0; eDir.Normalize(); mitk::Vector2D tDir2 = p3-p0; tDir2.Normalize(); mitk::Vector2D temp; temp.SetVnlVector(tRot.transpose() * tDir2.GetVnlVector()); // apply twist tRot[0][0] = tDir[0]*tDir2[0] + tDir[1]*tDir2[1]; tRot[1][1] = tRot[0][0]; tRot[1][0] = sin(acos(tRot[0][0])); tRot[0][1] = -tRot[1][0]; if (temp[1]<0) tRot.inplace_transpose(); m_2DPoints[j].SetVnlVector(tRot*m_2DPoints[j].GetVnlVector()); tDir = tDir2; // apply new ellipse shape newP[0] = m_2DPoints.at(j)[0]; newP[1] = m_2DPoints.at(j)[1]; // calculate normal mitk::PlaneGeometry* planeGeo = const_cast(figure->GetPlaneGeometry()); mitk::Vector3D perp = wc-planeGeo->ProjectPointOntoPlane(wc); perp.Normalize(); vnl_vector_fixed< double, 3 > n2 = planeGeo->GetNormalVnl(); wc = figure->GetWorldControlPoint(0); // is flip needed? if (dot_product(perp.GetVnlVector(),n2)>0 && dot_product(n,n2)<=0.00001) newP[0] *= -1; if (fliplist.at(k)>0) newP[0] *= -1; n = n2; alpha = acos(eDir[0]); if (eDir[1]>0) - alpha = 2*M_PI-alpha; + alpha = 2*itk::Math::pi-alpha; eRot[0][0] = cos(alpha); eRot[1][1] = eRot[0][0]; eRot[1][0] = sin(alpha); eRot[0][1] = -eRot[1][0]; newP = eRot*newP; newP[0] *= r1; newP[1] *= r2; newP = eRot.transpose()*newP; p0[0] += newP[0]; p0[1] += newP[1]; mitk::Point3D w; planeGeo->Map(p0, w); vtkIdType id = m_VtkPoints->InsertNextPoint(w.GetDataPointer()); container->GetPointIds()->InsertNextId(id); } m_VtkCellArray->InsertNextCell(container); } vtkSmartPointer fiberPolyData = vtkSmartPointer::New(); fiberPolyData->SetPoints(m_VtkPoints); fiberPolyData->SetLines(m_VtkCellArray); mitk::FiberBundle::Pointer mitkFiberBundle = mitk::FiberBundle::New(fiberPolyData); mitkFiberBundle->ResampleSpline(m_Parameters.m_Sampling, m_Parameters.m_Tension, m_Parameters.m_Continuity, m_Parameters.m_Bias); m_FiberBundles.push_back(mitkFiberBundle); } } } diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp index f3df3e4d13..32cd0a664c 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp @@ -1,383 +1,380 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 __itkKspaceImageFilter_txx #define __itkKspaceImageFilter_txx #include #include #include #include "itkKspaceImageFilter.h" #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - namespace itk { template< class ScalarType > KspaceImageFilter< ScalarType >::KspaceImageFilter() : m_Z(0) , m_UseConstantRandSeed(false) , m_SpikesPerSlice(0) , m_IsBaseline(true) { m_DiffusionGradientDirection.Fill(0.0); m_CoilPosition.Fill(0.0); m_FmapInterpolator = itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::New(); } template< class ScalarType > void KspaceImageFilter< ScalarType > ::BeforeThreadedGenerateData() { m_Spike = vcl_complex(0,0); m_SpikeLog = ""; typename OutputImageType::Pointer outputImage = OutputImageType::New(); itk::ImageRegion<2> region; region.SetSize(0, m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0)); region.SetSize(1, m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1)); outputImage->SetLargestPossibleRegion( region ); outputImage->SetBufferedRegion( region ); outputImage->SetRequestedRegion( region ); outputImage->Allocate(); outputImage->FillBuffer(m_Spike); m_KSpaceImage = InputImageType::New(); m_KSpaceImage->SetLargestPossibleRegion( region ); m_KSpaceImage->SetBufferedRegion( region ); m_KSpaceImage->SetRequestedRegion( region ); m_KSpaceImage->Allocate(); m_KSpaceImage->FillBuffer(0.0); m_Gamma = 42576000; // Gyromagnetic ratio in Hz/T (1.5T) if ( m_Parameters->m_SignalGen.m_EddyStrength>0 && m_DiffusionGradientDirection.GetNorm()>0.001) { m_DiffusionGradientDirection.Normalize(); m_DiffusionGradientDirection = m_DiffusionGradientDirection * m_Parameters->m_SignalGen.m_EddyStrength/1000 * m_Gamma; m_IsBaseline = false; } this->SetNthOutput(0, outputImage); for (int i=0; i<3; i++) for (int j=0; j<3; j++) m_Transform[i][j] = m_Parameters->m_SignalGen.m_ImageDirection[i][j] * m_Parameters->m_SignalGen.m_ImageSpacing[j]; float a = m_Parameters->m_SignalGen.m_ImageRegion.GetSize(0)*m_Parameters->m_SignalGen.m_ImageSpacing[0]; float b = m_Parameters->m_SignalGen.m_ImageRegion.GetSize(1)*m_Parameters->m_SignalGen.m_ImageSpacing[1]; float diagonal = sqrt(a*a+b*b)/1000; // image diagonal in m switch (m_Parameters->m_SignalGen.m_CoilSensitivityProfile) { case SignalGenerationParameters::COIL_CONSTANT: { m_CoilSensitivityFactor = 1; // same signal everywhere break; } case SignalGenerationParameters::COIL_LINEAR: { m_CoilSensitivityFactor = -1/diagonal; // about 50% of the signal in the image center remaining break; } case SignalGenerationParameters::COIL_EXPONENTIAL: { m_CoilSensitivityFactor = -log(0.1)/diagonal; // about 32% of the signal in the image center remaining break; } } switch (m_Parameters->m_SignalGen.m_AcquisitionType) { case SignalGenerationParameters::SingleShotEpi: m_ReadoutScheme = new mitk::SingleShotEpi(m_Parameters); break; case SignalGenerationParameters::SpinEcho: m_ReadoutScheme = new mitk::CartesianReadout(m_Parameters); break; default: m_ReadoutScheme = new mitk::SingleShotEpi(m_Parameters); } m_ReadoutScheme->AdjustEchoTime(); m_FmapInterpolator->SetInputImage(m_Parameters->m_SignalGen.m_FrequencyMap); } template< class ScalarType > float KspaceImageFilter< ScalarType >::CoilSensitivity(VectorType& pos) { // ************************************************************************* // Coil ring is moving with excited slice (FIX THIS SOMETIME) m_CoilPosition[2] = pos[2]; // ************************************************************************* switch (m_Parameters->m_SignalGen.m_CoilSensitivityProfile) { case SignalGenerationParameters::COIL_CONSTANT: return 1; case SignalGenerationParameters::COIL_LINEAR: { VectorType diff = pos-m_CoilPosition; float sens = diff.GetNorm()*m_CoilSensitivityFactor + 1; if (sens<0) sens = 0; return sens; } case SignalGenerationParameters::COIL_EXPONENTIAL: { VectorType diff = pos-m_CoilPosition; float dist = diff.GetNorm(); return std::exp(-dist*m_CoilSensitivityFactor); } default: return 1; } } template< class ScalarType > void KspaceImageFilter< ScalarType > ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType) { itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randGen->SetSeed(); if (m_UseConstantRandSeed) // always generate the same random numbers? { randGen->SetSeed(0); } else { randGen->SetSeed(); } typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); typedef ImageRegionConstIterator< InputImageType > InputIteratorType; float kxMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0); float kyMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1); float xMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(0); // scanner coverage in x-direction float yMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(1); // scanner coverage in y-direction float yMaxFov = yMax*m_Parameters->m_SignalGen.m_CroppingFactor; // actual FOV in y-direction (in x-direction FOV=xMax) float numPix = kxMax*kyMax; // Adjust noise variance since it is the intended variance in physical space and not in k-space: float noiseVar = m_Parameters->m_SignalGen.m_PartialFourier*m_Parameters->m_SignalGen.m_NoiseVariance/(kyMax*kxMax); while( !oit.IsAtEnd() ) { typename OutputImageType::IndexType out_idx = oit.GetIndex(); // time from maximum echo float t= m_ReadoutScheme->GetTimeFromMaxEcho(out_idx); // time passed since k-space readout started float tRead = m_ReadoutScheme->GetRedoutTime(out_idx); // time passes since application of the RF pulse float tRf = m_Parameters->m_SignalGen.m_tEcho+t; // calculate eddy current decay factor // (TODO: vielleicht umbauen dass hier die zeit vom letzten diffusionsgradienten an genommen wird. doku dann auch entsprechend anpassen.) float eddyDecay = 0; if ( m_Parameters->m_Misc.m_CheckAddEddyCurrentsBox && m_Parameters->m_SignalGen.m_EddyStrength>0) { eddyDecay = std::exp(-tRead/m_Parameters->m_SignalGen.m_Tau ); } // calcualte signal relaxation factors std::vector< float > relaxFactor; if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation) { for (unsigned int i=0; im_SignalGen.m_tInhom) * (1.0-std::exp(-(m_Parameters->m_SignalGen.m_tRep + tRf)/m_T1.at(i))) ); } } // get current k-space index (depends on the chosen k-space readout scheme) itk::Index< 2 > kIdx = m_ReadoutScheme->GetActualKspaceIndex(out_idx); // partial fourier bool pf = false; if (kIdx[1]>kyMax*m_Parameters->m_SignalGen.m_PartialFourier) pf = true; if (!pf) { // shift k for DFT: (0 -- N) --> (-N/2 -- N/2) float kx = kIdx[0]; float ky = kIdx[1]; if ((int)kxMax%2==1){ kx -= (kxMax-1)/2; } else{ kx -= kxMax/2; } if ((int)kyMax%2==1){ ky -= (kyMax-1)/2; } else{ ky -= kyMax/2; } // add ghosting by adding gradient delay induced offset if (out_idx[1]%2 == 1) kx -= m_Parameters->m_SignalGen.m_KspaceLineOffset; else kx += m_Parameters->m_SignalGen.m_KspaceLineOffset; vcl_complex s(0,0); InputIteratorType it(m_CompartmentImages.at(0), m_CompartmentImages.at(0)->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { typename InputImageType::IndexType input_idx = it.GetIndex(); float x = input_idx[0]; float y = input_idx[1]; if ((int)xMax%2==1){ x -= (xMax-1)/2; } else{ x -= xMax/2; } if ((int)yMax%2==1){ y -= (yMax-1)/2; } else{ y -= yMax/2; } VectorType pos; pos[0] = x; pos[1] = y; pos[2] = m_Z; pos = m_Transform*pos/1000; // vector from image center to current position (in meter) vcl_complex f(0, 0); // sum compartment signals and simulate relaxation for (unsigned int i=0; im_SignalGen.m_DoSimulateRelaxation) f += std::complex( m_CompartmentImages.at(i)->GetPixel(it.GetIndex()) * relaxFactor.at(i) * m_Parameters->m_SignalGen.m_SignalScale, 0); else f += std::complex( m_CompartmentImages.at(i)->GetPixel(it.GetIndex()) * m_Parameters->m_SignalGen.m_SignalScale, 0); if (m_Parameters->m_SignalGen.m_CoilSensitivityProfile!=SignalGenerationParameters::COIL_CONSTANT) f *= CoilSensitivity(pos); // simulate eddy currents and other distortions float omega = 0; // frequency offset if ( m_Parameters->m_SignalGen.m_EddyStrength>0 && m_Parameters->m_Misc.m_CheckAddEddyCurrentsBox && !m_IsBaseline) { omega += (m_DiffusionGradientDirection[0]*pos[0]+m_DiffusionGradientDirection[1]*pos[1]+m_DiffusionGradientDirection[2]*pos[2]) * eddyDecay; } if (m_Parameters->m_SignalGen.m_FrequencyMap.IsNotNull()) // simulate distortions { itk::Point point3D; itk::Image::IndexType index; index[0] = input_idx[0]; index[1] = input_idx[1]; index[2] = m_Zidx; if (m_Parameters->m_SignalGen.m_DoAddMotion) // we have to account for the head motion since this also moves our frequency map { m_Parameters->m_SignalGen.m_FrequencyMap->TransformIndexToPhysicalPoint(index, point3D); point3D = m_FiberBundle->TransformPoint( point3D.GetVnlVector(), -m_Rotation[0], -m_Rotation[1], -m_Rotation[2], -m_Translation[0], -m_Translation[1], -m_Translation[2] ); omega += mitk::imv::GetImageValue(point3D, true, m_FmapInterpolator); } else { omega += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(index); } } // if signal comes from outside FOV, mirror it back (wrap-around artifact - aliasing) if (y<-yMaxFov/2) y += yMaxFov; else if (y>=yMaxFov/2) y -= yMaxFov; // actual DFT term - s += f * std::exp( std::complex(0, 2 * M_PI * (kx*x/xMax + ky*y/yMaxFov + omega*t/1000 )) ); + s += f * std::exp( std::complex(0, 2 * itk::Math::pi * (kx*x/xMax + ky*y/yMaxFov + omega*t/1000 )) ); ++it; } s /= numPix; if (m_SpikesPerSlice>0 && sqrt(s.imag()*s.imag()+s.real()*s.real()) > sqrt(m_Spike.imag()*m_Spike.imag()+m_Spike.real()*m_Spike.real()) ) m_Spike = s; if (m_Parameters->m_SignalGen.m_NoiseVariance>0 && m_Parameters->m_Misc.m_CheckAddNoiseBox) s = vcl_complex(s.real()+randGen->GetNormalVariate(0,noiseVar), s.imag()+randGen->GetNormalVariate(0,noiseVar)); outputImage->SetPixel(kIdx, s); m_KSpaceImage->SetPixel(kIdx, sqrt(s.imag()*s.imag()+s.real()*s.real()) ); } ++oit; } } template< class ScalarType > void KspaceImageFilter< ScalarType > ::AfterThreadedGenerateData() { delete m_ReadoutScheme; typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); float kxMax = outputImage->GetLargestPossibleRegion().GetSize(0); // k-space size in x-direction float kyMax = outputImage->GetLargestPossibleRegion().GetSize(1); // k-space size in y-direction ImageRegionIterator< OutputImageType > oit(outputImage, outputImage->GetLargestPossibleRegion()); while( !oit.IsAtEnd() ) // use hermitian k-space symmetry to fill empty k-space parts resulting from partial fourier acquisition { itk::Index< 2 > kIdx; kIdx[0] = oit.GetIndex()[0]; kIdx[1] = oit.GetIndex()[1]; // reverse phase if (!m_Parameters->m_SignalGen.m_ReversePhase) kIdx[1] = kyMax-1-kIdx[1]; if (kIdx[1]>kyMax*m_Parameters->m_SignalGen.m_PartialFourier) { // reverse readout direction if (oit.GetIndex()[1]%2 == 1) kIdx[0] = kxMax-kIdx[0]-1; // calculate symmetric index itk::Index< 2 > kIdx2; kIdx2[0] = (int)(kxMax-kIdx[0]-(int)kxMax%2)%(int)kxMax; kIdx2[1] = (int)(kyMax-kIdx[1]-(int)kyMax%2)%(int)kyMax; // use complex conjugate of symmetric index value at current index vcl_complex s = outputImage->GetPixel(kIdx2); s = vcl_complex(s.real(), -s.imag()); outputImage->SetPixel(kIdx, s); m_KSpaceImage->SetPixel(kIdx, sqrt(s.imag()*s.imag()+s.real()*s.real()) ); } ++oit; } itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randGen->SetSeed(); if (m_UseConstantRandSeed) // always generate the same random numbers? randGen->SetSeed(0); else randGen->SetSeed(); m_Spike *= m_Parameters->m_SignalGen.m_SpikeAmplitude; itk::Index< 2 > spikeIdx; for (unsigned int i=0; iGetIntegerVariate()%(int)kxMax; spikeIdx[1] = randGen->GetIntegerVariate()%(int)kyMax; outputImage->SetPixel(spikeIdx, m_Spike); m_SpikeLog += "[" + boost::lexical_cast(spikeIdx[0]) + "," + boost::lexical_cast(spikeIdx[1]) + "," + boost::lexical_cast(m_Zidx) + "] Magnitude: " + boost::lexical_cast(m_Spike.real()) + "+" + boost::lexical_cast(m_Spike.imag()) + "i\n"; } } } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp index 6a1ca5117d..41827ad955 100755 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp @@ -1,1692 +1,1692 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "itkTractsToDWIImageFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace itk { template< class PixelType > TractsToDWIImageFilter< PixelType >::TractsToDWIImageFilter() : m_FiberBundle(nullptr) , m_StatusText("") , m_UseConstantRandSeed(false) , m_RandGen(itk::Statistics::MersenneTwisterRandomVariateGenerator::New()) { m_RandGen->SetSeed(); m_DoubleInterpolator = itk::LinearInterpolateImageFunction< ItkDoubleImgType, float >::New(); m_NullDir.Fill(0); } template< class PixelType > TractsToDWIImageFilter< PixelType >::~TractsToDWIImageFilter() { } template< class PixelType > TractsToDWIImageFilter< PixelType >::DoubleDwiType::Pointer TractsToDWIImageFilter< PixelType >:: SimulateKspaceAcquisition( std::vector< DoubleDwiType::Pointer >& compartment_images ) { unsigned int numFiberCompartments = m_Parameters.m_FiberModelList.size(); // create slice object ImageRegion<2> sliceRegion; sliceRegion.SetSize(0, m_WorkingImageRegion.GetSize()[0]); sliceRegion.SetSize(1, m_WorkingImageRegion.GetSize()[1]); Vector< double, 2 > sliceSpacing; sliceSpacing[0] = m_WorkingSpacing[0]; sliceSpacing[1] = m_WorkingSpacing[1]; DoubleDwiType::PixelType nullPix; nullPix.SetSize(compartment_images.at(0)->GetVectorLength()); nullPix.Fill(0.0); auto magnitudeDwiImage = DoubleDwiType::New(); magnitudeDwiImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); magnitudeDwiImage->SetOrigin( m_Parameters.m_SignalGen.m_ImageOrigin ); magnitudeDwiImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); magnitudeDwiImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); magnitudeDwiImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); magnitudeDwiImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); magnitudeDwiImage->SetVectorLength( compartment_images.at(0)->GetVectorLength() ); magnitudeDwiImage->Allocate(); magnitudeDwiImage->FillBuffer(nullPix); m_PhaseImage = DoubleDwiType::New(); m_PhaseImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); m_PhaseImage->SetOrigin( m_Parameters.m_SignalGen.m_ImageOrigin ); m_PhaseImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_PhaseImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_PhaseImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_PhaseImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_PhaseImage->SetVectorLength( compartment_images.at(0)->GetVectorLength() ); m_PhaseImage->Allocate(); m_PhaseImage->FillBuffer(nullPix); m_KspaceImage = DoubleDwiType::New(); m_KspaceImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); m_KspaceImage->SetOrigin( m_Parameters.m_SignalGen.m_ImageOrigin ); m_KspaceImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_KspaceImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_KspaceImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_KspaceImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_KspaceImage->SetVectorLength( m_Parameters.m_SignalGen.m_NumberOfCoils ); m_KspaceImage->Allocate(); m_KspaceImage->FillBuffer(nullPix); std::vector< unsigned int > spikeVolume; for (unsigned int i=0; iGetIntegerVariate()%(compartment_images.at(0)->GetVectorLength())); std::sort (spikeVolume.begin(), spikeVolume.end()); std::reverse (spikeVolume.begin(), spikeVolume.end()); // calculate coil positions double a = m_Parameters.m_SignalGen.m_ImageRegion.GetSize(0)*m_Parameters.m_SignalGen.m_ImageSpacing[0]; double b = m_Parameters.m_SignalGen.m_ImageRegion.GetSize(1)*m_Parameters.m_SignalGen.m_ImageSpacing[1]; double c = m_Parameters.m_SignalGen.m_ImageRegion.GetSize(2)*m_Parameters.m_SignalGen.m_ImageSpacing[2]; double diagonal = sqrt(a*a+b*b)/1000; // image diagonal in m m_CoilPointset = mitk::PointSet::New(); std::vector< itk::Vector > coilPositions; itk::Vector pos; pos.Fill(0.0); pos[1] = -diagonal/2; itk::Vector center; center[0] = a/2-m_Parameters.m_SignalGen.m_ImageSpacing[0]/2; center[1] = b/2-m_Parameters.m_SignalGen.m_ImageSpacing[2]/2; center[2] = c/2-m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; for (int c=0; cInsertPoint(c, pos*1000 + m_Parameters.m_SignalGen.m_ImageOrigin.GetVectorFromOrigin() + center ); - double rz = 360.0/m_Parameters.m_SignalGen.m_NumberOfCoils * M_PI/180; + double rz = 360.0/m_Parameters.m_SignalGen.m_NumberOfCoils * itk::Math::pi/180; vnl_matrix_fixed< double, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; pos.SetVnlVector(rotZ*pos.GetVnlVector()); } PrintToLog("0% 10 20 30 40 50 60 70 80 90 100%", false, true, false); PrintToLog("|----|----|----|----|----|----|----|----|----|----|\n*", false, false, false); unsigned long lastTick = 0; boost::progress_display disp(compartment_images.at(0)->GetVectorLength()*compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2)); #pragma omp parallel for for (int g=0; g<(int)compartment_images.at(0)->GetVectorLength(); g++) { if (this->GetAbortGenerateData()) continue; std::vector< unsigned int > spikeSlice; #pragma omp critical while (!spikeVolume.empty() && (int)spikeVolume.back()==g) { spikeSlice.push_back(m_RandGen->GetIntegerVariate()%compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2)); spikeVolume.pop_back(); } std::sort (spikeSlice.begin(), spikeSlice.end()); std::reverse (spikeSlice.begin(), spikeSlice.end()); for (unsigned int z=0; zGetLargestPossibleRegion().GetSize(2); z++) { std::vector< Float2DImageType::Pointer > compartment_slices; std::vector< float > t2Vector; std::vector< float > t1Vector; for (unsigned int i=0; i* signalModel; if (iSetLargestPossibleRegion( sliceRegion ); slice->SetBufferedRegion( sliceRegion ); slice->SetRequestedRegion( sliceRegion ); slice->SetSpacing(sliceSpacing); slice->Allocate(); slice->FillBuffer(0.0); // extract slice from channel g for (unsigned int y=0; yGetLargestPossibleRegion().GetSize(1); y++) for (unsigned int x=0; xGetLargestPossibleRegion().GetSize(0); x++) { Float2DImageType::IndexType index2D; index2D[0]=x; index2D[1]=y; DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; slice->SetPixel(index2D, compartment_images.at(i)->GetPixel(index3D)[g]); } compartment_slices.push_back(slice); t2Vector.push_back(signalModel->GetT2()); t1Vector.push_back(signalModel->GetT1()); } int numSpikes = 0; while (!spikeSlice.empty() && spikeSlice.back()==z) { numSpikes++; spikeSlice.pop_back(); } int spikeCoil = m_RandGen->GetIntegerVariate()%m_Parameters.m_SignalGen.m_NumberOfCoils; if (this->GetAbortGenerateData()) continue; for (int c=0; c::New(); idft->SetCompartmentImages(compartment_slices); idft->SetT2(t2Vector); idft->SetT1(t1Vector); idft->SetUseConstantRandSeed(m_UseConstantRandSeed); idft->SetParameters(&m_Parameters); idft->SetZ((float)z-(float)( compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2) -compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2)%2 ) / 2.0); idft->SetZidx(z); idft->SetCoilPosition(coilPositions.at(c)); idft->SetFiberBundle(m_FiberBundleWorkingCopy); idft->SetTranslation(m_Translations.at(g)); idft->SetRotation(m_Rotations.at(g)); idft->SetDiffusionGradientDirection(m_Parameters.m_SignalGen.GetGradientDirection(g)); if (c==spikeCoil) idft->SetSpikesPerSlice(numSpikes); idft->Update(); #pragma omp critical if (c==spikeCoil && numSpikes>0) { m_SpikeLog += "Volume " + boost::lexical_cast(g) + " Coil " + boost::lexical_cast(c) + "\n"; m_SpikeLog += idft->GetSpikeLog(); } Complex2DImageType::Pointer fSlice; fSlice = idft->GetOutput(); // fourier transform slice Complex2DImageType::Pointer newSlice; auto dft = itk::DftImageFilter< Float2DImageType::PixelType >::New(); dft->SetInput(fSlice); dft->SetParameters(m_Parameters); dft->Update(); newSlice = dft->GetOutput(); // put slice back into channel g for (unsigned int y=0; yGetLargestPossibleRegion().GetSize(1); y++) for (unsigned int x=0; xGetLargestPossibleRegion().GetSize(0); x++) { DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; Complex2DImageType::IndexType index2D; index2D[0]=x; index2D[1]=y; Complex2DImageType::PixelType cPix = newSlice->GetPixel(index2D); double magn = sqrt(cPix.real()*cPix.real()+cPix.imag()*cPix.imag()); double phase = 0; if (cPix.real()!=0) phase = atan( cPix.imag()/cPix.real() ); DoubleDwiType::PixelType dwiPix = magnitudeDwiImage->GetPixel(index3D); DoubleDwiType::PixelType phasePix = m_PhaseImage->GetPixel(index3D); if (m_Parameters.m_SignalGen.m_NumberOfCoils>1) { dwiPix[g] += magn*magn; phasePix[g] += phase*phase; } else { dwiPix[g] = magn; phasePix[g] = phase; } //#pragma omp critical { magnitudeDwiImage->SetPixel(index3D, dwiPix); m_PhaseImage->SetPixel(index3D, phasePix); // k-space image if (g==0) { DoubleDwiType::PixelType kspacePix = m_KspaceImage->GetPixel(index3D); kspacePix[c] = idft->GetKSpaceImage()->GetPixel(index2D); m_KspaceImage->SetPixel(index3D, kspacePix); } } } } if (m_Parameters.m_SignalGen.m_NumberOfCoils>1) { for (int y=0; y(magnitudeDwiImage->GetLargestPossibleRegion().GetSize(1)); y++) for (int x=0; x(magnitudeDwiImage->GetLargestPossibleRegion().GetSize(0)); x++) { DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; DoubleDwiType::PixelType magPix = magnitudeDwiImage->GetPixel(index3D); magPix[g] = sqrt(magPix[g]/m_Parameters.m_SignalGen.m_NumberOfCoils); DoubleDwiType::PixelType phasePix = m_PhaseImage->GetPixel(index3D); phasePix[g] = sqrt(phasePix[g]/m_Parameters.m_SignalGen.m_NumberOfCoils); //#pragma omp critical { magnitudeDwiImage->SetPixel(index3D, magPix); m_PhaseImage->SetPixel(index3D, phasePix); } } } ++disp; unsigned long newTick = 50*disp.count()/disp.expected_count(); for (unsigned long tick = 0; tick<(newTick-lastTick); tick++) PrintToLog("*", false, false, false); lastTick = newTick; } } PrintToLog("\n", false); return magnitudeDwiImage; } template< class PixelType > TractsToDWIImageFilter< PixelType >::ItkDoubleImgType::Pointer TractsToDWIImageFilter< PixelType >:: NormalizeInsideMask(ItkDoubleImgType::Pointer image) { double max = itk::NumericTraits< double >::min(); double min = itk::NumericTraits< double >::max(); itk::ImageRegionIterator< ItkDoubleImgType > it(image, image->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if (m_Parameters.m_SignalGen.m_MaskImage.IsNotNull() && m_Parameters.m_SignalGen.m_MaskImage->GetPixel(it.GetIndex())<=0) { it.Set(0.0); ++it; continue; } if (it.Get()>max) max = it.Get(); if (it.Get()::New(); scaler->SetInput(image); scaler->SetShift(-min); scaler->SetScale(1.0/(max-min)); scaler->Update(); return scaler->GetOutput(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::CheckVolumeFractionImages() { m_UseRelativeNonFiberVolumeFractions = false; // check for fiber volume fraction maps unsigned int fibVolImages = 0; for (std::size_t i=0; iGetVolumeFractionImage().IsNotNull()) { PrintToLog("Using volume fraction map for fiber compartment " + boost::lexical_cast(i+1)); fibVolImages++; } } // check for non-fiber volume fraction maps unsigned int nonfibVolImages = 0; for (std::size_t i=0; iGetVolumeFractionImage().IsNotNull()) { PrintToLog("Using volume fraction map for non-fiber compartment " + boost::lexical_cast(i+1)); nonfibVolImages++; } } // not all fiber compartments are using volume fraction maps // --> non-fiber volume fractions are assumed to be relative to the // non-fiber volume and not absolute voxel-volume fractions. // this means if two non-fiber compartments are used but only one of them // has an associated volume fraction map, the repesctive other volume fraction map // can be determined as inverse (1-val) of the present volume fraction map- if ( fibVolImages::New(); inverter->SetMaximum(1.0); if ( m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage().IsNull() && m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage().IsNotNull() ) { // m_Parameters.m_NonFiberModelList[1]->SetVolumeFractionImage( // NormalizeInsideMask( m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage() ) ); inverter->SetInput( m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage() ); inverter->Update(); m_Parameters.m_NonFiberModelList[0]->SetVolumeFractionImage(inverter->GetOutput()); } else if ( m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage().IsNull() && m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage().IsNotNull() ) { // m_Parameters.m_NonFiberModelList[0]->SetVolumeFractionImage( // NormalizeInsideMask( m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage() ) ); inverter->SetInput( m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage() ); inverter->Update(); m_Parameters.m_NonFiberModelList[1]->SetVolumeFractionImage(inverter->GetOutput()); } else { itkExceptionMacro("Something went wrong in automatically calculating the missing non-fiber volume fraction image!" " Did you use two non fiber compartments but only one volume fraction image?" " Then it should work and this error is really strange."); } m_UseRelativeNonFiberVolumeFractions = true; nonfibVolImages++; } // Up to two fiber compartments are allowed without volume fraction maps since the volume fractions can then be determined automatically if (m_Parameters.m_FiberModelList.size()>2 && fibVolImages!=m_Parameters.m_FiberModelList.size()) itkExceptionMacro("More than two fiber compartment selected but no corresponding volume fraction maps set!"); // One non-fiber compartment is allowed without volume fraction map since the volume fraction can then be determined automatically if (m_Parameters.m_NonFiberModelList.size()>1 && nonfibVolImages!=m_Parameters.m_NonFiberModelList.size()) itkExceptionMacro("More than one non-fiber compartment selected but no volume fraction maps set!"); if (fibVolImages0) { PrintToLog("Not all fiber compartments are using an associated volume fraction image.\n" "Assuming non-fiber volume fraction images to contain values relative to the" " remaining non-fiber volume, not absolute values."); m_UseRelativeNonFiberVolumeFractions = true; // itk::ImageFileWriter::Pointer wr = itk::ImageFileWriter::New(); // wr->SetInput(m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage()); // wr->SetFileName("/local/volumefraction.nrrd"); // wr->Update(); } // initialize the images that store the output volume fraction of each compartment m_VolumeFractions.clear(); for (std::size_t i=0; iSetSpacing( m_WorkingSpacing ); doubleImg->SetOrigin( m_WorkingOrigin ); doubleImg->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); doubleImg->SetLargestPossibleRegion( m_WorkingImageRegion ); doubleImg->SetBufferedRegion( m_WorkingImageRegion ); doubleImg->SetRequestedRegion( m_WorkingImageRegion ); doubleImg->Allocate(); doubleImg->FillBuffer(0); m_VolumeFractions.push_back(doubleImg); } } template< class PixelType > void TractsToDWIImageFilter< PixelType >::InitializeData() { m_Rotations.clear(); m_Translations.clear(); m_MotionLog = ""; m_SpikeLog = ""; // initialize output dwi image m_Parameters.m_SignalGen.m_CroppedRegion = m_Parameters.m_SignalGen.m_ImageRegion; m_Parameters.m_SignalGen.m_CroppedRegion.SetSize( 1, m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(1) *m_Parameters.m_SignalGen.m_CroppingFactor); itk::Point shiftedOrigin = m_Parameters.m_SignalGen.m_ImageOrigin; shiftedOrigin[1] += (m_Parameters.m_SignalGen.m_ImageRegion.GetSize(1) -m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(1))*m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; m_OutputImage = OutputImageType::New(); m_OutputImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); m_OutputImage->SetOrigin( shiftedOrigin ); m_OutputImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_OutputImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_OutputImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_OutputImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_OutputImage->SetVectorLength( m_Parameters.m_SignalGen.GetNumVolumes() ); m_OutputImage->Allocate(); typename OutputImageType::PixelType temp; temp.SetSize(m_Parameters.m_SignalGen.GetNumVolumes()); temp.Fill(0.0); m_OutputImage->FillBuffer(temp); // Apply in-plane upsampling for Gibbs ringing artifact double upsampling = 1; if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging) upsampling = 2; m_WorkingSpacing = m_Parameters.m_SignalGen.m_ImageSpacing; m_WorkingSpacing[0] /= upsampling; m_WorkingSpacing[1] /= upsampling; m_WorkingImageRegion = m_Parameters.m_SignalGen.m_ImageRegion; m_WorkingImageRegion.SetSize(0, m_Parameters.m_SignalGen.m_ImageRegion.GetSize()[0]*upsampling); m_WorkingImageRegion.SetSize(1, m_Parameters.m_SignalGen.m_ImageRegion.GetSize()[1]*upsampling); m_WorkingOrigin = m_Parameters.m_SignalGen.m_ImageOrigin; m_WorkingOrigin[0] -= m_Parameters.m_SignalGen.m_ImageSpacing[0]/2; m_WorkingOrigin[0] += m_WorkingSpacing[0]/2; m_WorkingOrigin[1] -= m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; m_WorkingOrigin[1] += m_WorkingSpacing[1]/2; m_WorkingOrigin[2] -= m_Parameters.m_SignalGen.m_ImageSpacing[2]/2; m_WorkingOrigin[2] += m_WorkingSpacing[2]/2; m_VoxelVolume = m_WorkingSpacing[0]*m_WorkingSpacing[1]*m_WorkingSpacing[2]; // generate double images to store the individual compartment signals m_CompartmentImages.clear(); int numFiberCompartments = m_Parameters.m_FiberModelList.size(); int numNonFiberCompartments = m_Parameters.m_NonFiberModelList.size(); for (int i=0; iSetSpacing( m_WorkingSpacing ); doubleDwi->SetOrigin( m_WorkingOrigin ); doubleDwi->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); doubleDwi->SetLargestPossibleRegion( m_WorkingImageRegion ); doubleDwi->SetBufferedRegion( m_WorkingImageRegion ); doubleDwi->SetRequestedRegion( m_WorkingImageRegion ); doubleDwi->SetVectorLength( m_Parameters.m_SignalGen.GetNumVolumes() ); doubleDwi->Allocate(); DoubleDwiType::PixelType pix; pix.SetSize(m_Parameters.m_SignalGen.GetNumVolumes()); pix.Fill(0.0); doubleDwi->FillBuffer(pix); m_CompartmentImages.push_back(doubleDwi); } if (m_FiberBundle.IsNull() && m_InputImage.IsNotNull()) { m_CompartmentImages.clear(); m_Parameters.m_SignalGen.m_DoAddMotion = false; m_Parameters.m_SignalGen.m_DoSimulateRelaxation = false; PrintToLog("Simulating acquisition for input diffusion-weighted image.", false); auto caster = itk::CastImageFilter< OutputImageType, DoubleDwiType >::New(); caster->SetInput(m_InputImage); caster->Update(); if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging) { PrintToLog("Upsampling input diffusion-weighted image for Gibbs ringing simulation.", false); auto resampler = itk::ResampleDwiImageFilter< double >::New(); resampler->SetInput(caster->GetOutput()); itk::Vector< double, 3 > samplingFactor; samplingFactor[0] = upsampling; samplingFactor[1] = upsampling; samplingFactor[2] = 1; resampler->SetSamplingFactor(samplingFactor); resampler->SetInterpolation(itk::ResampleDwiImageFilter< double >::Interpolate_WindowedSinc); resampler->Update(); m_CompartmentImages.push_back(resampler->GetOutput()); } else m_CompartmentImages.push_back(caster->GetOutput()); for (unsigned int g=0; g::New(); rescaler->SetInput(0,m_Parameters.m_SignalGen.m_MaskImage); rescaler->SetOutputMaximum(100); rescaler->SetOutputMinimum(0); rescaler->Update(); // resample mask image auto resampler = itk::ResampleImageFilter::New(); resampler->SetInput(rescaler->GetOutput()); resampler->SetOutputParametersFromImage(m_Parameters.m_SignalGen.m_MaskImage); resampler->SetSize(m_WorkingImageRegion.GetSize()); resampler->SetOutputSpacing(m_WorkingSpacing); resampler->SetOutputOrigin(m_WorkingOrigin); auto nn_interpolator = itk::NearestNeighborInterpolateImageFunction::New(); resampler->SetInterpolator(nn_interpolator); resampler->Update(); m_Parameters.m_SignalGen.m_MaskImage = resampler->GetOutput(); } // resample frequency map if (m_Parameters.m_SignalGen.m_FrequencyMap.IsNotNull()) { auto resampler = itk::ResampleImageFilter::New(); resampler->SetInput(m_Parameters.m_SignalGen.m_FrequencyMap); resampler->SetOutputParametersFromImage(m_Parameters.m_SignalGen.m_FrequencyMap); resampler->SetSize(m_WorkingImageRegion.GetSize()); resampler->SetOutputSpacing(m_WorkingSpacing); resampler->SetOutputOrigin(m_WorkingOrigin); auto nn_interpolator = itk::NearestNeighborInterpolateImageFunction::New(); resampler->SetInterpolator(nn_interpolator); resampler->Update(); m_Parameters.m_SignalGen.m_FrequencyMap = resampler->GetOutput(); } } m_MaskImageSet = true; if (m_Parameters.m_SignalGen.m_MaskImage.IsNull()) { // no input tissue mask is set -> create default PrintToLog("No tissue mask set", false); m_Parameters.m_SignalGen.m_MaskImage = ItkUcharImgType::New(); m_Parameters.m_SignalGen.m_MaskImage->SetSpacing( m_WorkingSpacing ); m_Parameters.m_SignalGen.m_MaskImage->SetOrigin( m_WorkingOrigin ); m_Parameters.m_SignalGen.m_MaskImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_Parameters.m_SignalGen.m_MaskImage->SetLargestPossibleRegion( m_WorkingImageRegion ); m_Parameters.m_SignalGen.m_MaskImage->SetBufferedRegion( m_WorkingImageRegion ); m_Parameters.m_SignalGen.m_MaskImage->SetRequestedRegion( m_WorkingImageRegion ); m_Parameters.m_SignalGen.m_MaskImage->Allocate(); m_Parameters.m_SignalGen.m_MaskImage->FillBuffer(100); m_MaskImageSet = false; } else { if (m_Parameters.m_SignalGen.m_MaskImage->GetLargestPossibleRegion()!=m_WorkingImageRegion) { itkExceptionMacro("Mask image and specified DWI geometry are not matching!"); } PrintToLog("Using tissue mask", false); } if (m_Parameters.m_SignalGen.m_DoAddMotion) { if (m_Parameters.m_SignalGen.m_DoRandomizeMotion) { PrintToLog("Random motion artifacts:", false); PrintToLog("Maximum rotation: +/-" + boost::lexical_cast(m_Parameters.m_SignalGen.m_Rotation) + "°", false); PrintToLog("Maximum translation: +/-" + boost::lexical_cast(m_Parameters.m_SignalGen.m_Translation) + "mm", false); } else { PrintToLog("Linear motion artifacts:", false); PrintToLog("Maximum rotation: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_Rotation) + "°", false); PrintToLog("Maximum translation: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_Translation) + "mm", false); } } if ( m_Parameters.m_SignalGen.m_MotionVolumes.empty() ) { // no motion in first volume m_Parameters.m_SignalGen.m_MotionVolumes.push_back(false); // motion in all other volumes while ( m_Parameters.m_SignalGen.m_MotionVolumes.size() < m_Parameters.m_SignalGen.GetNumVolumes() ) { m_Parameters.m_SignalGen.m_MotionVolumes.push_back(true); } } // we need to know for every volume if there is motion. if this information is missing, then set corresponding fal to false while ( m_Parameters.m_SignalGen.m_MotionVolumes.size()::New(); duplicator->SetInputImage(m_Parameters.m_SignalGen.m_MaskImage); duplicator->Update(); m_TransformedMaskImage = duplicator->GetOutput(); // second upsampling needed for motion artifacts ImageRegion<3> upsampledImageRegion = m_WorkingImageRegion; DoubleVectorType upsampledSpacing = m_WorkingSpacing; upsampledSpacing[0] /= 4; upsampledSpacing[1] /= 4; upsampledSpacing[2] /= 4; upsampledImageRegion.SetSize(0, m_WorkingImageRegion.GetSize()[0]*4); upsampledImageRegion.SetSize(1, m_WorkingImageRegion.GetSize()[1]*4); upsampledImageRegion.SetSize(2, m_WorkingImageRegion.GetSize()[2]*4); itk::Point upsampledOrigin = m_WorkingOrigin; upsampledOrigin[0] -= m_WorkingSpacing[0]/2; upsampledOrigin[0] += upsampledSpacing[0]/2; upsampledOrigin[1] -= m_WorkingSpacing[1]/2; upsampledOrigin[1] += upsampledSpacing[1]/2; upsampledOrigin[2] -= m_WorkingSpacing[2]/2; upsampledOrigin[2] += upsampledSpacing[2]/2; m_UpsampledMaskImage = ItkUcharImgType::New(); auto upsampler = itk::ResampleImageFilter::New(); upsampler->SetInput(m_Parameters.m_SignalGen.m_MaskImage); upsampler->SetOutputParametersFromImage(m_Parameters.m_SignalGen.m_MaskImage); upsampler->SetSize(upsampledImageRegion.GetSize()); upsampler->SetOutputSpacing(upsampledSpacing); upsampler->SetOutputOrigin(upsampledOrigin); auto nn_interpolator = itk::NearestNeighborInterpolateImageFunction::New(); upsampler->SetInterpolator(nn_interpolator); upsampler->Update(); m_UpsampledMaskImage = upsampler->GetOutput(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::InitializeFiberData() { // resample fiber bundle for sufficient voxel coverage PrintToLog("Resampling fibers ..."); m_SegmentVolume = 0.0001; float minSpacing = 1; if( m_WorkingSpacing[0]GetDeepCopy(); double volumeAccuracy = 10; m_FiberBundleWorkingCopy->ResampleLinear(minSpacing/volumeAccuracy); m_mmRadius = m_Parameters.m_SignalGen.m_AxonRadius/1000; auto caster = itk::CastImageFilter< itk::Image, itk::Image >::New(); caster->SetInput(m_TransformedMaskImage); caster->Update(); auto density_calculator = itk::TractDensityImageFilter< itk::Image >::New(); density_calculator->SetFiberBundle(m_FiberBundleWorkingCopy); density_calculator->SetInputImage(caster->GetOutput()); density_calculator->SetBinaryOutput(false); density_calculator->SetUseImageGeometry(true); density_calculator->SetDoFiberResampling(false); density_calculator->SetOutputAbsoluteValues(true); density_calculator->SetWorkOnFiberCopy(false); density_calculator->Update(); float max_density = density_calculator->GetMaxDensity(); if (m_mmRadius>0) { - m_SegmentVolume = M_PI*m_mmRadius*m_mmRadius*minSpacing/volumeAccuracy; + m_SegmentVolume = itk::Math::pi*m_mmRadius*m_mmRadius*minSpacing/volumeAccuracy; std::stringstream stream; stream << std::fixed << setprecision(2) << max_density * m_SegmentVolume; std::string s = stream.str(); PrintToLog("\nMax. fiber volume: " + s + "mm².", false, true, true); } else { std::stringstream stream; stream << std::fixed << setprecision(2) << max_density * m_SegmentVolume; std::string s = stream.str(); PrintToLog("\nMax. fiber volume: " + s + "mm² (before rescaling to voxel volume).", false, true, true); } float voxel_volume = m_WorkingSpacing[0]*m_WorkingSpacing[1]*m_WorkingSpacing[2]; float new_seg_vol = voxel_volume/max_density; - float new_fib_radius = 1000*std::sqrt(new_seg_vol*volumeAccuracy/(minSpacing*M_PI)); + float new_fib_radius = 1000*std::sqrt(new_seg_vol*volumeAccuracy/(minSpacing*itk::Math::pi)); std::stringstream stream; stream << std::fixed << setprecision(2) << new_fib_radius; std::string s = stream.str(); PrintToLog("\nA full fiber voxel corresponds to a fiber radius of ~" + s + "µm, given the current fiber configuration.", false, true, true); // a second fiber bundle is needed to store the transformed version of the m_FiberBundleWorkingCopy m_FiberBundleTransformed = m_FiberBundleWorkingCopy; } template< class PixelType > bool TractsToDWIImageFilter< PixelType >::PrepareLogFile() { assert( ! m_Logfile.is_open() ); std::string filePath; std::string fileName; // Get directory name: if (m_Parameters.m_Misc.m_OutputPath.size() > 0) { filePath = m_Parameters.m_Misc.m_OutputPath; if( *(--(filePath.cend())) != '/') { filePath.push_back('/'); } } else { filePath = mitk::IOUtil::GetTempPath() + '/'; } // check if directory exists, else use /tmp/: if( itksys::SystemTools::FileIsDirectory( filePath ) ) { while( *(--(filePath.cend())) == '/') { filePath.pop_back(); } filePath = filePath + '/'; } else { filePath = mitk::IOUtil::GetTempPath() + '/'; } // Get file name: if( ! m_Parameters.m_Misc.m_ResultNode->GetName().empty() ) { fileName = m_Parameters.m_Misc.m_ResultNode->GetName(); } else { fileName = ""; } if( ! m_Parameters.m_Misc.m_OutputPrefix.empty() ) { fileName = m_Parameters.m_Misc.m_OutputPrefix + fileName; } else { fileName = "fiberfox"; } // check if file already exists and DO NOT overwrite existing files: std::string NameTest = fileName; int c = 0; while( itksys::SystemTools::FileExists( filePath + '/' + fileName + ".log" ) && c <= std::numeric_limits::max() ) { fileName = NameTest + "_" + boost::lexical_cast(c); ++c; } try { m_Logfile.open( ( filePath + '/' + fileName + ".log" ).c_str() ); } catch (const std::ios_base::failure &fail) { MITK_ERROR << "itkTractsToDWIImageFilter.cpp: Exception " << fail.what() << " while trying to open file" << filePath << '/' << fileName << ".log"; return false; } if ( m_Logfile.is_open() ) { PrintToLog( "Logfile: " + filePath + '/' + fileName + ".log", false ); return true; } else { m_StatusText += "Logfile could not be opened!\n"; MITK_ERROR << "itkTractsToDWIImageFilter.cpp: Logfile could not be opened!"; return false; } } template< class PixelType > void TractsToDWIImageFilter< PixelType >::GenerateData() { // prepare logfile if ( ! PrepareLogFile() ) { this->SetAbortGenerateData( true ); return; } m_TimeProbe.Start(); // check input data if (m_FiberBundle.IsNull() && m_InputImage.IsNull()) itkExceptionMacro("Input fiber bundle and input diffusion-weighted image is nullptr!"); if (m_Parameters.m_FiberModelList.empty() && m_InputImage.IsNull()) itkExceptionMacro("No diffusion model for fiber compartments defined and input diffusion-weighted" " image is nullptr! At least one fiber compartment is necessary to simulate diffusion."); if (m_Parameters.m_NonFiberModelList.empty() && m_InputImage.IsNull()) itkExceptionMacro("No diffusion model for non-fiber compartments defined and input diffusion-weighted" " image is nullptr! At least one non-fiber compartment is necessary to simulate diffusion."); // int baselineIndex = m_Parameters.m_SignalGen.GetFirstBaselineIndex(); // if (baselineIndex<0) { itkExceptionMacro("No baseline index found!"); } if (!m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition) // No upsampling of input image needed if no k-space simulation is performed { m_Parameters.m_SignalGen.m_DoAddGibbsRinging = false; } if (m_UseConstantRandSeed) // always generate the same random numbers? { m_RandGen->SetSeed(0); } else { m_RandGen->SetSeed(); } InitializeData(); if ( m_FiberBundle.IsNotNull() ) // if no fiber bundle is found, we directly proceed to the k-space acquisition simulation { CheckVolumeFractionImages(); InitializeFiberData(); int numFiberCompartments = m_Parameters.m_FiberModelList.size(); int numNonFiberCompartments = m_Parameters.m_NonFiberModelList.size(); double maxVolume = 0; unsigned long lastTick = 0; int signalModelSeed = m_RandGen->GetIntegerVariate(); PrintToLog("\n", false, false); PrintToLog("Generating " + boost::lexical_cast(numFiberCompartments+numNonFiberCompartments) + "-compartment diffusion-weighted signal."); std::vector< int > bVals = m_Parameters.m_SignalGen.GetBvalues(); PrintToLog("b-values: ", false, false, true); for (auto v : bVals) PrintToLog(boost::lexical_cast(v) + " ", false, false, true); PrintToLog("\n", false, false, true); PrintToLog("\n", false, false, true); int numFibers = m_FiberBundleWorkingCopy->GetNumFibers(); boost::progress_display disp(numFibers*m_Parameters.m_SignalGen.GetNumVolumes()); if (m_FiberBundle->GetMeanFiberLength()<5.0) omp_set_num_threads(2); PrintToLog("0% 10 20 30 40 50 60 70 80 90 100%", false, true, false); PrintToLog("|----|----|----|----|----|----|----|----|----|----|\n*", false, false, false); for (unsigned int g=0; gSetSeed(signalModelSeed); for (std::size_t i=0; iSetSeed(signalModelSeed); // storing voxel-wise intra-axonal volume in mm³ auto intraAxonalVolumeImage = ItkDoubleImgType::New(); intraAxonalVolumeImage->SetSpacing( m_WorkingSpacing ); intraAxonalVolumeImage->SetOrigin( m_WorkingOrigin ); intraAxonalVolumeImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); intraAxonalVolumeImage->SetLargestPossibleRegion( m_WorkingImageRegion ); intraAxonalVolumeImage->SetBufferedRegion( m_WorkingImageRegion ); intraAxonalVolumeImage->SetRequestedRegion( m_WorkingImageRegion ); intraAxonalVolumeImage->Allocate(); intraAxonalVolumeImage->FillBuffer(0); maxVolume = 0; if (this->GetAbortGenerateData()) continue; vtkPolyData* fiberPolyData = m_FiberBundleTransformed->GetFiberPolyData(); // generate fiber signal (if there are any fiber models present) if (!m_Parameters.m_FiberModelList.empty()) { #pragma omp parallel for for( int i=0; iGetAbortGenerateData()) continue; float fiberWeight = m_FiberBundleTransformed->GetFiberWeight(i); int numPoints = -1; std::vector< itk::Vector > points_copy; #pragma omp critical { vtkCell* cell = fiberPolyData->GetCell(i); numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(j))); } if (numPoints<2) continue; for( int j=0; jGetAbortGenerateData()) { j=numPoints; continue; } itk::Point vertex = points_copy.at(j); itk::Vector v = points_copy.at(j); itk::Vector dir(3); if (j idx; itk::ContinuousIndex contIndex; m_TransformedMaskImage->TransformPhysicalPointToIndex(vertex, idx); m_TransformedMaskImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); if (!m_TransformedMaskImage->GetLargestPossibleRegion().IsInside(idx) || m_TransformedMaskImage->GetPixel(idx)<=0) continue; dir.Normalize(); // generate signal for each fiber compartment for (int k=0; kGetPixel(idx); pix[g] += fiberWeight*m_SegmentVolume*m_Parameters.m_FiberModelList[k]->SimulateMeasurement(g, dir); m_CompartmentImages.at(k)->SetPixel(idx, pix); } // update fiber volume image double vol = intraAxonalVolumeImage->GetPixel(idx) + m_SegmentVolume*fiberWeight; intraAxonalVolumeImage->SetPixel(idx, vol); // we assume that the first volume is always unweighted! if (vol>maxVolume) { maxVolume = vol; } } #pragma omp critical { // progress report ++disp; unsigned long newTick = 50*disp.count()/disp.expected_count(); for (unsigned int tick = 0; tick<(newTick-lastTick); ++tick) PrintToLog("*", false, false, false); lastTick = newTick; } } } // generate non-fiber signal ImageRegionIterator it3(m_TransformedMaskImage, m_TransformedMaskImage->GetLargestPossibleRegion()); double fact = 1; // density correction factor in mm³ if (m_Parameters.m_SignalGen.m_AxonRadius<0.0001) // the fullest voxel is always completely full fact = m_VoxelVolume/maxVolume; while(!it3.IsAtEnd()) { if (it3.Get()>0) { DoubleDwiType::IndexType index = it3.GetIndex(); itk::Point point; m_TransformedMaskImage->TransformIndexToPhysicalPoint(index, point); if ( m_Parameters.m_SignalGen.m_DoAddMotion && g>=0 && m_Parameters.m_SignalGen.m_MotionVolumes[g] ) { if (m_Parameters.m_SignalGen.m_DoRandomizeMotion) { point = m_FiberBundleWorkingCopy->TransformPoint( point.GetVnlVector(), -m_Rotation[0], -m_Rotation[1], -m_Rotation[2], -m_Translation[0], -m_Translation[1], -m_Translation[2] ); } else { point = m_FiberBundleWorkingCopy->TransformPoint( point.GetVnlVector(), -m_Rotation[0]*m_MotionCounter, -m_Rotation[1]*m_MotionCounter, -m_Rotation[2]*m_MotionCounter, -m_Translation[0]*m_MotionCounter, -m_Translation[1]*m_MotionCounter, -m_Translation[2]*m_MotionCounter ); } } double iAxVolume = intraAxonalVolumeImage->GetPixel(index); double fact3 = 1.0; if (iAxVolume>m_VoxelVolume) { fact3 = m_VoxelVolume/iAxVolume; fact = 1.0; } // if volume fraction image is set use it, otherwise use scaling factor to obtain one full fiber voxel double fact2 = fact*fact3; if ( m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()!=nullptr && iAxVolume>0.0001 ) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()); double val = mitk::imv::GetImageValue(point, true, m_DoubleInterpolator); if (val<0) mitkThrow() << "Volume fraction image (index 1) contains negative values (intra-axonal compartment)!"; fact2 = m_VoxelVolume*val/iAxVolume; } // adjust intra-axonal image value for (int i=0; iGetPixel(index); pix[g] *= fact2; m_CompartmentImages.at(i)->SetPixel(index, pix); } // simulate other compartments SimulateExtraAxonalSignal(index, iAxVolume*fact2, g); } ++it3; } } PrintToLog("\n", false); } if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } DoubleDwiType::Pointer doubleOutImage; double signalScale = m_Parameters.m_SignalGen.m_SignalScale; if ( m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition ) // do k-space stuff { PrintToLog("\n", false, false); PrintToLog("Simulating k-space acquisition using " +boost::lexical_cast(m_Parameters.m_SignalGen.m_NumberOfCoils) +" coil(s)"); switch (m_Parameters.m_SignalGen.m_AcquisitionType) { case SignalGenerationParameters::SingleShotEpi: { PrintToLog("Acquisition type: single shot EPI", false); break; } case SignalGenerationParameters::SpinEcho: { PrintToLog("Acquisition type: classic spin echo with cartesian k-space trajectory", false); break; } default: { PrintToLog("Acquisition type: single shot EPI", false); break; } } if (m_Parameters.m_SignalGen.m_NoiseVariance>0 && m_Parameters.m_Misc.m_CheckAddNoiseBox) PrintToLog("Simulating complex Gaussian noise", false); if (m_Parameters.m_SignalGen.m_DoSimulateRelaxation) PrintToLog("Simulating signal relaxation", false); if (m_Parameters.m_SignalGen.m_FrequencyMap.IsNotNull()) PrintToLog("Simulating distortions", false); if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging) PrintToLog("Simulating ringing artifacts", false); if (m_Parameters.m_SignalGen.m_EddyStrength>0) PrintToLog("Simulating eddy currents", false); if (m_Parameters.m_SignalGen.m_Spikes>0) PrintToLog("Simulating spikes", false); if (m_Parameters.m_SignalGen.m_CroppingFactor<1.0) PrintToLog("Simulating aliasing artifacts", false); if (m_Parameters.m_SignalGen.m_KspaceLineOffset>0) PrintToLog("Simulating ghosts", false); doubleOutImage = SimulateKspaceAcquisition(m_CompartmentImages); signalScale = 1; // already scaled in SimulateKspaceAcquisition() } else // don't do k-space stuff, just sum compartments { PrintToLog("Summing compartments"); doubleOutImage = m_CompartmentImages.at(0); for (unsigned int i=1; i::New(); adder->SetInput1(doubleOutImage); adder->SetInput2(m_CompartmentImages.at(i)); adder->Update(); doubleOutImage = adder->GetOutput(); } } if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } PrintToLog("Finalizing image"); if (signalScale>1) PrintToLog(" Scaling signal", false); if (m_Parameters.m_NoiseModel) PrintToLog(" Adding noise", false); unsigned int window = 0; unsigned int min = itk::NumericTraits::max(); ImageRegionIterator it4 (m_OutputImage, m_OutputImage->GetLargestPossibleRegion()); DoubleDwiType::PixelType signal; signal.SetSize(m_Parameters.m_SignalGen.GetNumVolumes()); boost::progress_display disp2(m_OutputImage->GetLargestPossibleRegion().GetNumberOfPixels()); PrintToLog("0% 10 20 30 40 50 60 70 80 90 100%", false, true, false); PrintToLog("|----|----|----|----|----|----|----|----|----|----|\n*", false, false, false); int lastTick = 0; while(!it4.IsAtEnd()) { if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } ++disp2; unsigned long newTick = 50*disp2.count()/disp2.expected_count(); for (unsigned long tick = 0; tick<(newTick-lastTick); tick++) PrintToLog("*", false, false, false); lastTick = newTick; typename OutputImageType::IndexType index = it4.GetIndex(); signal = doubleOutImage->GetPixel(index)*signalScale; if (m_Parameters.m_NoiseModel) m_Parameters.m_NoiseModel->AddNoise(signal); for (unsigned int i=0; i0) signal[i] = floor(signal[i]+0.5); else signal[i] = ceil(signal[i]-0.5); if ( (!m_Parameters.m_SignalGen.IsBaselineIndex(i) || signal.Size()==1) && signal[i]>window) window = signal[i]; if ( (!m_Parameters.m_SignalGen.IsBaselineIndex(i) || signal.Size()==1) && signal[i]SetNthOutput(0, m_OutputImage); PrintToLog("\n", false); PrintToLog("Finished simulation"); m_TimeProbe.Stop(); if (m_Parameters.m_SignalGen.m_DoAddMotion) { PrintToLog("\nHead motion log:", false); PrintToLog(m_MotionLog, false, false); } if (m_Parameters.m_SignalGen.m_Spikes>0) { PrintToLog("\nSpike log:", false); PrintToLog(m_SpikeLog, false, false); } if (m_Logfile.is_open()) m_Logfile.close(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::PrintToLog(std::string m, bool addTime, bool linebreak, bool stdOut) { // timestamp if (addTime) { m_Logfile << this->GetTime() << " > "; m_StatusText += this->GetTime() + " > "; if (stdOut) std::cout << this->GetTime() << " > "; } // message if (m_Logfile.is_open()) m_Logfile << m; m_StatusText += m; if (stdOut) std::cout << m; // new line if (linebreak) { if (m_Logfile.is_open()) m_Logfile << "\n"; m_StatusText += "\n"; if (stdOut) std::cout << "\n"; } } template< class PixelType > void TractsToDWIImageFilter< PixelType >::SimulateMotion(int g) { // is motion artifact enabled? // is the current volume g affected by motion? if ( m_Parameters.m_SignalGen.m_DoAddMotion && m_Parameters.m_SignalGen.m_MotionVolumes[g] && g(m_Parameters.m_SignalGen.GetNumVolumes()) ) { if ( m_Parameters.m_SignalGen.m_DoRandomizeMotion ) { // either undo last transform or work on fresh copy of untransformed fibers m_FiberBundleTransformed = m_FiberBundleWorkingCopy->GetDeepCopy(); m_Rotation[0] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Rotation[0]*2) -m_Parameters.m_SignalGen.m_Rotation[0]; m_Rotation[1] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Rotation[1]*2) -m_Parameters.m_SignalGen.m_Rotation[1]; m_Rotation[2] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Rotation[2]*2) -m_Parameters.m_SignalGen.m_Rotation[2]; m_Translation[0] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Translation[0]*2) -m_Parameters.m_SignalGen.m_Translation[0]; m_Translation[1] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Translation[1]*2) -m_Parameters.m_SignalGen.m_Translation[1]; m_Translation[2] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Translation[2]*2) -m_Parameters.m_SignalGen.m_Translation[2]; } else { m_Rotation = m_Parameters.m_SignalGen.m_Rotation / m_NumMotionVolumes; m_Translation = m_Parameters.m_SignalGen.m_Translation / m_NumMotionVolumes; m_MotionCounter++; } // move mask image if (m_MaskImageSet) { ImageRegionIterator maskIt(m_UpsampledMaskImage, m_UpsampledMaskImage->GetLargestPossibleRegion()); m_TransformedMaskImage->FillBuffer(0); while(!maskIt.IsAtEnd()) { if (maskIt.Get()<=0) { ++maskIt; continue; } DoubleDwiType::IndexType index = maskIt.GetIndex(); itk::Point point; m_UpsampledMaskImage->TransformIndexToPhysicalPoint(index, point); if (m_Parameters.m_SignalGen.m_DoRandomizeMotion) { point = m_FiberBundleWorkingCopy->TransformPoint(point.GetVnlVector(), m_Rotation[0],m_Rotation[1],m_Rotation[2], m_Translation[0],m_Translation[1],m_Translation[2]); } else { point = m_FiberBundleWorkingCopy->TransformPoint(point.GetVnlVector(), m_Rotation[0]*m_MotionCounter,m_Rotation[1]*m_MotionCounter,m_Rotation[2]*m_MotionCounter, m_Translation[0]*m_MotionCounter,m_Translation[1]*m_MotionCounter,m_Translation[2]*m_MotionCounter); } m_TransformedMaskImage->TransformPhysicalPointToIndex(point, index); if (m_TransformedMaskImage->GetLargestPossibleRegion().IsInside(index)) { m_TransformedMaskImage->SetPixel(index,100); } ++maskIt; } } if (m_Parameters.m_SignalGen.m_DoRandomizeMotion) { m_Rotations.push_back(m_Rotation); m_Translations.push_back(m_Translation); m_MotionLog += boost::lexical_cast(g) + " rotation: " + boost::lexical_cast(m_Rotation[0]) + "," + boost::lexical_cast(m_Rotation[1]) + "," + boost::lexical_cast(m_Rotation[2]) + ";"; m_MotionLog += " translation: " + boost::lexical_cast(m_Translation[0]) + "," + boost::lexical_cast(m_Translation[1]) + "," + boost::lexical_cast(m_Translation[2]) + "\n"; } else { m_Rotations.push_back(m_Rotation*m_MotionCounter); m_Translations.push_back(m_Translation*m_MotionCounter); m_MotionLog += boost::lexical_cast(g) + " rotation: " + boost::lexical_cast(m_Rotation[0]*m_MotionCounter) + "," + boost::lexical_cast(m_Rotation[1]*m_MotionCounter) + "," + boost::lexical_cast(m_Rotation[2]*m_MotionCounter) + ";"; m_MotionLog += " translation: " + boost::lexical_cast(m_Translation[0]*m_MotionCounter) + "," + boost::lexical_cast(m_Translation[1]*m_MotionCounter) + "," + boost::lexical_cast(m_Translation[2]*m_MotionCounter) + "\n"; } m_FiberBundleTransformed->TransformFibers(m_Rotation[0],m_Rotation[1],m_Rotation[2],m_Translation[0],m_Translation[1],m_Translation[2]); } else { m_Rotation.Fill(0.0); m_Translation.Fill(0.0); m_Rotations.push_back(m_Rotation); m_Translations.push_back(m_Translation); m_MotionLog += boost::lexical_cast(g) + " rotation: " + boost::lexical_cast(m_Rotation[0]) + "," + boost::lexical_cast(m_Rotation[1]) + "," + boost::lexical_cast(m_Rotation[2]) + ";"; m_MotionLog += " translation: " + boost::lexical_cast(m_Translation[0]) + "," + boost::lexical_cast(m_Translation[1]) + "," + boost::lexical_cast(m_Translation[2]) + "\n"; } } template< class PixelType > void TractsToDWIImageFilter< PixelType >:: SimulateExtraAxonalSignal(ItkUcharImgType::IndexType index, double intraAxonalVolume, int g) { int numFiberCompartments = m_Parameters.m_FiberModelList.size(); int numNonFiberCompartments = m_Parameters.m_NonFiberModelList.size(); if (intraAxonalVolume>0.0001 && m_Parameters.m_SignalGen.m_DoDisablePartialVolume) // only fiber in voxel { DoubleDwiType::PixelType pix = m_CompartmentImages.at(0)->GetPixel(index); if (g>=0) pix[g] *= m_VoxelVolume/intraAxonalVolume; else pix *= m_VoxelVolume/intraAxonalVolume; m_CompartmentImages.at(0)->SetPixel(index, pix); if (g==0) m_VolumeFractions.at(0)->SetPixel(index, 1); for (int i=1; iGetPixel(index); if (g>=0) pix[g] = 0.0; else pix.Fill(0.0); m_CompartmentImages.at(i)->SetPixel(index, pix); } } else { if (g==0) { m_VolumeFractions.at(0)->SetPixel(index, intraAxonalVolume/m_VoxelVolume); } // get non-transformed point (remove headmotion tranformation) // this point can then be transformed to each of the original images, regardless of their geometry itk::Point point; m_TransformedMaskImage->TransformIndexToPhysicalPoint(index, point); if ( m_Parameters.m_SignalGen.m_DoAddMotion && g>=0 && m_Parameters.m_SignalGen.m_MotionVolumes[g] ) { if (m_Parameters.m_SignalGen.m_DoRandomizeMotion) { point = m_FiberBundleWorkingCopy->TransformPoint(point.GetVnlVector(), -m_Rotation[0],-m_Rotation[1],-m_Rotation[2], -m_Translation[0],-m_Translation[1],-m_Translation[2]); } else { point = m_FiberBundleWorkingCopy->TransformPoint(point.GetVnlVector(), -m_Rotation[0]*m_MotionCounter,-m_Rotation[1]*m_MotionCounter,-m_Rotation[2]*m_MotionCounter, -m_Translation[0]*m_MotionCounter,-m_Translation[1]*m_MotionCounter,-m_Translation[2]*m_MotionCounter); } } if (m_Parameters.m_SignalGen.m_DoDisablePartialVolume) { int maxVolumeIndex = 0; double maxWeight = 0; for (int i=0; i1) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()); double val = mitk::imv::GetImageValue(point, true, m_DoubleInterpolator); if (val<0) mitkThrow() << "Volume fraction image (index " << i << ") contains values less than zero!"; else weight = val; } if (weight>maxWeight) { maxWeight = weight; maxVolumeIndex = i; } } DoubleDwiType::Pointer doubleDwi = m_CompartmentImages.at(maxVolumeIndex+numFiberCompartments); DoubleDwiType::PixelType pix = doubleDwi->GetPixel(index); if (g>=0) pix[g] += m_Parameters.m_NonFiberModelList[maxVolumeIndex]->SimulateMeasurement(g, m_NullDir)*m_VoxelVolume; else pix += m_Parameters.m_NonFiberModelList[maxVolumeIndex]->SimulateMeasurement(m_NullDir)*m_VoxelVolume; doubleDwi->SetPixel(index, pix); if (g==0) m_VolumeFractions.at(maxVolumeIndex+numFiberCompartments)->SetPixel(index, 1); } else { double extraAxonalVolume = m_VoxelVolume-intraAxonalVolume; // non-fiber volume if (extraAxonalVolume<0) { if (extraAxonalVolume<-0.001) MITK_ERROR << "Corrupted intra-axonal signal voxel detected. Fiber volume larger voxel volume! " << m_VoxelVolume << "<" << intraAxonalVolume; extraAxonalVolume = 0; } double interAxonalVolume = 0; if (numFiberCompartments>1) interAxonalVolume = extraAxonalVolume * intraAxonalVolume/m_VoxelVolume; // inter-axonal fraction of non fiber compartment double other = extraAxonalVolume - interAxonalVolume; // rest of compartment if (other<0) { if (other<-0.001) MITK_ERROR << "Corrupted signal voxel detected. Fiber volume larger voxel volume!"; other = 0; interAxonalVolume = extraAxonalVolume; } double compartmentSum = intraAxonalVolume; // adjust non-fiber and intra-axonal signal for (int i=1; iGetPixel(index); if (intraAxonalVolume>0) // remove scaling by intra-axonal volume from inter-axonal compartment { if (g>=0) pix[g] /= intraAxonalVolume; else pix /= intraAxonalVolume; } else { if (g>=0) pix[g] = 0; else pix *= 0; } if (m_Parameters.m_FiberModelList[i]->GetVolumeFractionImage()!=nullptr) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_FiberModelList[i]->GetVolumeFractionImage()); double val = mitk::imv::GetImageValue(point, true, m_DoubleInterpolator); if (val<0) mitkThrow() << "Volume fraction image (index " << i+1 << ") contains negative values!"; else weight = val*m_VoxelVolume; } compartmentSum += weight; if (g>=0) pix[g] *= weight; else pix *= weight; m_CompartmentImages.at(i)->SetPixel(index, pix); if (g==0) m_VolumeFractions.at(i)->SetPixel(index, weight/m_VoxelVolume); } for (int i=0; iGetPixel(index); if (m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()!=nullptr) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()); double val = mitk::imv::GetImageValue(point, true, m_DoubleInterpolator); if (val<0) mitkThrow() << "Volume fraction image (index " << numFiberCompartments+i+1 << ") contains negative values (non-fiber compartment)!"; else weight = val*m_VoxelVolume; if (m_UseRelativeNonFiberVolumeFractions) weight *= other/m_VoxelVolume; } compartmentSum += weight; if (g>=0) pix[g] += m_Parameters.m_NonFiberModelList[i]->SimulateMeasurement(g, m_NullDir)*weight; else pix += m_Parameters.m_NonFiberModelList[i]->SimulateMeasurement(m_NullDir)*weight; m_CompartmentImages.at(i+numFiberCompartments)->SetPixel(index, pix); if (g==0) m_VolumeFractions.at(i+numFiberCompartments)->SetPixel(index, weight/m_VoxelVolume); } if (compartmentSum/m_VoxelVolume>1.05) MITK_ERROR << "Compartments do not sum to 1 in voxel " << index << " (" << compartmentSum/m_VoxelVolume << ")"; } } } template< class PixelType > itk::Point TractsToDWIImageFilter< PixelType >::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } template< class PixelType > itk::Vector TractsToDWIImageFilter< PixelType >::GetItkVector(double point[3]) { itk::Vector itkVector; itkVector[0] = point[0]; itkVector[1] = point[1]; itkVector[2] = point[2]; return itkVector; } template< class PixelType > vnl_vector_fixed TractsToDWIImageFilter< PixelType >::GetVnlVector(double point[3]) { vnl_vector_fixed vnlVector; vnlVector[0] = point[0]; vnlVector[1] = point[1]; vnlVector[2] = point[2]; return vnlVector; } template< class PixelType > vnl_vector_fixed TractsToDWIImageFilter< PixelType >::GetVnlVector(Vector& vector) { vnl_vector_fixed vnlVector; vnlVector[0] = vector[0]; vnlVector[1] = vector[1]; vnlVector[2] = vector[2]; return vnlVector; } template< class PixelType > double TractsToDWIImageFilter< PixelType >::RoundToNearest(double num) { return (num > 0.0) ? floor(num + 0.5) : ceil(num - 0.5); } template< class PixelType > std::string TractsToDWIImageFilter< PixelType >::GetTime() { m_TimeProbe.Stop(); unsigned long total = RoundToNearest(m_TimeProbe.GetTotal()); unsigned long hours = total/3600; unsigned long minutes = (total%3600)/60; unsigned long seconds = total%60; std::string out = ""; out.append(boost::lexical_cast(hours)); out.append(":"); out.append(boost::lexical_cast(minutes)); out.append(":"); out.append(boost::lexical_cast(seconds)); m_TimeProbe.Start(); return out; } } diff --git a/Modules/DiffusionImaging/FiberTracking/IODataStructures/FiberBundle/mitkFiberBundle.cpp b/Modules/DiffusionImaging/FiberTracking/IODataStructures/FiberBundle/mitkFiberBundle.cpp index dc8766cbe6..072198911c 100755 --- a/Modules/DiffusionImaging/FiberTracking/IODataStructures/FiberBundle/mitkFiberBundle.cpp +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/FiberBundle/mitkFiberBundle.cpp @@ -1,2555 +1,2552 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ - -#define _USE_MATH_DEFINES #include "mitkFiberBundle.h" #include #include #include #include "mitkImagePixelReadAccessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include const char* mitk::FiberBundle::FIBER_ID_ARRAY = "Fiber_IDs"; mitk::FiberBundle::FiberBundle( vtkPolyData* fiberPolyData ) : m_NumFibers(0) { m_FiberWeights = vtkSmartPointer::New(); m_FiberWeights->SetName("FIBER_WEIGHTS"); m_FiberPolyData = vtkSmartPointer::New(); if (fiberPolyData != nullptr) m_FiberPolyData = fiberPolyData; else { this->m_FiberPolyData->SetPoints(vtkSmartPointer::New()); this->m_FiberPolyData->SetLines(vtkSmartPointer::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); return newFib; } vtkSmartPointer mitk::FiberBundle::GeneratePolyDataByIds(std::vector fiberIds, vtkSmartPointer weights) { vtkSmartPointer newFiberPolyData = vtkSmartPointer::New(); vtkSmartPointer newLineSet = vtkSmartPointer::New(); vtkSmartPointer newPointSet = vtkSmartPointer::New(); weights->SetNumberOfValues(fiberIds.size()); int counter = 0; auto finIt = fiberIds.begin(); while ( finIt != fiberIds.end() ) { if (*finIt < 0 || *finIt>GetNumFibers()){ MITK_INFO << "FiberID can not be negative or >NumFibers!!! check id Extraction!" << *finIt; break; } vtkSmartPointer fiber = m_FiberIdDataSet->GetCell(*finIt);//->DeepCopy(fiber); vtkSmartPointer fibPoints = fiber->GetPoints(); vtkSmartPointer newFiber = vtkSmartPointer::New(); newFiber->GetPointIds()->SetNumberOfIds( fibPoints->GetNumberOfPoints() ); for(int i=0; iGetNumberOfPoints(); 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 vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); // add current fiber bundle vtkSmartPointer weights = vtkSmartPointer::New(); int num_weights = this->GetNumFibers(); for (auto fib : fibs) num_weights += fib->GetNumFibers(); weights->SetNumberOfValues(num_weights); unsigned int counter = 0; for (int i=0; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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 (int i=0; iGetFiberPolyData()->GetNumberOfCells(); i++) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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 vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); // add current fiber bundle vtkSmartPointer weights = vtkSmartPointer::New(); weights->SetNumberOfValues(this->GetNumFibers()+fib->GetNumFibers()); unsigned int counter = 0; for (int i=0; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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 (int i=0; iGetFiberPolyData()->GetNumberOfCells(); i++) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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 vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); std::vector weights; for (int i=0; iGetNumFibers(); i++) { if ( (invert && this->GetFiberWeight(i)>weight_thr) || (!invert && this->GetFiberWeight(i)<=weight_thr)) continue; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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; iSetFiberWeight(i, weights.at(i)); return newFib; } // Only retain a subsample of the fibers mitk::FiberBundle::Pointer mitk::FiberBundle::SubsampleFibers(float factor) { vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); int new_num_fibs = this->GetNumFibers()*factor; MITK_INFO << "Subsampling fibers with factor " << factor << "(" << new_num_fibs << "/" << this->GetNumFibers() << ")"; // add current fiber bundle vtkSmartPointer weights = vtkSmartPointer::New(); weights->SetNumberOfValues(new_num_fibs); std::vector< int > ids; for (int i=0; iGetNumFibers(); i++) ids.push_back(i); std::random_shuffle(ids.begin(), ids.end()); unsigned int counter = 0; for (int i=0; iGetCell(ids.at(i)); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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); 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 vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); std::vector< std::vector< itk::Point > > points1; for( int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; itk::Point start = GetItkPoint(points->GetPoint(0)); itk::Point end = GetItkPoint(points->GetPoint(numPoints-1)); points1.push_back( {start, end} ); } std::vector< std::vector< itk::Point > > points2; for( int i=0; iGetNumFibers(); i++ ) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; itk::Point start = GetItkPoint(points->GetPoint(0)); itk::Point end = 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<(int)points1.size(); i++) { //#pragma omp critical // { // progress++; // std::cout << (int)(100*(float)progress/points1.size()) << "%" << '\r'; // cout.flush(); // } bool match = false; for (unsigned int j=0; jGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(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); } itk::Point mitk::FiberBundle::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } /* * set PolyData (additional flag to recompute fiber geometry, default = true) */ void mitk::FiberBundle::SetFiberPolyData(vtkSmartPointer fiberPD, bool updateGeometry) { if (fiberPD == nullptr) this->m_FiberPolyData = vtkSmartPointer::New(); else m_FiberPolyData->DeepCopy(fiberPD); m_NumFibers = m_FiberPolyData->GetNumberOfLines(); if (updateGeometry) UpdateFiberGeometry(); GenerateFiberIds(); ColorFibersByOrientation(); } /* * return vtkPolyData */ vtkSmartPointer mitk::FiberBundle::GetFiberPolyData() const { return m_FiberPolyData; } 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 = nullptr; extrPoints = m_FiberPolyData->GetPoints(); int 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}; int componentSize = 4; m_FiberColors = vtkSmartPointer::New(); m_FiberColors->Allocate(numOfPoints * componentSize); m_FiberColors->SetNumberOfComponents(componentSize); m_FiberColors->SetName("FIBER_COLORS"); int numOfFibers = m_FiberPolyData->GetNumberOfLines(); if (numOfFibers < 1) return; /* extract single fibers of fiberBundle */ vtkCellArray* fiberList = m_FiberPolyData->GetLines(); fiberList->InitTraversal(); for (int fi=0; fiGetNextCell(pointsPerFiber, idList); /* single fiber checkpoints: is number of points valid */ if (pointsPerFiber > 1) { /* operate on points of single fiber */ for (int i=0; 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] = (unsigned char) (255.0 * std::fabs(diff[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff[2])); rgba[3] = (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] = (unsigned char) (255.0 * std::fabs(diff1[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff1[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff1[2])); rgba[3] = (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] = (unsigned char) (255.0 * std::fabs(diff2[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff2[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff2[2])); rgba[3] = (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, bool normalize) { double window = 5; //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; int componentSize = 4; m_FiberColors = vtkSmartPointer::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * componentSize); m_FiberColors->SetNumberOfComponents(componentSize); m_FiberColors->SetName("FIBER_COLORS"); mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetTableRange(0.0, 0.8); lookupTable->Build(); mitkLookup->SetVtkLookupTable(lookupTable); mitkLookup->SetType(mitk::LookupTable::JET); std::vector< double > values; double min = 1; double max = 0; MITK_INFO << "Coloring fibers by curvature"; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); for (int i=0; iGetNumberOfCells(); i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures for (int j=0; j > vectors; vnl_vector_fixed< float, 3 > meanV; meanV.fill(0.0); while(dist1) { double p1[3]; points->GetPoint(c-1, p1); double p2[3]; points->GetPoint(c, p2); vnl_vector_fixed< float, 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(distGetPoint(c, p1); double p2[3]; points->GetPoint(c+1, p2); vnl_vector_fixed< float, 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; c1.0) angle = 1.0; if (angle<-1.0) angle = -1.0; - dev += acos(angle)*180/M_PI; + dev += acos(angle)*180/itk::Math::pi; } if (vectors.size()>0) dev /= vectors.size(); dev = 1.0-dev/180.0; values.push_back(dev); if (devmax) max = dev; } } unsigned int count = 0; for (int i=0; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); for (int j=0; j1) dev = 1; lookupTable->GetColor(dev, color); rgba[0] = (unsigned char) (255.0 * color[0]); rgba[1] = (unsigned char) (255.0 * color[1]); rgba[2] = (unsigned char) (255.0 * color[2]); rgba[3] = (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; iGetNumberOfTuples(); i++) { double faValue = FAValArray->GetValue(i); faValue = faValue * 255.0; m_FiberColors->SetComponent(i,3, (unsigned char) faValue ); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ResetFiberOpacity() { for(long i=0; iGetNumberOfTuples(); 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 normalize) { mitkPixelTypeMultiplex3( ColorFibersByScalarMap, FAimage->GetPixelType(), FAimage, opacity, normalize ); m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } template void mitk::FiberBundle::ColorFibersByScalarMap(const mitk::PixelType, mitk::Image::Pointer image, bool opacity, bool normalize) { m_FiberColors = vtkSmartPointer::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); mitk::ImagePixelReadAccessor readimage(image, image->GetVolumeData(0)); unsigned char rgba[4] = {0,0,0,0}; vtkPoints* pointSet = m_FiberPolyData->GetPoints(); mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetTableRange(0.0, 0.8); lookupTable->Build(); mitkLookup->SetVtkLookupTable(lookupTable); mitkLookup->SetType(mitk::LookupTable::JET); double min = 9999999; double max = -9999999; for(long i=0; iGetNumberOfPoints(); ++i) { Point3D px; px[0] = pointSet->GetPoint(i)[0]; px[1] = pointSet->GetPoint(i)[1]; px[2] = pointSet->GetPoint(i)[2]; double pixelValue = readimage.GetPixelByWorldCoordinates(px); if (pixelValue>max) max = pixelValue; if (pixelValueGetNumberOfPoints(); ++i) { Point3D px; px[0] = pointSet->GetPoint(i)[0]; px[1] = pointSet->GetPoint(i)[1]; px[2] = pointSet->GetPoint(i)[2]; double pixelValue = readimage.GetPixelByWorldCoordinates(px); if (normalize) pixelValue = (pixelValue-min)/(max-min); else if (pixelValue>1) pixelValue = 1; double color[3]; lookupTable->GetColor(1-pixelValue, color); rgba[0] = (unsigned char) (255.0 * color[0]); rgba[1] = (unsigned char) (255.0 * color[1]); rgba[2] = (unsigned char) (255.0 * color[2]); if (opacity) rgba[3] = (unsigned char) (255.0 * pixelValue); else rgba[3] = (unsigned char) (255.0); m_FiberColors->InsertTypedTuple(i, rgba); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorFibersByFiberWeights(bool opacity, bool normalize) { m_FiberColors = vtkSmartPointer::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); mitk::LookupTable::Pointer mitkLookup = mitk::LookupTable::New(); vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetTableRange(0.0, 0.8); lookupTable->Build(); mitkLookup->SetVtkLookupTable(lookupTable); mitkLookup->SetType(mitk::LookupTable::JET); unsigned char rgba[4] = {0,0,0,0}; unsigned int counter = 0; float max = -999999; float min = 999999; for (int i=0; iGetFiberWeight(i); if (weight>max) max = weight; if (weightGetCell(i); int numPoints = cell->GetNumberOfPoints(); double weight = this->GetFiberWeight(i); for (int j=0; j1) v = 1; double color[3]; lookupTable->GetColor(1-v, color); rgba[0] = (unsigned char) (255.0 * color[0]); rgba[1] = (unsigned char) (255.0 * color[1]); rgba[2] = (unsigned char) (255.0 * color[2]); if (opacity) rgba[3] = (unsigned char) (255.0 * v); else rgba[3] = (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::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; iGetNumberOfPoints(); ++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); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::GenerateFiberIds() { if (m_FiberPolyData == nullptr) return; vtkSmartPointer idFiberFilter = vtkSmartPointer::New(); idFiberFilter->SetInputData(m_FiberPolyData); idFiberFilter->CellIdsOn(); // idFiberFilter->PointIdsOn(); // point id's are not needed idFiberFilter->SetIdsArrayName(FIBER_ID_ARRAY); idFiberFilter->FieldDataOn(); idFiberFilter->Update(); m_FiberIdDataSet = idFiberFilter->GetOutput(); } float mitk::FiberBundle::GetOverlap(ItkUcharImgType* mask, bool do_resampling) { vtkSmartPointer PolyData = m_FiberPolyData; mitk::FiberBundle::Pointer fibCopy = this; if (do_resampling) { float minSpacing = 1; if(mask->GetSpacing()[0]GetSpacing()[1] && mask->GetSpacing()[0]GetSpacing()[2]) minSpacing = mask->GetSpacing()[0]; else if (mask->GetSpacing()[1] < mask->GetSpacing()[2]) minSpacing = mask->GetSpacing()[1]; else minSpacing = mask->GetSpacing()[2]; fibCopy = this->GetDeepCopy(); fibCopy->ResampleLinear(minSpacing/5); PolyData = fibCopy->GetFiberPolyData(); } MITK_INFO << "Calculating overlap"; int inside = 0; int outside = 0; boost::progress_display disp(m_NumFibers); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(j); itk::Point itkP; itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; itk::Index<3> idx; mask->TransformPhysicalPointToIndex(itkP, idx); if ( mask->GetLargestPossibleRegion().IsInside(idx) && mask->GetPixel(idx) != 0 ) inside++; else outside++; } } if (inside+outside==0) outside = 1; return (float)inside/(inside+outside); } mitk::FiberBundle::Pointer mitk::FiberBundle::RemoveFibersOutside(ItkUcharImgType* mask, bool invert) { float minSpacing = 1; if(mask->GetSpacing()[0]GetSpacing()[1] && mask->GetSpacing()[0]GetSpacing()[2]) minSpacing = mask->GetSpacing()[0]; else if (mask->GetSpacing()[1] < mask->GetSpacing()[2]) minSpacing = mask->GetSpacing()[1]; else minSpacing = mask->GetSpacing()[2]; mitk::FiberBundle::Pointer fibCopy = this->GetDeepCopy(); fibCopy->ResampleLinear(minSpacing/10); vtkSmartPointer PolyData =fibCopy->GetFiberPolyData(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); MITK_INFO << "Cutting fibers"; std::vector new_weights; boost::progress_display disp(m_NumFibers); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); if (numPoints>1) { int newNumPoints = 0; for (int j=0; jGetPoint(j); itk::Point itkP; itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; itk::Index<3> idx; mask->TransformPhysicalPointToIndex(itkP, idx); if ( mask->GetPixel(idx) != 0 && mask->GetLargestPossibleRegion().IsInside(idx) && !invert ) { vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); newNumPoints++; } else if ( (mask->GetPixel(idx) == 0 || !mask->GetLargestPossibleRegion().IsInside(idx)) && invert ) { vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); newNumPoints++; } else if (newNumPoints>0) { vtkNewCells->InsertNextCell(container); newNumPoints = 0; container = vtkSmartPointer::New(); } } if (newNumPoints>1) { vtkNewCells->InsertNextCell(container); new_weights.push_back(this->GetFiberWeight(i)); } } } if (vtkNewCells->GetNumberOfCells()<=0) return nullptr; vtkSmartPointer newPolyData = vtkSmartPointer::New(); newPolyData->SetPoints(vtkNewPoints); newPolyData->SetLines(vtkNewCells); mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(newPolyData); newFib->Compress(0.1); for (unsigned int i=0; iSetFiberWeight(i, new_weights.at(i)); return newFib; } mitk::FiberBundle::Pointer mitk::FiberBundle::ExtractFiberSubset(DataNode* roi, DataStorage* storage) { if (roi==nullptr || !(dynamic_cast(roi->GetData()) || dynamic_cast(roi->GetData())) ) return nullptr; std::vector tmp = ExtractFiberIdSubset(roi, storage); if (tmp.size()<=0) return mitk::FiberBundle::New(); vtkSmartPointer weights = vtkSmartPointer::New(); vtkSmartPointer pTmp = GeneratePolyDataByIds(tmp, weights); mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(pTmp); fib->SetFiberWeights(weights); return fib; } std::vector mitk::FiberBundle::ExtractFiberIdSubset(DataNode *roi, DataStorage* storage) { std::vector result; if (roi==nullptr || roi->GetData()==nullptr) return result; mitk::PlanarFigureComposite::Pointer pfc = dynamic_cast(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::iterator it; for (unsigned int i=1; iSize(); ++i) { std::vector inRoi = this->ExtractFiberIdSubset(children->ElementAt(i), storage); std::vector rest(std::min(result.size(),inRoi.size())); it = std::set_intersection(result.begin(), result.end(), inRoi.begin(), inRoi.end(), rest.begin() ); rest.resize( it - rest.begin() ); result = rest; } break; } case 1: // OR { MITK_INFO << "OR"; result = ExtractFiberIdSubset(children->ElementAt(0), storage); std::vector::iterator it; for (unsigned int i=1; iSize(); ++i) { it = result.end(); std::vector 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( it - result.begin() ); break; } case 2: // NOT { MITK_INFO << "NOT"; for(long i=0; iGetNumFibers(); i++) result.push_back(i); std::vector::iterator it; for (unsigned int i=0; iSize(); ++i) { std::vector inRoi = ExtractFiberIdSubset(children->ElementAt(i), storage); std::vector rest(result.size()-inRoi.size()); it = std::set_difference(result.begin(), result.end(), inRoi.begin(), inRoi.end(), rest.begin() ); rest.resize( it - rest.begin() ); result = rest; } break; } } } else if ( dynamic_cast(roi->GetData()) ) // actual extraction { if ( dynamic_cast(roi->GetData()) ) { mitk::PlanarFigure::Pointer planarPoly = dynamic_cast(roi->GetData()); //create vtkPolygon using controlpoints from planarFigure polygon vtkSmartPointer polygonVtk = vtkSmartPointer::New(); for (unsigned int i=0; iGetNumberOfControlPoints(); ++i) { itk::Point 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::progress_display disp(m_NumFibers); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(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(roi->GetData()) ) { mitk::PlanarFigure::Pointer planarFigure = dynamic_cast(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::progress_display disp(m_NumFibers); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(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 cleaner = vtkSmartPointer::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 = m_FiberPolyData->GetNumberOfCells(); if (m_FiberColors==nullptr || m_FiberColors->GetNumberOfTuples()!=m_FiberPolyData->GetNumberOfPoints()) this->ColorFibersByOrientation(); if (m_FiberWeights->GetNumberOfValues()!=m_NumFibers) { m_FiberWeights = vtkSmartPointer::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; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int p = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); float length = 0; for (int j=0; jGetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); float 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 += dist; } m_FiberLengths.push_back(length); m_MeanFiberLength += length; if (i==0) { m_MinFiberLength = length; m_MaxFiberLength = length; } else { if (lengthm_MaxFiberLength) m_MaxFiberLength = length; } } m_MeanFiberLength /= m_NumFibers; std::vector< float > sortedLengths = m_FiberLengths; std::sort(sortedLengths.begin(), sortedLengths.end()); for (int i=0; i1) 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); 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; iGetNumberOfValues(); i++) m_FiberWeights->SetValue(i, newWeight); } void mitk::FiberBundle::SetFiberWeights(vtkSmartPointer 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; iGetNumberOfValues(); 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 fiberColors) { for(long i=0; iGetNumberOfPoints(); ++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*M_PI/180; - ry = ry*M_PI/180; - rz = rz*M_PI/180; + 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; } itk::Point mitk::FiberBundle::TransformPoint(vnl_vector_fixed< double, 3 > point, double rx, double ry, double rz, double tx, double ty, double tz) { - rx = rx*M_PI/180; - ry = ry*M_PI/180; - rz = rz*M_PI/180; + rx = rx*itk::Math::pi/180; + ry = ry*itk::Math::pi/180; + rz = rz*itk::Math::pi/180; vnl_matrix_fixed< double, 3, 3 > rotX; rotX.set_identity(); rotX[1][1] = cos(rx); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(rx); rotX[2][1] = -rotX[1][2]; vnl_matrix_fixed< double, 3, 3 > rotY; rotY.set_identity(); rotY[0][0] = cos(ry); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(ry); rotY[2][0] = -rotY[0][2]; vnl_matrix_fixed< double, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; vnl_matrix_fixed< double, 3, 3 > rot = rotZ*rotY*rotX; mitk::BaseGeometry::Pointer geom = this->GetGeometry(); mitk::Point3D center = geom->GetCenter(); point[0] -= center[0]; point[1] -= center[1]; point[2] -= center[2]; point = rot*point; point[0] += center[0]+tx; point[1] += center[1]+ty; point[2] += center[2]+tz; itk::Point out; out[0] = point[0]; out[1] = point[1]; out[2] = point[2]; return out; } void mitk::FiberBundle::TransformFibers(itk::ScalableAffineTransform< mitk::ScalarType >::Pointer transform) { vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; j p = GetItkPoint(points->GetPoint(j)); p = transform->TransformPoint(p); vtkIdType id = vtkNewPoints->InsertNextPoint(p.GetDataPointer()); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer::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) { - rx = rx*M_PI/180; - ry = ry*M_PI/180; - rz = rz*M_PI/180; + rx = rx*itk::Math::pi/180; + ry = ry*itk::Math::pi/180; + rz = rz*itk::Math::pi/180; vnl_matrix_fixed< double, 3, 3 > rotX; rotX.set_identity(); rotX[1][1] = cos(rx); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(rx); rotX[2][1] = -rotX[1][2]; vnl_matrix_fixed< double, 3, 3 > rotY; rotY.set_identity(); rotY[0][0] = cos(ry); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(ry); rotY[2][0] = -rotY[0][2]; vnl_matrix_fixed< double, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; vnl_matrix_fixed< double, 3, 3 > rot = rotZ*rotY*rotX; mitk::BaseGeometry::Pointer geom = this->GetGeometry(); mitk::Point3D center = geom->GetCenter(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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::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*M_PI/180; - y = y*M_PI/180; - z = z*M_PI/180; + 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 vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::ScaleFibers(double x, double y, double z, bool subtractCenter) { MITK_INFO << "Scaling fibers"; boost::progress_display disp(m_NumFibers); mitk::BaseGeometry* geom = this->GetGeometry(); mitk::Point3D c = geom->GetCenter(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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::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 vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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::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::progress_display disp(m_NumFibers); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(j); p[axis] = -p[axis]; vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::RemoveDir(vnl_vector_fixed dir, double threshold) { dir.normalize(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); for (int i=0; iGetNumberOfCells(); i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures vtkSmartPointer container = vtkSmartPointer::New(); bool discard = false; for (int j=0; jGetPoint(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; jGetPoint(j, p1); vtkIdType id = vtkNewPoints->InsertNextPoint(p1); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } } m_FiberPolyData = vtkSmartPointer::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 vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); MITK_INFO << "Applying curvature threshold"; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); for (int i=0; iGetNumberOfCells(); i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(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] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; v2[0] = p3[0]-p2[0]; v2[1] = p3[1]-p2[1]; v2[2] = p3[2]-p2[2]; v3[0] = p1[0]-p3[0]; v3[1] = p1[1]-p3[1]; v3[2] = 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 && rInsertNextCell(container); container = vtkSmartPointer::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::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 || lengthInMMm_MaxFiberLength) // can't remove all fibers { MITK_WARN << "Process aborted. No fibers would be left!"; return false; } vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); float min = m_MaxFiberLength; boost::progress_display disp(m_NumFibers); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (m_FiberLengths.at(i)>=lengthInMM) { vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); if (m_FiberLengths.at(i)GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer::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 vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); MITK_INFO << "Removing long fibers"; boost::progress_display disp(m_NumFibers); for (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (m_FiberLengths.at(i)<=lengthInMM) { vtkSmartPointer container = vtkSmartPointer::New(); for (int j=0; jGetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer::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 vtkSmoothPoints = vtkSmartPointer::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 vtkSmoothCells = vtkSmartPointer::New(); //cellcontainer for smoothed lines MITK_INFO << "Smoothing fibers"; vtkSmartPointer newFiberWeights = vtkSmartPointer::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); std::vector< vtkSmartPointer > resampled_streamlines; resampled_streamlines.resize(m_NumFibers); boost::progress_display disp(m_NumFibers); #pragma omp parallel for for (int i=0; i newPoints = vtkSmartPointer::New(); float length = 0; #pragma omp critical { length = m_FiberLengths.at(i); ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jInsertNextPoint(points->GetPoint(j)); } int sampling = std::ceil(length/pointDistance); vtkSmartPointer xSpline = vtkSmartPointer::New(); vtkSmartPointer ySpline = vtkSmartPointer::New(); vtkSmartPointer zSpline = vtkSmartPointer::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 spline = vtkSmartPointer::New(); spline->SetXSpline(xSpline); spline->SetYSpline(ySpline); spline->SetZSpline(zSpline); spline->SetPoints(newPoints); vtkSmartPointer functionSource = vtkSmartPointer::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 smoothLine = vtkSmartPointer::New(); #pragma omp critical { for (int j=0; jGetNumberOfPoints(); j++) { vtkIdType id = vtkSmoothPoints->InsertNextPoint(tmpSmoothPnts->GetPoint(j)); smoothLine->GetPointIds()->InsertNextId(id); } resampled_streamlines[i] = smoothLine; } } for (auto container : resampled_streamlines) { vtkSmoothCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer::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 long mitk::FiberBundle::GetNumberOfPoints() const { unsigned long points = 0; for (int i=0; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); points += cell->GetNumberOfPoints(); } return points; } void mitk::FiberBundle::Compress(float error) { vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); MITK_INFO << "Compressing fibers"; unsigned long numRemovedPoints = 0; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); vtkSmartPointer newFiberWeights = vtkSmartPointer::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); #pragma omp parallel for for (int i=0; iGetNumberOfCells(); 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); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(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 int numPoints = vertices.size(); std::vector< int > removedPoints; removedPoints.resize(numPoints, 0); removedPoints[0]=-1; removedPoints[numPoints-1]=-1; vtkSmartPointer container = vtkSmartPointer::New(); int remCounter = 0; bool pointFound = true; while (pointFound) { pointFound = false; double minError = error; int removeIndex = -1; for (unsigned int j=0; j candV = vertices.at(j); int validP = -1; vnl_vector_fixed< double, 3 > pred; for (int k=j-1; k>=0; k--) if (removedPoints[k]<=0) { pred = vertices.at(k); validP = k; break; } int validS = -1; vnl_vector_fixed< double, 3 > succ; for (int k=j+1; k=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 (hcInsertNextPoint(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::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 vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); vtkSmartPointer newFiberWeights = vtkSmartPointer::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); unequal_fibs = false; //#pragma omp parallel for for (int i=0; iGetNumberOfCells(); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; float weight = 1; double seg_len = 0; //#pragma omp critical { weight = m_FiberWeights->GetValue(i); vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); if ((unsigned int)numPoints!=targetPoints) seg_len = this->GetFiberLength(i)/(targetPoints-1);; vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(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 container = vtkSmartPointer::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 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) { //#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::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } } } void mitk::FiberBundle::ResampleLinear(double pointDistance) { vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); MITK_INFO << "Resampling fibers (linear)"; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); vtkSmartPointer newFiberWeights = vtkSmartPointer::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); std::vector< vtkSmartPointer > resampled_streamlines; resampled_streamlines.resize(m_FiberPolyData->GetNumberOfCells()); #pragma omp parallel for for (int i=0; iGetNumberOfCells(); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; #pragma omp critical { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(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 container = vtkSmartPointer::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 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[i] = container; } } for (auto container : resampled_streamlines) { vtkNewCells->InsertNextCell(container); } if (vtkNewCells->GetNumberOfCells()>0) { m_FiberPolyData = vtkSmartPointer::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 (int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkCell* cell2 = fib->GetFiberPolyData()->GetCell(i); int 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; jGetPoint(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 << this->GetNameOfClass() << ":\n"; 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; Superclass::PrintSelf(os, indent); } /* 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/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp index 10bc1d4d8e..68191f92f7 100644 --- a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp @@ -1,968 +1,968 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ #define RAPIDXML_NO_EXCEPTIONS #include #include #include #include #include #include #include #include mitk::FiberfoxParameters::FiberfoxParameters() : m_NoiseModel(nullptr) { } mitk::FiberfoxParameters::FiberfoxParameters(const mitk::FiberfoxParameters& params) : m_NoiseModel(nullptr) { m_FiberGen = params.m_FiberGen; m_SignalGen = params.m_SignalGen; m_Misc = params.m_Misc; if (params.m_NoiseModel!=nullptr) { if (dynamic_cast*>(params.m_NoiseModel.get())) m_NoiseModel = std::make_shared< mitk::RicianNoiseModel<> >(); else if (dynamic_cast*>(params.m_NoiseModel.get())) m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel<> >(); m_NoiseModel->SetNoiseVariance(params.m_NoiseModel->GetNoiseVariance()); } for (unsigned int i=0; i* outModel = nullptr; mitk::DiffusionSignalModel<>* signalModel = nullptr; if (i*>(signalModel)) outModel = new mitk::StickModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::TensorModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::RawShModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::BallModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::AstroStickModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::DotModel<>(dynamic_cast*>(signalModel)); if (i theta; theta.set_size(NPoints); vnl_vector phi; phi.set_size(NPoints); - double C = sqrt(4*M_PI); + double C = sqrt(4*itk::Math::pi); phi(0) = 0.0; phi(NPoints-1) = 0.0; for(int i=0; i0 && i mitk::SignalGenerationParameters::GetBaselineIndices() { std::vector< int > result; for( unsigned int i=0; im_GradientDirections.size(); i++) if (m_GradientDirections.at(i).GetNorm()<0.0001) result.push_back(i); return result; } unsigned int mitk::SignalGenerationParameters::GetFirstBaselineIndex() { for( unsigned int i=0; im_GradientDirections.size(); i++) if (m_GradientDirections.at(i).GetNorm()<0.0001) return i; return -1; } bool mitk::SignalGenerationParameters::IsBaselineIndex(unsigned int idx) { if (m_GradientDirections.size()>idx && m_GradientDirections.at(idx).GetNorm()<0.0001) return true; return false; } unsigned int mitk::SignalGenerationParameters::GetNumWeightedVolumes() { return m_NumGradients; } unsigned int mitk::SignalGenerationParameters::GetNumBaselineVolumes() { return m_NumBaseline; } unsigned int mitk::SignalGenerationParameters::GetNumVolumes() { return m_GradientDirections.size(); } mitk::SignalGenerationParameters::GradientListType mitk::SignalGenerationParameters::GetGradientDirections() { return m_GradientDirections; } mitk::SignalGenerationParameters::GradientType mitk::SignalGenerationParameters::GetGradientDirection(unsigned int i) { return m_GradientDirections.at(i); } void mitk::SignalGenerationParameters::SetNumWeightedVolumes(int numGradients) { m_NumGradients = numGradients; GenerateGradientHalfShell(); } std::vector< int > mitk::SignalGenerationParameters::GetBvalues() { std::vector< int > bVals; for( GradientType g : m_GradientDirections) { float norm = g.GetNorm(); int bVal = std::round(norm*norm*m_Bvalue); if ( std::find(bVals.begin(), bVals.end(), bVal) == bVals.end() ) bVals.push_back(bVal); } return bVals; } double mitk::SignalGenerationParameters::GetBvalue() { return m_Bvalue; } void mitk::SignalGenerationParameters::SetGradienDirections(GradientListType gradientList) { m_GradientDirections = gradientList; m_NumGradients = 0; m_NumBaseline = 0; for( unsigned int i=0; im_GradientDirections.size(); i++) { float norm = m_GradientDirections.at(i).GetNorm(); if (norm>0.0001) m_NumGradients++; else m_NumBaseline++; } } void mitk::SignalGenerationParameters::SetGradienDirections(mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer gradientList) { m_NumGradients = 0; m_NumBaseline = 0; m_GradientDirections.clear(); for( unsigned int i=0; iSize(); i++) { GradientType g; g[0] = gradientList->at(i)[0]; g[1] = gradientList->at(i)[1]; g[2] = gradientList->at(i)[2]; m_GradientDirections.push_back(g); float norm = m_GradientDirections.at(i).GetNorm(); if (norm>0.0001) m_NumGradients++; else m_NumBaseline++; } } void mitk::FiberfoxParameters::SaveParameters(std::string filename) { if(filename.empty()) return; if(".ffp"!=filename.substr(filename.size()-4, 4)) filename += ".ffp"; 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; // fiber generation parameters parameters.put("fiberfox.fibers.distribution", m_FiberGen.m_Distribution); parameters.put("fiberfox.fibers.variance", m_FiberGen.m_Variance); parameters.put("fiberfox.fibers.density", m_FiberGen.m_Density); parameters.put("fiberfox.fibers.spline.sampling", m_FiberGen.m_Sampling); parameters.put("fiberfox.fibers.spline.tension", m_FiberGen.m_Tension); parameters.put("fiberfox.fibers.spline.continuity", m_FiberGen.m_Continuity); parameters.put("fiberfox.fibers.spline.bias", m_FiberGen.m_Bias); parameters.put("fiberfox.fibers.rotation.x", m_FiberGen.m_Rotation[0]); parameters.put("fiberfox.fibers.rotation.y", m_FiberGen.m_Rotation[1]); parameters.put("fiberfox.fibers.rotation.z", m_FiberGen.m_Rotation[2]); parameters.put("fiberfox.fibers.translation.x", m_FiberGen.m_Translation[0]); parameters.put("fiberfox.fibers.translation.y", m_FiberGen.m_Translation[1]); parameters.put("fiberfox.fibers.translation.z", m_FiberGen.m_Translation[2]); parameters.put("fiberfox.fibers.scale.x", m_FiberGen.m_Scale[0]); parameters.put("fiberfox.fibers.scale.y", m_FiberGen.m_Scale[1]); parameters.put("fiberfox.fibers.scale.z", m_FiberGen.m_Scale[2]); // image generation parameters parameters.put("fiberfox.image.basic.size.x", m_SignalGen.m_ImageRegion.GetSize(0)); parameters.put("fiberfox.image.basic.size.y", m_SignalGen.m_ImageRegion.GetSize(1)); parameters.put("fiberfox.image.basic.size.z", m_SignalGen.m_ImageRegion.GetSize(2)); parameters.put("fiberfox.image.basic.spacing.x", m_SignalGen.m_ImageSpacing[0]); parameters.put("fiberfox.image.basic.spacing.y", m_SignalGen.m_ImageSpacing[1]); parameters.put("fiberfox.image.basic.spacing.z", m_SignalGen.m_ImageSpacing[2]); parameters.put("fiberfox.image.basic.origin.x", m_SignalGen.m_ImageOrigin[0]); parameters.put("fiberfox.image.basic.origin.y", m_SignalGen.m_ImageOrigin[1]); parameters.put("fiberfox.image.basic.origin.z", m_SignalGen.m_ImageOrigin[2]); parameters.put("fiberfox.image.basic.direction.1", m_SignalGen.m_ImageDirection[0][0]); parameters.put("fiberfox.image.basic.direction.2", m_SignalGen.m_ImageDirection[0][1]); parameters.put("fiberfox.image.basic.direction.3", m_SignalGen.m_ImageDirection[0][2]); parameters.put("fiberfox.image.basic.direction.4", m_SignalGen.m_ImageDirection[1][0]); parameters.put("fiberfox.image.basic.direction.5", m_SignalGen.m_ImageDirection[1][1]); parameters.put("fiberfox.image.basic.direction.6", m_SignalGen.m_ImageDirection[1][2]); parameters.put("fiberfox.image.basic.direction.7", m_SignalGen.m_ImageDirection[2][0]); parameters.put("fiberfox.image.basic.direction.8", m_SignalGen.m_ImageDirection[2][1]); parameters.put("fiberfox.image.basic.direction.9", m_SignalGen.m_ImageDirection[2][2]); parameters.put("fiberfox.image.basic.numgradients", m_SignalGen.GetNumWeightedVolumes()); for( unsigned int i=0; im_SignalGen.GetNumVolumes(); i++) { parameters.put("fiberfox.image.gradients."+boost::lexical_cast(i)+".x", m_SignalGen.GetGradientDirection(i)[0]); parameters.put("fiberfox.image.gradients."+boost::lexical_cast(i)+".y", m_SignalGen.GetGradientDirection(i)[1]); parameters.put("fiberfox.image.gradients."+boost::lexical_cast(i)+".z", m_SignalGen.GetGradientDirection(i)[2]); } parameters.put("fiberfox.image.acquisitiontype", m_SignalGen.m_AcquisitionType); parameters.put("fiberfox.image.coilsensitivityprofile", m_SignalGen.m_CoilSensitivityProfile); parameters.put("fiberfox.image.numberofcoils", m_SignalGen.m_NumberOfCoils); parameters.put("fiberfox.image.reversephase", m_SignalGen.m_ReversePhase); parameters.put("fiberfox.image.partialfourier", m_SignalGen.m_PartialFourier); parameters.put("fiberfox.image.noisevariance", m_SignalGen.m_NoiseVariance); parameters.put("fiberfox.image.trep", m_SignalGen.m_tRep); parameters.put("fiberfox.image.signalScale", m_SignalGen.m_SignalScale); parameters.put("fiberfox.image.tEcho", m_SignalGen.m_tEcho); parameters.put("fiberfox.image.tLine", m_SignalGen.m_tLine); parameters.put("fiberfox.image.tInhom", m_SignalGen.m_tInhom); parameters.put("fiberfox.image.bvalue", m_SignalGen.m_Bvalue); parameters.put("fiberfox.image.simulatekspace", m_SignalGen.m_SimulateKspaceAcquisition); parameters.put("fiberfox.image.axonRadius", m_SignalGen.m_AxonRadius); parameters.put("fiberfox.image.doSimulateRelaxation", m_SignalGen.m_DoSimulateRelaxation); parameters.put("fiberfox.image.doDisablePartialVolume", m_SignalGen.m_DoDisablePartialVolume); parameters.put("fiberfox.image.artifacts.spikesnum", m_SignalGen.m_Spikes); parameters.put("fiberfox.image.artifacts.spikesscale", m_SignalGen.m_SpikeAmplitude); parameters.put("fiberfox.image.artifacts.kspaceLineOffset", m_SignalGen.m_KspaceLineOffset); parameters.put("fiberfox.image.artifacts.eddyStrength", m_SignalGen.m_EddyStrength); parameters.put("fiberfox.image.artifacts.eddyTau", m_SignalGen.m_Tau); parameters.put("fiberfox.image.artifacts.aliasingfactor", m_SignalGen.m_CroppingFactor); parameters.put("fiberfox.image.artifacts.addringing", m_SignalGen.m_DoAddGibbsRinging); parameters.put("fiberfox.image.artifacts.doAddMotion", m_SignalGen.m_DoAddMotion); parameters.put("fiberfox.image.artifacts.randomMotion", m_SignalGen.m_DoRandomizeMotion); parameters.put("fiberfox.image.artifacts.translation0", m_SignalGen.m_Translation[0]); parameters.put("fiberfox.image.artifacts.translation1", m_SignalGen.m_Translation[1]); parameters.put("fiberfox.image.artifacts.translation2", m_SignalGen.m_Translation[2]); parameters.put("fiberfox.image.artifacts.rotation0", m_SignalGen.m_Rotation[0]); parameters.put("fiberfox.image.artifacts.rotation1", m_SignalGen.m_Rotation[1]); parameters.put("fiberfox.image.artifacts.rotation2", m_SignalGen.m_Rotation[2]); parameters.put("fiberfox.image.artifacts.motionvolumes", m_Misc.m_MotionVolumesBox); parameters.put("fiberfox.image.artifacts.addnoise", m_Misc.m_CheckAddNoiseBox); parameters.put("fiberfox.image.artifacts.addghosts", m_Misc.m_CheckAddGhostsBox); parameters.put("fiberfox.image.artifacts.addaliasing", m_Misc.m_CheckAddAliasingBox); parameters.put("fiberfox.image.artifacts.addspikes", m_Misc.m_CheckAddSpikesBox); parameters.put("fiberfox.image.artifacts.addeddycurrents", m_Misc.m_CheckAddEddyCurrentsBox); parameters.put("fiberfox.image.artifacts.doAddDistortions", m_Misc.m_CheckAddDistortionsBox); parameters.put("fiberfox.image.outputvolumefractions", m_Misc.m_CheckOutputVolumeFractionsBox); parameters.put("fiberfox.image.showadvanced", m_Misc.m_CheckAdvancedSignalOptionsBox); parameters.put("fiberfox.image.signalmodelstring", m_Misc.m_SignalModelString); parameters.put("fiberfox.image.artifactmodelstring", m_Misc.m_ArtifactModelString); parameters.put("fiberfox.image.outpath", m_Misc.m_OutputPath); parameters.put("fiberfox.fibers.realtime", m_Misc.m_CheckRealTimeFibersBox); parameters.put("fiberfox.fibers.showadvanced", m_Misc.m_CheckAdvancedFiberOptionsBox); parameters.put("fiberfox.fibers.constantradius", m_Misc.m_CheckConstantRadiusBox); parameters.put("fiberfox.fibers.includeFiducials", m_Misc.m_CheckIncludeFiducialsBox); if (m_NoiseModel!=nullptr) { parameters.put("fiberfox.image.artifacts.noisevariance", m_NoiseModel->GetNoiseVariance()); if (dynamic_cast*>(m_NoiseModel.get())) parameters.put("fiberfox.image.artifacts.noisetype", "rice"); else if (dynamic_cast*>(m_NoiseModel.get())) parameters.put("fiberfox.image.artifacts.noisetype", "chisquare"); } for (std::size_t i=0; i* signalModel = nullptr; if (i(i)+".type", "fiber"); } else { signalModel = m_NonFiberModelList.at(i-m_FiberModelList.size()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".type", "non-fiber"); } if (dynamic_cast*>(signalModel)) { mitk::StickModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".model", "stick"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t1", model->GetT1()); } else if (dynamic_cast*>(signalModel)) { mitk::TensorModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".model", "tensor"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".d1", model->GetDiffusivity1()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".d2", model->GetDiffusivity2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".d3", model->GetDiffusivity3()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t1", model->GetT1()); } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".model", "prototype"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".minFA", model->GetFaRange().first); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".maxFA", model->GetFaRange().second); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".minADC", model->GetAdcRange().first); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".maxADC", model->GetAdcRange().second); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".maxNumSamples", model->GetMaxNumKernels()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".numSamples", model->GetNumberOfKernels()); int shOrder = model->GetShOrder(); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".numCoeffs", (shOrder*shOrder + shOrder + 2)/2 + shOrder); for (unsigned int j=0; jGetNumberOfKernels(); j++) { vnl_vector< double > coeffs = model->GetCoefficients(j); for (unsigned int k=0; k(i)+".kernels."+boost::lexical_cast(j)+".coeffs."+boost::lexical_cast(k), coeffs[k]); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".kernels."+boost::lexical_cast(j)+".B0", model->GetBaselineSignal(j)); } } else if (dynamic_cast*>(signalModel)) { mitk::BallModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".model", "ball"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t1", model->GetT1()); } else if (dynamic_cast*>(signalModel)) { mitk::AstroStickModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".model", "astrosticks"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t1", model->GetT1()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".randomize", model->GetRandomizeSticks()); } else if (dynamic_cast*>(signalModel)) { mitk::DotModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".model", "dot"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".t1", model->GetT1()); } if (signalModel!=nullptr) { parameters.put("fiberfox.image.compartments."+boost::lexical_cast(i)+".ID", signalModel->m_CompartmentId); if (signalModel->GetVolumeFractionImage().IsNotNull()) { try{ itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(filename+"_VOLUME"+boost::lexical_cast(signalModel->m_CompartmentId)+".nrrd"); writer->SetInput(signalModel->GetVolumeFractionImage()); writer->Update(); MITK_INFO << "Volume fraction image for compartment "+boost::lexical_cast(signalModel->m_CompartmentId)+" saved."; } catch(...) { } } } } boost::property_tree::xml_writer_settings writerSettings(' ', 2); boost::property_tree::xml_parser::write_xml(filename, parameters, std::locale(), writerSettings); try{ itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(filename+"_FMAP.nrrd"); writer->SetInput(m_SignalGen.m_FrequencyMap); writer->Update(); } catch(...) { MITK_INFO << "No frequency map saved."; } try{ itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(filename+"_MASK.nrrd"); writer->SetInput(m_SignalGen.m_MaskImage); writer->Update(); } catch(...) { MITK_INFO << "No mask image saved."; } setlocale(LC_ALL, currLocale.c_str()); } template< class ParameterType > ParameterType mitk::FiberfoxParameters::ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential) { try { return v.second.get(tag); } catch (...) { if (essential) { mitkThrow() << "Parameter file corrupted. Essential tag is missing: '" << tag << "'"; } if (tag!="artifacts.noisetype") { MITK_INFO << "Tag '" << tag << "' not found. Using default value '" << defaultValue << "'."; m_MissingTags += "\n- "; m_MissingTags += tag; } return defaultValue; } } void mitk::FiberfoxParameters::UpdateSignalModels() { for (mitk::DiffusionSignalModel<>* m : m_FiberModelList) { m->SetGradientList(m_SignalGen.m_GradientDirections); m->SetBvalue(m_SignalGen.m_Bvalue); } for (mitk::DiffusionSignalModel<>* m : m_NonFiberModelList) { m->SetGradientList(m_SignalGen.m_GradientDirections); m->SetBvalue(m_SignalGen.m_Bvalue); } } void mitk::FiberfoxParameters::SetNumWeightedVolumes(int numGradients) { m_SignalGen.SetNumWeightedVolumes(numGradients); UpdateSignalModels(); } void mitk::FiberfoxParameters::SetGradienDirections(mitk::SignalGenerationParameters::GradientListType gradientList) { m_SignalGen.SetGradienDirections(gradientList); UpdateSignalModels(); } void mitk::FiberfoxParameters::SetGradienDirections(mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer gradientList) { m_SignalGen.SetGradienDirections(gradientList); UpdateSignalModels(); } void mitk::FiberfoxParameters::SetBvalue(double Bvalue) { m_SignalGen.m_Bvalue = Bvalue; UpdateSignalModels(); } void mitk::FiberfoxParameters::GenerateGradientHalfShell() { m_SignalGen.GenerateGradientHalfShell(); UpdateSignalModels(); } void mitk::FiberfoxParameters::LoadParameters(std::string filename) { m_MissingTags = ""; 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::xml_parser::read_xml( filename, parameterTree ); m_FiberModelList.clear(); m_NonFiberModelList.clear(); if (m_NoiseModel) { m_NoiseModel = nullptr; } BOOST_FOREACH( boost::property_tree::ptree::value_type const& v1, parameterTree.get_child("fiberfox") ) { if( v1.first == "fibers" ) { m_Misc.m_CheckRealTimeFibersBox = ReadVal(v1,"realtime", m_Misc.m_CheckRealTimeFibersBox); m_Misc.m_CheckAdvancedFiberOptionsBox = ReadVal(v1,"showadvanced", m_Misc.m_CheckAdvancedFiberOptionsBox); m_Misc.m_CheckConstantRadiusBox = ReadVal(v1,"constantradius", m_Misc.m_CheckConstantRadiusBox); m_Misc.m_CheckIncludeFiducialsBox = ReadVal(v1,"includeFiducials", m_Misc.m_CheckIncludeFiducialsBox); switch (ReadVal(v1,"distribution", 0)) { case 0: m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_UNIFORM; break; case 1: m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_GAUSSIAN; break; default: m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_UNIFORM; } m_FiberGen.m_Variance = ReadVal(v1,"variance", m_FiberGen.m_Variance); m_FiberGen.m_Density = ReadVal(v1,"density", m_FiberGen.m_Density); m_FiberGen.m_Sampling = ReadVal(v1,"spline.sampling", m_FiberGen.m_Sampling); m_FiberGen.m_Tension = ReadVal(v1,"spline.tension", m_FiberGen.m_Tension); m_FiberGen.m_Continuity = ReadVal(v1,"spline.continuity", m_FiberGen.m_Continuity); m_FiberGen.m_Bias = ReadVal(v1,"spline.bias", m_FiberGen.m_Bias); m_FiberGen.m_Rotation[0] = ReadVal(v1,"rotation.x", m_FiberGen.m_Rotation[0]); m_FiberGen.m_Rotation[1] = ReadVal(v1,"rotation.y", m_FiberGen.m_Rotation[1]); m_FiberGen.m_Rotation[2] = ReadVal(v1,"rotation.z", m_FiberGen.m_Rotation[2]); m_FiberGen.m_Translation[0] = ReadVal(v1,"translation.x", m_FiberGen.m_Translation[0]); m_FiberGen.m_Translation[1] = ReadVal(v1,"translation.y", m_FiberGen.m_Translation[1]); m_FiberGen.m_Translation[2] = ReadVal(v1,"translation.z", m_FiberGen.m_Translation[2]); m_FiberGen.m_Scale[0] = ReadVal(v1,"scale.x", m_FiberGen.m_Scale[0]); m_FiberGen.m_Scale[1] = ReadVal(v1,"scale.y", m_FiberGen.m_Scale[1]); m_FiberGen.m_Scale[2] = ReadVal(v1,"scale.z", m_FiberGen.m_Scale[2]); } else if ( v1.first == "image" ) { m_Misc.m_SignalModelString = ReadVal(v1,"signalmodelstring", m_Misc.m_SignalModelString); m_Misc.m_ArtifactModelString = ReadVal(v1,"artifactmodelstring", m_Misc.m_ArtifactModelString); m_Misc.m_OutputPath = ReadVal(v1,"outpath", m_Misc.m_OutputPath); m_Misc.m_CheckOutputVolumeFractionsBox = ReadVal(v1,"outputvolumefractions", m_Misc.m_CheckOutputVolumeFractionsBox); m_Misc.m_CheckAdvancedSignalOptionsBox = ReadVal(v1,"showadvanced", m_Misc.m_CheckAdvancedSignalOptionsBox); m_Misc.m_CheckAddDistortionsBox = ReadVal(v1,"artifacts.doAddDistortions", m_Misc.m_CheckAddDistortionsBox); m_Misc.m_CheckAddNoiseBox = ReadVal(v1,"artifacts.addnoise", m_Misc.m_CheckAddNoiseBox); m_Misc.m_CheckAddGhostsBox = ReadVal(v1,"artifacts.addghosts", m_Misc.m_CheckAddGhostsBox); m_Misc.m_CheckAddAliasingBox = ReadVal(v1,"artifacts.addaliasing", m_Misc.m_CheckAddAliasingBox); m_Misc.m_CheckAddSpikesBox = ReadVal(v1,"artifacts.addspikes", m_Misc.m_CheckAddSpikesBox); m_Misc.m_CheckAddEddyCurrentsBox = ReadVal(v1,"artifacts.addeddycurrents", m_Misc.m_CheckAddEddyCurrentsBox); m_SignalGen.m_ImageRegion.SetSize(0, ReadVal(v1,"basic.size.x",m_SignalGen.m_ImageRegion.GetSize(0))); m_SignalGen.m_ImageRegion.SetSize(1, ReadVal(v1,"basic.size.y",m_SignalGen.m_ImageRegion.GetSize(1))); m_SignalGen.m_ImageRegion.SetSize(2, ReadVal(v1,"basic.size.z",m_SignalGen.m_ImageRegion.GetSize(2))); m_SignalGen.m_ImageSpacing[0] = ReadVal(v1,"basic.spacing.x",m_SignalGen.m_ImageSpacing[0]); m_SignalGen.m_ImageSpacing[1] = ReadVal(v1,"basic.spacing.y",m_SignalGen.m_ImageSpacing[1]); m_SignalGen.m_ImageSpacing[2] = ReadVal(v1,"basic.spacing.z",m_SignalGen.m_ImageSpacing[2]); m_SignalGen.m_ImageOrigin[0] = ReadVal(v1,"basic.origin.x",m_SignalGen.m_ImageOrigin[0]); m_SignalGen.m_ImageOrigin[1] = ReadVal(v1,"basic.origin.y",m_SignalGen.m_ImageOrigin[1]); m_SignalGen.m_ImageOrigin[2] = ReadVal(v1,"basic.origin.z",m_SignalGen.m_ImageOrigin[2]); m_SignalGen.m_ImageDirection[0][0] = ReadVal(v1,"basic.direction.1",m_SignalGen.m_ImageDirection[0][0]); m_SignalGen.m_ImageDirection[0][1] = ReadVal(v1,"basic.direction.2",m_SignalGen.m_ImageDirection[0][1]); m_SignalGen.m_ImageDirection[0][2] = ReadVal(v1,"basic.direction.3",m_SignalGen.m_ImageDirection[0][2]); m_SignalGen.m_ImageDirection[1][0] = ReadVal(v1,"basic.direction.4",m_SignalGen.m_ImageDirection[1][0]); m_SignalGen.m_ImageDirection[1][1] = ReadVal(v1,"basic.direction.5",m_SignalGen.m_ImageDirection[1][1]); m_SignalGen.m_ImageDirection[1][2] = ReadVal(v1,"basic.direction.6",m_SignalGen.m_ImageDirection[1][2]); m_SignalGen.m_ImageDirection[2][0] = ReadVal(v1,"basic.direction.7",m_SignalGen.m_ImageDirection[2][0]); m_SignalGen.m_ImageDirection[2][1] = ReadVal(v1,"basic.direction.8",m_SignalGen.m_ImageDirection[2][1]); m_SignalGen.m_ImageDirection[2][2] = ReadVal(v1,"basic.direction.9",m_SignalGen.m_ImageDirection[2][2]); m_SignalGen.m_AcquisitionType = (SignalGenerationParameters::AcquisitionType) ReadVal(v1,"acquisitiontype", m_SignalGen.m_AcquisitionType); m_SignalGen.m_CoilSensitivityProfile = (SignalGenerationParameters::CoilSensitivityProfile) ReadVal(v1,"coilsensitivityprofile", m_SignalGen.m_CoilSensitivityProfile); m_SignalGen.m_NumberOfCoils = ReadVal(v1,"numberofcoils", m_SignalGen.m_NumberOfCoils); m_SignalGen.m_ReversePhase = ReadVal(v1,"reversephase", m_SignalGen.m_ReversePhase); m_SignalGen.m_PartialFourier = ReadVal(v1,"partialfourier", m_SignalGen.m_PartialFourier); m_SignalGen.m_NoiseVariance = ReadVal(v1,"noisevariance", m_SignalGen.m_NoiseVariance); m_SignalGen.m_tRep = ReadVal(v1,"trep", m_SignalGen.m_tRep); m_SignalGen.m_SignalScale = ReadVal(v1,"signalScale", m_SignalGen.m_SignalScale); m_SignalGen.m_tEcho = ReadVal(v1,"tEcho", m_SignalGen.m_tEcho); m_SignalGen.m_tLine = ReadVal(v1,"tLine", m_SignalGen.m_tLine); m_SignalGen.m_tInhom = ReadVal(v1,"tInhom", m_SignalGen.m_tInhom); m_SignalGen.m_Bvalue = ReadVal(v1,"bvalue", m_SignalGen.m_Bvalue); m_SignalGen.m_SimulateKspaceAcquisition = ReadVal(v1,"simulatekspace", m_SignalGen.m_SimulateKspaceAcquisition); m_SignalGen.m_AxonRadius = ReadVal(v1,"axonRadius", m_SignalGen.m_AxonRadius); m_SignalGen.m_Spikes = ReadVal(v1,"artifacts.spikesnum", m_SignalGen.m_Spikes); m_SignalGen.m_SpikeAmplitude = ReadVal(v1,"artifacts.spikesscale", m_SignalGen.m_SpikeAmplitude); m_SignalGen.m_KspaceLineOffset = ReadVal(v1,"artifacts.kspaceLineOffset", m_SignalGen.m_KspaceLineOffset); m_SignalGen.m_EddyStrength = ReadVal(v1,"artifacts.eddyStrength", m_SignalGen.m_EddyStrength); m_SignalGen.m_Tau = ReadVal(v1,"artifacts.eddyTau", m_SignalGen.m_Tau); m_SignalGen.m_CroppingFactor = ReadVal(v1,"artifacts.aliasingfactor", m_SignalGen.m_CroppingFactor); m_SignalGen.m_DoAddGibbsRinging = ReadVal(v1,"artifacts.addringing", m_SignalGen.m_DoAddGibbsRinging); m_SignalGen.m_DoSimulateRelaxation = ReadVal(v1,"doSimulateRelaxation", m_SignalGen.m_DoSimulateRelaxation); m_SignalGen.m_DoDisablePartialVolume = ReadVal(v1,"doDisablePartialVolume", m_SignalGen.m_DoDisablePartialVolume); m_SignalGen.m_DoAddMotion = ReadVal(v1,"artifacts.doAddMotion", m_SignalGen.m_DoAddMotion); m_SignalGen.m_DoRandomizeMotion = ReadVal(v1,"artifacts.randomMotion", m_SignalGen.m_DoRandomizeMotion); m_SignalGen.m_Translation[0] = ReadVal(v1,"artifacts.translation0", m_SignalGen.m_Translation[0]); m_SignalGen.m_Translation[1] = ReadVal(v1,"artifacts.translation1", m_SignalGen.m_Translation[1]); m_SignalGen.m_Translation[2] = ReadVal(v1,"artifacts.translation2", m_SignalGen.m_Translation[2]); m_SignalGen.m_Rotation[0] = ReadVal(v1,"artifacts.rotation0", m_SignalGen.m_Rotation[0]); m_SignalGen.m_Rotation[1] = ReadVal(v1,"artifacts.rotation1", m_SignalGen.m_Rotation[1]); m_SignalGen.m_Rotation[2] = ReadVal(v1,"artifacts.rotation2", m_SignalGen.m_Rotation[2]); // m_SignalGen.SetNumWeightedVolumes(ReadVal(v1,"numgradients", m_SignalGen.GetNumWeightedVolumes())); SignalGenerationParameters::GradientListType gradients; BOOST_FOREACH( boost::property_tree::ptree::value_type const& v2, v1.second.get_child("gradients") ) { SignalGenerationParameters::GradientType g; g[0] = ReadVal(v2,"x",0); g[1] = ReadVal(v2,"y",0); g[2] = ReadVal(v2,"z",0); gradients.push_back(g); } m_SignalGen.SetGradienDirections(gradients); m_Misc.m_MotionVolumesBox = ReadVal(v1,"artifacts.motionvolumes", m_Misc.m_MotionVolumesBox); m_SignalGen.m_MotionVolumes.clear(); if ( m_Misc.m_MotionVolumesBox == "random" ) { for ( size_t i=0; i < m_SignalGen.GetNumVolumes(); ++i ) { m_SignalGen.m_MotionVolumes.push_back( bool( rand()%2 ) ); } MITK_DEBUG << "mitkFiberfoxParameters.cpp: Case m_Misc.m_MotionVolumesBox == \"random\"."; } else if ( ! m_Misc.m_MotionVolumesBox.empty() ) { std::stringstream stream( m_Misc.m_MotionVolumesBox ); std::vector numbers; int nummer = std::numeric_limits::max(); while( stream >> nummer ) { if( nummer < std::numeric_limits::max() ) { numbers.push_back( nummer ); } } // If a list of negative numbers is given: if( *(std::min_element( numbers.begin(), numbers.end() )) < 0 && *(std::max_element( numbers.begin(), numbers.end() )) <= 0 ) // cave: -0 == +0 { for ( size_t i=0; i(m_SignalGen.GetNumVolumes()) && -number >= 0 ) m_SignalGen.m_MotionVolumes.at(-number) = false; } MITK_DEBUG << "mitkFiberfoxParameters.cpp: Case list of negative numbers."; } // If a list of positive numbers is given: else if( *(std::min_element( numbers.begin(), numbers.end() )) >= 0 && *(std::max_element( numbers.begin(), numbers.end() )) >= 0 ) { for ( size_t i=0; i(m_SignalGen.GetNumVolumes()) && number >= 0) m_SignalGen.m_MotionVolumes.at(number) = true; } MITK_DEBUG << "mitkFiberfoxParameters.cpp: Case list of positive numbers."; } else { MITK_WARN << "mitkFiberfoxParameters.cpp: Inconsistent list of numbers in m_MotionVolumesBox."; break; } } else { MITK_WARN << "mitkFiberfoxParameters.cpp: Cannot make sense of string in m_MotionVolumesBox."; break; } try { if (ReadVal(v1,"artifacts.noisetype","")=="rice") { m_NoiseModel = std::make_shared< mitk::RicianNoiseModel<> >(); m_NoiseModel->SetNoiseVariance(ReadVal(v1,"artifacts.noisevariance",m_NoiseModel->GetNoiseVariance())); } } catch(...) { MITK_DEBUG << "mitkFiberfoxParameters.cpp: caught some error while trying m_NoiseModel->SetNoiseVariance()"; // throw; } try { if (ReadVal(v1,"artifacts.noisetype","")=="chisquare") { m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel<> >(); m_NoiseModel->SetNoiseVariance(ReadVal(v1,"artifacts.noisevariance",m_NoiseModel->GetNoiseVariance())); } } catch(...) { MITK_DEBUG << "mitkFiberfoxParameters.cpp: caught some error while trying m_NoiseModel->SetNoiseVariance()"; // throw; } BOOST_FOREACH( boost::property_tree::ptree::value_type const& v2, v1.second.get_child("compartments") ) { mitk::DiffusionSignalModel<>* signalModel = nullptr; std::string model = ReadVal(v2,"model","",true); if (model=="stick") { mitk::StickModel<>* model = new mitk::StickModel<>(); model->SetDiffusivity(ReadVal(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="tensor") { mitk::TensorModel<>* model = new mitk::TensorModel<>(); model->SetDiffusivity1(ReadVal(v2,"d1",model->GetDiffusivity1())); model->SetDiffusivity2(ReadVal(v2,"d2",model->GetDiffusivity2())); model->SetDiffusivity3(ReadVal(v2,"d3",model->GetDiffusivity3())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="ball") { mitk::BallModel<>* model = new mitk::BallModel<>(); model->SetDiffusivity(ReadVal(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="astrosticks") { mitk::AstroStickModel<>* model = new AstroStickModel<>(); model->SetDiffusivity(ReadVal(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->SetRandomizeSticks(ReadVal(v2,"randomize",model->GetRandomizeSticks())); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="dot") { mitk::DotModel<>* model = new mitk::DotModel<>(); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="prototype") { mitk::RawShModel<>* model = new mitk::RawShModel<>(); model->SetMaxNumKernels(ReadVal(v2,"maxNumSamples",model->GetMaxNumKernels())); model->SetFaRange(ReadVal(v2,"minFA",model->GetFaRange().first), ReadVal(v2,"maxFA",model->GetFaRange().second)); model->SetAdcRange(ReadVal(v2,"minADC",model->GetAdcRange().first), ReadVal(v2,"maxADC",model->GetAdcRange().second)); model->m_CompartmentId = ReadVal(v2,"ID",0,true); unsigned int numCoeffs = ReadVal(v2,"numCoeffs",0,true); unsigned int numSamples = ReadVal(v2,"numSamples",0,true); for (unsigned int j=0; j coeffs(numCoeffs); for (unsigned int k=0; k(v2,"kernels."+boost::lexical_cast(j)+".coeffs."+boost::lexical_cast(k),0,true); } model->SetShCoefficients( coeffs, ReadVal(v2,"kernels."+boost::lexical_cast(j)+".B0",0,true) ); } if (ReadVal(v2,"type","",true)=="fiber") { m_FiberModelList.push_back(model); } else if (ReadVal(v2,"type","",true)=="non-fiber") { m_NonFiberModelList.push_back(model); } // else ? signalModel = model; } if (signalModel!=nullptr) { signalModel->SetGradientList(gradients); try { itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); reader->SetFileName(filename+"_VOLUME"+ReadVal(v2,"ID","")+".nrrd"); reader->Update(); signalModel->SetVolumeFractionImage(reader->GetOutput()); MITK_INFO << "Volume fraction image loaded for compartment " << signalModel->m_CompartmentId; } catch(...) { MITK_INFO << "No volume fraction image found for compartment " << signalModel->m_CompartmentId; } } } } else { } } try { itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); 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."; } try { itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); reader->SetFileName(filename+"_MASK.nrrd"); reader->Update(); m_SignalGen.m_MaskImage = reader->GetOutput(); m_SignalGen.m_ImageRegion = m_SignalGen.m_MaskImage->GetLargestPossibleRegion(); m_SignalGen.m_ImageSpacing = m_SignalGen.m_MaskImage->GetSpacing(); m_SignalGen.m_ImageOrigin = m_SignalGen.m_MaskImage->GetOrigin(); m_SignalGen.m_ImageDirection = m_SignalGen.m_MaskImage->GetDirection(); MITK_INFO << "Mask image loaded."; } catch(...) { MITK_INFO << "No mask image found."; } setlocale(LC_ALL, currLocale.c_str()); } void mitk::FiberfoxParameters::PrintSelf() { MITK_INFO << "Not implemented :("; } diff --git a/Modules/DiffusionImaging/FiberTracking/Testing/mitkFiberProcessingTest.cpp b/Modules/DiffusionImaging/FiberTracking/Testing/mitkFiberProcessingTest.cpp index 0b245716c1..7f3d01eba0 100644 --- a/Modules/DiffusionImaging/FiberTracking/Testing/mitkFiberProcessingTest.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Testing/mitkFiberProcessingTest.cpp @@ -1,304 +1,304 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkTestingMacros.h" #include #include #include #include #include #include #include #include "mitkTestFixture.h" class mitkFiberProcessingTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkFiberProcessingTestSuite); MITK_TEST(Test1); MITK_TEST(Test2); MITK_TEST(Test3); MITK_TEST(Test4); MITK_TEST(Test5); MITK_TEST(Test6); MITK_TEST(Test7); MITK_TEST(Test8); MITK_TEST(Test9); MITK_TEST(Test10); MITK_TEST(Test11); MITK_TEST(Test12); MITK_TEST(Test13); MITK_TEST(Test14); MITK_TEST(Test15); MITK_TEST(Test16); MITK_TEST(Test17); MITK_TEST(Test18); CPPUNIT_TEST_SUITE_END(); typedef itk::Image ItkUcharImgType; private: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ mitk::FiberBundle::Pointer original; ItkUcharImgType::Pointer mask; public: void setUp() override { omp_set_num_threads(1); original = nullptr; original = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/original.fib")).front().GetPointer()); mitk::Image::Pointer img = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/MASK.nrrd"))[0].GetPointer()); mask = ItkUcharImgType::New(); mitk::CastToItkImage(img, mask); } void tearDown() override { original = nullptr; } void Test1() { MITK_INFO << "TEST 1: Remove by direction"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); vnl_vector_fixed dir; dir[0] = 0; dir[1] = 1; dir[2] = 0; - fib->RemoveDir(dir,cos(5.0*M_PI/180)); + fib->RemoveDir(dir,cos(5.0*itk::Math::pi/180)); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_direction.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test2() { MITK_INFO << "TEST 2: Remove by length"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->RemoveShortFibers(30); fib->RemoveLongFibers(40); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_length.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test3() { MITK_INFO << "TEST 3: Remove by curvature 1"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); auto filter = itk::FiberCurvatureFilter::New(); filter->SetInputFiberBundle(fib); filter->SetAngularDeviation(30); filter->SetDistance(5); filter->SetRemoveFibers(false); filter->SetUseMedian(true); filter->Update(); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_curvature1.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(filter->GetOutputFiberBundle())); } void Test4() { MITK_INFO << "TEST 4: Remove by curvature 2"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); auto filter = itk::FiberCurvatureFilter::New(); filter->SetInputFiberBundle(fib); filter->SetAngularDeviation(30); filter->SetDistance(5); filter->SetRemoveFibers(true); filter->SetUseMedian(true); filter->Update(); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_curvature2.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(filter->GetOutputFiberBundle())); } void Test5() { MITK_INFO << "TEST 5: Remove outside mask"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib = fib->RemoveFibersOutside(mask); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_outside.fib")).front().GetPointer()); mitk::IOUtil::Save(fib, mitk::IOUtil::GetTempPath()+"remove_outside.fib"); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test6() { MITK_INFO << "TEST 6: Remove inside mask"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib = fib->RemoveFibersOutside(mask, true); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_inside.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test7() { MITK_INFO << "TEST 7: Modify resample spline"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->ResampleSpline(5); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_resample.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test8() { MITK_INFO << "TEST 8: Modify compress"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->Compress(0.1); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_compress.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test9() { MITK_INFO << "TEST 9: Modify sagittal mirror"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->MirrorFibers(0); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_sagittal_mirror.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test10() { MITK_INFO << "TEST 10: Modify coronal mirror"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->MirrorFibers(1); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_coronal_mirror.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test11() { MITK_INFO << "TEST 11: Modify axial mirror"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->MirrorFibers(2); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_axial_mirror.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test12() { MITK_INFO << "TEST 12: Weight and join"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->SetFiberWeights(0.1); mitk::FiberBundle::Pointer fib2 = original->GetDeepCopy(); fib2->SetFiberWeight(3, 0.5); fib = fib->AddBundle(fib2); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_weighted_joined.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Number of fibers", ref->GetNumFibers() == fib->GetNumFibers()); for (int i=0; iGetNumFibers(); i++) CPPUNIT_ASSERT_MESSAGE("Fiber weights", ref->GetFiberWeight(i) == fib->GetFiberWeight(i)); } void Test13() { MITK_INFO << "TEST 13: Subtract"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); mitk::FiberBundle::Pointer fib2 = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/remove_length.fib")).front().GetPointer()); fib = fib->SubtractBundle(fib2); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/subtracted.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test14() { MITK_INFO << "TEST 14: rotate"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->TransformFibers(1,2,3,0,0,0); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/transform_rotate.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test15() { MITK_INFO << "TEST 15: translate"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->TransformFibers(0,0,0,1,2,3); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/transform_translate.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test16() { MITK_INFO << "TEST 16: scale 1"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->ScaleFibers(0.1, 0.2, 0.3); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/transform_scale1.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test17() { MITK_INFO << "TEST 17: scale 2"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->ScaleFibers(0.1, 0.2, 0.3, false); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/transform_scale2.fib")).front().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } void Test18() { MITK_INFO << "TEST 18: Modify resample linear"; mitk::FiberBundle::Pointer fib = original->GetDeepCopy(); fib->ResampleLinear(); mitk::FiberBundle::Pointer ref = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/FiberProcessing/modify_resample_linear.fib")).front().GetPointer()); mitk::IOUtil::Save(fib, mitk::IOUtil::GetTempPath()+"modify_resample_linear.fib"); CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(fib)); } }; MITK_TEST_SUITE_REGISTRATION(mitkFiberProcessing) diff --git a/Modules/DiffusionImaging/FiberTracking/Testing/mitkLocalFiberPlausibilityTest.cpp b/Modules/DiffusionImaging/FiberTracking/Testing/mitkLocalFiberPlausibilityTest.cpp index dfc7e9b9d5..bef2fe1e79 100755 --- a/Modules/DiffusionImaging/FiberTracking/Testing/mitkLocalFiberPlausibilityTest.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Testing/mitkLocalFiberPlausibilityTest.cpp @@ -1,159 +1,156 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - int mitkLocalFiberPlausibilityTest(int argc, char* argv[]) { omp_set_num_threads(1); MITK_TEST_BEGIN("mitkLocalFiberPlausibilityTest"); MITK_TEST_CONDITION_REQUIRED(argc==8,"check for input data") std::string fibFile = argv[1]; std::vector< std::string > referenceImages; referenceImages.push_back(argv[2]); referenceImages.push_back(argv[3]); std::string LDFP_ERROR_IMAGE = argv[4]; std::string LDFP_NUM_DIRECTIONS = argv[5]; std::string LDFP_VECTOR_FIELD = argv[6]; std::string LDFP_ERROR_IMAGE_IGNORE = argv[7]; float angularThreshold = 30; try { typedef itk::Image ItkUcharImgType; typedef itk::Image< itk::Vector< float, 3>, 3 > ItkDirectionImage3DType; typedef itk::VectorContainer< unsigned int, ItkDirectionImage3DType::Pointer > ItkDirectionImageContainerType; typedef itk::EvaluateDirectionImagesFilter< float > EvaluationFilterType; // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(mitk::IOUtil::Load(fibFile)[0].GetPointer()); // load reference directions ItkDirectionImageContainerType::Pointer referenceImageContainer = ItkDirectionImageContainerType::New(); for (unsigned int i=0; i(mitk::IOUtil::Load(referenceImages.at(i))[0].GetPointer()); typedef mitk::ImageToItk< ItkDirectionImage3DType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(img); caster->Update(); ItkDirectionImage3DType::Pointer itkImg = caster->GetOutput(); referenceImageContainer->InsertElement(referenceImageContainer->Size(),itkImg); } catch(...){ MITK_INFO << "could not load: " << referenceImages.at(i); } } ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); ItkDirectionImage3DType::Pointer dirImg = referenceImageContainer->GetElement(0); itkMaskImage->SetSpacing( dirImg->GetSpacing() ); itkMaskImage->SetOrigin( dirImg->GetOrigin() ); itkMaskImage->SetDirection( dirImg->GetDirection() ); itkMaskImage->SetLargestPossibleRegion( dirImg->GetLargestPossibleRegion() ); itkMaskImage->SetBufferedRegion( dirImg->GetLargestPossibleRegion() ); itkMaskImage->SetRequestedRegion( dirImg->GetLargestPossibleRegion() ); itkMaskImage->Allocate(); itkMaskImage->FillBuffer(1); // extract directions from fiber bundle itk::TractsToVectorImageFilter::Pointer fOdfFilter = itk::TractsToVectorImageFilter::New(); fOdfFilter->SetFiberBundle(inputTractogram); fOdfFilter->SetMaskImage(itkMaskImage); - fOdfFilter->SetAngularThreshold(cos(angularThreshold*M_PI/180)); + fOdfFilter->SetAngularThreshold(cos(angularThreshold*itk::Math::pi/180)); fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::SINGLE_VEC_NORM); fOdfFilter->SetMaxNumDirections(3); fOdfFilter->SetSizeThreshold(0.3); fOdfFilter->SetUseWorkingCopy(false); fOdfFilter->SetNumberOfThreads(1); fOdfFilter->Update(); itk::TractsToVectorImageFilter::ItkDirectionImageType::Pointer direction_image = fOdfFilter->GetDirectionImage(); // Get directions and num directions image ItkUcharImgType::Pointer numDirImage = fOdfFilter->GetNumDirectionsImage(); mitk::Image::Pointer mitkNumDirImage = mitk::Image::New(); mitkNumDirImage->InitializeByItk( numDirImage.GetPointer() ); mitkNumDirImage->SetVolume( numDirImage->GetBufferPointer() ); // mitk::FiberBundle::Pointer testDirections = fOdfFilter->GetOutputFiberBundle(); // evaluate directions with missing directions EvaluationFilterType::Pointer evaluationFilter = EvaluationFilterType::New(); // evaluationFilter->SetImageSet(directionImageContainer); evaluationFilter->SetReferenceImageSet(referenceImageContainer); evaluationFilter->SetMaskImage(itkMaskImage); evaluationFilter->SetIgnoreMissingDirections(false); evaluationFilter->Update(); EvaluationFilterType::OutputImageType::Pointer angularErrorImage = evaluationFilter->GetOutput(0); mitk::Image::Pointer mitkAngularErrorImage = mitk::Image::New(); mitkAngularErrorImage->InitializeByItk( angularErrorImage.GetPointer() ); mitkAngularErrorImage->SetVolume( angularErrorImage->GetBufferPointer() ); // evaluate directions without missing directions evaluationFilter->SetIgnoreMissingDirections(true); evaluationFilter->Update(); EvaluationFilterType::OutputImageType::Pointer angularErrorImageIgnore = evaluationFilter->GetOutput(0); mitk::Image::Pointer mitkAngularErrorImageIgnore = mitk::Image::New(); mitkAngularErrorImageIgnore->InitializeByItk( angularErrorImageIgnore.GetPointer() ); mitkAngularErrorImageIgnore->SetVolume( angularErrorImageIgnore->GetBufferPointer() ); mitk::Image::Pointer gtAngularErrorImageIgnore = dynamic_cast(mitk::IOUtil::Load(LDFP_ERROR_IMAGE_IGNORE)[0].GetPointer()); mitk::Image::Pointer gtAngularErrorImage = dynamic_cast(mitk::IOUtil::Load(LDFP_ERROR_IMAGE)[0].GetPointer()); mitk::Image::Pointer gtNumTestDirImage = dynamic_cast(mitk::IOUtil::Load(LDFP_NUM_DIRECTIONS)[0].GetPointer()); MITK_ASSERT_EQUAL(gtAngularErrorImageIgnore, mitkAngularErrorImageIgnore, "Check if error images are equal (ignored missing directions)."); MITK_ASSERT_EQUAL(gtAngularErrorImage, mitkAngularErrorImage, "Check if error images are equal."); MITK_ASSERT_EQUAL(gtNumTestDirImage, mitkNumDirImage, "Check if num direction images are equal."); } catch (itk::ExceptionObject e) { MITK_INFO << e; return EXIT_FAILURE; } catch (std::exception e) { MITK_INFO << e.what(); return EXIT_FAILURE; } catch (...) { MITK_INFO << "ERROR!?!"; return EXIT_FAILURE; } MITK_TEST_END(); } diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberDirectionExtraction.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberDirectionExtraction.cpp index c1a013d1cc..4a7c34740b 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberDirectionExtraction.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberDirectionExtraction.cpp @@ -1,178 +1,175 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include #include #include #include "mitkCommandLineParser.h" #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - /*! \brief Extract principal fiber directions from a tractogram */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Fiber Direction Extraction"); parser.setCategory("Fiber Tracking and Processing Methods"); parser.setDescription("Extract principal fiber directions from a tractogram"); parser.setContributor("MIC"); parser.setArgumentPrefix("--", "-"); parser.addArgument("input", "i", mitkCommandLineParser::InputFile, "Input:", "input tractogram (.fib/.trk)", us::Any(), false); parser.addArgument("out", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output root", us::Any(), false); parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask:", "mask image"); parser.addArgument("athresh", "a", mitkCommandLineParser::Float, "Angular threshold:", "angular threshold in degrees. closer fiber directions are regarded as one direction and clustered together.", 25, true); parser.addArgument("peakthresh", "t", mitkCommandLineParser::Float, "Peak size threshold:", "peak size threshold relative to largest peak in voxel", 0.2, true); parser.addArgument("verbose", "v", mitkCommandLineParser::Bool, "Verbose:", "output optional and intermediate calculation results"); parser.addArgument("numdirs", "d", mitkCommandLineParser::Int, "Max. num. directions:", "maximum number of fibers per voxel", 3, true); parser.addArgument("normalization", "n", mitkCommandLineParser::Int, "Normalization method:", "1=global maximum, 2=single vector, 3=voxel-wise maximum", 1); parser.addArgument("file_ending", "f", mitkCommandLineParser::String, "Image type:", ".nrrd, .nii, .nii.gz"); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; std::string fibFile = us::any_cast(parsedArgs["input"]); std::string maskImage(""); if (parsedArgs.count("mask")) maskImage = us::any_cast(parsedArgs["mask"]); float peakThreshold = 0.2; if (parsedArgs.count("peakthresh")) peakThreshold = us::any_cast(parsedArgs["peakthresh"]); float angularThreshold = 25; if (parsedArgs.count("athresh")) angularThreshold = us::any_cast(parsedArgs["athresh"]); std::string outRoot = us::any_cast(parsedArgs["out"]); bool verbose = false; if (parsedArgs.count("verbose")) verbose = us::any_cast(parsedArgs["verbose"]); int maxNumDirs = 3; if (parsedArgs.count("numdirs")) maxNumDirs = us::any_cast(parsedArgs["numdirs"]); int normalization = 1; if (parsedArgs.count("normalization")) normalization = us::any_cast(parsedArgs["normalization"]); std::string file_ending = ".nrrd"; if (parsedArgs.count("file_ending")) file_ending = us::any_cast(parsedArgs["file_ending"]); try { typedef itk::Image ItkUcharImgType; // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(mitk::IOUtil::Load(fibFile)[0].GetPointer()); // load/create mask image ItkUcharImgType::Pointer itkMaskImage = nullptr; if (maskImage.compare("")!=0) { std::cout << "Using mask image"; itkMaskImage = ItkUcharImgType::New(); mitk::Image::Pointer mitkMaskImage = dynamic_cast(mitk::IOUtil::Load(maskImage)[0].GetPointer()); mitk::CastToItkImage(mitkMaskImage, itkMaskImage); } // extract directions from fiber bundle itk::TractsToVectorImageFilter::Pointer fOdfFilter = itk::TractsToVectorImageFilter::New(); fOdfFilter->SetFiberBundle(inputTractogram); fOdfFilter->SetMaskImage(itkMaskImage); - fOdfFilter->SetAngularThreshold(cos(angularThreshold*M_PI/180)); + fOdfFilter->SetAngularThreshold(cos(angularThreshold*itk::Math::pi/180)); switch (normalization) { case 1: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::GLOBAL_MAX); break; case 2: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::SINGLE_VEC_NORM); break; case 3: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::MAX_VEC_NORM); break; } fOdfFilter->SetUseWorkingCopy(false); fOdfFilter->SetSizeThreshold(peakThreshold); fOdfFilter->SetMaxNumDirections(maxNumDirs); fOdfFilter->Update(); { itk::TractsToVectorImageFilter::ItkDirectionImageType::Pointer itkImg = fOdfFilter->GetDirectionImage(); typedef itk::ImageFileWriter< itk::TractsToVectorImageFilter::ItkDirectionImageType > WriterType; WriterType::Pointer writer = WriterType::New(); std::string outfilename = outRoot; outfilename.append("_DIRECTIONS"); outfilename.append(file_ending); writer->SetFileName(outfilename.c_str()); writer->SetInput(itkImg); writer->Update(); } if (verbose) { // write num direction image ItkUcharImgType::Pointer numDirImage = fOdfFilter->GetNumDirectionsImage(); typedef itk::ImageFileWriter< ItkUcharImgType > WriterType; WriterType::Pointer writer = WriterType::New(); std::string outfilename = outRoot; outfilename.append("_NUM_DIRECTIONS"); outfilename.append(file_ending); writer->SetFileName(outfilename.c_str()); writer->SetInput(numDirImage); writer->Update(); } } catch (itk::ExceptionObject e) { std::cout << e; 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/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/LocalDirectionalFiberPlausibility.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/LocalDirectionalFiberPlausibility.cpp index cc76a4e9f2..e9643af29b 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/LocalDirectionalFiberPlausibility.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/LocalDirectionalFiberPlausibility.cpp @@ -1,318 +1,315 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include #include #include #include #include "mitkCommandLineParser.h" #include #include #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - /*! \brief Calculate angular error of a tractogram with respect to the input reference directions. */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Local Directional Fiber Plausibility"); parser.setCategory("Fiber Tracking Evaluation"); parser.setDescription("Calculate angular error of a tractogram with respect to the input reference directions."); parser.setContributor("MIC"); parser.setArgumentPrefix("--", "-"); parser.addArgument("input", "i", mitkCommandLineParser::InputFile, "Input:", "input tractogram (.fib, vtk ascii file format)", us::Any(), false); parser.addArgument("reference", "r", mitkCommandLineParser::StringList, "Reference images:", "reference direction images", us::Any(), false); parser.addArgument("out", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output root", us::Any(), false); parser.addArgument("mask", "m", mitkCommandLineParser::StringList, "Masks:", "mask images"); parser.addArgument("athresh", "a", mitkCommandLineParser::Float, "Angular threshold:", "angular threshold in degrees. closer fiber directions are regarded as one direction and clustered together.", 25, true); parser.addArgument("sthresh", "s", mitkCommandLineParser::Float, "Size threshold:", "Relative peak size threshold per voxel.", 0.0, true); parser.addArgument("maxdirs", "md", mitkCommandLineParser::Int, "Max. Clusters:", "Maximum number of fiber clusters.", 0, true); parser.addArgument("verbose", "v", mitkCommandLineParser::Bool, "Verbose:", "output optional and intermediate calculation results"); parser.addArgument("ignore", "n", mitkCommandLineParser::Bool, "Ignore:", "don't increase error for missing or too many directions"); parser.addArgument("empty", "e", mitkCommandLineParser::Bool, "Empty Voxels:", "don't increase error for empty voxels"); parser.addArgument("fileID", "id", mitkCommandLineParser::String, "ID:", "optional ID field"); map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; mitkCommandLineParser::StringContainerType referenceImages = us::any_cast(parsedArgs["reference"]); mitkCommandLineParser::StringContainerType maskImages; if (parsedArgs.count("mask")) maskImages = us::any_cast(parsedArgs["mask"]); string fibFile = us::any_cast(parsedArgs["input"]); float angularThreshold = 25; if (parsedArgs.count("athresh")) angularThreshold = us::any_cast(parsedArgs["athresh"]); float sizeThreshold = 0; if (parsedArgs.count("sthresh")) sizeThreshold = us::any_cast(parsedArgs["sthresh"]); int maxDirs = 0; if (parsedArgs.count("maxdirs")) maxDirs = us::any_cast(parsedArgs["maxdirs"]); string outRoot = us::any_cast(parsedArgs["out"]); bool verbose = false; if (parsedArgs.count("verbose")) verbose = us::any_cast(parsedArgs["verbose"]); bool ignoreMissing = false; if (parsedArgs.count("ignore")) ignoreMissing = us::any_cast(parsedArgs["ignore"]); bool ignoreEmpty = false; if (parsedArgs.count("empty")) ignoreEmpty = us::any_cast(parsedArgs["empty"]); string fileID = ""; if (parsedArgs.count("fileID")) fileID = us::any_cast(parsedArgs["fileID"]); try { typedef itk::Image ItkUcharImgType; typedef itk::Image< itk::Vector< float, 3>, 3 > ItkDirectionImage3DType; typedef itk::VectorContainer< unsigned int, ItkDirectionImage3DType::Pointer > ItkDirectionImageContainerType; typedef itk::EvaluateDirectionImagesFilter< float > EvaluationFilterType; // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(mitk::IOUtil::LoadData(fibFile)[0].GetPointer()); // load reference directions ItkDirectionImageContainerType::Pointer referenceImageContainer = ItkDirectionImageContainerType::New(); for (unsigned int i=0; i(mitk::IOUtil::Load(referenceImages.at(i))[0].GetPointer()); typedef mitk::ImageToItk< ItkDirectionImage3DType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(img); caster->Update(); ItkDirectionImage3DType::Pointer itkImg = caster->GetOutput(); referenceImageContainer->InsertElement(referenceImageContainer->Size(),itkImg); } catch(...){ std::cout << "could not load: " << referenceImages.at(i); } } ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); ItkDirectionImage3DType::Pointer dirImg = referenceImageContainer->GetElement(0); itkMaskImage->SetSpacing( dirImg->GetSpacing() ); itkMaskImage->SetOrigin( dirImg->GetOrigin() ); itkMaskImage->SetDirection( dirImg->GetDirection() ); itkMaskImage->SetLargestPossibleRegion( dirImg->GetLargestPossibleRegion() ); itkMaskImage->SetBufferedRegion( dirImg->GetLargestPossibleRegion() ); itkMaskImage->SetRequestedRegion( dirImg->GetLargestPossibleRegion() ); itkMaskImage->Allocate(); itkMaskImage->FillBuffer(1); // extract directions from fiber bundle itk::TractsToVectorImageFilter::Pointer fOdfFilter = itk::TractsToVectorImageFilter::New(); fOdfFilter->SetFiberBundle(inputTractogram); fOdfFilter->SetMaskImage(itkMaskImage); - fOdfFilter->SetAngularThreshold(cos(angularThreshold*M_PI/180)); + fOdfFilter->SetAngularThreshold(cos(angularThreshold*itk::Math::pi/180)); fOdfFilter->SetNormalizeVectors(true); fOdfFilter->SetUseWorkingCopy(false); fOdfFilter->SetSizeThreshold(sizeThreshold); fOdfFilter->SetMaxNumDirections(maxDirs); fOdfFilter->Update(); if (verbose) { // write vector field mitk::FiberBundle::Pointer directions = fOdfFilter->GetOutputFiberBundle(); string outfilename = outRoot; outfilename.append("_VECTOR_FIELD.fib"); mitk::IOUtil::Save(directions.GetPointer(), outfilename ); // write direction images { itk::TractsToVectorImageFilter::ItkDirectionImageType::Pointer itkImg = fOdfFilter->GetDirectionImage(); typedef itk::ImageFileWriter< itk::TractsToVectorImageFilter::ItkDirectionImageType > WriterType; WriterType::Pointer writer = WriterType::New(); string outfilename = outRoot; outfilename.append("_DIRECTIONS.nrrd"); writer->SetFileName(outfilename.c_str()); writer->SetInput(itkImg); writer->Update(); } // write num direction image { ItkUcharImgType::Pointer numDirImage = fOdfFilter->GetNumDirectionsImage(); typedef itk::ImageFileWriter< ItkUcharImgType > WriterType; WriterType::Pointer writer = WriterType::New(); string outfilename = outRoot; outfilename.append("_NUM_DIRECTIONS.nrrd"); writer->SetFileName(outfilename.c_str()); writer->SetInput(numDirImage); writer->Update(); } } string logFile = outRoot; logFile.append("_ANGULAR_ERROR.csv"); ofstream file; file.open (logFile.c_str()); if (maskImages.size()>0) { for (unsigned int i=0; i(mitk::IOUtil::Load(maskImages.at(i))[0].GetPointer()); mitk::CastToItkImage(mitkMaskImage, itkMaskImage); // evaluate directions EvaluationFilterType::Pointer evaluationFilter = EvaluationFilterType::New(); //evaluationFilter->SetImageSet(directionImageContainer); evaluationFilter->SetReferenceImageSet(referenceImageContainer); evaluationFilter->SetMaskImage(itkMaskImage); evaluationFilter->SetIgnoreMissingDirections(ignoreMissing); evaluationFilter->SetIgnoreEmptyVoxels(ignoreEmpty); evaluationFilter->Update(); if (verbose) { EvaluationFilterType::OutputImageType::Pointer angularErrorImage = evaluationFilter->GetOutput(0); typedef itk::ImageFileWriter< EvaluationFilterType::OutputImageType > WriterType; WriterType::Pointer writer = WriterType::New(); string outfilename = outRoot; outfilename.append("_ERROR_IMAGE.nrrd"); writer->SetFileName(outfilename.c_str()); writer->SetInput(angularErrorImage); writer->Update(); } string maskFileName = itksys::SystemTools::GetFilenameWithoutExtension(maskImages.at(i)); unsigned found = maskFileName.find_last_of("_"); string sens = itksys::SystemTools::GetFilenameWithoutLastExtension(fibFile); if (!fileID.empty()) sens = fileID; sens.append(","); sens.append(maskFileName.substr(found+1)); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMeanAngularError())); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMedianAngularError())); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMaxAngularError())); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMinAngularError())); sens.append(","); sens.append(boost::lexical_cast(std::sqrt(evaluationFilter->GetVarAngularError()))); sens.append(";\n"); file << sens; } } else { // evaluate directions EvaluationFilterType::Pointer evaluationFilter = EvaluationFilterType::New(); //evaluationFilter->SetImageSet(directionImageContainer); evaluationFilter->SetReferenceImageSet(referenceImageContainer); evaluationFilter->SetMaskImage(itkMaskImage); evaluationFilter->SetIgnoreMissingDirections(ignoreMissing); evaluationFilter->SetIgnoreEmptyVoxels(ignoreEmpty); evaluationFilter->Update(); if (verbose) { EvaluationFilterType::OutputImageType::Pointer angularErrorImage = evaluationFilter->GetOutput(0); typedef itk::ImageFileWriter< EvaluationFilterType::OutputImageType > WriterType; WriterType::Pointer writer = WriterType::New(); string outfilename = outRoot; outfilename.append("_ERROR_IMAGE.nrrd"); writer->SetFileName(outfilename.c_str()); writer->SetInput(angularErrorImage); writer->Update(); } string sens = itksys::SystemTools::GetFilenameWithoutLastExtension(fibFile); if (!fileID.empty()) sens = fileID; sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMeanAngularError())); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMedianAngularError())); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMaxAngularError())); sens.append(","); sens.append(boost::lexical_cast(evaluationFilter->GetMinAngularError())); sens.append(","); sens.append(boost::lexical_cast(std::sqrt(evaluationFilter->GetVarAngularError()))); sens.append(";\n"); file << sens; } file.close(); } catch (itk::ExceptionObject e) { std::cout << e; 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/IGT/Algorithms/mitkNavigationDataEvaluationFilter.cpp b/Modules/IGT/Algorithms/mitkNavigationDataEvaluationFilter.cpp index d5c3e0ce31..d80a29b859 100644 --- a/Modules/IGT/Algorithms/mitkNavigationDataEvaluationFilter.cpp +++ b/Modules/IGT/Algorithms/mitkNavigationDataEvaluationFilter.cpp @@ -1,302 +1,298 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkNavigationDataEvaluationFilter.h" #include -#define _USE_MATH_DEFINES -#include - mitk::NavigationDataEvaluationFilter::NavigationDataEvaluationFilter() : mitk::NavigationDataToNavigationDataFilter() { } mitk::NavigationDataEvaluationFilter::~NavigationDataEvaluationFilter() { } void mitk::NavigationDataEvaluationFilter::GenerateData() { this->CreateOutputsForAllInputs(); // make sure that we have the same number of outputs as inputs this->CreateMembersForAllInputs(); /* update outputs with tracking data from tools */ for (unsigned int i = 0; i < this->GetNumberOfOutputs(); ++i) { //first copy outputs to inputs mitk::NavigationData* output = this->GetOutput(i); assert(output); const mitk::NavigationData* input = this->GetInput(i); assert(input); if (input->IsDataValid() == false) { output->SetDataValid(false); } else { output->Graft(input); } //then save statistics if (input->IsDataValid()) { m_LoggedPositions[i].push_back(input->GetPosition()); m_LoggedQuaternions[i].push_back(input->GetOrientation()); } else { m_InvalidSamples[i]++; } } } void mitk::NavigationDataEvaluationFilter::CreateMembersForAllInputs() { while (this->m_LoggedPositions.size() < this->GetNumberOfInputs()) { std::pair > newElement(m_LoggedPositions.size(), std::vector()); m_LoggedPositions.insert(newElement); } while (this->m_LoggedQuaternions.size() < this->GetNumberOfInputs()) { std::pair > newElement(m_LoggedQuaternions.size(), std::vector()); m_LoggedQuaternions.insert(newElement); } while (this->m_InvalidSamples.size() < this->GetNumberOfInputs()) { std::pair newElement(m_InvalidSamples.size(), 0); m_InvalidSamples.insert(newElement); } } void mitk::NavigationDataEvaluationFilter::ResetStatistic() { for (unsigned int i = 0; i < m_LoggedPositions.size(); i++) m_LoggedPositions[i] = std::vector(); for (unsigned int i = 0; i < m_LoggedQuaternions.size(); i++) m_LoggedQuaternions[i] = std::vector(); for (unsigned int i = 0; i < m_InvalidSamples.size(); i++) m_InvalidSamples[i] = 0; } int mitk::NavigationDataEvaluationFilter::GetNumberOfAnalysedNavigationData(int input) { return this->m_LoggedPositions[input].size(); } mitk::Point3D mitk::NavigationDataEvaluationFilter::GetPositionMean(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionMean(); } mitk::Vector3D mitk::NavigationDataEvaluationFilter::GetPositionStandardDeviation(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionStandardDeviation(); } mitk::Vector3D mitk::NavigationDataEvaluationFilter::GetPositionSampleStandardDeviation(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionSampleStandardDeviation(); } mitk::Quaternion mitk::NavigationDataEvaluationFilter::GetQuaternionMean(int input) { return GetMean(m_LoggedQuaternions[input]); } mitk::Quaternion mitk::NavigationDataEvaluationFilter::GetQuaternionStandardDeviation(int input) { mitk::Quaternion returnValue; std::vector list1 = std::vector(); std::vector list2 = std::vector(); std::vector list3 = std::vector(); std::vector list4 = std::vector(); for (unsigned int i = 0; i < m_LoggedQuaternions[input].size(); i++) { list1.push_back(m_LoggedQuaternions[input].at(i)[0]); list2.push_back(m_LoggedQuaternions[input].at(i)[1]); list3.push_back(m_LoggedQuaternions[input].at(i)[2]); list4.push_back(m_LoggedQuaternions[input].at(i)[3]); } mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(); returnValue[0] = myCalculator->GetStabw(list1); returnValue[1] = myCalculator->GetStabw(list2); returnValue[2] = myCalculator->GetStabw(list3); returnValue[3] = myCalculator->GetStabw(list4); return returnValue; } mitk::Vector3D mitk::NavigationDataEvaluationFilter::GetEulerAnglesMean(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(QuaternionsToEulerAngles(m_LoggedQuaternions[input]))); mitk::Vector3D returnValue; returnValue[0] = myCalculator->GetPositionMean()[0]; returnValue[1] = myCalculator->GetPositionMean()[1]; returnValue[2] = myCalculator->GetPositionMean()[2]; return returnValue; } double mitk::NavigationDataEvaluationFilter::GetEulerAnglesRMS(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(QuaternionsToEulerAngles(m_LoggedQuaternions[input]))); return myCalculator->GetPositionErrorRMS(); } double mitk::NavigationDataEvaluationFilter::GetEulerAnglesRMSDegree(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(QuaternionsToEulerAnglesGrad(m_LoggedQuaternions[input]))); return myCalculator->GetPositionErrorRMS(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorMean(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorMean(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorStandardDeviation(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorStandardDeviation(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorSampleStandardDeviation(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorSampleStandardDeviation(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorRMS(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorRMS(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorMedian(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorMedian(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorMax(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorMax(); } double mitk::NavigationDataEvaluationFilter::GetPositionErrorMin(int input) { mitk::PointSetStatisticsCalculator::Pointer myCalculator = mitk::PointSetStatisticsCalculator::New(VectorToPointSet(m_LoggedPositions[input])); return myCalculator->GetPositionErrorMin(); } int mitk::NavigationDataEvaluationFilter::GetNumberOfInvalidSamples(int input) { return m_InvalidSamples[input]; } double mitk::NavigationDataEvaluationFilter::GetPercentageOfInvalidSamples(int input) { return (m_InvalidSamples[input] / (m_InvalidSamples[input] + ((double)m_LoggedPositions[input].size())))*100.0; } mitk::Quaternion mitk::NavigationDataEvaluationFilter::GetMean(std::vector list) { //calculate mean mitk::Quaternion mean; mean[0] = 0; mean[1] = 0; mean[2] = 0; mean[3] = 0; for (unsigned int i = 0; i < list.size(); i++) { mean[0] += list.at(i)[0]; mean[1] += list.at(i)[1]; mean[2] += list.at(i)[2]; mean[3] += list.at(i)[3]; } mean[0] /= list.size(); mean[1] /= list.size(); mean[2] /= list.size(); mean[3] /= list.size(); return mean; } mitk::PointSet::Pointer mitk::NavigationDataEvaluationFilter::VectorToPointSet(std::vector pSet) { mitk::PointSet::Pointer returnValue = mitk::PointSet::New(); for (unsigned int i = 0; i < pSet.size(); i++) returnValue->InsertPoint(i, pSet.at(i)); return returnValue; } mitk::PointSet::Pointer mitk::NavigationDataEvaluationFilter::VectorToPointSet(std::vector pSet) { mitk::PointSet::Pointer returnValue = mitk::PointSet::New(); for (unsigned int i = 0; i < pSet.size(); i++) { mitk::Point3D thisPoint; thisPoint[0] = pSet.at(i)[0]; thisPoint[1] = pSet.at(i)[1]; thisPoint[2] = pSet.at(i)[2]; returnValue->InsertPoint(i, thisPoint); } return returnValue; } std::vector mitk::NavigationDataEvaluationFilter::QuaternionsToEulerAngles(std::vector quaterions) { std::vector returnValue = std::vector(); for (unsigned int i = 0; i < quaterions.size(); i++) { mitk::Vector3D eulerAngles; mitk::Quaternion currentQuaternion = quaterions.at(i); currentQuaternion.normalize(); //must be normalized due to the documentation of the vnl method rotation_euler_angles() eulerAngles[0] = currentQuaternion.rotation_euler_angles()[0]; eulerAngles[1] = currentQuaternion.rotation_euler_angles()[1]; eulerAngles[2] = currentQuaternion.rotation_euler_angles()[2]; returnValue.push_back(eulerAngles); } return returnValue; } std::vector mitk::NavigationDataEvaluationFilter::QuaternionsToEulerAnglesGrad(std::vector quaterions) { - double PI = M_PI; std::vector returnValue = std::vector(); std::vector eulerAnglesRadians = QuaternionsToEulerAngles(quaterions); for (unsigned int i = 0; i < eulerAnglesRadians.size(); i++) { mitk::Vector3D currentAngles; - currentAngles[0] = (eulerAnglesRadians.at(i)[0] / PI) * 180; - currentAngles[1] = (eulerAnglesRadians.at(i)[1] / PI) * 180; - currentAngles[2] = (eulerAnglesRadians.at(i)[2] / PI) * 180; + currentAngles[0] = (eulerAnglesRadians.at(i)[0] / itk::Math::pi) * 180; + currentAngles[1] = (eulerAnglesRadians.at(i)[1] / itk::Math::pi) * 180; + currentAngles[2] = (eulerAnglesRadians.at(i)[2] / itk::Math::pi) * 180; returnValue.push_back(currentAngles); } return returnValue; } mitk::Point3D mitk::NavigationDataEvaluationFilter::GetLoggedPosition(unsigned int i, int input) { mitk::Point3D returnValue; if (m_LoggedPositions[input].size() <= i) returnValue.Fill(0); else returnValue = m_LoggedPositions[input].at(i); return returnValue; } mitk::Quaternion mitk::NavigationDataEvaluationFilter::GetLoggedOrientation(unsigned int i, int input) { mitk::Quaternion returnValue; if (m_LoggedQuaternions[input].size() <= i) returnValue.fill(0); else returnValue = m_LoggedQuaternions[input].at(i); return returnValue; } diff --git a/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp b/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp index 3f772b99bd..31eb691c2b 100644 --- a/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp +++ b/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp @@ -1,469 +1,466 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 -#define _USE_MATH_DEFINES -#include - #include BYTE MotionBuf[0x1FA400]; mitk::PolhemusInterface::PolhemusInterface() : m_continousTracking(false) { m_pdiDev = new CPDIdev(); m_numberOfTools = 0; } mitk::PolhemusInterface::~PolhemusInterface() { delete m_pdiDev; } bool mitk::PolhemusInterface::InitializeDevice() { m_pdiDev->ResetTracker(); m_pdiDev->ResetSAlignment(-1); m_pdiDev->Trace(TRUE, 7); m_continousTracking = false; return true; } bool mitk::PolhemusInterface::SetupDevice() { m_pdiDev->SetPnoBuffer(MotionBuf, 0x1FA400); m_pdiDev->SetMetric(true); //use cm instead of inches m_pdiDev->StartPipeExport(); CPDImdat pdiMDat; pdiMDat.Empty(); pdiMDat.Append(PDI_MODATA_FRAMECOUNT); pdiMDat.Append(PDI_MODATA_POS); pdiMDat.Append(PDI_MODATA_ORI); m_pdiDev->SetSDataList(-1, pdiMDat); CPDIbiterr cBE; m_pdiDev->GetBITErrs(cBE); if (!(cBE.IsClear())) { m_pdiDev->ClearBITErrs(); } return true; } bool mitk::PolhemusInterface::StartTracking() { m_continousTracking = true; return m_pdiDev->StartContPno(0); } bool mitk::PolhemusInterface::StopTracking() { m_continousTracking = false; m_pdiDev->StopContPno(); return true; } bool mitk::PolhemusInterface::OpenConnection() { bool returnValue; //Initialize, and if it is not successful, return false. if (!InitializeDevice()) { returnValue = false; } //Connect else if (m_pdiDev->CnxReady()) { returnValue = true; } //If it is not successful, search for connections. else { CPDIser pdiSer; m_pdiDev->SetSerialIF(&pdiSer); ePiCommType eType = m_pdiDev->DiscoverCnx(); switch (eType) { case PI_CNX_USB: MITK_INFO << "USB Connection: " << m_pdiDev->GetLastResultStr(); break; case PI_CNX_SERIAL: MITK_INFO << "Serial Connection: " << m_pdiDev->GetLastResultStr(); break; default: MITK_INFO << "DiscoverCnx result: " << m_pdiDev->GetLastResultStr(); break; } //Setup device if (!SetupDevice()) { returnValue = false; } else { returnValue = m_pdiDev->CnxReady(); } } return returnValue; } bool mitk::PolhemusInterface::Connect() { bool returnValue = OpenConnection(); if (!returnValue) { return returnValue; } m_numberOfTools = this->GetNumberOfTools(); //Get the tracking data to find out which tools are available. std::vector _trackingData = GetFrame(); //if we have more/less tools than before, reset all data. //check with toolStorage changes is nto enough, 'cause a sensor could just have been unplugged. if (m_ToolPorts.size() != _trackingData.size()) { m_ToolPorts.clear(); m_Hemispheres.clear(); m_HemisphereTracking.clear(); } //if we have the same number of tools as before, check if they are still the same. if (m_ToolPorts.size() == _trackingData.size()) { for (size_t i = 0; i < _trackingData.size(); ++i) { //if they are not the same, clear hemispheres and toolNames and break. if (m_ToolPorts[i] != _trackingData.at(i).id) { m_ToolPorts.clear(); m_Hemispheres.clear(); m_HemisphereTracking.clear(); break; } } } //if we don't have old tool names or if the old ones don't match any more, assign them again. if (m_ToolPorts.size() == 0) { for (size_t i = 0; i < _trackingData.size(); ++i) { m_ToolPorts.push_back(_trackingData.at(i).id); } //and reset the hemisphere parameters m_Hemispheres.clear(); m_HemisphereTracking.clear(); mitk::Vector3D temp; mitk::FillVector3D(temp, 1, 0, 0); m_Hemispheres.assign(m_numberOfTools, temp); m_HemisphereTracking.assign(m_numberOfTools, false); } return returnValue; } bool mitk::PolhemusInterface::Disconnect() { bool returnValue = true; //If Tracking is running, stop tracking first if (m_continousTracking) { this->StopTracking(); } returnValue = m_pdiDev->Disconnect(); MITK_INFO << "Disconnect: " << m_pdiDev->GetLastResultStr(); return returnValue; } std::vector mitk::PolhemusInterface::AutoDetectTools() { OpenConnection(); std::vector frame = GetSingleFrame(); m_pdiDev->Disconnect(); return frame; } unsigned int mitk::PolhemusInterface::GetNumberOfTools() { std::vector _trackingData = GetFrame(); return _trackingData.size(); } std::vector mitk::PolhemusInterface::GetFrame() { if (m_continousTracking) return this->GetLastFrame(); else return this->GetSingleFrame(); } std::vector mitk::PolhemusInterface::GetLastFrame() { PBYTE pBuf; DWORD dwSize; //read one frame if (!m_pdiDev->LastPnoPtr(pBuf, dwSize)) { MITK_WARN << m_pdiDev->GetLastResultStr(); } std::vector returnValue = ParsePolhemusRawData(pBuf, dwSize); if (returnValue.empty()) { MITK_WARN << "Cannot parse data / no tools present"; } return returnValue; } std::vector mitk::PolhemusInterface::GetSingleFrame() { if (m_continousTracking) { MITK_WARN << "Cannot get a single frame when continuous tracking is on!"; return std::vector(); } PBYTE pBuf; DWORD dwSize; //read one frame if (!m_pdiDev->ReadSinglePnoBuf(pBuf, dwSize)) { MITK_WARN << m_pdiDev->GetLastResultStr(); return std::vector(); } return ParsePolhemusRawData(pBuf, dwSize); } std::vector mitk::PolhemusInterface::ParsePolhemusRawData(PBYTE pBuf, DWORD dwSize) { std::vector returnValue; DWORD i = 0; while (i < dwSize) { BYTE ucSensor = pBuf[i + 2]; SHORT shSize = pBuf[i + 6]; // skip rest of header i += 8; PDWORD pFC = (PDWORD)(&pBuf[i]); PFLOAT pPno = (PFLOAT)(&pBuf[i + 4]); mitk::PolhemusInterface::trackingData currentTrackingData; currentTrackingData.id = ucSensor; currentTrackingData.pos[0] = pPno[0] * 10; //from cm to mm currentTrackingData.pos[1] = pPno[1] * 10; currentTrackingData.pos[2] = pPno[2] * 10; - double azimuthAngle = pPno[3] / 180 * M_PI; //from degree to rad - double elevationAngle = pPno[4] / 180 * M_PI; - double rollAngle = pPno[5] / 180 * M_PI; + double azimuthAngle = pPno[3] / 180 * itk::Math::pi; //from degree to rad + double elevationAngle = pPno[4] / 180 * itk::Math::pi; + double rollAngle = pPno[5] / 180 * itk::Math::pi; vnl_quaternion eulerQuat(rollAngle, elevationAngle, azimuthAngle); currentTrackingData.rot = eulerQuat; returnValue.push_back(currentTrackingData); i += shSize; } return returnValue; } void mitk::PolhemusInterface::SetHemisphereTrackingEnabled(bool _HeisphereTrackingEnabeled, int _tool) { //only if connection is ready! if (!this->m_pdiDev->CnxReady()) return; if (m_Hemispheres.empty()) { MITK_ERROR << "No Hemispheres. This should never happen when connected. Check your code!"; } //HemisphereTracking is switched on by SetSHemiTrack(-1). "-1" means for all sensors. //To switch heisphere tracking of, you need to set a hemisphere vector e.g. by calling SetSHemisphere(-1, { (float)1,0,0 }) if (_HeisphereTrackingEnabeled) { m_pdiDev->SetSHemiTrack(_tool); if (_tool != -1) { m_HemisphereTracking.at(GetToolIndex(_tool)) = true; } else { m_HemisphereTracking.assign(m_numberOfTools, true); } } //switch HemiTracking OFF else { //Get Tool Position. ToDo, this should not be the tool tip but the sensor position. Any chance, to get that from Polhemus interface?! std::vector _position = GetFrame(); for (int index : GetToolIterator(_tool)) { //Scalar product between mitk::point and mitk::vector double _scalarProduct = _position.at(index).pos.GetVectorFromOrigin() * m_Hemispheres.at(index); //if scalar product is negative, then the tool is in the opposite sphere then when we started to track. //Hence, we have to set the inverted hemisphere. //For default (1|0|0) this means, if x is negative, we have to set (-1|0|0). But we want to keep it generic if user sets different hemisphere... if (_scalarProduct < 0) { m_Hemispheres.at(index) = -1. * m_Hemispheres.at(index); } else if (_scalarProduct == 0) MITK_ERROR << "Something went wrong. Hemisphere or Position should not be zero."; SetHemisphere(m_ToolPorts[index], m_Hemispheres.at(index)); } } } void mitk::PolhemusInterface::ToggleHemisphere(int _tool) { //only if connection is ready! if (!this->m_pdiDev->CnxReady()) return; //toggle. for (int index : GetToolIterator(_tool)) { if (m_HemisphereTracking.at(index)) { SetHemisphereTrackingEnabled(false, m_ToolPorts[index]); this->SetHemisphere(m_ToolPorts[index], -1.*m_Hemispheres.at(index)); SetHemisphereTrackingEnabled(true, m_ToolPorts[index]); } else { this->SetHemisphere(m_ToolPorts[index], -1.*m_Hemispheres.at(index)); } } } void mitk::PolhemusInterface::AdjustHemisphere(int _tool) { //only if connection is ready! if (!this->m_pdiDev->CnxReady()) return; mitk::Vector3D _hemisphere; mitk::FillVector3D(_hemisphere, 1, 0, 0); for (int index : GetToolIterator(_tool)) { if (m_HemisphereTracking.at(index)) { SetHemisphereTrackingEnabled(false, m_ToolPorts[index]); this->SetHemisphere(m_ToolPorts[index], _hemisphere); SetHemisphereTrackingEnabled(true, m_ToolPorts[index]); } else { this->SetHemisphere(m_ToolPorts[index], _hemisphere); } } } void mitk::PolhemusInterface::SetHemisphere(int _tool, mitk::Vector3D _hemisphere) { //only if connection is ready! if (!this->m_pdiDev->CnxReady()) return; m_pdiDev->SetSHemisphere(_tool, { (float)_hemisphere[0], (float)_hemisphere[1], (float)_hemisphere[2] }); for (int index : GetToolIterator(_tool)) { if (_hemisphere.GetNorm() != 0) { m_HemisphereTracking.at(index) = false; m_Hemispheres.at(index) = _hemisphere; } else { m_HemisphereTracking.at(index) = true; //don't set the Hemisphere to (0|0|0), as we want to remember the old one. } } } mitk::Vector3D mitk::PolhemusInterface::GetHemisphere(int _tool) { if (_tool == -1) { MITK_WARN << "Can't return hemisphere for all tools. Returning Hemisphere of first tool " << m_ToolPorts[0]; return m_Hemispheres.at(0); } return m_Hemispheres.at(GetToolIndex(_tool)); } bool mitk::PolhemusInterface::GetHemisphereTrackingEnabled(int _tool) { //if tool is -1, this means "All Tools". We return true if HemiTracking is enabled for all tools, and false if it is off for at least one tool. if (_tool == -1) { bool _returnValue = true; for (bool currentValue : m_HemisphereTracking) _returnValue = _returnValue && currentValue; return _returnValue; } else return m_HemisphereTracking.at(GetToolIndex(_tool)); } std::vector mitk::PolhemusInterface::GetToolPorts() { return m_ToolPorts; } int mitk::PolhemusInterface::GetToolIndex(int _tool) { if (_tool == -1) return -1; else return std::find(m_ToolPorts.begin(), m_ToolPorts.end(), _tool) - m_ToolPorts.begin(); } std::vector mitk::PolhemusInterface::GetToolIterator(int _tool) { std::vector _iterator; if (_tool == -1) { for (int i = 0; i < static_cast(m_numberOfTools); ++i) _iterator.push_back(i); } else { _iterator.push_back(GetToolIndex(_tool)); } return _iterator; } void mitk::PolhemusInterface::PrintStatus() { MITK_INFO << "Polhemus status: " << this->m_pdiDev->CnxReady(); } \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp b/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp index 08f92552bc..343aecf11a 100644 --- a/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp +++ b/Modules/PhotoacousticsAlgorithms/mitkPhotoacousticImage.cpp @@ -1,522 +1,520 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ -#define _USE_MATH_DEFINES -#include #include "mitkPhotoacousticImage.h" #include "ITKFilter/ITKUltrasound/itkBModeImageFilter.h" #include "ITKFilter/itkPhotoacousticBModeImageFilter.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkPhotoacousticBeamformingFilter.h" #include #include #include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" // itk dependencies #include "itkImage.h" #include "itkResampleImageFilter.h" #include "itkCastImageFilter.h" #include "itkCropImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include #include "itkMultiplyImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include // needed itk image filters #include "mitkITKImageImport.h" #include "itkFFTShiftImageFilter.h" #include "itkMultiplyImageFilter.h" #include "itkComplexToModulusImageFilter.h" #include #include "ITKFilter/ITKUltrasound/itkFFT1DComplexConjugateToRealImageFilter.h" #include "ITKFilter/ITKUltrasound/itkFFT1DRealToComplexConjugateImageFilter.h" mitk::PhotoacousticImage::PhotoacousticImage() { MITK_INFO << "[PhotoacousticImage Debug] created that image"; } mitk::PhotoacousticImage::~PhotoacousticImage() { MITK_INFO << "[PhotoacousticImage Debug] destroyed that image"; } mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method, bool UseGPU, bool UseLogFilter, float resampleSpacing) { // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::IdentityTransform TransformType; if (method == BModeMethod::Abs) { mitk::Image::Pointer input; mitk::Image::Pointer out; if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") input = inputImage; else input = ApplyCropping(inputImage, 0, 0, 0, 0, 0, inputImage->GetDimension(2) - 1); if (!UseGPU) { PhotoacousticBModeFilter::Pointer filter = PhotoacousticBModeFilter::New(); filter->SetParameters(UseLogFilter); filter->SetInput(input); filter->Update(); out = filter->GetOutput(); if (resampleSpacing == 0) return out; } #ifdef PHOTOACOUSTICS_USE_GPU else { PhotoacousticOCLBModeFilter::Pointer filter = PhotoacousticOCLBModeFilter::New(); filter->SetParameters(UseLogFilter); filter->SetInput(input); filter->Update(); out = filter->GetOutput(); if (resampleSpacing == 0) return out; } #endif typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(out, itkImage); itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; outputSpacing[0] = itkImage->GetSpacing()[0]; outputSpacing[1] = resampleSpacing; outputSpacing[2] = itkImage->GetSpacing()[2]; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } else if (method == BModeMethod::ShapeDetection) { typedef itk::BModeImageFilter < itkFloatImageType, itkFloatImageType > BModeFilterType; BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); itkFloatImageType::Pointer bmode; if (UseLogFilter) { bModeFilter->SetInput(itkImage); bModeFilter->SetDirection(1); bmode = bModeFilter->GetOutput(); } else { photoacousticBModeFilter->SetInput(itkImage); photoacousticBModeFilter->SetDirection(1); bmode = photoacousticBModeFilter->GetOutput(); } // resampleSpacing == 0 means: do no resampling if (resampleSpacing == 0) { return mitk::GrabItkImageMemory(bmode); } itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; outputSpacing[0] = itkImage->GetSpacing()[0]; outputSpacing[1] = resampleSpacing; outputSpacing[2] = itkImage->GetSpacing()[2]; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; resampleImageFilter->SetInput(bmode); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } return nullptr; } /*mitk::Image::Pointer mitk::PhotoacousticImage::ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scattering) { typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::MultiplyImageFilter MultiplyImageFilterType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); multiplyFilter->SetInput1(itkImage); multiplyFilter->SetInput2(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); }*/ mitk::Image::Pointer mitk::PhotoacousticImage::ApplyResampling(mitk::Image::Pointer inputImage, unsigned int outputSize[2]) { typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); typedef itk::LinearInterpolateImageFunction T_Interpolator; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); itkFloatImageType::SpacingType outputSpacingItk; itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSizeItk = inputSizeItk; outputSizeItk[0] = outputSize[0]; outputSizeItk[1] = outputSize[1]; outputSizeItk[2] = inputSizeItk[2]; outputSpacingItk[0] = itkImage->GetSpacing()[0] * (static_cast(inputSizeItk[0]) / static_cast(outputSizeItk[0])); outputSpacingItk[1] = itkImage->GetSpacing()[1] * (static_cast(inputSizeItk[1]) / static_cast(outputSizeItk[1])); outputSpacingItk[2] = itkImage->GetSpacing()[2]; typedef itk::IdentityTransform TransformType; T_Interpolator::Pointer _pInterpolator = T_Interpolator::New(); resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSizeItk); resampleImageFilter->SetOutputSpacing(outputSpacingItk); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->SetInterpolator(_pInterpolator); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::PhotoacousticImage::ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice) { unsigned int inputDim[3] = { inputImage->GetDimension(0), inputImage->GetDimension(1), inputImage->GetDimension(2) }; unsigned int outputDim[3] = { inputImage->GetDimension(0) - left - right, inputImage->GetDimension(1) - (unsigned int)above - (unsigned int)below, (unsigned int)maxSlice - (unsigned int)minSlice + 1 }; void* inputData; float* outputData = new float[outputDim[0] * outputDim[1] * outputDim[2]]; ImageReadAccessor acc(inputImage); inputData = const_cast(acc.GetData()); // convert the data to float by default // as of now only those float, short, float are used at all... though it's easy to add other ones if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") { // copy the data into the cropped image for (unsigned short sl = 0; sl < outputDim[2]; ++sl) { for (unsigned short l = 0; l < outputDim[0]; ++l) { for (unsigned short s = 0; s < outputDim[1]; ++s) { outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((float*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; } } } } else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (short)" || inputImage->GetPixelType().GetTypeAsString() == " (short)") { // copy the data unsigned shorto the cropped image for (unsigned short sl = 0; sl < outputDim[2]; ++sl) { for (unsigned short l = 0; l < outputDim[0]; ++l) { for (unsigned short s = 0; s < outputDim[1]; ++s) { outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((short*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; } } } } else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (double)" || inputImage->GetPixelType().GetTypeAsString() == " (double)") { // copy the data unsigned shorto the cropped image for (unsigned short sl = 0; sl < outputDim[2]; ++sl) { for (unsigned short l = 0; l < outputDim[0]; ++l) { for (unsigned short s = 0; s < outputDim[1]; ++s) { outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((double*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; } } } } else { MITK_INFO << "Could not determine pixel type"; } mitk::Image::Pointer output = mitk::Image::New(); output->Initialize(mitk::MakeScalarPixelType(), 3, outputDim); output->SetSpacing(inputImage->GetGeometry()->GetSpacing()); output->SetImportVolume(outputData, 0, 0, mitk::Image::ReferenceMemory); return output; } mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings config, int cutoff, std::function progressHandle) { config.RecordTime = config.RecordTime - (cutoff) / inputImage->GetDimension(1) * config.RecordTime; // adjust the recorded time lost by cropping progressHandle(0, "cropping image"); if (!config.partial) { config.CropBounds[0] = 0; config.CropBounds[1] = inputImage->GetDimension(2) - 1; } Image::Pointer processedImage = ApplyCropping(inputImage, cutoff, 0, 0, 0, config.CropBounds[0], config.CropBounds[1]); config.inputDim[0] = processedImage->GetDimension(0); config.inputDim[1] = processedImage->GetDimension(1); config.inputDim[2] = processedImage->GetDimension(2); // perform the beamforming BeamformingFilter::Pointer Beamformer = BeamformingFilter::New(); Beamformer->SetInput(processedImage); Beamformer->Configure(config); Beamformer->SetProgressHandle(progressHandle); Beamformer->UpdateLargestPossibleRegion(); processedImage = Beamformer->GetOutput(); return processedImage; } mitk::Image::Pointer mitk::PhotoacousticImage::BandpassFilter(mitk::Image::Pointer data, float recordTime, float BPHighPass, float BPLowPass, float alpha) { bool powerOfTwo = false; int finalPower = 0; for (int i = 1; pow(2, i) <= data->GetDimension(1); ++i) { finalPower = i; if (pow(2, i) == data->GetDimension(1)) { powerOfTwo = true; } } if (!powerOfTwo) { unsigned int dim[2] = { data->GetDimension(0), (unsigned int)pow(2,finalPower+1)}; data = ApplyResampling(data, dim); } MITK_INFO << data->GetDimension(0); // do a fourier transform, multiply with an appropriate window for the filter, and transform back typedef float PixelType; typedef itk::Image< PixelType, 3 > RealImageType; RealImageType::Pointer image; mitk::CastToItkImage(data, image); typedef itk::FFT1DRealToComplexConjugateImageFilter ForwardFFTFilterType; typedef ForwardFFTFilterType::OutputImageType ComplexImageType; ForwardFFTFilterType::Pointer forwardFFTFilter = ForwardFFTFilterType::New(); forwardFFTFilter->SetInput(image); forwardFFTFilter->SetDirection(1); try { forwardFFTFilter->UpdateOutputInformation(); } catch (itk::ExceptionObject & error) { std::cerr << "Error: " << error << std::endl; MITK_WARN << "Bandpass could not be applied"; return data; } float singleVoxel = 1 / (recordTime / data->GetDimension(1)) / 2 / 1000; float cutoffPixelHighPass = std::min(BPHighPass / singleVoxel, (float)data->GetDimension(1) / 2); float cutoffPixelLowPass = std::min(BPLowPass / singleVoxel, (float)data->GetDimension(1) / 2 - cutoffPixelHighPass); RealImageType::Pointer fftMultiplicator = BPFunction(data, cutoffPixelHighPass, cutoffPixelLowPass, alpha); typedef itk::MultiplyImageFilter< ComplexImageType, RealImageType, ComplexImageType > MultiplyFilterType; MultiplyFilterType::Pointer multiplyFilter = MultiplyFilterType::New(); multiplyFilter->SetInput1(forwardFFTFilter->GetOutput()); multiplyFilter->SetInput2(fftMultiplicator); /*itk::ComplexToModulusImageFilter::Pointer toReal = itk::ComplexToModulusImageFilter::New(); toReal->SetInput(forwardFFTFilter->GetOutput()); return GrabItkImageMemory(toReal->GetOutput()); return GrabItkImageMemory(fftMultiplicator); *///DEBUG typedef itk::FFT1DComplexConjugateToRealImageFilter< ComplexImageType, RealImageType > InverseFilterType; InverseFilterType::Pointer inverseFFTFilter = InverseFilterType::New(); inverseFFTFilter->SetInput(multiplyFilter->GetOutput()); inverseFFTFilter->SetDirection(1); return GrabItkImageMemory(inverseFFTFilter->GetOutput()); } itk::Image::Pointer mitk::PhotoacousticImage::BPFunction(mitk::Image::Pointer reference, int cutoffFrequencyPixelHighPass, int cutoffFrequencyPixelLowPass, float alpha) { float* imageData = new float[reference->GetDimension(0)*reference->GetDimension(1)]; // tukey window float width = reference->GetDimension(1) / 2 - (float)cutoffFrequencyPixelHighPass - (float)cutoffFrequencyPixelLowPass; float center = (float)cutoffFrequencyPixelHighPass / 2 + width / 2; MITK_INFO << width << "width " << center << "center " << alpha; for (unsigned int n = 0; n < reference->GetDimension(1); ++n) { imageData[reference->GetDimension(0)*n] = 0; } for (int n = 0; n < width; ++n) { if (n <= (alpha*(width - 1)) / 2) { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(M_PI*(2 * n / (alpha*(width - 1)) - 1))) / 2; + imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(itk::Math::pi*(2 * n / (alpha*(width - 1)) - 1))) / 2; } else if (n >= (width - 1)*(1 - alpha / 2) && n <= (width - 1)) { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(M_PI*(2 * n / (alpha*(width - 1)) + 1 - 2 / alpha))) / 2; + imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(itk::Math::pi*(2 * n / (alpha*(width - 1)) + 1 - 2 / alpha))) / 2; } else { imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = 1; } } // Butterworth-Filter /* // first, write the HighPass if (cutoffFrequencyPixelHighPass != reference->GetDimension(1) / 2) { for (int n = 0; n < reference->GetDimension(1) / 2; ++n) { imageData[reference->GetDimension(0)*n] = 1 / (1 + pow( (float)n / (float)(reference->GetDimension(1) / 2 - cutoffFrequencyPixelHighPass) , 2 * butterworthOrder)); } } else { for (int n = 0; n < reference->GetDimension(1) / 2; ++n) { imageData[reference->GetDimension(0)*n] = 1; } } // now, the LowPass for (int n = 0; n < reference->GetDimension(1) / 2; ++n) { imageData[reference->GetDimension(0)*n] *= 1 / (1 + pow( (float)(reference->GetDimension(1) / 2 - 1 - n) / (float)(reference->GetDimension(1) / 2 - cutoffFrequencyPixelLowPass) , 2 * butterworthOrder)); } */ // mirror the first half of the image for (unsigned int n = reference->GetDimension(1) / 2; n < reference->GetDimension(1); ++n) { imageData[reference->GetDimension(0)*n] = imageData[(reference->GetDimension(1) - (n + 1)) * reference->GetDimension(0)]; } // copy and paste to all lines for (unsigned int line = 1; line < reference->GetDimension(0); ++line) { for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) { imageData[reference->GetDimension(0)*sample + line] = imageData[reference->GetDimension(0)*sample]; } } typedef itk::Image< float, 3U > ImageType; ImageType::RegionType region; ImageType::IndexType start; start.Fill(0); region.SetIndex(start); ImageType::SizeType size; size[0] = reference->GetDimension(0); size[1] = reference->GetDimension(1); size[2] = reference->GetDimension(2); region.SetSize(size); ImageType::SpacingType SpacingItk; SpacingItk[0] = reference->GetGeometry()->GetSpacing()[0]; SpacingItk[1] = reference->GetGeometry()->GetSpacing()[1]; SpacingItk[2] = reference->GetGeometry()->GetSpacing()[2]; ImageType::Pointer image = ImageType::New(); image->SetRegions(region); image->Allocate(); image->FillBuffer(itk::NumericTraits::Zero); image->SetSpacing(SpacingItk); ImageType::IndexType pixelIndex; for (ImageType::IndexValueType slice = 0; slice < reference->GetDimension(2); ++slice) { for (ImageType::IndexValueType line = 0; line < reference->GetDimension(0); ++line) { for (ImageType::IndexValueType sample = 0; sample < reference->GetDimension(1); ++sample) { pixelIndex[0] = line; pixelIndex[1] = sample; pixelIndex[2] = slice; image->SetPixel(pixelIndex, imageData[line + sample*reference->GetDimension(0)]); } } } delete[] imageData; return image; } \ No newline at end of file diff --git a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp index c44b1a98d7..08f79410c3 100644 --- a/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.cpp @@ -1,116 +1,114 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN -#define _USE_MATH_DEFINES -#include #include "./OpenCLFilter/mitkPhotoacousticOCLUsedLinesCalculation.h" #include "usServiceReference.h" #include "mitkImageReadAccessor.h" mitk::OCLUsedLinesCalculation::OCLUsedLinesCalculation() : m_PixelCalculation(NULL) { this->AddSourceFile("UsedLinesCalculation.cl"); this->m_FilterID = "UsedLinesCalculation"; m_ChunkSize[0] = 128; m_ChunkSize[1] = 128; m_ChunkSize[2] = 8; this->Initialize(); } mitk::OCLUsedLinesCalculation::~OCLUsedLinesCalculation() { if (this->m_PixelCalculation) { clReleaseKernel(m_PixelCalculation); } } void mitk::OCLUsedLinesCalculation::Update() { //Check if context & program available if (!this->Initialize()) { us::ServiceReference ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); // clean-up also the resources resources->InvalidateStorage(); mitkThrow() << "Filter is not initialized. Cannot update."; } else { // Execute this->Execute(); } } void mitk::OCLUsedLinesCalculation::Execute() { cl_int clErr = 0; unsigned int gridDim[3] = { m_Conf.ReconstructionLines, m_Conf.SamplesPerLine, 1 }; size_t outputSize = gridDim[0] * gridDim[1] * 3; try { this->InitExecNoInput(this->m_PixelCalculation, gridDim, outputSize, sizeof(unsigned short)); } catch (const mitk::Exception& e) { MITK_ERROR << "Caught exception while initializing UsedLines filter: " << e.what(); return; } // This calculation is the same for all kernels, so for performance reasons simply perform it here instead of within the kernels - m_part = (tan(m_Conf.Angle / 360 * 2 * M_PI) * ((m_Conf.SpeedOfSound * m_Conf.TimeSpacing)) / (m_Conf.Pitch * m_Conf.TransducerElements)) * m_Conf.inputDim[0]; + m_part = (tan(m_Conf.Angle / 360 * 2 * itk::Math::pi) * ((m_Conf.SpeedOfSound * m_Conf.TimeSpacing)) / (m_Conf.Pitch * m_Conf.TransducerElements)) * m_Conf.inputDim[0]; clErr = clSetKernelArg(this->m_PixelCalculation, 1, sizeof(cl_float), &(this->m_part)); clErr |= clSetKernelArg(this->m_PixelCalculation, 2, sizeof(cl_uint), &(this->m_Conf.inputDim[0])); clErr |= clSetKernelArg(this->m_PixelCalculation, 3, sizeof(cl_uint), &(this->m_Conf.inputDim[1])); clErr |= clSetKernelArg(this->m_PixelCalculation, 4, sizeof(cl_uint), &(this->m_Conf.ReconstructionLines)); clErr |= clSetKernelArg(this->m_PixelCalculation, 5, sizeof(cl_uint), &(this->m_Conf.SamplesPerLine)); CHECK_OCL_ERR(clErr); // execute the filter on a 2D NDRange if (!this->ExecuteKernelChunksInBatches(m_PixelCalculation, 2, m_ChunkSize, 16, 50)) mitkThrow() << "openCL Error when executing Kernel"; // signalize the GPU-side data changed m_Output->Modified(GPU_DATA); } us::Module *mitk::OCLUsedLinesCalculation::GetModule() { return us::GetModuleContext()->GetModule(); } bool mitk::OCLUsedLinesCalculation::Initialize() { bool buildErr = true; cl_int clErr = 0; if (OclFilter::Initialize()) { this->m_PixelCalculation = clCreateKernel(this->m_ClProgram, "ckUsedLines", &clErr); buildErr |= CHECK_OCL_ERR(clErr); } return (OclFilter::IsInitialized() && buildErr); } #endif diff --git a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp b/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp index 46abbcbb98..20abc2c982 100644 --- a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticBeamformingFilter.cpp @@ -1,804 +1,801 @@ /*=================================================================== mitkPhotoacousticBeamformingFilter The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ -#define _USE_MATH_DEFINES - #include "mitkProperties.h" #include "mitkImageReadAccessor.h" #include #include #include -#include #include #include #include "mitkImageCast.h" #include "mitkPhotoacousticBeamformingFilter.h" mitk::BeamformingFilter::BeamformingFilter() : m_OutputData(nullptr), m_InputData(nullptr), m_Message("noMessage") { this->SetNumberOfIndexedInputs(1); this->SetNumberOfRequiredInputs(1); m_ProgressHandle = [](int, std::string) {}; m_BeamformingOclFilter = mitk::PhotoacousticOCLBeamformingFilter::New(); m_VonHannFunction = VonHannFunction(m_Conf.apodizationArraySize); m_HammFunction = HammFunction(m_Conf.apodizationArraySize); m_BoxFunction = BoxFunction(m_Conf.apodizationArraySize); } void mitk::BeamformingFilter::SetProgressHandle(std::function progressHandle) { m_ProgressHandle = progressHandle; } mitk::BeamformingFilter::~BeamformingFilter() { delete[] m_VonHannFunction; delete[] m_HammFunction; delete[] m_BoxFunction; } void mitk::BeamformingFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image* output = this->GetOutput(); mitk::Image* input = const_cast (this->GetInput()); if (!output->IsInitialized()) { return; } input->SetRequestedRegionToLargestPossibleRegion(); //GenerateTimeInInputRegion(output, input); } void mitk::BeamformingFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); unsigned int dim[] = { m_Conf.ReconstructionLines, m_Conf.SamplesPerLine, input->GetDimension(2) }; output->Initialize(mitk::MakeScalarPixelType(), 3, dim); mitk::Vector3D spacing; spacing[0] = m_Conf.Pitch * m_Conf.TransducerElements * 1000 / m_Conf.ReconstructionLines; spacing[1] = (m_Conf.TimeSpacing * m_Conf.inputDim[1]) / 2 * m_Conf.SpeedOfSound * 1000 / m_Conf.SamplesPerLine; spacing[2] = 1; output->GetGeometry()->SetSpacing(spacing); output->GetGeometry()->Modified(); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } void mitk::BeamformingFilter::GenerateData() { GenerateOutputInformation(); mitk::Image::Pointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (!output->IsInitialized()) return; float* ApodWindow; if (m_ConfOld.apodizationArraySize != m_Conf.apodizationArraySize) { delete[] m_VonHannFunction; delete[] m_HammFunction; delete[] m_BoxFunction; m_VonHannFunction = VonHannFunction(m_Conf.apodizationArraySize); m_HammFunction = HammFunction(m_Conf.apodizationArraySize); m_BoxFunction = BoxFunction(m_Conf.apodizationArraySize); m_ConfOld = m_Conf; } // set the appropiate apodization window switch (m_Conf.Apod) { case BeamformingSettings::Apodization::Hann: ApodWindow = m_VonHannFunction; break; case BeamformingSettings::Apodization::Hamm: ApodWindow = m_HammFunction; break; case BeamformingSettings::Apodization::Box: ApodWindow = m_BoxFunction; break; default: ApodWindow = m_BoxFunction; break; } auto begin = std::chrono::high_resolution_clock::now(); // debbuging the performance... if (!m_Conf.UseGPU) { int progInterval = output->GetDimension(2) / 20 > 1 ? output->GetDimension(2) / 20 : 1; // the interval at which we update the gui progress bar float inputDim[2] = { (float)input->GetDimension(0), (float)input->GetDimension(1) }; float outputDim[2] = { (float)output->GetDimension(0), (float)output->GetDimension(1) }; for (unsigned int i = 0; i < output->GetDimension(2); ++i) // seperate Slices should get Beamforming seperately applied { mitk::ImageReadAccessor inputReadAccessor(input, input->GetSliceData(i)); // first, we check whether the dara is float, other formats are unsupported if (input->GetPixelType().GetTypeAsString() == "scalar (float)" || input->GetPixelType().GetTypeAsString() == " (float)") { m_InputData = (float*)inputReadAccessor.GetData(); } else { MITK_INFO << "Pixel type is not float, abort"; return; } m_OutputData = new float[m_Conf.ReconstructionLines*m_Conf.SamplesPerLine]; // fill the image with zeros for (int l = 0; l < outputDim[0]; ++l) { for (int s = 0; s < outputDim[1]; ++s) { m_OutputData[l*(short)outputDim[1] + s] = 0; } } std::thread *threads = new std::thread[(short)outputDim[0]]; // every line will be beamformed in a seperate thread if (m_Conf.Algorithm == BeamformingSettings::BeamformingAlgorithm::DAS) { if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) { for (short line = 0; line < outputDim[0]; ++line) { threads[line] = std::thread(&BeamformingFilter::DASQuadraticLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); } } else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) { for (short line = 0; line < outputDim[0]; ++line) { threads[line] = std::thread(&BeamformingFilter::DASSphericalLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); } } } else if (m_Conf.Algorithm == BeamformingSettings::BeamformingAlgorithm::DMAS) { if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) { for (short line = 0; line < outputDim[0]; ++line) { threads[line] = std::thread(&BeamformingFilter::DMASQuadraticLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); } } else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) { for (short line = 0; line < outputDim[0]; ++line) { threads[line] = std::thread(&BeamformingFilter::DMASSphericalLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); } } } else if (m_Conf.Algorithm == BeamformingSettings::BeamformingAlgorithm::sDMAS) { if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::QuadApprox) { for (short line = 0; line < outputDim[0]; ++line) { threads[line] = std::thread(&BeamformingFilter::sDMASQuadraticLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); } } else if (m_Conf.DelayCalculationMethod == BeamformingSettings::DelayCalc::Spherical) { for (short line = 0; line < outputDim[0]; ++line) { threads[line] = std::thread(&BeamformingFilter::sDMASSphericalLine, this, m_InputData, m_OutputData, inputDim, outputDim, line, ApodWindow, m_Conf.apodizationArraySize); } } } // wait for all lines to finish for (short line = 0; line < outputDim[0]; ++line) { threads[line].join(); } output->SetSlice(m_OutputData, i); if (i % progInterval == 0) m_ProgressHandle((int)((i + 1) / (float)output->GetDimension(2) * 100), "performing reconstruction"); delete[] m_OutputData; m_OutputData = nullptr; m_InputData = nullptr; } } #if defined(PHOTOACOUSTICS_USE_GPU) || DOXYGEN else { try { // first, we check whether the data is float, other formats are unsupported if (!(input->GetPixelType().GetTypeAsString() == "scalar (float)" || input->GetPixelType().GetTypeAsString() == " (float)")) { MITK_ERROR << "Pixel type is not float, abort"; return; } unsigned long availableMemory = m_BeamformingOclFilter->GetDeviceMemory(); unsigned int batchSize = 16; unsigned int batches = (unsigned int)((float)input->GetDimension(2)/batchSize) + (input->GetDimension(2)%batchSize > 0); unsigned int batchDim[] = { input->GetDimension(0), input->GetDimension(1), batchSize }; unsigned int batchDimLast[] = { input->GetDimension(0), input->GetDimension(1), input->GetDimension(2) % batchSize }; // the following safeguard is probably only needed for absurdly small GPU memory for (batchSize = 16; (unsigned long)batchSize * ((unsigned long)(batchDim[0] * batchDim[1]) * 4 + // single input image (float) (unsigned long)(m_Conf.ReconstructionLines * m_Conf.SamplesPerLine) * 4) // single output image (float) > availableMemory - (unsigned long)(m_Conf.ReconstructionLines / 2 * m_Conf.SamplesPerLine) * 2 - // Delays buffer (unsigned short) (unsigned long)(m_Conf.ReconstructionLines * m_Conf.SamplesPerLine) * 3 * 2 - // UsedLines buffer (unsigned short) 50 * 1024 * 1024; // 50 MB buffer for local data, system purposes etc --batchSize) {} if (batchSize < 1) { MITK_ERROR << "device memory too small for GPU beamforming"; return; } mitk::ImageReadAccessor copy(input); for(unsigned int i = 0; i < batches; ++i) { m_ProgressHandle(input->GetDimension(2)/batches * i, "performing reconstruction"); mitk::Image::Pointer inputBatch = mitk::Image::New(); if(i == batches - 1 && (input->GetDimension(2)%batchSize > 0)) { inputBatch->Initialize(mitk::MakeScalarPixelType(), 3, batchDimLast); m_Conf.inputDim[2] = batchDimLast[2]; } else { inputBatch->Initialize(mitk::MakeScalarPixelType(), 3, batchDim); m_Conf.inputDim[2] = batchDim[2]; } inputBatch->SetSpacing(input->GetGeometry()->GetSpacing()); inputBatch->SetImportVolume(&(((float*)copy.GetData())[input->GetDimension(0) * input->GetDimension(1) * batchSize * i])); m_BeamformingOclFilter->SetApodisation(ApodWindow, m_Conf.apodizationArraySize); m_BeamformingOclFilter->SetConfig(m_Conf); m_BeamformingOclFilter->SetInput(inputBatch); m_BeamformingOclFilter->Update(); void* out = m_BeamformingOclFilter->GetOutput(); for(unsigned int slice = 0; slice < m_Conf.inputDim[2]; ++slice) { output->SetImportSlice( &(((float*)out)[m_Conf.ReconstructionLines * m_Conf.SamplesPerLine * slice]), batchSize * i + slice, 0, 0, mitk::Image::ImportMemoryManagementType::CopyMemory); } } } catch (mitk::Exception &e) { std::string errorMessage = "Caught unexpected exception "; errorMessage.append(e.what()); MITK_ERROR << errorMessage; float* dummyData = new float[m_Conf.ReconstructionLines * m_Conf.SamplesPerLine * m_Conf.inputDim[2]]; output->SetImportVolume(dummyData, 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory); m_Message = "An openCL error occurred; all GPU operations in this and the next session may be corrupted."; } } #endif m_TimeOfHeaderInitialization.Modified(); auto end = std::chrono::high_resolution_clock::now(); MITK_INFO << "Beamforming of " << output->GetDimension(2) << " Images completed in " << ((float)std::chrono::duration_cast(end - begin).count()) / 1000000 << "ms" << std::endl; } float* mitk::BeamformingFilter::VonHannFunction(int samples) { float* ApodWindow = new float[samples]; for (int n = 0; n < samples; ++n) { - ApodWindow[n] = (1 - cos(2 * M_PI * n / (samples - 1))) / 2; + ApodWindow[n] = (1 - cos(2 * itk::Math::pi * n / (samples - 1))) / 2; } return ApodWindow; } float* mitk::BeamformingFilter::HammFunction(int samples) { float* ApodWindow = new float[samples]; for (int n = 0; n < samples; ++n) { - ApodWindow[n] = 0.54 - 0.46*cos(2 * M_PI*n / (samples - 1)); + ApodWindow[n] = 0.54 - 0.46*cos(2 * itk::Math::pi*n / (samples - 1)); } return ApodWindow; } float* mitk::BeamformingFilter::BoxFunction(int samples) { float* ApodWindow = new float[samples]; for (int n = 0; n < samples; ++n) { ApodWindow[n] = 1; } return ApodWindow; } void mitk::BeamformingFilter::DASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) { float& inputS = inputDim[1]; float& inputL = inputDim[0]; float& outputS = outputDim[1]; float& outputL = outputDim[0]; short AddSample = 0; short maxLine = 0; short minLine = 0; float delayMultiplicator = 0; float l_i = 0; float s_i = 0; float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * M_PI); + float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / m_Conf.TransducerElements; float apod_mult = 1; short usedLines = (maxLine - minLine); //quadratic delay l_i = line / outputL * inputL; for (short sample = 0; sample < outputS; ++sample) { s_i = (float)sample / outputS * inputS / 2; part = part_multiplicator*s_i; if (part < 1) part = 1; maxLine = (short)std::min((l_i + part) + 1, inputL); minLine = (short)std::max((l_i - part), 0.0f); usedLines = (maxLine - minLine); apod_mult = (float)apodArraySize / (float)usedLines; delayMultiplicator = pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*m_Conf.TransducerElements) / inputL), 2) / s_i / 2; for (short l_s = minLine; l_s < maxLine; ++l_s) { AddSample = delayMultiplicator * pow((l_s - l_i), 2) + s_i + (1 - m_Conf.isPhotoacousticImage)*s_i; if (AddSample < inputS && AddSample >= 0) output[sample*(short)outputL + line] += input[l_s + AddSample*(short)inputL] * apodisation[(short)((l_s - minLine)*apod_mult)]; else --usedLines; } output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / usedLines; } } void mitk::BeamformingFilter::DASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) { float& inputS = inputDim[1]; float& inputL = inputDim[0]; float& outputS = outputDim[1]; float& outputL = outputDim[0]; short AddSample = 0; short maxLine = 0; short minLine = 0; float l_i = 0; float s_i = 0; float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * M_PI); + float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; float apod_mult = 1; short usedLines = (maxLine - minLine); //exact delay l_i = (float)line / outputL * inputL; for (short sample = 0; sample < outputS; ++sample) { s_i = (float)sample / outputS * inputS / 2; part = part_multiplicator*s_i; if (part < 1) part = 1; maxLine = (short)std::min((l_i + part) + 1, inputL); minLine = (short)std::max((l_i - part), 0.0f); usedLines = (maxLine - minLine); apod_mult = (float)apodArraySize / (float)usedLines; for (short l_s = minLine; l_s < maxLine; ++l_s) { AddSample = (int)sqrt( pow(s_i, 2) + pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (((float)l_s - l_i)*m_Conf.Pitch*(float)m_Conf.TransducerElements) / inputL), 2) ) + (1 - m_Conf.isPhotoacousticImage)*s_i; if (AddSample < inputS && AddSample >= 0) output[sample*(short)outputL + line] += input[l_s + AddSample*(short)inputL] * apodisation[(short)((l_s - minLine)*apod_mult)]; else --usedLines; } output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / usedLines; } } void mitk::BeamformingFilter::DMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) { float& inputS = inputDim[1]; float& inputL = inputDim[0]; float& outputS = outputDim[1]; float& outputL = outputDim[0]; short maxLine = 0; short minLine = 0; float delayMultiplicator = 0; float l_i = 0; float s_i = 0; float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * M_PI); + float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; float apod_mult = 1; float mult = 0; short usedLines = (maxLine - minLine); //quadratic delay l_i = line / outputL * inputL; for (short sample = 0; sample < outputS; ++sample) { s_i = sample / outputS * inputS / 2; part = part_multiplicator*s_i; if (part < 1) part = 1; maxLine = (short)std::min((l_i + part) + 1, inputL); minLine = (short)std::max((l_i - part), 0.0f); usedLines = (maxLine - minLine); apod_mult = (float)apodArraySize / (float)usedLines; delayMultiplicator = pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*m_Conf.TransducerElements) / inputL), 2) / s_i / 2; //calculate the AddSamples beforehand to save some time short* AddSample = new short[maxLine - minLine]; for (short l_s = 0; l_s < maxLine - minLine; ++l_s) { AddSample[l_s] = (short)(delayMultiplicator * pow((minLine + l_s - l_i), 2) + s_i) + (1 - m_Conf.isPhotoacousticImage)*s_i; } float s_1 = 0; float s_2 = 0; for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) { if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) { for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) { if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) { s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); } } } else --usedLines; } output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)); delete[] AddSample; } } void mitk::BeamformingFilter::DMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) { float& inputS = inputDim[1]; float& inputL = inputDim[0]; float& outputS = outputDim[1]; float& outputL = outputDim[0]; short maxLine = 0; short minLine = 0; float l_i = 0; float s_i = 0; float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * M_PI); + float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; float apod_mult = 1; float mult = 0; short usedLines = (maxLine - minLine); //exact delay l_i = (float)line / outputL * inputL; for (short sample = 0; sample < outputS; ++sample) { s_i = (float)sample / outputS * inputS / 2; part = part_multiplicator*s_i; if (part < 1) part = 1; maxLine = (short)std::min((l_i + part) + 1, inputL); minLine = (short)std::max((l_i - part), 0.0f); usedLines = (maxLine - minLine); apod_mult = (float)apodArraySize / (float)usedLines; //calculate the AddSamples beforehand to save some time short* AddSample = new short[maxLine - minLine]; for (short l_s = 0; l_s < maxLine - minLine; ++l_s) { AddSample[l_s] = (short)sqrt( pow(s_i, 2) + pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (((float)minLine + (float)l_s - l_i)*m_Conf.Pitch*(float)m_Conf.TransducerElements) / inputL), 2) ) + (1 - m_Conf.isPhotoacousticImage)*s_i; } float s_1 = 0; float s_2 = 0; for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) { if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) { for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) { if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) { s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); } } } else --usedLines; } output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)); delete[] AddSample; } } void mitk::BeamformingFilter::sDMASQuadraticLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) { float& inputS = inputDim[1]; float& inputL = inputDim[0]; float& outputS = outputDim[1]; float& outputL = outputDim[0]; short maxLine = 0; short minLine = 0; float delayMultiplicator = 0; float l_i = 0; float s_i = 0; float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * M_PI); + float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; float apod_mult = 1; float mult = 0; short usedLines = (maxLine - minLine); //quadratic delay l_i = line / outputL * inputL; for (short sample = 0; sample < outputS; ++sample) { s_i = sample / outputS * inputS / 2; part = part_multiplicator*s_i; if (part < 1) part = 1; maxLine = (short)std::min((l_i + part) + 1, inputL); minLine = (short)std::max((l_i - part), 0.0f); usedLines = (maxLine - minLine); apod_mult = (float)apodArraySize / (float)usedLines; delayMultiplicator = pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (m_Conf.Pitch*m_Conf.TransducerElements) / inputL), 2) / s_i / 2; //calculate the AddSamples beforehand to save some time short* AddSample = new short[maxLine - minLine]; for (short l_s = 0; l_s < maxLine - minLine; ++l_s) { AddSample[l_s] = (short)(delayMultiplicator * pow((minLine + l_s - l_i), 2) + s_i) + (1 - m_Conf.isPhotoacousticImage)*s_i; } float s_1 = 0; float s_2 = 0; float sign = 0; for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) { if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) { s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; sign += s_1; for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) { if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) { s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); } } } else --usedLines; } output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)) * ((sign > 0) - (sign < 0)); delete[] AddSample; } } void mitk::BeamformingFilter::sDMASSphericalLine(float* input, float* output, float inputDim[2], float outputDim[2], const short& line, float* apodisation, const short& apodArraySize) { float& inputS = inputDim[1]; float& inputL = inputDim[0]; float& outputS = outputDim[1]; float& outputL = outputDim[0]; short maxLine = 0; short minLine = 0; float l_i = 0; float s_i = 0; float part = 0.07 * inputL; - float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * M_PI); + float tan_phi = std::tan(m_Conf.Angle / 360 * 2 * itk::Math::pi); float part_multiplicator = tan_phi * m_Conf.TimeSpacing * m_Conf.SpeedOfSound / m_Conf.Pitch * inputL / (float)m_Conf.TransducerElements; float apod_mult = 1; float mult = 0; short usedLines = (maxLine - minLine); //exact delay l_i = (float)line / outputL * inputL; for (short sample = 0; sample < outputS; ++sample) { s_i = (float)sample / outputS * inputS / 2; part = part_multiplicator*s_i; if (part < 1) part = 1; maxLine = (short)std::min((l_i + part) + 1, inputL); minLine = (short)std::max((l_i - part), 0.0f); usedLines = (maxLine - minLine); apod_mult = (float)apodArraySize / (float)usedLines; //calculate the AddSamples beforehand to save some time short* AddSample = new short[maxLine - minLine]; for (short l_s = 0; l_s < maxLine - minLine; ++l_s) { AddSample[l_s] = (short)sqrt( pow(s_i, 2) + pow((1 / (m_Conf.TimeSpacing*m_Conf.SpeedOfSound) * (((float)minLine + (float)l_s - l_i)*m_Conf.Pitch*(float)m_Conf.TransducerElements) / inputL), 2) ) + (1 - m_Conf.isPhotoacousticImage)*s_i; } float s_1 = 0; float s_2 = 0; float sign = 0; for (short l_s1 = minLine; l_s1 < maxLine - 1; ++l_s1) { if (AddSample[l_s1 - minLine] < inputS && AddSample[l_s1 - minLine] >= 0) { s_1 = input[l_s1 + AddSample[l_s1 - minLine] * (short)inputL]; sign += s_1; for (short l_s2 = l_s1 + 1; l_s2 < maxLine; ++l_s2) { if (AddSample[l_s2 - minLine] < inputS && AddSample[l_s2 - minLine] >= 0) { s_2 = input[l_s2 + AddSample[l_s2 - minLine] * (short)inputL]; mult = s_2 * apodisation[(int)((l_s2 - minLine)*apod_mult)] * s_1 * apodisation[(int)((l_s1 - minLine)*apod_mult)]; output[sample*(short)outputL + line] += sqrt(fabs(mult)) * ((mult > 0) - (mult < 0)); } } } else --usedLines; } output[sample*(short)outputL + line] = output[sample*(short)outputL + line] / (float)(pow(usedLines, 2) - (usedLines - 1)) * ((sign > 0) - (sign < 0)); delete[] AddSample; } } diff --git a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp b/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp index a06f20c76f..d4961e2260 100644 --- a/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp +++ b/Modules/PhotoacousticsAlgorithms/source/mitkPhotoacousticImage.cpp @@ -1,541 +1,539 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ -#define _USE_MATH_DEFINES -#include #include "mitkPhotoacousticImage.h" #include "../ITKFilter/ITKUltrasound/itkBModeImageFilter.h" #include "../ITKFilter/itkPhotoacousticBModeImageFilter.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkPhotoacousticBeamformingFilter.h" #include #include #include "./OpenCLFilter/mitkPhotoacousticBModeFilter.h" // itk dependencies #include "itkImage.h" #include "itkResampleImageFilter.h" #include "itkCastImageFilter.h" #include "itkCropImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include #include "itkMultiplyImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include // needed itk image filters #include "mitkITKImageImport.h" #include "itkFFTShiftImageFilter.h" #include "itkMultiplyImageFilter.h" #include "itkComplexToModulusImageFilter.h" #include #include "../ITKFilter/ITKUltrasound/itkFFT1DComplexConjugateToRealImageFilter.h" #include "../ITKFilter/ITKUltrasound/itkFFT1DRealToComplexConjugateImageFilter.h" mitk::PhotoacousticImage::PhotoacousticImage() : m_BeamformingFilter(BeamformingFilter::New()) { MITK_INFO << "[PhotoacousticImage Debug] created that image"; } mitk::PhotoacousticImage::~PhotoacousticImage() { MITK_INFO << "[PhotoacousticImage Debug] destroyed that image"; } mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBmodeFilter(mitk::Image::Pointer inputImage, BModeMethod method, bool UseGPU, bool UseLogFilter, float resampleSpacing) { // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::IdentityTransform TransformType; if (method == BModeMethod::Abs) { mitk::Image::Pointer input; mitk::Image::Pointer out; if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") input = inputImage; else input = ApplyCropping(inputImage, 0, 0, 0, 0, 0, inputImage->GetDimension(2) - 1); if (!UseGPU) { PhotoacousticBModeFilter::Pointer filter = PhotoacousticBModeFilter::New(); filter->SetParameters(UseLogFilter); filter->SetInput(input); filter->Update(); out = filter->GetOutput(); if (resampleSpacing == 0) return out; } #ifdef PHOTOACOUSTICS_USE_GPU else { PhotoacousticOCLBModeFilter::Pointer filter = PhotoacousticOCLBModeFilter::New(); filter->SetParameters(UseLogFilter); filter->SetInput(input); filter->Update(); out = filter->GetOutput(); if (resampleSpacing == 0) return out; } #endif typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(out, itkImage); itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; outputSpacing[0] = itkImage->GetSpacing()[0]; outputSpacing[1] = resampleSpacing; outputSpacing[2] = itkImage->GetSpacing()[2]; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } else if (method == BModeMethod::EnvelopeDetection) { typedef itk::BModeImageFilter < itkFloatImageType, itkFloatImageType > BModeFilterType; BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); itkFloatImageType::Pointer bmode; if (UseLogFilter) { bModeFilter->SetInput(itkImage); bModeFilter->SetDirection(1); bmode = bModeFilter->GetOutput(); } else { photoacousticBModeFilter->SetInput(itkImage); photoacousticBModeFilter->SetDirection(1); bmode = photoacousticBModeFilter->GetOutput(); } // resampleSpacing == 0 means: do no resampling if (resampleSpacing == 0) { return mitk::GrabItkImageMemory(bmode); } itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; outputSpacing[0] = itkImage->GetSpacing()[0]; outputSpacing[1] = resampleSpacing; outputSpacing[2] = itkImage->GetSpacing()[2]; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; resampleImageFilter->SetInput(bmode); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } return nullptr; } /*mitk::Image::Pointer mitk::PhotoacousticImage::ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scattering) { typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::MultiplyImageFilter MultiplyImageFilterType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); multiplyFilter->SetInput1(itkImage); multiplyFilter->SetInput2(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); }*/ mitk::Image::Pointer mitk::PhotoacousticImage::ApplyResampling(mitk::Image::Pointer inputImage, unsigned int outputSize[2]) { typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); typedef itk::LinearInterpolateImageFunction T_Interpolator; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); itkFloatImageType::SpacingType outputSpacingItk; itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSizeItk = inputSizeItk; outputSizeItk[0] = outputSize[0]; outputSizeItk[1] = outputSize[1]; outputSizeItk[2] = inputSizeItk[2]; outputSpacingItk[0] = itkImage->GetSpacing()[0] * (static_cast(inputSizeItk[0]) / static_cast(outputSizeItk[0])); outputSpacingItk[1] = itkImage->GetSpacing()[1] * (static_cast(inputSizeItk[1]) / static_cast(outputSizeItk[1])); outputSpacingItk[2] = itkImage->GetSpacing()[2]; typedef itk::IdentityTransform TransformType; T_Interpolator::Pointer _pInterpolator = T_Interpolator::New(); resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSizeItk); resampleImageFilter->SetOutputSpacing(outputSpacingItk); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->SetInterpolator(_pInterpolator); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::PhotoacousticImage::ApplyCropping(mitk::Image::Pointer inputImage, int above, int below, int right, int left, int minSlice, int maxSlice) { unsigned int inputDim[3] = { inputImage->GetDimension(0), inputImage->GetDimension(1), inputImage->GetDimension(2) }; unsigned int outputDim[3] = { inputImage->GetDimension(0) - left - right, inputImage->GetDimension(1) - (unsigned int)above - (unsigned int)below, (unsigned int)maxSlice - (unsigned int)minSlice + 1 }; void* inputData; float* outputData = new float[outputDim[0] * outputDim[1] * outputDim[2]]; ImageReadAccessor acc(inputImage); inputData = const_cast(acc.GetData()); // convert the data to float by default // as of now only float, short, double are used at all. if (inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)") { // copy the data into the cropped image for (unsigned short sl = 0; sl < outputDim[2]; ++sl) { for (unsigned short l = 0; l < outputDim[0]; ++l) { for (unsigned short s = 0; s < outputDim[1]; ++s) { outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((float*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; } } } } else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (short)" || inputImage->GetPixelType().GetTypeAsString() == " (short)") { // copy the data to the cropped image for (unsigned short sl = 0; sl < outputDim[2]; ++sl) { for (unsigned short l = 0; l < outputDim[0]; ++l) { for (unsigned short s = 0; s < outputDim[1]; ++s) { outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((short*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; } } } } else if (inputImage->GetPixelType().GetTypeAsString() == "scalar (double)" || inputImage->GetPixelType().GetTypeAsString() == " (double)") { // copy the data to the cropped image for (unsigned short sl = 0; sl < outputDim[2]; ++sl) { for (unsigned short l = 0; l < outputDim[0]; ++l) { for (unsigned short s = 0; s < outputDim[1]; ++s) { outputData[l + s*(unsigned short)outputDim[0] + sl*outputDim[0] * outputDim[1]] = (float)((double*)inputData)[(l + left) + (s + above)*(unsigned short)inputDim[0] + (sl + minSlice)*inputDim[0] * inputDim[1]]; } } } } else { MITK_INFO << "Could not determine pixel type"; } mitk::Image::Pointer output = mitk::Image::New(); output->Initialize(mitk::MakeScalarPixelType(), 3, outputDim); output->SetSpacing(inputImage->GetGeometry()->GetSpacing()); output->SetImportVolume(outputData, 0, 0, mitk::Image::ReferenceMemory); return output; } mitk::Image::Pointer mitk::PhotoacousticImage::ApplyBeamforming(mitk::Image::Pointer inputImage, BeamformingSettings config, std::string& message, std::function progressHandle) { Image::Pointer processedImage = inputImage; if (inputImage->GetDimension() != 3) { processedImage->Initialize(mitk::MakeScalarPixelType(), 3, inputImage->GetDimensions()); processedImage->SetSpacing(inputImage->GetGeometry()->GetSpacing()); mitk::ImageReadAccessor copy(inputImage); processedImage->SetImportVolume(copy.GetData()); } config.RecordTime = config.RecordTime - (float)(config.upperCutoff) / (float)inputImage->GetDimension(1) * config.RecordTime; // adjust the recorded time lost by cropping progressHandle(0, "converting image"); if (!config.partial) { config.CropBounds[0] = 0; config.CropBounds[1] = inputImage->GetDimension(2) - 1; } processedImage = ApplyCropping(inputImage, config.upperCutoff, 0, 0, 0, config.CropBounds[0], config.CropBounds[1]); config.inputDim[0] = processedImage->GetDimension(0); config.inputDim[1] = processedImage->GetDimension(1); config.inputDim[2] = processedImage->GetDimension(2); // perform the beamforming m_BeamformingFilter->SetInput(processedImage); m_BeamformingFilter->Configure(config); m_BeamformingFilter->SetProgressHandle(progressHandle); m_BeamformingFilter->UpdateLargestPossibleRegion(); processedImage = m_BeamformingFilter->GetOutput(); message = m_BeamformingFilter->GetMessageString(); return processedImage; } mitk::Image::Pointer mitk::PhotoacousticImage::BandpassFilter(mitk::Image::Pointer data, float recordTime, float BPHighPass, float BPLowPass, float alpha) { bool powerOfTwo = false; int finalPower = 0; for (int i = 1; pow(2, i) <= data->GetDimension(1); ++i) { finalPower = i; if (pow(2, i) == data->GetDimension(1)) { powerOfTwo = true; } } if (!powerOfTwo) { unsigned int dim[2] = { data->GetDimension(0), (unsigned int)pow(2,finalPower+1)}; data = ApplyResampling(data, dim); } MITK_INFO << data->GetDimension(0); // do a fourier transform, multiply with an appropriate window for the filter, and transform back typedef float PixelType; typedef itk::Image< PixelType, 3 > RealImageType; RealImageType::Pointer image; mitk::CastToItkImage(data, image); typedef itk::FFT1DRealToComplexConjugateImageFilter ForwardFFTFilterType; typedef ForwardFFTFilterType::OutputImageType ComplexImageType; ForwardFFTFilterType::Pointer forwardFFTFilter = ForwardFFTFilterType::New(); forwardFFTFilter->SetInput(image); forwardFFTFilter->SetDirection(1); try { forwardFFTFilter->UpdateOutputInformation(); } catch (itk::ExceptionObject & error) { std::cerr << "Error: " << error << std::endl; MITK_WARN << "Bandpass could not be applied"; return data; } float singleVoxel = 1 / (recordTime / data->GetDimension(1)) / 2 / 1000; float cutoffPixelHighPass = std::min(BPHighPass / singleVoxel, (float)data->GetDimension(1) / 2); float cutoffPixelLowPass = std::min(BPLowPass / singleVoxel, (float)data->GetDimension(1) / 2 - cutoffPixelHighPass); RealImageType::Pointer fftMultiplicator = BPFunction(data, cutoffPixelHighPass, cutoffPixelLowPass, alpha); typedef itk::MultiplyImageFilter< ComplexImageType, RealImageType, ComplexImageType > MultiplyFilterType; MultiplyFilterType::Pointer multiplyFilter = MultiplyFilterType::New(); multiplyFilter->SetInput1(forwardFFTFilter->GetOutput()); multiplyFilter->SetInput2(fftMultiplicator); /*itk::ComplexToModulusImageFilter::Pointer toReal = itk::ComplexToModulusImageFilter::New(); toReal->SetInput(forwardFFTFilter->GetOutput()); return GrabItkImageMemory(toReal->GetOutput()); return GrabItkImageMemory(fftMultiplicator); *///DEBUG typedef itk::FFT1DComplexConjugateToRealImageFilter< ComplexImageType, RealImageType > InverseFilterType; InverseFilterType::Pointer inverseFFTFilter = InverseFilterType::New(); inverseFFTFilter->SetInput(multiplyFilter->GetOutput()); inverseFFTFilter->SetDirection(1); return GrabItkImageMemory(inverseFFTFilter->GetOutput()); } itk::Image::Pointer mitk::PhotoacousticImage::BPFunction(mitk::Image::Pointer reference, int cutoffFrequencyPixelHighPass, int cutoffFrequencyPixelLowPass, float alpha) { float* imageData = new float[reference->GetDimension(0)*reference->GetDimension(1)]; // tukey window float width = reference->GetDimension(1) / 2 - (float)cutoffFrequencyPixelHighPass - (float)cutoffFrequencyPixelLowPass; float center = (float)cutoffFrequencyPixelHighPass / 2 + width / 2; MITK_INFO << width << "width " << center << "center " << alpha; for (unsigned int n = 0; n < reference->GetDimension(1); ++n) { imageData[reference->GetDimension(0)*n] = 0; } if (alpha < 0.00001) { for (int n = 0; n < width; ++n) { if (n <= (alpha*(width - 1)) / 2) { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(M_PI*(2 * n / (alpha*(width - 1)) - 1))) / 2; + imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(itk::Math::pi*(2 * n / (alpha*(width - 1)) - 1))) / 2; } else if (n >= (width - 1)*(1 - alpha / 2)) { - imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(M_PI*(2 * n / (alpha*(width - 1)) + 1 - 2 / alpha))) / 2; + imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = (1 + cos(itk::Math::pi*(2 * n / (alpha*(width - 1)) + 1 - 2 / alpha))) / 2; } else { imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = 1; } } } else { for (int n = 0; n < width; ++n) { imageData[reference->GetDimension(0)*(int)(n + center - (width / 2))] = 1; } } // Butterworth-Filter /* // first, write the HighPass if (cutoffFrequencyPixelHighPass != reference->GetDimension(1) / 2) { for (int n = 0; n < reference->GetDimension(1) / 2; ++n) { imageData[reference->GetDimension(0)*n] = 1 / (1 + pow( (float)n / (float)(reference->GetDimension(1) / 2 - cutoffFrequencyPixelHighPass) , 2 * butterworthOrder)); } } else { for (int n = 0; n < reference->GetDimension(1) / 2; ++n) { imageData[reference->GetDimension(0)*n] = 1; } } // now, the LowPass for (int n = 0; n < reference->GetDimension(1) / 2; ++n) { imageData[reference->GetDimension(0)*n] *= 1 / (1 + pow( (float)(reference->GetDimension(1) / 2 - 1 - n) / (float)(reference->GetDimension(1) / 2 - cutoffFrequencyPixelLowPass) , 2 * butterworthOrder)); } */ // mirror the first half of the image for (unsigned int n = reference->GetDimension(1) / 2; n < reference->GetDimension(1); ++n) { imageData[reference->GetDimension(0)*n] = imageData[(reference->GetDimension(1) - (n + 1)) * reference->GetDimension(0)]; } // copy and paste to all lines for (unsigned int line = 1; line < reference->GetDimension(0); ++line) { for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) { imageData[reference->GetDimension(0)*sample + line] = imageData[reference->GetDimension(0)*sample]; } } typedef itk::Image< float, 3U > ImageType; ImageType::RegionType region; ImageType::IndexType start; start.Fill(0); region.SetIndex(start); ImageType::SizeType size; size[0] = reference->GetDimension(0); size[1] = reference->GetDimension(1); size[2] = reference->GetDimension(2); region.SetSize(size); ImageType::SpacingType SpacingItk; SpacingItk[0] = reference->GetGeometry()->GetSpacing()[0]; SpacingItk[1] = reference->GetGeometry()->GetSpacing()[1]; SpacingItk[2] = reference->GetGeometry()->GetSpacing()[2]; ImageType::Pointer image = ImageType::New(); image->SetRegions(region); image->Allocate(); image->FillBuffer(itk::NumericTraits::Zero); image->SetSpacing(SpacingItk); ImageType::IndexType pixelIndex; for (unsigned int slice = 0; slice < reference->GetDimension(2); ++slice) { for (unsigned int line = 0; line < reference->GetDimension(0); ++line) { for (unsigned int sample = 0; sample < reference->GetDimension(1); ++sample) { pixelIndex[0] = line; pixelIndex[1] = sample; pixelIndex[2] = slice; image->SetPixel(pixelIndex, imageData[line + sample*reference->GetDimension(0)]); } } } delete[] imageData; return image; } diff --git a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp index 106c89f089..236cf7c25e 100644 --- a/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp +++ b/Modules/PhotoacousticsLib/src/Domain/Vessel/mitkPAVessel.cpp @@ -1,169 +1,167 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkPAVessel.h" #include -#define _USE_MATH_DEFINES -#include #include mitk::pa::Vessel::Vessel(VesselProperties::Pointer initialProperties) : - m_RangeDistribution(M_PI / 16, M_PI / 8), + m_RangeDistribution(itk::Math::pi / 16, itk::Math::pi / 8), m_SignDistribution(-1, 1) { m_Finished = false; //Copy this so it may be reused for other vessels. m_VesselProperties = VesselProperties::New(initialProperties); m_RadiusRangeDistribution = std::uniform_real_distribution<>(NEW_RADIUS_MINIMUM_RELATIVE_SIZE, NEW_RADIUS_MAXIMUM_RELATIVE_SIZE); m_VesselMeanderStrategy = VesselMeanderStrategy::New(); m_WalkedDistance = 0; } mitk::pa::Vessel::~Vessel() { m_VesselProperties = nullptr; m_VesselMeanderStrategy = nullptr; } void mitk::pa::Vessel::ExpandVessel(InSilicoTissueVolume::Pointer volume, CalculateNewVesselPositionCallback calculateNewPosition, double bendingFactor, std::mt19937* rng) { Vector::Pointer oldPosition = m_VesselProperties->GetPositionVector()->Clone(); (m_VesselMeanderStrategy->*calculateNewPosition)(m_VesselProperties->GetPositionVector(), m_VesselProperties->GetDirectionVector(), bendingFactor, rng); DrawVesselInVolume(oldPosition, volume); } bool mitk::pa::Vessel::CanBifurcate() { return m_VesselProperties->GetBifurcationFrequency() < m_WalkedDistance; } int mitk::pa::Vessel::GetSign(std::mt19937 *rng) { if (m_SignDistribution(*rng) < 0) return -1; return 1; } mitk::pa::Vessel::Pointer mitk::pa::Vessel::Bifurcate(std::mt19937* rng) { VesselProperties::Pointer vesselParams = VesselProperties::New(m_VesselProperties); double thetaChange = m_RangeDistribution(*rng) * GetSign(rng); double phiChange = m_RangeDistribution(*rng) * GetSign(rng); vesselParams->GetDirectionVector()->Rotate(thetaChange, phiChange); m_VesselProperties->GetDirectionVector()->Rotate(-thetaChange, -phiChange); double newRadius = m_RadiusRangeDistribution(*rng)*m_VesselProperties->GetRadiusInVoxel(); vesselParams->SetRadiusInVoxel(newRadius); m_VesselProperties->SetRadiusInVoxel( sqrt(m_VesselProperties->GetRadiusInVoxel()*m_VesselProperties->GetRadiusInVoxel() - newRadius*newRadius)); m_WalkedDistance = 0; return Vessel::New(vesselParams); } void mitk::pa::Vessel::DrawVesselInVolume(Vector::Pointer fromPosition, InSilicoTissueVolume::Pointer volume) { Vector::Pointer diffVector = Vector::New(); Vector::Pointer toPosition = m_VesselProperties->GetPositionVector(); diffVector->SetElement(0, fromPosition->GetElement(0) - toPosition->GetElement(0)); diffVector->SetElement(1, fromPosition->GetElement(1) - toPosition->GetElement(1)); diffVector->SetElement(2, fromPosition->GetElement(2) - toPosition->GetElement(2)); //1/SCALING_FACTOR steps along the direction vector are taken and drawn into the image. Vector::Pointer stepSize = Vector::New(); stepSize->SetValue(m_VesselProperties->GetDirectionVector()); stepSize->Scale(SCALING_FACTOR); while (diffVector->GetNorm() >= SCALING_FACTOR) { m_WalkedDistance += stepSize->GetNorm(); fromPosition->SetElement(0, fromPosition->GetElement(0) + stepSize->GetElement(0)); fromPosition->SetElement(1, fromPosition->GetElement(1) + stepSize->GetElement(1)); fromPosition->SetElement(2, fromPosition->GetElement(2) + stepSize->GetElement(2)); int xPos = fromPosition->GetElement(0); int yPos = fromPosition->GetElement(1); int zPos = fromPosition->GetElement(2); if (!volume->IsInsideVolume(xPos, yPos, zPos)) { m_VesselProperties->SetRadiusInVoxel(0); break; } double radius = m_VesselProperties->GetRadiusInVoxel(); for (int x = xPos - radius; x <= xPos + radius; x++) for (int y = yPos - radius; y <= yPos + radius; y++) for (int z = zPos - radius; z <= zPos + radius; z++) { if (radius*radius >= (x - xPos)*(x - xPos) + (y - yPos)*(y - yPos) + (z - zPos)*(z - zPos)) { volume->SetVolumeValues(x, y, z, m_VesselProperties->GetAbsorptionCoefficient(), m_VesselProperties->GetScatteringCoefficient(), m_VesselProperties->GetAnisotopyCoefficient(), mitk::pa::InSilicoTissueVolume::SegmentationType::VESSEL); } } diffVector->SetElement(0, fromPosition->GetElement(0) - toPosition->GetElement(0)); diffVector->SetElement(1, fromPosition->GetElement(1) - toPosition->GetElement(1)); diffVector->SetElement(2, fromPosition->GetElement(2) - toPosition->GetElement(2)); } } bool mitk::pa::Vessel::IsFinished() { return m_VesselProperties->GetRadiusInVoxel() < MINIMUM_VESSEL_RADIUS; } bool mitk::pa::Equal(const Vessel::Pointer leftHandSide, const Vessel::Pointer rightHandSide, double eps, bool verbose) { MITK_INFO(verbose) << "=== mitk::pa::Vessel Equal ==="; if (rightHandSide.IsNull() || leftHandSide.IsNull()) { MITK_INFO(verbose) << "Cannot compare nullpointers"; return false; } if (leftHandSide->IsFinished() != rightHandSide->IsFinished()) { MITK_INFO(verbose) << "Not same finished state."; return false; } if (leftHandSide->CanBifurcate() != rightHandSide->CanBifurcate()) { MITK_INFO(verbose) << "Not same bifurcation state."; return false; } if (!Equal(leftHandSide->GetVesselProperties(), rightHandSide->GetVesselProperties(), eps, verbose)) { MITK_INFO(verbose) << "Vesselproperties not equal"; return false; } return true; } diff --git a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVectorTest.cpp b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVectorTest.cpp index 2d87611c2d..7c7e18cff9 100644 --- a/Modules/PhotoacousticsLib/test/mitkPhotoacousticVectorTest.cpp +++ b/Modules/PhotoacousticsLib/test/mitkPhotoacousticVectorTest.cpp @@ -1,260 +1,258 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include -#define _USE_MATH_DEFINES -#include #include "mitkPAVector.h" class mitkPhotoacousticVectorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPhotoacousticVectorTestSuite); MITK_TEST(TestNormalizeVector); MITK_TEST(TestRotateVectorZeroDegrees); MITK_TEST(TestRotatedVectorPositiveDegrees); MITK_TEST(TestRotateVectorZeroDegrees); MITK_TEST(TestScaleVector); MITK_TEST(TestCloneVector); CPPUNIT_TEST_SUITE_END(); private: mitk::pa::Vector::Pointer m_TestVector; mitk::pa::Vector::Pointer m_TestReturnVector; const double DIF_VAL = 0.001; const double TWO_PI = 6.283185; public: void setUp() override { m_TestVector = mitk::pa::Vector::New(); m_TestReturnVector = mitk::pa::Vector::New(); } void TestNormalizeVector() { std::stringstream output; int a = 2; int b = 3; int c = 4; m_TestVector->SetElement(0, a); m_TestVector->SetElement(1, b); m_TestVector->SetElement(2, c); output << "The vectorlength should be"; output << sqrt(a*a + b*b + c*c); CPPUNIT_ASSERT_EQUAL_MESSAGE(output.str(), sqrt(a*a + b*b + c*c), m_TestVector->GetNorm()); output.flush(); m_TestVector->Normalize(); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vectorlength should be 1.", true, m_TestVector->GetNorm() - 1 < DIF_VAL); } void TestRotateVectorZeroDegrees() { int a = 1; int b = 2; int c = 3; double length; m_TestVector->SetElement(0, a); m_TestVector->SetElement(1, b); m_TestVector->SetElement(2, c); length = m_TestVector->GetNorm(); m_TestVector->Rotate(0, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector length should be equal", length, m_TestVector->GetNorm()); CPPUNIT_ASSERT_MESSAGE("The vector value at index0 should be 1.0", m_TestVector->GetElement(0) - 1 < DIF_VAL); CPPUNIT_ASSERT_MESSAGE("The vector value at index1 should be 2.0", m_TestVector->GetElement(1) - 2 < DIF_VAL); CPPUNIT_ASSERT_MESSAGE("The vector value at index2 should be 3.0", m_TestVector->GetElement(2) - 3 < DIF_VAL); } void TestRotatedVectorPositiveDegrees() { MITK_INFO << atan2(0, 0); for (int r = 0; r < 10; r++) { for (double phi = 0.1; phi < 3; phi += 0.1) { for (double theta = 0.1; theta < 3; theta += 0.1) { double rotateTheta = 0.1; double rotatePhi = 0.1; m_TestVector->SetElement(0, r * sin(theta) * cos(phi)); m_TestVector->SetElement(1, r * sin(theta) * sin(phi)); m_TestVector->SetElement(2, r * cos(theta)); m_TestVector->Rotate(rotateTheta, rotatePhi); double newTheta = fmod(theta + rotateTheta, TWO_PI); double newPhi = fmod(phi + rotatePhi, TWO_PI); double expectedX = r * sin(newTheta) * cos(newPhi); double expectedY = r * sin(newTheta) * sin(newPhi); double expectedZ = r * cos(newTheta); CPPUNIT_ASSERT_MESSAGE("The vector value at index0 should be " + std::to_string(expectedX) + " but was " + std::to_string(m_TestVector->GetElement(0)) + " at r=" + std::to_string(r) + " phi=" + std::to_string(phi) + " theta=" + std::to_string(theta), m_TestVector->GetElement(0) - expectedX < DIF_VAL); CPPUNIT_ASSERT_MESSAGE("The vector value at index1 should be " + std::to_string(expectedY) + " but was " + std::to_string(m_TestVector->GetElement(0)) + " at r=" + std::to_string(r) + " phi=" + std::to_string(phi) + " theta=" + std::to_string(theta), m_TestVector->GetElement(1) - expectedY < DIF_VAL); CPPUNIT_ASSERT_MESSAGE("The vector value at index2 should be " + std::to_string(expectedZ) + " but was " + std::to_string(m_TestVector->GetElement(0)) + " at r=" + std::to_string(r) + " phi=" + std::to_string(phi) + " theta=" + std::to_string(theta), m_TestVector->GetElement(2) - expectedZ < DIF_VAL); } } } } void TestRotatedVectorNegativeDegrees() { for (int r = 0; r < 10; r++) { for (double phi = -0.1; phi > -3; phi -= 0.1) { for (double theta = -0.1; theta > -3; theta -= 0.1) { double rotateTheta = -0.1; double rotatePhi = -0.1; m_TestVector->SetElement(0, r * sin(theta) * cos(phi)); m_TestVector->SetElement(1, r * sin(theta) * sin(phi)); m_TestVector->SetElement(2, r * cos(theta)); m_TestVector->Rotate(rotateTheta, rotatePhi); double newTheta = fmod(theta + rotateTheta, TWO_PI); double newPhi = fmod(phi + rotatePhi, TWO_PI); double expectedX = r * sin(newTheta) * cos(newPhi); double expectedY = r * sin(newTheta) * sin(newPhi); double expectedZ = r * cos(newTheta); CPPUNIT_ASSERT_MESSAGE("The vector value at index0 should be " + std::to_string(expectedX) + " but was " + std::to_string(m_TestVector->GetElement(0)) + " at r=" + std::to_string(r) + " phi=" + std::to_string(phi) + " theta=" + std::to_string(theta), m_TestVector->GetElement(0) - expectedX < DIF_VAL); CPPUNIT_ASSERT_MESSAGE("The vector value at index1 should be " + std::to_string(expectedY) + " but was " + std::to_string(m_TestVector->GetElement(0)) + " at r=" + std::to_string(r) + " phi=" + std::to_string(phi) + " theta=" + std::to_string(theta), m_TestVector->GetElement(1) - expectedY < DIF_VAL); CPPUNIT_ASSERT_MESSAGE("The vector value at index2 should be " + std::to_string(expectedZ) + " but was " + std::to_string(m_TestVector->GetElement(0)) + " at r=" + std::to_string(r) + " phi=" + std::to_string(phi) + " theta=" + std::to_string(theta), m_TestVector->GetElement(2) - expectedZ < DIF_VAL); } } } } void TestScaleVector() { double a = 1.0; double b = 2.0; double c = 3.0; for (double testFactor = -2.0; testFactor <= 2.0; testFactor += 0.3) { double potElement0Fctr; double potElement1Fctr; double potElement2Fctr; std::stringstream output; m_TestVector->SetElement(0, a); m_TestVector->SetElement(1, b); m_TestVector->SetElement(2, c); potElement0Fctr = (m_TestVector->GetElement(0)*testFactor)*(m_TestVector->GetElement(0)*testFactor); potElement1Fctr = (m_TestVector->GetElement(1)*testFactor)*(m_TestVector->GetElement(1)*testFactor); potElement2Fctr = (m_TestVector->GetElement(2)*testFactor)*(m_TestVector->GetElement(2)*testFactor); m_TestVector->Scale(testFactor); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector length should not be equal", sqrt(potElement0Fctr + potElement1Fctr + potElement2Fctr), m_TestVector->GetNorm()); output << "The vector value at index0 should be"; output << a*testFactor; CPPUNIT_ASSERT_EQUAL_MESSAGE(output.str(), a*testFactor, m_TestVector->GetElement(0)); output.flush(); output << "The vector value at index1 should be"; output << b*testFactor; CPPUNIT_ASSERT_EQUAL_MESSAGE(output.str(), b*testFactor, m_TestVector->GetElement(1)); output.flush(); output << "The vector value at index2 should be"; output << c*testFactor; CPPUNIT_ASSERT_EQUAL_MESSAGE(output.str(), c*testFactor, m_TestVector->GetElement(2)); output.flush(); } } void TestCloneVector() { int a = 1; int b = 2; int c = 3; m_TestVector->SetElement(0, a); m_TestVector->SetElement(1, b); m_TestVector->SetElement(2, c); m_TestReturnVector = m_TestVector->Clone(); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector length should be equal", (m_TestVector->GetNorm()), m_TestReturnVector->GetNorm()); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be equal", m_TestVector->GetElement(0), m_TestReturnVector->GetElement(0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index1 should be equal", m_TestVector->GetElement(1), m_TestReturnVector->GetElement(1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index2 should be equal", m_TestVector->GetElement(2), m_TestReturnVector->GetElement(2)); - m_TestReturnVector->Rotate(M_PI / 4, M_PI / 4); + m_TestReturnVector->Rotate(itk::Math::pi / 4, itk::Math::pi / 4); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be not equal", true, m_TestVector->GetElement(0) != m_TestReturnVector->GetElement(0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be not equal", true, m_TestVector->GetElement(1) != m_TestReturnVector->GetElement(1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be not equal", true, m_TestVector->GetElement(2) != m_TestReturnVector->GetElement(2)); for (double testFactor = -2.0; testFactor <= 2.0; testFactor += 0.3) { m_TestReturnVector->Scale(testFactor); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be not equal", true, m_TestVector->GetElement(0) != m_TestReturnVector->GetElement(0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be not equal", true, m_TestVector->GetElement(1) != m_TestReturnVector->GetElement(1)); CPPUNIT_ASSERT_EQUAL_MESSAGE("The vector value at index0 should be not equal", true, m_TestVector->GetElement(2) != m_TestReturnVector->GetElement(2)); } } void tearDown() override { m_TestVector = nullptr; m_TestReturnVector = nullptr; } }; MITK_TEST_SUITE_REGISTRATION(mitkPhotoacousticVector) diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp index 60ccdfecd0..58a21ea294 100644 --- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp +++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp @@ -1,311 +1,308 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkPlanarEllipse.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include -#define _USE_MATH_DEFINES -#include - mitk::PlanarEllipse::PlanarEllipse() : FEATURE_ID_MAJOR_AXIS(Superclass::AddFeature("Major Axis", "mm")), FEATURE_ID_MINOR_AXIS(Superclass::AddFeature("Minor Axis", "mm")), FEATURE_ID_AREA(Superclass::AddFeature("Area", "mm2")), m_MinRadius(0), m_MaxRadius(100), m_MinMaxRadiusContraintsActive(false), m_TreatAsCircle(true) { // Ellipse has three control points this->ResetNumberOfControlPoints(4); this->SetNumberOfPolyLines(2); this->SetProperty("closed", mitk::BoolProperty::New(true)); } bool mitk::PlanarEllipse::SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist) { if (index == 0) // moving center point and control points accordingly { const Point2D ¢erPoint = GetControlPoint(0); Point2D boundaryPoint1 = GetControlPoint(1); Point2D boundaryPoint2 = GetControlPoint(2); Point2D boundaryPoint3 = GetControlPoint(3); const vnl_vector vec = (point.GetVnlVector() - centerPoint.GetVnlVector()); boundaryPoint1[0] += vec[0]; boundaryPoint1[1] += vec[1]; boundaryPoint2[0] += vec[0]; boundaryPoint2[1] += vec[1]; boundaryPoint3[0] += vec[0]; boundaryPoint3[1] += vec[1]; PlanarFigure::SetControlPoint(0, point, createIfDoesNotExist); PlanarFigure::SetControlPoint(1, boundaryPoint1, createIfDoesNotExist); PlanarFigure::SetControlPoint(2, boundaryPoint2, createIfDoesNotExist); PlanarFigure::SetControlPoint(3, boundaryPoint3, createIfDoesNotExist); return true; } else if (index < 3) { PlanarFigure::SetControlPoint(index, point, createIfDoesNotExist); int otherIndex = index + 1; if (otherIndex > 2) otherIndex = 1; const Point2D ¢erPoint = GetControlPoint(0); Point2D otherPoint = GetControlPoint(otherIndex); Point2D point3 = GetControlPoint(3); const Vector2D vec1 = point - centerPoint; Vector2D vec2; if (index == 1 && m_TreatAsCircle) { const float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index == 1) vec2[0] *= -1; else vec2[1] *= -1; otherPoint = centerPoint + vec2; PlanarFigure::SetControlPoint(otherIndex, otherPoint, createIfDoesNotExist); const float r = centerPoint.EuclideanDistanceTo(otherPoint); // adjust additional third control point const Point2D p3 = this->GetControlPoint(3); Vector2D vec3; vec3[0] = p3[0] - centerPoint[0]; vec3[1] = p3[1] - centerPoint[1]; if (vec3[0] != 0 || vec3[1] != 0) { vec3.Normalize(); vec3 *= r; } else { vec3[0] = r; vec3[1] = 0; } point3 = centerPoint + vec3; PlanarFigure::SetControlPoint(3, point3, createIfDoesNotExist); } else if (vec1.GetNorm() > 0) { const float r = centerPoint.EuclideanDistanceTo(otherPoint); const float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index == 1) vec2[0] *= -1; else vec2[1] *= -1; vec2.Normalize(); vec2 *= r; if (vec2.GetNorm() > 0) { otherPoint = centerPoint + vec2; PlanarFigure::SetControlPoint(otherIndex, otherPoint, createIfDoesNotExist); } // adjust third control point Vector2D vec3 = point3 - centerPoint; vec3.Normalize(); const double r1 = centerPoint.EuclideanDistanceTo(GetControlPoint(1)); const double r2 = centerPoint.EuclideanDistanceTo(GetControlPoint(2)); const Point2D newPoint = centerPoint + vec3 * std::max(r1, r2); PlanarFigure::SetControlPoint(3, newPoint, createIfDoesNotExist); m_TreatAsCircle = false; } return true; } else if (index == 3) { const Point2D centerPoint = GetControlPoint(0); Vector2D vec3 = point - centerPoint; vec3.Normalize(); const double r1 = centerPoint.EuclideanDistanceTo(GetControlPoint(1)); const double r2 = centerPoint.EuclideanDistanceTo(GetControlPoint(2)); const Point2D newPoint = centerPoint + vec3 * std::max(r1, r2); PlanarFigure::SetControlPoint(index, newPoint, createIfDoesNotExist); m_TreatAsCircle = false; return true; } return false; } void mitk::PlanarEllipse::PlaceFigure(const mitk::Point2D &point) { PlanarFigure::PlaceFigure(point); m_SelectedControlPoint = 1; } mitk::Point2D mitk::PlanarEllipse::ApplyControlPointConstraints(unsigned int index, const Point2D &point) { return point; Point2D indexPoint; this->GetPlaneGeometry()->WorldToIndex(point, indexPoint); BoundingBox::BoundsArrayType bounds = this->GetPlaneGeometry()->GetBounds(); if (indexPoint[0] < bounds[0]) { indexPoint[0] = bounds[0]; } if (indexPoint[0] > bounds[1]) { indexPoint[0] = bounds[1]; } if (indexPoint[1] < bounds[2]) { indexPoint[1] = bounds[2]; } if (indexPoint[1] > bounds[3]) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; this->GetPlaneGeometry()->IndexToWorld(indexPoint, constrainedPoint); if (m_MinMaxRadiusContraintsActive) { if (index != 0) { const Point2D ¢erPoint = this->GetControlPoint(0); const double euclideanDinstanceFromCenterToPoint1 = centerPoint.EuclideanDistanceTo(point); Vector2D vectorProjectedPoint = point - centerPoint; vectorProjectedPoint.Normalize(); if (euclideanDinstanceFromCenterToPoint1 > m_MaxRadius) { vectorProjectedPoint *= m_MaxRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } else if (euclideanDinstanceFromCenterToPoint1 < m_MinRadius) { vectorProjectedPoint *= m_MinRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } } } return constrainedPoint; } void mitk::PlanarEllipse::GeneratePolyLine() { // clear the PolyLine-Contrainer, it will be reconstructed soon enough... this->ClearPolyLines(); const Point2D ¢erPoint = GetControlPoint(0); const Point2D &boundaryPoint1 = GetControlPoint(1); const Point2D &boundaryPoint2 = GetControlPoint(2); Vector2D dir = boundaryPoint1 - centerPoint; dir.Normalize(); vnl_matrix_fixed rot; // differentiate between clockwise and counterclockwise rotation int start = 0; int end = 64; if (dir[1] < 0) { dir[0] = -dir[0]; start = -32; end = 32; } // construct rotation matrix to align ellipse with control point vector rot[0][0] = dir[0]; rot[1][1] = rot[0][0]; rot[1][0] = sin(acos(rot[0][0])); rot[0][1] = -rot[1][0]; const double radius1 = centerPoint.EuclideanDistanceTo(boundaryPoint1); const double radius2 = centerPoint.EuclideanDistanceTo(boundaryPoint2); // Generate poly-line with 64 segments for (int t = start; t < end; ++t) { const double alpha = (double)t * vnl_math::pi / 32.0; // construct the new polyline point ... vnl_vector_fixed vec; vec[0] = radius1 * cos(alpha); vec[1] = radius2 * sin(alpha); vec = rot * vec; Point2D polyLinePoint; polyLinePoint[0] = centerPoint[0] + vec[0]; polyLinePoint[1] = centerPoint[1] + vec[1]; // ... and append it to the PolyLine. // No extending supported here, so we can set the index of the PolyLineElement to '0' this->AppendPointToPolyLine(0, polyLinePoint); } this->AppendPointToPolyLine(1, centerPoint); this->AppendPointToPolyLine(1, this->GetControlPoint(3)); } void mitk::PlanarEllipse::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A circle does not require a helper object } void mitk::PlanarEllipse::EvaluateFeaturesInternal() { const Point2D centerPoint = this->GetControlPoint(0); const auto longAxisLength = centerPoint.EuclideanDistanceTo(this->GetControlPoint(1)); const auto shortAxisLength = centerPoint.EuclideanDistanceTo(this->GetControlPoint(2)); this->SetQuantity(FEATURE_ID_MAJOR_AXIS, 2 * longAxisLength); this->SetQuantity(FEATURE_ID_MINOR_AXIS, 2 * shortAxisLength); - this->SetQuantity(FEATURE_ID_AREA, longAxisLength * shortAxisLength * M_PI); + this->SetQuantity(FEATURE_ID_AREA, longAxisLength * shortAxisLength * itk::Math::pi); } void mitk::PlanarEllipse::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } bool mitk::PlanarEllipse::Equals(const mitk::PlanarFigure &other) const { const auto *otherEllipse = dynamic_cast(&other); if (otherEllipse) { if (this->m_TreatAsCircle != otherEllipse->m_TreatAsCircle) return false; return Superclass::Equals(other); } else { return false; } } diff --git a/Modules/ToFHardware/PMD/mitkThreadedToFRawDataReconstruction.cpp b/Modules/ToFHardware/PMD/mitkThreadedToFRawDataReconstruction.cpp index 833750f2dc..cb9de7fd47 100644 --- a/Modules/ToFHardware/PMD/mitkThreadedToFRawDataReconstruction.cpp +++ b/Modules/ToFHardware/PMD/mitkThreadedToFRawDataReconstruction.cpp @@ -1,336 +1,331 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ // mitk includes #include "mitkThreadedToFRawDataReconstruction.h" #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" // stl includes #include #include #include // vtk includes #include #include #include // itk includes #include #include #ifdef WIN32 #include #else #include #endif #include -#define _USE_MATH_DEFINES -#include - -#define PI M_PI; #define cAir 299704944; #define fMod 20000000; namespace mitk { ThreadedToFRawDataReconstruction::ThreadedToFRawDataReconstruction(): m_Threader(0), m_CISDist(0), m_CISAmpl(0), m_CISInten(0), m_ThreadedCISDist(0), m_ThreadedCISAmpl(0), m_ThreadedCISInten(0), m_Init(0), m_Width(0), m_Height(0), m_SourceDataSize(0), m_ImageSize(0), m_SourceData(0) { m_ThreadData = new ThreadDataStruct; m_ThreadData->m_ModulationFrequency = fMod; m_ThreadData->m_ImageDataMutex = itk::FastMutexLock::New(); m_ThreadData->m_ThreadDataMutex = itk::FastMutexLock::New(); m_StackSize = 1; } ThreadedToFRawDataReconstruction::~ThreadedToFRawDataReconstruction() { if(m_ThreadData != nullptr) delete m_ThreadData; if(m_CISDist != nullptr) delete[] m_CISDist; if(m_CISAmpl != nullptr) delete[] m_CISAmpl; if(m_CISInten != nullptr) delete[] m_CISInten; if(m_ThreadedCISInten != nullptr) delete[] m_ThreadedCISInten; if(m_ThreadedCISAmpl != nullptr) delete[] m_ThreadedCISAmpl; if(m_ThreadedCISDist != nullptr) delete[] m_ThreadedCISDist; } void ThreadedToFRawDataReconstruction::Initialize(int width, int height, int modulationFrequency, int sourceDataSize ) { m_Width = width; m_Height = height; m_SourceDataSize = sourceDataSize; m_ImageSize = width * height; m_ThreadData->m_ModulationFrequency = modulationFrequency * 1e6; if(!m_Init) { m_SourceData = vtkShortArray::New(); m_SourceData->SetNumberOfComponents(m_SourceDataSize); m_SourceData->SetNumberOfTuples(4); m_SourceData->Allocate(1); m_CISDist = new float[m_ImageSize]; m_CISAmpl = new float[m_ImageSize]; m_CISInten = new float[m_ImageSize]; m_ThreadedCISDist = new float[m_ImageSize]; m_ThreadedCISAmpl = new float[m_ImageSize]; m_ThreadedCISInten = new float[m_ImageSize]; m_ThreadData->m_OutputData.push_back( m_ThreadedCISDist ); m_ThreadData->m_OutputData.push_back( m_ThreadedCISAmpl ); m_ThreadData->m_OutputData.push_back( m_ThreadedCISInten ); m_Init = true; } } void ThreadedToFRawDataReconstruction::SetChannelData(vtkShortArray* sourceData) { m_SourceData->DeepCopy(sourceData); } void ThreadedToFRawDataReconstruction::GetDistances(float* dist) { memcpy(dist, m_CISDist, m_ImageSize*sizeof(float) ); } void ThreadedToFRawDataReconstruction::GetAmplitudes(float* ampl) { memcpy(ampl, m_CISAmpl, m_ImageSize*sizeof(float)); } void ThreadedToFRawDataReconstruction::GetIntensities(float* inten) { memcpy(inten, m_CISInten, m_ImageSize*sizeof(float)); } void ThreadedToFRawDataReconstruction::GetAllData(float* dist, float* ampl, float* inten) { memcpy(dist, m_CISDist, m_ImageSize*sizeof(float) ); memcpy(ampl, m_CISAmpl, m_ImageSize*sizeof(float)); memcpy(inten, m_CISInten, m_ImageSize*sizeof(float)); } void ThreadedToFRawDataReconstruction::GenerateData() { if(m_Init) { this->BeforeThreadedGenerateData(); } } void ThreadedToFRawDataReconstruction::BeforeThreadedGenerateData() { int sourceDataSize = m_SourceDataSize; int lineWidth = m_Width; int frameHeight = m_Height; int channelSize = lineWidth*frameHeight << 1; int quadChannelSize = channelSize * 0.25; std::vector quad = std::vector(quadChannelSize); // clean the thread data array m_ThreadData->m_InputData.erase(m_ThreadData->m_InputData.begin(),m_ThreadData->m_InputData.end()); int channelNo = 0; while(channelNo < m_SourceData->GetNumberOfTuples()) { short* sourceData = new short[channelSize]; m_SourceData->GetTupleValue(channelNo, sourceData); quad.insert(quad.begin(), sourceData, sourceData+channelSize); m_ThreadData->m_InputData.push_back(quad); delete[]sourceData; ++channelNo; } if(m_Threader.IsNull()) { m_Threader = this->GetMultiThreader(); } int maxThreadNr = 0; if(m_Threader->GetGlobalDefaultNumberOfThreads()> 5) { maxThreadNr = 5; } else if(m_Threader->GetGlobalMaximumNumberOfThreads()>5) { maxThreadNr = 5; } else { maxThreadNr = m_Threader->GetGlobalMaximumNumberOfThreads(); } if ( m_ThreadData->m_Barrier.IsNull()) { m_ThreadData->m_Barrier = itk::Barrier::New(); m_ThreadData->m_Barrier->Initialize(maxThreadNr); } m_ThreadData->m_DataSize = quadChannelSize; m_ThreadData->m_LineWidth = lineWidth; m_ThreadData->m_FrameHeight = frameHeight * 0.25; std::vector threadIDVector; int threadcounter = 0; while(threadcounter != maxThreadNr-1) { if (m_Threader->GetNumberOfThreads() < m_Threader->GetGlobalMaximumNumberOfThreads()) { int threadID = m_Threader->SpawnThread(this->ThreadedGenerateDataCallbackFunction, m_ThreadData); threadIDVector.push_back(threadID); threadcounter++; } } m_ThreadData->m_Barrier->Wait(); int count = 0; while(count != threadIDVector.size()) { m_Threader->TerminateThread(threadIDVector.at(count)); count++; } m_ThreadData->m_ImageDataMutex->Lock(); memcpy(m_CISDist, m_ThreadData->m_OutputData.at(0), (channelSize * 0.5)*sizeof(float)); memcpy(m_CISAmpl, m_ThreadData->m_OutputData.at(1), (channelSize * 0.5)*sizeof(float)); memcpy(m_CISInten, m_ThreadData->m_OutputData.at(2), (channelSize * 0.5)*sizeof(float)); m_ThreadData->m_ImageDataMutex->Unlock(); } ITK_THREAD_RETURN_TYPE ThreadedToFRawDataReconstruction::ThreadedGenerateDataCallbackFunction(void* data) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)data; if (pInfo == nullptr) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == nullptr) { return ITK_THREAD_RETURN_VALUE; } int quadrant = pInfo->ThreadID; ThreadDataStruct* threadData = (ThreadDataStruct*) pInfo->UserData; // some needed variables int x = 0; double phi = 0; double phi2 = 0; double A1 = 0; double A2 = 0; double A3 = 0; double A4 = 0; double A5 = 0; double A6 = 0; double A7 = 0; double A8 = 0; double A3m1 = 0; double A4m2 = 0; double A7m5 = 0; double A8m6 = 0; double cair = cAir; - double pi = PI; - double twoPi = pi + pi; + double twoPi = itk::Math::pi + itk::Math::pi; long modFreq = fMod; threadData->m_ThreadDataMutex->Lock(); std::vector quad1 = threadData->m_InputData.at(0); std::vector quad2 = threadData->m_InputData.at(1); std::vector quad3 = threadData->m_InputData.at(2); std::vector quad4 = threadData->m_InputData.at(3); int index = quadrant << 1; int index2 = 3-quadrant; modFreq = threadData->m_ModulationFrequency; int linewidth = threadData->m_LineWidth; int frameheight = threadData->m_FrameHeight; threadData->m_ThreadDataMutex->Unlock(); - double intermed1 = cair/(pi*(modFreq << 2)); + double intermed1 = cair/(itk::Math::pi*(modFreq << 2)); double intermed2 = intermed1*500; int doubleLwidth = linewidth << 1; int datasize = doubleLwidth*frameheight << 2; do { index += doubleLwidth; x++; do { index -= 8; A1 = htons(quad1.at(index)); A2 = htons(quad2.at(index)); A3 = htons(quad3.at(index)); A4 = htons(quad4.at(index)); A5 = htons(quad1.at(index+1)); A6 = htons(quad2.at(index+1)); A7 = htons(quad3.at(index+1)); A8 = htons(quad4.at(index+1)); - phi = atan2((A3 - A1),(A2 - A4)) + pi; + phi = atan2((A3 - A1),(A2 - A4)) + itk::Math::pi; phi2 = atan2((A7 - A5),(A6 - A8)); if(phi2<0) phi2 +=twoPi; A3m1 = A3*A3 - 2*A3*A1 + A1*A1; A4m2 = A4*A4 - 2*A4*A2 + A2*A2; A7m5 = A7*A7 - 2*A7*A5 + A5*A5; A8m6 = A8*A8 - 2*A8*A6 + A6*A6; threadData->m_ImageDataMutex->Lock(); threadData->m_OutputData.at(0)[index2] = (phi+phi2)*intermed2; //(((phi*intermed1) + (phi2*intermed1))/2)*1000; threadData->m_OutputData.at(1)[index2] = (sqrt(A3m1 + A4m2)+sqrt(A7m5 + A8m6))*0.5; //(sqrt(A3m1 + A4m2)/2) + (sqrt(A7m5 + A8m6)/2); threadData->m_OutputData.at(2)[index2] = (A1+A2+A3+A4+A5+A6+A7+A8)*0.125; threadData->m_ImageDataMutex->Unlock(); index2 += 4; }while(index2 <= (x*linewidth) - (1+quadrant)); index += doubleLwidth; }while(index < datasize); threadData->m_Barrier->Wait(); return ITK_THREAD_RETURN_VALUE; } void ThreadedToFRawDataReconstruction::Update() { this->GenerateData(); } } // end mitk namespace diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp index ce7dae7c7c..99b2bef836 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp @@ -1,2902 +1,2898 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "QmitkFiberfoxView.h" // MITK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RAPIDXML_NO_EXCEPTIONS #include #include #include #include #include "usModuleRegistry.h" #include #include #include #include #include #include #include #include #include #include "mitkNodePredicateDataType.h" #include #include #include #include #define _USE_MATH_DEFINES #include QmitkFiberfoxWorker::QmitkFiberfoxWorker(QmitkFiberfoxView* view) : m_View(view) { } void QmitkFiberfoxWorker::run() { try{ m_View->m_TractsToDwiFilter->Update(); } catch( ... ) { } m_View->m_Thread.quit(); } const std::string QmitkFiberfoxView::VIEW_ID = "org.mitk.views.fiberfoxview"; QmitkFiberfoxView::QmitkFiberfoxView() : QmitkAbstractView() , m_Controls( 0 ) , m_SelectedImageNode( nullptr ) , m_Worker(this) , m_ThreadIsRunning(false) { m_Worker.moveToThread(&m_Thread); connect(&m_Thread, SIGNAL(started()), this, SLOT(BeforeThread())); connect(&m_Thread, SIGNAL(started()), &m_Worker, SLOT(run())); connect(&m_Thread, SIGNAL(finished()), this, SLOT(AfterThread())); // connect(&m_Thread, SIGNAL(terminated()), this, SLOT(AfterThread())); m_SimulationTimer = new QTimer(this); } void QmitkFiberfoxView::KillThread() { MITK_INFO << "Aborting DWI simulation."; m_TractsToDwiFilter->SetAbortGenerateData(true); m_Controls->m_AbortSimulationButton->setEnabled(false); m_Controls->m_AbortSimulationButton->setText("Aborting simulation ..."); } void QmitkFiberfoxView::BeforeThread() { m_SimulationTime = QTime::currentTime(); m_SimulationTimer->start(100); m_Controls->m_AbortSimulationButton->setVisible(true); m_Controls->m_GenerateImageButton->setVisible(false); m_Controls->m_SimulationStatusText->setVisible(true); m_ThreadIsRunning = true; } void QmitkFiberfoxView::AfterThread() { UpdateSimulationStatus(); m_SimulationTimer->stop(); m_Controls->m_AbortSimulationButton->setVisible(false); m_Controls->m_AbortSimulationButton->setEnabled(true); m_Controls->m_AbortSimulationButton->setText("Abort simulation"); m_Controls->m_GenerateImageButton->setVisible(true); m_ThreadIsRunning = false; QString statusText; FiberfoxParameters parameters; mitk::Image::Pointer mitkImage = mitk::Image::New(); statusText = QString(m_TractsToDwiFilter->GetStatusText().c_str()); if (m_TractsToDwiFilter->GetAbortGenerateData()) { MITK_INFO << "Simulation aborted."; return; } parameters = m_TractsToDwiFilter->GetParameters(); mitkImage = mitk::GrabItkImageMemory( m_TractsToDwiFilter->GetOutput() ); mitkImage->GetPropertyList()->ReplaceProperty( mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New( parameters.m_SignalGen.GetGradientDirections() )); mitkImage->SetProperty( mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New( parameters.m_SignalGen.GetBvalue() )); mitk::DiffusionPropertyHelper propertyHelper( mitkImage ); propertyHelper.InitializeImage(); parameters.m_Misc.m_ResultNode->SetData( mitkImage ); GetDataStorage()->Add(parameters.m_Misc.m_ResultNode, parameters.m_Misc.m_ParentNode); parameters.m_Misc.m_ResultNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New(m_TractsToDwiFilter->GetLevelWindow()) ); if (m_Controls->m_VolumeFractionsBox->isChecked()) { std::vector< itk::TractsToDWIImageFilter< short >::ItkDoubleImgType::Pointer > volumeFractions = m_TractsToDwiFilter->GetVolumeFractions(); for (unsigned int k=0; kInitializeByItk(volumeFractions.at(k).GetPointer()); image->SetVolume(volumeFractions.at(k)->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("CompartmentVolume-"+QString::number(k).toStdString()); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); } if (m_TractsToDwiFilter->GetPhaseImage().IsNotNull()) { mitk::Image::Pointer phaseImage = mitk::Image::New(); itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer itkPhase = m_TractsToDwiFilter->GetPhaseImage(); phaseImage = mitk::GrabItkImageMemory( itkPhase.GetPointer() ); mitk::DataNode::Pointer phaseNode = mitk::DataNode::New(); phaseNode->SetData( phaseImage ); phaseNode->SetName("Phase Image"); GetDataStorage()->Add(phaseNode, parameters.m_Misc.m_ResultNode); } if (m_TractsToDwiFilter->GetKspaceImage().IsNotNull()) { mitk::Image::Pointer image = mitk::Image::New(); itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer itkImage = m_TractsToDwiFilter->GetKspaceImage(); image = mitk::GrabItkImageMemory( itkImage.GetPointer() ); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("k-Space"); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); } { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(m_TractsToDwiFilter->GetCoilPointset()); node->SetName("Coil Positions"); node->SetProperty("pointsize", mitk::FloatProperty::New(parameters.m_SignalGen.m_ImageSpacing[0]/4)); node->SetProperty("color", mitk::ColorProperty::New(0, 1, 0)); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); } } m_TractsToDwiFilter = nullptr; if (parameters.m_Misc.m_AfterSimulationMessage.size()>0) QMessageBox::information( nullptr, "Warning", parameters.m_Misc.m_AfterSimulationMessage.c_str()); mitk::BaseData::Pointer basedata = parameters.m_Misc.m_ResultNode->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } if (!parameters.m_Misc.m_OutputPath.empty()) { try{ QString outputFileName(parameters.m_Misc.m_OutputPath.c_str()); outputFileName += parameters.m_Misc.m_ResultNode->GetName().c_str(); outputFileName.replace(QString("."), QString("_")); SaveParameters(outputFileName+".ffp"); outputFileName += ".dwi"; QString status("Saving output image to "); status += outputFileName; m_Controls->m_SimulationStatusText->append(status); mitk::IOUtil::Save(mitkImage, outputFileName.toStdString()); m_Controls->m_SimulationStatusText->append("File saved successfully."); } catch (itk::ExceptionObject &e) { QString status("Exception during DWI writing: "); status += e.GetDescription(); m_Controls->m_SimulationStatusText->append(status); } catch (...) { m_Controls->m_SimulationStatusText->append("Unknown exception during DWI writing!"); } } parameters.m_SignalGen.m_FrequencyMap = nullptr; } void QmitkFiberfoxView::UpdateSimulationStatus() { QString statusText = QString(m_TractsToDwiFilter->GetStatusText().c_str()); if (QString::compare(m_SimulationStatusText,statusText)!=0) { m_Controls->m_SimulationStatusText->clear(); m_Controls->m_SimulationStatusText->setText(statusText); QScrollBar *vScrollBar = m_Controls->m_SimulationStatusText->verticalScrollBar(); vScrollBar->triggerAction(QScrollBar::SliderToMaximum); } } // Destructor QmitkFiberfoxView::~QmitkFiberfoxView() { delete m_SimulationTimer; } void QmitkFiberfoxView::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::QmitkFiberfoxViewControls; m_Controls->setupUi( parent ); m_Controls->m_StickWidget1->setVisible(true); m_Controls->m_StickWidget2->setVisible(false); m_Controls->m_ZeppelinWidget1->setVisible(false); m_Controls->m_ZeppelinWidget2->setVisible(false); m_Controls->m_TensorWidget1->setVisible(false); m_Controls->m_TensorWidget2->setVisible(false); m_Controls->m_BallWidget1->setVisible(true); m_Controls->m_BallWidget2->setVisible(false); m_Controls->m_BallWidget2->SetT1(4500); m_Controls->m_AstrosticksWidget1->setVisible(false); m_Controls->m_AstrosticksWidget2->setVisible(false); m_Controls->m_AstrosticksWidget2->SetT1(4500); m_Controls->m_DotWidget1->setVisible(false); m_Controls->m_DotWidget2->setVisible(false); m_Controls->m_DotWidget2->SetT1(4500); m_Controls->m_PrototypeWidget1->setVisible(false); m_Controls->m_PrototypeWidget2->setVisible(false); m_Controls->m_PrototypeWidget3->setVisible(false); m_Controls->m_PrototypeWidget4->setVisible(false); m_Controls->m_PrototypeWidget3->SetMinFa(0.0); m_Controls->m_PrototypeWidget3->SetMaxFa(0.15); m_Controls->m_PrototypeWidget4->SetMinFa(0.0); m_Controls->m_PrototypeWidget4->SetMaxFa(0.15); m_Controls->m_PrototypeWidget3->SetMinAdc(0.0); m_Controls->m_PrototypeWidget3->SetMaxAdc(0.001); m_Controls->m_PrototypeWidget4->SetMinAdc(0.003); m_Controls->m_PrototypeWidget4->SetMaxAdc(0.004); m_Controls->m_Comp2FractionFrame->setVisible(false); m_Controls->m_Comp4FractionFrame->setVisible(false); m_Controls->m_DiffusionPropsMessage->setVisible(false); m_Controls->m_GeometryMessage->setVisible(false); m_Controls->m_AdvancedSignalOptionsFrame->setVisible(false); m_Controls->m_AdvancedFiberOptionsFrame->setVisible(false); m_Controls->m_VarianceBox->setVisible(false); m_Controls->m_NoiseFrame->setVisible(false); m_Controls->m_GhostFrame->setVisible(false); m_Controls->m_DistortionsFrame->setVisible(false); m_Controls->m_EddyFrame->setVisible(false); m_Controls->m_SpikeFrame->setVisible(false); m_Controls->m_AliasingFrame->setVisible(false); m_Controls->m_MotionArtifactFrame->setVisible(false); m_ParameterFile = QDir::currentPath()+"/param.ffp"; m_Controls->m_AbortSimulationButton->setVisible(false); m_Controls->m_SimulationStatusText->setVisible(false); m_Controls->m_FrequencyMapBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp1VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp2VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp3VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp4VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_MaskComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_TemplateComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_FiberBundleComboBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isFiberBundle = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateIsDWI::Pointer isDwi = mitk::NodePredicateIsDWI::New( ); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("Odfmage"); mitk::NodePredicateOr::Pointer isDiffusionImage = mitk::NodePredicateOr::New(isDwi, isDti); isDiffusionImage = mitk::NodePredicateOr::New(isDiffusionImage, isOdf); mitk::NodePredicateNot::Pointer noDiffusionImage = mitk::NodePredicateNot::New(isDiffusionImage); mitk::NodePredicateAnd::Pointer isNonDiffMitkImage = mitk::NodePredicateAnd::New(isMitkImage, noDiffusionImage); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isBinaryMitkImage = mitk::NodePredicateAnd::New( isNonDiffMitkImage, isBinaryPredicate ); m_Controls->m_FrequencyMapBox->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp1VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp1VolumeFraction->SetZeroEntryText("--"); m_Controls->m_Comp2VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp2VolumeFraction->SetZeroEntryText("--"); m_Controls->m_Comp3VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp3VolumeFraction->SetZeroEntryText("--"); m_Controls->m_Comp4VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp4VolumeFraction->SetZeroEntryText("--"); m_Controls->m_MaskComboBox->SetPredicate(isBinaryMitkImage); m_Controls->m_MaskComboBox->SetZeroEntryText("--"); m_Controls->m_TemplateComboBox->SetPredicate(isMitkImage); m_Controls->m_TemplateComboBox->SetZeroEntryText("--"); m_Controls->m_FiberBundleComboBox->SetPredicate(isFiberBundle); m_Controls->m_FiberBundleComboBox->SetZeroEntryText("--"); QFont font; font.setFamily("Courier"); font.setStyleHint(QFont::Monospace); font.setFixedPitch(true); font.setPointSize(7); m_Controls->m_SimulationStatusText->setFont(font); connect( m_SimulationTimer, SIGNAL(timeout()), this, SLOT(UpdateSimulationStatus()) ); connect((QObject*) m_Controls->m_AbortSimulationButton, SIGNAL(clicked()), (QObject*) this, SLOT(KillThread())); connect((QObject*) m_Controls->m_GenerateImageButton, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateImage())); connect((QObject*) m_Controls->m_GenerateFibersButton, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateFibers())); connect((QObject*) m_Controls->m_CircleButton, SIGNAL(clicked()), (QObject*) this, SLOT(OnDrawROI())); connect((QObject*) m_Controls->m_FlipButton, SIGNAL(clicked()), (QObject*) this, SLOT(OnFlipButton())); connect((QObject*) m_Controls->m_JoinBundlesButton, SIGNAL(clicked()), (QObject*) this, SLOT(JoinBundles())); connect((QObject*) m_Controls->m_VarianceBox, SIGNAL(valueChanged(double)), (QObject*) this, SLOT(OnVarianceChanged(double))); connect((QObject*) m_Controls->m_DistributionBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnDistributionChanged(int))); connect((QObject*) m_Controls->m_FiberDensityBox, SIGNAL(valueChanged(int)), (QObject*) this, SLOT(OnFiberDensityChanged(int))); connect((QObject*) m_Controls->m_FiberSamplingBox, SIGNAL(valueChanged(double)), (QObject*) this, SLOT(OnFiberSamplingChanged(double))); connect((QObject*) m_Controls->m_TensionBox, SIGNAL(valueChanged(double)), (QObject*) this, SLOT(OnTensionChanged(double))); connect((QObject*) m_Controls->m_ContinuityBox, SIGNAL(valueChanged(double)), (QObject*) this, SLOT(OnContinuityChanged(double))); connect((QObject*) m_Controls->m_BiasBox, SIGNAL(valueChanged(double)), (QObject*) this, SLOT(OnBiasChanged(double))); connect((QObject*) m_Controls->m_AddNoise, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddNoise(int))); connect((QObject*) m_Controls->m_AddGhosts, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddGhosts(int))); connect((QObject*) m_Controls->m_AddDistortions, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddDistortions(int))); connect((QObject*) m_Controls->m_AddEddy, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddEddy(int))); connect((QObject*) m_Controls->m_AddSpikes, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddSpikes(int))); connect((QObject*) m_Controls->m_AddAliasing, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddAliasing(int))); connect((QObject*) m_Controls->m_AddMotion, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddMotion(int))); connect((QObject*) m_Controls->m_ConstantRadiusBox, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnConstantRadius(int))); connect((QObject*) m_Controls->m_CopyBundlesButton, SIGNAL(clicked()), (QObject*) this, SLOT(CopyBundles())); connect((QObject*) m_Controls->m_TransformBundlesButton, SIGNAL(clicked()), (QObject*) this, SLOT(ApplyTransform())); connect((QObject*) m_Controls->m_AlignOnGrid, SIGNAL(clicked()), (QObject*) this, SLOT(AlignOnGrid())); connect((QObject*) m_Controls->m_Compartment1Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp1ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_Compartment2Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp2ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_Compartment3Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp3ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_Compartment4Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp4ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_AdvancedOptionsBox, SIGNAL( stateChanged(int)), (QObject*) this, SLOT(ShowAdvancedOptions(int))); connect((QObject*) m_Controls->m_AdvancedOptionsBox_2, SIGNAL( stateChanged(int)), (QObject*) this, SLOT(ShowAdvancedOptions(int))); connect((QObject*) m_Controls->m_SaveParametersButton, SIGNAL(clicked()), (QObject*) this, SLOT(SaveParameters())); connect((QObject*) m_Controls->m_LoadParametersButton, SIGNAL(clicked()), (QObject*) this, SLOT(LoadParameters())); connect((QObject*) m_Controls->m_OutputPathButton, SIGNAL(clicked()), (QObject*) this, SLOT(SetOutputPath())); connect((QObject*) m_Controls->m_MaskComboBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnMaskSelected(int))); connect((QObject*) m_Controls->m_TemplateComboBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnTemplateSelected(int))); connect((QObject*) m_Controls->m_FiberBundleComboBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnFibSelected(int))); } } void QmitkFiberfoxView::OnMaskSelected(int ) { UpdateGui(); } void QmitkFiberfoxView::OnTemplateSelected(int ) { UpdateGui(); } void QmitkFiberfoxView::OnFibSelected(int ) { UpdateGui(); } FiberfoxParameters QmitkFiberfoxView::UpdateImageParameters(bool all, bool save) { FiberfoxParameters parameters; parameters.m_Misc.m_OutputPath = ""; parameters.m_Misc.m_CheckAdvancedFiberOptionsBox = m_Controls->m_AdvancedOptionsBox->isChecked(); parameters.m_Misc.m_CheckAdvancedSignalOptionsBox = m_Controls->m_AdvancedOptionsBox_2->isChecked(); parameters.m_Misc.m_CheckOutputVolumeFractionsBox = m_Controls->m_VolumeFractionsBox->isChecked(); parameters.m_Misc.m_AfterSimulationMessage = ""; std::string outputPath = m_Controls->m_SavePathEdit->text().toStdString(); if (outputPath.compare("-")!=0) { parameters.m_Misc.m_OutputPath = outputPath; parameters.m_Misc.m_OutputPath += "/"; } switch(m_Controls->m_DistributionBox->currentIndex()) { case 0: parameters.m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_UNIFORM; break; case 1: parameters.m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_GAUSSIAN; break; default: parameters.m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_UNIFORM; } parameters.m_FiberGen.m_Variance = m_Controls->m_VarianceBox->value(); parameters.m_FiberGen.m_Density = m_Controls->m_FiberDensityBox->value(); parameters.m_FiberGen.m_Sampling = m_Controls->m_FiberSamplingBox->value(); parameters.m_FiberGen.m_Tension = m_Controls->m_TensionBox->value(); parameters.m_FiberGen.m_Continuity = m_Controls->m_ContinuityBox->value(); parameters.m_FiberGen.m_Bias = m_Controls->m_BiasBox->value(); parameters.m_FiberGen.m_Rotation[0] = m_Controls->m_XrotBox->value(); parameters.m_FiberGen.m_Rotation[1] = m_Controls->m_YrotBox->value(); parameters.m_FiberGen.m_Rotation[2] = m_Controls->m_ZrotBox->value(); parameters.m_FiberGen.m_Translation[0] = m_Controls->m_XtransBox->value(); parameters.m_FiberGen.m_Translation[1] = m_Controls->m_YtransBox->value(); parameters.m_FiberGen.m_Translation[2] = m_Controls->m_ZtransBox->value(); parameters.m_FiberGen.m_Scale[0] = m_Controls->m_XscaleBox->value(); parameters.m_FiberGen.m_Scale[1] = m_Controls->m_YscaleBox->value(); parameters.m_FiberGen.m_Scale[2] = m_Controls->m_ZscaleBox->value(); if (!all) return parameters; if (m_Controls->m_MaskComboBox->GetSelectedNode().IsNotNull()) { mitk::Image::Pointer mitkMaskImage = dynamic_cast(m_Controls->m_MaskComboBox->GetSelectedNode()->GetData()); mitk::CastToItkImage(mitkMaskImage, parameters.m_SignalGen.m_MaskImage); itk::ImageDuplicator::Pointer duplicator = itk::ImageDuplicator::New(); duplicator->SetInputImage(parameters.m_SignalGen.m_MaskImage); duplicator->Update(); parameters.m_SignalGen.m_MaskImage = duplicator->GetOutput(); } if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( m_Controls->m_TemplateComboBox->GetSelectedNode())) // use parameters of selected DWI { mitk::Image::Pointer dwi = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(dwi, itkVectorImagePointer); parameters.m_SignalGen.m_ImageRegion = itkVectorImagePointer->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkVectorImagePointer->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkVectorImagePointer->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkVectorImagePointer->GetDirection(); parameters.SetBvalue(static_cast(dwi->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); parameters.SetGradienDirections(static_cast( dwi->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()); } else if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull()) // use geometry of selected image { mitk::Image::Pointer img = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); itk::Image< float, 3 >::Pointer itkImg = itk::Image< float, 3 >::New(); CastToItkImage< itk::Image< float, 3 > >(img, itkImg); parameters.m_SignalGen.m_ImageRegion = itkImg->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkImg->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkImg->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkImg->GetDirection(); parameters.SetNumWeightedVolumes(m_Controls->m_NumGradientsBox->value()); parameters.SetBvalue(m_Controls->m_BvalueBox->value()); } else if (parameters.m_SignalGen.m_MaskImage.IsNotNull()) // use geometry of mask image { ItkUcharImgType::Pointer itkImg = parameters.m_SignalGen.m_MaskImage; parameters.m_SignalGen.m_ImageRegion = itkImg->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkImg->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkImg->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkImg->GetDirection(); parameters.SetNumWeightedVolumes(m_Controls->m_NumGradientsBox->value()); parameters.SetBvalue(m_Controls->m_BvalueBox->value()); } else // use GUI parameters { parameters.m_SignalGen.m_ImageRegion.SetSize(0, m_Controls->m_SizeX->value()); parameters.m_SignalGen.m_ImageRegion.SetSize(1, m_Controls->m_SizeY->value()); parameters.m_SignalGen.m_ImageRegion.SetSize(2, m_Controls->m_SizeZ->value()); parameters.m_SignalGen.m_ImageSpacing[0] = m_Controls->m_SpacingX->value(); parameters.m_SignalGen.m_ImageSpacing[1] = m_Controls->m_SpacingY->value(); parameters.m_SignalGen.m_ImageSpacing[2] = m_Controls->m_SpacingZ->value(); parameters.m_SignalGen.m_ImageOrigin[0] = parameters.m_SignalGen.m_ImageSpacing[0]/2; parameters.m_SignalGen.m_ImageOrigin[1] = parameters.m_SignalGen.m_ImageSpacing[1]/2; parameters.m_SignalGen.m_ImageOrigin[2] = parameters.m_SignalGen.m_ImageSpacing[2]/2; parameters.m_SignalGen.m_ImageDirection.SetIdentity(); parameters.SetNumWeightedVolumes(m_Controls->m_NumGradientsBox->value()); parameters.SetBvalue(m_Controls->m_BvalueBox->value()); parameters.GenerateGradientHalfShell(); } // signal relaxation parameters.m_SignalGen.m_DoSimulateRelaxation = false; if (m_Controls->m_RelaxationBox->isChecked() && (m_Controls->m_FiberBundleComboBox->GetSelectedNode().IsNotNull() || save) ) { parameters.m_SignalGen.m_DoSimulateRelaxation = true; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Relaxation", BoolProperty::New(true)); parameters.m_Misc.m_ArtifactModelString += "_RELAX"; } parameters.m_SignalGen.m_SimulateKspaceAcquisition = parameters.m_SignalGen.m_DoSimulateRelaxation; // N/2 ghosts parameters.m_Misc.m_CheckAddGhostsBox = m_Controls->m_AddGhosts->isChecked(); if (m_Controls->m_AddGhosts->isChecked()) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_Misc.m_ArtifactModelString += "_GHOST"; parameters.m_SignalGen.m_KspaceLineOffset = m_Controls->m_kOffsetBox->value(); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Ghost", DoubleProperty::New(parameters.m_SignalGen.m_KspaceLineOffset)); } else parameters.m_SignalGen.m_KspaceLineOffset = 0; // Aliasing parameters.m_Misc.m_CheckAddAliasingBox = m_Controls->m_AddAliasing->isChecked(); if (m_Controls->m_AddAliasing->isChecked()) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_Misc.m_ArtifactModelString += "_ALIASING"; parameters.m_SignalGen.m_CroppingFactor = (100-m_Controls->m_WrapBox->value())/100; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Aliasing", DoubleProperty::New(m_Controls->m_WrapBox->value())); } // Spikes parameters.m_Misc.m_CheckAddSpikesBox = m_Controls->m_AddSpikes->isChecked(); if (m_Controls->m_AddSpikes->isChecked()) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_SignalGen.m_Spikes = m_Controls->m_SpikeNumBox->value(); parameters.m_SignalGen.m_SpikeAmplitude = m_Controls->m_SpikeScaleBox->value(); parameters.m_Misc.m_ArtifactModelString += "_SPIKES"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Spikes.Number", IntProperty::New(parameters.m_SignalGen.m_Spikes)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Spikes.Amplitude", DoubleProperty::New(parameters.m_SignalGen.m_SpikeAmplitude)); } // gibbs ringing parameters.m_SignalGen.m_DoAddGibbsRinging = m_Controls->m_AddGibbsRinging->isChecked(); if (m_Controls->m_AddGibbsRinging->isChecked()) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Ringing", BoolProperty::New(true)); parameters.m_Misc.m_ArtifactModelString += "_RINGING"; } // add distortions parameters.m_Misc.m_CheckAddDistortionsBox = m_Controls->m_AddDistortions->isChecked(); if (m_Controls->m_AddDistortions->isChecked() && m_Controls->m_FrequencyMapBox->GetSelectedNode().IsNotNull()) { mitk::DataNode::Pointer fMapNode = m_Controls->m_FrequencyMapBox->GetSelectedNode(); mitk::Image* img = dynamic_cast(fMapNode->GetData()); ItkFloatImgType::Pointer itkImg = ItkFloatImgType::New(); CastToItkImage< ItkFloatImgType >(img, itkImg); if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNull()) // use geometry of frequency map { parameters.m_SignalGen.m_ImageRegion = itkImg->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkImg->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkImg->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkImg->GetDirection(); } if (parameters.m_SignalGen.m_ImageRegion.GetSize(0)==itkImg->GetLargestPossibleRegion().GetSize(0) && parameters.m_SignalGen.m_ImageRegion.GetSize(1)==itkImg->GetLargestPossibleRegion().GetSize(1) && parameters.m_SignalGen.m_ImageRegion.GetSize(2)==itkImg->GetLargestPossibleRegion().GetSize(2)) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; itk::ImageDuplicator::Pointer duplicator = itk::ImageDuplicator::New(); duplicator->SetInputImage(itkImg); duplicator->Update(); parameters.m_SignalGen.m_FrequencyMap = duplicator->GetOutput(); parameters.m_Misc.m_ArtifactModelString += "_DISTORTED"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Distortions", BoolProperty::New(true)); } } parameters.m_SignalGen.m_EddyStrength = 0; parameters.m_Misc.m_CheckAddEddyCurrentsBox = m_Controls->m_AddEddy->isChecked(); if (m_Controls->m_AddEddy->isChecked()) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_SignalGen.m_EddyStrength = m_Controls->m_EddyGradientStrength->value(); parameters.m_Misc.m_ArtifactModelString += "_EDDY"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Eddy-strength", DoubleProperty::New(parameters.m_SignalGen.m_EddyStrength)); } // Motion parameters.m_SignalGen.m_DoAddMotion = false; parameters.m_SignalGen.m_DoRandomizeMotion = m_Controls->m_RandomMotion->isChecked(); parameters.m_SignalGen.m_Translation[0] = m_Controls->m_MaxTranslationBoxX->value(); parameters.m_SignalGen.m_Translation[1] = m_Controls->m_MaxTranslationBoxY->value(); parameters.m_SignalGen.m_Translation[2] = m_Controls->m_MaxTranslationBoxZ->value(); parameters.m_SignalGen.m_Rotation[0] = m_Controls->m_MaxRotationBoxX->value(); parameters.m_SignalGen.m_Rotation[1] = m_Controls->m_MaxRotationBoxY->value(); parameters.m_SignalGen.m_Rotation[2] = m_Controls->m_MaxRotationBoxZ->value(); parameters.m_SignalGen.m_MotionVolumes.clear(); parameters.m_Misc.m_MotionVolumesBox = m_Controls->m_MotionVolumesBox->text().toStdString(); if ( m_Controls->m_AddMotion->isChecked() && (m_Controls->m_FiberBundleComboBox->GetSelectedNode().IsNotNull() || save) ) { parameters.m_SignalGen.m_DoAddMotion = true; parameters.m_Misc.m_ArtifactModelString += "_MOTION"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Random", BoolProperty::New(parameters.m_SignalGen.m_DoRandomizeMotion)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Translation-x", DoubleProperty::New(parameters.m_SignalGen.m_Translation[0])); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Translation-y", DoubleProperty::New(parameters.m_SignalGen.m_Translation[1])); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Translation-z", DoubleProperty::New(parameters.m_SignalGen.m_Translation[2])); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Rotation-x", DoubleProperty::New(parameters.m_SignalGen.m_Rotation[0])); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Rotation-y", DoubleProperty::New(parameters.m_SignalGen.m_Rotation[1])); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Rotation-z", DoubleProperty::New(parameters.m_SignalGen.m_Rotation[2])); if ( parameters.m_Misc.m_MotionVolumesBox == "random" ) { for ( size_t i=0; i < parameters.m_SignalGen.GetNumVolumes(); ++i ) { parameters.m_SignalGen.m_MotionVolumes.push_back( bool( rand()%2 ) ); } MITK_DEBUG << "QmitkFiberfoxView.cpp: Case m_Misc.m_MotionVolumesBox == \"random\"."; } else if ( ! parameters.m_Misc.m_MotionVolumesBox.empty() ) { std::stringstream stream( parameters.m_Misc.m_MotionVolumesBox ); std::vector numbers; int number = std::numeric_limits::max(); while( stream >> number ) { if( number < std::numeric_limits::max() ) { numbers.push_back( number ); } } // If a list of negative numbers is given: if( *(std::min_element( numbers.begin(), numbers.end() )) < 0 && *(std::max_element( numbers.begin(), numbers.end() )) <= 0 ) // cave: -0 == +0 { for ( size_t i=0; i < parameters.m_SignalGen.GetNumVolumes(); ++i ) { parameters.m_SignalGen.m_MotionVolumes.push_back( true ); } // set all true except those given. for( auto iter = std::begin( numbers ); iter != std::end( numbers ); ++iter ) { if ( -(*iter) < (int)parameters.m_SignalGen.GetNumVolumes() && -(*iter) >= 0 ) { parameters.m_SignalGen.m_MotionVolumes.at( -(*iter) ) = false; } } MITK_DEBUG << "QmitkFiberfoxView.cpp: Case list of negative numbers."; } // If a list of positive numbers is given: else if( *(std::min_element( numbers.begin(), numbers.end() )) >= 0 && *(std::max_element( numbers.begin(), numbers.end() )) >= 0 ) { for ( size_t i=0; i < parameters.m_SignalGen.GetNumVolumes(); ++i ) { parameters.m_SignalGen.m_MotionVolumes.push_back( false ); } // set all false except those given. for( auto iter = std::begin( numbers ); iter != std::end( numbers ); ++iter ) { if ( *iter < (int)parameters.m_SignalGen.GetNumVolumes() && *iter >= 0 ) { parameters.m_SignalGen.m_MotionVolumes.at( *iter ) = true; } } MITK_DEBUG << "QmitkFiberfoxView.cpp: Case list of positive numbers."; } else { MITK_ERROR << "QmitkFiberfoxView.cpp: Inconsistent list of numbers in m_MotionVolumesBox."; } } else { MITK_WARN << "QmitkFiberfoxView.cpp: Unrecognised parameters.m_Misc.m_MotionVolumesBox: " << parameters.m_Misc.m_MotionVolumesBox; parameters.m_Misc.m_MotionVolumesBox = "random"; // set default. for (unsigned int i=0; im_AcquisitionTypeBox->currentIndex(); parameters.m_SignalGen.m_CoilSensitivityProfile = (SignalGenerationParameters::CoilSensitivityProfile)m_Controls->m_CoilSensBox->currentIndex(); parameters.m_SignalGen.m_NumberOfCoils = m_Controls->m_NumCoilsBox->value(); parameters.m_SignalGen.m_PartialFourier = m_Controls->m_PartialFourier->value(); parameters.m_SignalGen.m_ReversePhase = m_Controls->m_ReversePhaseBox->isChecked(); parameters.m_SignalGen.m_tLine = m_Controls->m_LineReadoutTimeBox->value(); parameters.m_SignalGen.m_tInhom = m_Controls->m_T2starBox->value(); parameters.m_SignalGen.m_tEcho = m_Controls->m_TEbox->value(); parameters.m_SignalGen.m_tRep = m_Controls->m_TRbox->value(); parameters.m_SignalGen.m_DoDisablePartialVolume = m_Controls->m_EnforcePureFiberVoxelsBox->isChecked(); parameters.m_SignalGen.m_AxonRadius = m_Controls->m_FiberRadius->value(); parameters.m_SignalGen.m_SignalScale = m_Controls->m_SignalScaleBox->value(); double voxelVolume = parameters.m_SignalGen.m_ImageSpacing[0] * parameters.m_SignalGen.m_ImageSpacing[1] * parameters.m_SignalGen.m_ImageSpacing[2]; if ( parameters.m_SignalGen.m_SignalScale*voxelVolume > itk::NumericTraits::max()*0.75 ) { parameters.m_SignalGen.m_SignalScale = itk::NumericTraits::max()*0.75/voxelVolume; m_Controls->m_SignalScaleBox->setValue(parameters.m_SignalGen.m_SignalScale); QMessageBox::information( nullptr, "Warning", "Maximum signal exceeding data type limits. Automatically adjusted to " + QString::number(parameters.m_SignalGen.m_SignalScale) + " to obtain a maximum signal of 75% of the data type maximum." " Relaxation and other effects that affect the signal intensities are not accounted for."); } // Noise parameters.m_Misc.m_CheckAddNoiseBox = m_Controls->m_AddNoise->isChecked(); parameters.m_SignalGen.m_NoiseVariance = 0; if (m_Controls->m_AddNoise->isChecked()) { double noiseVariance = m_Controls->m_NoiseLevel->value(); switch (m_Controls->m_NoiseDistributionBox->currentIndex()) { case 0: { if (noiseVariance>0) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_Misc.m_ArtifactModelString += "_COMPLEX-GAUSSIAN-"; parameters.m_SignalGen.m_NoiseVariance = m_Controls->m_NoiseLevel->value(); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Complex Gaussian")); } break; } case 1: { if (noiseVariance>0) { parameters.m_NoiseModel = std::make_shared< mitk::RicianNoiseModel >(); parameters.m_Misc.m_ArtifactModelString += "_RICIAN-"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Rician")); parameters.m_NoiseModel->SetNoiseVariance(noiseVariance); } break; } case 2: { if (noiseVariance>0) { parameters.m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel >(); parameters.m_Misc.m_ArtifactModelString += "_CHISQUARED-"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Chi-squared")); parameters.m_NoiseModel->SetNoiseVariance(noiseVariance); } break; } default: { if (noiseVariance>0) { parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; parameters.m_Misc.m_ArtifactModelString += "_COMPLEX-GAUSSIAN-"; parameters.m_SignalGen.m_NoiseVariance = m_Controls->m_NoiseLevel->value(); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Complex Gaussian")); } break; } } if (noiseVariance>0) { parameters.m_Misc.m_ArtifactModelString += QString::number(noiseVariance).toStdString(); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Variance", DoubleProperty::New(noiseVariance)); } } // signal models { // compartment 1 switch (m_Controls->m_Compartment1Box->currentIndex()) { case 0: { mitk::StickModel* model = new mitk::StickModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_StickWidget1->GetD()); model->SetT2(m_Controls->m_StickWidget1->GetT2()); model->SetT1(m_Controls->m_StickWidget1->GetT1()); model->m_CompartmentId = 1; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Stick"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Stick") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D", DoubleProperty::New(m_Controls->m_StickWidget1->GetD()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.T2", DoubleProperty::New(model->GetT2()) ); break; } case 1: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_ZeppelinWidget1->GetD1()); model->SetDiffusivity2(m_Controls->m_ZeppelinWidget1->GetD2()); model->SetDiffusivity3(m_Controls->m_ZeppelinWidget1->GetD2()); model->SetT2(m_Controls->m_ZeppelinWidget1->GetT2()); model->SetT1(m_Controls->m_ZeppelinWidget1->GetT1()); model->m_CompartmentId = 1; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Zeppelin"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Zeppelin") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D1", DoubleProperty::New(m_Controls->m_ZeppelinWidget1->GetD1()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D2", DoubleProperty::New(m_Controls->m_ZeppelinWidget1->GetD2()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.T2", DoubleProperty::New(model->GetT2()) ); break; } case 2: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_TensorWidget1->GetD1()); model->SetDiffusivity2(m_Controls->m_TensorWidget1->GetD2()); model->SetDiffusivity3(m_Controls->m_TensorWidget1->GetD3()); model->SetT2(m_Controls->m_TensorWidget1->GetT2()); model->SetT1(m_Controls->m_TensorWidget1->GetT1()); model->m_CompartmentId = 1; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Tensor"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Tensor") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D1", DoubleProperty::New(m_Controls->m_TensorWidget1->GetD1()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D2", DoubleProperty::New(m_Controls->m_TensorWidget1->GetD2()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D3", DoubleProperty::New(m_Controls->m_TensorWidget1->GetD3()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.T2", DoubleProperty::New(model->GetT2()) ); break; } case 3: { mitk::RawShModel* model = new mitk::RawShModel(); parameters.m_SignalGen.m_DoSimulateRelaxation = false; model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetMaxNumKernels(m_Controls->m_PrototypeWidget1->GetNumberOfSamples()); model->SetFaRange(m_Controls->m_PrototypeWidget1->GetMinFa(), m_Controls->m_PrototypeWidget1->GetMaxFa()); model->SetAdcRange(m_Controls->m_PrototypeWidget1->GetMinAdc(), m_Controls->m_PrototypeWidget1->GetMaxAdc()); model->m_CompartmentId = 1; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Prototype"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Prototype") ); break; } } if (m_Controls->m_Comp1VolumeFraction->GetSelectedNode().IsNotNull()) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp1VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer comp1VolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, comp1VolumeImage); parameters.m_FiberModelList.back()->SetVolumeFractionImage(comp1VolumeImage); } // compartment 2 switch (m_Controls->m_Compartment2Box->currentIndex()) { case 0: break; case 1: { mitk::StickModel* model = new mitk::StickModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_StickWidget2->GetD()); model->SetT2(m_Controls->m_StickWidget2->GetT2()); model->SetT1(m_Controls->m_StickWidget2->GetT1()); model->m_CompartmentId = 2; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Stick"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Description", StringProperty::New("Inter-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Model", StringProperty::New("Stick") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D", DoubleProperty::New(m_Controls->m_StickWidget2->GetD()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.T2", DoubleProperty::New(model->GetT2()) ); break; } case 2: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_ZeppelinWidget2->GetD1()); model->SetDiffusivity2(m_Controls->m_ZeppelinWidget2->GetD2()); model->SetDiffusivity3(m_Controls->m_ZeppelinWidget2->GetD2()); model->SetT2(m_Controls->m_ZeppelinWidget2->GetT2()); model->SetT1(m_Controls->m_ZeppelinWidget2->GetT1()); model->m_CompartmentId = 2; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Zeppelin"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Description", StringProperty::New("Inter-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Model", StringProperty::New("Zeppelin") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D1", DoubleProperty::New(m_Controls->m_ZeppelinWidget2->GetD1()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D2", DoubleProperty::New(m_Controls->m_ZeppelinWidget2->GetD2()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.T2", DoubleProperty::New(model->GetT2()) ); break; } case 3: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_TensorWidget2->GetD1()); model->SetDiffusivity2(m_Controls->m_TensorWidget2->GetD2()); model->SetDiffusivity3(m_Controls->m_TensorWidget2->GetD3()); model->SetT2(m_Controls->m_TensorWidget2->GetT2()); model->SetT1(m_Controls->m_TensorWidget2->GetT1()); model->m_CompartmentId = 2; parameters.m_FiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Tensor"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Description", StringProperty::New("Inter-axonal compartment") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Model", StringProperty::New("Tensor") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D1", DoubleProperty::New(m_Controls->m_TensorWidget2->GetD1()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D2", DoubleProperty::New(m_Controls->m_TensorWidget2->GetD2()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D3", DoubleProperty::New(m_Controls->m_TensorWidget2->GetD3()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.T2", DoubleProperty::New(model->GetT2()) ); break; } } if (m_Controls->m_Comp2VolumeFraction->GetSelectedNode().IsNotNull() && parameters.m_FiberModelList.size()==2) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp2VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer comp1VolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, comp1VolumeImage); parameters.m_FiberModelList.back()->SetVolumeFractionImage(comp1VolumeImage); } // compartment 3 switch (m_Controls->m_Compartment3Box->currentIndex()) { case 0: { mitk::BallModel* model = new mitk::BallModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_BallWidget1->GetD()); model->SetT2(m_Controls->m_BallWidget1->GetT2()); model->SetT1(m_Controls->m_BallWidget1->GetT1()); model->m_CompartmentId = 3; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Ball"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Ball") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.D", DoubleProperty::New(m_Controls->m_BallWidget1->GetD()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.T2", DoubleProperty::New(model->GetT2()) ); break; } case 1: { mitk::AstroStickModel* model = new mitk::AstroStickModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_AstrosticksWidget1->GetD()); model->SetT2(m_Controls->m_AstrosticksWidget1->GetT2()); model->SetT1(m_Controls->m_AstrosticksWidget1->GetT1()); model->SetRandomizeSticks(m_Controls->m_AstrosticksWidget1->GetRandomizeSticks()); model->m_CompartmentId = 3; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Astrosticks"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Astrosticks") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.D", DoubleProperty::New(m_Controls->m_AstrosticksWidget1->GetD()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.T2", DoubleProperty::New(model->GetT2()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.RandomSticks", BoolProperty::New(m_Controls->m_AstrosticksWidget1->GetRandomizeSticks()) ); break; } case 2: { mitk::DotModel* model = new mitk::DotModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetT2(m_Controls->m_DotWidget1->GetT2()); model->SetT1(m_Controls->m_DotWidget1->GetT1()); model->m_CompartmentId = 3; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Dot"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Dot") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.T2", DoubleProperty::New(model->GetT2()) ); break; } case 3: { mitk::RawShModel* model = new mitk::RawShModel(); parameters.m_SignalGen.m_DoSimulateRelaxation = false; model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetMaxNumKernels(m_Controls->m_PrototypeWidget3->GetNumberOfSamples()); model->SetFaRange(m_Controls->m_PrototypeWidget3->GetMinFa(), m_Controls->m_PrototypeWidget3->GetMaxFa()); model->SetAdcRange(m_Controls->m_PrototypeWidget3->GetMinAdc(), m_Controls->m_PrototypeWidget3->GetMaxAdc()); model->m_CompartmentId = 3; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Prototype"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Prototype") ); break; } } if (m_Controls->m_Comp3VolumeFraction->GetSelectedNode().IsNotNull()) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp3VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer comp1VolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, comp1VolumeImage); parameters.m_NonFiberModelList.back()->SetVolumeFractionImage(comp1VolumeImage); } switch (m_Controls->m_Compartment4Box->currentIndex()) { case 0: break; case 1: { mitk::BallModel* model = new mitk::BallModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_BallWidget2->GetD()); model->SetT2(m_Controls->m_BallWidget2->GetT2()); model->SetT1(m_Controls->m_BallWidget2->GetT1()); model->m_CompartmentId = 4; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Ball"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Ball") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.D", DoubleProperty::New(m_Controls->m_BallWidget2->GetD()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.T2", DoubleProperty::New(model->GetT2()) ); break; } case 2: { mitk::AstroStickModel* model = new mitk::AstroStickModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_AstrosticksWidget2->GetD()); model->SetT2(m_Controls->m_AstrosticksWidget2->GetT2()); model->SetT1(m_Controls->m_AstrosticksWidget2->GetT1()); model->SetRandomizeSticks(m_Controls->m_AstrosticksWidget2->GetRandomizeSticks()); model->m_CompartmentId = 4; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Astrosticks"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Astrosticks") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.D", DoubleProperty::New(m_Controls->m_AstrosticksWidget2->GetD()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.T2", DoubleProperty::New(model->GetT2()) ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.RandomSticks", BoolProperty::New(m_Controls->m_AstrosticksWidget2->GetRandomizeSticks()) ); break; } case 3: { mitk::DotModel* model = new mitk::DotModel(); model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetT2(m_Controls->m_DotWidget2->GetT2()); model->SetT1(m_Controls->m_DotWidget2->GetT1()); model->m_CompartmentId = 4; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Dot"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Dot") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.T2", DoubleProperty::New(model->GetT2()) ); break; } case 4: { mitk::RawShModel* model = new mitk::RawShModel(); parameters.m_SignalGen.m_DoSimulateRelaxation = false; model->SetGradientList(parameters.m_SignalGen.GetGradientDirections()); model->SetMaxNumKernels(m_Controls->m_PrototypeWidget4->GetNumberOfSamples()); model->SetFaRange(m_Controls->m_PrototypeWidget4->GetMinFa(), m_Controls->m_PrototypeWidget4->GetMaxFa()); model->SetAdcRange(m_Controls->m_PrototypeWidget4->GetMinAdc(), m_Controls->m_PrototypeWidget4->GetMaxAdc()); model->m_CompartmentId = 4; parameters.m_NonFiberModelList.push_back(model); parameters.m_Misc.m_SignalModelString += "Prototype"; parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Prototype") ); break; } } if (m_Controls->m_Comp4VolumeFraction->GetSelectedNode().IsNotNull() && parameters.m_NonFiberModelList.size()==2) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp4VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer compVolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, compVolumeImage); parameters.m_NonFiberModelList.back()->SetVolumeFractionImage(compVolumeImage); } } parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.SignalScale", IntProperty::New(parameters.m_SignalGen.m_SignalScale)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.FiberRadius", IntProperty::New(parameters.m_SignalGen.m_AxonRadius)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Tinhom", DoubleProperty::New(parameters.m_SignalGen.m_tInhom)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Tline", DoubleProperty::New(parameters.m_SignalGen.m_tLine)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.TE", DoubleProperty::New(parameters.m_SignalGen.m_tEcho)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.b-value", DoubleProperty::New(parameters.m_SignalGen.GetBvalue())); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.NoPartialVolume", BoolProperty::New(parameters.m_SignalGen.m_DoDisablePartialVolume)); parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Relaxation", BoolProperty::New(parameters.m_SignalGen.m_DoSimulateRelaxation)); parameters.m_Misc.m_ResultNode->AddProperty("binary", BoolProperty::New(false)); parameters.m_Misc.m_CheckRealTimeFibersBox = m_Controls->m_RealTimeFibers->isChecked(); parameters.m_Misc.m_CheckAdvancedFiberOptionsBox = m_Controls->m_AdvancedOptionsBox->isChecked(); parameters.m_Misc.m_CheckIncludeFiducialsBox = m_Controls->m_IncludeFiducials->isChecked(); parameters.m_Misc.m_CheckConstantRadiusBox = m_Controls->m_ConstantRadiusBox->isChecked(); return parameters; } void QmitkFiberfoxView::SaveParameters(QString filename) { FiberfoxParameters ffParamaters = UpdateImageParameters(true, true); std::vector< int > bVals = ffParamaters.m_SignalGen.GetBvalues(); std::cout << "b-values: "; for (auto v : bVals) std::cout << v << " "; std::cout << std::endl; bool ok = true; bool first = true; bool dosampling = false; mitk::Image::Pointer diffImg = nullptr; itk::Image< itk::DiffusionTensor3D< double >, 3 >::Pointer tensorImage = nullptr; const int shOrder = 2; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter QballFilterType; QballFilterType::CoefficientImageType::Pointer itkFeatureImage = nullptr; ItkDoubleImgType::Pointer adcImage = nullptr; for (unsigned int i=0; i* model = nullptr; if (i* >(ffParamaters.m_FiberModelList.at(i)); } else { model = dynamic_cast< mitk::RawShModel<>* >(ffParamaters.m_NonFiberModelList.at(i-ffParamaters.m_FiberModelList.size())); } if ( model!=nullptr && model->GetNumberOfKernels() <= 0 ) { if (first==true) { if ( QMessageBox::question(nullptr, "Prototype signal sampling", "Do you want to sample prototype signals from the selected diffusion-weighted imag and save them?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes ) dosampling = true; first = false; if ( dosampling && (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNull() || !mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()) ) ) ) { QMessageBox::information(nullptr, "Parameter file not saved", "No diffusion-weighted image selected to sample signal from."); return; } else if (dosampling) { diffImg = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); filter->SetBValue( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str() ).GetPointer() ) ->GetValue() ); filter->SetGradientImage( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetBValue( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); qballfilter->SetGradientImage( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(), itkVectorImagePointer ); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput( itkVectorImagePointer ); adcFilter->SetGradientDirections( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); adcFilter->SetB_value( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); adcFilter->Update(); adcImage = adcFilter->GetOutput(); } } typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); filter->SetBValue( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); filter->SetGradientImage( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetBValue( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); qballfilter->SetGradientImage( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(), itkVectorImagePointer ); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput( itkVectorImagePointer ); adcFilter->SetGradientDirections( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); adcFilter->SetB_value( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); adcFilter->Update(); adcImage = adcFilter->GetOutput(); if (dosampling && diffImg.IsNotNull()) { ok = model->SampleKernels(diffImg, ffParamaters.m_SignalGen.m_MaskImage, tensorImage, itkFeatureImage, adcImage); if (!ok) { QMessageBox::information( nullptr, "Parameter file not saved", "No valid prototype signals could be sampled."); return; } } } } ffParamaters.SaveParameters(filename.toStdString()); m_ParameterFile = filename; } void QmitkFiberfoxView::SaveParameters() { QString filename = QFileDialog::getSaveFileName( 0, tr("Save Parameters"), m_ParameterFile, tr("Fiberfox Parameters (*.ffp)") ); SaveParameters(filename); } void QmitkFiberfoxView::LoadParameters() { QString filename = QFileDialog::getOpenFileName(0, tr("Load Parameters"), QString(itksys::SystemTools::GetFilenamePath(m_ParameterFile.toStdString()).c_str()), tr("Fiberfox Parameters (*.ffp)") ); if(filename.isEmpty() || filename.isNull()) return; m_ParameterFile = filename; FiberfoxParameters parameters = UpdateImageParameters(); parameters.LoadParameters(filename.toStdString()); if (parameters.m_MissingTags.size()>0) { QString missing("Parameter file might be corrupted. The following parameters could not be read: "); missing += QString(parameters.m_MissingTags.c_str()); missing += "\nDefault values have been assigned to the missing parameters."; QMessageBox::information( nullptr, "Warning!", missing); } m_Controls->m_RealTimeFibers->setChecked(parameters.m_Misc.m_CheckRealTimeFibersBox); m_Controls->m_AdvancedOptionsBox->setChecked(parameters.m_Misc.m_CheckAdvancedFiberOptionsBox); m_Controls->m_IncludeFiducials->setChecked(parameters.m_Misc.m_CheckIncludeFiducialsBox); m_Controls->m_ConstantRadiusBox->setChecked(parameters.m_Misc.m_CheckConstantRadiusBox); m_Controls->m_DistributionBox->setCurrentIndex(parameters.m_FiberGen.m_Distribution); m_Controls->m_VarianceBox->setValue(parameters.m_FiberGen.m_Variance); m_Controls->m_FiberDensityBox->setValue(parameters.m_FiberGen.m_Density); m_Controls->m_FiberSamplingBox->setValue(parameters.m_FiberGen.m_Sampling); m_Controls->m_TensionBox->setValue(parameters.m_FiberGen.m_Tension); m_Controls->m_ContinuityBox->setValue(parameters.m_FiberGen.m_Continuity); m_Controls->m_BiasBox->setValue(parameters.m_FiberGen.m_Bias); m_Controls->m_XrotBox->setValue(parameters.m_FiberGen.m_Rotation[0]); m_Controls->m_YrotBox->setValue(parameters.m_FiberGen.m_Rotation[1]); m_Controls->m_ZrotBox->setValue(parameters.m_FiberGen.m_Rotation[2]); m_Controls->m_XtransBox->setValue(parameters.m_FiberGen.m_Translation[0]); m_Controls->m_YtransBox->setValue(parameters.m_FiberGen.m_Translation[1]); m_Controls->m_ZtransBox->setValue(parameters.m_FiberGen.m_Translation[2]); m_Controls->m_XscaleBox->setValue(parameters.m_FiberGen.m_Scale[0]); m_Controls->m_YscaleBox->setValue(parameters.m_FiberGen.m_Scale[1]); m_Controls->m_ZscaleBox->setValue(parameters.m_FiberGen.m_Scale[2]); // image generation parameters m_Controls->m_SizeX->setValue(parameters.m_SignalGen.m_ImageRegion.GetSize(0)); m_Controls->m_SizeY->setValue(parameters.m_SignalGen.m_ImageRegion.GetSize(1)); m_Controls->m_SizeZ->setValue(parameters.m_SignalGen.m_ImageRegion.GetSize(2)); m_Controls->m_SpacingX->setValue(parameters.m_SignalGen.m_ImageSpacing[0]); m_Controls->m_SpacingY->setValue(parameters.m_SignalGen.m_ImageSpacing[1]); m_Controls->m_SpacingZ->setValue(parameters.m_SignalGen.m_ImageSpacing[2]); m_Controls->m_NumGradientsBox->setValue(parameters.m_SignalGen.GetNumWeightedVolumes()); m_Controls->m_BvalueBox->setValue(parameters.m_SignalGen.GetBvalue()); m_Controls->m_SignalScaleBox->setValue(parameters.m_SignalGen.m_SignalScale); m_Controls->m_TEbox->setValue(parameters.m_SignalGen.m_tEcho); m_Controls->m_LineReadoutTimeBox->setValue(parameters.m_SignalGen.m_tLine); m_Controls->m_T2starBox->setValue(parameters.m_SignalGen.m_tInhom); m_Controls->m_FiberRadius->setValue(parameters.m_SignalGen.m_AxonRadius); m_Controls->m_RelaxationBox->setChecked(parameters.m_SignalGen.m_DoSimulateRelaxation); m_Controls->m_EnforcePureFiberVoxelsBox->setChecked(parameters.m_SignalGen.m_DoDisablePartialVolume); m_Controls->m_ReversePhaseBox->setChecked(parameters.m_SignalGen.m_ReversePhase); m_Controls->m_PartialFourier->setValue(parameters.m_SignalGen.m_PartialFourier); m_Controls->m_TRbox->setValue(parameters.m_SignalGen.m_tRep); m_Controls->m_NumCoilsBox->setValue(parameters.m_SignalGen.m_NumberOfCoils); m_Controls->m_CoilSensBox->setCurrentIndex(parameters.m_SignalGen.m_CoilSensitivityProfile); m_Controls->m_AcquisitionTypeBox->setCurrentIndex(parameters.m_SignalGen.m_AcquisitionType); if (parameters.m_NoiseModel!=nullptr) { m_Controls->m_AddNoise->setChecked(parameters.m_Misc.m_CheckAddNoiseBox); if (dynamic_cast*>(parameters.m_NoiseModel.get())) { m_Controls->m_NoiseDistributionBox->setCurrentIndex(0); } else if (dynamic_cast*>(parameters.m_NoiseModel.get())) { m_Controls->m_NoiseDistributionBox->setCurrentIndex(1); } m_Controls->m_NoiseLevel->setValue(parameters.m_NoiseModel->GetNoiseVariance()); } else { m_Controls->m_AddNoise->setChecked(parameters.m_Misc.m_CheckAddNoiseBox); m_Controls->m_NoiseLevel->setValue(parameters.m_SignalGen.m_NoiseVariance); } m_Controls->m_VolumeFractionsBox->setChecked(parameters.m_Misc.m_CheckOutputVolumeFractionsBox); m_Controls->m_AdvancedOptionsBox_2->setChecked(parameters.m_Misc.m_CheckAdvancedSignalOptionsBox); m_Controls->m_AddGhosts->setChecked(parameters.m_Misc.m_CheckAddGhostsBox); m_Controls->m_AddAliasing->setChecked(parameters.m_Misc.m_CheckAddAliasingBox); m_Controls->m_AddDistortions->setChecked(parameters.m_Misc.m_CheckAddDistortionsBox); m_Controls->m_AddSpikes->setChecked(parameters.m_Misc.m_CheckAddSpikesBox); m_Controls->m_AddEddy->setChecked(parameters.m_Misc.m_CheckAddEddyCurrentsBox); m_Controls->m_kOffsetBox->setValue(parameters.m_SignalGen.m_KspaceLineOffset); m_Controls->m_WrapBox->setValue(100*(1-parameters.m_SignalGen.m_CroppingFactor)); m_Controls->m_SpikeNumBox->setValue(parameters.m_SignalGen.m_Spikes); m_Controls->m_SpikeScaleBox->setValue(parameters.m_SignalGen.m_SpikeAmplitude); m_Controls->m_EddyGradientStrength->setValue(parameters.m_SignalGen.m_EddyStrength); m_Controls->m_AddGibbsRinging->setChecked(parameters.m_SignalGen.m_DoAddGibbsRinging); m_Controls->m_AddMotion->setChecked(parameters.m_SignalGen.m_DoAddMotion); m_Controls->m_RandomMotion->setChecked(parameters.m_SignalGen.m_DoRandomizeMotion); m_Controls->m_MotionVolumesBox->setText(QString(parameters.m_Misc.m_MotionVolumesBox.c_str())); m_Controls->m_MaxTranslationBoxX->setValue(parameters.m_SignalGen.m_Translation[0]); m_Controls->m_MaxTranslationBoxY->setValue(parameters.m_SignalGen.m_Translation[1]); m_Controls->m_MaxTranslationBoxZ->setValue(parameters.m_SignalGen.m_Translation[2]); m_Controls->m_MaxRotationBoxX->setValue(parameters.m_SignalGen.m_Rotation[0]); m_Controls->m_MaxRotationBoxY->setValue(parameters.m_SignalGen.m_Rotation[1]); m_Controls->m_MaxRotationBoxZ->setValue(parameters.m_SignalGen.m_Rotation[2]); m_Controls->m_Compartment1Box->setCurrentIndex(0); m_Controls->m_Compartment2Box->setCurrentIndex(0); m_Controls->m_Compartment3Box->setCurrentIndex(0); m_Controls->m_Compartment4Box->setCurrentIndex(0); for (unsigned int i=0; i* signalModel = nullptr; if (iGetVolumeFractionImage().IsNotNull() ) { compVolNode = mitk::DataNode::New(); mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(signalModel->GetVolumeFractionImage().GetPointer()); image->SetVolume(signalModel->GetVolumeFractionImage()->GetBufferPointer()); compVolNode->SetData( image ); compVolNode->SetName("Compartment volume "+QString::number(signalModel->m_CompartmentId).toStdString()); GetDataStorage()->Add(compVolNode); } switch (signalModel->m_CompartmentId) { case 1: { if (compVolNode.IsNotNull()) m_Controls->m_Comp1VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::StickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_StickWidget1->SetT2(model->GetT2()); m_Controls->m_StickWidget1->SetT1(model->GetT1()); m_Controls->m_StickWidget1->SetD(model->GetDiffusivity()); m_Controls->m_Compartment1Box->setCurrentIndex(0); break; } else if (dynamic_cast*>(signalModel)) { mitk::TensorModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_TensorWidget1->SetT2(model->GetT2()); m_Controls->m_TensorWidget1->SetT1(model->GetT1()); m_Controls->m_TensorWidget1->SetD1(model->GetDiffusivity1()); m_Controls->m_TensorWidget1->SetD2(model->GetDiffusivity2()); m_Controls->m_TensorWidget1->SetD3(model->GetDiffusivity3()); m_Controls->m_Compartment1Box->setCurrentIndex(2); break; } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_PrototypeWidget1->SetNumberOfSamples(model->GetMaxNumKernels()); m_Controls->m_PrototypeWidget1->SetMinFa(model->GetFaRange().first); m_Controls->m_PrototypeWidget1->SetMaxFa(model->GetFaRange().second); m_Controls->m_PrototypeWidget1->SetMinAdc(model->GetAdcRange().first); m_Controls->m_PrototypeWidget1->SetMaxAdc(model->GetAdcRange().second); m_Controls->m_Compartment1Box->setCurrentIndex(3); break; } break; } case 2: { if (compVolNode.IsNotNull()) m_Controls->m_Comp2VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::StickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_StickWidget2->SetT2(model->GetT2()); m_Controls->m_StickWidget2->SetT1(model->GetT1()); m_Controls->m_StickWidget2->SetD(model->GetDiffusivity()); m_Controls->m_Compartment2Box->setCurrentIndex(1); break; } else if (dynamic_cast*>(signalModel)) { mitk::TensorModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_TensorWidget2->SetT2(model->GetT2()); m_Controls->m_TensorWidget2->SetT1(model->GetT1()); m_Controls->m_TensorWidget2->SetD1(model->GetDiffusivity1()); m_Controls->m_TensorWidget2->SetD2(model->GetDiffusivity2()); m_Controls->m_TensorWidget2->SetD3(model->GetDiffusivity3()); m_Controls->m_Compartment2Box->setCurrentIndex(3); break; } break; } case 3: { if (compVolNode.IsNotNull()) m_Controls->m_Comp3VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::BallModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_BallWidget1->SetT2(model->GetT2()); m_Controls->m_BallWidget1->SetT1(model->GetT1()); m_Controls->m_BallWidget1->SetD(model->GetDiffusivity()); m_Controls->m_Compartment3Box->setCurrentIndex(0); break; } else if (dynamic_cast*>(signalModel)) { mitk::AstroStickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_AstrosticksWidget1->SetT2(model->GetT2()); m_Controls->m_AstrosticksWidget1->SetT1(model->GetT1()); m_Controls->m_AstrosticksWidget1->SetD(model->GetDiffusivity()); m_Controls->m_AstrosticksWidget1->SetRandomizeSticks(model->GetRandomizeSticks()); m_Controls->m_Compartment3Box->setCurrentIndex(1); break; } else if (dynamic_cast*>(signalModel)) { mitk::DotModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_DotWidget1->SetT2(model->GetT2()); m_Controls->m_DotWidget1->SetT1(model->GetT1()); m_Controls->m_Compartment3Box->setCurrentIndex(2); break; } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_PrototypeWidget3->SetNumberOfSamples(model->GetMaxNumKernels()); m_Controls->m_PrototypeWidget3->SetMinFa(model->GetFaRange().first); m_Controls->m_PrototypeWidget3->SetMaxFa(model->GetFaRange().second); m_Controls->m_PrototypeWidget3->SetMinAdc(model->GetAdcRange().first); m_Controls->m_PrototypeWidget3->SetMaxAdc(model->GetAdcRange().second); m_Controls->m_Compartment3Box->setCurrentIndex(3); break; } break; } case 4: { if (compVolNode.IsNotNull()) m_Controls->m_Comp4VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::BallModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_BallWidget2->SetT2(model->GetT2()); m_Controls->m_BallWidget2->SetT1(model->GetT1()); m_Controls->m_BallWidget2->SetD(model->GetDiffusivity()); m_Controls->m_Compartment4Box->setCurrentIndex(1); break; } else if (dynamic_cast*>(signalModel)) { mitk::AstroStickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_AstrosticksWidget2->SetT2(model->GetT2()); m_Controls->m_AstrosticksWidget2->SetT1(model->GetT1()); m_Controls->m_AstrosticksWidget2->SetD(model->GetDiffusivity()); m_Controls->m_AstrosticksWidget2->SetRandomizeSticks(model->GetRandomizeSticks()); m_Controls->m_Compartment4Box->setCurrentIndex(2); break; } else if (dynamic_cast*>(signalModel)) { mitk::DotModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_DotWidget2->SetT2(model->GetT2()); m_Controls->m_DotWidget2->SetT1(model->GetT1()); m_Controls->m_Compartment4Box->setCurrentIndex(3); break; } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_PrototypeWidget4->SetNumberOfSamples(model->GetMaxNumKernels()); m_Controls->m_PrototypeWidget4->SetMinFa(model->GetFaRange().first); m_Controls->m_PrototypeWidget4->SetMaxFa(model->GetFaRange().second); m_Controls->m_PrototypeWidget4->SetMinAdc(model->GetAdcRange().first); m_Controls->m_PrototypeWidget4->SetMaxAdc(model->GetAdcRange().second); m_Controls->m_Compartment4Box->setCurrentIndex(4); break; } break; } } } if ( parameters.m_SignalGen.m_MaskImage ) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(parameters.m_SignalGen.m_MaskImage.GetPointer()); image->SetVolume(parameters.m_SignalGen.m_MaskImage->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Tissue mask"); GetDataStorage()->Add(node); m_Controls->m_MaskComboBox->SetSelectedNode(node); } if ( parameters.m_SignalGen.m_FrequencyMap ) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(parameters.m_SignalGen.m_FrequencyMap.GetPointer()); image->SetVolume(parameters.m_SignalGen.m_FrequencyMap->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Frequency map"); GetDataStorage()->Add(node); m_Controls->m_FrequencyMapBox->SetSelectedNode(node); } } void QmitkFiberfoxView::ShowAdvancedOptions(int state) { if (state) { m_Controls->m_AdvancedFiberOptionsFrame->setVisible(true); m_Controls->m_AdvancedSignalOptionsFrame->setVisible(true); m_Controls->m_AdvancedOptionsBox->setChecked(true); m_Controls->m_AdvancedOptionsBox_2->setChecked(true); } else { m_Controls->m_AdvancedFiberOptionsFrame->setVisible(false); m_Controls->m_AdvancedSignalOptionsFrame->setVisible(false); m_Controls->m_AdvancedOptionsBox->setChecked(false); m_Controls->m_AdvancedOptionsBox_2->setChecked(false); } } void QmitkFiberfoxView::Comp1ModelFrameVisibility(int index) { m_Controls->m_StickWidget1->setVisible(false); m_Controls->m_ZeppelinWidget1->setVisible(false); m_Controls->m_TensorWidget1->setVisible(false); m_Controls->m_PrototypeWidget1->setVisible(false); switch (index) { case 0: m_Controls->m_StickWidget1->setVisible(true); break; case 1: m_Controls->m_ZeppelinWidget1->setVisible(true); break; case 2: m_Controls->m_TensorWidget1->setVisible(true); break; case 3: m_Controls->m_PrototypeWidget1->setVisible(true); break; } } void QmitkFiberfoxView::Comp2ModelFrameVisibility(int index) { m_Controls->m_StickWidget2->setVisible(false); m_Controls->m_ZeppelinWidget2->setVisible(false); m_Controls->m_TensorWidget2->setVisible(false); m_Controls->m_Comp2FractionFrame->setVisible(false); switch (index) { case 0: break; case 1: m_Controls->m_StickWidget2->setVisible(true); m_Controls->m_Comp2FractionFrame->setVisible(true); break; case 2: m_Controls->m_ZeppelinWidget2->setVisible(true); m_Controls->m_Comp2FractionFrame->setVisible(true); break; case 3: m_Controls->m_TensorWidget2->setVisible(true); m_Controls->m_Comp2FractionFrame->setVisible(true); break; } } void QmitkFiberfoxView::Comp3ModelFrameVisibility(int index) { m_Controls->m_BallWidget1->setVisible(false); m_Controls->m_AstrosticksWidget1->setVisible(false); m_Controls->m_DotWidget1->setVisible(false); m_Controls->m_PrototypeWidget3->setVisible(false); switch (index) { case 0: m_Controls->m_BallWidget1->setVisible(true); break; case 1: m_Controls->m_AstrosticksWidget1->setVisible(true); break; case 2: m_Controls->m_DotWidget1->setVisible(true); break; case 3: m_Controls->m_PrototypeWidget3->setVisible(true); break; } } void QmitkFiberfoxView::Comp4ModelFrameVisibility(int index) { m_Controls->m_BallWidget2->setVisible(false); m_Controls->m_AstrosticksWidget2->setVisible(false); m_Controls->m_DotWidget2->setVisible(false); m_Controls->m_PrototypeWidget4->setVisible(false); m_Controls->m_Comp4FractionFrame->setVisible(false); switch (index) { case 0: break; case 1: m_Controls->m_BallWidget2->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; case 2: m_Controls->m_AstrosticksWidget2->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; case 3: m_Controls->m_DotWidget2->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; case 4: m_Controls->m_PrototypeWidget4->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; } } void QmitkFiberfoxView::OnConstantRadius(int value) { if (value>0 && m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnAddMotion(int value) { if (value>0) m_Controls->m_MotionArtifactFrame->setVisible(true); else m_Controls->m_MotionArtifactFrame->setVisible(false); } void QmitkFiberfoxView::OnAddAliasing(int value) { if (value>0) m_Controls->m_AliasingFrame->setVisible(true); else m_Controls->m_AliasingFrame->setVisible(false); } void QmitkFiberfoxView::OnAddSpikes(int value) { if (value>0) m_Controls->m_SpikeFrame->setVisible(true); else m_Controls->m_SpikeFrame->setVisible(false); } void QmitkFiberfoxView::OnAddEddy(int value) { if (value>0) m_Controls->m_EddyFrame->setVisible(true); else m_Controls->m_EddyFrame->setVisible(false); } void QmitkFiberfoxView::OnAddDistortions(int value) { if (value>0) m_Controls->m_DistortionsFrame->setVisible(true); else m_Controls->m_DistortionsFrame->setVisible(false); } void QmitkFiberfoxView::OnAddGhosts(int value) { if (value>0) m_Controls->m_GhostFrame->setVisible(true); else m_Controls->m_GhostFrame->setVisible(false); } void QmitkFiberfoxView::OnAddNoise(int value) { if (value>0) m_Controls->m_NoiseFrame->setVisible(true); else m_Controls->m_NoiseFrame->setVisible(false); } void QmitkFiberfoxView::OnDistributionChanged(int value) { if (value==1) m_Controls->m_VarianceBox->setVisible(true); else m_Controls->m_VarianceBox->setVisible(false); if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnVarianceChanged(double) { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnFiberDensityChanged(int) { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnFiberSamplingChanged(double) { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnTensionChanged(double) { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnContinuityChanged(double) { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnBiasChanged(double) { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::AlignOnGrid() { for (unsigned int i=0; i(m_SelectedFiducials.at(i)->GetData()); mitk::Point3D wc0 = pe->GetWorldControlPoint(0); mitk::DataStorage::SetOfObjects::ConstPointer parentFibs = GetDataStorage()->GetSources(m_SelectedFiducials.at(i)); for( mitk::DataStorage::SetOfObjects::const_iterator it = parentFibs->begin(); it != parentFibs->end(); ++it ) { mitk::DataNode::Pointer pFibNode = *it; if ( pFibNode.IsNotNull() && dynamic_cast(pFibNode->GetData()) ) { mitk::DataStorage::SetOfObjects::ConstPointer parentImgs = GetDataStorage()->GetSources(pFibNode); for( mitk::DataStorage::SetOfObjects::const_iterator it2 = parentImgs->begin(); it2 != parentImgs->end(); ++it2 ) { mitk::DataNode::Pointer pImgNode = *it2; if ( pImgNode.IsNotNull() && dynamic_cast(pImgNode->GetData()) ) { mitk::Image::Pointer img = dynamic_cast(pImgNode->GetData()); mitk::BaseGeometry::Pointer geom = img->GetGeometry(); itk::Index<3> idx; geom->WorldToIndex(wc0, idx); mitk::Point3D cIdx; cIdx[0]=idx[0]; cIdx[1]=idx[1]; cIdx[2]=idx[2]; mitk::Point3D world; geom->IndexToWorld(cIdx,world); mitk::Vector3D trans = world - wc0; pe->GetGeometry()->Translate(trans); break; } } break; } } } for(unsigned int i=0; iGetSources(fibNode); for( mitk::DataStorage::SetOfObjects::const_iterator it = sources->begin(); it != sources->end(); ++it ) { mitk::DataNode::Pointer imgNode = *it; if ( imgNode.IsNotNull() && dynamic_cast(imgNode->GetData()) ) { mitk::DataStorage::SetOfObjects::ConstPointer derivations = GetDataStorage()->GetDerivations(fibNode); for( mitk::DataStorage::SetOfObjects::const_iterator it2 = derivations->begin(); it2 != derivations->end(); ++it2 ) { mitk::DataNode::Pointer fiducialNode = *it2; if ( fiducialNode.IsNotNull() && dynamic_cast(fiducialNode->GetData()) ) { mitk::PlanarEllipse::Pointer pe = dynamic_cast(fiducialNode->GetData()); mitk::Point3D wc0 = pe->GetWorldControlPoint(0); mitk::Image::Pointer img = dynamic_cast(imgNode->GetData()); mitk::BaseGeometry::Pointer geom = img->GetGeometry(); itk::Index<3> idx; geom->WorldToIndex(wc0, idx); mitk::Point3D cIdx; cIdx[0]=idx[0]; cIdx[1]=idx[1]; cIdx[2]=idx[2]; mitk::Point3D world; geom->IndexToWorld(cIdx,world); mitk::Vector3D trans = world - wc0; pe->GetGeometry()->Translate(trans); } } break; } } } for(unsigned int i=0; i(m_SelectedImages.at(i)->GetData()); mitk::DataStorage::SetOfObjects::ConstPointer derivations = GetDataStorage()->GetDerivations(m_SelectedImages.at(i)); for( mitk::DataStorage::SetOfObjects::const_iterator it = derivations->begin(); it != derivations->end(); ++it ) { mitk::DataNode::Pointer fibNode = *it; if ( fibNode.IsNotNull() && dynamic_cast(fibNode->GetData()) ) { mitk::DataStorage::SetOfObjects::ConstPointer derivations2 = GetDataStorage()->GetDerivations(fibNode); for( mitk::DataStorage::SetOfObjects::const_iterator it2 = derivations2->begin(); it2 != derivations2->end(); ++it2 ) { mitk::DataNode::Pointer fiducialNode = *it2; if ( fiducialNode.IsNotNull() && dynamic_cast(fiducialNode->GetData()) ) { mitk::PlanarEllipse::Pointer pe = dynamic_cast(fiducialNode->GetData()); mitk::Point3D wc0 = pe->GetWorldControlPoint(0); mitk::BaseGeometry::Pointer geom = img->GetGeometry(); itk::Index<3> idx; geom->WorldToIndex(wc0, idx); mitk::Point3D cIdx; cIdx[0]=idx[0]; cIdx[1]=idx[1]; cIdx[2]=idx[2]; mitk::Point3D world; geom->IndexToWorld(cIdx,world); mitk::Vector3D trans = world - wc0; pe->GetGeometry()->Translate(trans); } } } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::OnFlipButton() { if (m_SelectedFiducial.IsNull()) return; std::map::iterator it = m_DataNodeToPlanarFigureData.find(m_SelectedFiducial.GetPointer()); if( it != m_DataNodeToPlanarFigureData.end() ) { QmitkPlanarFigureData& data = it->second; data.m_Flipped += 1; data.m_Flipped %= 2; } if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } QmitkFiberfoxView::GradientListType QmitkFiberfoxView::GenerateHalfShell(int NPoints) { NPoints *= 2; GradientListType pointshell; int numB0 = NPoints/20; if (numB0==0) numB0=1; GradientType g; g.Fill(0.0); for (int i=0; i theta; theta.set_size(NPoints); vnl_vector phi; phi.set_size(NPoints); - double C = sqrt(4*M_PI); + double C = sqrt(4*itk::Math::pi); phi(0) = 0.0; phi(NPoints-1) = 0.0; for(int i=0; i0 && i std::vector > QmitkFiberfoxView::MakeGradientList() { std::vector > retval; vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); // Add 0 vector for B0 int numB0 = ndirs/10; if (numB0==0) numB0=1; itk::Vector v; v.Fill(0.0); for (int i=0; i v; v[0] = U->get(0,i); v[1] = U->get(1,i); v[2] = U->get(2,i); retval.push_back(v); } return retval; } void QmitkFiberfoxView::OnAddBundle() { if (m_SelectedImageNode.IsNull()) return; mitk::DataStorage::SetOfObjects::ConstPointer children = GetDataStorage()->GetDerivations(m_SelectedImageNode); mitk::FiberBundle::Pointer bundle = mitk::FiberBundle::New(); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( bundle ); QString name = QString("Bundle_%1").arg(children->size()); node->SetName(name.toStdString()); m_SelectedBundles.push_back(node); UpdateGui(); GetDataStorage()->Add(node, m_SelectedImageNode); } void QmitkFiberfoxView::OnDrawROI() { if (m_SelectedBundles.empty()) OnAddBundle(); if (m_SelectedBundles.empty()) return; mitk::DataStorage::SetOfObjects::ConstPointer children = GetDataStorage()->GetDerivations(m_SelectedBundles.at(0)); mitk::PlanarEllipse::Pointer figure = mitk::PlanarEllipse::New(); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( figure ); node->SetBoolProperty("planarfigure.3drendering", true); node->SetBoolProperty("planarfigure.3drendering.fill", true); QList nodes = this->GetDataManagerSelection(); for( int i=0; iSetSelected(false); m_SelectedFiducial = node; QString name = QString("Fiducial_%1").arg(children->size()); node->SetName(name.toStdString()); node->SetSelected(true); this->DisableCrosshairNavigation(); mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(node->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( node ); } UpdateGui(); GetDataStorage()->Add(node, m_SelectedBundles.at(0)); } bool CompareLayer(mitk::DataNode::Pointer i,mitk::DataNode::Pointer j) { int li = -1; i->GetPropertyValue("layer", li); int lj = -1; j->GetPropertyValue("layer", lj); return liGetSources(m_SelectedFiducial); for( mitk::DataStorage::SetOfObjects::const_iterator it = parents->begin(); it != parents->end(); ++it ) if(dynamic_cast((*it)->GetData())) m_SelectedBundles.push_back(*it); if (m_SelectedBundles.empty()) return; } FiberfoxParameters parameters = UpdateImageParameters(false); for (unsigned int i=0; iGetDerivations(m_SelectedBundles.at(i)); std::vector< mitk::DataNode::Pointer > childVector; for( mitk::DataStorage::SetOfObjects::const_iterator it = children->begin(); it != children->end(); ++it ) childVector.push_back(*it); std::sort(childVector.begin(), childVector.end(), CompareLayer); std::vector< mitk::PlanarEllipse::Pointer > fib; std::vector< unsigned int > flip; float radius = 1; int count = 0; for( std::vector< mitk::DataNode::Pointer >::const_iterator it = childVector.begin(); it != childVector.end(); ++it ) { mitk::DataNode::Pointer node = *it; if ( node.IsNotNull() && dynamic_cast(node->GetData()) ) { mitk::PlanarEllipse* ellipse = dynamic_cast(node->GetData()); if (m_Controls->m_ConstantRadiusBox->isChecked()) { ellipse->SetTreatAsCircle(true); mitk::Point2D c = ellipse->GetControlPoint(0); mitk::Point2D p = ellipse->GetControlPoint(1); mitk::Vector2D v = p-c; if (count==0) { radius = v.GetVnlVector().magnitude(); ellipse->SetControlPoint(1, p); ellipse->Modified(); } else { v.Normalize(); v *= radius; ellipse->SetControlPoint(1, c+v); ellipse->Modified(); } } fib.push_back(ellipse); std::map::iterator it = m_DataNodeToPlanarFigureData.find(node.GetPointer()); if( it != m_DataNodeToPlanarFigureData.end() ) { QmitkPlanarFigureData& data = it->second; flip.push_back(data.m_Flipped); } else flip.push_back(0); } count++; } if (fib.size()>1) { parameters.m_FiberGen.m_Fiducials.push_back(fib); parameters.m_FiberGen.m_FlipList.push_back(flip); } else if (fib.size()>0) m_SelectedBundles.at(i)->SetData( mitk::FiberBundle::New() ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } itk::FibersFromPlanarFiguresFilter::Pointer filter = itk::FibersFromPlanarFiguresFilter::New(); filter->SetParameters(parameters.m_FiberGen); filter->Update(); std::vector< mitk::FiberBundle::Pointer > fiberBundles = filter->GetFiberBundles(); for (unsigned int i=0; iSetData( fiberBundles.at(i) ); if (fiberBundles.at(i)->GetNumFibers()>50000) m_SelectedBundles.at(i)->SetVisibility(false); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberfoxView::GenerateImage() { if (m_Controls->m_FiberBundleComboBox->GetSelectedNode().IsNull() && !mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( m_Controls->m_TemplateComboBox->GetSelectedNode())) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateGradientImage( m_Controls->m_SizeX->value(), m_Controls->m_SizeY->value(), m_Controls->m_SizeZ->value(), m_Controls->m_SpacingX->value(), m_Controls->m_SpacingY->value(), m_Controls->m_SpacingZ->value()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Dummy"); unsigned int window = m_Controls->m_SizeX->value()*m_Controls->m_SizeY->value()*m_Controls->m_SizeZ->value(); unsigned int level = window/2; mitk::LevelWindow lw; lw.SetLevelWindow(level, window); node->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( lw ) ); GetDataStorage()->Add(node); m_SelectedImageNode = node; mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } UpdateGui(); QMessageBox::information(nullptr, "Template image generated", "You have selected no fiber bundle or diffusion-weighted image, which can be used to simulate a new diffusion-weighted image. A template image with the specified geometry has been generated that can be used to draw artificial fibers (see tab 'Fiber Definition')."); } else if (m_Controls->m_FiberBundleComboBox->GetSelectedNode().IsNotNull()) SimulateImageFromFibers(m_Controls->m_FiberBundleComboBox->GetSelectedNode()); else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( m_Controls->m_TemplateComboBox->GetSelectedNode()) ) SimulateForExistingDwi(m_Controls->m_TemplateComboBox->GetSelectedNode()); else QMessageBox::information(nullptr, "No image generated", "You have selected no fiber bundle or diffusion-weighted image, which can be used to simulate a new diffusion-weighted image."); } void QmitkFiberfoxView::SimulateForExistingDwi(mitk::DataNode* imageNode) { bool isDiffusionImage( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(imageNode->GetData())) ); if ( !isDiffusionImage ) { return; } FiberfoxParameters parameters = UpdateImageParameters(); mitk::Image::Pointer diffImg = dynamic_cast(imageNode->GetData()); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); m_TractsToDwiFilter = itk::TractsToDWIImageFilter< short >::New(); parameters.m_Misc.m_ParentNode = imageNode; parameters.m_SignalGen.m_SignalScale = 1; parameters.m_Misc.m_ResultNode->SetName(parameters.m_Misc.m_ParentNode->GetName() +"_D"+QString::number(parameters.m_SignalGen.m_ImageRegion.GetSize(0)).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageRegion.GetSize(1)).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageRegion.GetSize(2)).toStdString() +"_S"+QString::number(parameters.m_SignalGen.m_ImageSpacing[0]).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageSpacing[1]).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageSpacing[2]).toStdString() +"_b"+QString::number(parameters.m_SignalGen.GetBvalue()).toStdString() +"_"+parameters.m_Misc.m_SignalModelString +parameters.m_Misc.m_ArtifactModelString); m_TractsToDwiFilter->SetParameters(parameters); m_TractsToDwiFilter->SetInputImage(itkVectorImagePointer); m_Thread.start(QThread::LowestPriority); } void QmitkFiberfoxView::SimulateImageFromFibers(mitk::DataNode* fiberNode) { mitk::FiberBundle::Pointer fiberBundle = dynamic_cast(fiberNode->GetData()); if (fiberBundle->GetNumFibers()<=0) { return; } FiberfoxParameters parameters = UpdateImageParameters(); m_TractsToDwiFilter = itk::TractsToDWIImageFilter< short >::New(); parameters.m_Misc.m_ParentNode = fiberNode; parameters.m_Misc.m_ResultNode->SetName(parameters.m_Misc.m_ParentNode->GetName() +"_D"+QString::number(parameters.m_SignalGen.m_ImageRegion.GetSize(0)).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageRegion.GetSize(1)).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageRegion.GetSize(2)).toStdString() +"_S"+QString::number(parameters.m_SignalGen.m_ImageSpacing[0]).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageSpacing[1]).toStdString() +"-"+QString::number(parameters.m_SignalGen.m_ImageSpacing[2]).toStdString() +"_b"+QString::number(parameters.m_SignalGen.GetBvalue()).toStdString() +"_"+parameters.m_Misc.m_SignalModelString +parameters.m_Misc.m_ArtifactModelString); if ( m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast (m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()) ) ) { bool first = true; bool ok = true; mitk::Image::Pointer diffImg = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); itk::Image< itk::DiffusionTensor3D< double >, 3 >::Pointer tensorImage = nullptr; const int shOrder = 2; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter QballFilterType; QballFilterType::CoefficientImageType::Pointer itkFeatureImage = nullptr; ItkDoubleImgType::Pointer adcImage = nullptr; for (unsigned int i=0; i* model = nullptr; if (i* >(parameters.m_FiberModelList.at(i)); else model = dynamic_cast< mitk::RawShModel<>* >(parameters.m_NonFiberModelList.at(i-parameters.m_FiberModelList.size())); if (model!=0 && model->GetNumberOfKernels()<=0) { if (first==true) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); filter->SetBValue( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); filter->SetGradientImage( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetGradientImage( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(), itkVectorImagePointer ); qballfilter->SetBValue( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput( itkVectorImagePointer ); adcFilter->SetGradientDirections( static_cast ( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); adcFilter->SetB_value( static_cast (diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); adcFilter->Update(); adcImage = adcFilter->GetOutput(); } ok = model->SampleKernels(diffImg, parameters.m_SignalGen.m_MaskImage, tensorImage, itkFeatureImage, adcImage); if (!ok) break; } } if (!ok) { QMessageBox::information( nullptr, "Simulation cancelled", "No valid prototype signals could be sampled."); return; } } else if ( m_Controls->m_Compartment1Box->currentIndex()==3 || m_Controls->m_Compartment3Box->currentIndex()==3 || m_Controls->m_Compartment4Box->currentIndex()==4 ) { QMessageBox::information( nullptr, "Simulation cancelled", "Prototype signal but no diffusion-weighted image selected to sample signal from."); return; } m_TractsToDwiFilter->SetParameters(parameters); m_TractsToDwiFilter->SetFiberBundle(fiberBundle); m_Thread.start(QThread::LowestPriority); } void QmitkFiberfoxView::ApplyTransform() { std::vector< mitk::DataNode::Pointer > selectedBundles; for(unsigned int i=0; iGetDerivations(m_SelectedImages.at(i)); for( mitk::DataStorage::SetOfObjects::const_iterator it = derivations->begin(); it != derivations->end(); ++it ) { mitk::DataNode::Pointer fibNode = *it; if ( fibNode.IsNotNull() && dynamic_cast(fibNode->GetData()) ) selectedBundles.push_back(fibNode); } } if (selectedBundles.empty()) selectedBundles = m_SelectedBundles2; if (!selectedBundles.empty()) { for (std::vector::const_iterator it = selectedBundles.begin(); it!=selectedBundles.end(); ++it) { mitk::FiberBundle::Pointer fib = dynamic_cast((*it)->GetData()); fib->RotateAroundAxis(m_Controls->m_XrotBox->value(), m_Controls->m_YrotBox->value(), m_Controls->m_ZrotBox->value()); fib->TranslateFibers(m_Controls->m_XtransBox->value(), m_Controls->m_YtransBox->value(), m_Controls->m_ZtransBox->value()); fib->ScaleFibers(m_Controls->m_XscaleBox->value(), m_Controls->m_YscaleBox->value(), m_Controls->m_ZscaleBox->value()); // handle child fiducials if (m_Controls->m_IncludeFiducials->isChecked()) { mitk::DataStorage::SetOfObjects::ConstPointer derivations = GetDataStorage()->GetDerivations(*it); for( mitk::DataStorage::SetOfObjects::const_iterator it2 = derivations->begin(); it2 != derivations->end(); ++it2 ) { mitk::DataNode::Pointer fiducialNode = *it2; if ( fiducialNode.IsNotNull() && dynamic_cast(fiducialNode->GetData()) ) { mitk::PlanarEllipse* pe = dynamic_cast(fiducialNode->GetData()); mitk::BaseGeometry* geom = pe->GetGeometry(); // translate mitk::Vector3D world; world[0] = m_Controls->m_XtransBox->value(); world[1] = m_Controls->m_YtransBox->value(); world[2] = m_Controls->m_ZtransBox->value(); geom->Translate(world); // calculate rotation matrix - double x = m_Controls->m_XrotBox->value()*M_PI/180; - double y = m_Controls->m_YrotBox->value()*M_PI/180; - double z = m_Controls->m_ZrotBox->value()*M_PI/180; + double x = m_Controls->m_XrotBox->value()*itk::Math::pi/180; + double y = m_Controls->m_YrotBox->value()*itk::Math::pi/180; + double z = m_Controls->m_ZrotBox->value()*itk::Math::pi/180; itk::Matrix< double, 3, 3 > rotX; rotX.SetIdentity(); rotX[1][1] = cos(x); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(x); rotX[2][1] = -rotX[1][2]; itk::Matrix< double, 3, 3 > rotY; rotY.SetIdentity(); rotY[0][0] = cos(y); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(y); rotY[2][0] = -rotY[0][2]; itk::Matrix< double, 3, 3 > rotZ; rotZ.SetIdentity(); rotZ[0][0] = cos(z); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(z); rotZ[1][0] = -rotZ[0][1]; itk::Matrix< double, 3, 3 > rot = rotZ*rotY*rotX; // transform control point coordinate into geometry translation geom->SetOrigin(pe->GetWorldControlPoint(0)); mitk::Point2D cp; cp.Fill(0.0); pe->SetControlPoint(0, cp); // rotate fiducial geom->GetIndexToWorldTransform()->SetMatrix(rot*geom->GetIndexToWorldTransform()->GetMatrix()); // implicit translation mitk::Vector3D trans; trans[0] = geom->GetOrigin()[0]-fib->GetGeometry()->GetCenter()[0]; trans[1] = geom->GetOrigin()[1]-fib->GetGeometry()->GetCenter()[1]; trans[2] = geom->GetOrigin()[2]-fib->GetGeometry()->GetCenter()[2]; mitk::Vector3D newWc = rot*trans; newWc = newWc-trans; geom->Translate(newWc); pe->Modified(); } } } } } else { for (unsigned int i=0; i(m_SelectedFiducials.at(i)->GetData()); mitk::BaseGeometry* geom = pe->GetGeometry(); // translate mitk::Vector3D world; world[0] = m_Controls->m_XtransBox->value(); world[1] = m_Controls->m_YtransBox->value(); world[2] = m_Controls->m_ZtransBox->value(); geom->Translate(world); // calculate rotation matrix - double x = m_Controls->m_XrotBox->value()*M_PI/180; - double y = m_Controls->m_YrotBox->value()*M_PI/180; - double z = m_Controls->m_ZrotBox->value()*M_PI/180; + double x = m_Controls->m_XrotBox->value()*itk::Math::pi/180; + double y = m_Controls->m_YrotBox->value()*itk::Math::pi/180; + double z = m_Controls->m_ZrotBox->value()*itk::Math::pi/180; itk::Matrix< double, 3, 3 > rotX; rotX.SetIdentity(); rotX[1][1] = cos(x); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(x); rotX[2][1] = -rotX[1][2]; itk::Matrix< double, 3, 3 > rotY; rotY.SetIdentity(); rotY[0][0] = cos(y); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(y); rotY[2][0] = -rotY[0][2]; itk::Matrix< double, 3, 3 > rotZ; rotZ.SetIdentity(); rotZ[0][0] = cos(z); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(z); rotZ[1][0] = -rotZ[0][1]; itk::Matrix< double, 3, 3 > rot = rotZ*rotY*rotX; // transform control point coordinate into geometry translation geom->SetOrigin(pe->GetWorldControlPoint(0)); mitk::Point2D cp; cp.Fill(0.0); pe->SetControlPoint(0, cp); // rotate fiducial geom->GetIndexToWorldTransform()->SetMatrix(rot*geom->GetIndexToWorldTransform()->GetMatrix()); pe->Modified(); } if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberfoxView::CopyBundles() { if ( m_SelectedBundles.size()<1 ){ QMessageBox::information( nullptr, "Warning", "Select at least one fiber bundle!"); MITK_WARN("QmitkFiberFoxView") << "Select at least one fiber bundle!"; return; } for (std::vector::const_iterator it = m_SelectedBundles.begin(); it!=m_SelectedBundles.end(); ++it) { // find parent image mitk::DataNode::Pointer parentNode; mitk::DataStorage::SetOfObjects::ConstPointer parentImgs = GetDataStorage()->GetSources(*it); for( mitk::DataStorage::SetOfObjects::const_iterator it2 = parentImgs->begin(); it2 != parentImgs->end(); ++it2 ) { mitk::DataNode::Pointer pImgNode = *it2; if ( pImgNode.IsNotNull() && dynamic_cast(pImgNode->GetData()) ) { parentNode = pImgNode; break; } } mitk::FiberBundle::Pointer fib = dynamic_cast((*it)->GetData()); mitk::FiberBundle::Pointer newBundle = fib->GetDeepCopy(); QString name((*it)->GetName().c_str()); name += "_copy"; mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); if (parentNode.IsNotNull()) GetDataStorage()->Add(fbNode, parentNode); else GetDataStorage()->Add(fbNode); // copy child fiducials if (m_Controls->m_IncludeFiducials->isChecked()) { mitk::DataStorage::SetOfObjects::ConstPointer derivations = GetDataStorage()->GetDerivations(*it); for( mitk::DataStorage::SetOfObjects::const_iterator it2 = derivations->begin(); it2 != derivations->end(); ++it2 ) { mitk::DataNode::Pointer fiducialNode = *it2; if ( fiducialNode.IsNotNull() && dynamic_cast(fiducialNode->GetData()) ) { mitk::PlanarEllipse::Pointer pe = dynamic_cast(fiducialNode->GetData())->Clone(); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(pe); newNode->SetName(fiducialNode->GetName()); newNode->SetBoolProperty("planarfigure.3drendering", true); GetDataStorage()->Add(newNode, fbNode); } } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberfoxView::JoinBundles() { if ( m_SelectedBundles.size()<2 ){ QMessageBox::information( nullptr, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberFoxView") << "Select at least two fiber bundles!"; return; } std::vector::const_iterator it = m_SelectedBundles.begin(); mitk::FiberBundle::Pointer newBundle = dynamic_cast((*it)->GetData()); QString name(""); name += QString((*it)->GetName().c_str()); ++it; for (; it!=m_SelectedBundles.end(); ++it) { newBundle = newBundle->AddBundle(dynamic_cast((*it)->GetData())); name += "+"+QString((*it)->GetName().c_str()); } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberfoxView::UpdateGui() { m_Controls->m_GeometryFrame->setEnabled(true); m_Controls->m_GeometryMessage->setVisible(false); m_Controls->m_DiffusionPropsMessage->setVisible(false); m_Controls->m_FiberGenMessage->setVisible(true); m_Controls->m_TransformBundlesButton->setEnabled(false); m_Controls->m_CopyBundlesButton->setEnabled(false); m_Controls->m_GenerateFibersButton->setEnabled(false); m_Controls->m_FlipButton->setEnabled(false); m_Controls->m_CircleButton->setEnabled(false); m_Controls->m_BvalueBox->setEnabled(true); m_Controls->m_NumGradientsBox->setEnabled(true); m_Controls->m_JoinBundlesButton->setEnabled(false); m_Controls->m_AlignOnGrid->setEnabled(false); // Fiber generation gui if (m_SelectedFiducial.IsNotNull()) { m_Controls->m_TransformBundlesButton->setEnabled(true); m_Controls->m_FlipButton->setEnabled(true); m_Controls->m_AlignOnGrid->setEnabled(true); } if (m_SelectedImageNode.IsNotNull() || !m_SelectedBundles.empty()) { m_Controls->m_CircleButton->setEnabled(true); m_Controls->m_FiberGenMessage->setVisible(false); } if (m_SelectedImageNode.IsNotNull() && !m_SelectedBundles.empty()) m_Controls->m_AlignOnGrid->setEnabled(true); if (!m_SelectedBundles.empty()) { m_Controls->m_TransformBundlesButton->setEnabled(true); m_Controls->m_CopyBundlesButton->setEnabled(true); m_Controls->m_GenerateFibersButton->setEnabled(true); if (m_SelectedBundles.size()>1) m_Controls->m_JoinBundlesButton->setEnabled(true); } // Signal generation gui if (m_Controls->m_MaskComboBox->GetSelectedNode().IsNotNull() || m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull()) { m_Controls->m_GeometryMessage->setVisible(true); m_Controls->m_GeometryFrame->setEnabled(false); } if ( m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()) ) ) { m_Controls->m_DiffusionPropsMessage->setVisible(true); m_Controls->m_BvalueBox->setEnabled(false); m_Controls->m_NumGradientsBox->setEnabled(false); m_Controls->m_GeometryMessage->setVisible(true); m_Controls->m_GeometryFrame->setEnabled(false); } } void QmitkFiberfoxView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) { m_SelectedBundles2.clear(); m_SelectedImages.clear(); m_SelectedFiducials.clear(); m_SelectedFiducial = nullptr; m_SelectedBundles.clear(); m_SelectedImageNode = nullptr; // iterate all selected objects, adjust warning visibility for( int i=0; i(node->GetData()) ) { m_SelectedImages.push_back(node); m_SelectedImageNode = node; } else if ( node.IsNotNull() && dynamic_cast(node->GetData()) ) { m_SelectedBundles2.push_back(node); if (m_Controls->m_RealTimeFibers->isChecked()) { m_SelectedBundles.push_back(node); mitk::FiberBundle::Pointer newFib = dynamic_cast(node->GetData()); if (newFib->GetNumFibers()!=m_Controls->m_FiberDensityBox->value()) GenerateFibers(); } else m_SelectedBundles.push_back(node); } else if ( node.IsNotNull() && dynamic_cast(node->GetData()) ) { m_SelectedFiducials.push_back(node); m_SelectedFiducial = node; m_SelectedBundles.clear(); mitk::DataStorage::SetOfObjects::ConstPointer parents = GetDataStorage()->GetSources(node); for( mitk::DataStorage::SetOfObjects::const_iterator it = parents->begin(); it != parents->end(); ++it ) { mitk::DataNode::Pointer pNode = *it; if ( pNode.IsNotNull() && dynamic_cast(pNode->GetData()) ) m_SelectedBundles.push_back(pNode); } } } UpdateGui(); } void QmitkFiberfoxView::EnableCrosshairNavigation() { if (m_Controls->m_RealTimeFibers->isChecked()) GenerateFibers(); } void QmitkFiberfoxView::DisableCrosshairNavigation() { } void QmitkFiberfoxView::NodeRemoved(const mitk::DataNode* node) { mitk::DataNode* nonConstNode = const_cast(node); std::map::iterator it = m_DataNodeToPlanarFigureData.find(nonConstNode); if (dynamic_cast(node->GetData())) { m_SelectedBundles.clear(); m_SelectedBundles2.clear(); } else if (dynamic_cast(node->GetData())) m_SelectedImages.clear(); if( it != m_DataNodeToPlanarFigureData.end() ) { QmitkPlanarFigureData& data = it->second; // remove observers data.m_Figure->RemoveObserver( data.m_EndPlacementObserverTag ); data.m_Figure->RemoveObserver( data.m_SelectObserverTag ); data.m_Figure->RemoveObserver( data.m_StartInteractionObserverTag ); data.m_Figure->RemoveObserver( data.m_EndInteractionObserverTag ); m_DataNodeToPlanarFigureData.erase( it ); } } void QmitkFiberfoxView::NodeAdded( const mitk::DataNode* node ) { // add observer for selection in renderwindow mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); bool isPositionMarker (false); node->GetBoolProperty("isContourMarker", isPositionMarker); if( figure && !isPositionMarker ) { MITK_DEBUG << "figure added. will add interactor if needed."; mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(node->GetDataInteractor().GetPointer()); mitk::DataNode* nonConstNode = const_cast( node ); 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( nonConstNode ); } MITK_DEBUG << "will now add observers for planarfigure"; QmitkPlanarFigureData data; data.m_Figure = figure; // // add observer for event when figure has been placed typedef itk::SimpleMemberCommand< QmitkFiberfoxView > SimpleCommandType; // SimpleCommandType::Pointer initializationCommand = SimpleCommandType::New(); // initializationCommand->SetCallbackFunction( this, &QmitkFiberfoxView::PlanarFigureInitialized ); // data.m_EndPlacementObserverTag = figure->AddObserver( mitk::EndPlacementPlanarFigureEvent(), initializationCommand ); // add observer for event when figure is picked (selected) typedef itk::MemberCommand< QmitkFiberfoxView > MemberCommandType; MemberCommandType::Pointer selectCommand = MemberCommandType::New(); selectCommand->SetCallbackFunction( this, &QmitkFiberfoxView::PlanarFigureSelected ); data.m_SelectObserverTag = figure->AddObserver( mitk::SelectPlanarFigureEvent(), selectCommand ); // add observer for event when interaction with figure starts SimpleCommandType::Pointer startInteractionCommand = SimpleCommandType::New(); startInteractionCommand->SetCallbackFunction( this, &QmitkFiberfoxView::DisableCrosshairNavigation); data.m_StartInteractionObserverTag = figure->AddObserver( mitk::StartInteractionPlanarFigureEvent(), startInteractionCommand ); // add observer for event when interaction with figure starts SimpleCommandType::Pointer endInteractionCommand = SimpleCommandType::New(); endInteractionCommand->SetCallbackFunction( this, &QmitkFiberfoxView::EnableCrosshairNavigation); data.m_EndInteractionObserverTag = figure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand ); m_DataNodeToPlanarFigureData[nonConstNode] = data; } } void QmitkFiberfoxView::PlanarFigureSelected( itk::Object* object, const itk::EventObject& ) { mitk::TNodePredicateDataType::Pointer isPf = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allPfs = this->GetDataStorage()->GetSubset( isPf ); for ( mitk::DataStorage::SetOfObjects::const_iterator it = allPfs->begin(); it!=allPfs->end(); ++it) { mitk::DataNode* node = *it; if( node->GetData() == object ) { node->SetSelected(true); m_SelectedFiducial = node; } else node->SetSelected(false); } UpdateGui(); this->RequestRenderWindowUpdate(); } void QmitkFiberfoxView::SetFocus() { m_Controls->m_CircleButton->setFocus(); } void QmitkFiberfoxView::SetOutputPath() { // SELECT FOLDER DIALOG std::string outputPath; outputPath = QFileDialog::getExistingDirectory(nullptr, "Save images to...", QString(outputPath.c_str())).toStdString(); if (outputPath.empty()) m_Controls->m_SavePathEdit->setText("-"); else { outputPath += "/"; m_Controls->m_SavePathEdit->setText(QString(outputPath.c_str())); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp index a0f8f877b6..e6ba55f4b5 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp @@ -1,1596 +1,1592 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "QmitkFiberProcessingView.h" // Qt #include // MITK #include #include #include #include #include #include #include #include #include #include #include "usModuleRegistry.h" #include #include "mitkNodePredicateDataType.h" #include #include #include #include // ITK #include #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - - const std::string QmitkFiberProcessingView::VIEW_ID = "org.mitk.views.fiberprocessing"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace mitk; QmitkFiberProcessingView::QmitkFiberProcessingView() : QmitkAbstractView() , m_Controls( 0 ) , m_CircleCounter(0) , m_PolygonCounter(0) , m_UpsamplingFactor(1) { } // Destructor QmitkFiberProcessingView::~QmitkFiberProcessingView() { RemoveObservers(); } void QmitkFiberProcessingView::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::QmitkFiberProcessingViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_CircleButton, SIGNAL( clicked() ), this, SLOT( OnDrawCircle() ) ); connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ), this, SLOT( OnDrawPolygon() ) ); connect(m_Controls->PFCompoANDButton, SIGNAL(clicked()), this, SLOT(GenerateAndComposite()) ); connect(m_Controls->PFCompoORButton, SIGNAL(clicked()), this, SLOT(GenerateOrComposite()) ); connect(m_Controls->PFCompoNOTButton, SIGNAL(clicked()), this, SLOT(GenerateNotComposite()) ); connect(m_Controls->m_GenerateRoiImage, SIGNAL(clicked()), this, SLOT(GenerateRoiImage()) ); connect(m_Controls->m_JoinBundles, SIGNAL(clicked()), this, SLOT(JoinBundles()) ); connect(m_Controls->m_SubstractBundles, SIGNAL(clicked()), this, SLOT(SubstractBundles()) ); connect(m_Controls->m_CopyBundle, SIGNAL(clicked()), this, SLOT(CopyBundles()) ); connect(m_Controls->m_ExtractFibersButton, SIGNAL(clicked()), this, SLOT(Extract())); connect(m_Controls->m_RemoveButton, SIGNAL(clicked()), this, SLOT(Remove())); connect(m_Controls->m_ModifyButton, SIGNAL(clicked()), this, SLOT(Modify())); connect(m_Controls->m_ExtractionMethodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect(m_Controls->m_RemovalMethodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect(m_Controls->m_ModificationMethodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect(m_Controls->m_ExtractionBoxMask, SIGNAL(currentIndexChanged(int)), this, SLOT(OnMaskExtractionChanged())); m_Controls->m_ColorMapBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); mitk::NodePredicateOr::Pointer isDiffusionImage = mitk::NodePredicateOr::New(isDwi, isDti); isDiffusionImage = mitk::NodePredicateOr::New(isDiffusionImage, isOdf); mitk::NodePredicateNot::Pointer noDiffusionImage = mitk::NodePredicateNot::New(isDiffusionImage); mitk::NodePredicateAnd::Pointer finalPredicate = mitk::NodePredicateAnd::New(isMitkImage, noDiffusionImage); m_Controls->m_ColorMapBox->SetPredicate(finalPredicate); m_Controls->label_17->setVisible(false); m_Controls->m_FiberExtractionFractionBox->setVisible(false); } UpdateGui(); } void QmitkFiberProcessingView::OnMaskExtractionChanged() { if (m_Controls->m_ExtractionBoxMask->currentIndex() == 2 || m_Controls->m_ExtractionBoxMask->currentIndex() == 3) { m_Controls->label_17->setVisible(true); m_Controls->m_FiberExtractionFractionBox->setVisible(true); m_Controls->m_BothEnds->setVisible(false); } else { m_Controls->label_17->setVisible(false); m_Controls->m_FiberExtractionFractionBox->setVisible(false); if (m_Controls->m_ExtractionBoxMask->currentIndex() != 3) m_Controls->m_BothEnds->setVisible(true); } } void QmitkFiberProcessingView::SetFocus() { m_Controls->toolBoxx->setFocus(); } void QmitkFiberProcessingView::Modify() { switch (m_Controls->m_ModificationMethodBox->currentIndex()) { case 0: { ResampleSelectedBundlesSpline(); break; } case 1: { ResampleSelectedBundlesLinear(); break; } case 2: { CompressSelectedBundles(); break; } case 3: { DoImageColorCoding(); break; } case 4: { MirrorFibers(); break; } case 5: { WeightFibers(); break; } case 6: { DoCurvatureColorCoding(); break; } case 7: { DoWeightColorCoding(); break; } } } void QmitkFiberProcessingView::WeightFibers() { float weight = this->m_Controls->m_BundleWeightBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->SetFiberWeights(weight); } } void QmitkFiberProcessingView::Remove() { switch (m_Controls->m_RemovalMethodBox->currentIndex()) { case 0: { RemoveDir(); break; } case 1: { PruneBundle(); break; } case 2: { ApplyCurvatureThreshold(); break; } case 3: { RemoveWithMask(false); break; } case 4: { RemoveWithMask(true); break; } case 5: { ApplyWeightThreshold(); break; } } } void QmitkFiberProcessingView::Extract() { switch (m_Controls->m_ExtractionMethodBox->currentIndex()) { case 0: { ExtractWithPlanarFigure(); break; } case 1: { switch (m_Controls->m_ExtractionBoxMask->currentIndex()) { { case 0: ExtractWithMask(true, false); break; } { case 1: ExtractWithMask(true, true); break; } { case 2: ExtractWithMask(false, false); break; } { case 3: ExtractWithMask(false, true); break; } } break; } } } void QmitkFiberProcessingView::PruneBundle() { int minLength = this->m_Controls->m_PruneFibersMinBox->value(); int maxLength = this->m_Controls->m_PruneFibersMaxBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); if (!fib->RemoveShortFibers(minLength)) QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); else if (!fib->RemoveLongFibers(maxLength)) QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ApplyWeightThreshold() { float thr = this->m_Controls->m_WeightThresholdBox->value(); std::vector< DataNode::Pointer > nodes = m_SelectedFB; for (auto node : nodes) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); mitk::FiberBundle::Pointer newFib = fib->FilterByWeights(thr); if (newFib->GetNumFibers()>0) { newFib->ColorFibersByFiberWeights(false, true); node->SetData(newFib); } else QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ApplyCurvatureThreshold() { int angle = this->m_Controls->m_CurvSpinBox->value(); int dist = this->m_Controls->m_CurvDistanceSpinBox->value(); std::vector< DataNode::Pointer > nodes = m_SelectedFB; for (auto node : nodes) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); itk::FiberCurvatureFilter::Pointer filter = itk::FiberCurvatureFilter::New(); filter->SetInputFiberBundle(fib); filter->SetAngularDeviation(angle); filter->SetDistance(dist); filter->SetRemoveFibers(m_Controls->m_RemoveCurvedFibersBox->isChecked()); filter->Update(); mitk::FiberBundle::Pointer newFib = filter->GetOutputFiberBundle(); if (newFib->GetNumFibers()>0) { newFib->ColorFibersByOrientation(); node->SetData(newFib); } else QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::RemoveDir() { for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); vnl_vector_fixed dir; dir[0] = m_Controls->m_ExtractDirX->value(); dir[1] = m_Controls->m_ExtractDirY->value(); dir[2] = m_Controls->m_ExtractDirZ->value(); - fib->RemoveDir(dir,cos((float)m_Controls->m_ExtractAngle->value()*M_PI/180)); + fib->RemoveDir(dir,cos((float)m_Controls->m_ExtractAngle->value()*itk::Math::pi/180)); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::RemoveWithMask(bool removeInside) { if (m_RoiImageNode.IsNull()) return; mitk::Image::Pointer mitkMask = dynamic_cast(m_RoiImageNode->GetData()); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); ItkUCharImageType::Pointer mask = ItkUCharImageType::New(); mitk::CastToItkImage(mitkMask, mask); mitk::FiberBundle::Pointer newFib = fib->RemoveFibersOutside(mask, removeInside); if (newFib->GetNumFibers()<=0) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); continue; } node->SetData(newFib); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ExtractWithMask(bool onlyEnds, bool invert) { if (m_RoiImageNode.IsNull()) return; mitk::Image::Pointer mitkMask = dynamic_cast(m_RoiImageNode->GetData()); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); QString name(node->GetName().c_str()); ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(mitkMask, mask); itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); extractor->SetInputFiberBundle(fib); extractor->SetRoiImages({mask}); extractor->SetThreshold(m_Controls->m_FiberExtractionThresholdBox->value()); extractor->SetOverlapFraction(m_Controls->m_FiberExtractionFractionBox->value()); extractor->SetBothEnds(m_Controls->m_BothEnds->isChecked()); extractor->SetInterpolate(m_Controls->m_InterpolateRoiBox->isChecked()); if (invert) extractor->SetNoPositives(true); else extractor->SetNoNegatives(true); if (onlyEnds) extractor->SetMode(itk::FiberExtractionFilter::MODE::ENDPOINTS); extractor->Update(); mitk::FiberBundle::Pointer newFib; if (invert) newFib = extractor->GetNegatives().at(0); else newFib = extractor->GetPositives().at(0); if (newFib.IsNull() || newFib->GetNumFibers()<=0) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); continue; } DataNode::Pointer newNode = DataNode::New(); newNode->SetData(newFib); if (invert) { name += "_not"; if (onlyEnds) name += "-ending-in-mask"; else name += "-passing-mask"; } else { if (onlyEnds) name += "_ending-in-mask"; else name += "_passing-mask"; } newNode->SetName(name.toStdString()); GetDataStorage()->Add(newNode); node->SetVisibility(false); } } void QmitkFiberProcessingView::GenerateRoiImage() { if (m_SelectedPF.empty()) return; mitk::BaseGeometry::Pointer geometry; if (!m_SelectedFB.empty()) { mitk::FiberBundle::Pointer fib = dynamic_cast(m_SelectedFB.front()->GetData()); geometry = fib->GetGeometry(); } else if (m_SelectedImage) geometry = m_SelectedImage->GetGeometry(); else return; itk::Vector spacing = geometry->GetSpacing(); spacing /= m_UpsamplingFactor; mitk::Point3D newOrigin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); newOrigin[0] += bounds.GetElement(0); newOrigin[1] += bounds.GetElement(2); newOrigin[2] += bounds.GetElement(4); itk::Matrix direction; itk::ImageRegion<3> imageRegion; for (int i=0; i<3; i++) for (int j=0; j<3; j++) direction[j][i] = geometry->GetMatrixColumn(i)[j]/spacing[j]; imageRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); imageRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); imageRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); m_PlanarFigureImage = ItkUCharImageType::New(); m_PlanarFigureImage->SetSpacing( spacing ); // Set the image spacing m_PlanarFigureImage->SetOrigin( newOrigin ); // Set the image origin m_PlanarFigureImage->SetDirection( direction ); // Set the image direction m_PlanarFigureImage->SetRegions( imageRegion ); m_PlanarFigureImage->Allocate(); m_PlanarFigureImage->FillBuffer( 0 ); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); std::string name = m_SelectedPF.at(0)->GetName(); WritePfToImage(m_SelectedPF.at(0), tmpImage); for (unsigned int i=1; iGetName(); WritePfToImage(m_SelectedPF.at(i), tmpImage); } DataNode::Pointer node = DataNode::New(); tmpImage = Image::New(); tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); node->SetData(tmpImage); node->SetName(name); this->GetDataStorage()->Add(node); } void QmitkFiberProcessingView::WritePfToImage(mitk::DataNode::Pointer node, mitk::Image* image) { if (dynamic_cast(node->GetData())) { m_PlanarFigure = dynamic_cast(node->GetData()); AccessFixedDimensionByItk_2( image, InternalReorientImagePlane, 3, m_PlanarFigure->GetGeometry(), -1); AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateMaskFromPlanarFigure, 3, 2, node->GetName() ); } else if (dynamic_cast(node->GetData())) { DataStorage::SetOfObjects::ConstPointer children = GetDataStorage()->GetDerivations(node); for (unsigned int i=0; iSize(); i++) { WritePfToImage(children->at(i), image); } } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalReorientImagePlane( const itk::Image< TPixel, VImageDimension > *image, mitk::BaseGeometry* planegeo3D, int additionalIndex ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< float, VImageDimension > FloatImageType; typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); mitk::PlaneGeometry* planegeo = dynamic_cast(planegeo3D); float upsamp = m_UpsamplingFactor; float gausssigma = 0.5; // Spacing typename ResamplerType::SpacingType spacing = planegeo->GetSpacing(); spacing[0] = image->GetSpacing()[0] / upsamp; spacing[1] = image->GetSpacing()[1] / upsamp; spacing[2] = image->GetSpacing()[2]; resampler->SetOutputSpacing( spacing ); // Size typename ResamplerType::SizeType size; size[0] = planegeo->GetExtentInMM(0) / spacing[0]; size[1] = planegeo->GetExtentInMM(1) / spacing[1]; size[2] = 1; resampler->SetSize( size ); // Origin typename mitk::Point3D orig = planegeo->GetOrigin(); typename mitk::Point3D corrorig; planegeo3D->WorldToIndex(orig,corrorig); corrorig[0] += 0.5/upsamp; corrorig[1] += 0.5/upsamp; corrorig[2] += 0; planegeo3D->IndexToWorld(corrorig,corrorig); resampler->SetOutputOrigin(corrorig ); // Direction typename ResamplerType::DirectionType direction; typename mitk::AffineTransform3D::MatrixType matrix = planegeo->GetIndexToWorldTransform()->GetMatrix(); for(unsigned int c=0; cSetOutputDirection( direction ); // Gaussian interpolation if(gausssigma != 0) { double sigma[3]; for( unsigned int d = 0; d < 3; d++ ) sigma[d] = gausssigma * image->GetSpacing()[d]; double alpha = 2.0; typedef itk::GaussianInterpolateImageFunction GaussianInterpolatorType; typename GaussianInterpolatorType::Pointer interpolator = GaussianInterpolatorType::New(); interpolator->SetInputImage( image ); interpolator->SetParameters( sigma, alpha ); resampler->SetInterpolator( interpolator ); } else { typedef typename itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( image ); resampler->SetInterpolator( interpolator ); } resampler->SetInput( image ); resampler->SetDefaultPixelValue(0); resampler->Update(); if(additionalIndex < 0) { this->m_InternalImage = mitk::Image::New(); this->m_InternalImage->InitializeByItk( resampler->GetOutput() ); this->m_InternalImage->SetVolume( resampler->GetOutput()->GetBufferPointer() ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalCalculateMaskFromPlanarFigure( itk::Image< TPixel, VImageDimension > *image, unsigned int axis, std::string ) { typedef itk::Image< TPixel, VImageDimension > ImageType; // Generate mask image as new image with same header as input image and // initialize with "1". ItkUCharImageType::Pointer newMaskImage = ItkUCharImageType::New(); newMaskImage->SetSpacing( image->GetSpacing() ); // Set the image spacing newMaskImage->SetOrigin( image->GetOrigin() ); // Set the image origin newMaskImage->SetDirection( image->GetDirection() ); // Set the image direction newMaskImage->SetRegions( image->GetLargestPossibleRegion() ); newMaskImage->Allocate(); newMaskImage->FillBuffer( 1 ); // Generate VTK polygon from (closed) PlanarFigure polyline // (The polyline points are shifted by -0.5 in z-direction to make sure // that the extrusion filter, which afterwards elevates all points by +0.5 // in z-direction, creates a 3D object which is cut by the the plane z=0) const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const BaseGeometry *imageGeometry3D = m_InternalImage->GetGeometry( 0 ); vtkPolyData *polyline = vtkPolyData::New(); polyline->Allocate( 1, 1 ); // Determine x- and y-dimensions depending on principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // Create VTK polydata object of polyline contour vtkPoints *points = vtkPoints::New(); PlanarFigure::PolyLineType::const_iterator it; unsigned int numberOfPoints = 0; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected image Point2D point2D = *it; planarFigurePlaneGeometry->WorldToIndex(point2D, point2D); point2D[0] -= 0.5/m_UpsamplingFactor; point2D[1] -= 0.5/m_UpsamplingFactor; planarFigurePlaneGeometry->IndexToWorld(point2D, point2D); planarFigurePlaneGeometry->Map( point2D, point3D ); // Polygons (partially) outside of the image bounds can not be processed further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { float bounds[2] = {0,0}; bounds[0] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i0); bounds[1] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i1); imageGeometry3D->WorldToIndex( point3D, point3D ); if (point3D[i0]<0) point3D[i0] = 0.0; else if (point3D[i0]>bounds[0]) point3D[i0] = bounds[0]-0.001; if (point3D[i1]<0) point3D[i1] = 0.0; else if (point3D[i1]>bounds[1]) point3D[i1] = bounds[1]-0.001; points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } else { imageGeometry3D->WorldToIndex( point3D, point3D ); // Add point to polyline array points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } } polyline->SetPoints( points ); points->Delete(); vtkIdType *ptIds = new vtkIdType[numberOfPoints]; for ( vtkIdType i = 0; i < numberOfPoints; ++i ) ptIds[i] = i; polyline->InsertNextCell( VTK_POLY_LINE, numberOfPoints, ptIds ); // Extrude the generated contour polygon vtkLinearExtrusionFilter *extrudeFilter = vtkLinearExtrusionFilter::New(); extrudeFilter->SetInputData( polyline ); extrudeFilter->SetScaleFactor( 1 ); extrudeFilter->SetExtrusionTypeToNormalExtrusion(); extrudeFilter->SetVector( 0.0, 0.0, 1.0 ); // Make a stencil from the extruded polygon vtkPolyDataToImageStencil *polyDataToImageStencil = vtkPolyDataToImageStencil::New(); polyDataToImageStencil->SetInputConnection( extrudeFilter->GetOutputPort() ); // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< ItkUCharImageType > ImageImportType; typedef itk::VTKImageExport< ItkUCharImageType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( newMaskImage ); vtkImageImport *vtkImporter = vtkImageImport::New(); this->ConnectPipelines( itkExporter, vtkImporter ); vtkImporter->Update(); // Apply the generated image stencil to the input image vtkImageStencil *imageStencilFilter = vtkImageStencil::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(polyDataToImageStencil->GetOutputPort() ); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); // Export from VTK back to ITK vtkImageExport *vtkExporter = vtkImageExport::New(); vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); // calculate cropping bounding box m_InternalImageMask3D = itkImporter->GetOutput(); m_InternalImageMask3D->SetDirection(image->GetDirection()); itk::ImageRegionConstIterator itmask(m_InternalImageMask3D, m_InternalImageMask3D->GetLargestPossibleRegion()); itk::ImageRegionIterator itimage(image, image->GetLargestPossibleRegion()); itmask.GoToBegin(); itimage.GoToBegin(); typename ImageType::SizeType lowersize = {{itk::NumericTraits::max(),itk::NumericTraits::max(),itk::NumericTraits::max()}}; typename ImageType::SizeType uppersize = {{0,0,0}}; while( !itmask.IsAtEnd() ) { if(itmask.Get() == 0) itimage.Set(0); else { typename ImageType::IndexType index = itimage.GetIndex(); typename ImageType::SizeType signedindex; signedindex[0] = index[0]; signedindex[1] = index[1]; signedindex[2] = index[2]; lowersize[0] = signedindex[0] < lowersize[0] ? signedindex[0] : lowersize[0]; lowersize[1] = signedindex[1] < lowersize[1] ? signedindex[1] : lowersize[1]; lowersize[2] = signedindex[2] < lowersize[2] ? signedindex[2] : lowersize[2]; uppersize[0] = signedindex[0] > uppersize[0] ? signedindex[0] : uppersize[0]; uppersize[1] = signedindex[1] > uppersize[1] ? signedindex[1] : uppersize[1]; uppersize[2] = signedindex[2] > uppersize[2] ? signedindex[2] : uppersize[2]; } ++itmask; ++itimage; } typename ImageType::IndexType index; index[0] = lowersize[0]; index[1] = lowersize[1]; index[2] = lowersize[2]; typename ImageType::SizeType size; size[0] = uppersize[0] - lowersize[0] + 1; size[1] = uppersize[1] - lowersize[1] + 1; size[2] = uppersize[2] - lowersize[2] + 1; itk::ImageRegion<3> cropRegion = itk::ImageRegion<3>(index, size); // crop internal mask typedef itk::RegionOfInterestImageFilter< ItkUCharImageType, ItkUCharImageType > ROIMaskFilterType; typename ROIMaskFilterType::Pointer roi2 = ROIMaskFilterType::New(); roi2->SetRegionOfInterest(cropRegion); roi2->SetInput(m_InternalImageMask3D); roi2->Update(); m_InternalImageMask3D = roi2->GetOutput(); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_InternalImageMask3D.GetPointer()); tmpImage->SetVolume(m_InternalImageMask3D->GetBufferPointer()); Image::Pointer tmpImage2 = Image::New(); tmpImage2->InitializeByItk(m_PlanarFigureImage.GetPointer()); const BaseGeometry *pfImageGeometry3D = tmpImage2->GetGeometry( 0 ); const BaseGeometry *intImageGeometry3D = tmpImage->GetGeometry( 0 ); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType imageIterator (m_InternalImageMask3D, m_InternalImageMask3D->GetRequestedRegion()); imageIterator.GoToBegin(); while ( !imageIterator.IsAtEnd() ) { unsigned char val = imageIterator.Value(); if (val>0) { itk::Index<3> index = imageIterator.GetIndex(); Point3D point; point[0] = index[0]; point[1] = index[1]; point[2] = index[2]; intImageGeometry3D->IndexToWorld(point, point); pfImageGeometry3D->WorldToIndex(point, point); point[i0] += 0.5; point[i1] += 0.5; index[0] = point[0]; index[1] = point[1]; index[2] = point[2]; if (pfImageGeometry3D->IsIndexInside(index)) m_PlanarFigureImage->SetPixel(index, 1); } ++imageIterator; } // Clean up VTK objects polyline->Delete(); extrudeFilter->Delete(); polyDataToImageStencil->Delete(); vtkImporter->Delete(); imageStencilFilter->Delete(); //vtkExporter->Delete(); // TODO: crashes when outcommented; memory leak?? delete[] ptIds; } void QmitkFiberProcessingView::UpdateGui() { m_Controls->m_FibLabel->setText("mandatory"); m_Controls->m_PfLabel->setText("needed for extraction"); m_Controls->m_InputData->setTitle("Please Select Input Data"); m_Controls->m_RemoveButton->setEnabled(false); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->PFCompoANDButton->setEnabled(false); m_Controls->PFCompoORButton->setEnabled(false); m_Controls->PFCompoNOTButton->setEnabled(false); m_Controls->m_GenerateRoiImage->setEnabled(false); m_Controls->m_ExtractFibersButton->setEnabled(false); m_Controls->m_ModifyButton->setEnabled(false); m_Controls->m_CopyBundle->setEnabled(false); m_Controls->m_JoinBundles->setEnabled(false); m_Controls->m_SubstractBundles->setEnabled(false); // disable alle frames m_Controls->m_BundleWeightFrame->setVisible(false); m_Controls->m_ExtactionFramePF->setVisible(false); m_Controls->m_RemoveDirectionFrame->setVisible(false); m_Controls->m_RemoveLengthFrame->setVisible(false); m_Controls->m_RemoveCurvatureFrame->setVisible(false); m_Controls->m_RemoveByWeightFrame->setVisible(false); m_Controls->m_SmoothFibersFrame->setVisible(false); m_Controls->m_CompressFibersFrame->setVisible(false); m_Controls->m_ColorFibersFrame->setVisible(false); m_Controls->m_MirrorFibersFrame->setVisible(false); m_Controls->m_MaskExtractionFrame->setVisible(false); m_Controls->m_ColorMapBox->setVisible(false); bool pfSelected = !m_SelectedPF.empty(); bool fibSelected = !m_SelectedFB.empty(); bool multipleFibsSelected = (m_SelectedFB.size()>1); bool maskSelected = m_RoiImageNode.IsNotNull(); bool imageSelected = m_SelectedImage.IsNotNull(); // toggle visibility of elements according to selected method switch ( m_Controls->m_ExtractionMethodBox->currentIndex() ) { case 0: m_Controls->m_ExtactionFramePF->setVisible(true); break; case 1: m_Controls->m_MaskExtractionFrame->setVisible(true); break; } switch ( m_Controls->m_RemovalMethodBox->currentIndex() ) { case 0: m_Controls->m_RemoveDirectionFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; case 1: m_Controls->m_RemoveLengthFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; case 2: m_Controls->m_RemoveCurvatureFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; case 3: break; case 4: break; case 5: m_Controls->m_RemoveByWeightFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; } switch ( m_Controls->m_ModificationMethodBox->currentIndex() ) { case 0: m_Controls->m_SmoothFibersFrame->setVisible(true); break; case 1: m_Controls->m_SmoothFibersFrame->setVisible(true); break; case 2: m_Controls->m_CompressFibersFrame->setVisible(true); break; case 3: m_Controls->m_ColorFibersFrame->setVisible(true); m_Controls->m_ColorMapBox->setVisible(true); break; case 4: m_Controls->m_MirrorFibersFrame->setVisible(true); if (m_SelectedSurfaces.size()>0) m_Controls->m_ModifyButton->setEnabled(true); break; case 5: m_Controls->m_BundleWeightFrame->setVisible(true); break; case 6: m_Controls->m_ColorFibersFrame->setVisible(true); break; case 7: m_Controls->m_ColorFibersFrame->setVisible(true); break; } // are fiber bundles selected? if ( fibSelected ) { m_Controls->m_CopyBundle->setEnabled(true); m_Controls->m_ModifyButton->setEnabled(true); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); m_Controls->m_FibLabel->setText(QString(m_SelectedFB.at(0)->GetName().c_str())); // one bundle and one planar figure needed to extract fibers if (pfSelected && m_Controls->m_ExtractionMethodBox->currentIndex()==0) { m_Controls->m_InputData->setTitle("Input Data"); m_Controls->m_PfLabel->setText(QString(m_SelectedPF.at(0)->GetName().c_str())); m_Controls->m_ExtractFibersButton->setEnabled(true); } // more than two bundles needed to join/subtract if (multipleFibsSelected) { m_Controls->m_FibLabel->setText("multiple bundles selected"); m_Controls->m_JoinBundles->setEnabled(true); m_Controls->m_SubstractBundles->setEnabled(true); } if (maskSelected && m_Controls->m_ExtractionMethodBox->currentIndex()==1) { m_Controls->m_InputData->setTitle("Input Data"); m_Controls->m_PfLabel->setText(QString(m_RoiImageNode->GetName().c_str())); m_Controls->m_ExtractFibersButton->setEnabled(true); } if (maskSelected && (m_Controls->m_RemovalMethodBox->currentIndex()==3 || m_Controls->m_RemovalMethodBox->currentIndex()==4) ) { m_Controls->m_InputData->setTitle("Input Data"); m_Controls->m_PfLabel->setText(QString(m_RoiImageNode->GetName().c_str())); m_Controls->m_RemoveButton->setEnabled(true); } } // are planar figures selected? if (pfSelected) { if ( fibSelected || m_SelectedImage.IsNotNull()) m_Controls->m_GenerateRoiImage->setEnabled(true); if (m_SelectedPF.size() > 1) { m_Controls->PFCompoANDButton->setEnabled(true); m_Controls->PFCompoORButton->setEnabled(true); } else m_Controls->PFCompoNOTButton->setEnabled(true); } // is image selected if (imageSelected || maskSelected) { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); } } void QmitkFiberProcessingView::NodeRemoved(const mitk::DataNode* node ) { for (auto fnode: m_SelectedFB) if (node == fnode) { m_SelectedFB.clear(); break; } berry::IWorkbenchPart::Pointer nullPart; QList nodes; OnSelectionChanged(nullPart, nodes); } void QmitkFiberProcessingView::NodeAdded(const mitk::DataNode* ) { if (!m_Controls->m_InteractiveBox->isChecked()) { berry::IWorkbenchPart::Pointer nullPart; QList nodes; OnSelectionChanged(nullPart, nodes); } } void QmitkFiberProcessingView::OnEndInteraction() { if (m_Controls->m_InteractiveBox->isChecked()) ExtractWithPlanarFigure(true); } void QmitkFiberProcessingView::AddObservers() { typedef itk::SimpleMemberCommand< QmitkFiberProcessingView > SimpleCommandType; for (auto node : m_SelectedPF) { mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); if (figure!=nullptr) { figure->RemoveAllObservers(); // add observer for event when interaction with figure starts SimpleCommandType::Pointer endInteractionCommand = SimpleCommandType::New(); endInteractionCommand->SetCallbackFunction( this, &QmitkFiberProcessingView::OnEndInteraction); m_EndInteractionObserverTag = figure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand ); } } } void QmitkFiberProcessingView::RemoveObservers() { for (auto node : m_SelectedPF) { mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); if (figure!=nullptr) figure->RemoveAllObservers(); } } void QmitkFiberProcessingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { RemoveObservers(); //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection std::vector lastSelectedFB = m_SelectedFB; m_SelectedFB.clear(); m_SelectedPF.clear(); m_SelectedSurfaces.clear(); m_SelectedImage = nullptr; m_RoiImageNode = nullptr; for (auto node: nodes) { if ( dynamic_cast(node->GetData()) ) m_SelectedFB.push_back(node); else if (dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || dynamic_cast(node->GetData())) m_SelectedPF.push_back(node); else if (dynamic_cast(node->GetData())) { m_SelectedImage = dynamic_cast(node->GetData()); if (m_SelectedImage->GetDimension()==3) m_RoiImageNode = node; } else if (dynamic_cast(node->GetData())) m_SelectedSurfaces.push_back(dynamic_cast(node->GetData())); } // if we perform interactive fiber extraction, we want to avoid auto-selection of the extracted bundle if (m_SelectedFB.empty() && m_Controls->m_InteractiveBox->isChecked()) m_SelectedFB = lastSelectedFB; // if no fibers or surfaces are selected, select topmost if (m_SelectedFB.empty() && m_SelectedSurfaces.empty()) { int maxLayer = 0; itk::VectorContainer::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; iSize(); i++) if (dynamic_cast(nodes->at(i)->GetData())) { mitk::DataStorage::SetOfObjects::ConstPointer sources = GetDataStorage()->GetSources(nodes->at(i)); if (sources->Size()>0) continue; int layer = 0; nodes->at(i)->GetPropertyValue("layer", layer); if (layer>=maxLayer) { maxLayer = layer; m_SelectedFB.clear(); m_SelectedFB.push_back(nodes->at(i)); } } } // if no plar figure is selected, select topmost if (m_SelectedPF.empty()) { int maxLayer = 0; itk::VectorContainer::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; iSize(); i++) if (dynamic_cast(nodes->at(i)->GetData()) || dynamic_cast(nodes->at(i)->GetData()) || dynamic_cast(nodes->at(i)->GetData())) { mitk::DataStorage::SetOfObjects::ConstPointer sources = GetDataStorage()->GetSources(nodes->at(i)); if (sources->Size()>0) continue; int layer = 0; nodes->at(i)->GetPropertyValue("layer", layer); if (layer>=maxLayer) { maxLayer = layer; m_SelectedPF.clear(); m_SelectedPF.push_back(nodes->at(i)); } } } AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::OnDrawPolygon() { mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); } void QmitkFiberProcessingView::OnDrawCircle() { mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_CircleCounter)); } void QmitkFiberProcessingView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *, mitk::BaseProperty* ) { // initialize figure's geometry with empty geometry mitk::PlaneGeometry::Pointer emptygeometry = mitk::PlaneGeometry::New(); figure->SetPlaneGeometry( emptygeometry ); //set desired data to DataNode where Planarfigure is stored mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); newNode->SetBoolProperty("planarfigure.3drendering", true); newNode->SetBoolProperty("planarfigure.3drendering.fill", true); mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(newNode->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(newNode); } // figure drawn on the topmost layer / image GetDataStorage()->Add(newNode ); RemoveObservers(); for(unsigned int i = 0; i < m_SelectedPF.size(); i++) m_SelectedPF[i]->SetSelected(false); newNode->SetSelected(true); m_SelectedPF.clear(); m_SelectedPF.push_back(newNode); AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::ExtractWithPlanarFigure(bool interactive) { if ( m_SelectedFB.empty() || m_SelectedPF.empty() ){ QMessageBox::information( nullptr, "Warning", "No fibe bundle selected!"); return; } try { std::vector fiberBundles = m_SelectedFB; mitk::DataNode::Pointer planarFigure = m_SelectedPF.at(0); for (unsigned int i=0; i(fiberBundles.at(i)->GetData()); mitk::FiberBundle::Pointer extFB = fib->ExtractFiberSubset(planarFigure, GetDataStorage()); if (interactive && 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); } float op = 5.0/sqrt(fib->GetNumFibers()); float currentOp = 0; fiberBundles.at(i)->GetFloatProperty("opacity", currentOp); if (currentOp!=op) { fib->SetFiberColors(255, 255, 255); fiberBundles.at(i)->SetFloatProperty("opacity", op); fiberBundles.at(i)->SetBoolProperty("Fiber2DfadeEFX", false); } m_InteractiveNode->SetData(extFB); } else { if (extFB->GetNumFibers()<=0) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); continue; } mitk::DataNode::Pointer node; node = mitk::DataNode::New(); node->SetData(extFB); QString name(fiberBundles.at(i)->GetName().c_str()); name += "*"; node->SetName(name.toStdString()); fiberBundles.at(i)->SetVisibility(false); GetDataStorage()->Add(node); } } } catch(const std::out_of_range& ) { QMessageBox::warning( nullptr, "Fiber extraction failed", "Did you only create the planar figure, using the circle or polygon button, but forgot to actually place it in the image afterwards? \nAfter creating a planar figure, simply left-click at the desired position in the image or on the tractogram to place it."); } } void QmitkFiberProcessingView::GenerateAndComposite() { mitk::PlanarFigureComposite::Pointer PFCAnd = mitk::PlanarFigureComposite::New(); PFCAnd->setOperationType(mitk::PlanarFigureComposite::AND); mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName("AND"); newPFCNode->SetData(PFCAnd); AddCompositeToDatastorage(newPFCNode, m_SelectedPF); RemoveObservers(); m_SelectedPF.clear(); m_SelectedPF.push_back(newPFCNode); AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::GenerateOrComposite() { mitk::PlanarFigureComposite::Pointer PFCOr = mitk::PlanarFigureComposite::New(); PFCOr->setOperationType(mitk::PlanarFigureComposite::OR); mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName("OR"); newPFCNode->SetData(PFCOr); RemoveObservers(); AddCompositeToDatastorage(newPFCNode, m_SelectedPF); m_SelectedPF.clear(); m_SelectedPF.push_back(newPFCNode); UpdateGui(); } void QmitkFiberProcessingView::GenerateNotComposite() { mitk::PlanarFigureComposite::Pointer PFCNot = mitk::PlanarFigureComposite::New(); PFCNot->setOperationType(mitk::PlanarFigureComposite::NOT); mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName("NOT"); newPFCNode->SetData(PFCNot); RemoveObservers(); AddCompositeToDatastorage(newPFCNode, m_SelectedPF); m_SelectedPF.clear(); m_SelectedPF.push_back(newPFCNode); AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::AddCompositeToDatastorage(mitk::DataNode::Pointer pfc, std::vector children, mitk::DataNode::Pointer parentNode ) { pfc->SetSelected(true); if (parentNode.IsNotNull()) GetDataStorage()->Add(pfc, parentNode); else GetDataStorage()->Add(pfc); for (auto child : children) { if (dynamic_cast(child->GetData())) { mitk::DataNode::Pointer newChild; newChild = mitk::DataNode::New(); newChild->SetData(dynamic_cast(child->GetData())); newChild->SetName( child->GetName() ); newChild->SetBoolProperty("planarfigure.3drendering", true); newChild->SetBoolProperty("planarfigure.3drendering.fill", true); GetDataStorage()->Add(newChild, pfc); GetDataStorage()->Remove(child); } else if (dynamic_cast(child->GetData())) { mitk::DataNode::Pointer newChild; newChild = mitk::DataNode::New(); newChild->SetData(dynamic_cast(child->GetData())); newChild->SetName( child->GetName() ); std::vector< mitk::DataNode::Pointer > grandChildVector; mitk::DataStorage::SetOfObjects::ConstPointer grandchildren = GetDataStorage()->GetDerivations(child); for( mitk::DataStorage::SetOfObjects::const_iterator it = grandchildren->begin(); it != grandchildren->end(); ++it ) grandChildVector.push_back(*it); AddCompositeToDatastorage(newChild, grandChildVector, pfc); GetDataStorage()->Remove(child); } } UpdateGui(); } void QmitkFiberProcessingView::CopyBundles() { if ( m_SelectedFB.empty() ){ QMessageBox::information( nullptr, "Warning", "Select at least one fiber bundle!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least one fiber bundle!"; return; } for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); mitk::FiberBundle::Pointer newFib = fib->GetDeepCopy(); node->SetVisibility(false); QString name(""); name += QString(m_SelectedFB.at(0)->GetName().c_str()); name += "_copy"; mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newFib); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } UpdateGui(); } void QmitkFiberProcessingView::JoinBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( nullptr, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } m_SelectedFB.at(0)->SetVisibility(false); mitk::FiberBundle::Pointer newBundle = dynamic_cast(m_SelectedFB.at(0)->GetData()); std::vector< mitk::FiberBundle::Pointer > tractograms; for (unsigned int i=1; iSetVisibility(false); tractograms.push_back(dynamic_cast(m_SelectedFB.at(i)->GetData())); } newBundle = newBundle->AddBundles(tractograms); mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName("Joined_Tractograms"); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); UpdateGui(); } void QmitkFiberProcessingView::SubstractBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( nullptr, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } mitk::FiberBundle::Pointer newBundle = dynamic_cast(m_SelectedFB.at(0)->GetData()); m_SelectedFB.at(0)->SetVisibility(false); QString name(""); name += QString(m_SelectedFB.at(0)->GetName().c_str()); for (unsigned int i=1; iSubtractBundle(dynamic_cast(m_SelectedFB.at(i)->GetData())); if (newBundle.IsNull()) break; name += "-"+QString(m_SelectedFB.at(i)->GetName().c_str()); m_SelectedFB.at(i)->SetVisibility(false); } if (newBundle.IsNull()) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers. Did you select the fiber bundles in the correct order? X-Y is not equal to Y-X!"); return; } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); UpdateGui(); } void QmitkFiberProcessingView::ResampleSelectedBundlesSpline() { double factor = this->m_Controls->m_SmoothFibersBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ResampleSpline(factor); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ResampleSelectedBundlesLinear() { double factor = this->m_Controls->m_SmoothFibersBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ResampleLinear(factor); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::CompressSelectedBundles() { double factor = this->m_Controls->m_ErrorThresholdBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->Compress(factor); fib->ColorFibersByOrientation(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::DoImageColorCoding() { if (m_Controls->m_ColorMapBox->GetSelectedNode().IsNull()) { QMessageBox::information(nullptr, "Bundle coloring aborted:", "No image providing the scalar values for coloring the selected bundle available."); return; } for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ColorFibersByScalarMap(dynamic_cast(m_Controls->m_ColorMapBox->GetSelectedNode()->GetData()), m_Controls->m_FiberOpacityBox->isChecked(), m_Controls->m_NormalizeColorValues->isChecked()); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkFiberProcessingView::DoCurvatureColorCoding() { for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ColorFibersByCurvature(m_Controls->m_FiberOpacityBox->isChecked(), m_Controls->m_NormalizeColorValues->isChecked()); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkFiberProcessingView::DoWeightColorCoding() { for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ColorFibersByFiberWeights(m_Controls->m_FiberOpacityBox->isChecked(), m_Controls->m_NormalizeColorValues->isChecked()); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkFiberProcessingView::MirrorFibers() { unsigned int axis = this->m_Controls->m_MirrorFibersBox->currentIndex(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); if (m_SelectedImage.IsNotNull()) fib->SetReferenceGeometry(m_SelectedImage->GetGeometry()); fib->MirrorFibers(axis); } for (auto surf : m_SelectedSurfaces) { vtkSmartPointer poly = surf->GetVtkPolyData(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); for (int i=0; iGetNumberOfPoints(); i++) { double* point = poly->GetPoint(i); point[axis] *= -1; vtkNewPoints->InsertNextPoint(point); } poly->SetPoints(vtkNewPoints); surf->CalculateBoundingBox(); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } 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 cd1cd24e86..4bbd7bb442 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,499 +1,498 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 // Qmitk #include "QmitkFiberQuantificationView.h" // Qt #include // MITK #include #include #include #include #include #include #include #include // ITK #include #include #include #include -#include #include 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::Pointer isFib = mitk::TNodePredicateDataType::New(); m_Controls->m_TractBox->SetPredicate( isFib ); m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_ImageBox->SetZeroEntryText("--"); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::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; QList selection = GetDataManagerSelection(); berry::IWorkbenchPart::Pointer nullPart; OnSelectionChanged(nullPart, selection); } void QmitkFiberQuantificationView::Hidden() { m_Visible = false; } void QmitkFiberQuantificationView::SetFocus() { m_Controls->m_ProcessFiberBundleButton->setFocus(); } void QmitkFiberQuantificationView::CalculateFiberDirections() { typedef itk::Image ItkUcharImgType; // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(m_SelectedFB.back()->GetData()); itk::TractsToVectorImageFilter::Pointer fOdfFilter = itk::TractsToVectorImageFilter::New(); if (m_SelectedImage.IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); mitk::CastToItkImage(m_SelectedImage, itkMaskImage); fOdfFilter->SetMaskImage(itkMaskImage); } // extract directions from fiber bundle fOdfFilter->SetFiberBundle(inputTractogram); - fOdfFilter->SetAngularThreshold(cos(m_Controls->m_AngularThreshold->value()*M_PI/180)); + fOdfFilter->SetAngularThreshold(cos(m_Controls->m_AngularThreshold->value()*itk::Math::pi/180)); switch (m_Controls->m_FiberDirNormBox->currentIndex()) { case 0: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::GLOBAL_MAX); break; case 1: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::SINGLE_VEC_NORM); break; case 2: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::MAX_VEC_NORM); break; } fOdfFilter->SetUseWorkingCopy(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(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(m_Controls->m_ImageBox->GetSelectedNode()->GetData()); m_Controls->m_ProcessFiberBundleButton->setEnabled(!m_SelectedFB.empty()); m_Controls->m_ExtractFiberPeaks->setEnabled(!m_SelectedFB.empty()); GenerateStats(); } void QmitkFiberQuantificationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& ) { UpdateGui(); } void QmitkFiberQuantificationView::GenerateStats() { if ( m_SelectedFB.empty() || !m_Visible ) return; QString stats(""); for( unsigned int i=0; i(node->GetData())) { if (i>0) stats += "\n-----------------------------\n"; stats += QString(node->GetName().c_str()) + "\n"; mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); stats += "Number of fibers: "+ QString::number(fib->GetNumFibers()) + "\n"; stats += "Number of points: "+ QString::number(fib->GetNumberOfPoints()) + "\n"; stats += "Min. length: "+ QString::number(fib->GetMinFiberLength(),'f',1) + " mm\n"; stats += "Max. length: "+ QString::number(fib->GetMaxFiberLength(),'f',1) + " mm\n"; stats += "Mean length: "+ QString::number(fib->GetMeanFiberLength(),'f',1) + " mm\n"; stats += "Median length: "+ QString::number(fib->GetMedianFiberLength(),'f',1) + " mm\n"; stats += "Standard deviation: "+ QString::number(fib->GetLengthStDev(),'f',1) + " mm\n"; vtkSmartPointer weights = fib->GetFiberWeights(); if (weights!=nullptr) { std::vector< float > weights2; for (int i=0; iGetSize(); i++) weights2.push_back(weights->GetValue(i)); std::sort(weights2.begin(), weights2.end()); stats += "\nFiber weight statistics\n"; stats += "Min: " + QString::number(weights2.front()) + "\n"; stats += "1% quantile: " + QString::number(weights2.at(weights2.size()*0.01)) + "\n"; stats += "5% quantile: " + QString::number(weights2.at(weights2.size()*0.05)) + "\n"; stats += "25% quantile: " + QString::number(weights2.at(weights2.size()*0.25)) + "\n"; stats += "Median: " + QString::number(weights2.at(weights2.size()*0.5)) + "\n"; stats += "75% quantile: " + QString::number(weights2.at(weights2.size()*0.75)) + "\n"; stats += "95% quantile: " + QString::number(weights2.at(weights2.size()*0.95)) + "\n"; stats += "99% quantile: " + QString::number(weights2.at(weights2.size()*0.99)) + "\n"; stats += "Max: " + QString::number(weights2.back()) + "\n"; } else stats += "No fiber weight array found.\n"; } } this->m_Controls->m_StatsTextEdit->setText(stats); } 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(node->GetData())) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); QString name(node->GetName().c_str()); DataNode::Pointer newNode = nullptr; switch(generationMethod){ case 0: newNode = GenerateTractDensityImage(fib, false, true); name += "_TDI"; break; case 1: newNode = GenerateTractDensityImage(fib, false, false); name += "_TDI"; break; case 2: newNode = GenerateTractDensityImage(fib, true, false); 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; } 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 fiberPolyData = fib->GetFiberPolyData(); int count = 0; int numFibers = fib->GetNumFibers(); for( int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (numPoints>0) { double* point = points->GetPoint(0); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } if (numPoints>2) { double* point = points->GetPoint(numPoints-1); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; 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 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 OutPixType; typedef itk::Image 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::Pointer itkImage = itk::Image::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image 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; } // generate tract density image from fiber bundle mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateTractDensityImage(mitk::FiberBundle::Pointer fib, bool binary, bool absolute) { mitk::DataNode::Pointer node = mitk::DataNode::New(); if (binary) { typedef unsigned char OutPixType; typedef itk::Image OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetBinaryOutput(binary); 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 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); mitk::Label::Pointer label = multilabelImage->GetActiveLabel(); label->SetName("Tractogram"); // label->SetColor(color); label->SetValue(1); // multilabelImage->GetActiveLabelSet()->AddLabel(label); multilabelImage->GetActiveLabelSet()->SetActiveLabel(1); PropertyList::Pointer dicomSegPropertyList = mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentationProperties(m_SelectedImage->GetPropertyList()); multilabelImage->GetPropertyList()->ConcatenatePropertyList(dicomSegPropertyList); mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentProperties(multilabelImage->GetActiveLabel(multilabelImage->GetActiveLayer())); // init data node node->SetData(multilabelImage); } else { // init data node node->SetData(img); } } else { typedef float OutPixType; typedef itk::Image OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetBinaryOutput(binary); 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 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.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp index e7a473ad33..5e9242917c 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp @@ -1,382 +1,380 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 #include // Blueberry #include #include // Qmitk #include "QmitkOdfMaximaExtractionView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt #include const std::string QmitkOdfMaximaExtractionView::VIEW_ID = "org.mitk.views.odfmaximaextractionview"; using namespace mitk; QmitkOdfMaximaExtractionView::QmitkOdfMaximaExtractionView() : m_Controls(nullptr) { } // Destructor QmitkOdfMaximaExtractionView::~QmitkOdfMaximaExtractionView() { } void QmitkOdfMaximaExtractionView::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::QmitkOdfMaximaExtractionViewControls; m_Controls->setupUi(parent); connect((QObject*)m_Controls->m_StartPeakExtractionButton, SIGNAL(clicked()), (QObject*) this, SLOT(StartPeakExtraction())); connect((QObject*)m_Controls->m_ImportShCoeffs, SIGNAL(clicked()), (QObject*) this, SLOT(ConvertShCoeffs())); m_Controls->m_MaskBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateNot::Pointer isDwi = mitk::NodePredicateNot::New(mitk::NodePredicateIsDWI::New()); mitk::NodePredicateNot::Pointer isOdf = mitk::NodePredicateNot::New(mitk::NodePredicateDataType::New("OdfImage")); mitk::NodePredicateAnd::Pointer unwanted = mitk::NodePredicateAnd::New(isOdf, isDwi); mitk::NodePredicateDimension::Pointer dim3 = mitk::NodePredicateDimension::New(3); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); m_Controls->m_MaskBox->SetPredicate(mitk::NodePredicateAnd::New(mitk::NodePredicateAnd::New(unwanted, dim3), isBinaryPredicate)); m_Controls->m_ImageBox->SetPredicate(mitk::NodePredicateAnd::New(mitk::NodePredicateAnd::New(unwanted, isMitkImage), mitk::NodePredicateNot::New(isBinaryPredicate))); m_Controls->m_MaskBox->SetZeroEntryText("--"); connect( (QObject*)(m_Controls->m_ImageBox), SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnImageSelectionChanged()) ); m_Controls->m_StartPeakExtractionButton->setVisible(false); m_Controls->m_ImportShCoeffs->setVisible(false); } } void QmitkOdfMaximaExtractionView::SetFocus() { } void QmitkOdfMaximaExtractionView::StartPeakExtraction() { if (dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData()) != nullptr) { StartTensorPeakExtraction(dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData())); } else { StartMaximaExtraction(dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData())); } } template void QmitkOdfMaximaExtractionView::TemplatedConvertShCoeffs(mitk::Image* mitkImg) { typedef itk::ShCoefficientImageImporter< float, shOrder > FilterType; typedef mitk::ImageToItk< itk::Image< float, 4 > > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkImg); caster->Update(); typename FilterType::Pointer filter = FilterType::New(); filter->SetInputImage(caster->GetOutput()); filter->GenerateData(); typename FilterType::CoefficientImageType::Pointer itkCi = filter->GetCoefficientImage(); { mitk::Image::Pointer img = dynamic_cast(mitk::ShImage::New().GetPointer()); img->InitializeByItk(itkCi.GetPointer()); img->SetVolume(itkCi->GetBufferPointer()); DataNode::Pointer node = DataNode::New(); node->SetData(img); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_ShImage_Imported"; node->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); } } void QmitkOdfMaximaExtractionView::ConvertShCoeffs() { if (m_Controls->m_ImageBox->GetSelectedNode().IsNull()) return; Image::Pointer mitkImg = dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData()); if (mitkImg->GetDimension() != 4 && mitkImg->GetLargestPossibleRegion().GetSize()[3]<6) { MITK_INFO << "wrong image type (need 4 dimensions)"; return; } int nrCoeffs = mitkImg->GetLargestPossibleRegion().GetSize()[3]; switch (nrCoeffs) { case 6: TemplatedConvertShCoeffs<2>(mitkImg); break; case 15: TemplatedConvertShCoeffs<4>(mitkImg); break; case 28: TemplatedConvertShCoeffs<6>(mitkImg); break; case 45: TemplatedConvertShCoeffs<8>(mitkImg); break; case 66: TemplatedConvertShCoeffs<10>(mitkImg); break; case 91: TemplatedConvertShCoeffs<12>(mitkImg); break; default : QMessageBox::warning(nullptr, "Error", "Only spherical harmonics orders 2-12 are supported.", QMessageBox::Ok); } } void QmitkOdfMaximaExtractionView::StartTensorPeakExtraction(mitk::TensorImage* img) { typedef itk::DiffusionTensorPrincipalDirectionImageFilter< float > MaximaExtractionFilterType; MaximaExtractionFilterType::Pointer filter = MaximaExtractionFilterType::New(); filter->SetUsePolarCoordinates(false); mitk::BaseGeometry::Pointer geometry; try{ ItkTensorImage::Pointer itkImage = ItkTensorImage::New(); CastToItkImage(img, itkImage); filter->SetInput(itkImage); geometry = img->GetGeometry(); } catch (itk::ExceptionObject &e) { MITK_INFO << "wrong image type: " << e.what(); QMessageBox::warning(nullptr, "Wrong pixel type", "Could not perform Tensor Principal Direction Extraction due to Image has wrong pixel type.", QMessageBox::Ok); return; } if (m_Controls->m_MaskBox->GetSelectedNode().IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); Image::Pointer mitkMaskImg = dynamic_cast(m_Controls->m_MaskBox->GetSelectedNode()->GetData()); CastToItkImage(mitkMaskImg, itkMaskImage); filter->SetMaskImage(itkMaskImage); } if (m_Controls->m_NormalizationBox->currentIndex() == 0) filter->SetNormalizeVectors(false); filter->SetFaThreshold(m_Controls->m_AbsoluteThresholdBox->value()); filter->Update(); MaximaExtractionFilterType::PeakImageType::Pointer itkImg = filter->GetPeakImage(); mitk::Image::Pointer mitkPeakImage = dynamic_cast(PeakImage::New().GetPointer()); CastToMitkImage(itkImg, mitkPeakImage); DataNode::Pointer node = DataNode::New(); node->SetData(mitkPeakImage); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_PrincipalDirection"; node->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); if (m_Controls->m_OutputNumDirectionsBox->isChecked()) { ItkUcharImgType::Pointer numDirImage = filter->GetOutput(); mitk::Image::Pointer image2 = mitk::Image::New(); image2->InitializeByItk(numDirImage.GetPointer()); image2->SetVolume(numDirImage->GetBufferPointer()); DataNode::Pointer node2 = DataNode::New(); node2->SetData(image2); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_NumDirections"; node2->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node2, m_Controls->m_ImageBox->GetSelectedNode()); } } template void QmitkOdfMaximaExtractionView::StartMaximaExtraction(Image *image) { typedef itk::FiniteDiffOdfMaximaExtractionFilter< float, shOrder, 10000 > MaximaExtractionFilterType; typename MaximaExtractionFilterType::Pointer filter = MaximaExtractionFilterType::New(); switch (m_Controls->m_ToolkitBox->currentIndex()) { case 0: filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); break; case 1: filter->SetToolkit(MaximaExtractionFilterType::FSL); break; default: filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); } mitk::BaseGeometry::Pointer geometry; try{ typedef ImageToItk< typename MaximaExtractionFilterType::CoefficientImageType > CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput(image); caster->Update(); filter->SetInput(caster->GetOutput()); geometry = image->GetGeometry(); } catch (itk::ExceptionObject &e) { MITK_INFO << "wrong image type: " << e.what(); QMessageBox::warning(nullptr, "Wrong pixel type", "Could not perform Finite Differences Extraction due to Image has wrong pixel type.", QMessageBox::Ok); return; } - filter->SetAngularThreshold(cos((float)m_Controls->m_AngularThreshold->value()*M_PI / 180)); - filter->SetClusteringThreshold(cos((float)m_Controls->m_ClusteringAngleBox->value()*M_PI / 180)); + filter->SetAngularThreshold(cos((float)m_Controls->m_AngularThreshold->value()*itk::Math::pi / 180)); + filter->SetClusteringThreshold(cos((float)m_Controls->m_ClusteringAngleBox->value()*itk::Math::pi / 180)); filter->SetMaxNumPeaks(m_Controls->m_MaxNumPeaksBox->value()); filter->SetPeakThreshold(m_Controls->m_PeakThresholdBox->value()); filter->SetAbsolutePeakThreshold(m_Controls->m_AbsoluteThresholdBox->value()); if (m_Controls->m_MaskBox->GetSelectedNode().IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); Image::Pointer mitkMaskImg = dynamic_cast(m_Controls->m_MaskBox->GetSelectedNode()->GetData()); CastToItkImage(mitkMaskImg, itkMaskImage); filter->SetMaskImage(itkMaskImage); } switch (m_Controls->m_NormalizationBox->currentIndex()) { case 0: filter->SetNormalizationMethod(MaximaExtractionFilterType::NO_NORM); break; case 1: filter->SetNormalizationMethod(MaximaExtractionFilterType::MAX_VEC_NORM); break; case 2: filter->SetNormalizationMethod(MaximaExtractionFilterType::SINGLE_VEC_NORM); break; } filter->Update(); typename MaximaExtractionFilterType::PeakImageType::Pointer itkImg = filter->GetPeakImage(); mitk::Image::Pointer img = dynamic_cast(PeakImage::New().GetPointer()); CastToMitkImage(itkImg, img); DataNode::Pointer node = DataNode::New(); node->SetData(img); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_PEAKS"; node->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); if (m_Controls->m_OutputNumDirectionsBox->isChecked()) { ItkUcharImgType::Pointer numDirImage = filter->GetNumDirectionsImage(); mitk::Image::Pointer image2 = mitk::Image::New(); CastToMitkImage(numDirImage, image2); DataNode::Pointer node2 = DataNode::New(); node2->SetData(image2); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_NUM_DIRECTIONS"; node2->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node2, m_Controls->m_ImageBox->GetSelectedNode()); } } void QmitkOdfMaximaExtractionView::StartMaximaExtraction(Image* img) { mitk::PixelType pixT = img->GetPixelType(); switch (pixT.GetNumberOfComponents()) { case 6: StartMaximaExtraction<2>(img); break; case 15: StartMaximaExtraction<4>(img); break; case 28: StartMaximaExtraction<6>(img); break; case 45: StartMaximaExtraction<8>(img); break; case 66: StartMaximaExtraction<10>(img); break; case 91: StartMaximaExtraction<12>(img); break; default : QMessageBox::warning(nullptr, "Error", "Only spherical harmonics orders 2-12 are supported.", QMessageBox::Ok); } } void QmitkOdfMaximaExtractionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer , const QList& nodes) { (void) nodes; this->OnImageSelectionChanged(); } void QmitkOdfMaximaExtractionView::OnImageSelectionChanged() { m_Controls->m_StartPeakExtractionButton->setVisible(false); m_Controls->m_ImportShCoeffs->setVisible(false); mitk::DataNode::Pointer node = m_Controls->m_ImageBox->GetSelectedNode(); if (node.IsNull()) return; Image::Pointer img = dynamic_cast(node->GetData()); if (img->GetDimension()==4) m_Controls->m_ImportShCoeffs->setVisible(true); else m_Controls->m_StartPeakExtractionButton->setVisible(true); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.partialvolume/src/internal/QmitkPartialVolumeAnalysisView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.partialvolume/src/internal/QmitkPartialVolumeAnalysisView.cpp index 7458c81cb4..c6a01f14c1 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.partialvolume/src/internal/QmitkPartialVolumeAnalysisView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.partialvolume/src/internal/QmitkPartialVolumeAnalysisView.cpp @@ -1,2144 +1,2139 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "QmitkPartialVolumeAnalysisView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QmitkSliderNavigatorWidget.h" #include #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkProgressBar.h" #include "mitkImageCast.h" #include "mitkImageToItk.h" #include "mitkITKImageImport.h" #include "mitkDataNodeObject.h" #include "mitkNodePredicateData.h" #include "mitkPlanarFigureInteractor.h" #include "mitkTensorImage.h" #include "mitkPlanarCircle.h" #include "mitkPlanarRectangle.h" #include "mitkPlanarPolygon.h" #include "mitkPartialVolumeAnalysisClusteringCalculator.h" #include "usModuleRegistry.h" #include #include "itkTensorDerivedMeasurementsFilter.h" #include "itkDiffusionTensor3D.h" #include "itkCartesianToPolarVectorImageFilter.h" #include "itkPolarToCartesianVectorImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkMaskImageFilter.h" #include "itkCastImageFilter.h" #include "itkImageMomentsCalculator.h" #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - -#define PVA_PI M_PI - const std::string QmitkPartialVolumeAnalysisView::VIEW_ID = "org.mitk.views.partialvolumeanalysisview"; class QmitkRequestStatisticsUpdateEvent : public QEvent { public: enum Type { StatisticsUpdateRequest = QEvent::MaxUser - 1025 }; QmitkRequestStatisticsUpdateEvent() : QEvent( (QEvent::Type) StatisticsUpdateRequest ) {}; }; typedef itk::Image ImageType; typedef itk::Image FloatImageType; typedef itk::Image, 3> VectorImageType; inline bool my_isnan(float x) { volatile float d = x; if(d!=d) return true; if(d==d) return false; return d != d; } QmitkPartialVolumeAnalysisView::QmitkPartialVolumeAnalysisView(QObject * /*parent*/, const char * /*name*/) : QmitkAbstractView(), m_Controls(nullptr), m_TimeStepperAdapter(nullptr), m_MeasurementInfoRenderer(nullptr), m_MeasurementInfoAnnotation(nullptr), m_IsTensorImage(false), m_SelectedRenderWindow(nullptr), m_LastRenderWindow(nullptr), m_ImageObserverTag(-1), m_ImageMaskObserverTag(-1), m_PlanarFigureObserverTag(-1), m_CurrentStatisticsValid(false), m_StatisticsUpdatePending(false), m_GaussianSigmaChangedSliding(false), m_NumberBinsSliding(false), m_UpsamplingChangedSliding(false), m_EllipseCounter(0), m_RectangleCounter(0), m_PolygonCounter(0), m_CurrentFigureNodeInitialized(false), m_QuantifyClass(2), m_IconTexOFF(new QIcon(":/QmitkPartialVolumeAnalysisView/texIntOFFIcon.png")), m_IconTexON(new QIcon(":/QmitkPartialVolumeAnalysisView/texIntONIcon.png")), m_TexIsOn(true), m_Visible(false) { } QmitkPartialVolumeAnalysisView::~QmitkPartialVolumeAnalysisView() { if ( m_SelectedImage.IsNotNull() ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask.IsNotNull() ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure.IsNotNull() ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_SelectedPlanarFigure->RemoveObserver( m_InitializedObserverTag ); } this->GetDataStorage()->AddNodeEvent -= mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage ); m_SelectedPlanarFigureNodes->NodeChanged.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedPlanarFigureNodes->NodeRemoved.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); m_SelectedPlanarFigureNodes->PropertyChanged.RemoveListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); m_SelectedImageNodes->NodeChanged.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedImageNodes->NodeRemoved.RemoveListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); m_SelectedImageNodes->PropertyChanged.RemoveListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); } void QmitkPartialVolumeAnalysisView::CreateQtPartControl(QWidget *parent) { if (m_Controls == nullptr) { m_Controls = new Ui::QmitkPartialVolumeAnalysisViewControls; m_Controls->setupUi(parent); this->CreateConnections(); } SetHistogramVisibility(); m_Controls->m_TextureIntON->setIcon(*m_IconTexON); m_Controls->m_SimilarAnglesFrame->setVisible(false); m_Controls->m_SimilarAnglesLabel->setVisible(false); vtkTextProperty *textProp = vtkTextProperty::New(); textProp->SetColor(1.0, 1.0, 1.0); m_MeasurementInfoAnnotation = vtkCornerAnnotation::New(); m_MeasurementInfoAnnotation->SetMaximumFontSize(12); m_MeasurementInfoAnnotation->SetTextProperty(textProp); m_MeasurementInfoRenderer = vtkRenderer::New(); m_MeasurementInfoRenderer->AddActor(m_MeasurementInfoAnnotation); m_SelectedPlanarFigureNodes = mitk::DataStorageSelection::New(this->GetDataStorage(), false); m_SelectedPlanarFigureNodes->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedPlanarFigureNodes->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); m_SelectedPlanarFigureNodes->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); m_SelectedImageNodes = mitk::DataStorageSelection::New(this->GetDataStorage(), false); m_SelectedImageNodes->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkPartialVolumeAnalysisView::PropertyChanged ) ); m_SelectedImageNodes->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeChanged ) ); m_SelectedImageNodes->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeRemoved ) ); this->GetDataStorage()->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage ) ); Select(nullptr,true,true); SetAdvancedVisibility(); } void QmitkPartialVolumeAnalysisView::SetFocus() { m_Controls->m_CircleButton->setFocus(); } void QmitkPartialVolumeAnalysisView::SetHistogramVisibility() { m_Controls->m_HistogramWidget->setVisible(m_Controls->m_DisplayHistogramCheckbox->isChecked()); } void QmitkPartialVolumeAnalysisView::SetAdvancedVisibility() { m_Controls->frame_7->setVisible(m_Controls->m_AdvancedCheckbox->isChecked()); } void QmitkPartialVolumeAnalysisView::CreateConnections() { if ( m_Controls ) { connect( m_Controls->m_DisplayHistogramCheckbox, SIGNAL( clicked() ) , this, SLOT( SetHistogramVisibility() ) ); connect( m_Controls->m_AdvancedCheckbox, SIGNAL( clicked() ) , this, SLOT( SetAdvancedVisibility() ) ); connect( m_Controls->m_NumberBinsSlider, SIGNAL( sliderReleased () ), this, SLOT( NumberBinsReleasedSlider( ) ) ); connect( m_Controls->m_UpsamplingSlider, SIGNAL( sliderReleased( ) ), this, SLOT( UpsamplingReleasedSlider( ) ) ); connect( m_Controls->m_GaussianSigmaSlider, SIGNAL( sliderReleased( ) ), this, SLOT( GaussianSigmaReleasedSlider( ) ) ); connect( m_Controls->m_SimilarAnglesSlider, SIGNAL( sliderReleased( ) ), this, SLOT( SimilarAnglesReleasedSlider( ) ) ); connect( m_Controls->m_NumberBinsSlider, SIGNAL( valueChanged (int) ), this, SLOT( NumberBinsChangedSlider( int ) ) ); connect( m_Controls->m_UpsamplingSlider, SIGNAL( valueChanged( int ) ), this, SLOT( UpsamplingChangedSlider( int ) ) ); connect( m_Controls->m_GaussianSigmaSlider, SIGNAL( valueChanged( int ) ), this, SLOT( GaussianSigmaChangedSlider( int ) ) ); connect( m_Controls->m_SimilarAnglesSlider, SIGNAL( valueChanged( int ) ), this, SLOT( SimilarAnglesChangedSlider(int) ) ); connect( m_Controls->m_OpacitySlider, SIGNAL( valueChanged( int ) ), this, SLOT( OpacityChangedSlider(int) ) ); connect( (QObject*)(m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(ToClipBoard())); connect( m_Controls->m_CircleButton, SIGNAL( clicked() ) , this, SLOT( ActionDrawEllipseTriggered() ) ); connect( m_Controls->m_RectangleButton, SIGNAL( clicked() ) , this, SLOT( ActionDrawRectangleTriggered() ) ); connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ) , this, SLOT( ActionDrawPolygonTriggered() ) ); connect( m_Controls->m_GreenRadio, SIGNAL( clicked(bool) ) , this, SLOT( GreenRadio(bool) ) ); connect( m_Controls->m_PartialVolumeRadio, SIGNAL( clicked(bool) ) , this, SLOT( PartialVolumeRadio(bool) ) ); connect( m_Controls->m_BlueRadio, SIGNAL( clicked(bool) ) , this, SLOT( BlueRadio(bool) ) ); connect( m_Controls->m_AllRadio, SIGNAL( clicked(bool) ) , this, SLOT( AllRadio(bool) ) ); connect( m_Controls->m_EstimateCircle, SIGNAL( clicked() ) , this, SLOT( EstimateCircle() ) ); connect( (QObject*)(m_Controls->m_TextureIntON), SIGNAL(clicked()), this, SLOT(TextIntON()) ); connect( m_Controls->m_ExportClusteringResultsButton, SIGNAL(clicked()), this, SLOT(ExportClusteringResults())); } } void QmitkPartialVolumeAnalysisView::ExportClusteringResults() { if (m_ClusteringResult.IsNull() || m_SelectedImage.IsNull()) return; mitk::BaseGeometry* geometry = m_SelectedImage->GetGeometry(); itk::Image< short, 3>::Pointer referenceImage = itk::Image< short, 3>::New(); itk::Vector newSpacing = geometry->GetSpacing(); mitk::Point3D newOrigin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); newOrigin[0] += bounds.GetElement(0); newOrigin[1] += bounds.GetElement(2); newOrigin[2] += bounds.GetElement(4); itk::Matrix newDirection; itk::ImageRegion<3> imageRegion; for (int i=0; i<3; i++) for (int j=0; j<3; j++) newDirection[j][i] = geometry->GetMatrixColumn(i)[j]/newSpacing[j]; imageRegion.SetSize(0, geometry->GetExtent(0)); imageRegion.SetSize(1, geometry->GetExtent(1)); imageRegion.SetSize(2, geometry->GetExtent(2)); // apply new image parameters referenceImage->SetSpacing( newSpacing ); referenceImage->SetOrigin( newOrigin ); referenceImage->SetDirection( newDirection ); referenceImage->SetRegions( imageRegion ); referenceImage->Allocate(); typedef itk::Image< float, 3 > OutType; mitk::Image::Pointer mitkInImage = dynamic_cast(m_ClusteringResult->GetData()); typedef itk::Image< itk::RGBAPixel, 3 > ItkRgbaImageType; typedef mitk::ImageToItk< ItkRgbaImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkInImage); caster->Update(); ItkRgbaImageType::Pointer itkInImage = caster->GetOutput(); typedef itk::ExtractChannelFromRgbaImageFilter< itk::Image< short, 3>, OutType > ExtractionFilterType; ExtractionFilterType::Pointer filter = ExtractionFilterType::New(); filter->SetInput(itkInImage); filter->SetChannel(ExtractionFilterType::ALPHA); filter->SetReferenceImage(referenceImage); filter->Update(); OutType::Pointer outImg = filter->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); node->SetName("Clustering Result"); GetDataStorage()->Add(node); } void QmitkPartialVolumeAnalysisView::EstimateCircle() { typedef itk::Image SegImageType; SegImageType::Pointer mask_itk = SegImageType::New(); typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(m_SelectedImageMask); caster->Update(); typedef itk::ImageMomentsCalculator< SegImageType > MomentsType; MomentsType::Pointer momentsCalc = MomentsType::New(); momentsCalc->SetImage(caster->GetOutput()); momentsCalc->Compute(); MomentsType::VectorType cog = momentsCalc->GetCenterOfGravity(); MomentsType::MatrixType axes = momentsCalc->GetPrincipalAxes(); // moments-coord conversion // third coordinate min oder max? // max-min = extent MomentsType::AffineTransformPointer trafo = momentsCalc->GetPhysicalAxesToPrincipalAxesTransform(); itk::ImageRegionIterator itimage(caster->GetOutput(), caster->GetOutput()->GetLargestPossibleRegion()); itimage.GoToBegin(); double max = -9999999999.0; double min = 9999999999.0; while( !itimage.IsAtEnd() ) { if(itimage.Get()) { ImageType::IndexType index = itimage.GetIndex(); itk::Point point; caster->GetOutput()->TransformIndexToPhysicalPoint(index,point); itk::Point newPoint; newPoint = trafo->TransformPoint(point); if(newPoint[2]max) max = newPoint[2]; } ++itimage; } double extent = max - min; MITK_DEBUG << "EXTENT = " << extent; mitk::Point3D origin; mitk::Vector3D right, bottom, normal; double factor = 1000.0; mitk::FillVector3D(origin, cog[0]-factor*axes[1][0]-factor*axes[2][0], cog[1]-factor*axes[1][1]-factor*axes[2][1], cog[2]-factor*axes[1][2]-factor*axes[2][2]); // mitk::FillVector3D(normal, axis[0][0],axis[0][1],axis[0][2]); mitk::FillVector3D(bottom, 2*factor*axes[1][0], 2*factor*axes[1][1], 2*factor*axes[1][2]); mitk::FillVector3D(right, 2*factor*axes[2][0], 2*factor*axes[2][1], 2*factor*axes[2][2]); mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector()); planegeometry->SetOrigin(origin); double len1 = sqrt(axes[1][0]*axes[1][0] + axes[1][1]*axes[1][1] + axes[1][2]*axes[1][2]); double len2 = sqrt(axes[2][0]*axes[2][0] + axes[2][1]*axes[2][1] + axes[2][2]*axes[2][2]); mitk::Point2D point1; point1[0] = factor*len1; point1[1] = factor*len2; mitk::Point2D point2; point2[0] = factor*len1+extent*.5; point2[1] = factor*len2; mitk::PlanarCircle::Pointer circle = mitk::PlanarCircle::New(); circle->SetPlaneGeometry(planegeometry); circle->PlaceFigure( point1 ); circle->SetControlPoint(0,point1); circle->SetControlPoint(1,point2); //circle->SetCurrentControlPoint( point2 ); mitk::PlanarFigure::PolyLineType polyline = circle->GetPolyLine( 0 ); MITK_DEBUG << "SIZE of planar figure polyline: " << polyline.size(); AddFigureToDataStorage(circle, "Circle"); } bool QmitkPartialVolumeAnalysisView::AssertDrawingIsPossible(bool checked) { if (m_SelectedImageNodes->GetNode().IsNull()) { checked = false; this->HandleException("Please select an image!", dynamic_cast(this->parent()), true); return false; } //this->GetRenderWindowPart(OPEN)->EnableSlicingPlanes(false); return checked; } void QmitkPartialVolumeAnalysisView::ActionDrawEllipseTriggered() { bool checked = m_Controls->m_CircleButton->isChecked(); if(!this->AssertDrawingIsPossible(checked)) return; mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); // using PV_ prefix for planar figures from this view // to distinguish them from that ones created throught the measurement view this->AddFigureToDataStorage(figure, QString("PV_Circle%1").arg(++m_EllipseCounter)); MITK_DEBUG << "PlanarCircle created ..."; } void QmitkPartialVolumeAnalysisView::ActionDrawRectangleTriggered() { bool checked = m_Controls->m_RectangleButton->isChecked(); if(!this->AssertDrawingIsPossible(checked)) return; mitk::PlanarRectangle::Pointer figure = mitk::PlanarRectangle::New(); // using PV_ prefix for planar figures from this view // to distinguish them from that ones created throught the measurement view this->AddFigureToDataStorage(figure, QString("PV_Rectangle%1").arg(++m_RectangleCounter)); MITK_DEBUG << "PlanarRectangle created ..."; } void QmitkPartialVolumeAnalysisView::ActionDrawPolygonTriggered() { bool checked = m_Controls->m_PolygonButton->isChecked(); if(!this->AssertDrawingIsPossible(checked)) return; mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); // using PV_ prefix for planar figures from this view // to distinguish them from that ones created throught the measurement view this->AddFigureToDataStorage(figure, QString("PV_Polygon%1").arg(++m_PolygonCounter)); MITK_DEBUG << "PlanarPolygon created ..."; } void QmitkPartialVolumeAnalysisView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *propertyKey, mitk::BaseProperty *property ) { mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); // Add custom property, if available if ( (propertyKey != nullptr) && (property != nullptr) ) { newNode->AddProperty( propertyKey, property ); } // figure drawn on the topmost layer / image this->GetDataStorage()->Add(newNode, m_SelectedImageNodes->GetNode() ); QList selectedNodes = this->GetDataManagerSelection(); for(int i = 0; i < selectedNodes.size(); ++i) { selectedNodes[i]->SetSelected(false); } std::vector selectedPFNodes = m_SelectedPlanarFigureNodes->GetNodes(); for(std::size_t i = 0; i < selectedPFNodes.size(); ++i) { selectedPFNodes[i]->SetSelected(false); } newNode->SetSelected(true); Select(newNode); } void QmitkPartialVolumeAnalysisView::PlanarFigureInitialized() { if(m_SelectedPlanarFigureNodes->GetNode().IsNull()) return; m_CurrentFigureNodeInitialized = true; this->Select(m_SelectedPlanarFigureNodes->GetNode()); m_Controls->m_CircleButton->setChecked(false); m_Controls->m_RectangleButton->setChecked(false); m_Controls->m_PolygonButton->setChecked(false); //this->GetRenderWindowPart(OPEN)->EnableSlicingPlanes(true); this->RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::PlanarFigureFocus(mitk::DataNode* node) { mitk::PlanarFigure* _PlanarFigure = 0; _PlanarFigure = dynamic_cast (node->GetData()); if (_PlanarFigure) { FindRenderWindow(node); const mitk::PlaneGeometry* _PlaneGeometry = _PlanarFigure->GetPlaneGeometry(); // make node visible if (m_SelectedRenderWindow) { mitk::Point3D centerP = _PlaneGeometry->GetOrigin(); m_SelectedRenderWindow->GetSliceNavigationController()->ReorientSlices( centerP, _PlaneGeometry->GetNormal()); m_SelectedRenderWindow->GetSliceNavigationController()->SelectSliceByPoint( centerP); } } } void QmitkPartialVolumeAnalysisView::FindRenderWindow(mitk::DataNode* node) { if (node && dynamic_cast (node->GetData())) { m_SelectedRenderWindow = 0; bool PlanarFigureInitializedWindow = false; foreach(QmitkRenderWindow * window, this->GetRenderWindowPart()->GetQmitkRenderWindows().values()) { if (!m_SelectedRenderWindow && node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, window->GetRenderer())) { m_SelectedRenderWindow = window; } } } } void QmitkPartialVolumeAnalysisView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList &nodes) { m_Controls->m_InputData->setTitle("Please Select Input Data"); if (!m_Visible) return; if ( nodes.empty() ) { if (m_ClusteringResult.IsNotNull()) { this->GetDataStorage()->Remove(m_ClusteringResult); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } Select(nullptr, true, true); } for (int i=0; iRemoveOrphanImages(); bool somethingChanged = false; if(node.IsNull()) { somethingChanged = true; if(clearMaskOnFirstArgnullptr) { if ( (m_SelectedImageMask.IsNotNull()) && (m_ImageMaskObserverTag >= 0) ) { m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); m_ImageMaskObserverTag = -1; } if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_PlanarFigureObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_PlanarFigureObserverTag = -1; } if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_InitializedObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_InitializedObserverTag ); m_InitializedObserverTag = -1; } m_SelectedPlanarFigure = nullptr; m_SelectedPlanarFigureNodes->RemoveAllNodes(); m_CurrentFigureNodeInitialized = false; m_SelectedRenderWindow = 0; m_SelectedMaskNode = nullptr; m_SelectedImageMask = nullptr; } if(clearImageOnFirstArgnullptr) { if ( (m_SelectedImage.IsNotNull()) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } m_SelectedImageNodes->RemoveAllNodes(); m_SelectedImage = nullptr; m_IsTensorImage = false; m_FAImage = nullptr; m_RDImage = nullptr; m_ADImage = nullptr; m_MDImage = nullptr; m_CAImage = nullptr; m_DirectionComp1Image = nullptr; m_DirectionComp2Image = nullptr; m_AngularErrorImage = nullptr; m_Controls->m_SimilarAnglesFrame->setVisible(false); m_Controls->m_SimilarAnglesLabel->setVisible(false); } } else { typedef itk::SimpleMemberCommand< QmitkPartialVolumeAnalysisView > ITKCommandType; ITKCommandType::Pointer changeListener; changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkPartialVolumeAnalysisView::RequestStatisticsUpdate ); // Get selected element mitk::TensorImage *selectedTensorImage = dynamic_cast< mitk::TensorImage * >( node->GetData() ); mitk::Image *selectedImage = dynamic_cast< mitk::Image * >( node->GetData() ); mitk::PlanarFigure *selectedPlanar = dynamic_cast< mitk::PlanarFigure * >( node->GetData() ); bool isMask = false; bool isImage = false; bool isPlanar = false; bool isTensorImage = false; if (selectedTensorImage != nullptr) { isTensorImage = true; } else if(selectedImage != nullptr) { node->GetPropertyValue("binary", isMask); isImage = !isMask; } else if ( (selectedPlanar != nullptr) ) { isPlanar = true; } // image if(isImage && selectedImage->GetDimension()==3) { if(selectedImage != m_SelectedImage.GetPointer()) { somethingChanged = true; if ( (m_SelectedImage.IsNotNull()) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } *m_SelectedImageNodes = node; m_SelectedImage = selectedImage; m_IsTensorImage = false; m_FAImage = nullptr; m_RDImage = nullptr; m_ADImage = nullptr; m_MDImage = nullptr; m_CAImage = nullptr; m_DirectionComp1Image = nullptr; m_DirectionComp2Image = nullptr; m_AngularErrorImage = nullptr; // Add change listeners to selected objects m_ImageObserverTag = m_SelectedImage->AddObserver( itk::ModifiedEvent(), changeListener ); m_Controls->m_SimilarAnglesFrame->setVisible(false); m_Controls->m_SimilarAnglesLabel->setVisible(false); m_Controls->m_SelectedImageLabel->setText( m_SelectedImageNodes->GetNode()->GetName().c_str() ); } } //planar if(isPlanar) { if(selectedPlanar != m_SelectedPlanarFigure.GetPointer()) { MITK_DEBUG << "Planar selection changed"; somethingChanged = true; // Possibly previous change listeners if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_PlanarFigureObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_PlanarFigureObserverTag = -1; } if ( (m_SelectedPlanarFigure.IsNotNull()) && (m_InitializedObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_InitializedObserverTag ); m_InitializedObserverTag = -1; } m_SelectedPlanarFigure = selectedPlanar; *m_SelectedPlanarFigureNodes = node; m_CurrentFigureNodeInitialized = selectedPlanar->IsPlaced(); m_SelectedMaskNode = nullptr; m_SelectedImageMask = nullptr; m_PlanarFigureObserverTag = m_SelectedPlanarFigure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), changeListener ); if(!m_CurrentFigureNodeInitialized) { typedef itk::SimpleMemberCommand< QmitkPartialVolumeAnalysisView > ITKCommandType; ITKCommandType::Pointer initializationCommand; initializationCommand = ITKCommandType::New(); // set the callback function of the member command initializationCommand->SetCallbackFunction( this, &QmitkPartialVolumeAnalysisView::PlanarFigureInitialized ); // add an observer m_InitializedObserverTag = selectedPlanar->AddObserver( mitk::EndPlacementPlanarFigureEvent(), initializationCommand ); } m_Controls->m_SelectedMaskLabel->setText( m_SelectedPlanarFigureNodes->GetNode()->GetName().c_str() ); PlanarFigureFocus(node); } } //mask this->m_Controls->m_EstimateCircle->setEnabled(isMask && selectedImage->GetDimension()==3); if(isMask && selectedImage->GetDimension()==3) { if(selectedImage != m_SelectedImage.GetPointer()) { somethingChanged = true; if ( (m_SelectedImageMask.IsNotNull()) && (m_ImageMaskObserverTag >= 0) ) { m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); m_ImageMaskObserverTag = -1; } m_SelectedMaskNode = node; m_SelectedImageMask = selectedImage; m_SelectedPlanarFigure = nullptr; m_SelectedPlanarFigureNodes->RemoveAllNodes(); m_ImageMaskObserverTag = m_SelectedImageMask->AddObserver( itk::ModifiedEvent(), changeListener ); m_Controls->m_SelectedMaskLabel->setText( m_SelectedMaskNode->GetName().c_str() ); } } //tensor image if(isTensorImage && selectedTensorImage->GetDimension()==3) { if(selectedImage != m_SelectedImage.GetPointer()) { somethingChanged = true; if ( (m_SelectedImage.IsNotNull()) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } *m_SelectedImageNodes = node; m_SelectedImage = selectedImage; m_IsTensorImage = true; ExtractTensorImages(selectedImage); // Add change listeners to selected objects m_ImageObserverTag = m_SelectedImage->AddObserver( itk::ModifiedEvent(), changeListener ); m_Controls->m_SimilarAnglesFrame->setVisible(true); m_Controls->m_SimilarAnglesLabel->setVisible(true); m_Controls->m_SelectedImageLabel->setText( m_SelectedImageNodes->GetNode()->GetName().c_str() ); } } } if(somethingChanged) { this->SetMeasurementInfoToRenderWindow(""); if(m_SelectedPlanarFigure.IsNull() && m_SelectedImageMask.IsNull() ) { m_Controls->m_SelectedMaskLabel->setText("mandatory"); m_Controls->m_ResampleOptionsFrame->setEnabled(false); m_Controls->m_HistogramWidget->setEnabled(false); m_Controls->m_ClassSelector->setEnabled(false); m_Controls->m_DisplayHistogramCheckbox->setEnabled(false); m_Controls->m_AdvancedCheckbox->setEnabled(false); m_Controls->frame_7->setEnabled(false); } else { m_Controls->m_ResampleOptionsFrame->setEnabled(true); m_Controls->m_HistogramWidget->setEnabled(true); m_Controls->m_ClassSelector->setEnabled(true); m_Controls->m_DisplayHistogramCheckbox->setEnabled(true); m_Controls->m_AdvancedCheckbox->setEnabled(true); m_Controls->frame_7->setEnabled(true); } // Clear statistics / histogram GUI if nothing is selected if ( m_SelectedImage.IsNull() ) { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->m_OpacityFrame->setEnabled(false); m_Controls->m_SelectedImageLabel->setText("mandatory"); } else { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); m_Controls->m_OpacityFrame->setEnabled(true); } if( !m_Visible || m_SelectedImage.IsNull() || (m_SelectedPlanarFigure.IsNull() && m_SelectedImageMask.IsNull()) ) { m_Controls->m_InputData->setTitle("Please Select Input Data"); m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; } else { m_Controls->m_InputData->setTitle("Input Data"); this->RequestStatisticsUpdate(); } } } void QmitkPartialVolumeAnalysisView::ShowClusteringResults() { typedef itk::Image MaskImageType; mitk::Image::Pointer mask = 0; MaskImageType::Pointer itkmask = 0; if(m_IsTensorImage && m_Controls->m_SimilarAnglesSlider->value() != 0) { typedef itk::Image AngularErrorImageType; typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(m_AngularErrorImage); caster->Update(); typedef itk::BinaryThresholdImageFilter< AngularErrorImageType, MaskImageType > ThreshType; ThreshType::Pointer thresh = ThreshType::New(); - thresh->SetUpperThreshold((90-m_Controls->m_SimilarAnglesSlider->value())*(PVA_PI/180.0)); + thresh->SetUpperThreshold((90-m_Controls->m_SimilarAnglesSlider->value())*(itk::Math::pi/180.0)); thresh->SetInsideValue(1.0); thresh->SetInput(caster->GetOutput()); thresh->Update(); itkmask = thresh->GetOutput(); mask = mitk::Image::New(); mask->InitializeByItk(itkmask.GetPointer()); mask->SetVolume(itkmask->GetBufferPointer()); // GetDataStorage()->Remove(m_newnode); // m_newnode = mitk::DataNode::New(); // m_newnode->SetData(mask); // m_newnode->SetName("masking node"); // m_newnode->SetIntProperty( "layer", 1002 ); // GetDataStorage()->Add(m_newnode, m_SelectedImageNodes->GetNode()); } mitk::Image::Pointer clusteredImage; ClusteringType::Pointer clusterer = ClusteringType::New(); if(m_QuantifyClass==3) { if(m_IsTensorImage) { double *green_fa, *green_rd, *green_ad, *green_md; //double *greengray_fa, *greengray_rd, *greengray_ad, *greengray_md; double *gray_fa, *gray_rd, *gray_ad, *gray_md; //double *redgray_fa, *redgray_rd, *redgray_ad, *redgray_md; double *red_fa, *red_rd, *red_ad, *red_md; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(0); mitk::Image::ConstPointer imgToCluster = tmpImg; red_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->b, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(3); mitk::Image::ConstPointer imgToCluster3 = tmpImg; red_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentRGBClusteringResults->rgbChannels->b, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(4); mitk::Image::ConstPointer imgToCluster4 = tmpImg; red_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentRGBClusteringResults->rgbChannels->b, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(5); mitk::Image::ConstPointer imgToCluster5 = tmpImg; red_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentRGBClusteringResults->rgbChannels->r, mask); green_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentRGBClusteringResults->rgbChannels->g, mask); gray_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentRGBClusteringResults->rgbChannels->b, mask); // clipboard QString clipboardText("FA\t%1\t%2\t\t%3\t%4\t\t%5\t%6\t"); clipboardText = clipboardText .arg(red_fa[0]).arg(red_fa[1]) .arg(gray_fa[0]).arg(gray_fa[1]) .arg(green_fa[0]).arg(green_fa[1]); QString clipboardText3("RD\t%1\t%2\t\t%3\t%4\t\t%5\t%6\t"); clipboardText3 = clipboardText3 .arg(red_rd[0]).arg(red_rd[1]) .arg(gray_rd[0]).arg(gray_rd[1]) .arg(green_rd[0]).arg(green_rd[1]); QString clipboardText4("AD\t%1\t%2\t\t%3\t%4\t\t%5\t%6\t"); clipboardText4 = clipboardText4 .arg(red_ad[0]).arg(red_ad[1]) .arg(gray_ad[0]).arg(gray_ad[1]) .arg(green_ad[0]).arg(green_ad[1]); QString clipboardText5("MD\t%1\t%2\t\t%3\t%4\t\t%5\t%6"); clipboardText5 = clipboardText5 .arg(red_md[0]).arg(red_md[1]) .arg(gray_md[0]).arg(gray_md[1]) .arg(green_md[0]).arg(green_md[1]); QApplication::clipboard()->setText(clipboardText+clipboardText3+clipboardText4+clipboardText5, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("%1 %2 %3 \n"); plainInfoText = plainInfoText .arg("Red ", 20) .arg("Gray ", 20) .arg("Green", 20); QString plainInfoText0("FA:%1 ± %2%3 ± %4%5 ± %6\n"); plainInfoText0 = plainInfoText0 .arg(red_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(red_fa[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(gray_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(gray_fa[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(green_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(green_fa[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText3("RDx10³:%1 ± %2%3 ± %4%5 ± %6\n"); plainInfoText3 = plainInfoText3 .arg(1000.0 * red_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_rd[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(1000.0 * gray_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_rd[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(1000.0 * green_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_rd[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText4("ADx10³:%1 ± %2%3 ± %4%5 ± %6\n"); plainInfoText4 = plainInfoText4 .arg(1000.0 * red_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_ad[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(1000.0 * gray_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_ad[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(1000.0 * green_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_ad[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText5("MDx10³:%1 ± %2%3 ± %4%5 ± %6"); plainInfoText5 = plainInfoText5 .arg(1000.0 * red_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_md[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(1000.0 * gray_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * gray_md[1], -10, 'g', 2, QLatin1Char( ' ' )) .arg(1000.0 * green_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * green_md[1], -10, 'g', 2, QLatin1Char( ' ' )); this->SetMeasurementInfoToRenderWindow(plainInfoText+plainInfoText0+plainInfoText3+plainInfoText4+plainInfoText5); } else { double* green; double* gray; double* red; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; red = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->r); green = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->g); gray = clusterer->PerformQuantification(imgToCluster, m_CurrentRGBClusteringResults->rgbChannels->b); // clipboard QString clipboardText("%1\t%2\t\t%3\t%4\t\t%5\t%6"); clipboardText = clipboardText.arg(red[0]).arg(red[1]) .arg(gray[0]).arg(gray[1]) .arg(green[0]).arg(green[1]); QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("Red: %1 ± %2\nGray: %3 ± %4\nGreen: %5 ± %6"); plainInfoText = plainInfoText.arg(red[0]).arg(red[1]) .arg(gray[0]).arg(gray[1]) .arg(green[0]).arg(green[1]); this->SetMeasurementInfoToRenderWindow(plainInfoText); } clusteredImage = m_CurrentRGBClusteringResults->rgb; } else { if(m_IsTensorImage) { double *red_fa, *red_rd, *red_ad, *red_md; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(0); mitk::Image::ConstPointer imgToCluster = tmpImg; red_fa = clusterer->PerformQuantification(imgToCluster, m_CurrentPerformClusteringResults->clusteredImage, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(3); mitk::Image::ConstPointer imgToCluster3 = tmpImg; red_rd = clusterer->PerformQuantification(imgToCluster3, m_CurrentPerformClusteringResults->clusteredImage, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(4); mitk::Image::ConstPointer imgToCluster4 = tmpImg; red_ad = clusterer->PerformQuantification(imgToCluster4, m_CurrentPerformClusteringResults->clusteredImage, mask); tmpImg = m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(5); mitk::Image::ConstPointer imgToCluster5 = tmpImg; red_md = clusterer->PerformQuantification(imgToCluster5, m_CurrentPerformClusteringResults->clusteredImage, mask); // clipboard QString clipboardText("FA\t%1\t%2\t"); clipboardText = clipboardText .arg(red_fa[0]).arg(red_fa[1]); QString clipboardText3("RD\t%1\t%2\t"); clipboardText3 = clipboardText3 .arg(red_rd[0]).arg(red_rd[1]); QString clipboardText4("AD\t%1\t%2\t"); clipboardText4 = clipboardText4 .arg(red_ad[0]).arg(red_ad[1]); QString clipboardText5("MD\t%1\t%2\t"); clipboardText5 = clipboardText5 .arg(red_md[0]).arg(red_md[1]); QApplication::clipboard()->setText(clipboardText+clipboardText3+clipboardText4+clipboardText5, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("%1 \n"); plainInfoText = plainInfoText .arg("Red ", 20); QString plainInfoText0("FA:%1 ± %2\n"); plainInfoText0 = plainInfoText0 .arg(red_fa[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(red_fa[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText3("RDx10³:%1 ± %2\n"); plainInfoText3 = plainInfoText3 .arg(1000.0 * red_rd[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_rd[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText4("ADx10³:%1 ± %2\n"); plainInfoText4 = plainInfoText4 .arg(1000.0 * red_ad[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_ad[1], -10, 'g', 2, QLatin1Char( ' ' )); QString plainInfoText5("MDx10³:%1 ± %2"); plainInfoText5 = plainInfoText5 .arg(1000.0 * red_md[0], 10, 'g', 2, QLatin1Char( ' ' )).arg(1000.0 * red_md[1], -10, 'g', 2, QLatin1Char( ' ' )); this->SetMeasurementInfoToRenderWindow(plainInfoText+plainInfoText0+plainInfoText3+plainInfoText4+plainInfoText5); } else { double* quant; mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; quant = clusterer->PerformQuantification(imgToCluster, m_CurrentPerformClusteringResults->clusteredImage); // clipboard QString clipboardText("%1\t%2"); clipboardText = clipboardText.arg(quant[0]).arg(quant[1]); QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); // now paint infos also on renderwindow QString plainInfoText("Measurement: %1 ± %2"); plainInfoText = plainInfoText.arg(quant[0]).arg(quant[1]); this->SetMeasurementInfoToRenderWindow(plainInfoText); } clusteredImage = m_CurrentPerformClusteringResults->displayImage; } if(mask.IsNotNull()) { typedef itk::Image,3> RGBImageType; typedef mitk::ImageToItk ClusterCasterType; ClusterCasterType::Pointer clCaster = ClusterCasterType::New(); clCaster->SetInput(clusteredImage); clCaster->Update(); clCaster->GetOutput(); typedef itk::MaskImageFilter< RGBImageType, MaskImageType, RGBImageType > MaskType; MaskType::Pointer masker = MaskType::New(); masker->SetInput1(clCaster->GetOutput()); masker->SetInput2(itkmask); masker->Update(); clusteredImage = mitk::Image::New(); clusteredImage->InitializeByItk(masker->GetOutput()); clusteredImage->SetVolume(masker->GetOutput()->GetBufferPointer()); } if(m_ClusteringResult.IsNotNull()) { this->GetDataStorage()->Remove(m_ClusteringResult); } m_ClusteringResult = mitk::DataNode::New(); m_ClusteringResult->SetBoolProperty("helper object", true); m_ClusteringResult->SetIntProperty( "layer", 1000 ); m_ClusteringResult->SetBoolProperty("texture interpolation", m_TexIsOn); m_ClusteringResult->SetData(clusteredImage); m_ClusteringResult->SetName("Clusterprobs"); this->GetDataStorage()->Add(m_ClusteringResult, m_SelectedImageNodes->GetNode()); if(m_SelectedPlanarFigure.IsNotNull() && m_SelectedPlanarFigureNodes->GetNode().IsNotNull()) { m_SelectedPlanarFigureNodes->GetNode()->SetIntProperty( "layer", 1001 ); } this->RequestRenderWindowUpdate(); } void QmitkPartialVolumeAnalysisView::UpdateStatistics() { if(!m_CurrentFigureNodeInitialized && m_SelectedPlanarFigure.IsNotNull()) { MITK_DEBUG << "Selected planar figure not initialized. No stats calculation performed."; return; } // Remove any cached images that are no longer referenced elsewhere this->RemoveOrphanImages(); if ( m_SelectedImage.IsNotNull() ) { // Check if a the selected image is a multi-channel image. If yes, statistics // cannot be calculated currently. if ( !m_IsTensorImage && m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 ) { QMessageBox::information( nullptr, "Warning", "Non-tensor multi-component images not supported."); m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; return; } m_CurrentStatisticsCalculator = nullptr; if(!m_IsTensorImage) { // Retrieve HistogramStatisticsCalculator from has map (or create a new one // for this image if non-existant) PartialVolumeAnalysisMapType::iterator it = m_PartialVolumeAnalysisMap.find( m_SelectedImage ); if ( it != m_PartialVolumeAnalysisMap.end() ) { m_CurrentStatisticsCalculator = it->second; } } if(m_CurrentStatisticsCalculator.IsNull()) { m_CurrentStatisticsCalculator = mitk::PartialVolumeAnalysisHistogramCalculator::New(); m_CurrentStatisticsCalculator->SetPlanarFigureThickness(m_Controls->m_PlanarFiguresThickness->value()); if(m_IsTensorImage) { m_CurrentStatisticsCalculator->SetImage( m_CAImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_FAImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_DirectionComp1Image ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_DirectionComp2Image ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_RDImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_ADImage ); m_CurrentStatisticsCalculator->AddAdditionalResamplingImage( m_MDImage ); } else { m_CurrentStatisticsCalculator->SetImage( m_SelectedImage ); } m_PartialVolumeAnalysisMap[m_SelectedImage] = m_CurrentStatisticsCalculator; MITK_DEBUG << "Creating StatisticsCalculator"; } std::string maskName; std::string maskType; unsigned int maskDimension; if ( m_SelectedImageMask.IsNotNull() ) { mitk::PixelType pixelType = m_SelectedImageMask->GetPixelType(); MITK_DEBUG << pixelType.GetPixelTypeAsString(); if(pixelType.GetComponentTypeAsString() == "char") { MITK_DEBUG << "Pixel type is char instead of uchar"; return; } if(pixelType.GetBitsPerComponent() == 16) { //convert from ushort to uchar typedef itk::Image UCharImageType; UCharImageType::Pointer charImage; if(pixelType.GetComponentTypeAsString() == "short" ) { typedef itk::Image ShortImageType; ShortImageType::Pointer shortImage; mitk::CastToItkImage( m_SelectedImageMask, shortImage ); typedef itk::ImageDuplicator< ShortImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( shortImage ); duplicator->Update(); typedef itk::CastImageFilter ImageCasterType; ImageCasterType::Pointer caster = ImageCasterType::New(); caster->SetInput( duplicator->GetOutput() ); caster->Update(); charImage = caster->GetOutput(); } else { typedef itk::Image UShortImageType; UShortImageType::Pointer shortImage; mitk::CastToItkImage( m_SelectedImageMask, shortImage ); typedef itk::ImageDuplicator< UShortImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( shortImage ); duplicator->Update(); typedef itk::CastImageFilter ImageCasterType; ImageCasterType::Pointer caster = ImageCasterType::New(); caster->SetInput( duplicator->GetOutput() ); caster->Update(); charImage = caster->GetOutput(); } m_SelectedImageMask = nullptr; m_SelectedImageMask = mitk::Image::New(); m_SelectedImageMask->InitializeByItk( charImage.GetPointer() ); m_SelectedImageMask->SetVolume( charImage->GetBufferPointer() ); mitk::CastToMitkImage(charImage, m_SelectedImageMask); } m_CurrentStatisticsCalculator->SetImageMask( m_SelectedImageMask ); m_CurrentStatisticsCalculator->SetMaskingModeToImage(); maskName = m_SelectedMaskNode->GetName(); maskType = m_SelectedImageMask->GetNameOfClass(); maskDimension = 3; std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); } else if ( m_SelectedPlanarFigure.IsNotNull() && m_SelectedPlanarFigureNodes->GetNode().IsNotNull()) { m_CurrentStatisticsCalculator->SetPlanarFigure( m_SelectedPlanarFigure ); m_CurrentStatisticsCalculator->SetMaskingModeToPlanarFigure(); maskName = m_SelectedPlanarFigureNodes->GetNode()->GetName(); maskType = m_SelectedPlanarFigure->GetNameOfClass(); maskDimension = 2; } else { m_CurrentStatisticsCalculator->SetMaskingModeToNone(); maskName = "-"; maskType = ""; maskDimension = 0; } bool statisticsChanged = false; bool statisticsCalculationSuccessful = false; // Initialize progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo( 100 ); // Install listener for progress events and initialize progress bar typedef itk::SimpleMemberCommand< QmitkPartialVolumeAnalysisView > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &QmitkPartialVolumeAnalysisView::UpdateProgressBar ); unsigned long progressObserverTag = m_CurrentStatisticsCalculator ->AddObserver( itk::ProgressEvent(), progressListener ); ClusteringType::ParamsType *cparams = 0; ClusteringType::ClusterResultType *cresult = 0; ClusteringType::HistType *chist = 0; try { m_CurrentStatisticsCalculator->SetNumberOfBins(m_Controls->m_NumberBins->text().toInt()); m_CurrentStatisticsCalculator->SetUpsamplingFactor(m_Controls->m_Upsampling->text().toDouble()); m_CurrentStatisticsCalculator->SetGaussianSigma(m_Controls->m_GaussianSigma->text().toDouble()); // Compute statistics statisticsChanged = m_CurrentStatisticsCalculator->ComputeStatistics( ); mitk::Image* tmpImg = m_CurrentStatisticsCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; if(imgToCluster.IsNotNull()) { // perform clustering const HistogramType *histogram = m_CurrentStatisticsCalculator->GetHistogram( ); if(histogram != nullptr) { ClusteringType::Pointer clusterer = ClusteringType::New(); clusterer->SetStepsNumIntegration(200); clusterer->SetMaxIt(1000); mitk::Image::Pointer pFiberImg; if(m_QuantifyClass==3) { if(m_Controls->m_Quantiles->isChecked()) { m_CurrentRGBClusteringResults = clusterer->PerformRGBQuantiles(imgToCluster, histogram, m_Controls->m_q1->value(),m_Controls->m_q2->value()); } else { m_CurrentRGBClusteringResults = clusterer->PerformRGBClustering(imgToCluster, histogram); } pFiberImg = m_CurrentRGBClusteringResults->rgbChannels->r; cparams = m_CurrentRGBClusteringResults->params; cresult = m_CurrentRGBClusteringResults->result; chist = m_CurrentRGBClusteringResults->hist; } else { if(m_Controls->m_Quantiles->isChecked()) { m_CurrentPerformClusteringResults = clusterer->PerformQuantiles(imgToCluster, histogram, m_Controls->m_q1->value(),m_Controls->m_q2->value()); } else { m_CurrentPerformClusteringResults = clusterer->PerformClustering(imgToCluster, histogram, m_QuantifyClass); } pFiberImg = m_CurrentPerformClusteringResults->clusteredImage; cparams = m_CurrentPerformClusteringResults->params; cresult = m_CurrentPerformClusteringResults->result; chist = m_CurrentPerformClusteringResults->hist; } if(m_IsTensorImage) { m_AngularErrorImage = clusterer->CaculateAngularErrorImage( m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(1), m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(2), pFiberImg); // GetDataStorage()->Remove(m_newnode2); // m_newnode2 = mitk::DataNode::New(); // m_newnode2->SetData(m_AngularErrorImage); // m_newnode2->SetName(("AngularError")); // m_newnode2->SetIntProperty( "layer", 1003 ); // GetDataStorage()->Add(m_newnode2, m_SelectedImageNodes->GetNode()); // newnode = mitk::DataNode::New(); // newnode->SetData(m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(1)); // newnode->SetName(("Comp1")); // GetDataStorage()->Add(newnode, m_SelectedImageNodes->GetNode()); // newnode = mitk::DataNode::New(); // newnode->SetData(m_CurrentStatisticsCalculator->GetInternalAdditionalResampledImage(2)); // newnode->SetName(("Comp2")); // GetDataStorage()->Add(newnode, m_SelectedImageNodes->GetNode()); } ShowClusteringResults(); } } statisticsCalculationSuccessful = true; } catch ( const std::runtime_error &e ) { QMessageBox::information( nullptr, "Warning", e.what()); } catch ( const std::exception &e ) { MITK_ERROR << "Caught exception: " << e.what(); QMessageBox::information( nullptr, "Warning", e.what()); } m_CurrentStatisticsCalculator->RemoveObserver( progressObserverTag ); // Make sure that progress bar closes mitk::ProgressBar::GetInstance()->Progress( 100 ); if ( statisticsCalculationSuccessful ) { if ( statisticsChanged ) { // Do not show any error messages m_CurrentStatisticsValid = true; } // m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetParameters( cparams, cresult, chist ); // m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); } else { m_Controls->m_SelectedMaskLabel->setText("mandatory"); // Clear statistics and histogram m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; // If a (non-closed) PlanarFigure is selected, display a line profile widget if ( m_SelectedPlanarFigure.IsNotNull() ) { // TODO: enable line profile widget //m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); //m_Controls->m_LineProfileWidget->SetImage( m_SelectedImage ); //m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); //m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); } } } } void QmitkPartialVolumeAnalysisView::SetMeasurementInfoToRenderWindow(const QString& text) { FindRenderWindow(m_SelectedPlanarFigureNodes->GetNode()); if(m_LastRenderWindow != m_SelectedRenderWindow) { if(m_LastRenderWindow) { QObject::disconnect( m_LastRenderWindow, SIGNAL( destroyed(QObject*) ) , this, SLOT( OnRenderWindowDelete(QObject*) ) ); } m_LastRenderWindow = m_SelectedRenderWindow; if(m_LastRenderWindow) { QObject::connect( m_LastRenderWindow, SIGNAL( destroyed(QObject*) ) , this, SLOT( OnRenderWindowDelete(QObject*) ) ); } } if(m_LastRenderWindow && m_SelectedPlanarFigureNodes->GetNode().IsNotNull()) { if (!text.isEmpty()) { m_MeasurementInfoAnnotation->SetText(1, text.toLatin1().data()); mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->InsertForegroundRenderer( m_MeasurementInfoRenderer, true); } else { if (mitk::VtkLayerController::GetInstance( m_LastRenderWindow->GetRenderWindow()) ->IsRendererInserted( m_MeasurementInfoRenderer)) mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->RemoveRenderer( m_MeasurementInfoRenderer); } } else { mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); if ( renderWindowPart == nullptr ) { return; } if (!text.isEmpty()) { m_MeasurementInfoAnnotation->SetText(1, text.toLatin1().data()); mitk::VtkLayerController::GetInstance(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderWindow())->InsertForegroundRenderer( m_MeasurementInfoRenderer, true); } else { if (mitk::VtkLayerController::GetInstance( renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderWindow()) ->IsRendererInserted( m_MeasurementInfoRenderer)) mitk::VtkLayerController::GetInstance(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderWindow())->RemoveRenderer( m_MeasurementInfoRenderer); } } } void QmitkPartialVolumeAnalysisView::UpdateProgressBar() { mitk::ProgressBar::GetInstance()->Progress(); } void QmitkPartialVolumeAnalysisView::RequestStatisticsUpdate() { if ( !m_StatisticsUpdatePending ) { QApplication::postEvent( this, new QmitkRequestStatisticsUpdateEvent ); m_StatisticsUpdatePending = true; } } void QmitkPartialVolumeAnalysisView::RemoveOrphanImages() { PartialVolumeAnalysisMapType::iterator it = m_PartialVolumeAnalysisMap.begin(); while ( it != m_PartialVolumeAnalysisMap.end() ) { mitk::Image *image = it->first; mitk::PartialVolumeAnalysisHistogramCalculator *calculator = it->second; ++it; mitk::NodePredicateData::Pointer hasImage = mitk::NodePredicateData::New( image ); if ( this->GetDataStorage()->GetNode( hasImage ) == nullptr ) { if ( m_SelectedImage == image ) { m_SelectedImage = nullptr; m_SelectedImageNodes->RemoveAllNodes(); } if ( m_CurrentStatisticsCalculator == calculator ) { m_CurrentStatisticsCalculator = nullptr; } m_PartialVolumeAnalysisMap.erase( image ); it = m_PartialVolumeAnalysisMap.begin(); } } } void QmitkPartialVolumeAnalysisView::ExtractTensorImages( mitk::Image::Pointer tensorimage) { typedef itk::Image< itk::DiffusionTensor3D, 3> TensorImageType; typedef mitk::ImageToItk CastType; CastType::Pointer caster = CastType::New(); caster->SetInput(tensorimage); caster->Update(); TensorImageType::Pointer image = caster->GetOutput(); typedef itk::TensorDerivedMeasurementsFilter MeasurementsType; MeasurementsType::Pointer measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::FA); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer fa = measurementsCalculator->GetOutput(); m_FAImage = mitk::Image::New(); m_FAImage->InitializeByItk(fa.GetPointer()); m_FAImage->SetVolume(fa->GetBufferPointer()); // mitk::DataNode::Pointer node = mitk::DataNode::New(); // node->SetData(m_FAImage); // GetDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::CA); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer ca = measurementsCalculator->GetOutput(); m_CAImage = mitk::Image::New(); m_CAImage->InitializeByItk(ca.GetPointer()); m_CAImage->SetVolume(ca->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::RD); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer rd = measurementsCalculator->GetOutput(); m_RDImage = mitk::Image::New(); m_RDImage->InitializeByItk(rd.GetPointer()); m_RDImage->SetVolume(rd->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::AD); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer ad = measurementsCalculator->GetOutput(); m_ADImage = mitk::Image::New(); m_ADImage->InitializeByItk(ad.GetPointer()); m_ADImage->SetVolume(ad->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDataStorage()->Add(node); measurementsCalculator = MeasurementsType::New(); measurementsCalculator->SetInput(image ); measurementsCalculator->SetMeasure(MeasurementsType::RA); measurementsCalculator->Update(); MeasurementsType::OutputImageType::Pointer md = measurementsCalculator->GetOutput(); m_MDImage = mitk::Image::New(); m_MDImage->InitializeByItk(md.GetPointer()); m_MDImage->SetVolume(md->GetBufferPointer()); // node = mitk::DataNode::New(); // node->SetData(m_CAImage); // GetDataStorage()->Add(node); typedef DirectionsFilterType::PeakImageType DirImageType; DirectionsFilterType::Pointer dirFilter = DirectionsFilterType::New(); dirFilter->SetInput(image ); dirFilter->SetUsePolarCoordinates(true); dirFilter->Update(); ItkUcharImgType::Pointer numDirImage = dirFilter->GetOutput(); DirImageType::Pointer dirImage = dirFilter->GetPeakImage(); itk::ImageRegionIterator itd(dirImage, dirImage->GetLargestPossibleRegion()); itd.GoToBegin(); while( !itd.IsAtEnd() ) { DirImageType::PixelType direction = itd.Get(); direction = fabs(direction); itd.Set(direction); ++itd; } typedef itk::Image CompImageType; CompImageType::Pointer comp1 = CompImageType::New(); comp1->SetSpacing( numDirImage->GetSpacing() ); // Set the image spacing comp1->SetOrigin( numDirImage->GetOrigin() ); // Set the image origin comp1->SetDirection( numDirImage->GetDirection() ); // Set the image direction comp1->SetRegions( numDirImage->GetLargestPossibleRegion() ); comp1->Allocate(); CompImageType::Pointer comp2 = CompImageType::New(); comp2->SetSpacing( numDirImage->GetSpacing() ); // Set the image spacing comp2->SetOrigin( numDirImage->GetOrigin() ); // Set the image origin comp2->SetDirection( numDirImage->GetDirection() ); // Set the image direction comp2->SetRegions( numDirImage->GetLargestPossibleRegion() ); comp2->Allocate(); itk::ImageRegionIterator it1(comp1, comp1->GetLargestPossibleRegion()); itk::ImageRegionIterator it2(comp2, comp2->GetLargestPossibleRegion()); it1.GoToBegin(); it2.GoToBegin(); while( !it2.IsAtEnd() ) { DirImageType::IndexType peakIndex; peakIndex[0] = it1.GetIndex()[0]; peakIndex[1] = it1.GetIndex()[1]; peakIndex[2] = it1.GetIndex()[2]; peakIndex[3] = 1; it1.Set(dirImage->GetPixel(peakIndex)); peakIndex[3] = 2; it2.Set(dirImage->GetPixel(peakIndex)); ++it1; ++it2; } m_DirectionComp1Image = mitk::Image::New(); m_DirectionComp1Image->InitializeByItk(comp1.GetPointer()); m_DirectionComp1Image->SetVolume(comp1->GetBufferPointer()); m_DirectionComp2Image = mitk::Image::New(); m_DirectionComp2Image->InitializeByItk(comp2.GetPointer()); m_DirectionComp2Image->SetVolume(comp2->GetBufferPointer()); } void QmitkPartialVolumeAnalysisView::OnRenderWindowDelete(QObject * obj) { if(obj == m_LastRenderWindow) m_LastRenderWindow = 0; if(obj == m_SelectedRenderWindow) m_SelectedRenderWindow = 0; } bool QmitkPartialVolumeAnalysisView::event( QEvent *event ) { if ( event->type() == (QEvent::Type) QmitkRequestStatisticsUpdateEvent::StatisticsUpdateRequest ) { // Update statistics m_StatisticsUpdatePending = false; this->UpdateStatistics(); return true; } return false; } void QmitkPartialVolumeAnalysisView::Activated() { mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigure* figure = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; // finally add all nodes to the model for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figure = dynamic_cast(node->GetData()); if(figure) { figureInteractor = dynamic_cast(node->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( node ); } } } } void QmitkPartialVolumeAnalysisView::Deactivated() { } void QmitkPartialVolumeAnalysisView::ActivatedZombieView(berry::IWorkbenchPartReference::Pointer) { this->SetMeasurementInfoToRenderWindow(""); mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigure* figure = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; // finally add all nodes to the model for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figure = dynamic_cast(node->GetData()); if(figure) { figureInteractor = dynamic_cast(node->GetDataInteractor().GetPointer()); if(figureInteractor) figureInteractor->SetDataNode( nullptr ); } } } void QmitkPartialVolumeAnalysisView::Hidden() { if (m_ClusteringResult.IsNotNull()) { this->GetDataStorage()->Remove(m_ClusteringResult); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } Select(nullptr, true, true); m_Visible = false; } void QmitkPartialVolumeAnalysisView::Visible() { m_Visible = true; berry::IWorkbenchPart::Pointer bla; if (!this->GetCurrentSelection().empty()) { this->OnSelectionChanged(bla, this->GetCurrentSelection()); } else { this->OnSelectionChanged(bla, this->GetDataManagerSelection()); } } void QmitkPartialVolumeAnalysisView::GreenRadio(bool checked) { if(checked) { m_Controls->m_PartialVolumeRadio->setChecked(false); m_Controls->m_BlueRadio->setChecked(false); m_Controls->m_AllRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(true); } m_QuantifyClass = 0; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::PartialVolumeRadio(bool checked) { if(checked) { m_Controls->m_GreenRadio->setChecked(false); m_Controls->m_BlueRadio->setChecked(false); m_Controls->m_AllRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(true); } m_QuantifyClass = 1; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::BlueRadio(bool checked) { if(checked) { m_Controls->m_PartialVolumeRadio->setChecked(false); m_Controls->m_GreenRadio->setChecked(false); m_Controls->m_AllRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(true); } m_QuantifyClass = 2; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::AllRadio(bool checked) { if(checked) { m_Controls->m_BlueRadio->setChecked(false); m_Controls->m_PartialVolumeRadio->setChecked(false); m_Controls->m_GreenRadio->setChecked(false); m_Controls->m_ExportClusteringResultsButton->setEnabled(false); } m_QuantifyClass = 3; RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::NumberBinsChangedSlider(int) { m_Controls->m_NumberBins->setText(QString("%1").arg(m_Controls->m_NumberBinsSlider->value()*5.0)); } void QmitkPartialVolumeAnalysisView::UpsamplingChangedSlider(int) { m_Controls->m_Upsampling->setText(QString("%1").arg(m_Controls->m_UpsamplingSlider->value()/10.0)); } void QmitkPartialVolumeAnalysisView::GaussianSigmaChangedSlider(int) { m_Controls->m_GaussianSigma->setText(QString("%1").arg(m_Controls->m_GaussianSigmaSlider->value()/100.0)); } void QmitkPartialVolumeAnalysisView::SimilarAnglesChangedSlider(int) { m_Controls->m_SimilarAngles->setText(QString("%1°").arg(90-m_Controls->m_SimilarAnglesSlider->value())); ShowClusteringResults(); } void QmitkPartialVolumeAnalysisView::OpacityChangedSlider(int v ) { if(m_SelectedImageNodes->GetNode().IsNotNull()) { float opacImag = 1.0f-(v-5)/5.0f; opacImag = opacImag < 0 ? 0 : opacImag; m_SelectedImageNodes->GetNode()->SetFloatProperty("opacity", opacImag); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } if(m_ClusteringResult.IsNotNull()) { float opacClust = v/5.0f; opacClust = opacClust > 1 ? 1 : opacClust; m_ClusteringResult->SetFloatProperty("opacity", opacClust); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkPartialVolumeAnalysisView::NumberBinsReleasedSlider( ) { RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::UpsamplingReleasedSlider( ) { RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::GaussianSigmaReleasedSlider( ) { RequestStatisticsUpdate(); } void QmitkPartialVolumeAnalysisView::SimilarAnglesReleasedSlider( ) { } void QmitkPartialVolumeAnalysisView::ToClipBoard() { std::vector* > vals = m_Controls->m_HistogramWidget->m_Vals; QString clipboardText; for (std::vector* >::iterator it = vals.begin(); it != vals.end(); ++it) { for (std::vector::iterator it2 = (**it).begin(); it2 != (**it).end(); ++it2) { clipboardText.append(QString("%1 \t").arg(*it2)); } clipboardText.append(QString("\n")); } QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); } void QmitkPartialVolumeAnalysisView::PropertyChanged(const mitk::DataNode* /*node*/, const mitk::BaseProperty* /*prop*/) { } void QmitkPartialVolumeAnalysisView::NodeChanged(const mitk::DataNode* /*node*/) { } void QmitkPartialVolumeAnalysisView::NodeRemoved(const mitk::DataNode* node) { if (dynamic_cast(node->GetData())) this->GetDataStorage()->Remove(m_ClusteringResult); if( node == m_SelectedPlanarFigureNodes->GetNode().GetPointer() || node == m_SelectedMaskNode.GetPointer() ) { this->Select(nullptr,true,false); SetMeasurementInfoToRenderWindow(""); } if( node == m_SelectedImageNodes->GetNode().GetPointer() ) { this->Select(nullptr,false,true); SetMeasurementInfoToRenderWindow(""); } } void QmitkPartialVolumeAnalysisView::NodeAddedInDataStorage(const mitk::DataNode* node) { if(!m_Visible) return; mitk::DataNode* nonConstNode = const_cast(node); mitk::PlanarFigure* figure = dynamic_cast(nonConstNode->GetData()); if(figure) { // set interactor for new node (if not already set) mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(node->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( nonConstNode ); } // remove uninitialized old planars if( m_SelectedPlanarFigureNodes->GetNode().IsNotNull() && m_CurrentFigureNodeInitialized == false ) { this->GetDataStorage()->Remove(m_SelectedPlanarFigureNodes->GetNode()); } } } void QmitkPartialVolumeAnalysisView::TextIntON() { if(m_ClusteringResult.IsNotNull()) { if(m_TexIsOn) { m_Controls->m_TextureIntON->setIcon(*m_IconTexOFF); } else { m_Controls->m_TextureIntON->setIcon(*m_IconTexON); } m_ClusteringResult->SetBoolProperty("texture interpolation", !m_TexIsOn); m_TexIsOn = !m_TexIsOn; this->RequestRenderWindowUpdate(); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkGibbsTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkGibbsTrackingView.cpp index 596fced7d0..71a145ed12 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkGibbsTrackingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkGibbsTrackingView.cpp @@ -1,608 +1,608 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 // Qmitk #include "QmitkGibbsTrackingView.h" // Qt #include #include #include #include // MITK #include #include #include #include #include #include #include #include #include #include // ITK #include #include #include // MISC #include QmitkTrackingWorker::QmitkTrackingWorker(QmitkGibbsTrackingView* view) : m_View(view) { } void QmitkTrackingWorker::run() { m_View->m_GlobalTracker = QmitkGibbsTrackingView::GibbsTrackingFilterType::New(); m_View->m_GlobalTracker->SetOdfImage(m_View->m_ItkOdfImage); m_View->m_GlobalTracker->SetTensorImage(m_View->m_ItkTensorImage); m_View->m_GlobalTracker->SetMaskImage(m_View->m_MaskImage); m_View->m_GlobalTracker->SetStartTemperature((float)m_View->m_Controls->m_StartTempSlider->value()/100); m_View->m_GlobalTracker->SetEndTemperature((float)m_View->m_Controls->m_EndTempSlider->value()/10000); m_View->m_GlobalTracker->SetIterations(m_View->m_Controls->m_IterationsBox->text().toDouble()); m_View->m_GlobalTracker->SetParticleWeight((float)m_View->m_Controls->m_ParticleWeightSlider->value()/10000); m_View->m_GlobalTracker->SetParticleWidth((float)(m_View->m_Controls->m_ParticleWidthSlider->value())/10); m_View->m_GlobalTracker->SetParticleLength((float)(m_View->m_Controls->m_ParticleLengthSlider->value())/10); m_View->m_GlobalTracker->SetInexBalance((float)m_View->m_Controls->m_InExBalanceSlider->value()/10); m_View->m_GlobalTracker->SetMinFiberLength(m_View->m_Controls->m_FiberLengthSlider->value()); - m_View->m_GlobalTracker->SetCurvatureThreshold(cos((float)m_View->m_Controls->m_CurvatureThresholdSlider->value()*M_PI/180)); + m_View->m_GlobalTracker->SetCurvatureThreshold(cos((float)m_View->m_Controls->m_CurvatureThresholdSlider->value()*itk::Math::pi/180)); m_View->m_GlobalTracker->SetRandomSeed(m_View->m_Controls->m_RandomSeedSlider->value()); try{ m_View->m_GlobalTracker->Update(); } catch( mitk::Exception e ) { MITK_ERROR << "Internal error occured: " << e.what() << "\nAborting"; } m_View->m_TrackingThread.quit(); } const std::string QmitkGibbsTrackingView::VIEW_ID = "org.mitk.views.gibbstracking"; QmitkGibbsTrackingView::QmitkGibbsTrackingView() : QmitkAbstractView() , m_Controls( 0 ) , m_TrackingNode(nullptr) , m_FiberBundle(nullptr) , m_MaskImage(nullptr) , m_TensorImage(nullptr) , m_OdfImage(nullptr) , m_ItkOdfImage(nullptr) , m_ItkTensorImage(nullptr) , m_ImageNode(nullptr) , m_MaskImageNode(nullptr) , m_FiberBundleNode(nullptr) , m_ThreadIsRunning(false) , m_ElapsedTime(0) , m_GlobalTracker(nullptr) , m_TrackingWorker(this) { 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); } QmitkGibbsTrackingView::~QmitkGibbsTrackingView() { if (m_GlobalTracker.IsNull()) return; m_GlobalTracker->SetAbortTracking(true); m_TrackingThread.wait(); } // update tracking status and generate fiber bundle void QmitkGibbsTrackingView::TimerUpdate() { UpdateTrackingStatus(); GenerateFiberBundle(); } // tell global tractography filter to stop after current step void QmitkGibbsTrackingView::StopGibbsTracking() { if (m_GlobalTracker.IsNull()) return; m_GlobalTracker->SetAbortTracking(true); m_Controls->m_TrackingStop->setEnabled(false); m_Controls->m_TrackingStop->setText("Stopping Tractography ..."); m_TrackingNode = nullptr; } // update gui elements and generate fiber bundle after tracking is finished void QmitkGibbsTrackingView::AfterThread() { m_ThreadIsRunning = false; m_TrackingTimer->stop(); UpdateGUI(); if( !m_GlobalTracker->GetIsInValidState() ) { QMessageBox::critical( nullptr, "Gibbs Tracking", "An internal error occured. Tracking aborted.\n Please check the log for details." ); m_FiberBundleNode = nullptr; return; } UpdateTrackingStatus(); if(m_Controls->m_ParticleWeightSlider->value()==0) { m_Controls->m_ParticleWeightLabel->setText(QString::number(m_GlobalTracker->GetParticleWeight())); m_Controls->m_ParticleWeightSlider->setValue(m_GlobalTracker->GetParticleWeight()*10000); } if(m_Controls->m_ParticleWidthSlider->value()==0) { m_Controls->m_ParticleWidthLabel->setText(QString::number(m_GlobalTracker->GetParticleWidth())); m_Controls->m_ParticleWidthSlider->setValue(m_GlobalTracker->GetParticleWidth()*10); } if(m_Controls->m_ParticleLengthSlider->value()==0) { m_Controls->m_ParticleLengthLabel->setText(QString::number(m_GlobalTracker->GetParticleLength())); m_Controls->m_ParticleLengthSlider->setValue(m_GlobalTracker->GetParticleLength()*10); } GenerateFiberBundle(); m_FiberBundleNode = 0; m_GlobalTracker = 0; // images not needed anymore ( relevant only for computation ) // we need to release them to remove the memory access block created through CastToItk<> calls this->m_ItkOdfImage = 0; this->m_ItkTensorImage = 0; } // start tracking timer and update gui elements before tracking is started void QmitkGibbsTrackingView::BeforeThread() { m_ThreadIsRunning = true; m_TrackingTime = QTime::currentTime(); m_ElapsedTime = 0; m_TrackingTimer->start(1000); UpdateGUI(); } // setup gui elements and signal/slot connections void QmitkGibbsTrackingView::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::QmitkGibbsTrackingViewControls; m_Controls->setupUi( parent ); AdvancedSettings(); connect( m_TrackingTimer, SIGNAL(timeout()), this, SLOT(TimerUpdate()) ); connect( m_Controls->m_TrackingStop, SIGNAL(clicked()), this, SLOT(StopGibbsTracking()) ); connect( m_Controls->m_TrackingStart, SIGNAL(clicked()), this, SLOT(StartGibbsTracking()) ); connect( m_Controls->m_AdvancedSettingsCheckbox, SIGNAL(clicked()), this, SLOT(AdvancedSettings()) ); connect( m_Controls->m_SaveTrackingParameters, SIGNAL(clicked()), this, SLOT(SaveTrackingParameters()) ); connect( m_Controls->m_LoadTrackingParameters, SIGNAL(clicked()), this, SLOT(LoadTrackingParameters()) ); connect( m_Controls->m_ParticleWidthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleWidth(int)) ); connect( m_Controls->m_ParticleLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleLength(int)) ); connect( m_Controls->m_InExBalanceSlider, SIGNAL(valueChanged(int)), this, SLOT(SetInExBalance(int)) ); connect( m_Controls->m_FiberLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetFiberLength(int)) ); connect( m_Controls->m_ParticleWeightSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleWeight(int)) ); connect( m_Controls->m_StartTempSlider, SIGNAL(valueChanged(int)), this, SLOT(SetStartTemp(int)) ); connect( m_Controls->m_EndTempSlider, SIGNAL(valueChanged(int)), this, SLOT(SetEndTemp(int)) ); connect( m_Controls->m_CurvatureThresholdSlider, SIGNAL(valueChanged(int)), this, SLOT(SetCurvatureThreshold(int)) ); connect( m_Controls->m_RandomSeedSlider, SIGNAL(valueChanged(int)), this, SLOT(SetRandomSeed(int)) ); connect( m_Controls->m_OutputFileButton, SIGNAL(clicked()), this, SLOT(SetOutputFile()) ); m_Controls->m_InputImageBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isOdfImagePredicate = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isTensorImagePredicate = mitk::TNodePredicateDataType::New(); m_Controls->m_InputImageBox->SetPredicate( mitk::NodePredicateOr::New(isOdfImagePredicate, isTensorImagePredicate) ); m_Controls->m_MaskImageBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_MaskImageBox->SetZeroEntryText("--"); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); m_Controls->m_MaskImageBox->SetPredicate( mitk::NodePredicateAnd::New(isBinaryPredicate, mitk::NodePredicateAnd::New(isImagePredicate, is3D)) ); connect( (QObject*)(m_Controls->m_MaskImageBox), SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGUI())); connect( (QObject*)(m_Controls->m_InputImageBox), SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGUI())); } } void QmitkGibbsTrackingView::SetFocus() { m_Controls->m_TrackingStart->setFocus(); } void QmitkGibbsTrackingView::SetInExBalance(int value) { m_Controls->m_InExBalanceLabel->setText(QString::number((float)value/10)); } void QmitkGibbsTrackingView::SetFiberLength(int value) { m_Controls->m_FiberLengthLabel->setText(QString::number(value)+"mm"); } void QmitkGibbsTrackingView::SetRandomSeed(int value) { if (value>=0) m_Controls->m_RandomSeedLabel->setText(QString::number(value)); else m_Controls->m_RandomSeedLabel->setText("auto"); } void QmitkGibbsTrackingView::SetParticleWeight(int value) { if (value>0) m_Controls->m_ParticleWeightLabel->setText(QString::number((float)value/10000)); else m_Controls->m_ParticleWeightLabel->setText("auto"); } void QmitkGibbsTrackingView::SetStartTemp(int value) { m_Controls->m_StartTempLabel->setText(QString::number((float)value/100)); } void QmitkGibbsTrackingView::SetEndTemp(int value) { m_Controls->m_EndTempLabel->setText(QString::number((float)value/10000)); } void QmitkGibbsTrackingView::SetParticleWidth(int value) { if (value>0) m_Controls->m_ParticleWidthLabel->setText(QString::number((float)value/10)+" mm"); else m_Controls->m_ParticleWidthLabel->setText("auto"); } void QmitkGibbsTrackingView::SetParticleLength(int value) { if (value>0) m_Controls->m_ParticleLengthLabel->setText(QString::number((float)value/10)+" mm"); else m_Controls->m_ParticleLengthLabel->setText("auto"); } void QmitkGibbsTrackingView::SetCurvatureThreshold(int value) { m_Controls->m_CurvatureThresholdLabel->setText(QString::number(value)+"°"); } // called if datamanager selection changes void QmitkGibbsTrackingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& ) { UpdateGUI(); } void QmitkGibbsTrackingView::NodeRemoved(const mitk::DataNode * node) { if (m_ThreadIsRunning) { if (node==m_TrackingNode.GetPointer()) { StopGibbsTracking(); } } } // update gui elements displaying trackings status void QmitkGibbsTrackingView::UpdateTrackingStatus() { if (m_GlobalTracker.IsNull()) return; m_ElapsedTime += m_TrackingTime.elapsed()/1000; m_TrackingTime.restart(); unsigned long hours = m_ElapsedTime/3600; unsigned long minutes = (m_ElapsedTime%3600)/60; unsigned long seconds = m_ElapsedTime%60; m_Controls->m_ProposalAcceptance->setText(QString::number(m_GlobalTracker->GetProposalAcceptance()*100)+"%"); m_Controls->m_TrackingTimeLabel->setText( QString::number(hours)+QString("h ")+QString::number(minutes)+QString("m ")+QString::number(seconds)+QString("s") ); m_Controls->m_NumConnectionsLabel->setText( QString::number(m_GlobalTracker->GetNumConnections()) ); m_Controls->m_NumParticlesLabel->setText( QString::number(m_GlobalTracker->GetNumParticles()) ); m_Controls->m_CurrentStepLabel->setText( QString::number(100*m_GlobalTracker->GetCurrentIteration()/m_GlobalTracker->GetIterations())+"%" ); m_Controls->m_AcceptedFibersLabel->setText( QString::number(m_GlobalTracker->GetNumAcceptedFibers()) ); } // update gui elements (enable/disable elements and set tooltips) void QmitkGibbsTrackingView::UpdateGUI() { if (!m_ThreadIsRunning && m_Controls->m_InputImageBox->GetSelectedNode().IsNotNull()) { m_Controls->m_TrackingStop->setEnabled(false); m_Controls->m_TrackingStart->setEnabled(true); m_Controls->m_LoadTrackingParameters->setEnabled(true); m_Controls->m_IterationsBox->setEnabled(true); m_Controls->m_AdvancedFrame->setEnabled(true); m_Controls->m_TrackingStop->setText("Stop Tractography"); m_Controls->m_TrackingStart->setToolTip("Start tractography. No further change of parameters possible."); m_Controls->m_TrackingStop->setToolTip(""); m_Controls->m_MaskImageBox->setEnabled(true); m_Controls->m_InputImageBox->setEnabled(true); } else if (!m_ThreadIsRunning) { m_Controls->m_TrackingStop->setEnabled(false); m_Controls->m_TrackingStart->setEnabled(false); m_Controls->m_LoadTrackingParameters->setEnabled(true); m_Controls->m_IterationsBox->setEnabled(true); m_Controls->m_AdvancedFrame->setEnabled(true); m_Controls->m_TrackingStop->setText("Stop Tractography"); m_Controls->m_TrackingStart->setToolTip("No ODF image selected."); m_Controls->m_TrackingStop->setToolTip(""); m_Controls->m_MaskImageBox->setEnabled(true); m_Controls->m_InputImageBox->setEnabled(true); } else { m_Controls->m_TrackingStop->setEnabled(true); m_Controls->m_TrackingStart->setEnabled(false); m_Controls->m_LoadTrackingParameters->setEnabled(false); m_Controls->m_IterationsBox->setEnabled(false); m_Controls->m_AdvancedFrame->setEnabled(false); m_Controls->m_AdvancedFrame->setVisible(false); m_Controls->m_AdvancedSettingsCheckbox->setChecked(false); m_Controls->m_TrackingStart->setToolTip("Tracking in progress."); m_Controls->m_TrackingStop->setToolTip("Stop tracking and display results."); m_Controls->m_MaskImageBox->setEnabled(false); m_Controls->m_InputImageBox->setEnabled(false); } } // show/hide advanced settings frame void QmitkGibbsTrackingView::AdvancedSettings() { m_Controls->m_AdvancedFrame->setVisible(m_Controls->m_AdvancedSettingsCheckbox->isChecked()); } // check for mask and odf and start tracking thread void QmitkGibbsTrackingView::StartGibbsTracking() { if(m_ThreadIsRunning) { MITK_WARN("QmitkGibbsTrackingView")<<"Thread already running!"; return; } m_GlobalTracker = nullptr; if (m_Controls->m_InputImageBox->GetSelectedNode().IsNull()) { QMessageBox::information( nullptr, "Warning", "Please load and select a Odf image before starting image processing."); return; } m_ImageNode = m_Controls->m_InputImageBox->GetSelectedNode(); if (dynamic_cast(m_ImageNode->GetData())) m_OdfImage = dynamic_cast(m_ImageNode->GetData()); else if (dynamic_cast(m_ImageNode->GetData())) m_TensorImage = dynamic_cast(m_ImageNode->GetData()); if (m_OdfImage.IsNull() && m_TensorImage.IsNull()) return; // cast odf to itk m_TrackingNode = m_ImageNode; m_ItkTensorImage = nullptr; m_ItkOdfImage = nullptr; m_MaskImage = nullptr; if (m_OdfImage.IsNotNull()) { m_ItkOdfImage = ItkOdfImgType::New(); mitk::CastToItkImage(m_OdfImage, m_ItkOdfImage); } else { m_ItkTensorImage = ItkTensorImage::New(); mitk::CastToItkImage(m_TensorImage, m_ItkTensorImage); } // mask image found? // catch exceptions thrown by the itkAccess macros try{ if(m_Controls->m_MaskImageBox->GetSelectedNode().IsNotNull()) { m_MaskImageNode = m_Controls->m_MaskImageBox->GetSelectedNode(); if (dynamic_cast(m_MaskImageNode->GetData())) mitk::CastToItkImage(dynamic_cast(m_MaskImageNode->GetData()), m_MaskImage); } } catch(...){}; // start worker thread m_TrackingThread.start(QThread::LowestPriority); } // generate mitkFiberBundle from tracking filter output void QmitkGibbsTrackingView::GenerateFiberBundle() { if (m_GlobalTracker.IsNull() || (!(m_Controls->m_VisualizationCheckbox->isChecked() || m_Controls->m_VisualizeOnceButton->isChecked()) && m_ThreadIsRunning)) return; if (m_Controls->m_VisualizeOnceButton->isChecked()) m_Controls->m_VisualizeOnceButton->setChecked(false); vtkSmartPointer fiberBundle = m_GlobalTracker->GetFiberBundle(); if ( m_GlobalTracker->GetNumAcceptedFibers()==0 ) return; m_FiberBundle = mitk::FiberBundle::New(fiberBundle); m_FiberBundle->SetReferenceGeometry(dynamic_cast(m_ImageNode->GetData())->GetGeometry()); if (m_FiberBundleNode.IsNotNull()){ GetDataStorage()->Remove(m_FiberBundleNode); m_FiberBundleNode = 0; } m_GlobalTracker->SetDicomProperties(m_FiberBundle); m_FiberBundleNode = mitk::DataNode::New(); m_FiberBundleNode->SetData(m_FiberBundle); QString name("FiberBundle_"); name += m_ImageNode->GetName().c_str(); name += "_Gibbs"; m_FiberBundleNode->SetName(name.toStdString()); m_FiberBundleNode->SetVisibility(true); if (!m_OutputFileName.isEmpty() && !m_ThreadIsRunning) { try { mitk::IOUtil::Save(m_FiberBundle.GetPointer(),m_OutputFileName.toStdString()); QMessageBox::information(nullptr, "Fiber bundle saved to", m_OutputFileName); } catch (itk::ExceptionObject &ex) { QMessageBox::information(nullptr, "Fiber bundle could not be saved", QString("%1\n%2\n%3\n%4\n%5\n%6").arg(ex.GetNameOfClass()).arg(ex.GetFile()).arg(ex.GetLine()).arg(ex.GetLocation()).arg(ex.what()).arg(ex.GetDescription())); } } if(m_ImageNode.IsNull()) GetDataStorage()->Add(m_FiberBundleNode); else GetDataStorage()->Add(m_FiberBundleNode, m_ImageNode); } void QmitkGibbsTrackingView::SetOutputFile() { // SELECT FOLDER DIALOG m_OutputFileName = QFileDialog::getSaveFileName(0, tr("Set file name"), QDir::currentPath()+"/FiberBundle.fib", tr("Fiber Bundle (*.fib)") ); if (m_OutputFileName.isEmpty()) m_Controls->m_OutputFileLabel->setText("N/A"); else m_Controls->m_OutputFileLabel->setText(m_OutputFileName); } // save current tracking paramters as xml file (.gtp) void QmitkGibbsTrackingView::SaveTrackingParameters() { TiXmlDocument documentXML; TiXmlDeclaration* declXML = new TiXmlDeclaration( "1.0", "", "" ); documentXML.LinkEndChild( declXML ); TiXmlElement* mainXML = new TiXmlElement("global_tracking_parameter_file"); mainXML->SetAttribute("file_version", "0.1"); documentXML.LinkEndChild(mainXML); TiXmlElement* paramXML = new TiXmlElement("parameter_set"); paramXML->SetAttribute("iterations", m_Controls->m_IterationsBox->text().toStdString()); paramXML->SetAttribute("particle_length", QString::number((float)m_Controls->m_ParticleLengthSlider->value()/10).toStdString()); paramXML->SetAttribute("particle_width", QString::number((float)m_Controls->m_ParticleWidthSlider->value()/10).toStdString()); paramXML->SetAttribute("particle_weight", QString::number((float)m_Controls->m_ParticleWeightSlider->value()/10000).toStdString()); paramXML->SetAttribute("temp_start", QString::number((float)m_Controls->m_StartTempSlider->value()/100).toStdString()); paramXML->SetAttribute("temp_end", QString::number((float)m_Controls->m_EndTempSlider->value()/10000).toStdString()); paramXML->SetAttribute("inexbalance", QString::number((float)m_Controls->m_InExBalanceSlider->value()/10).toStdString()); paramXML->SetAttribute("fiber_length", QString::number(m_Controls->m_FiberLengthSlider->value()).toStdString()); paramXML->SetAttribute("curvature_threshold", QString::number(m_Controls->m_CurvatureThresholdSlider->value()).toStdString()); mainXML->LinkEndChild(paramXML); QString filename = QFileDialog::getSaveFileName( 0, tr("Save Parameters"), QDir::currentPath()+"/param.gtp", tr("Global Tracking Parameters (*.gtp)") ); if(filename.isEmpty() || filename.isNull()) return; if(!filename.endsWith(".gtp")) filename += ".gtp"; documentXML.SaveFile( filename.toStdString() ); } // load current tracking paramters from xml file (.gtp) void QmitkGibbsTrackingView::LoadTrackingParameters() { QString filename = QFileDialog::getOpenFileName(0, tr("Load Parameters"), QDir::currentPath(), tr("Global Tracking Parameters (*.gtp)") ); if(filename.isEmpty() || filename.isNull()) return; TiXmlDocument doc( filename.toStdString() ); doc.LoadFile(); TiXmlHandle hDoc(&doc); TiXmlElement* pElem; TiXmlHandle hRoot(0); pElem = hDoc.FirstChildElement().Element(); hRoot = TiXmlHandle(pElem); pElem = hRoot.FirstChildElement("parameter_set").Element(); QString iterations(pElem->Attribute("iterations")); m_Controls->m_IterationsBox->setText(iterations); QString particleLength(pElem->Attribute("particle_length")); float pLength = particleLength.toFloat(); QString particleWidth(pElem->Attribute("particle_width")); float pWidth = particleWidth.toFloat(); if (pLength==0) m_Controls->m_ParticleLengthLabel->setText("auto"); else m_Controls->m_ParticleLengthLabel->setText(particleLength+" mm"); if (pWidth==0) m_Controls->m_ParticleWidthLabel->setText("auto"); else m_Controls->m_ParticleWidthLabel->setText(particleWidth+" mm"); m_Controls->m_ParticleWidthSlider->setValue(pWidth*10); m_Controls->m_ParticleLengthSlider->setValue(pLength*10); QString partWeight(pElem->Attribute("particle_weight")); m_Controls->m_ParticleWeightSlider->setValue(partWeight.toFloat()*10000); m_Controls->m_ParticleWeightLabel->setText(partWeight); QString startTemp(pElem->Attribute("temp_start")); m_Controls->m_StartTempSlider->setValue(startTemp.toFloat()*100); m_Controls->m_StartTempLabel->setText(startTemp); QString endTemp(pElem->Attribute("temp_end")); m_Controls->m_EndTempSlider->setValue(endTemp.toFloat()*10000); m_Controls->m_EndTempLabel->setText(endTemp); QString inExBalance(pElem->Attribute("inexbalance")); m_Controls->m_InExBalanceSlider->setValue(inExBalance.toFloat()*10); m_Controls->m_InExBalanceLabel->setText(inExBalance); QString fiberLength(pElem->Attribute("fiber_length")); m_Controls->m_FiberLengthSlider->setValue(fiberLength.toInt()); m_Controls->m_FiberLengthLabel->setText(fiberLength+"mm"); QString curvThres(pElem->Attribute("curvature_threshold")); m_Controls->m_CurvatureThresholdSlider->setValue(curvThres.toInt()); m_Controls->m_CurvatureThresholdLabel->setText(curvThres+"°"); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/deprecated/QmitkDiffusionTensorEstimation.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/deprecated/QmitkDiffusionTensorEstimation.cpp index c1586ea49c..25b1b8868c 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/deprecated/QmitkDiffusionTensorEstimation.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/deprecated/QmitkDiffusionTensorEstimation.cpp @@ -1,2499 +1,2494 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "itkTimeProbe.h" #include "QmitkDiffusionTensorEstimation.h" #include "QmitkDiffusionTensorEstimationControls.h" #include #include "QmitkDataTreeComboBox.h" #include "QmitkDataTreeListView.h" #include "QmitkDiffusionTensorIcon.h" #include #include "QmitkPropertyViewFactory.h" #include #include #include #include #include #include #include // properties #include "mitkStringProperty.h" #include "mitkProperties.h" #include "mitkMaterialProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkScalarModeProperty.h" #include "mitkLookupTableProperty.h" #include "mitkLookupTable.h" #include "mitkTransferFunctionProperty.h" #include "mitkGridRepresentationProperty.h" #include "mitkGridVolumeMapperProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkVectorImageMapper2D.h" #include "mitkOdfVtkMapper2D.h" #include "itkOrientedImage.h" #include "itkVectorImage.h" #include "itkImageSeriesReader.h" #include "itkImageFileWriter.h" #include "itkExceptionObject.h" #include "itkDiffusionTensor3DReconstructionImageFilter.h" #include "itkDiffusionTensor3D.h" #include "itkTensorFractionalAnisotropyImageFilter.h" #include "itkTensorRelativeAnisotropyImageFilter.h" #include "itkGDCMSeriesFileNames.h" #include "itkImageRegionConstIterator.h" #include "itkRescaleIntensityImageFilter.h" #include "itkDiffusionQballReconstructionImageFilter.h" #include "itkAnalyticalDiffusionQballReconstructionImageFilter.h" #include "itkPointShell.h" #include "itkRGBPixel.h" #include "itkOrientationDistributionFunction.h" #include "itkDiffusionOdfPrincipleDirectionsImageFilter.h" #include "itkDiffusionOdfGeneralizedFaImageFilter.h" #include "itkShiftScaleImageFilter.h" #include "itkDiffusionOdfPrepareVisualizationImageFilter.h" #include "itkDiffusionTensorPrincipleDirectionImageFilter.h" #include "itkDiffusionOdfSphericalDeconvolutionImageFilter.h" #include "itkVectorImagesAngularErrorImageFilter.h" #include "mitkDicomDiffusionVolumeHeaderReader.h" #include "mitkGroupDiffusionHeadersFilter.h" #include "mitkDicomDiffusionVolumesReader.h" #include "mitkNrrdDiffusionVolumesWriter.h" #include "mitkNrrdDiffusionVolumesReader.h" #include "mitkDiffusionVolumes.h" #include "mitkDataTreeFilterFunctions.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkTeemDiffusionTensor3DReconstructionImageFilter.h" #include "mitkSurface.h" #include "mitkDataNodeFactory.h" #include "vtkPolyData.h" #include "vtkPoints.h" #include "vtkCellArray.h" #include "vtkDelaunay2D.h" #include "vtkCleanPolyData.h" #include "vtkAppendPolyData.h" #include "mitkImageCast.h" #include #include #include -#define _USE_MATH_DEFINES -#include - -#define DIFF_EST_PI M_PI - typedef float TTensorPixelType; typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; typedef itk::VectorImage< DiffusionPixelType, 3 > DiffusionImageType; //CAST_N_VEC(3) //CAST_N_VEC(42) //CAST_N_VEC(92) //CAST_N_VEC(162) //CAST_N_VEC(252) //CAST_N_VEC(362) //CAST_N_VEC(492) //CAST_N_VEC(642) //CAST_N_VEC(812) //CAST_N_VEC(1002) const int QmitkDiffusionTensorEstimation::odfsize = 252; const int QmitkDiffusionTensorEstimation::nrconvkernels = 252; // Compile-time Square Root Computation: ceil(sqrt(N)) template struct Root; template struct Root { static const int root = Mid; }; template struct Root { static const int mean = (Low + High)/2; static const bool down = (mean * mean >= Size); static const int root = Root::root; }; QmitkDiffusionTensorEstimation::QmitkDiffusionTensorEstimation(QObject *parent, const char *name, mitk::DataTreeIteratorBase* it) : QmitkAbstractView(parent, name, it), m_Controls(nullptr) { SetAvailability(true); m_FilterInitialized = false; } QmitkDiffusionTensorEstimation::~QmitkDiffusionTensorEstimation() {} QWidget * QmitkDiffusionTensorEstimation::CreateControlWidget(QWidget *parent) { if (m_Controls == nullptr) { m_Controls = new QmitkDiffusionTensorEstimationControls(parent); } m_Controls->m_TensorEstimationTeemErrorImage->setChecked(false); m_Controls->m_TensorEstimationTeemSigmaEdit->setText("NaN"); m_Controls->m_TensorEstimationTeemEstimationMethodCombo->insertItem("LLS (Linear Least Squares)"); m_Controls->m_TensorEstimationTeemEstimationMethodCombo->insertItem("MLE (Maximum Likelihood)"); m_Controls->m_TensorEstimationTeemEstimationMethodCombo->insertItem("NLS (Nonlinear Least Squares)"); m_Controls->m_TensorEstimationTeemEstimationMethodCombo->insertItem("WLS (Weighted Least Squares)"); m_Controls->m_TensorEstimationTeemNumItsSpin->setValue(1); m_Controls->m_TensorEstimationTeemConfThresholdEdit->setText("NaN"); m_Controls->m_TensorEstimationTeemFuzzyEdit->setText("0.0"); m_Controls->m_TensorEstimationTeemMinValEdit->setText("1.0"); m_Controls->m_OdfReconstructionThreasholdEdit->setText("0.0"); m_Controls->m_OdfStandardAlgorithmsOrderSpinbox->setValue(0); m_Controls->m_OdfStandardAlgorithmsProbThreshEdit->setText(QString::number(1.0/double(odfsize))); m_Controls->m_OdfReconstructionNumberThreadsSpinbox->setValue(4); m_Controls->m_OdfReconstructionMaxLLevelComboBox->insertItem( QString("2") ); m_Controls->m_OdfReconstructionMaxLLevelComboBox->insertItem( QString("4") ); m_Controls->m_OdfReconstructionMaxLLevelComboBox->insertItem( QString("6") ); m_Controls->m_OdfReconstructionMaxLLevelComboBox->insertItem( QString("8") ); m_Controls->m_OdfReconstructionMaxLLevelComboBox->setCurrentItem( 3 ); m_Controls->m_OdfReconstructionNumberThreadsAnalyticalSpinbox->setValue(4); m_Controls->m_OdfReconstructionThreasholdAnalyticalEdit->setText("0.0"); m_Controls->m_OdfReconstructionLambdaLineEdit->setText("0.006"); m_Controls->m_OdfStandardAlgorithmsNumberThreadsSpinbox->setValue(4); m_Controls->m_OdfStandardAlgorithmsDeconvNumberThreadsSpinbox->setValue(4); m_Controls->m_OdfStandardAlgorithmsDeconvolutionThreshEdit->setText("0.1"); m_Controls->m_OdfStandardAlgorithmsDeconvolutionAngResThresholdEdit->setText("15"); m_Controls->m_OdfStandardAlgorithmsGFAParam1->setText("2"); m_Controls->m_OdfStandardAlgorithmsGFAParam2->setText("1"); return m_Controls; } void QmitkDiffusionTensorEstimation::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_TensorEstimationButton), SIGNAL(clicked()),(QObject*) this, SLOT(TensorEstimationButton())); connect( (QObject*)(m_Controls->m_OdfReconstructionButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfReconstructionButton())); connect( (QObject*)(m_Controls->m_OdfReconstructionAnalyticalButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfReconstructionAnalyticalButton())); connect( (QObject*)(m_Controls->m_TensorEstimationTeemEstimateButton), SIGNAL(clicked()),(QObject*) this, SLOT(TensorEstimationTeemEstimateButton())); connect( (QObject*)(m_Controls->m_TensorVolumesSaveButton), SIGNAL(clicked()),(QObject*) this, SLOT(TensorVolumesSaveButton())); connect( (QObject*)(m_Controls->m_TensorVolumesLoadButton), SIGNAL(clicked()),(QObject*) this, SLOT(TensorVolumesLoadButton())); connect( (QObject*)(m_Controls->m_TensorVolumesRemoveButton), SIGNAL(clicked()),(QObject*) this, SLOT(TensorVolumesRemoveButton())); connect( (QObject*)(m_Controls->m_StandardAlgorithmsFAButton), SIGNAL(clicked()),(QObject*) this, SLOT(StandardAlgorithmsFAButton())); connect( (QObject*)(m_Controls->m_StandardAlgorithmsRAButton), SIGNAL(clicked()),(QObject*) this, SLOT(StandardAlgorithmsRAButton())); connect( (QObject*)(m_Controls->m_StandardAlgorithmsDirectionButton), SIGNAL(clicked()),(QObject*) this, SLOT(StandardAlgorithmsDirectionButton())); connect( (QObject*)(m_Controls->m_OdfVolumesSaveButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfVolumesSaveButton())); connect( (QObject*)(m_Controls->m_OdfVolumesLoadButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfVolumesLoadButton())); connect( (QObject*)(m_Controls->m_OdfVolumesRemoveButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfVolumesRemoveButton())); connect( (QObject*)(m_Controls->m_OdfStandardAlgorithmsDirectionButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfStandardAlgorithmsDirectionButton())); connect( (QObject*)(m_Controls->m_OdfStandardAlgorithmsDeconvolutionButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfStandardAlgorithmsDeconvolutionButton())); connect( (QObject*)(m_Controls->m_OdfStandardAlgorithmsGFAButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfStandardAlgorithmsGFAButton())); connect( (QObject*)(m_Controls->m_OdfVolumesVisualizeSelectedButton), SIGNAL(clicked()),(QObject*) this, SLOT(OdfVolumesVisualizeSelectedButton())); connect( (QObject*)(m_Controls->m_DirectionVolumesSaveButton), SIGNAL(clicked()),(QObject*) this, SLOT(DirectionVolumesSaveButton())); connect( (QObject*)(m_Controls->m_DirectionVolumesLoadButton), SIGNAL(clicked()),(QObject*) this, SLOT(DirectionVolumesLoadButton())); connect( (QObject*)(m_Controls->m_DirectionVolumesRemoveButton), SIGNAL(clicked()),(QObject*) this, SLOT(DirectionVolumesRemoveButton())); connect( (QObject*)(m_Controls->m_DirectionVolumesAngularErrorButton), SIGNAL(clicked()),(QObject*) this, SLOT(DirectionVolumesAngularErrorButton())); connect( (QObject*)(m_Controls->m_DiffusionVolumesLoadButton), SIGNAL(clicked()),(QObject*) this, SLOT(DiffusionVolumesLoadButton())); connect( (QObject*)(m_Controls->m_DiffusionVolumeSaveButton), SIGNAL(clicked()),(QObject*) this, SLOT(DiffusionVolumeSaveButton())); connect( (QObject*)(m_Controls->m_DiffusionVolumesRemoveButton), SIGNAL(clicked()),(QObject*) this, SLOT(DiffusionVolumesRemoveButton())); connect( (QObject*)(m_Controls->m_TensorEstimationDiffusionVolumesSelectAllButton), SIGNAL(clicked()),(QObject*) this, SLOT(DiffusionVolumesSelectAll())); } } QAction * QmitkDiffusionTensorEstimation::CreateAction(QActionGroup *parent) { //action = new QAction( tr( "Brain Atrophy" ), pixmap, tr( "BrainAtrophy" ), 0, parent, "BrainAtrophy" ); QImage icon = qembed_findImage("QmitkDiffusionTensorEstimation"); QPixmap pixmap(icon); QAction* action; action = new QAction( tr( "Diffusion Tensor Estimation" ), pixmap, tr( "QmitkDiffusionTensorEstimation menu" ), 0, parent, "QmitkDiffusionTensorEstimation" ); return action; } void QmitkDiffusionTensorEstimation::TreeChanged() { m_Controls->m_TensorEstimationDiffusionVolumesSelector->Update(); m_Controls->m_TensorVolumesSelector->Update(); m_Controls->m_OdfVolumesSelector->Update(); m_Controls->m_DirectionVolumesSelector->Update(); if(m_DiffusionVolumesDataTreeFilter &&m_DiffusionVolumesDataTreeFilter->GetItems()->Size() > 0) { m_Controls->m_TensorEstimationButton->setEnabled(true); m_Controls->m_OdfReconstructionButton->setEnabled(true); m_Controls->m_OdfReconstructionAnalyticalButton->setEnabled(true); } else { m_Controls->m_OdfReconstructionButton->setEnabled(false); m_Controls->m_OdfReconstructionAnalyticalButton->setEnabled(false); m_Controls->m_TensorEstimationButton->setEnabled(false); } if(m_TensorVolumesDataTreeFilter && m_TensorVolumesDataTreeFilter->GetItems()->Size() > 0) { m_Controls->m_TensorVolumesSaveButton->setEnabled(true); m_Controls->m_TensorVolumesRemoveButton->setEnabled(true); } else { m_Controls->m_TensorVolumesSaveButton->setEnabled(false); m_Controls->m_TensorVolumesRemoveButton->setEnabled(false); } if(m_OdfVolumesDataTreeFilter && m_OdfVolumesDataTreeFilter->GetItems()->Size() > 0) { m_Controls->m_OdfVolumesSaveButton->setEnabled(true); m_Controls->m_OdfVolumesRemoveButton->setEnabled(true); m_Controls->m_OdfVolumesVisualizeSelectedButton->setEnabled(true); } else { m_Controls->m_OdfVolumesSaveButton->setEnabled(false); m_Controls->m_OdfVolumesRemoveButton->setEnabled(false); m_Controls->m_OdfVolumesVisualizeSelectedButton->setEnabled(false); } if(m_DirectionVolumesDataTreeFilter && m_DirectionVolumesDataTreeFilter->GetItems()->Size() > 0) { m_Controls->m_DirectionVolumesSaveButton->setEnabled(true); m_Controls->m_DirectionVolumesRemoveButton->setEnabled(true); } else { m_Controls->m_DirectionVolumesSaveButton->setEnabled(false); m_Controls->m_DirectionVolumesRemoveButton->setEnabled(false); } if(m_DirectionVolumesDataTreeFilter && m_DirectionVolumesDataTreeFilter->GetItems()->Size() > 1) { m_Controls->m_DirectionVolumesAngularErrorButton->setEnabled(true); } else { m_Controls->m_DirectionVolumesAngularErrorButton->setEnabled(false); } } void QmitkDiffusionTensorEstimation::Activated() { if (m_FilterInitialized) return; // diffusion volumes filter m_DiffusionVolumesDataTreeFilter = mitk::DataTreeFilter::New( GetDataTreeIterator()->GetTree() ); m_DiffusionVolumesDataTreeFilter->SetSelectionMode(mitk::DataTreeFilter::MULTI_SELECT); m_DiffusionVolumesDataTreeFilter->SetHierarchyHandling(mitk::DataTreeFilter::FLATTEN_HIERARCHY); m_DiffusionVolumesDataTreeFilter->SetFilter( mitk::IsBaseDataType >() ); // show diffusion volumes mitk::DataTreeFilter::PropertyList visible_props; visible_props.push_back("name"); m_DiffusionVolumesDataTreeFilter->SetVisibleProperties(visible_props); mitk::DataTreeFilter::PropertyList property_labels; property_labels.push_back("Diffusion Volumes"); m_DiffusionVolumesDataTreeFilter->SetPropertiesLabels(property_labels); m_Controls->m_TensorEstimationDiffusionVolumesSelector->SetDataTree( GetDataTreeIterator()->GetTree() ); m_Controls->m_TensorEstimationDiffusionVolumesSelector->SetFilter( m_DiffusionVolumesDataTreeFilter ); m_Controls->m_TensorEstimationDiffusionVolumesSelector->SetAutoUpdate( false ); m_Controls->m_TensorEstimationDiffusionVolumesSelector->setStretchedColumn(1); // tensor volumes filter m_TensorVolumesDataTreeFilter = mitk::DataTreeFilter::New( GetDataTreeIterator()->GetTree() ); m_TensorVolumesDataTreeFilter->SetSelectionMode(mitk::DataTreeFilter::SINGLE_SELECT); m_TensorVolumesDataTreeFilter->SetHierarchyHandling(mitk::DataTreeFilter::FLATTEN_HIERARCHY); m_TensorVolumesDataTreeFilter->SetFilter( mitk::IsBaseDataTypeWithBoolProperty("IsTensorVolume") ); // show tensor volumes m_TensorVolumesDataTreeFilter->SetVisibleProperties(visible_props); mitk::DataTreeFilter::PropertyList tensor_property_labels; tensor_property_labels.push_back("Tensor Volumes"); m_TensorVolumesDataTreeFilter->SetPropertiesLabels(tensor_property_labels); m_Controls->m_TensorVolumesSelector->SetDataTree( GetDataTreeIterator()->GetTree() ); m_Controls->m_TensorVolumesSelector->SetFilter( m_TensorVolumesDataTreeFilter ); m_Controls->m_TensorVolumesSelector->SetAutoUpdate( false ); m_Controls->m_TensorVolumesSelector->setStretchedColumn(1); // Odf volumes filter m_OdfVolumesDataTreeFilter = mitk::DataTreeFilter::New( GetDataTreeIterator()->GetTree() ); m_OdfVolumesDataTreeFilter->SetSelectionMode(mitk::DataTreeFilter::MULTI_SELECT); m_OdfVolumesDataTreeFilter->SetHierarchyHandling(mitk::DataTreeFilter::FLATTEN_HIERARCHY); m_OdfVolumesDataTreeFilter->SetFilter( mitk::IsBaseDataTypeWithBoolProperty("IsOdfVolume") ); m_OdfVolumesDataTreeFilter->SetVisibleProperties(visible_props); mitk::DataTreeFilter::PropertyList Odf_property_labels; Odf_property_labels.push_back("ODF Volumes"); m_OdfVolumesDataTreeFilter->SetPropertiesLabels(Odf_property_labels); m_Controls->m_OdfVolumesSelector->SetDataTree( GetDataTreeIterator()->GetTree() ); m_Controls->m_OdfVolumesSelector->SetFilter( m_OdfVolumesDataTreeFilter ); m_Controls->m_OdfVolumesSelector->SetAutoUpdate( false ); m_Controls->m_OdfVolumesSelector->setStretchedColumn(1); // direction volumes filter m_DirectionVolumesDataTreeFilter = mitk::DataTreeFilter::New( GetDataTreeIterator()->GetTree() ); m_DirectionVolumesDataTreeFilter->SetSelectionMode(mitk::DataTreeFilter::MULTI_SELECT); m_DirectionVolumesDataTreeFilter->SetHierarchyHandling(mitk::DataTreeFilter::FLATTEN_HIERARCHY); m_DirectionVolumesDataTreeFilter->SetFilter( mitk::IsBaseDataTypeWithBoolProperty("IsDirectionVolume") ); m_DirectionVolumesDataTreeFilter->SetVisibleProperties(visible_props); mitk::DataTreeFilter::PropertyList direction_property_labels; direction_property_labels.push_back("Direction Volumes"); m_DirectionVolumesDataTreeFilter->SetPropertiesLabels(direction_property_labels); m_Controls->m_DirectionVolumesSelector->SetDataTree( GetDataTreeIterator()->GetTree() ); m_Controls->m_DirectionVolumesSelector->SetFilter( m_DirectionVolumesDataTreeFilter ); m_Controls->m_DirectionVolumesSelector->SetAutoUpdate( false ); m_Controls->m_DirectionVolumesSelector->setStretchedColumn(1); m_FilterInitialized = true; TreeChanged(); } void QmitkDiffusionTensorEstimation::Deactivated() { } void QmitkDiffusionTensorEstimation::Visible() { } void QmitkDiffusionTensorEstimation::Hidden() { } void QmitkDiffusionTensorEstimation::TensorVolumesSaveButton() { // GET SELECTED ITEM const mitk::DataTreeFilter::Item* selectedItem = m_TensorVolumesDataTreeFilter->GetSelectedItem(); if( !selectedItem ) return; mitk::Image::Pointer tensorVol = static_cast(selectedItem->GetNode()->GetData()); TensorImageType::Pointer itkTensorVol = TensorImageType::New(); mitk::CastToItkImage(tensorVol, itkTensorVol); // SELECT FILE DIALOG std::string sName = selectedItem->GetNode()->GetName(); QString qName; qName.sprintf("%s.nhdr",sName.c_str()); QString filename = QFileDialog::getSaveFileName( qName, "Nrrd Images (*.nrrd *.nhdr)", this->m_Controls, "save file dialog", "Select Nrrd Outputfile" ); if ( !filename ) return; // WRITING TENSORS TO FILE MBI_INFO << "Writing tensors "; typedef itk::ImageFileWriter TensorWriterType; TensorWriterType::Pointer tensorWriter = TensorWriterType::New(); tensorWriter->SetFileName(filename.ascii()); tensorWriter->SetInput(itkTensorVol); tensorWriter->Update(); } void QmitkDiffusionTensorEstimation::TensorVolumesLoadButton() { // SELECT FOLDER DIALOG QFileDialog* w = new QFileDialog( this->m_Controls, "Select DWI data file", TRUE ); w->setMode( QFileDialog::ExistingFiles ); w->setFilter( "Nrrd Images (*.nrrd *.nhdr)" ); // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) return; QStringList filenames = w->selectedFiles(); QStringList::Iterator it = filenames.begin(); while( it != filenames.end() ) { std::string filename = ( *it ).ascii(); ++it; // READING TENSOR VOLUME typedef itk::ImageFileReader ReaderType; ReaderType::Pointer tensorReader = ReaderType::New(); tensorReader->SetFileName(filename); try { tensorReader->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; } // Tensorvolume mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( tensorReader->GetOutput() ); image->SetVolume( tensorReader->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, itksys::SystemTools::GetFilenameName(filename)); node->SetProperty( "IsTensorVolume", mitk::BoolProperty::New( true ) ); TreeChanged(); } } void QmitkDiffusionTensorEstimation::TensorVolumesRemoveButton() { m_TensorVolumesDataTreeFilter->DeleteSelectedItems(); } void QmitkDiffusionTensorEstimation::OdfVolumesSaveButton() { // GET SELECTED ITEM const mitk::DataTreeFilter::Item* selectedItem = m_OdfVolumesDataTreeFilter->GetSelectedItem(); if( !selectedItem ) return; mitk::Image::Pointer OdfVol = static_cast(selectedItem->GetNode()->GetData()); if( !OdfVol)return; typedef itk::Image,3 > IType; IType::Pointer itkOdfVol = IType::New(); mitk::CastToItkImage(OdfVol, itkOdfVol); typedef itk::VectorImage VarVecImgType; VarVecImgType::Pointer vecImg = VarVecImgType::New(); vecImg->SetSpacing( itkOdfVol->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( itkOdfVol->GetOrigin() ); // Set the image origin vecImg->SetDirection( itkOdfVol->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( itkOdfVol->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( itkOdfVol->GetLargestPossibleRegion() ); vecImg->SetVectorLength(odfsize); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot = ot.Begin(); itk::ImageRegionIterator it (itkOdfVol, itkOdfVol->GetLargestPossibleRegion() ); it = it.Begin(); MBI_DEBUG << it.Get(); for (it = it.Begin(); !it.IsAtEnd(); ++it) { itk::Vector vec = it.Get(); VarVecImgType::PixelType varvec(vec.GetDataPointer(), odfsize); ot.Set(varvec); ++ot; } // SELECT FILE DIALOG std::string sName = selectedItem->GetNode()->GetName(); QString qName; qName.sprintf("%s.nhdr",sName.c_str()); QString filename = QFileDialog::getSaveFileName( qName, "Nrrd Images (*.nrrd *.nhdr)", this->m_Controls, "save file dialog", "Select Nrrd Outputfile" ); if ( !filename ) return; // WRITING TENSORS TO FILE MBI_INFO << "Writing data "; typedef itk::ImageFileWriter OdfWriterType; OdfWriterType::Pointer OdfWriter = OdfWriterType::New(); OdfWriter->SetFileName(filename.ascii()); OdfWriter->SetInput(vecImg); OdfWriter->Update(); } void QmitkDiffusionTensorEstimation::OdfVolumesLoadButton() { // SELECT FOLDER DIALOG QFileDialog* w = new QFileDialog( this->m_Controls, "Select DWI data file", TRUE ); w->setMode( QFileDialog::ExistingFiles ); w->setFilter( "Nrrd Images (*.nrrd *.nhdr)" ); // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) return; QStringList filenames = w->selectedFiles(); QStringList::Iterator it = filenames.begin(); while( it != filenames.end() ) { std::string filename = ( *it ).ascii(); ++it; // READING TENSOR VOLUME typedef itk::Image,3 > IVType; typedef itk::ImageFileReader ReaderType; ReaderType::Pointer OdfReader = ReaderType::New(); OdfReader->SetFileName(filename); try { OdfReader->Update(); } catch (itk::ExceptionObject e) { MBI_LOG << e; } //itk::ImageRegionConstIterator it (OdfReader->GetOutput(), OdfReader->GetOutput()->GetLargestPossibleRegion() ); //it = it.Begin(); //std::cout << it.Get() << std::endl; // Tensorvolume mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( OdfReader->GetOutput() ); image->SetVolume( OdfReader->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, itksys::SystemTools::GetFilenameName(filename)); node->SetProperty( "IsOdfVolume", mitk::BoolProperty::New( true ) ); node->SetProperty( "visible", mitk::BoolProperty::New( false ) ); TreeChanged(); } } void QmitkDiffusionTensorEstimation::OdfVolumesRemoveButton() { m_OdfVolumesDataTreeFilter->DeleteSelectedItems(); } void QmitkDiffusionTensorEstimation::OdfVolumesVisualizeSelectedButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::Item* item = m_OdfVolumesDataTreeFilter->GetSelectedItem(); if(!item)return; typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; mitk::Image* vol = static_cast(item->GetNode()->GetData()); OdfVectorImgType::Pointer itkvol = OdfVectorImgType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = item->GetProperty("name"); // PREPARE FOR VISUALIZATION clock.Start(); MBI_INFO << "Preparing for Visualization "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Preparing for Visualization of %s", nodename.c_str())); typedef itk::DiffusionOdfPrepareVisualizationImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(itkvol); filter->SetNumberOfThreads(4); switch(m_Controls->m_OdfVolumesVisualizeNormalizationMethod->currentItem()) { case 0: { filter->SetNormalizationMethod(FilterType::PV_MIN_MAX); break; } case 1: { filter->SetNormalizationMethod(FilterType::PV_NONE); break; } case 2: { filter->SetNormalizationMethod(FilterType::PV_MAX); break; } case 3: { filter->SetNormalizationMethod(FilterType::PV_GLOBAL_MAX); break; } case 4: { filter->SetNormalizationMethod(FilterType::PV_MIN_MAX_INVERT); break; } default: { filter->SetNormalizationMethod(FilterType::PV_MIN_MAX); break; } } if(m_Controls->m_OdfVolumesVisualizeScaleGfaCheckbox->isChecked() ) { typedef itk::DiffusionOdfGeneralizedFaImageFilter GfaFilterType; filter->SetDoScaleGfa(true); float p1 = m_Controls->m_OdfStandardAlgorithmsGFAParam1->text().toFloat(); float p2 = m_Controls->m_OdfStandardAlgorithmsGFAParam2->text().toFloat(); switch(m_Controls->m_OdfStandardAlgorithmsGFAMethod->currentItem()) { case 0: filter->SetScaleByGfaType(GfaFilterType::GFA_STANDARD); break; case 1: filter->SetScaleByGfaType(GfaFilterType::GFA_QUANTILES_HIGH_LOW); break; case 2: filter->SetScaleByGfaType(GfaFilterType::GFA_QUANTILE_HIGH); break; case 3: filter->SetScaleByGfaType(GfaFilterType::GFA_MAX_ODF_VALUE); break; case 4: filter->SetScaleByGfaType(GfaFilterType::GFA_DECONVOLUTION_COEFFS); break; case 5: filter->SetScaleByGfaType(GfaFilterType::GFA_MIN_MAX_NORMALIZED_STANDARD); break; case 6: filter->SetScaleByGfaType(GfaFilterType::GFA_NORMALIZED_ENTROPY); break; case 7: filter->SetScaleByGfaType(GfaFilterType::GFA_NEMATIC_ORDER_PARAMETER); break; case 8: filter->SetScaleByGfaType(GfaFilterType::GFA_QUANTILES_LOW_HIGH); break; case 9: filter->SetScaleByGfaType(GfaFilterType::GFA_QUANTILE_LOW); break; case 10: filter->SetScaleByGfaType(GfaFilterType::GFA_MIN_ODF_VALUE); break; case 11: filter->SetScaleByGfaType(GfaFilterType::GFA_STD_BY_MAX); break; case 12: filter->SetScaleByGfaType(GfaFilterType::GFA_PRINCIPLE_CURVATURE); filter->SetGfaParam1(p1); break; case 13: filter->SetScaleByGfaType(GfaFilterType::GFA_GENERALIZED_GFA); filter->SetGfaParam1(p1); filter->SetGfaParam2(p2); break; default: filter->SetScaleByGfaType(GfaFilterType::GFA_STANDARD); } } filter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // VIZ TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, nodename.append(" Viz")); node->SetProperty( "IsOdfVolume", mitk::BoolProperty::New( true ) ); node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 1500 ) ); node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); node->SetProperty( "layer", mitk::IntProperty::New( 1 ) ); node->SetProperty( "global_scaling", mitk::FloatProperty::New( 1.0 ) ); mitk::OdfVtkMapper2D::Pointer odfMapper = mitk::OdfVtkMapper2D::New(); node->SetMapper(1,odfMapper); mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::DirectionVolumesSaveButton() { // GET SELECTED ITEM const mitk::DataTreeFilter::Item* selectedItem = m_DirectionVolumesDataTreeFilter->GetSelectedItem(); if( !selectedItem ) return; mitk::Image::Pointer vol = static_cast(selectedItem->GetNode()->GetData()); if( !vol)return; typedef itk::Image,3 > IType; IType::Pointer itkVol = IType::New(); mitk::CastToItkImage(vol, itkVol); typedef itk::VectorImage VarVecImgType; VarVecImgType::Pointer vecImg = VarVecImgType::New(); vecImg->SetSpacing( itkVol->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( itkVol->GetOrigin() ); // Set the image origin vecImg->SetDirection( itkVol->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( itkVol->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( itkVol->GetLargestPossibleRegion() ); vecImg->SetVectorLength(3); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot = ot.Begin(); itk::ImageRegionIterator it (itkVol, itkVol->GetLargestPossibleRegion() ); it = it.Begin(); for (it = it.Begin(); !it.IsAtEnd(); ++it) { itk::Vector vec = it.Get(); VarVecImgType::PixelType varvec(vec.GetDataPointer(), 3); ot.Set(varvec); ++ot; } // SELECT FILE DIALOG std::string sName = selectedItem->GetNode()->GetName(); QString qName; qName.sprintf("%s.nhdr",sName.c_str()); QString filename = QFileDialog::getSaveFileName( qName, "Nrrd Images (*.nrrd *.nhdr)", this->m_Controls, "save file dialog", "Select Nrrd Outputfile" ); if ( !filename ) return; // WRITING TENSORS TO FILE MBI_INFO << "Writing data "; typedef itk::ImageFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName(filename.ascii()); writer->SetInput(vecImg); writer->Update(); } void QmitkDiffusionTensorEstimation::DirectionVolumesLoadButton() { // SELECT FOLDER DIALOG QFileDialog* w = new QFileDialog( this->m_Controls, "Select DWI data file", TRUE ); w->setMode( QFileDialog::ExistingFiles ); w->setFilter( "Nrrd Images (*.nrrd *.nhdr)" ); // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) return; QStringList filenames = w->selectedFiles(); QStringList::Iterator it = filenames.begin(); while( it != filenames.end() ) { std::string filename = ( *it ).ascii(); ++it; // READING VOLUME typedef itk::Image,3 > IType; typedef itk::ImageFileReader ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(filename); try { reader->Update(); } catch (itk::ExceptionObject e) { MBI_INFO << e << std::endl; } // Tensorvolume mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( reader->GetOutput() ); image->SetVolume( reader->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, itksys::SystemTools::GetFilenameName(filename)); node->SetProperty( "IsDirectionVolume", mitk::BoolProperty::New( true ) ); mitk::VectorImageMapper2D::Pointer mapper = mitk::VectorImageMapper2D::New(); node->SetMapper(1,mapper); TreeChanged(); } } void QmitkDiffusionTensorEstimation::DirectionVolumesRemoveButton() { m_DirectionVolumesDataTreeFilter->DeleteSelectedItems(); } void QmitkDiffusionTensorEstimation::TensorEstimationTeemEstimateButton() { try { itk::TimeProbe clock; const mitk::DataTreeFilter::ItemSet* selectedItems = m_DiffusionVolumesDataTreeFilter->GetSelectedItems(); int nrFiles = selectedItems->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataTreeFilter::ItemSet::const_iterator itemiter( selectedItems->begin() ); mitk::DataTreeFilter::ItemSet::const_iterator itemiterend( selectedItems->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionVolumes* vols = static_cast*>( (*itemiter)->GetNode()->GetData()); std::string nodename = (*itemiter)->GetProperty("name"); itemiter++; // TENSOR RECONSTRUCTION clock.Start(); MBI_INFO << "Teem Tensor reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Teem Tensor reconstruction for %s", nodename.c_str())); typedef mitk::TeemDiffusionTensor3DReconstructionImageFilter< DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer tensorReconstructionFilter = TensorReconstructionImageFilterType::New(); tensorReconstructionFilter->SetInput( vols ); tensorReconstructionFilter->SetEstimateErrorImage( m_Controls->m_TensorEstimationTeemErrorImage->isChecked() ); if(!m_Controls->m_TensorEstimationTeemSigmaEdit->text().contains(QString("NaN"))) tensorReconstructionFilter->SetSigma( m_Controls->m_TensorEstimationTeemSigmaEdit->text().toFloat() ); switch(m_Controls->m_TensorEstimationTeemEstimationMethodCombo->currentItem()) { case 0: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsLLS); break; case 1: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsNLS); break; case 2: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsWLS); break; case 3: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsMLE); break; default: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsLLS); } tensorReconstructionFilter->SetNumIterations( m_Controls->m_TensorEstimationTeemNumItsSpin->value() ); if(!m_Controls->m_TensorEstimationTeemConfThresholdEdit->text().contains(QString("NaN"))) tensorReconstructionFilter->SetConfidenceThreshold( m_Controls->m_TensorEstimationTeemConfThresholdEdit->text().toDouble() ); tensorReconstructionFilter->SetConfidenceFuzzyness( m_Controls->m_TensorEstimationTeemFuzzyEdit->text().toFloat() ); tensorReconstructionFilter->SetMinPlausibleValue( m_Controls->m_TensorEstimationTeemMinValEdit->text().toDouble() ); tensorReconstructionFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // TENSORS TO DATATREE //mitk::DataNode::Pointer node=mitk::DataNode::New(); //node->SetData( tensorReconstructionFilter->GetOutput() ); //mitk::DataStorage::GetInstance()->Add(node); //SetDefaultNodeProperties(node, nodename.append(" tensors")); //node->SetProperty( "IsConfidenceTensorVolume", mitk::BoolProperty::New( true ) ); mitk::DataNode::Pointer node2=mitk::DataNode::New(); node2->SetData( tensorReconstructionFilter->GetOutputItk() ); SetDefaultNodeProperties(node2, nodename.append(" (itk)")); node2->SetProperty( "IsTensorVolume", mitk::BoolProperty::New( true ) ); nodes.push_back(node2); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) mitk::DataStorage::GetInstance()->Add(*nodeIt); mitk::ProgressBar::GetInstance()->Progress(); TreeChanged(); m_Controls->update(); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles)); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex; return ; } } void QmitkDiffusionTensorEstimation::TensorEstimationButton() { try { itk::TimeProbe clock; const mitk::DataTreeFilter::ItemSet* selectedItems = m_DiffusionVolumesDataTreeFilter->GetSelectedItems(); int nrFiles = selectedItems->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataTreeFilter::ItemSet::const_iterator itemiter( selectedItems->begin() ); mitk::DataTreeFilter::ItemSet::const_iterator itemiterend( selectedItems->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionVolumes* vols = static_cast*>( (*itemiter)->GetNode()->GetData()); std::string nodename = (*itemiter)->GetProperty("name"); itemiter++; // TENSOR RECONSTRUCTION clock.Start(); MBI_INFO << "Tensor reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Tensor reconstruction for %s", nodename.c_str())); typedef itk::DiffusionTensor3DReconstructionImageFilter< DiffusionPixelType, DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer tensorReconstructionFilter = TensorReconstructionImageFilterType::New(); tensorReconstructionFilter->SetGradientImage( vols->GetDirections(), vols->GetImage() ); tensorReconstructionFilter->SetNumberOfThreads( m_Controls->m_TensorEstimationNumberThreadsSpinbox->value() ); tensorReconstructionFilter->SetBValue(vols->GetB_Value()); tensorReconstructionFilter->SetThreshold( m_Controls->m_TensorEstimationThreasholdEdit->text().toFloat() ); tensorReconstructionFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // TENSORS TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( tensorReconstructionFilter->GetOutput() ); image->SetVolume( tensorReconstructionFilter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); SetDefaultNodeProperties(node, nodename.append(" tensors")); node->SetProperty( "IsTensorVolume", mitk::BoolProperty::New( true ) ); nodes.push_back(node); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) mitk::DataStorage::GetInstance()->Add(*nodeIt); mitk::ProgressBar::GetInstance()->Progress(); TreeChanged(); m_Controls->update(); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles)); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex; return ; } } void QmitkDiffusionTensorEstimation::OdfReconstructionButton() { try { itk::TimeProbe clock; const mitk::DataTreeFilter::ItemSet* selectedItems = m_DiffusionVolumesDataTreeFilter->GetSelectedItems(); int nrFiles = selectedItems->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataTreeFilter::ItemSet::const_iterator itemiter( selectedItems->begin() ); mitk::DataTreeFilter::ItemSet::const_iterator itemiterend( selectedItems->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionVolumes* vols = static_cast*>( (*itemiter)->GetNode()->GetData()); std::string nodename = (*itemiter)->GetProperty("name"); ++itemiter; // Odf RECONSTRUCTION clock.Start(); MBI_INFO << "Odf reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Odf reconstruction for %s", nodename.c_str())); typedef itk::DiffusionQballReconstructionImageFilter< DiffusionPixelType, DiffusionPixelType, TTensorPixelType, odfsize> //int NOdfDirections = 162, QballReconstructionImageFilterType; QballReconstructionImageFilterType::Pointer filter = QballReconstructionImageFilterType::New(); filter->SetGradientImage( vols->GetDirections(), vols->GetImage() ); filter->SetNumberOfThreads( m_Controls->m_OdfReconstructionNumberThreadsSpinbox->value() ); filter->SetBValue(vols->GetB_Value()); filter->SetThreshold( m_Controls->m_OdfReconstructionThreasholdEdit->text().toFloat() ); int normalization = m_Controls->m_OdfReconstructionPostprocessingMethod->currentItem(); switch(normalization) { case 0: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); break; } case 1: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO_B_VALUE); break; } case 2: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO); break; } case 3: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_NONE); break; } default: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); } } filter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." << std::endl; // ODFs TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( filter->GetOutput() ); //image->SetImportVolume( filter->GetOutput()->GetBufferPointer(), 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_QN%1").arg(normalization); SetDefaultNodeProperties(node, newname.ascii()); node->SetProperty( "IsOdfVolume", mitk::BoolProperty::New( true ) ); nodes.push_back(node); // B-Zero TO DATATREE mitk::Image::Pointer image4 = mitk::Image::New(); image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); mitk::DataNode::Pointer node4=mitk::DataNode::New(); node4->SetData( image4 ); SetDefaultNodeProperties(node4, nodename.append("B0")); nodes.push_back(node4); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) mitk::DataStorage::GetInstance()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles)); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex; return ; } } template void QmitkDiffusionTensorEstimation::ReconstructAnalytically( mitk::DiffusionVolumes* vols, float lambda, std::string nodename, std::vector* nodes) { typedef itk::AnalyticalDiffusionQballReconstructionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetGradientImage( vols->GetDirections(), vols->GetImage() ); filter->SetNumberOfThreads( m_Controls->m_OdfReconstructionNumberThreadsAnalyticalSpinbox->value() ); filter->SetBValue(vols->GetB_Value()); filter->SetThreshold( m_Controls->m_OdfReconstructionThreasholdAnalyticalEdit->text().toFloat() ); filter->SetLambda(lambda); filter->SetAdcProfileOnly(m_Controls->m_OdfReconstructionAdcOnlyCheckbox->isChecked()); int normalization = m_Controls->m_OdfReconstructionPostprocessingMethodAnalytical->currentItem(); switch(normalization) { case 0: { filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); break; } case 1: { filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO_B_VALUE); break; } case 2: { filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO); break; } case 3: { filter->SetNormalizationMethod(FilterType::QBAR_NONE); break; } default: { filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); } } filter->Update(); // ODFs TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); nodes->push_back(node); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_QA%1").arg(normalization); SetDefaultNodeProperties(node, newname.ascii()); node->SetProperty( "IsOdfVolume", mitk::BoolProperty::New( true ) ); // B-Zero TO DATATREE mitk::Image::Pointer image4 = mitk::Image::New(); image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); mitk::DataNode::Pointer node4=mitk::DataNode::New(); node4->SetData( image4 ); nodes->push_back(node4); SetDefaultNodeProperties(node4, nodename.append("B0")); } void QmitkDiffusionTensorEstimation::OdfReconstructionAnalyticalButton() { try { itk::TimeProbe clock; const mitk::DataTreeFilter::ItemSet* selectedItems = m_DiffusionVolumesDataTreeFilter->GetSelectedItems(); int nrFiles = selectedItems->size(); if (!nrFiles) return; std::vector lambdas; float minLambda = m_Controls->m_OdfReconstructionLambdaLineEdit->text().toFloat(); if(m_Controls->m_OdfReconstructionLambdaMultiCheckbox->isChecked()) { float stepLambda = m_Controls->m_OdfReconstructionLambdaStepLineEdit->text().toFloat(); float maxLambda = m_Controls->m_QBallReconstructionLambdaMaxLineEdit->text().toFloat(); for(float l=minLambda; lAddStepsToDo(nrFiles*nLambdas); mitk::DataTreeFilter::ItemSet::const_iterator itemiter( selectedItems->begin() ); mitk::DataTreeFilter::ItemSet::const_iterator itemiterend( selectedItems->end() ); std::vector* nodes = new std::vector(); while ( itemiter != itemiterend ) // for all items { mitk::DiffusionVolumes* vols = static_cast*>( (*itemiter)->GetNode()->GetData()); std::string nodename = (*itemiter)->GetProperty("name"); itemiter++; // Odf RECONSTRUCTION clock.Start(); MBI_INFO << "Odf reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Odf reconstruction for %s", nodename.c_str())); for(int i=0; im_OdfReconstructionMaxLLevelComboBox->currentItem()) { case 0: { ReconstructAnalytically<2>(vols, currentLambda, nodename, nodes); break; } case 1: { ReconstructAnalytically<4>(vols, currentLambda, nodename, nodes); break; } case 2: { ReconstructAnalytically<6>(vols, currentLambda, nodename, nodes); break; } case 3: { ReconstructAnalytically<8>(vols, currentLambda, nodename, nodes); break; } } clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; mitk::ProgressBar::GetInstance()->Progress(); } } std::vector::iterator nodeIt; for(nodeIt = nodes->begin(); nodeIt != nodes->end(); ++nodeIt) mitk::DataStorage::GetInstance()->Add(*nodeIt); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles)); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex; return ; } } void QmitkDiffusionTensorEstimation::StandardAlgorithmsFAButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::Item* item = m_TensorVolumesDataTreeFilter->GetSelectedItem(); if(!item)return; mitk::Image* vol = static_cast(item->GetNode()->GetData()); itk::Image::Pointer itkvol = itk::Image::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = item->GetProperty("name"); // COMPUTE FA clock.Start(); MBI_INFO << "Computing FA "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Computing FA for %s", nodename.c_str())); typedef TensorPixelType::RealValueType RealValueType; typedef itk::Image< RealValueType, 3 > FAImageType; typedef itk::TensorFractionalAnisotropyImageFilter< TensorImageType, FAImageType > FAFilterType; FAFilterType::Pointer fractionalAnisotropyFilter = FAFilterType::New(); fractionalAnisotropyFilter->SetInput( itkvol ); typedef itk::ShiftScaleImageFilter ShiftScaleFilterType; ShiftScaleFilterType::Pointer multi = ShiftScaleFilterType::New(); multi->SetShift(0); multi->SetScale(200);//itk::NumericTraits::max() multi->SetInput(fractionalAnisotropyFilter->GetOutput()); multi->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // FA TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( multi->GetOutput() ); image->SetVolume( multi->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, nodename.append(" FA")); node->SetProperty( "IsFAVolume", mitk::BoolProperty::New( true ) ); mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::StandardAlgorithmsRAButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::Item* item = m_TensorVolumesDataTreeFilter->GetSelectedItem(); if(!item)return; mitk::Image* vol = static_cast(item->GetNode()->GetData()); itk::Image::Pointer itkvol = itk::Image::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = item->GetProperty("name"); // COMPUTE RA clock.Start(); MBI_INFO << "Computing RA "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Computing RA for %s", nodename.c_str())); typedef TensorPixelType::RealValueType RealValueType; typedef itk::Image< RealValueType, 3 > RAImageType; typedef itk::TensorRelativeAnisotropyImageFilter< TensorImageType, RAImageType > RAFilterType; RAFilterType::Pointer relativeAnisotropyFilter = RAFilterType::New(); relativeAnisotropyFilter->SetInput( itkvol ); relativeAnisotropyFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // FA TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( relativeAnisotropyFilter->GetOutput() ); image->SetVolume( relativeAnisotropyFilter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, nodename.append(" RA")); node->SetProperty( "IsRAVolume", mitk::BoolProperty::New( true ) ); mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::StandardAlgorithmsDirectionButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::Item* item = m_TensorVolumesDataTreeFilter->GetSelectedItem(); if(!item)return; typedef itk::DiffusionTensor3D TensorType; typedef itk::Image TensorImgType; mitk::Image* vol = static_cast(item->GetNode()->GetData()); TensorImgType::Pointer itkvol = TensorImgType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = item->GetProperty("name"); clock.Start(); MBI_INFO << "Computing Diffusion Direction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Computing Diffusion Direction for %s", nodename.c_str())); typedef itk::DiffusionTensorPrincipleDirectionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(itkvol); filter->SetNumberOfThreads(4); filter->Update(); itk::ImageRegionIterator it (filter->GetOutput(), filter->GetOutput()->GetLargestPossibleRegion() ); it = it.Begin(); // VECTORFIELD MBI_DEBUG << "Converting to Vectorfield"; typedef itk::Image, 3> VecImgType2; VecImgType2::Pointer vecImg5 = VecImgType2::New(); vecImg5->SetSpacing( itkvol->GetSpacing() ); // Set the image spacing vecImg5->SetOrigin( itkvol->GetOrigin() ); // Set the image origin vecImg5->SetDirection( itkvol->GetDirection() ); // Set the image direction vecImg5->SetLargestPossibleRegion( itkvol->GetLargestPossibleRegion()); vecImg5->SetBufferedRegion( vecImg5->GetLargestPossibleRegion() ); vecImg5->Allocate(); itk::ImageRegionIterator ot5 (vecImg5, vecImg5->GetLargestPossibleRegion() ); ot5 = ot5.Begin(); typedef FilterType::OutputImageType::PixelType VecPixType; for (it = it.Begin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); itk::Vector pix; TTensorPixelType uvec[3] = {(TTensorPixelType)(vec[0]),(TTensorPixelType)(vec[1]),(TTensorPixelType)(vec[2])}; //TTensorPixelType uvec[3] = {(TTensorPixelType)(vec[1]),(TTensorPixelType)(-vec[0]),(TTensorPixelType)(vec[2])}; pix = uvec; ot5.Set(pix); ++ot5; } // Vectors TO DATATREE mitk::Image::Pointer image5 = mitk::Image::New(); image5->InitializeByItk( vecImg5.GetPointer() ); image5->SetVolume( vecImg5->GetBufferPointer() ); mitk::DataNode::Pointer node5=mitk::DataNode::New(); node5->SetData( image5 ); node5->SetName( nodename.append(" vecs").c_str()); mitk::DataStorage::GetInstance()->Add(node5); node5->SetProperty( "IsDirectionVolume", mitk::BoolProperty::New( true ) ); node5->SetProperty( "NormalizeVecs", mitk::BoolProperty::New( true ) ); node5->SetProperty( "Scale", mitk::FloatProperty::New( 1.0 ) ); node5->SetProperty( "LineWidth", mitk::FloatProperty::New( 1 ) ); mitk::VectorImageMapper2D::Pointer vecMapper5 = mitk::VectorImageMapper2D::New(); node5->SetMapper(1,vecMapper5); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::OdfStandardAlgorithmsGFAButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::ItemSet* selectedItems = m_OdfVolumesDataTreeFilter->GetSelectedItems(); int nrFiles = selectedItems->size(); if (!nrFiles) return; mitk::DataTreeFilter::ItemSet::const_iterator itemiter( selectedItems->begin() ); mitk::DataTreeFilter::ItemSet::const_iterator itemiterend( selectedItems->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; mitk::Image* vol = static_cast((*itemiter)->GetNode()->GetData()); OdfVectorImgType::Pointer itkvol = OdfVectorImgType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = (*itemiter)->GetProperty("name"); ++itemiter; float p1 = m_Controls->m_OdfStandardAlgorithmsGFAParam1->text().toFloat(); float p2 = m_Controls->m_OdfStandardAlgorithmsGFAParam2->text().toFloat(); // COMPUTE RA clock.Start(); MBI_INFO << "Computing GFA "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Computing GFA for %s", nodename.c_str())); typedef OdfVectorType::ValueType RealValueType; typedef itk::Image< RealValueType, 3 > RAImageType; typedef itk::DiffusionOdfGeneralizedFaImageFilter GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); gfaFilter->SetInput(itkvol); gfaFilter->SetNumberOfThreads(4); double scale = 1; std::string newname; newname.append(nodename); newname.append(" GFA"); switch(m_Controls->m_OdfStandardAlgorithmsGFAMethod->currentItem()) { case 0: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); newname.append("00"); scale = 200.0; break; } case 1: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILES_HIGH_LOW); newname.append("01"); scale = 200.0; break; } case 2: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILE_HIGH); newname.append("02"); scale = 200.0; break; } case 3: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_MAX_ODF_VALUE); newname.append("03"); scale = 200.0; break; } case 4: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_DECONVOLUTION_COEFFS); newname.append("04"); scale = 200.0; break; } case 5: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_MIN_MAX_NORMALIZED_STANDARD); newname.append("05"); scale = 200.0; break; } case 6: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_NORMALIZED_ENTROPY); newname.append("06"); break; } case 7: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_NEMATIC_ORDER_PARAMETER); newname.append("07"); scale = 200.0; break; } case 8: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILES_LOW_HIGH); newname.append("08"); scale = 200.0; break; } case 9: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_QUANTILE_LOW); newname.append("09"); scale = 200.0; break; } case 10: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_MIN_ODF_VALUE); newname.append("10"); scale = 200.0; break; } case 11: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_STD_BY_MAX); newname.append("11"); scale = 200.0; break; } case 12: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_PRINCIPLE_CURVATURE); newname.append("12"); gfaFilter->SetParam1(p1); scale = 200.0; break; } case 13: { gfaFilter->SetComputationMethod(GfaFilterType::GFA_GENERALIZED_GFA); QString paramString; paramString = paramString.append(" K%1P%2").arg(p1).arg(p2); newname.append("13").append(paramString.ascii()); gfaFilter->SetParam1(p1); gfaFilter->SetParam2(p2); scale = 200.0; break; } default: { newname.append("0"); gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); scale = 200.0; } } gfaFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; typedef itk::Image ImgType; ImgType::Pointer img = ImgType::New(); img->SetSpacing( gfaFilter->GetOutput()->GetSpacing() ); // Set the image spacing img->SetOrigin( gfaFilter->GetOutput()->GetOrigin() ); // Set the image origin img->SetDirection( gfaFilter->GetOutput()->GetDirection() ); // Set the image direction img->SetLargestPossibleRegion( gfaFilter->GetOutput()->GetLargestPossibleRegion()); img->SetBufferedRegion( gfaFilter->GetOutput()->GetLargestPossibleRegion() ); img->Allocate(); itk::ImageRegionIterator ot (img, img->GetLargestPossibleRegion() ); ot = ot.Begin(); itk::ImageRegionConstIterator it (gfaFilter->GetOutput(), gfaFilter->GetOutput()->GetLargestPossibleRegion() ); it = it.Begin(); for (it = it.Begin(); !it.IsAtEnd(); ++it) { GfaFilterType::OutputImageType::PixelType val = it.Get(); ot.Set(val * scale); ++ot; } // GFA TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( img.GetPointer() ); image->SetVolume( img->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); nodes.push_back(node); SetDefaultNodeProperties(node, newname.c_str()); node->SetProperty( "IsGFAVolume", mitk::BoolProperty::New( true ) ); mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) mitk::DataStorage::GetInstance()->Add(*nodeIt); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::OdfStandardAlgorithmsDirectionButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::Item* item = m_OdfVolumesDataTreeFilter->GetSelectedItem(); if(!item)return; typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; mitk::Image* vol = static_cast(item->GetNode()->GetData()); OdfVectorImgType::Pointer itkvol = OdfVectorImgType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = item->GetProperty("name"); clock.Start(); MBI_INFO << "Computing Diffusion Direction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Computing Diffusion Direction for %s", nodename.c_str())); typedef itk::DiffusionOdfGeneralizedFaImageFilter GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); gfaFilter->SetInput(itkvol); gfaFilter->SetNumberOfThreads(4); gfaFilter->Update(); itk::ImageRegionIterator itGfa (gfaFilter->GetOutput(), gfaFilter->GetOutput()->GetLargestPossibleRegion() ); itGfa = itGfa.Begin(); int numdir = m_Controls->m_OdfStandardAlgorithmsOrderSpinbox->value(); typedef itk::DiffusionOdfPrincipleDirectionsImageFilter PrincipleDirectionsFilterType; PrincipleDirectionsFilterType::Pointer principleDirectionsFilter = PrincipleDirectionsFilterType::New(); principleDirectionsFilter->SetThreshold(m_Controls->m_OdfStandardAlgorithmsProbThreshEdit->text().toFloat()); principleDirectionsFilter->SetNrDirectionToExtract(numdir); principleDirectionsFilter->SetInput(itkvol); principleDirectionsFilter->SetNumberOfThreads(m_Controls->m_OdfStandardAlgorithmsNumberThreadsSpinbox->value()); principleDirectionsFilter->SetMultiplyGfa(false); principleDirectionsFilter->Update(); itk::ImageRegionIterator it (principleDirectionsFilter->GetOutput(), principleDirectionsFilter->GetOutput()->GetLargestPossibleRegion() ); if(numdir == 0) { MBI_INFO << "Converting to RGB"; typedef itk::Image, 3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( itkvol->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( itkvol->GetOrigin() ); // Set the image origin vecImg->SetDirection( itkvol->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( itkvol->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( vecImg->GetLargestPossibleRegion() ); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot = ot.Begin(); typedef PrincipleDirectionsFilterType::OutputImageType::PixelType VecPixType; for (it = it.Begin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); itk::RGBPixel pix; vec*=200*itGfa.Get(); vec[0] = abs(vec[0]); vec[1] = abs(vec[1]); vec[2] = abs(vec[2]); if(vec[0] > 255 || vec[1] > 255 || vec[2] > 255) { // should never get in here double max = vec[0]; max = maxInitializeByItk( vecImg.GetPointer() ); image2->SetVolume( vecImg->GetBufferPointer() ); mitk::DataNode::Pointer node2=mitk::DataNode::New(); node2->SetData( image2 ); mitk::DataStorage::GetInstance()->Add(node2); switch(numdir) { case 0: { SetDefaultNodeProperties(node2, nodename.append(" PD0")); break; } case 1: { SetDefaultNodeProperties(node2, nodename.append(" PD1")); } case 2: { SetDefaultNodeProperties(node2, nodename.append(" PD2")); } default: { SetDefaultNodeProperties(node2, nodename.append(" PDn")); } } node2->SetProperty( "IsRGBVolume", mitk::BoolProperty::New( true ) ); } // VECTORFIELD MBI_DEBUG << "Converting to Vectorfield"; typedef itk::Image, 3> VecImgType2; VecImgType2::Pointer vecImg5 = VecImgType2::New(); vecImg5->SetSpacing( itkvol->GetSpacing() ); // Set the image spacing vecImg5->SetOrigin( itkvol->GetOrigin() ); // Set the image origin vecImg5->SetDirection( itkvol->GetDirection() ); // Set the image direction vecImg5->SetLargestPossibleRegion( itkvol->GetLargestPossibleRegion()); vecImg5->SetBufferedRegion( vecImg5->GetLargestPossibleRegion() ); vecImg5->Allocate(); itk::ImageRegionIterator ot5 (vecImg5, vecImg5->GetLargestPossibleRegion() ); ot5 = ot5.Begin(); typedef PrincipleDirectionsFilterType::OutputImageType::PixelType VecPixType; for (it = it.Begin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); itk::Vector pix; TTensorPixelType uvec[3] = {(TTensorPixelType)(vec[0]),(TTensorPixelType)(vec[1]),(TTensorPixelType)(vec[2])}; pix = uvec; ot5.Set(pix); ++ot5; } // Vectors TO DATATREE mitk::Image::Pointer image5 = mitk::Image::New(); image5->InitializeByItk( vecImg5.GetPointer() ); image5->SetVolume( vecImg5->GetBufferPointer() ); mitk::DataNode::Pointer node5=mitk::DataNode::New(); node5->SetData( image5 ); mitk::DataStorage::GetInstance()->Add(node5); switch(numdir) { case 0: { SetDefaultNodeProperties(node5, nodename.append(" Vec0")); break; } case 1: { SetDefaultNodeProperties(node5, nodename.append(" Vec1")); } case 2: { SetDefaultNodeProperties(node5, nodename.append(" Vec2")); } default: { SetDefaultNodeProperties(node5, nodename.append(" Vecn")); } } node5->SetProperty( "IsDirectionVolume", mitk::BoolProperty::New( true ) ); node5->SetProperty( "NormalizeVecs", mitk::BoolProperty::New( true ) ); node5->SetProperty( "Scale", mitk::FloatProperty::New( 0.8 ) ); node5->SetProperty( "LineWidth", mitk::FloatProperty::New( 3 ) ); mitk::VectorImageMapper2D::Pointer vecMapper5 = mitk::VectorImageMapper2D::New(); node5->SetMapper(1,vecMapper5); m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::OdfStandardAlgorithmsDeconvolutionButton() { itk::TimeProbe clock; QString status; const mitk::DataTreeFilter::Item* item = m_OdfVolumesDataTreeFilter->GetSelectedItem(); if(!item)return; typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; mitk::Image* vol = static_cast(item->GetNode()->GetData()); OdfVectorImgType::Pointer itkvol = OdfVectorImgType::New(); mitk::CastToItkImage(vol, itkvol); std::string nodename = item->GetProperty("name"); clock.Start(); MBI_INFO << "Computing Diffusion Direction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Computing Diffusion Direction for %s", nodename.c_str())); typedef itk::DiffusionOdfGeneralizedFaImageFilter GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); gfaFilter->SetInput(itkvol); gfaFilter->SetNumberOfThreads(4); gfaFilter->Update(); itk::ImageRegionIterator itGfa (gfaFilter->GetOutput(), gfaFilter->GetOutput()->GetLargestPossibleRegion() ); itGfa = itGfa.Begin(); int numdirs = m_Controls->m_OdfStandardAlgorithmsDeconvolutionSpinbox->value(); //vnl_matrix_fixed* kernels // = new vnl_matrix_fixed(); //itk::ImageRegionIterator inIt(itkvol, itkvol->GetLargestPossibleRegion()); //inIt.GoToBegin(); //for(int i=0; i DeconvolutionFilterType; DeconvolutionFilterType::Pointer devonvolutionFilter = DeconvolutionFilterType::New(); devonvolutionFilter->SetFractionalThreshold(m_Controls->m_OdfStandardAlgorithmsDeconvolutionThreshEdit->text().toFloat()); if(!m_Controls->m_OdfStandardAlgorithmsDeconvolutionAngResThresholdEdit->text().contains(QString("NaN"))) { float angRes = m_Controls->m_OdfStandardAlgorithmsDeconvolutionAngResThresholdEdit->text().toFloat(); - angRes /= 360/DIFF_EST_PI; + angRes /= 360/itk::Math::pi; devonvolutionFilter->SetAngularResolutionThreshold(angRes); } devonvolutionFilter->SetSamplingQuantileStart(m_Controls->m_OdfStandardAlgorithmsDeconvQuantStart->text().toFloat()); devonvolutionFilter->SetSamplingQuantileStep(m_Controls->m_OdfStandardAlgorithmsDeconvQuantStep->text().toFloat()); devonvolutionFilter->SetMinimumNumberOfSamples(m_Controls->m_OdfStandardAlgorithmsDeconvQuantMinNr->text().toInt()); devonvolutionFilter->SetIterateQuantiles(m_Controls->m_OdfStandardAlgorithmsDeconvQuantMulti->isChecked()); devonvolutionFilter->SetNrDirectionsToExtract(numdirs); devonvolutionFilter->SetInput(itkvol); devonvolutionFilter->SetNumberOfThreads(m_Controls->m_OdfStandardAlgorithmsDeconvNumberThreadsSpinbox->value()); devonvolutionFilter->SetGfaImage(gfaFilter->GetOutput()); //devonvolutionFilter->SetPresetConvolutionKernels(kernels); devonvolutionFilter->Update(); for(int i=0; i it (devonvolutionFilter->GetOutput(i), devonvolutionFilter->GetOutput()->GetLargestPossibleRegion() ); it = it.Begin(); if(i==0) { MBI_INFO << "Converting to RGB"; typedef itk::Image, 3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( itkvol->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( itkvol->GetOrigin() ); // Set the image origin vecImg->SetDirection( itkvol->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( itkvol->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( vecImg->GetLargestPossibleRegion() ); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot = ot.Begin(); typedef DeconvolutionFilterType::OutputImageType::PixelType VecPixType; for (it = it.Begin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); vnl_vector_fixed vnlvec = vec.GetVnlVector(); TTensorPixelType len = vnlvec.two_norm(); vnlvec = vnlvec.normalize(); itk::RGBPixel pix; vnlvec*=200*itGfa.Get(); vnlvec[0] = abs(vnlvec[0]); vnlvec[1] = abs(vnlvec[1]); vnlvec[2] = abs(vnlvec[2]); if(vnlvec[0] > 255 || vnlvec[1] > 255 || vnlvec[2] > 255) { //should never get in here double max = vnlvec[0]; max = maxInitializeByItk( vecImg.GetPointer() ); image2->SetVolume( vecImg->GetBufferPointer() ); mitk::DataNode::Pointer node2=mitk::DataNode::New(); node2->SetData( image2 ); mitk::DataStorage::GetInstance()->Add(node2); switch(i) { case 0: { SetDefaultNodeProperties(node2, nodename.append(" PD0")); break; } case 1: { SetDefaultNodeProperties(node2, nodename.append(" PD1")); break; } case 2: { SetDefaultNodeProperties(node2, nodename.append(" PD2")); break; } default: { SetDefaultNodeProperties(node2, nodename.append(" PDn")); break; } } node2->SetProperty( "IsRGBVolume", mitk::BoolProperty::New( true ) ); } // VECTORFIELD MBI_INFO << "Converting to Vectorfield"; typedef itk::Image, 3> VecImgType2; VecImgType2::Pointer vecImg5 = VecImgType2::New(); vecImg5->SetSpacing( itkvol->GetSpacing() ); // Set the image spacing vecImg5->SetOrigin( itkvol->GetOrigin() ); // Set the image origin vecImg5->SetDirection( itkvol->GetDirection() ); // Set the image direction vecImg5->SetLargestPossibleRegion( itkvol->GetLargestPossibleRegion()); vecImg5->SetBufferedRegion( vecImg5->GetLargestPossibleRegion() ); vecImg5->Allocate(); itk::ImageRegionIterator ot5 (vecImg5, vecImg5->GetLargestPossibleRegion() ); ot5 = ot5.Begin(); typedef DeconvolutionFilterType::OutputImageType::PixelType VecPixType; for (it = it.Begin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); vnl_vector_fixed vnlvec = vec.GetVnlVector(); vnlvec = vnlvec.normalize(); itk::Vector pix; TTensorPixelType uvec[3] = {(TTensorPixelType)(vnlvec[0]),(TTensorPixelType)(vnlvec[1]),(TTensorPixelType)(vnlvec[2])}; pix = uvec; ot5.Set(pix); ++ot5; } // Vectors TO DATATREE mitk::Image::Pointer image5 = mitk::Image::New(); image5->InitializeByItk( vecImg5.GetPointer() ); image5->SetVolume( vecImg5->GetBufferPointer() ); mitk::DataNode::Pointer node5=mitk::DataNode::New(); node5->SetData( image5 ); mitk::DataStorage::GetInstance()->Add(node5); switch(i) { case 0: { SetDefaultNodeProperties(node5, nodename.append(" Vec0")); break; } case 1: { SetDefaultNodeProperties(node5, nodename.append(" Vec1")); break; } case 2: { SetDefaultNodeProperties(node5, nodename.append(" Vec2")); break; } default: { SetDefaultNodeProperties(node5, nodename.append(" Vecn")); break; } } node5->SetProperty( "IsDirectionVolume", mitk::BoolProperty::New( true ) ); node5->SetProperty( "NormalizeVecs", mitk::BoolProperty::New( true ) ); node5->SetProperty( "Scale", mitk::FloatProperty::New( 0.8 ) ); node5->SetProperty( "LineWidth", mitk::FloatProperty::New( 3 ) ); mitk::VectorImageMapper2D::Pointer vecMapper5 = mitk::VectorImageMapper2D::New(); node5->SetMapper(1,vecMapper5); } m_DataTreeIterator->GetTree()->Modified(); this->GetRenderWindowPart()->RequestUpdate(); TreeChanged(); m_Controls->update(); } void QmitkDiffusionTensorEstimation::SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name) { node->SetProperty( "volumerendering", mitk::BoolProperty::New( false ) ); node->SetProperty( "use color", mitk::BoolProperty::New( true ) ); node->SetProperty( "texture interpolation", mitk::BoolProperty::New( true ) ); node->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); node->SetProperty( "layer", mitk::IntProperty::New(0)); node->SetProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); node->SetOpacity(1.0f); node->SetColor(1.0,1.0,1.0); node->SetVisibility(true); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; // levelwindow.SetAuto( image ); levWinProp->SetLevelWindow( levelwindow ); node->GetPropertyList()->SetProperty( "levelwindow", levWinProp ); // add a default rainbow lookup table for color mapping if(!node->GetProperty("LookupTable")) { mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); vtkLut->SetHueRange(0.6667, 0.0); vtkLut->SetTableRange(0.0, 20.0); vtkLut->Build(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "LookupTable", mitkLutProp ); } if(!node->GetProperty("binary")) node->SetProperty( "binary", mitk::BoolProperty::New( false ) ); // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); // set foldername as string property mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); node->SetProperty( "name", nameProp ); } void QmitkDiffusionTensorEstimation::DirectionVolumesAngularErrorButton() { try { const mitk::DataTreeFilter::ItemSet* selectedItems = m_DirectionVolumesDataTreeFilter->GetSelectedItems(); int nrFiles = selectedItems->size(); if (nrFiles != 2) return; mitk::DataTreeFilter::ItemSet::const_iterator itemiter( selectedItems->begin() ); mitk::Image::Pointer vol1 = static_cast((*itemiter)->GetNode()->GetData()); if( !vol1)return; std::string nodename1 = (*itemiter)->GetProperty("name"); itemiter++; mitk::Image::Pointer vol2 = static_cast((*itemiter)->GetNode()->GetData()); if( !vol2)return; std::string nodename2 = (*itemiter)->GetProperty("name"); typedef itk::Image,3 > IType; IType::Pointer itkVol1 = IType::New(); mitk::CastToItkImage(vol1, itkVol1); IType::Pointer itkVol2 = IType::New(); mitk::CastToItkImage(vol2, itkVol2); typedef itk::VectorImagesAngularErrorImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(itkVol1); filter->SetImage2(itkVol2.GetPointer()); filter->SetNumberOfThreads(4); filter->Update(); // Angluar Error TO DATATREE mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, nodename1.append(" ").append(nodename2).append(" ERR")); node->SetProperty( "IsErrorVolume", mitk::BoolProperty::New( true ) ); TreeChanged(); m_Controls->update(); QString status; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished computing Angular Error")); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex; return ; } } //void QmitkDiffusionTensorEstimation::DwiStandardAlgorithmsGFAButton() //{ // // itk::TimeProbe clock; // QString status; // const mitk::DataTreeFilter::Item* item // = m_DiffusionVolumesDataTreeFilter->GetSelectedItem(); // if(!item)return; // // typedef itk::Vector OdfVectorType; // typedef itk::Image OdfVectorImgType; // mitk::Image* vol = // static_cast(item->GetNode()->GetData()); // OdfVectorImgType::Pointer itkvol = OdfVectorImgType::New(); // mitk::CastToItkImage(vol, itkvol); // std::string nodename = item->GetProperty("name"); // // // COMPUTE RA // clock.Start(); // std::cout << "Computing GFA "; // mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( // "Computing GFA for %s", nodename.c_str())); // typedef OdfVectorType::ValueType RealValueType; // typedef itk::Image< RealValueType, 3 > RAImageType; // typedef itk::DiffusionOdfGeneralizedFaImageFilter // GfaFilterType; // GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); // gfaFilter->SetInput(itkvol); // gfaFilter->SetNumberOfThreads(4); // switch(m_Controls->m_OdfStandardAlgorithmsGFAMethodSpinbox->value()) // { // case 1: // { // gfaFilter->SetComputationMethod(GfaFilterType::STANDARD); // break; // } // case 2: // { // gfaFilter->SetComputationMethod(GfaFilterType::QUANTILES_HIGH_LOW); // break; // } // case 3: // { // gfaFilter->SetComputationMethod(GfaFilterType::QUANTILES_MIDDLE); // break; // } // case 4: // { // gfaFilter->SetComputationMethod(GfaFilterType::MAX_ODF_VALUE); // break; // } // case 5: // { // gfaFilter->SetComputationMethod(GfaFilterType::DECONVOLUTION_COEFFS); // break; // } // default: // { // gfaFilter->SetComputationMethod(GfaFilterType::STANDARD); // } // } // gfaFilter->Update(); // clock.Stop(); // std::cout << "took " << clock.GetMeanTime() << "s." << std::endl; // // typedef itk::Image ImgType; // ImgType::Pointer img = ImgType::New(); // img->SetSpacing( gfaFilter->GetOutput()->GetSpacing() ); // Set the image spacing // img->SetOrigin( gfaFilter->GetOutput()->GetOrigin() ); // Set the image origin // img->SetDirection( gfaFilter->GetOutput()->GetDirection() ); // Set the image direction // img->SetLargestPossibleRegion( gfaFilter->GetOutput()->GetLargestPossibleRegion()); // img->SetBufferedRegion( gfaFilter->GetOutput()->GetLargestPossibleRegion() ); // img->Allocate(); // itk::ImageRegionIterator ot (img, img->GetLargestPossibleRegion() ); // ot = ot.Begin(); // itk::ImageRegionConstIterator it // (gfaFilter->GetOutput(), gfaFilter->GetOutput()->GetLargestPossibleRegion() ); // it = it.Begin(); // // for (it = it.Begin(); !it.IsAtEnd(); ++it) // { // GfaFilterType::OutputImageType::PixelType val = it.Get(); // ot.Set(val * 200); // ++ot; // } // // // // GFA TO DATATREE // mitk::Image::Pointer image = mitk::Image::New(); // image->InitializeByItk( img.GetPointer() ); // image->SetVolume( img->GetBufferPointer() ); // mitk::DataNode::Pointer node=mitk::DataNode::New(); // node->SetData( image ); // mitk::DataStorage::GetInstance()->Add(node); // SetDefaultNodeProperties(node, nodename.append(" GFA")); // node->SetProperty( "IsGFAVolume", mitk::BoolProperty::New( true ) ); // // mitk::StatusBar::GetInstance()->DisplayText("Computation complete."); // // m_DataTreeIterator->GetTree()->Modified(); // this->GetRenderWindowPart()->RequestUpdate(); // TreeChanged(); // m_Controls->update(); // //} void QmitkDiffusionTensorEstimation::DiffusionVolumeSaveButton() { // GET SELECTED ITEM const mitk::DataTreeFilter::Item* selectedItem = m_DiffusionVolumesDataTreeFilter->GetSelectedItem(); if( !selectedItem ) return; mitk::DiffusionVolumes::Pointer diffVolumes = static_cast*>(selectedItem->GetNode()->GetData()); std::string sName = selectedItem->GetNode()->GetName(); QString qName; qName.sprintf("%s.nhdr",sName.c_str()); // SELECT FILE DIALOG //QFileDialog::getSaveFileName() //QFileDialog* w = new QFileDialog( this->m_Controls, "Select Nrrd Outputfile", TRUE ); //w->setMode( QFileDialog::AnyFile ); //w->setFilter( "Nrrd Images (*.nrrd *.nhdr)" ); //w->setName(qName); //if ( w->exec() != QDialog::Accepted ) // return; //QString filename = w->selectedFile(); QString filename = QFileDialog::getSaveFileName( qName, "Nrrd Images (*.nrrd *.nhdr)", this->m_Controls, "save file dialog", "Select Nrrd Outputfile" ); if ( !filename ) return; // WRITING DWIs TO NRRD VOLUME typedef mitk::NrrdDiffusionVolumesWriter WriterType; WriterType::Pointer nrrdWriter = WriterType::New(); nrrdWriter->SetInput( diffVolumes->GetImage() ); nrrdWriter->SetDirections(diffVolumes->GetDirections()); nrrdWriter->SetB_Value(diffVolumes->GetB_Value()); nrrdWriter->SetFileName(filename.ascii()); try { nrrdWriter->Update(); } catch (itk::ExceptionObject e) { MBI_INFO << e; } } void QmitkDiffusionTensorEstimation::DiffusionVolumesLoadButton() { // SELECT FOLDER DIALOG QFileDialog* w = new QFileDialog( this->m_Controls, "Select DWI data file", TRUE ); w->setMode( QFileDialog::ExistingFiles ); w->setFilter( "Nrrd Images (*.nrrd *.nhdr)" ); // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) return; QStringList filenames = w->selectedFiles(); QStringList::Iterator it = filenames.begin(); while( it != filenames.end() ) { std::string filename = ( *it ).ascii(); ++it; // READING NRRD DWI VOLUMES typedef mitk::NrrdDiffusionVolumesReader ReaderType; ReaderType::Pointer nrrdReader = ReaderType::New(); nrrdReader->SetFileName(filename); try { nrrdReader->Update(); // DWI TO DATATREE typedef mitk::DiffusionVolumes DiffVolumesType; DiffVolumesType::Pointer diffVolumes = DiffVolumesType::New(); diffVolumes->SetDirections(nrrdReader->GetDiffusionVectors()); diffVolumes->SetB_Value(nrrdReader->GetB_Value()); diffVolumes->SetImage(nrrdReader->GetOutput()); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( diffVolumes ); mitk::DataStorage::GetInstance()->Add(node); SetDefaultNodeProperties(node, itksys::SystemTools::GetFilenameName(filename)); TreeChanged(); } catch (itk::ExceptionObject e) { MBI_INFO << e; } } } void QmitkDiffusionTensorEstimation::DiffusionVolumesRemoveButton() { m_DiffusionVolumesDataTreeFilter->DeleteSelectedItems(); } void QmitkDiffusionTensorEstimation::DiffusionVolumesSelectAll() { const mitk::DataTreeFilter::ItemList* items = m_DiffusionVolumesDataTreeFilter->GetItems(); mitk::DataTreeFilter::ConstItemIterator itemiter( items->Begin() ); mitk::DataTreeFilter::ConstItemIterator itemiterend( items->End() ); while ( itemiter != itemiterend ) { m_DiffusionVolumesDataTreeFilter->SelectItem(*itemiter); ++itemiter; } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp index eaed0fb39c..563a0d7966 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp @@ -1,315 +1,314 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ -#define _USE_MATH_DEFINES + #include "mitkNavigationDataCSVSequentialPlayer.h" #include #include #include #include -#include mitk::NavigationDataCSVSequentialPlayer::NavigationDataCSVSequentialPlayer() : mitk::NavigationDataPlayerBase() { m_NavigationDatas = std::vector(); m_CurrentPos = 0; m_Filetype = mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV; } mitk::NavigationDataCSVSequentialPlayer::~NavigationDataCSVSequentialPlayer() { } bool mitk::NavigationDataCSVSequentialPlayer::IsAtEnd() { return m_CurrentPos >= static_cast(m_NavigationDatas.size()); } void mitk::NavigationDataCSVSequentialPlayer:: SetFileName(const std::string& fileName) { this->SetNumberOfIndexedOutputs(1); FillOutputEmpty(0); MITK_INFO << "Reading file: " << fileName; m_NavigationDatas = GetNavigationDatasFromFile(fileName); this->Modified(); } void mitk::NavigationDataCSVSequentialPlayer::FillOutputEmpty(int number) { this->SetNthOutput(number, GetEmptyNavigationData()); } mitk::NavigationData::Pointer mitk::NavigationDataCSVSequentialPlayer::GetEmptyNavigationData() { mitk::NavigationData::Pointer emptyNd = mitk::NavigationData::New(); mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); position.Fill(0.0); emptyNd->SetPosition(position); emptyNd->SetOrientation(orientation); emptyNd->SetDataValid(false); return emptyNd; } int mitk::NavigationDataCSVSequentialPlayer::GetNumberOfSnapshots() { return m_NavigationDatas.size(); } void mitk::NavigationDataCSVSequentialPlayer::GenerateData() { for (unsigned int index = 0; index < this->GetNumberOfOutputs(); index++) { mitk::NavigationData* output = this->GetOutput(index); if (m_CurrentPos > static_cast(m_NavigationDatas.size())) { FillOutputEmpty(index); return; } output->Graft(this->m_NavigationDatas.at(m_CurrentPos)); m_CurrentPos++; } } void mitk::NavigationDataCSVSequentialPlayer::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } std::vector mitk::NavigationDataCSVSequentialPlayer::GetNavigationDatasFromFile(std::string filename) { std::vector returnValue = std::vector(); std::vector fileContentLineByLine = GetFileContentLineByLine(filename); std::size_t i = m_HeaderRow ? 1 //file has a header row, so it has to be skipped when reading the NavigationDatas : 0; //file has no header row, so no need to skip the first row for ( ; i < fileContentLineByLine.size(); ++i) { returnValue.push_back(GetNavigationDataOutOfOneLine(fileContentLineByLine.at(i))); } return returnValue; } std::vector mitk::NavigationDataCSVSequentialPlayer::GetFileContentLineByLine(std::string filename) { std::vector readData = std::vector(); //save old locale char * oldLocale; oldLocale = setlocale(LC_ALL, 0); //define own locale std::locale C("C"); setlocale(LC_ALL, "C"); //read file std::ifstream file; file.open(filename.c_str(), std::ios::in); if (file.good()) { //read out file file.seekg(0L, std::ios::beg); // move to begin of file int count = 0; while (!file.eof()) { std::string buffer; std::getline(file, buffer); // read out file line by line readData.push_back(buffer); ++count; if (count == m_SampleCount) count = 0; } } file.close(); //switch back to old locale setlocale(LC_ALL, oldLocale); return readData; } mitk::NavigationData::Pointer mitk::NavigationDataCSVSequentialPlayer::GetNavigationDataOutOfOneLine(std::string line) { mitk::NavigationData::Pointer returnValue = mitk::NavigationData::New(); QString myLine = QString(line.c_str()); QStringList myLineList = myLine.split(m_SeparatorSign); mitk::Point3D position; mitk::Quaternion orientation; bool valid = false; //this is for custom csv files. You have adapt the column numbers to correctly //interpret your csv file. if (m_Filetype == mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV) { if (myLineList.size() < m_MinNumberOfColumns) { MITK_ERROR << "Error: cannot read line: only found " << myLineList.size() << " fields. Last field: " << myLineList.at(myLineList.size() - 1).toStdString(); returnValue = GetEmptyNavigationData(); return returnValue; } valid = true; //if no valid flag is given: simply set to true //############# Variant for the Aurora measurements ############### //############# (CUSTOM .csv files from MITK) ############### position[0] = myLineList.at(3).toDouble(); position[1] = myLineList.at(4).toDouble(); position[2] = myLineList.at(5).toDouble(); orientation[0] = myLineList.at(6).toDouble(); //qx orientation[1] = myLineList.at(7).toDouble(); //qy orientation[2] = myLineList.at(8).toDouble(); //qz orientation[3] = myLineList.at(9).toDouble(); //qr if(!m_RightHanded) //MITK uses a right handed coordinate system, so the position needs to be converted { position[0] = position[0]*(-1); } if (m_UseQuats) //Use Quaternions to construct the orientation of the NavigationData { orientation[0] = myLineList.at(m_Qx).toDouble(); //qx orientation[1] = myLineList.at(m_Qy).toDouble(); //qy orientation[2] = myLineList.at(m_Qz).toDouble(); //qz orientation[3] = myLineList.at(m_Qr).toDouble(); //qr } else //Use the Euler Angles to construct the orientation of the NavigationData { double azimuthAngle; double elevationAngle; double rollAngle; if(m_Azimuth < 0) //azimuth is not defined so set him to zero { azimuthAngle = 0; } else { azimuthAngle = myLineList.at(m_Azimuth).toDouble(); } if(m_Elevation < 0)// elevation is not defined so set him to zero { elevationAngle = 0; } else { elevationAngle = myLineList.at(m_Elevation).toDouble(); } if(m_Roll < 0) //roll is not defined so set him to zero { rollAngle = 0; } else { rollAngle = myLineList.at(m_Roll).toDouble(); } if (!m_EulersInRadiants) //the Euler Angles are in Degrees but MITK uses radiants so they need to be converted { - azimuthAngle = azimuthAngle / 180 * M_PI; - elevationAngle = elevationAngle / 180 * M_PI; - rollAngle = rollAngle / 180 * M_PI; + azimuthAngle = azimuthAngle / 180 * itk::Math::pi; + elevationAngle = elevationAngle / 180 * itk::Math::pi; + rollAngle = rollAngle / 180 * itk::Math::pi; } vnl_quaternion eulerQuat(rollAngle, elevationAngle, azimuthAngle); orientation = eulerQuat; } if(!m_RightHanded) //MITK uses a right handed coordinate system, so the orientation needs to be converted { //code block for conversion from left-handed to right-handed mitk::Quaternion linksZuRechtsdrehend; - double rotationAngle = -M_PI; + double rotationAngle = -itk::Math::pi; double rotationAxis[3]; rotationAxis[0] = 0; rotationAxis[1] = 0; rotationAxis[2] = 1; linksZuRechtsdrehend[3] = cos(rotationAngle / 2); linksZuRechtsdrehend[0] = rotationAxis[0] * sin(rotationAngle / 2); linksZuRechtsdrehend[1] = rotationAxis[1] * sin(rotationAngle / 2); linksZuRechtsdrehend[2] = rotationAxis[2] * sin(rotationAngle / 2); orientation = orientation * linksZuRechtsdrehend; } } //this is for MITK csv files that have been recorded with the MITK //navigation data recorder. You can also use the navigation data player //class from the MITK-IGT module instead. else if (m_Filetype == mitk::NavigationDataCSVSequentialPlayer::NavigationDataCSV) { if (myLineList.size() < 8) { MITK_ERROR << "Error: cannot read line: only found " << myLineList.size() << " fields. Last field: " << myLineList.at(myLineList.size() - 1).toStdString(); returnValue = GetEmptyNavigationData(); return returnValue; } if (myLineList.at(3).toStdString() == "1") valid = true; position[0] = myLineList.at(2).toDouble(); position[1] = myLineList.at(3).toDouble(); position[2] = myLineList.at(4).toDouble(); orientation[0] = myLineList.at(5).toDouble(); //qx orientation[1] = myLineList.at(6).toDouble(); //qy orientation[2] = myLineList.at(7).toDouble(); //qz orientation[3] = myLineList.at(8).toDouble(); //qr } returnValue->SetDataValid(valid); returnValue->SetPosition(position); returnValue->SetOrientation(orientation); return returnValue; } void mitk::NavigationDataCSVSequentialPlayer::SetOptions(bool rightHanded, char separatorSign, int sampleCount, bool headerRow, int xPos, int yPos, int zPos, bool useQuats, int qx, int qy, int qz, int qr, int azimuth, int elevation, int roll, bool eulerInRadiants, int minNumberOfColumns) { m_RightHanded = rightHanded; m_SeparatorSign = separatorSign; m_SampleCount = sampleCount; m_HeaderRow = headerRow; m_XPos = xPos; m_YPos = yPos; m_ZPos = zPos; m_UseQuats = useQuats; m_Qx = qx; m_Qy = qy; m_Qz = qz; m_Qr = qr; m_Azimuth = azimuth; m_Elevation = elevation; m_Roll = roll; m_EulersInRadiants = eulerInRadiants; m_MinNumberOfColumns = minNumberOfColumns; }