diff --git a/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp b/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp index 73240049dc..eca061815f 100644 --- a/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp +++ b/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp @@ -1,314 +1,320 @@ /*=================================================================== 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 <mitkTestFixture.h> #include "mitkIOUtil.h" #include "itkArray2D.h" #include <mitkLibSVMClassifier.h> #include <itkLabelSampler.h> #include <mitkImageCast.h> #include <mitkStandaloneDataStorage.h> #include <itkCSVArray2DFileReader.h> #include <itkCSVArray2DDataObject.h> #include <itkCSVNumericObjectFileWriter.h> //#include <boost/algorithm/string.hpp> class mitkLibSVMClassifierTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLibSVMClassifierTestSuite); MITK_TEST(TrainSVMClassifier_MatlabDataSet_shouldReturnTrue); MITK_TEST(TrainSVMClassifier_BreastCancerDataSet_shouldReturnTrue); CPPUNIT_TEST_SUITE_END(); private: typedef Eigen::Matrix<double ,Eigen::Dynamic,Eigen::Dynamic> MatrixDoubleType; typedef Eigen::Matrix<int, Eigen::Dynamic,Eigen::Dynamic> MatrixIntType; Eigen::MatrixXd m_TrainingMatrixX; Eigen::MatrixXi m_TrainingLabelMatrixY; Eigen::MatrixXd m_TestXPredict; Eigen::MatrixXi m_TestYPredict; mitk::LibSVMClassifier::Pointer classifier; public: /*Reading an file, which includes the trainingdataset and the testdataset, and convert the content of the file into an 2dim matrixpair. There are an delimiter, which separates the matrix into an trainingmatrix and testmatrix */ template<typename T> std::pair<Eigen::Matrix<T ,Eigen::Dynamic,Eigen::Dynamic>,Eigen::Matrix<T ,Eigen::Dynamic,Eigen::Dynamic> >convertCSVToMatrix(const std::string &path, char delimiter,double range, bool isXMatrix) { typename itk::CSVArray2DFileReader<T>::Pointer fr = itk::CSVArray2DFileReader<T>::New(); fr->SetFileName(path); fr->SetFieldDelimiterCharacter(delimiter); fr->HasColumnHeadersOff(); fr->HasRowHeadersOff(); fr->Parse(); try{ fr->Update(); }catch(itk::ExceptionObject& ex){ cout << "Exception caught!" << std::endl; cout << ex << std::endl; } typename itk::CSVArray2DDataObject<T>::Pointer p = fr->GetOutput(); unsigned int maxrowrange = p->GetMatrix().rows(); unsigned int c = p->GetMatrix().cols(); unsigned int percentRange = (unsigned int)(maxrowrange*range); if(isXMatrix == true){ Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> trainMatrixX(percentRange,c); Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> testMatrixXPredict(maxrowrange-percentRange,c); - for(int row = 0; row < percentRange; row++){ - for(int col = 0; col < c; col++){ + for(unsigned int row = 0; row < percentRange; row++){ + for(unsigned int col = 0; col < c; col++){ trainMatrixX(row,col) = p->GetData(row,col); } } - for(int row = percentRange; row < maxrowrange; row++){ - for(int col = 0; col < c; col++){ + for(unsigned int row = percentRange; row < maxrowrange; row++){ + for(unsigned int col = 0; col < c; col++){ testMatrixXPredict(row-percentRange,col) = p->GetData(row,col); } } return std::make_pair(trainMatrixX,testMatrixXPredict); } else if(isXMatrix == false){ Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> trainLabelMatrixY(percentRange,c); Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> testMatrixYPredict(maxrowrange-percentRange,c); - for(int row = 0; row < percentRange; row++){ - for(int col = 0; col < c; col++){ + for(unsigned int row = 0; row < percentRange; row++){ + for(unsigned int col = 0; col < c; col++){ trainLabelMatrixY(row,col) = p->GetData(row,col); } } - for(int row = percentRange; row < maxrowrange; row++){ - for(int col = 0; col < c; col++){ + for(unsigned int row = percentRange; row < maxrowrange; row++){ + for(unsigned int col = 0; col < c; col++){ testMatrixYPredict(row-percentRange,col) = p->GetData(row,col); } } return std::make_pair(trainLabelMatrixY,testMatrixYPredict); } } /* Reading an csv-data and transfer the included datas into an matrix. */ template<typename T> Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> readCsvData(const std::string &path, char delimiter) { typename itk::CSVArray2DFileReader<T>::Pointer fr = itk::CSVArray2DFileReader<T>::New(); fr->SetFileName(path); fr->SetFieldDelimiterCharacter(delimiter); fr->HasColumnHeadersOff(); fr->HasRowHeadersOff(); fr->Parse(); try{ fr->Update(); }catch(itk::ExceptionObject& ex){ cout << "Exception caught!" << std::endl; cout << ex << std::endl; } typename itk::CSVArray2DDataObject<T>::Pointer p = fr->GetOutput(); unsigned int maxrowrange = p->GetMatrix().rows(); unsigned int maxcols = p->GetMatrix().cols(); Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> matrix(maxrowrange,maxcols); for(int rows = 0; rows < maxrowrange; rows++){ for(int cols = 0; cols < maxcols; cols++ ){ matrix(rows,cols) = p->GetData(rows,cols); } } return matrix; } /* Write the content of the array into an own csv-data in the following sequence: root.csv: 1 2 3 0 0 4 writen.csv: 1 1:2 2:3 3:0 4:0 5:4 */ template<typename T> void writeMatrixToCsv(Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> paramMatrix,const std::string &path) { std::ofstream outputstream (path,std::ofstream::out); // 682 if(outputstream.is_open()){ for(int i = 0; i < paramMatrix.rows(); i++){ outputstream << paramMatrix(i,0); for(int j = 1; j < 11; j++){ outputstream << " " << j << ":" << paramMatrix(i,j); } outputstream << endl; } outputstream.close(); } else{ cout << "Unable to write into CSV" << endl; } } /* Train the classifier with an exampledataset of mattlab. Note: The included data are gau�an normaldistributed. */ void TrainSVMClassifier_MatlabDataSet_shouldReturnTrue() { /* Declarating an featurematrixdataset, the first matrix of the matrixpair is the trainingmatrix and the second one is the testmatrix.*/ std::pair<MatrixDoubleType,MatrixDoubleType> matrixDouble; matrixDouble = convertCSVToMatrix<double>(GetTestDataFilePath("Classification/FeaturematrixMatlab.csv"),';',0.5,true); m_TrainingMatrixX = matrixDouble.first; m_TestXPredict = matrixDouble.second; /* The declaration of the labelmatrixdataset is equivalent to the declaration of the featurematrixdataset.*/ std::pair<MatrixIntType,MatrixIntType> matrixInt; matrixInt = convertCSVToMatrix<int>(GetTestDataFilePath("Classification/LabelmatrixMatlab.csv"),';',0.5,false); m_TrainingLabelMatrixY = matrixInt.first; m_TestYPredict = matrixInt.second; classifier = mitk::LibSVMClassifier::New(); /* Setting of the SVM-Parameters*/ classifier->SetGamma(1/(double)(m_TrainingMatrixX.cols())); classifier->SetSvmType(0); classifier->SetKernelType(0); /* Train the classifier, by giving trainingdataset for the labels and features. The result in an colunmvector of the labels.*/ classifier->Train(m_TrainingMatrixX,m_TrainingLabelMatrixY); Eigen::MatrixXi classes = classifier->Predict(m_TestXPredict); /* Testing the matching between the calculated colunmvector and the result of the SVM */ unsigned int maxrows = classes.rows(); bool isYPredictVector = false; int count = 0; - for(int i= 0; i < maxrows; i++){ - if(classes(i,0) == m_TestYPredict(i,0)){ + for (unsigned int i = 0; i < maxrows; i++) + { + if(classes(i, 0) == m_TestYPredict(i, 0)) + { isYPredictVector = true; - count++; + ++count; } } + MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isEqual<int>(m_TestYPredict,classes),"Expected vector and occured vector match."); } // Method of testing for assertions. template<typename T> bool isEqual(Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> expected, Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> actual) { bool isSimilar = true; unsigned int mrow = expected.rows(); unsigned int mcol = expected.cols(); - for(int i = 0; i < mrow; i++){ - for(int j = 0; j < mcol; j++){ + for(unsigned int i = 0; i < mrow; i++){ + for(unsigned int j = 0; j < mcol; j++){ if(expected(i,j) != actual(i,j)){ isSimilar = false; } } } return isSimilar; } // Method of intervalltesting template<typename T> bool isIntervall(Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> expected, Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> actual, double lowrange, double toprange) { bool isInIntervall = false; int count = 0; unsigned int rowRange = expected.rows(); unsigned int colRange = expected.cols(); - for(int i = 0; i < rowRange; i++){ - for(int j = 0; j < colRange; j++){ + for(unsigned int i = 0; i < rowRange; i++){ + for(unsigned int j = 0; j < colRange; j++){ if(expected(i,j) == actual(i,j)){ count++; } } double valueOfMatch = 100*count/(double)(rowRange); if((lowrange <= valueOfMatch) && (toprange >= valueOfMatch)){ isInIntervall = true; } } return isInIntervall; } /* Train the classifier with the dataset of breastcancer patients from the LibSVM Libary */ void TrainSVMClassifier_BreastCancerDataSet_shouldReturnTrue() { /* Declarating an featurematrixdataset, the first matrix of the matrixpair is the trainingmatrix and the second one is the testmatrix.*/ std::pair<MatrixDoubleType,MatrixDoubleType> matrixDouble; matrixDouble = convertCSVToMatrix<double>(GetTestDataFilePath("Classification/FeaturematrixBreastcancer.csv"),';',0.5,true); m_TrainingMatrixX = matrixDouble.first; m_TestXPredict = matrixDouble.second; /* The declaration of the labelmatrixdataset is equivalent to the declaration of the featurematrixdataset.*/ std::pair<MatrixIntType,MatrixIntType> matrixInt; matrixInt = convertCSVToMatrix<int>(GetTestDataFilePath("Classification/LabelmatrixBreastcancer.csv"),';',0.5,false); m_TrainingLabelMatrixY = matrixInt.first; m_TestYPredict = matrixInt.second; /* Setting of the SVM-Parameters*/ classifier = mitk::LibSVMClassifier::New(); classifier->SetGamma(1/(double)(m_TrainingMatrixX.cols())); classifier->SetSvmType(0); classifier->SetKernelType(2); /* Train the classifier, by giving trainingdataset for the labels and features. The result in an colunmvector of the labels.*/ classifier->Train(m_TrainingMatrixX,m_TrainingLabelMatrixY); Eigen::MatrixXi classes = classifier->Predict(m_TestXPredict); /* Testing the matching between the calculated colunmvector and the result of the SVM */ unsigned int maxrows = classes.rows(); bool isYPredictVector = false; int count = 0; - for(int i= 0; i < maxrows; i++){ - if(classes(i,0) == m_TestYPredict(i,0)){ + for (unsigned int i = 0; i < maxrows; i++) + { + if (classes(i, 0) == m_TestYPredict(i, 0)) + { isYPredictVector = true; - count++; + ++count; } } + MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isIntervall<int>(m_TestYPredict,classes,75,100),"Testvalue is in range."); } void TestThreadedDecisionForest() { } }; MITK_TEST_SUITE_REGISTRATION(mitkLibSVMClassifier) \ No newline at end of file diff --git a/Modules/Core/test/mitkSlicedGeometry3DTest.cpp b/Modules/Core/test/mitkSlicedGeometry3DTest.cpp index 9e532acfd5..65db326c85 100644 --- a/Modules/Core/test/mitkSlicedGeometry3DTest.cpp +++ b/Modules/Core/test/mitkSlicedGeometry3DTest.cpp @@ -1,184 +1,184 @@ /*=================================================================== 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 "mitkImage.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" #include "mitkTestingMacros.h" #include <vnl/vnl_quaternion.h> #include <vnl/vnl_quaternion.hxx> #include <fstream> #include <array> static const mitk::ScalarType slicedGeometryEps = 1e-9; // Set epsilon to float precision for this test static mitk::PlaneGeometry::Pointer createPlaneGeometry() { auto planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->Initialize(); return planeGeometry; } static mitk::SlicedGeometry3D::Pointer createSlicedGeometry(const mitk::Point3D &origin, const mitk::Vector3D &spacing, int numberOfSlices) { auto slicedGeometry = mitk::SlicedGeometry3D::New(); slicedGeometry->InitializeSlicedGeometry(static_cast<unsigned int>(numberOfSlices)); slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); - for (unsigned int i = 0; i < numberOfSlices; ++i) + for (int i = 0; i < numberOfSlices; ++i) { auto planeGeometry = createPlaneGeometry(); slicedGeometry->SetPlaneGeometry(planeGeometry, i); } return slicedGeometry; } static mitk::SlicedGeometry3D::Pointer createEvenlySpacedSlicedGeometry(mitk::PlaneGeometry::Pointer planeGeometry, mitk::ScalarType spacing, int numberOfSlices) { auto slicedGeometry = mitk::SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planeGeometry, spacing, numberOfSlices); return slicedGeometry; } template<class T> static T createArray(mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) { T array; mitk::FillVector3D(array, x, y, z); return array; } static mitk::Point3D createPoint(mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) { return createArray<mitk::Point3D>(x, y, z); } static mitk::Vector3D createVector(mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) { return createArray<mitk::Vector3D>(x, y, z); } static mitk::VnlVector createVnlVector(mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) { mitk::VnlVector vector(3); mitk::FillVector3D(vector, x, y, z); return vector; } void mitkSlicedGeometry3D_ChangeImageGeometryConsideringOriginOffset_Test() { MITK_TEST_OUTPUT(<< "====== mitkSlicedGeometry3D_ChangeImageGeometryConsideringOriginOffset_Test() ======"); auto origin = createPoint(91.3, -13.3, 0); auto spacing = createVector(1.0, 0.9, 0.3); auto numberOfSlices = 5; auto slicedGeometry = createSlicedGeometry(origin, spacing, numberOfSlices); MITK_TEST_OUTPUT(<< "SlicedGeometry3D isn't an image geometry by default"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetImageGeometry() == false, ""); MITK_TEST_OUTPUT(<< "First and last PlaneGeometry in SlicedGeometry3D are not image geometries"); auto firstPlaneGeometry = slicedGeometry->GetPlaneGeometry(0); auto lastPlaneGeometry = slicedGeometry->GetPlaneGeometry(numberOfSlices - 1); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetImageGeometry() == false, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetImageGeometry() == false, ""); auto originOfSlicedGeometry = slicedGeometry->GetOrigin(); auto originOfFirstPlaneGeometry = firstPlaneGeometry->GetOrigin(); auto originOfLastPlaneGeometry = lastPlaneGeometry->GetOrigin(); auto firstCornerPointOfSlicedGeometry = slicedGeometry->GetCornerPoint(0); auto secondCornerPointOfFirstPlaneGeometry = firstPlaneGeometry->GetCornerPoint(1); auto thirdCornerPointOfLastPlaneGeometry = lastPlaneGeometry->GetCornerPoint(2); MITK_TEST_OUTPUT(<< "Calling SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset(true)"); slicedGeometry->ChangeImageGeometryConsideringOriginOffset(true); MITK_TEST_OUTPUT(<< "SlicedGeometry3D is an image geometry"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetImageGeometry() == true, ""); MITK_TEST_OUTPUT(<< "First and last PlaneGeometry in SlicedGeometry3D are image geometries"); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetImageGeometry() == true, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetImageGeometry() == true, ""); MITK_TEST_OUTPUT(<< "Corner points of geometries didn't change"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetCornerPoint(0) == firstCornerPointOfSlicedGeometry, ""); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetCornerPoint(1) == secondCornerPointOfFirstPlaneGeometry, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetCornerPoint(2) == thirdCornerPointOfLastPlaneGeometry, ""); MITK_TEST_OUTPUT(<< "Offsets were added to geometry origins"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetOrigin() == originOfSlicedGeometry + slicedGeometry->GetSpacing() * 0.5, ""); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetOrigin() == originOfFirstPlaneGeometry + firstPlaneGeometry->GetSpacing() * 0.5, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetOrigin() == originOfLastPlaneGeometry + lastPlaneGeometry->GetSpacing() * 0.5, ""); MITK_TEST_OUTPUT(<< "Calling SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset(false)"); slicedGeometry->ChangeImageGeometryConsideringOriginOffset(false); MITK_TEST_OUTPUT(<< "SlicedGeometry3D isn't an image geometry anymore"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetImageGeometry() == false, ""); MITK_TEST_OUTPUT(<< "First and last PlaneGeometry in SlicedGeometry3D are no image geometries anymore"); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetImageGeometry() == false, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetImageGeometry() == false, ""); MITK_TEST_OUTPUT(<< "Corner points of geometries didn't change"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetCornerPoint(0) == firstCornerPointOfSlicedGeometry, ""); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetCornerPoint(1) == secondCornerPointOfFirstPlaneGeometry, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetCornerPoint(2) == thirdCornerPointOfLastPlaneGeometry, ""); MITK_TEST_OUTPUT(<< "Offsets were subtracted from geometry origins"); MITK_TEST_CONDITION_REQUIRED(slicedGeometry->GetOrigin() == originOfSlicedGeometry, ""); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry->GetOrigin() == originOfFirstPlaneGeometry, ""); MITK_TEST_CONDITION_REQUIRED(lastPlaneGeometry->GetOrigin() == originOfLastPlaneGeometry, ""); } int mitkSlicedGeometry3DTest(int, char *[]) { mitk::ScalarType width = 100.0; mitk::ScalarType widthInMM = width; mitk::ScalarType height = 200.0; mitk::ScalarType heightInMM = height; mitk::ScalarType thicknessInMM = 3.0; auto right = createVector(widthInMM, 0.0, 0.0); auto bottom = createVector(0.0, heightInMM, 0.0); auto normal = createVector(0.0, 0.0, thicknessInMM); auto spacing = createVector(1.0, 1.0, thicknessInMM); auto planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(right, bottom, &spacing); auto numberOfSlices = 5; auto slicedGeometry = createEvenlySpacedSlicedGeometry(planeGeometry, thicknessInMM, numberOfSlices); auto firstPlaneGeometry = slicedGeometry->GetPlaneGeometry(0); MITK_TEST_OUTPUT(<< "Check if first PlaneGeometry of evenly spaced SlicedGeometry is valid"); MITK_TEST_CONDITION_REQUIRED(firstPlaneGeometry != nullptr, ""); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(firstPlaneGeometry->GetAxisVector(0), planeGeometry->GetAxisVector(0), slicedGeometryEps), ""); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(firstPlaneGeometry->GetAxisVector(1), planeGeometry->GetAxisVector(1), slicedGeometryEps), ""); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(firstPlaneGeometry->GetAxisVector(2), planeGeometry->GetAxisVector(2), slicedGeometryEps), ""); auto lastPlaneGeometry = slicedGeometry->GetPlaneGeometry(numberOfSlices - 1); auto expectedOriginOfLastSlice = createPoint(0.0, 0.0, thicknessInMM * (numberOfSlices - 1)); MITK_TEST_OUTPUT(<< "Check if origin of last PlaneGeometry is at expected location"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(lastPlaneGeometry->GetOrigin(), expectedOriginOfLastSlice, slicedGeometryEps), ""); mitkSlicedGeometry3D_ChangeImageGeometryConsideringOriginOffset_Test(); std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } diff --git a/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp b/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp index 0eb23063f7..c7ca1d3048 100644 --- a/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp +++ b/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp @@ -1,129 +1,129 @@ /*=================================================================== 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 <mitkTestFixture.h> #include "mitkRTStructureSetReader.h" #include <mitkIOUtil.h> class mitkRTStructureSetReaderTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkRTStructureSetReaderTestSuite); MITK_TEST(TestStructureSets); CPPUNIT_TEST_SUITE_END(); public: void setUp() override { } void TestStructureSets() { auto structureSetReader = mitk::RTStructureSetReader(); structureSetReader.SetInput(GetTestDataFilePath("RT/StructureSet/RS.dcm")); auto readerOutput = structureSetReader.Read(); CPPUNIT_ASSERT_EQUAL_MESSAGE("reader output should have one entry.", static_cast<unsigned int>(16), static_cast<unsigned int>(readerOutput.size())); auto contourModelVectorCorrect = LoadGroundTruthData(); std::vector<mitk::ContourModelSet::Pointer> contourModelVectorTest; for (const auto& aStruct : readerOutput){ mitk::ContourModelSet::Pointer contourModelSet = dynamic_cast<mitk::ContourModelSet*>(aStruct.GetPointer()); //only compare structs with content if (contourModelSet->GetSize() > 0){ contourModelVectorTest.push_back(contourModelSet); } } bool equal = true; for (const auto& aStructTest : contourModelVectorTest) { const std::string nameTest = aStructTest->GetProperty("name")->GetValueAsString(); for (const auto& aStructCorrect : contourModelVectorCorrect) { const std::string nameCorrect = aStructCorrect->GetProperty("name")->GetValueAsString(); if (nameTest == nameCorrect){ if (!Compare(aStructTest, aStructCorrect)){ equal = false; } } } } CPPUNIT_ASSERT(equal); } bool Compare(mitk::ContourModelSet::Pointer c1, mitk::ContourModelSet::Pointer c2){ if(c1->GetSize()!=c2->GetSize()) { MITK_INFO << "Number of ContourModelSets different" << std::endl; return false; } else { for(int i=0;i<c1->GetSize();++i) { mitk::ContourModel::Pointer cm1 = c1->GetContourModelAt(i); mitk::ContourModel::Pointer cm2 = c2->GetContourModelAt(i); if(cm1->GetNumberOfVertices()!=cm2->GetNumberOfVertices()) { MITK_INFO << "Number of Vertices different" << std::endl; return false; } else { - for(unsigned int j=0;j<cm1->GetNumberOfVertices();++j) + for(int j=0;j<cm1->GetNumberOfVertices();++j) { mitk::Point3D p1 = cm1->GetVertexAt(i)->Coordinates; mitk::Point3D p2 = cm2->GetVertexAt(i)->Coordinates; if (!Equal(p1, p2, 0.001)){ return false; } } } } } return true; } mitk::ContourModelSet::Pointer LoadFileWithNameProperty(const std::string& filename, const std::string& propertyName){ auto readerOutput = mitk::IOUtil::Load(GetTestDataFilePath(filename)); mitk::ContourModelSet::Pointer contourSet = dynamic_cast<mitk::ContourModelSet*>(readerOutput.at(0).GetPointer()); contourSet->SetProperty("name", mitk::StringProperty::New(propertyName)); return contourSet; } std::vector<mitk::ContourModelSet::Pointer> LoadGroundTruthData() { std::vector<mitk::ContourModelSet::Pointer> allStructs; allStructs.push_back(LoadFileWithNameProperty("RT/StructureSet/BODY.cnt_set", "BODY")); allStructs.push_back(LoadFileWithNameProperty("RT/StructureSet/Bladder.cnt_set", "Bladder")); allStructs.push_back(LoadFileWithNameProperty("RT/StructureSet/Femoral Head Lt.cnt_set", "Femoral Head Lt")); allStructs.push_back(LoadFileWithNameProperty("RT/StructureSet/Femoral Head RT.cnt_set", "Femoral Head RT")); allStructs.push_back(LoadFileWithNameProperty("RT/StructureSet/PTV.cnt_set", "PTV")); allStructs.push_back(LoadFileWithNameProperty("RT/StructureSet/Rectum.cnt_set", "Rectum")); return allStructs; } }; MITK_TEST_SUITE_REGISTRATION(mitkRTStructureSetReader) diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h index 6b01bbfaba..b70da654b0 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h @@ -1,340 +1,340 @@ /*=================================================================== 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 _TrackingDataHandler #define _TrackingDataHandler #include <mitkBaseData.h> #include <itkPoint.h> #include <itkImage.h> #include <deque> #include <MitkFiberTrackingExports.h> #include <boost/random/discrete_distribution.hpp> #include <boost/random/variate_generator.hpp> #include <boost/random/mersenne_twister.hpp> namespace mitk { /** * \brief Abstract class for tracking handler. A tracking handler deals with determining the next progression direction of a streamline fiber. There are different handlers for tensor images, peak images, ... */ class MITKFIBERTRACKING_EXPORT TrackingDataHandler { public: enum MODE { DETERMINISTIC, PROBABILISTIC }; TrackingDataHandler(); virtual ~TrackingDataHandler(){} typedef boost::mt19937 BoostRngType; typedef itk::Image<unsigned char, 3> ItkUcharImgType; typedef itk::Image<short, 3> ItkShortImgType; typedef itk::Image<float, 3> ItkFloatImgType; typedef itk::Image<double, 3> ItkDoubleImgType; typedef vnl_vector_fixed< float, 3 > TrackingDirectionType; virtual TrackingDirectionType ProposeDirection(itk::Point<float, 3>& pos, std::deque< TrackingDirectionType >& olddirs, itk::Index<3>& oldIndex) = 0; ///< predicts next progression direction at the given position virtual void InitForTracking() = 0; virtual itk::Vector<double, 3> GetSpacing() = 0; virtual itk::Point<float,3> GetOrigin() = 0; virtual itk::Matrix<double, 3, 3> GetDirection() = 0; virtual itk::ImageRegion<3> GetLargestPossibleRegion() = 0; virtual void SetMode(MODE m) = 0; MODE GetMode(){ return m_Mode; } void SetAngularThreshold( float a ){ m_AngularThreshold = a; } void SetInterpolate( bool interpolate ){ m_Interpolate = interpolate; } void SetFlipX( bool f ){ m_FlipX = f; } void SetFlipY( bool f ){ m_FlipY = f; } void SetFlipZ( bool f ){ m_FlipZ = f; } protected: float m_AngularThreshold; bool m_Interpolate; bool m_FlipX; bool m_FlipY; bool m_FlipZ; MODE m_Mode; BoostRngType m_Rng; template< class TPixelType > TPixelType GetImageValue(itk::Point<float, 3> itkP, itk::Image<TPixelType, 3>* image, vnl_vector_fixed<float, 8>& interpWeights){ // transform physical point to index coordinates itk::Index<3> idx; itk::ContinuousIndex< float, 3> cIdx; image->TransformPhysicalPointToIndex(itkP, idx); image->TransformPhysicalPointToContinuousIndex(itkP, cIdx); TPixelType pix = 0.0; if ( image->GetLargestPossibleRegion().IsInside(idx) ) { pix = image->GetPixel(idx); } else return pix; float frac_x = cIdx[0] - idx[0]; float frac_y = cIdx[1] - idx[1]; float frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? if (idx[0] >= 0 && idx[0] < image->GetLargestPossibleRegion().GetSize(0)-1 && idx[1] >= 0 && idx[1] < image->GetLargestPossibleRegion().GetSize(1)-1 && idx[2] >= 0 && idx[2] < image->GetLargestPossibleRegion().GetSize(2)-1) { // trilinear interpolation interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); pix = image->GetPixel(idx) * interpWeights[0]; typename itk::Image<TPixelType, 3>::IndexType tmpIdx = idx; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[7]; } if (pix!=pix) mitkThrow() << "nan values in image!"; return pix; } template< class TPixelType > TPixelType GetImageValue(itk::Point<float, 3> itkP, itk::Image<TPixelType, 3>* image, bool interpolate){ // transform physical point to index coordinates itk::Index<3> idx; itk::ContinuousIndex< float, 3> cIdx; image->TransformPhysicalPointToIndex(itkP, idx); image->TransformPhysicalPointToContinuousIndex(itkP, cIdx); TPixelType pix = 0.0; if ( image->GetLargestPossibleRegion().IsInside(idx) ) { pix = image->GetPixel(idx); if (!interpolate) return pix; } else return pix; float frac_x = cIdx[0] - idx[0]; float frac_y = cIdx[1] - idx[1]; float frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx[0] >= 0 && idx[0] < image->GetLargestPossibleRegion().GetSize(0)-1 && - idx[1] >= 0 && idx[1] < image->GetLargestPossibleRegion().GetSize(1)-1 && - idx[2] >= 0 && idx[2] < image->GetLargestPossibleRegion().GetSize(2)-1) + if (idx[0] >= 0 && idx[0] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(0) - 1) && + idx[1] >= 0 && idx[1] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(1) - 1) && + idx[2] >= 0 && idx[2] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed<float, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); pix = image->GetPixel(idx) * interpWeights[0]; typename itk::Image<TPixelType, 3>::IndexType tmpIdx = idx; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[7]; } if (pix!=pix) mitkThrow() << "nan values in image!"; return pix; } template< class TPixelType, int components > itk::Vector< TPixelType, components > GetImageValue(itk::Point<float, 3> itkP, itk::Image<itk::Vector< TPixelType, components >, 3>* image, bool interpolate){ // transform physical point to index coordinates itk::Index<3> idx; itk::ContinuousIndex< float, 3> cIdx; image->TransformPhysicalPointToIndex(itkP, idx); image->TransformPhysicalPointToContinuousIndex(itkP, cIdx); itk::Vector< TPixelType, components > pix = 0.0; if ( image->GetLargestPossibleRegion().IsInside(idx) ) { pix = image->GetPixel(idx); if (!interpolate) return pix; } else return pix; float frac_x = cIdx[0] - idx[0]; float frac_y = cIdx[1] - idx[1]; float frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx[0] >= 0 && idx[0] < image->GetLargestPossibleRegion().GetSize(0)-1 && - idx[1] >= 0 && idx[1] < image->GetLargestPossibleRegion().GetSize(1)-1 && - idx[2] >= 0 && idx[2] < image->GetLargestPossibleRegion().GetSize(2)-1) + if (idx[0] >= 0 && idx[0] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(0) - 1) && + idx[1] >= 0 && idx[1] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(1) - 1) && + idx[2] >= 0 && idx[2] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed<float, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); pix = image->GetPixel(idx) * interpWeights[0]; typename itk::Image<itk::Vector< TPixelType, components >, 3>::IndexType tmpIdx = idx; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[7]; } if (pix!=pix) mitkThrow() << "nan values in image!"; return pix; } }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp index 01f07fb4ad..c71276bfc1 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp @@ -1,271 +1,271 @@ /*=================================================================== 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 "mitkTrackingHandlerPeaks.h" namespace mitk { TrackingHandlerPeaks::TrackingHandlerPeaks() : m_PeakThreshold(0.1) , m_ApplyDirectionMatrix(false) { } TrackingHandlerPeaks::~TrackingHandlerPeaks() { } void TrackingHandlerPeaks::InitForTracking() { MITK_INFO << "Initializing peak tracker."; itk::Vector<double, 4> spacing4 = m_PeakImage->GetSpacing(); itk::Point<float, 4> origin4 = m_PeakImage->GetOrigin(); itk::Matrix<double, 4, 4> direction4 = m_PeakImage->GetDirection(); itk::ImageRegion<4> imageRegion4 = m_PeakImage->GetLargestPossibleRegion(); spacing3[0] = spacing4[0]; spacing3[1] = spacing4[1]; spacing3[2] = spacing4[2]; origin3[0] = origin4[0]; origin3[1] = origin4[1]; origin3[2] = origin4[2]; for (int r=0; r<3; r++) for (int c=0; c<3; c++) { direction3[r][c] = direction4[r][c]; m_FloatImageRotation[r][c] = direction4[r][c]; } imageRegion3.SetSize(0, imageRegion4.GetSize()[0]); imageRegion3.SetSize(1, imageRegion4.GetSize()[1]); imageRegion3.SetSize(2, imageRegion4.GetSize()[2]); m_DummyImage = ItkUcharImgType::New(); m_DummyImage->SetSpacing( spacing3 ); m_DummyImage->SetOrigin( origin3 ); m_DummyImage->SetDirection( direction3 ); m_DummyImage->SetRegions( imageRegion3 ); m_DummyImage->Allocate(); m_DummyImage->FillBuffer(0.0); m_NumDirs = imageRegion4.GetSize(3)/3; } vnl_vector_fixed<float,3> TrackingHandlerPeaks::GetMatchingDirection(itk::Index<3> idx3, vnl_vector_fixed<float,3>& oldDir) { vnl_vector_fixed<float,3> out_dir; out_dir.fill(0); float angle = 0; float mag = oldDir.magnitude(); if (mag<mitk::eps) { // try m_NumDirs times to get a non-zero random direction for (int j=0; j<m_NumDirs; j++) { int i = std::rand()%m_NumDirs; out_dir = GetDirection(idx3, i); if (out_dir.magnitude()>mitk::eps) { oldDir[0] = out_dir[0]; oldDir[1] = out_dir[1]; oldDir[2] = out_dir[2]; break; } } // if you didn't find a non-zero random direction, take first non-zero direction you find for (int i=0; i<m_NumDirs; i++) { out_dir = GetDirection(idx3, i); if (out_dir.magnitude()>mitk::eps) { oldDir[0] = out_dir[0]; oldDir[1] = out_dir[1]; oldDir[2] = out_dir[2]; break; } } } else { for (int i=0; i<m_NumDirs; i++) { vnl_vector_fixed<float,3> dir = GetDirection(idx3, i); mag = dir.magnitude(); if (mag>mitk::eps) dir.normalize(); float a = dot_product(dir, oldDir); if (fabs(a)>angle) { angle = fabs(a); if (a<0) out_dir = -dir; else out_dir = dir; out_dir *= mag; out_dir *= angle; // shrink contribution of direction if is less parallel to previous direction } } } return out_dir; } vnl_vector_fixed<float,3> TrackingHandlerPeaks::GetDirection(itk::Index<3> idx3, int dirIdx) { vnl_vector_fixed<float,3> dir; dir.fill(0.0); if ( !m_DummyImage->GetLargestPossibleRegion().IsInside(idx3) ) return dir; PeakImgType::IndexType idx4; idx4.SetElement(0,idx3[0]); idx4.SetElement(1,idx3[1]); idx4.SetElement(2,idx3[2]); for (int k=0; k<3; k++) { idx4.SetElement(3, dirIdx*3 + k); dir[k] = m_PeakImage->GetPixel(idx4); } if (m_FlipX) dir[0] *= -1; if (m_FlipY) dir[1] *= -1; if (m_FlipZ) dir[2] *= -1; if (m_ApplyDirectionMatrix) dir = m_FloatImageRotation*dir; return dir; } vnl_vector_fixed<float,3> TrackingHandlerPeaks::GetDirection(itk::Point<float, 3> itkP, bool interpolate, vnl_vector_fixed<float,3> oldDir){ // transform physical point to index coordinates itk::Index<3> idx3; itk::ContinuousIndex< float, 3> cIdx; m_DummyImage->TransformPhysicalPointToIndex(itkP, idx3); m_DummyImage->TransformPhysicalPointToContinuousIndex(itkP, cIdx); vnl_vector_fixed<float,3> dir; dir.fill(0.0); if ( !m_DummyImage->GetLargestPossibleRegion().IsInside(idx3) ) return dir; if (interpolate) { float frac_x = cIdx[0] - idx3[0]; float frac_y = cIdx[1] - idx3[1]; float frac_z = cIdx[2] - idx3[2]; if (frac_x<0) { idx3[0] -= 1; frac_x += 1; } if (frac_y<0) { idx3[1] -= 1; frac_y += 1; } if (frac_z<0) { idx3[2] -= 1; frac_z += 1; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx3[0] >= 0 && idx3[0] < m_DummyImage->GetLargestPossibleRegion().GetSize(0)-1 && - idx3[1] >= 0 && idx3[1] < m_DummyImage->GetLargestPossibleRegion().GetSize(1)-1 && - idx3[2] >= 0 && idx3[2] < m_DummyImage->GetLargestPossibleRegion().GetSize(2)-1) + if (idx3[0] >= 0 && idx3[0] < static_cast<itk::IndexValueType>(m_DummyImage->GetLargestPossibleRegion().GetSize(0) - 1) && + idx3[1] >= 0 && idx3[1] < static_cast<itk::IndexValueType>(m_DummyImage->GetLargestPossibleRegion().GetSize(1) - 1) && + idx3[2] >= 0 && idx3[2] < static_cast<itk::IndexValueType>(m_DummyImage->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed<float, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); dir = GetMatchingDirection(idx3, oldDir) * interpWeights[0]; itk::Index<3> tmpIdx = idx3; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[1]; tmpIdx = idx3; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[2]; tmpIdx = idx3; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[3]; tmpIdx = idx3; tmpIdx[0]++; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[4]; tmpIdx = idx3; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[5]; tmpIdx = idx3; tmpIdx[2]++; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[6]; tmpIdx = idx3; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[7]; } } else dir = GetMatchingDirection(idx3, oldDir); return dir; } vnl_vector_fixed<float,3> TrackingHandlerPeaks::ProposeDirection(itk::Point<float, 3>& pos, std::deque<vnl_vector_fixed<float, 3> >& olddirs, itk::Index<3>& oldIndex) { // CHECK: wann wird wo normalisiert vnl_vector_fixed<float,3> output_direction; output_direction.fill(0); itk::Index<3> index; m_DummyImage->TransformPhysicalPointToIndex(pos, index); vnl_vector_fixed<float,3> oldDir = olddirs.back(); float old_mag = oldDir.magnitude(); if (!m_Interpolate && oldIndex==index) return oldDir; output_direction = GetDirection(pos, m_Interpolate, oldDir); float mag = output_direction.magnitude(); if (mag>=m_PeakThreshold) { output_direction.normalize(); float a = 1; if (old_mag>0.5) a = dot_product(output_direction, oldDir); if (a>m_AngularThreshold) output_direction *= mag; else output_direction.fill(0); } else output_direction.fill(0); return output_direction; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp index a6e29bee52..8252cbcbfd 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp @@ -1,946 +1,946 @@ /*=================================================================== 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 _TrackingForestHandler_cpp #define _TrackingForestHandler_cpp #include "mitkTrackingHandlerRandomForest.h" #include <itkTractDensityImageFilter.h> #include <mitkDiffusionPropertyHelper.h> namespace mitk { template< int ShOrder, int NumberOfSignalFeatures > TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::TrackingHandlerRandomForest() : m_WmSampleDistance(-1) , m_NumTrees(30) , m_MaxTreeDepth(25) , m_SampleFraction(1.0) , m_NumberOfSamples(0) , m_GmSamplesPerVoxel(-1) , m_NumPreviousDirections(1) , m_ZeroDirWmFeatures(true) , m_BidirectionalFiberSampling(false) , m_MaxNumWmSamples(-1) { vnl_vector_fixed<float,3> ref; ref.fill(0); ref[0]=1; itk::OrientationDistributionFunction< float, 200 > odf; m_DirectionContainer.clear(); for (unsigned int i = 0; i<odf.GetNumberOfComponents(); i++) { vnl_vector_fixed<float,3> odf_dir; odf_dir[0] = odf.GetDirection(i)[0]; odf_dir[1] = odf.GetDirection(i)[1]; odf_dir[2] = odf.GetDirection(i)[2]; if (dot_product(ref, odf_dir)>0) // only used directions on one hemisphere m_DirectionContainer.push_back(odf_dir); // store indices for later mapping the classifier output to the actual direction } m_OdfFloatDirs.set_size(m_DirectionContainer.size(), 3); for (unsigned int i=0; i<m_DirectionContainer.size(); i++) { m_OdfFloatDirs[i][0] = m_DirectionContainer.at(i)[0]; m_OdfFloatDirs[i][1] = m_DirectionContainer.at(i)[1]; m_OdfFloatDirs[i][2] = m_DirectionContainer.at(i)[2]; } } template< int ShOrder, int NumberOfSignalFeatures > TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::~TrackingHandlerRandomForest() { } template< int ShOrder, int NumberOfSignalFeatures > typename TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::DwiFeatureImageType::PixelType TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::GetDwiFeaturesAtPosition(itk::Point<float, 3> itkP, typename DwiFeatureImageType::Pointer image, bool interpolate) { // transform physical point to index coordinates itk::Index<3> idx; itk::ContinuousIndex< float, 3> cIdx; image->TransformPhysicalPointToIndex(itkP, idx); image->TransformPhysicalPointToContinuousIndex(itkP, cIdx); typename DwiFeatureImageType::PixelType pix; pix.Fill(0.0); if ( image->GetLargestPossibleRegion().IsInside(idx) ) { pix = image->GetPixel(idx); if (!interpolate) return pix; } else return pix; float frac_x = cIdx[0] - idx[0]; float frac_y = cIdx[1] - idx[1]; float frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx[0] >= 0 && idx[0] < image->GetLargestPossibleRegion().GetSize(0)-1 && - idx[1] >= 0 && idx[1] < image->GetLargestPossibleRegion().GetSize(1)-1 && - idx[2] >= 0 && idx[2] < image->GetLargestPossibleRegion().GetSize(2)-1) + if (idx[0] >= 0 && idx[0] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(0) - 1) && + idx[1] >= 0 && idx[1] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(1) - 1) && + idx[2] >= 0 && idx[2] < static_cast<itk::IndexValueType>(image->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed<float, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); pix = image->GetPixel(idx) * interpWeights[0]; typename DwiFeatureImageType::IndexType tmpIdx = idx; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; pix += image->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; pix += image->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; pix += image->GetPixel(tmpIdx) * interpWeights[7]; } return pix; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InputDataValidForTracking() { if (m_InputDwis.empty()) mitkThrow() << "No diffusion-weighted images set!"; if (!IsForestValid()) mitkThrow() << "No or invalid random forest detected!"; } template< int ShOrder, int NumberOfSignalFeatures> template<typename T> typename std::enable_if< NumberOfSignalFeatures <=99, T >::type TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitDwiImageFeatures(mitk::Image::Pointer mitk_dwi) { MITK_INFO << "Calculating spherical harmonics features"; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter<short,short,float,ShOrder, 2*NumberOfSignalFeatures> InterpolationFilterType; typename InterpolationFilterType::Pointer filter = InterpolationFilterType::New(); filter->SetGradientImage( mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(mitk_dwi), mitk::DiffusionPropertyHelper::GetItkVectorImage(mitk_dwi) ); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(mitk_dwi)); filter->SetLambda(0.006); filter->SetNormalizationMethod(InterpolationFilterType::QBAR_RAW_SIGNAL); filter->Update(); m_DwiFeatureImages.push_back(filter->GetCoefficientImage()); return true; } template< int ShOrder, int NumberOfSignalFeatures> template<typename T> typename std::enable_if< NumberOfSignalFeatures >=100, T >::type TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitDwiImageFeatures(mitk::Image::Pointer mitk_dwi) { MITK_INFO << "Interpolating raw dwi signal features"; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter<short,short,float,ShOrder, 2*NumberOfSignalFeatures> InterpolationFilterType; typename InterpolationFilterType::Pointer filter = InterpolationFilterType::New(); filter->SetGradientImage( mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(mitk_dwi), mitk::DiffusionPropertyHelper::GetItkVectorImage(mitk_dwi) ); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(mitk_dwi)); filter->SetLambda(0.006); filter->SetNormalizationMethod(InterpolationFilterType::QBAR_RAW_SIGNAL); filter->Update(); typename DwiFeatureImageType::Pointer dwiFeatureImage = DwiFeatureImageType::New(); dwiFeatureImage->SetSpacing(filter->GetOutput()->GetSpacing()); dwiFeatureImage->SetOrigin(filter->GetOutput()->GetOrigin()); dwiFeatureImage->SetDirection(filter->GetOutput()->GetDirection()); dwiFeatureImage->SetLargestPossibleRegion(filter->GetOutput()->GetLargestPossibleRegion()); dwiFeatureImage->SetBufferedRegion(filter->GetOutput()->GetLargestPossibleRegion()); dwiFeatureImage->SetRequestedRegion(filter->GetOutput()->GetLargestPossibleRegion()); dwiFeatureImage->Allocate(); // get signal values and store them in the feature image vnl_vector_fixed<double,3> ref; ref.fill(0); ref[0]=1; itk::OrientationDistributionFunction< float, 2*NumberOfSignalFeatures > odf; itk::ImageRegionIterator< typename InterpolationFilterType::OutputImageType > it(filter->GetOutput(), filter->GetOutput()->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { typename DwiFeatureImageType::PixelType pix; int f = 0; for (unsigned int i = 0; i<odf.GetNumberOfComponents(); i++) { if (dot_product(ref, odf.GetDirection(i))>0) // only used directions on one hemisphere { pix[f] = it.Get()[i]; f++; } } dwiFeatureImage->SetPixel(it.GetIndex(), pix); ++it; } m_DwiFeatureImages.push_back(dwiFeatureImage); return true; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitForTracking() { MITK_INFO << "Initializing random forest tracker."; InputDataValidForTracking(); m_DwiFeatureImages.clear(); InitDwiImageFeatures<>(m_InputDwis.at(0)); } template< int ShOrder, int NumberOfSignalFeatures > vnl_vector_fixed<float,3> TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::ProposeDirection(itk::Point<float, 3>& pos, std::deque<vnl_vector_fixed<float, 3> >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed<float,3> output_direction; output_direction.fill(0); itk::Index<3> idx; m_DwiFeatureImages.at(0)->TransformPhysicalPointToIndex(pos, idx); bool check_last_dir = false; vnl_vector_fixed<float,3> last_dir; if (!olddirs.empty()) { last_dir = olddirs.back(); if (last_dir.magnitude()>0.5) check_last_dir = true; } if (!m_Interpolate && oldIndex==idx) return last_dir; // store feature pixel values in a vigra data type vigra::MultiArray<2, float> featureData = vigra::MultiArray<2, float>( vigra::Shape2(1,m_Forest->feature_count()) ); typename DwiFeatureImageType::PixelType dwiFeaturePixel = GetDwiFeaturesAtPosition(pos, m_DwiFeatureImages.at(0), m_Interpolate); for (unsigned int f=0; f<NumberOfSignalFeatures; f++) featureData(0,f) = dwiFeaturePixel[f]; vnl_matrix_fixed<double,3,3> direction_matrix = m_DwiFeatureImages.at(0)->GetDirection().GetVnlMatrix(); vnl_matrix_fixed<double,3,3> inverse_direction_matrix = m_DwiFeatureImages.at(0)->GetInverseDirection().GetVnlMatrix(); // append normalized previous direction(s) to feature vector int i = 0; vnl_vector_fixed<double,3> ref; ref.fill(0); ref[0]=1; for (auto d : olddirs) { vnl_vector_fixed<double,3> tempD; tempD[0] = d[0]; tempD[1] = d[1]; tempD[2] = d[2]; if (m_FlipX) tempD[0] *= -1; if (m_FlipY) tempD[1] *= -1; if (m_FlipZ) tempD[2] *= -1; tempD = inverse_direction_matrix * tempD; last_dir[0] = tempD[0]; last_dir[1] = tempD[1]; last_dir[2] = tempD[2]; int c = 0; - for (unsigned int f=NumberOfSignalFeatures+3*i; f<NumberOfSignalFeatures+3*(i+1); f++) + for (int f=NumberOfSignalFeatures+3*i; f<NumberOfSignalFeatures+3*(i+1); f++) { if (dot_product(ref, tempD)<0) featureData(0,f) = -tempD[c]; else featureData(0,f) = tempD[c]; c++; } i++; } // additional feature images if (m_AdditionalFeatureImages.size()>0) { int c = 0; for (auto img : m_AdditionalFeatureImages.at(0)) { float v = GetImageValue<float>(pos, img, false); featureData(0,NumberOfSignalFeatures+m_NumPreviousDirections*3+c) = v; c++; } } // perform classification vigra::MultiArray<2, float> probs(vigra::Shape2(1, m_Forest->class_count())); m_Forest->predictProbabilities(featureData, probs); vnl_vector< float > angles = m_OdfFloatDirs*last_dir; vnl_vector< float > probs2; probs2.set_size(m_DirectionContainer.size()); probs2.fill(0.0); // used for probabilistic direction sampling float probs_sum = 0; float pNonFib = 0; // probability that we left the white matter float w = 0; // weight of the predicted direction for (int i=0; i<m_Forest->class_count(); i++) // for each class (number of possible directions + out-of-wm class) { if (probs(0,i)>0) // if probability of respective class is 0, do nothing { // get label of class (does not correspond to the loop variable i) unsigned int classLabel = 0; m_Forest->ext_param_.to_classlabel(i, classLabel); if (classLabel<m_DirectionContainer.size()) // does class label correspond to a direction or to the out-of-wm class? { if (m_Mode==MODE::PROBABILISTIC) { if (!check_last_dir || fabs(angles[classLabel])>=m_AngularThreshold) probs2[classLabel] = probs(0,i); probs_sum += probs2[classLabel]; } else if (m_Mode==MODE::DETERMINISTIC) { vnl_vector_fixed<float,3> d = m_DirectionContainer.at(classLabel); // get direction vector assiciated with the respective direction index if (check_last_dir) // do we have a previous streamline direction or did we just start? { // TODO: check if hard curvature threshold is necessary. // alternatively try square of dot product as weight. // TODO: check if additional weighting with dot product as directional prior is necessary. are there alternatives on the classification level? float dot = angles[classLabel]; // claculate angle between the candidate direction vector and the previous streamline direction if (fabs(dot)>=m_AngularThreshold) // is angle between the directions smaller than our hard threshold? { if (dot<0) // make sure we don't walk backwards d *= -1; float w_i = probs(0,i)*fabs(dot); output_direction += w_i*d; // weight contribution to output direction with its probability and the angular deviation from the previous direction w += w_i; // increase output weight of the final direction } } else { output_direction += probs(0,i)*d; w += probs(0,i); } } } else pNonFib += probs(0,i); // probability that we are not in the white matter anymore } } if (m_Mode==MODE::PROBABILISTIC && pNonFib<0.5) { probs2 /= probs_sum; boost::random::discrete_distribution<int, float> dist(probs2.begin(), probs2.end()); int sampled_idx = 0; #pragma omp critical { boost::random::variate_generator<boost::random::mt19937&, boost::random::discrete_distribution<int,float>> sampler(m_Rng, dist); sampled_idx = sampler(); } output_direction = m_DirectionContainer.at(sampled_idx); w = probs2[sampled_idx]; if (check_last_dir && angles[sampled_idx]<0) // make sure we don't walk backwards output_direction *= -1; } // if we did not find a suitable direction, make sure that we return (0,0,0) if (pNonFib>w && w>0) output_direction.fill(0.0); else { vnl_vector_fixed<double,3> tempD; tempD[0] = output_direction[0]; tempD[1] = output_direction[1]; tempD[2] = output_direction[2]; tempD = direction_matrix * tempD; output_direction[0] = tempD[0]; output_direction[1] = tempD[1]; output_direction[2] = tempD[2]; if (m_FlipX) output_direction[0] *= -1; if (m_FlipY) output_direction[1] *= -1; if (m_FlipZ) output_direction[2] *= -1; } return output_direction * w; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::StartTraining() { m_StartTime = std::chrono::system_clock::now(); InputDataValidForTraining(); InitForTraining(); CalculateTrainingSamples(); MITK_INFO << "Maximum tree depths: " << m_MaxTreeDepth; MITK_INFO << "Sample fraction per tree: " << m_SampleFraction; MITK_INFO << "Number of trees: " << m_NumTrees; DefaultSplitType splitter; splitter.UsePointBasedWeights(true); splitter.SetWeights(m_Weights); splitter.UseRandomSplit(false); splitter.SetPrecision(mitk::eps); splitter.SetMaximumTreeDepth(m_MaxTreeDepth); std::vector< std::shared_ptr< vigra::RandomForest<int> > > trees; int count = 0; #pragma omp parallel for for (int i = 0; i < m_NumTrees; ++i) { std::shared_ptr< vigra::RandomForest<int> > lrf = std::make_shared< vigra::RandomForest<int> >(); lrf->set_options().use_stratification(vigra::RF_NONE); // How the data should be made equal lrf->set_options().sample_with_replacement(true); // if sampled with replacement or not lrf->set_options().samples_per_tree(m_SampleFraction); // Fraction of samples that are used to train a tree lrf->set_options().tree_count(1); // Number of trees that are calculated; lrf->set_options().min_split_node_size(5); // Minimum number of datapoints that must be in a node lrf->ext_param_.max_tree_depth = m_MaxTreeDepth; lrf->learn(m_FeatureData, m_LabelData,vigra::rf::visitors::VisitorBase(),splitter); #pragma omp critical { count++; MITK_INFO << "Tree " << count << " finished training."; trees.push_back(lrf); } } for (int i = 1; i < m_NumTrees; ++i) trees.at(0)->trees_.push_back(trees.at(i)->trees_[0]); m_Forest = trees.at(0); m_Forest->options_.tree_count_ = m_NumTrees; MITK_INFO << "Training finsihed"; m_EndTime = std::chrono::system_clock::now(); std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(m_EndTime - m_StartTime); std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(m_EndTime - m_StartTime); mm %= 60; MITK_INFO << "Training took " << hh.count() << "h and " << mm.count() << "m"; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InputDataValidForTraining() { if (m_InputDwis.empty()) mitkThrow() << "No diffusion-weighted images set!"; if (m_Tractograms.empty()) mitkThrow() << "No tractograms set!"; if (m_InputDwis.size()!=m_Tractograms.size()) mitkThrow() << "Unequal number of diffusion-weighted images and tractograms detected!"; } template< int ShOrder, int NumberOfSignalFeatures > bool TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::IsForestValid() { int additional_features = 0; if (m_AdditionalFeatureImages.size()>0) additional_features = m_AdditionalFeatureImages.at(0).size(); if (!m_Forest) MITK_ERROR << "Forest could not be read!"; else { if (m_Forest->tree_count()<=0) MITK_ERROR << "Forest contains no trees!"; if ( m_Forest->feature_count()!=(NumberOfSignalFeatures+3*m_NumPreviousDirections+additional_features) ) MITK_ERROR << "Wrong number of features in forest: got " << m_Forest->feature_count() << ", expected " << (NumberOfSignalFeatures+3*m_NumPreviousDirections+additional_features); } if(m_Forest && m_Forest->tree_count()>0 && m_Forest->feature_count()==(NumberOfSignalFeatures+3*m_NumPreviousDirections+additional_features)) return true; return false; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitForTraining() { typedef itk::AnalyticalDiffusionQballReconstructionImageFilter<short,short,float,ShOrder, 2*NumberOfSignalFeatures> InterpolationFilterType; MITK_INFO << "Spherical signal interpolation and sampling ..."; for (unsigned int i=0; i<m_InputDwis.size(); i++) { InitDwiImageFeatures<>(m_InputDwis.at(i)); if (i>=m_AdditionalFeatureImages.size()) { m_AdditionalFeatureImages.push_back(std::vector< ItkFloatImgType::Pointer >()); } if (i>=m_FiberVolumeModImages.size()) { ItkFloatImgType::Pointer img = ItkFloatImgType::New(); img->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); img->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); img->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); img->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); img->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); img->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); img->Allocate(); img->FillBuffer(1); m_FiberVolumeModImages.push_back(img); } if (m_FiberVolumeModImages.at(i)==nullptr) { m_FiberVolumeModImages.at(i) = ItkFloatImgType::New(); m_FiberVolumeModImages.at(i)->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); m_FiberVolumeModImages.at(i)->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); m_FiberVolumeModImages.at(i)->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); m_FiberVolumeModImages.at(i)->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_FiberVolumeModImages.at(i)->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_FiberVolumeModImages.at(i)->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_FiberVolumeModImages.at(i)->Allocate(); m_FiberVolumeModImages.at(i)->FillBuffer(1); } if (i>=m_MaskImages.size()) { ItkUcharImgType::Pointer newMask = ItkUcharImgType::New(); newMask->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); newMask->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); newMask->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); newMask->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); newMask->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); newMask->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); newMask->Allocate(); newMask->FillBuffer(1); m_MaskImages.push_back(newMask); } if (m_MaskImages.at(i)==nullptr) { m_MaskImages.at(i) = ItkUcharImgType::New(); m_MaskImages.at(i)->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); m_MaskImages.at(i)->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); m_MaskImages.at(i)->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); m_MaskImages.at(i)->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_MaskImages.at(i)->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_MaskImages.at(i)->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_MaskImages.at(i)->Allocate(); m_MaskImages.at(i)->FillBuffer(1); } } MITK_INFO << "Resampling fibers and calculating number of samples ..."; m_NumberOfSamples = 0; m_SampleUsage.clear(); for (unsigned int t=0; t<m_Tractograms.size(); t++) { ItkUcharImgType::Pointer mask = m_MaskImages.at(t); ItkUcharImgType::Pointer wmmask; if (t<m_WhiteMatterImages.size() && m_WhiteMatterImages.at(t)!=nullptr) wmmask = m_WhiteMatterImages.at(t); else { MITK_INFO << "No white-matter mask found. Using fiber envelope."; itk::TractDensityImageFilter< ItkUcharImgType >::Pointer env = itk::TractDensityImageFilter< ItkUcharImgType >::New(); env->SetFiberBundle(m_Tractograms.at(t)); env->SetInputImage(mask); env->SetBinaryOutput(true); env->SetUseImageGeometry(true); env->Update(); wmmask = env->GetOutput(); if (t>=m_WhiteMatterImages.size()) m_WhiteMatterImages.push_back(wmmask); else m_WhiteMatterImages.at(t) = wmmask; } // Calculate white-matter samples if (m_WmSampleDistance<0) { typename DwiFeatureImageType::Pointer image = m_DwiFeatureImages.at(t); float minSpacing = 1; if(image->GetSpacing()[0]<image->GetSpacing()[1] && image->GetSpacing()[0]<image->GetSpacing()[2]) minSpacing = image->GetSpacing()[0]; else if (image->GetSpacing()[1] < image->GetSpacing()[2]) minSpacing = image->GetSpacing()[1]; else minSpacing = image->GetSpacing()[2]; m_WmSampleDistance = minSpacing*0.5; } m_Tractograms.at(t)->ResampleLinear(m_WmSampleDistance); unsigned int wmSamples = m_Tractograms.at(t)->GetNumberOfPoints()-2*m_Tractograms.at(t)->GetNumFibers(); if (m_BidirectionalFiberSampling) wmSamples *= 2; if (m_ZeroDirWmFeatures) wmSamples *= (m_NumPreviousDirections+1); MITK_INFO << "White matter samples available: " << wmSamples; // upper limit for samples if (m_MaxNumWmSamples>0 && wmSamples>m_MaxNumWmSamples) { if ((float)m_MaxNumWmSamples/wmSamples > 0.8) { m_SampleUsage.push_back(std::vector<bool>(wmSamples, true)); m_NumberOfSamples += wmSamples; } else { m_SampleUsage.push_back(std::vector<bool>(wmSamples, false)); m_NumberOfSamples += m_MaxNumWmSamples; wmSamples = m_MaxNumWmSamples; MITK_INFO << "Limiting white matter samples to: " << m_MaxNumWmSamples; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randgen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randgen->SetSeed(); unsigned int c = 0; while (c<m_MaxNumWmSamples) { int idx = randgen->GetIntegerVariate(m_MaxNumWmSamples-1); if (m_SampleUsage[t][idx]==false) { m_SampleUsage[t][idx]=true; c++; } } } } else { m_SampleUsage.push_back(std::vector<bool>(wmSamples, true)); m_NumberOfSamples += wmSamples; } // calculate gray-matter samples itk::ImageRegionConstIterator<ItkUcharImgType> it(wmmask, wmmask->GetLargestPossibleRegion()); int OUTOFWM = 0; while(!it.IsAtEnd()) { if (it.Get()==0 && mask->GetPixel(it.GetIndex())>0) OUTOFWM++; ++it; } MITK_INFO << "Non-white matter voxels: " << OUTOFWM; if (m_GmSamplesPerVoxel>0) { m_GmSamples.push_back(m_GmSamplesPerVoxel); m_NumberOfSamples += m_GmSamplesPerVoxel*OUTOFWM; } else if (OUTOFWM>0) { int gm_per_voxel = 0.5+(float)wmSamples/(float)OUTOFWM; if (gm_per_voxel<=0) gm_per_voxel = 1; m_GmSamples.push_back(gm_per_voxel); m_NumberOfSamples += m_GmSamples.back()*OUTOFWM; MITK_INFO << "Non-white matter samples per voxel: " << m_GmSamples.back(); } else { m_GmSamples.push_back(0); } MITK_INFO << "Non-white matter samples: " << m_GmSamples.back()*OUTOFWM; } MITK_INFO << "Number of samples: " << m_NumberOfSamples; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::CalculateTrainingSamples() { vnl_vector_fixed<float,3> ref; ref.fill(0); ref[0]=1; m_FeatureData.reshape( vigra::Shape2(m_NumberOfSamples, NumberOfSignalFeatures+m_NumPreviousDirections*3+m_AdditionalFeatureImages.at(0).size()) ); m_LabelData.reshape( vigra::Shape2(m_NumberOfSamples,1) ); m_Weights.reshape( vigra::Shape2(m_NumberOfSamples,1) ); MITK_INFO << "Number of features: " << m_FeatureData.shape(1); itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer m_RandGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); m_RandGen->SetSeed(); MITK_INFO << "Creating training data ..."; unsigned int sampleCounter = 0; for (unsigned int t=0; t<m_Tractograms.size(); t++) { ItkFloatImgType::Pointer fiber_folume = m_FiberVolumeModImages.at(t); typename DwiFeatureImageType::Pointer image = m_DwiFeatureImages.at(t); ItkUcharImgType::Pointer wmMask = m_WhiteMatterImages.at(t); ItkUcharImgType::Pointer mask; if (t<m_MaskImages.size()) mask = m_MaskImages.at(t); // non-white matter samples itk::ImageRegionConstIterator<ItkUcharImgType> it(wmMask, wmMask->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if (it.Get()==0 && (mask.IsNull() || (mask.IsNotNull() && mask->GetPixel(it.GetIndex())>0))) { typename DwiFeatureImageType::PixelType pix = image->GetPixel(it.GetIndex()); // random directions for (unsigned int i=0; i<m_GmSamples.at(t); i++) { // diffusion signal features for (unsigned int f=0; f<NumberOfSignalFeatures; f++) { m_FeatureData(sampleCounter,f) = pix[f]; if(m_FeatureData(sampleCounter,f)!=m_FeatureData(sampleCounter,f)) m_FeatureData(sampleCounter,f) = 0; } // direction feature int num_zero_dirs = m_RandGen->GetIntegerVariate(m_NumPreviousDirections); // how many directions should be zero? for (unsigned int i=0; i<m_NumPreviousDirections; i++) { int c=0; vnl_vector_fixed<float,3> probe; if (i<num_zero_dirs) probe.fill(0.0); else { probe[0] = m_RandGen->GetVariate()*2-1; probe[1] = m_RandGen->GetVariate()*2-1; probe[2] = m_RandGen->GetVariate()*2-1; probe.normalize(); if (dot_product(ref, probe)<0) probe *= -1; } for (unsigned int f=NumberOfSignalFeatures+3*i; f<NumberOfSignalFeatures+3*(i+1); f++) { m_FeatureData(sampleCounter,f) = probe[c]; c++; } } // additional feature images int add_feat_c = 0; for (auto img : m_AdditionalFeatureImages.at(t)) { itk::Point<float, 3> itkP; image->TransformIndexToPhysicalPoint(it.GetIndex(), itkP); float v = GetImageValue<float>(itkP, img, false); m_FeatureData(sampleCounter,NumberOfSignalFeatures+m_NumPreviousDirections*3+add_feat_c) = v; add_feat_c++; } // label and sample weight m_LabelData(sampleCounter,0) = m_DirectionContainer.size(); m_Weights(sampleCounter,0) = 1.0; sampleCounter++; } } ++it; } unsigned int num_gm_samples = sampleCounter; // white matter samples mitk::FiberBundle::Pointer fib = m_Tractograms.at(t); vtkSmartPointer< vtkPolyData > polyData = fib->GetFiberPolyData(); vnl_vector_fixed<float,3> zero_dir; zero_dir.fill(0.0); for (int i=0; i<fib->GetNumFibers(); i++) { vtkCell* cell = polyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); float fiber_weight = fib->GetFiberWeight(i); for (int n = 0; n<=m_NumPreviousDirections; n++) { if (!m_ZeroDirWmFeatures) n = m_NumPreviousDirections; for (bool reverse : {false, true}) { for (int j=1; j<numPoints-1; j++) { if (!m_SampleUsage[t][sampleCounter-num_gm_samples]) continue; itk::Point<float, 3> itkP1, itkP2; int num_nonzero_dirs = m_NumPreviousDirections; if (!reverse) num_nonzero_dirs = std::min(n, j); else num_nonzero_dirs = std::min(n, numPoints-j-1); vnl_vector_fixed<float,3> dir; // zero directions for (unsigned int k=0; k<m_NumPreviousDirections-num_nonzero_dirs; k++) { dir.fill(0.0); int c = 0; for (unsigned int f=NumberOfSignalFeatures+3*k; f<NumberOfSignalFeatures+3*(k+1); f++) { m_FeatureData(sampleCounter,f) = dir[c]; c++; } } // nonzero directions for (unsigned int k=0; k<num_nonzero_dirs; k++) { double* p = nullptr; int n_idx = num_nonzero_dirs-k; if (!reverse) { p = points->GetPoint(j-n_idx); itkP1[0] = p[0]; itkP1[1] = p[1]; itkP1[2] = p[2]; p = points->GetPoint(j-n_idx+1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } else { p = points->GetPoint(j+n_idx); itkP1[0] = p[0]; itkP1[1] = p[1]; itkP1[2] = p[2]; p = points->GetPoint(j+n_idx-1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } dir[0]=itkP1[0]-itkP2[0]; dir[1]=itkP1[1]-itkP2[1]; dir[2]=itkP1[2]-itkP2[2]; if (dir.magnitude()<0.0001) mitkThrow() << "streamline error!"; dir.normalize(); if (dir[0]!=dir[0] || dir[1]!=dir[1] || dir[2]!=dir[2]) mitkThrow() << "ERROR: NaN direction!"; if (dot_product(ref, dir)<0) dir *= -1; int c = 0; for (unsigned int f=NumberOfSignalFeatures+3*(k+m_NumPreviousDirections-num_nonzero_dirs); f<NumberOfSignalFeatures+3*(k+1+m_NumPreviousDirections-num_nonzero_dirs); f++) { m_FeatureData(sampleCounter,f) = dir[c]; c++; } } // get target direction double* p = points->GetPoint(j); itkP1[0] = p[0]; itkP1[1] = p[1]; itkP1[2] = p[2]; if (reverse) { p = points->GetPoint(j-1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } else { p = points->GetPoint(j+1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } dir[0]=itkP2[0]-itkP1[0]; dir[1]=itkP2[1]-itkP1[1]; dir[2]=itkP2[2]-itkP1[2]; if (dir.magnitude()<0.0001) mitkThrow() << "streamline error!"; dir.normalize(); if (dir[0]!=dir[0] || dir[1]!=dir[1] || dir[2]!=dir[2]) mitkThrow() << "ERROR: NaN direction!"; if (dot_product(ref, dir)<0) dir *= -1; // image features float volume_mod = GetImageValue<float>(itkP1, fiber_folume, false); // diffusion signal features typename DwiFeatureImageType::PixelType pix = GetDwiFeaturesAtPosition(itkP1, image, m_Interpolate); for (unsigned int f=0; f<NumberOfSignalFeatures; f++) m_FeatureData(sampleCounter,f) = pix[f]; // additional feature images int add_feat_c = 0; for (auto img : m_AdditionalFeatureImages.at(t)) { float v = GetImageValue<float>(itkP1, img, false); add_feat_c++; m_FeatureData(sampleCounter,NumberOfSignalFeatures+2+add_feat_c) = v; } // set label values float angle = 0; float m = dir.magnitude(); if (m>0.0001) { int l = 0; for (auto d : m_DirectionContainer) { float a = fabs(dot_product(dir, d)); if (a>angle) { m_LabelData(sampleCounter,0) = l; m_Weights(sampleCounter,0) = fiber_weight*volume_mod; angle = a; } l++; } } sampleCounter++; } if (!m_BidirectionalFiberSampling) // don't sample fibers backward break; } } } } m_Tractograms.clear(); MITK_INFO << "done"; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::SaveForest(std::string forestFile) { MITK_INFO << "Saving forest to " << forestFile; if (IsForestValid()) { vigra::rf_export_HDF5( *m_Forest, forestFile, "" ); MITK_INFO << "Forest saved successfully."; } else MITK_INFO << "Forest invalid! Could not be saved."; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::LoadForest(std::string forestFile) { MITK_INFO << "Loading forest from " << forestFile; m_Forest = std::make_shared< vigra::RandomForest<int> >(); vigra::rf_import_HDF5( *m_Forest, forestFile); } } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp index f6828b3126..255dba0ace 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp @@ -1,344 +1,344 @@ /*=================================================================== 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 "mitkTrackingHandlerTensor.h" namespace mitk { TrackingHandlerTensor::TrackingHandlerTensor() : m_InterpolateTensors(true) , m_FaThreshold(0.1) { } TrackingHandlerTensor::~TrackingHandlerTensor() { } void TrackingHandlerTensor::InitForTracking() { MITK_INFO << "Initializing tensor tracker."; m_NumberOfInputs = m_TensorImages.size(); for (int i=0; i<m_NumberOfInputs; i++) { ItkPDImgType::Pointer pdImage = ItkPDImgType::New(); pdImage->SetSpacing( m_TensorImages.at(0)->GetSpacing() ); pdImage->SetOrigin( m_TensorImages.at(0)->GetOrigin() ); pdImage->SetDirection( m_TensorImages.at(0)->GetDirection() ); pdImage->SetRegions( m_TensorImages.at(0)->GetLargestPossibleRegion() ); pdImage->Allocate(); m_PdImage.push_back(pdImage); ItkDoubleImgType::Pointer emaxImage = ItkDoubleImgType::New(); emaxImage->SetSpacing( m_TensorImages.at(0)->GetSpacing() ); emaxImage->SetOrigin( m_TensorImages.at(0)->GetOrigin() ); emaxImage->SetDirection( m_TensorImages.at(0)->GetDirection() ); emaxImage->SetRegions( m_TensorImages.at(0)->GetLargestPossibleRegion() ); emaxImage->Allocate(); emaxImage->FillBuffer(1.0); m_EmaxImage.push_back(emaxImage); } bool useUserFaImage = true; if (m_FaImage.IsNull()) { m_FaImage = ItkFloatImgType::New(); m_FaImage->SetSpacing( m_TensorImages.at(0)->GetSpacing() ); m_FaImage->SetOrigin( m_TensorImages.at(0)->GetOrigin() ); m_FaImage->SetDirection( m_TensorImages.at(0)->GetDirection() ); m_FaImage->SetRegions( m_TensorImages.at(0)->GetLargestPossibleRegion() ); m_FaImage->Allocate(); m_FaImage->FillBuffer(0.0); useUserFaImage = false; } typedef itk::DiffusionTensor3D<float> TensorType; TensorType::EigenValuesArrayType eigenvalues; TensorType::EigenVectorsMatrixType eigenvectors; for (unsigned int x=0; x<m_TensorImages.at(0)->GetLargestPossibleRegion().GetSize()[0]; x++) for (unsigned int y=0; y<m_TensorImages.at(0)->GetLargestPossibleRegion().GetSize()[1]; y++) for (unsigned int z=0; z<m_TensorImages.at(0)->GetLargestPossibleRegion().GetSize()[2]; z++) { ItkTensorImageType::IndexType index; index[0] = x; index[1] = y; index[2] = z; for (int i=0; i<m_NumberOfInputs; i++) { ItkTensorImageType::PixelType tensor = m_TensorImages.at(i)->GetPixel(index); vnl_vector_fixed<float,3> dir; tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); dir[0] = eigenvectors(2, 0); dir[1] = eigenvectors(2, 1); dir[2] = eigenvectors(2, 2); if (dir.magnitude()>mitk::eps) dir.normalize(); else dir.fill(0.0); m_PdImage.at(i)->SetPixel(index, dir); if (!useUserFaImage) m_FaImage->SetPixel(index, m_FaImage->GetPixel(index)+tensor.GetFractionalAnisotropy()); m_EmaxImage.at(i)->SetPixel(index, 2/eigenvalues[2]); } if (!useUserFaImage) m_FaImage->SetPixel(index, m_FaImage->GetPixel(index)/m_NumberOfInputs); } if (m_F+m_G>1.0) { float temp = m_F+m_G; m_F /= temp; m_G /= temp; } } vnl_vector_fixed<float,3> TrackingHandlerTensor::GetMatchingDirection(itk::Index<3> idx, vnl_vector_fixed<float,3>& oldDir, int& image_num) { vnl_vector_fixed<float,3> out_dir; out_dir.fill(0); float angle = 0; float mag = oldDir.magnitude(); if (mag<mitk::eps) { for (unsigned int i=0; i<m_PdImage.size(); i++) { out_dir = m_PdImage.at(i)->GetPixel(idx); if (out_dir.magnitude()>0.5) { image_num = i; oldDir[0] = out_dir[0]; oldDir[1] = out_dir[1]; oldDir[2] = out_dir[2]; break; } } } else { for (unsigned int i=0; i<m_PdImage.size(); i++) { vnl_vector_fixed<float,3> dir = m_PdImage.at(i)->GetPixel(idx); float a = dot_product(dir, oldDir); if (fabs(a)>angle) { image_num = i; angle = fabs(a); if (a<0) out_dir = -dir; else out_dir = dir; out_dir *= angle; // shrink contribution of direction if is less parallel to previous direction } } } return out_dir; } vnl_vector_fixed<float,3> TrackingHandlerTensor::GetDirection(itk::Point<float, 3> itkP, vnl_vector_fixed<float,3> oldDir, TensorType& tensor) { // transform physical point to index coordinates itk::Index<3> idx; itk::ContinuousIndex< float, 3> cIdx; m_FaImage->TransformPhysicalPointToIndex(itkP, idx); m_FaImage->TransformPhysicalPointToContinuousIndex(itkP, cIdx); vnl_vector_fixed<float,3> dir; dir.fill(0.0); if ( !m_FaImage->GetLargestPossibleRegion().IsInside(idx) ) return dir; int image_num = -1; if (!m_Interpolate) { dir = GetMatchingDirection(idx, oldDir, image_num); if (image_num>=0) tensor = m_TensorImages[image_num]->GetPixel(idx) * m_EmaxImage[image_num]->GetPixel(idx); } else { float frac_x = cIdx[0] - idx[0]; float frac_y = cIdx[1] - idx[1]; float frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx[0] >= 0 && idx[0] < m_FaImage->GetLargestPossibleRegion().GetSize(0)-1 && - idx[1] >= 0 && idx[1] < m_FaImage->GetLargestPossibleRegion().GetSize(1)-1 && - idx[2] >= 0 && idx[2] < m_FaImage->GetLargestPossibleRegion().GetSize(2)-1) + if (idx[0] >= 0 && idx[0] < static_cast<itk::IndexValueType>(m_FaImage->GetLargestPossibleRegion().GetSize(0) - 1) && + idx[1] >= 0 && idx[1] < static_cast<itk::IndexValueType>(m_FaImage->GetLargestPossibleRegion().GetSize(1) - 1) && + idx[2] >= 0 && idx[2] < static_cast<itk::IndexValueType>(m_FaImage->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed<float, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); dir = GetMatchingDirection(idx, oldDir, image_num) * interpWeights[0]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(idx) * m_EmaxImage[image_num]->GetPixel(idx) * interpWeights[0]; itk::Index<3> tmpIdx = idx; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[1]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[2]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[3]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[4]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[5]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[6]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[7]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[7]; } } return dir; } vnl_vector_fixed<float,3> TrackingHandlerTensor::GetLargestEigenvector(TensorType& tensor) { vnl_vector_fixed<float,3> dir; TensorType::EigenValuesArrayType eigenvalues; TensorType::EigenVectorsMatrixType eigenvectors; tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); dir[0] = eigenvectors(2, 0); dir[1] = eigenvectors(2, 1); dir[2] = eigenvectors(2, 2); if (dir.magnitude()<mitk::eps) dir.fill(0.0); return dir; } vnl_vector_fixed<float,3> TrackingHandlerTensor::ProposeDirection(itk::Point<float, 3>& pos, std::deque<vnl_vector_fixed<float, 3> >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed<float,3> output_direction; output_direction.fill(0); TensorType tensor; tensor.Fill(0); try { itk::Index<3> index; m_TensorImages.at(0)->TransformPhysicalPointToIndex(pos, index); float fa = GetImageValue<float>(pos, m_FaImage, m_Interpolate); if (fa<m_FaThreshold) return output_direction; vnl_vector_fixed<float,3> oldDir = olddirs.back(); if (m_FlipX) oldDir[0] *= -1; if (m_FlipY) oldDir[1] *= -1; if (m_FlipZ) oldDir[2] *= -1; float old_mag = oldDir.magnitude(); if (!m_Interpolate && oldIndex==index) return oldDir; output_direction = GetDirection(pos, oldDir, tensor); float mag = output_direction.magnitude(); if (mag>=mitk::eps) { output_direction.normalize(); if (old_mag>0.5 && m_G>mitk::eps) // TEND tracking { output_direction[0] = m_F*output_direction[0] + (1-m_F)*( (1-m_G)*oldDir[0] + m_G*(tensor[0]*oldDir[0] + tensor[1]*oldDir[1] + tensor[2]*oldDir[2])); output_direction[1] = m_F*output_direction[1] + (1-m_F)*( (1-m_G)*oldDir[1] + m_G*(tensor[1]*oldDir[0] + tensor[3]*oldDir[1] + tensor[4]*oldDir[2])); output_direction[2] = m_F*output_direction[2] + (1-m_F)*( (1-m_G)*oldDir[2] + m_G*(tensor[2]*oldDir[0] + tensor[4]*oldDir[1] + tensor[5]*oldDir[2])); output_direction.normalize(); } float a = 1; if (old_mag>0.5) a = dot_product(output_direction, oldDir); if (a>m_AngularThreshold) output_direction *= mag; else output_direction.fill(0); } else output_direction.fill(0); } catch(...) { } if (m_FlipX) output_direction[0] *= -1; if (m_FlipY) output_direction[1] *= -1; if (m_FlipZ) output_direction[2] *= -1; return output_direction; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp index e103248dd8..6d578d7472 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp @@ -1,943 +1,943 @@ /*=================================================================== 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 <time.h> #include <stdio.h> #include <stdlib.h> #include <omp.h> #include "itkStreamlineTrackingFilter.h" #include <itkImageRegionConstIterator.h> #include <itkImageRegionConstIteratorWithIndex.h> #include <itkImageRegionIterator.h> #include <itkImageFileWriter.h> #include "itkPointShell.h" #define _USE_MATH_DEFINES #include <math.h> namespace itk { StreamlineTrackingFilter ::StreamlineTrackingFilter() : m_PauseTracking(false) , m_AbortTracking(false) , m_FiberPolyData(nullptr) , m_Points(nullptr) , m_Cells(nullptr) , m_AngularThreshold(-1) , m_StepSize(0) , m_MaxLength(10000) , m_MinTractLength(20.0) , m_MaxTractLength(400.0) , m_SeedsPerVoxel(1) , m_RandomSampling(false) , m_SamplingDistance(-1) , m_DeflectionMod(1.0) , m_OnlyForwardSamples(true) , m_UseStopVotes(true) , m_NumberOfSamples(30) , m_NumPreviousDirections(1) , m_StoppingRegions(nullptr) , m_SeedImage(nullptr) , m_MaskImage(nullptr) , m_AposterioriCurvCheck(false) , m_AvoidStop(true) , m_DemoMode(false) , m_SeedOnlyGm(false) , m_ControlGmEndings(false) , m_WmLabel(3) // mrtrix 5ttseg labels , m_GmLabel(1) // mrtrix 5ttseg labels , m_StepSizeVox(-1) , m_SamplingDistanceVox(-1) , m_AngularThresholdDeg(-1) , m_MaxNumTracts(-1) , m_Random(true) , m_Verbose(true) { this->SetNumberOfRequiredInputs(0); } void StreamlineTrackingFilter::BeforeTracking() { std::cout.setf(std::ios::boolalpha); 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(); float minSpacing; if(imageSpacing[0]<imageSpacing[1] && imageSpacing[0]<imageSpacing[2]) minSpacing = imageSpacing[0]; else if (imageSpacing[1] < imageSpacing[2]) minSpacing = imageSpacing[1]; else minSpacing = imageSpacing[2]; if (m_StepSizeVox<mitk::eps) m_StepSize = 0.5*minSpacing; else m_StepSize = m_StepSizeVox*minSpacing; if (m_AngularThresholdDeg<0) { if (m_StepSize/minSpacing<=1.0) m_AngularThreshold = std::cos( 0.5 * M_PI * m_StepSize/minSpacing ); else m_AngularThreshold = std::cos( 0.5 * M_PI ); } else m_AngularThreshold = std::cos( m_AngularThresholdDeg*M_PI/180.0 ); m_TrackingHandler->SetAngularThreshold(m_AngularThreshold); if (m_SamplingDistanceVox<mitk::eps) m_SamplingDistance = minSpacing*0.25; else m_SamplingDistance = m_SamplingDistanceVox * minSpacing; m_PolyDataContainer.clear(); for (unsigned int i=0; i<this->GetNumberOfThreads(); i++) { PolyDataType poly = PolyDataType::New(); m_PolyDataContainer.push_back(poly); } if (m_StoppingRegions.IsNull()) { m_StoppingRegions = ItkUcharImgType::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; if (m_SeedImage.IsNull()) { m_SeedImage = ItkUcharImgType::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 std::cout << "StreamlineTracking - Using seed image" << std::endl; if (m_MaskImage.IsNull()) { // initialize mask image m_MaskImage = ItkUcharImgType::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; if (m_TissueImage.IsNull()) { if (m_SeedOnlyGm) { MITK_WARN << "StreamlineTracking - Cannot seed in gray matter. No tissue type image set."; m_SeedOnlyGm = false; } if (m_ControlGmEndings) { MITK_WARN << "StreamlineTracking - Cannot control gray matter endings. No tissue type image set."; m_ControlGmEndings = false; } } else { if (m_ControlGmEndings) m_SeedOnlyGm = true; if (m_ControlGmEndings || m_SeedOnlyGm) std::cout << "StreamlineTracking - Using tissue image" << std::endl; else MITK_WARN << "StreamlineTracking - Tissue image set but no gray matter seeding or fiber endpoint-control enabled" << std::endl; } 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_SeedOnlyGm && m_ControlGmEndings) InitGrayMatterEndings(); 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; else std::cout << "StreamlineTracking - Mode: ???" << std::endl; std::cout << "StreamlineTracking - Angular threshold: " << m_AngularThreshold << " (" << 180*std::acos( m_AngularThreshold )/M_PI << "°)" << std::endl; std::cout << "StreamlineTracking - Stepsize: " << m_StepSize << "mm (" << m_StepSize/minSpacing << "*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 - Num. neighborhood samples: " << m_NumberOfSamples << std::endl; std::cout << "StreamlineTracking - Max. sampling distance: " << m_SamplingDistance << "mm (" << m_SamplingDistance/minSpacing << "*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_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::InitGrayMatterEndings() { m_TrackingHandler->SetAngularThreshold(0); m_GmStubs.clear(); if (m_TissueImage.IsNotNull()) { std::cout << "StreamlineTracking - initializing GM endings" << std::endl; ImageRegionConstIterator< ItkUcharImgType > it(m_TissueImage, m_TissueImage->GetLargestPossibleRegion() ); it.GoToBegin(); vnl_vector_fixed<float,3> d1; d1.fill(0.0); std::deque< vnl_vector_fixed<float,3> > olddirs; while (olddirs.size()<m_NumPreviousDirections) olddirs.push_back(d1); while( !it.IsAtEnd() ) { if (it.Value()==m_GmLabel) { ItkUcharImgType::IndexType s_idx = it.GetIndex(); itk::ContinuousIndex<float, 3> start; m_TissueImage->TransformIndexToPhysicalPoint(s_idx, start); itk::Point<float, 3> wm_p; float max = -1; FiberType fib; for (int x : {-1,0,1}) for (int y : {-1,0,1}) for (int z : {-1,0,1}) { if (x==y && y==z) continue; ItkUcharImgType::IndexType e_idx; e_idx[0] = s_idx[0] + x; e_idx[1] = s_idx[1] + y; e_idx[2] = s_idx[2] + z; if ( !m_TissueImage->GetLargestPossibleRegion().IsInside(e_idx) || m_TissueImage->GetPixel(e_idx)!=m_WmLabel ) continue; itk::ContinuousIndex<float, 3> end; m_TissueImage->TransformIndexToPhysicalPoint(e_idx, end); d1 = m_TrackingHandler->ProposeDirection(end, olddirs, s_idx); if (d1.magnitude()<0.0001) continue; d1.normalize(); vnl_vector_fixed< float, 3 > d2; d2[0] = end[0] - start[0]; d2[1] = end[1] - start[1]; d2[2] = end[2] - start[2]; d2.normalize(); float a = fabs(dot_product(d1,d2)); if (a>max) { max = a; wm_p = end; } } if (max>=0) { fib.push_back(start); fib.push_back(wm_p); m_GmStubs.push_back(fib); } } ++it; } } m_TrackingHandler->SetAngularThreshold(m_AngularThreshold); } void StreamlineTrackingFilter::CalculateNewPosition(itk::Point<float, 3>& pos, vnl_vector_fixed<float, 3>& dir) { pos[0] += dir[0]*m_StepSize; pos[1] += dir[1]*m_StepSize; pos[2] += dir[2]*m_StepSize; } bool StreamlineTrackingFilter ::IsValidPosition(itk::Point<float, 3> &pos) { ItkUcharImgType::IndexType idx; m_MaskImage->TransformPhysicalPointToIndex(pos, idx); if (!m_MaskImage->GetLargestPossibleRegion().IsInside(idx) || m_MaskImage->GetPixel(idx)==0) return false; return true; } float StreamlineTrackingFilter::GetRandDouble(float min, float max) { return (float)(rand()%((int)(10000*(max-min))) + 10000*min)/10000; } std::vector< vnl_vector_fixed<float,3> > StreamlineTrackingFilter::CreateDirections(int NPoints) { std::vector< vnl_vector_fixed<float,3> > 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); phi[0] = 0.0; phi[NPoints-1] = 0.0; for(int i=0; i<NPoints; i++) { theta[i] = acos(-1.0+2.0*i/(NPoints-1.0)) - M_PI / 2.0; if( i>0 && i<NPoints-1) { phi[i] = (phi[i-1] + C / sqrt(NPoints*(1-(-1.0+2.0*i/(NPoints-1.0))*(-1.0+2.0*i/(NPoints-1.0))))); // % (2*DIST_POINTSHELL_PI); } } for(int i=0; i<NPoints; i++) { vnl_vector_fixed<float,3> 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<float,3> StreamlineTrackingFilter::GetNewDirection(itk::Point<float, 3> &pos, std::deque<vnl_vector_fixed<float, 3> >& olddirs, itk::Index<3> &oldIndex) { if (m_DemoMode) { m_SamplingPointset->Clear(); m_AlternativePointset->Clear(); m_StopVotePointset->Clear(); } vnl_vector_fixed<float,3> direction; direction.fill(0); ItkUcharImgType::IndexType idx; m_MaskImage->TransformPhysicalPointToIndex(pos, idx); if (IsValidPosition(pos)) { if (m_StoppingRegions->GetPixel(idx)>0) return direction; direction = m_TrackingHandler->ProposeDirection(pos, olddirs, oldIndex); // get direction proposal at current streamline position } else return direction; vnl_vector_fixed<float,3> olddir = olddirs.back(); std::vector< vnl_vector_fixed<float,3> > probeVecs = CreateDirections(m_NumberOfSamples); itk::Point<float, 3> sample_pos; int alternatives = 1; int stop_votes = 0; int possible_stop_votes = 0; for (int i=0; i<probeVecs.size(); i++) { vnl_vector_fixed<float,3> d; bool is_stop_voter = false; if (m_RandomSampling) { d[0] = GetRandDouble(); d[1] = GetRandDouble(); d[2] = GetRandDouble(); d.normalize(); d *= 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<float,3> tempDir; tempDir.fill(0.0); if (IsValidPosition(sample_pos)) 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<float,3> tempDir; tempDir.fill(0.0); if (IsValidPosition(sample_pos)) 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++; } } if (direction.magnitude()>0.001 && (possible_stop_votes==0 || (float)stop_votes/possible_stop_votes<0.5) ) direction.normalize(); else direction.fill(0); return direction; } float StreamlineTrackingFilter::FollowStreamline(itk::Point<float, 3> pos, vnl_vector_fixed<float,3> dir, FiberType* fib, float tractLength, bool front) { vnl_vector_fixed<float,3> zero_dir; zero_dir.fill(0.0); std::deque< vnl_vector_fixed<float,3> > last_dirs; - for (int i=0; i<m_NumPreviousDirections-1; i++) + for (unsigned int i=0; i<m_NumPreviousDirections-1; i++) last_dirs.push_back(zero_dir); for (int step=0; step< m_MaxLength/2; step++) { ItkUcharImgType::IndexType oldIndex; m_StoppingRegions->TransformPhysicalPointToIndex(pos, oldIndex); // get new position CalculateNewPosition(pos, dir); // is new position inside of image and mask if (m_AbortTracking) // if not end streamline { return tractLength; } else // if yes, add new point to streamline { tractLength += m_StepSize; if (front) fib->push_front(pos); else fib->push_back(pos); if (m_AposterioriCurvCheck) { int curv = CheckCurvature(fib, front); if (curv>0) { tractLength -= m_StepSize*curv; while (curv>0) { if (front) fib->pop_front(); else fib->pop_back(); curv--; } return tractLength; } } if (tractLength>m_MaxTractLength) return tractLength; } #pragma omp critical if (m_DemoMode) // CHECK: warum sind die samplingpunkte der streamline in der visualisierung immer einen schritt voras? { m_BuildFibersReady++; m_Tractogram.push_back(*fib); BuildFibers(true); m_Stop = true; while (m_Stop){ } } dir.normalize(); 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; } int StreamlineTrackingFilter::CheckCurvature(FiberType* fib, bool front) { float m_Distance = 5; if (fib->size()<3) return 0; float dist = 0; std::vector< vnl_vector_fixed< float, 3 > > vectors; vnl_vector_fixed< float, 3 > meanV; meanV.fill(0); float dev = 0; if (front) { int c=0; while(dist<m_Distance && c<fib->size()-1) { itk::Point<float> p1 = fib->at(c); itk::Point<float> p2 = fib->at(c+1); 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); if (c==0) meanV += v; c++; } } else { int c=fib->size()-1; while(dist<m_Distance && c>0) { itk::Point<float> p1 = fib->at(c); itk::Point<float> p2 = fib->at(c-1); 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); if (c==fib->size()-1) meanV += v; c--; } } meanV.normalize(); for (int c=0; c<vectors.size(); c++) { float angle = dot_product(meanV, vectors.at(c)); if (angle>1.0) angle = 1.0; if (angle<-1.0) angle = -1.0; dev += acos(angle)*180/M_PI; } if (vectors.size()>0) dev /= vectors.size(); if (dev<30) return 0; else return vectors.size(); } void StreamlineTrackingFilter::GenerateData() { this->BeforeTracking(); std::vector< itk::Point<float> > seedpoints; if (!m_ControlGmEndings) { typedef ImageRegionConstIterator< ItkUcharImgType > MaskIteratorType; MaskIteratorType sit(m_SeedImage, m_SeedImage->GetLargestPossibleRegion() ); MaskIteratorType mit(m_MaskImage, m_MaskImage->GetLargestPossibleRegion() ); sit.GoToBegin(); mit.GoToBegin(); while( !sit.IsAtEnd() ) { if (sit.Value()==0 || mit.Value()==0 || (m_SeedOnlyGm && m_TissueImage->GetPixel(sit.GetIndex())!=m_GmLabel)) { ++sit; ++mit; continue; } for (int s=0; s<m_SeedsPerVoxel; s++) { ItkUcharImgType::IndexType index = sit.GetIndex(); itk::ContinuousIndex<float, 3> start; if (m_SeedsPerVoxel>1) { start[0] = index[0]+GetRandDouble(-0.5, 0.5); start[1] = index[1]+GetRandDouble(-0.5, 0.5); start[2] = index[2]+GetRandDouble(-0.5, 0.5); } else { start[0] = index[0]; start[1] = index[1]; start[2] = index[2]; } itk::Point<float> worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint( start, worldPos ); seedpoints.push_back(worldPos); } ++sit; ++mit; } } else { for (auto s : m_GmStubs) seedpoints.push_back(s[1]); } if (m_Random) { std::srand(std::time(0)); std::random_shuffle ( seedpoints.begin(), seedpoints.end() ); } bool stop = false; unsigned int current_tracts = 0; int num_seeds = seedpoints.size(); itk::Index<3> zeroIndex; zeroIndex.Fill(0); int 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 && !stop) { int temp_i = i; #pragma omp critical i++; if (temp_i>=num_seeds || stop) continue; else if (m_Verbose && i%print_interval==0) #pragma omp critical { progress += print_interval; std::cout << " \r"; if (m_MaxNumTracts>0) std::cout << "Tried: " << progress << "/" << num_seeds << " | Accepted: " << current_tracts << "/" << m_MaxNumTracts << '\r'; else std::cout << "Tried: " << progress << "/" << num_seeds << " | Accepted: " << current_tracts << '\r'; cout.flush(); } itk::Point<float> worldPos = seedpoints.at(temp_i); FiberType fib; float tractLength = 0; unsigned int counter = 0; // get starting direction vnl_vector_fixed<float,3> dir; dir.fill(0.0); std::deque< vnl_vector_fixed<float,3> > olddirs; while (olddirs.size()<m_NumPreviousDirections) olddirs.push_back(dir); // start without old directions (only zero directions) vnl_vector_fixed< float, 3 > gm_start_dir; if (m_ControlGmEndings) { gm_start_dir[0] = m_GmStubs[temp_i][1][0] - m_GmStubs[temp_i][0][0]; gm_start_dir[1] = m_GmStubs[temp_i][1][1] - m_GmStubs[temp_i][0][1]; gm_start_dir[2] = m_GmStubs[temp_i][1][2] - m_GmStubs[temp_i][0][2]; gm_start_dir.normalize(); olddirs.pop_back(); olddirs.push_back(gm_start_dir); } if (IsValidPosition(worldPos)) dir = m_TrackingHandler->ProposeDirection(worldPos, olddirs, zeroIndex); if (dir.magnitude()>0.0001) { if (m_ControlGmEndings) { float a = dot_product(gm_start_dir, dir); if (a<0) dir = -dir; } // forward tracking tractLength = FollowStreamline(worldPos, dir, &fib, 0, false); fib.push_front(worldPos); if (m_ControlGmEndings) { fib.push_front(m_GmStubs[temp_i][0]); CheckFiberForGmEnding(&fib); } else { // backward tracking (only if we don't explicitely start in the GM) tractLength = FollowStreamline(worldPos, -dir, &fib, tractLength, true); if (m_ControlGmEndings) { CheckFiberForGmEnding(&fib); std::reverse(fib.begin(),fib.end()); CheckFiberForGmEnding(&fib); } } counter = fib.size(); #pragma omp critical if (tractLength>=m_MinTractLength && counter>=2) { if (!stop) { m_Tractogram.push_back(fib); current_tracts++; } - if (m_MaxNumTracts>0 && current_tracts>=m_MaxNumTracts) + if (m_MaxNumTracts > 0 && current_tracts>=static_cast<unsigned int>(m_MaxNumTracts)) { if (!stop) { std::cout << " \r"; MITK_INFO << "Reconstructed maximum number of tracts (" << current_tracts << "). Stopping tractography."; } stop = true; } } } } this->AfterTracking(); } void StreamlineTrackingFilter::CheckFiberForGmEnding(FiberType* fib) { if (m_TissueImage.IsNull()) return; // first check if the current fibe rendpoint is located inside of the white matter // if not, remove last fiber point and repeat bool in_wm = false; while (!in_wm && fib->size()>2) { ItkUcharImgType::IndexType idx; m_TissueImage->TransformPhysicalPointToIndex(fib->back(), idx); if (m_TissueImage->GetPixel(idx)==m_WmLabel) in_wm = true; else fib->pop_back(); } if (fib->size()<3 || !in_wm) { fib->clear(); return; } // get fiber direction at end point vnl_vector_fixed< float, 3 > d1; d1[0] = fib->back()[0] - fib->at(fib->size()-2)[0]; d1[1] = fib->back()[1] - fib->at(fib->size()-2)[1]; d1[2] = fib->back()[2] - fib->at(fib->size()-2)[2]; d1.normalize(); // find closest gray matter voxel ItkUcharImgType::IndexType s_idx; m_TissueImage->TransformPhysicalPointToIndex(fib->back(), s_idx); itk::Point<float> gm_endp; float max = -1; for (int x : {-1,0,1}) for (int y : {-1,0,1}) for (int z : {-1,0,1}) { if (x==y && y==z) continue; ItkUcharImgType::IndexType e_idx; e_idx[0] = s_idx[0] + x; e_idx[1] = s_idx[1] + y; e_idx[2] = s_idx[2] + z; if ( !m_TissueImage->GetLargestPossibleRegion().IsInside(e_idx) || m_TissueImage->GetPixel(e_idx)!=m_GmLabel ) continue; itk::ContinuousIndex<float, 3> end; m_TissueImage->TransformIndexToPhysicalPoint(e_idx, end); vnl_vector_fixed< float, 3 > d2; d2[0] = end[0] - fib->back()[0]; d2[1] = end[1] - fib->back()[1]; d2[2] = end[2] - fib->back()[2]; d2.normalize(); float a = dot_product(d1,d2); if (a>max) { max = a; gm_endp = end; } } if (max>=0) fib->push_back(gm_endp); else // no gray matter enpoint found -> delete fiber fib->clear(); } void StreamlineTrackingFilter::BuildFibers(bool check) { if (m_BuildFibersReady<omp_get_num_threads() && check) return; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); for (int i=0; i<m_Tractogram.size(); i++) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); FiberType fib = m_Tractogram.at(i); for (FiberType::iterator it = fib.begin(); it!=fib.end(); ++it) { vtkIdType id = vNewPoints->InsertNextPoint((*it).GetDataPointer()); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } if (check) for (int i=0; i<m_BuildFibersReady; i++) m_Tractogram.pop_back(); m_BuildFibersReady = 0; m_FiberPolyData->SetPoints(vNewPoints); m_FiberPolyData->SetLines(vNewLines); m_BuildFibersFinished = true; } void StreamlineTrackingFilter::AfterTracking() { std::cout << " \r"; MITK_INFO << "Reconstructed " << m_Tractogram.size() << " fibers."; MITK_INFO << "Generating polydata "; BuildFibers(false); MITK_INFO << "done"; m_EndTime = std::chrono::system_clock::now(); std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(m_EndTime - m_StartTime); std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(m_EndTime - m_StartTime); std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(m_EndTime - m_StartTime); mm %= 60; ss %= 60; MITK_INFO << "Tracking took " << hh.count() << "h, " << mm.count() << "m and " << ss.count() << "s"; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp index e23724c631..6f8bf5a1e7 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/SignalModels/mitkRawShModel.cpp @@ -1,390 +1,390 @@ /*=================================================================== 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 <vnl/vnl_cross.h> #include <vnl/vnl_quaternion.h> #include <mitkRawShModel.h> #include <boost/math/special_functions.hpp> #include <itkOrientationDistributionFunction.h> #include <mitkFiberfoxParameters.h> #include <itkDiffusionTensor3DReconstructionImageFilter.h> #include <itkAdcImageFilter.h> #include <itkAnalyticalDiffusionQballReconstructionImageFilter.h> #include <mitkDiffusionPropertyHelper.h> #include <mitkImageCast.h> #include <mitkProperties.h> 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<short, 3> 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->SetGradientImage( static_cast<mitk::GradientDirectionsProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->SetBValue(static_cast<mitk::FloatProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); filter->Update(); tensorImage = filter->GetOutput(); } const int NumCoeffs = (shOrder*shOrder + shOrder + 2)/2 + shOrder; if (itkFeatureImage.IsNull()) { QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetGradientImage( static_cast<mitk::GradientDirectionsProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer ); qballfilter->SetBValue(static_cast<mitk::FloatProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); 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<mitk::GradientDirectionsProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()); adcFilter->SetB_value(static_cast<mitk::FloatProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue()); adcFilter->Update(); adcImage = adcFilter->GetOutput(); } int b0Index; for (unsigned int i=0; i<static_cast<mitk::GradientDirectionsProperty*>( diffImg->GetProperty(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer()->Size(); i++) if ( static_cast<mitk::GradientDirectionsProperty*>( 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<static_cast<mitk::GradientDirectionsProperty*>( 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<double> 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; c<itkv.Size(); c++) coeffs[c] = itkv[c]/max; if ( this->GetMaxNumKernels()>this->GetNumberOfKernels() && FA>m_FaRange.first && FA<m_FaRange.second && ADC>m_AdcRange.first && ADC<m_AdcRange.second) { if (this->SetShCoefficients( coeffs, (double)itkVectorImagePointer->GetPixel(it.GetIndex())[b0Index]/max )) { tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); itk::Vector<double,3> 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 > void RawShModel< ScalarType >::Cart2Sph( GradientListType gradients ) { m_SphCoords.set_size(gradients.size(), 2); m_SphCoords.fill(0.0); for (unsigned int i=0; i<gradients.size(); i++) { GradientType g = gradients[i]; if( g.GetNorm() > 0.0001 ) { gradients[i].Normalize(); m_SphCoords(i,0) = acos(gradients[i][2]); // theta m_SphCoords(i,1) = atan2(gradients[i][1], gradients[i][0]); // phi } } } template< class ScalarType > void RawShModel< ScalarType >::SetFiberDirection(GradientType fiberDirection) { this->m_FiberDirection = fiberDirection; this->m_FiberDirection.Normalize(); RandomModel(); GradientType axis = itk::CrossProduct(this->m_FiberDirection, m_PrototypeMaxDirection.at(m_ModelIndex)); axis.Normalize(); vnl_quaternion<double> rotation(axis.GetVnlVector(), acos(dot_product(this->m_FiberDirection.GetVnlVector(), m_PrototypeMaxDirection.at(m_ModelIndex).GetVnlVector()))); rotation.normalize(); GradientListType gradients; for (unsigned int i=0; i<this->m_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); } 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<double, 10000> odf; // GradientListType gradients; // for (unsigned int i=0; i<odf.GetNumberOfComponents(); i++) // { // GradientType dir; dir[0]=odf.GetDirection(i)[0]; dir[1]=odf.GetDirection(i)[1]; dir[2]=odf.GetDirection(i)[2]; // dir.Normalize(); // gradients.push_back(dir); // } // Cart2Sph( this->m_GradientList ); // PixelType signal = SimulateMeasurement(); // int minDirIdx = 0; // double min = itk::NumericTraits<double>::max(); // for (unsigned int i=0; i<signal.GetSize(); i++) // { // if (signal[i]>b0 || signal[i]<0) // { // MITK_INFO << "Corrupted signal value detected. Kernel rejected."; // m_B0Signal.pop_back(); // m_ShCoefficients.pop_back(); // return false; // } // if (signal[i]<min) // { // min = signal[i]; // minDirIdx = i; // } // } // GradientType maxDir = this->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) { ScalarType signal = 0; if (m_ModelIndex==-1) RandomModel(); 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<=m_ShOrder; l=l+2) + for (int l=0; l<=static_cast<int>(m_ShOrder); l=l+2) for (m=-l; m<=l; m++) { plm = legendre_p<double>(l,abs(m),cos(m_SphCoords(dir,0))); mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial<double>(l-abs(m))/factorial<double>(l+abs(m)))*plm; double basis; if (m<0) basis = sqrt(2.0)*mag*cos(fabs((double)m)*m_SphCoords(dir,1)); else if (m==0) basis = mag; else basis = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*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() { 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); for (int p=0; p<M; p++) { if (this->m_GradientList[p].GetNorm()>0.001) { j=0; - for (int l=0; l<=m_ShOrder; l=l+2) + for (int l=0; l<=static_cast<int>(m_ShOrder); l=l+2) for (m=-l; m<=l; m++) { plm = legendre_p<double>(l,abs(m),cos(m_SphCoords(p,0))); mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial<double>(l-abs(m))/factorial<double>(l+abs(m)))*plm; if (m<0) shBasis(p,j) = sqrt(2.0)*mag*cos(fabs((double)m)*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*m_SphCoords(p,1)); j++; } double val = 0; for (unsigned int cidx=0; cidx<m_ShCoefficients.at(m_ModelIndex).size(); cidx++) val += m_ShCoefficients.at(m_ModelIndex)[cidx]*shBasis(p,cidx); signal[p] = val; } else signal[p] = m_B0Signal.at(m_ModelIndex); } m_ModelIndex = -1; return signal; } diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp index 6d2506664a..59561e595e 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkFibersFromPlanarFiguresFilter.cpp @@ -1,241 +1,241 @@ /*=================================================================== 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 <math.h> // MITK #include <itkOrientationDistributionFunction.h> #include <itkDiffusionQballGeneralizedFaImageFilter.h> #include <mitkStandardFileLocations.h> #include <mitkFiberBuilder.h> #include <mitkMetropolisHastingsSampler.h> #include <itkTensorImageToQBallImageFilter.h> #include <mitkGibbsEnergyComputer.h> #include <mitkRotationOperation.h> #include <mitkInteractionConst.h> // ITK #include <itkImageDuplicator.h> #include <itkResampleImageFilter.h> #include <itkTimeProbe.h> #include <itkMersenneTwisterRandomVariateGenerator.h> // MISC #include <math.h> namespace itk{ FibersFromPlanarFiguresFilter::FibersFromPlanarFiguresFilter() { } FibersFromPlanarFiguresFilter::~FibersFromPlanarFiguresFilter() { } void FibersFromPlanarFiguresFilter::GeneratePoints() { Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New(); randGen->SetSeed((unsigned int)0); m_2DPoints.clear(); - int count = 0; + 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_Parameters.m_Fiducials.size(); i++) if (m_Parameters.m_Fiducials.at(i).size()<2) itkExceptionMacro("At least 2 fiducials needed per fiber bundle!"); for (unsigned int i=0; i<m_Parameters.m_Fiducials.size(); i++) { vtkSmartPointer<vtkCellArray> m_VtkCellArray = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> m_VtkPoints = vtkSmartPointer<vtkPoints>::New(); vector< mitk::PlanarEllipse::Pointer > bundle = m_Parameters.m_Fiducials.at(i); vector< unsigned int > fliplist; if (i<m_Parameters.m_FlipList.size()) fliplist = m_Parameters.m_FlipList.at(i); else fliplist.resize(bundle.size(), 0); if (fliplist.size()<bundle.size()) fliplist.resize(bundle.size(), 0); GeneratePoints(); - for (int j=0; j<m_Parameters.m_Density; j++) + for (unsigned int j = 0; j < m_Parameters.m_Density; ++j) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::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<double, 2, 2> 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; vnl_matrix_fixed<double, 2, 2> 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; k<bundle.size(); k++) { figure = bundle.at(k); p0 = figure->GetControlPoint(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<mitk::PlaneGeometry*>(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; 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<vtkPolyData> fiberPolyData = vtkSmartPointer<vtkPolyData>::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 bbe0243cb6..5e69f8965a 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp @@ -1,468 +1,468 @@ /*=================================================================== 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 <time.h> #include <stdio.h> #include <stdlib.h> #include "itkKspaceImageFilter.h" #include <itkImageRegionConstIterator.h> #include <itkImageRegionConstIteratorWithIndex.h> #include <itkImageRegionIterator.h> #include <itkImageFileWriter.h> #include <mitkSingleShotEpi.h> #include <mitkCartesianReadout.h> #define _USE_MATH_DEFINES #include <math.h> namespace itk { template< class TPixelType > KspaceImageFilter< TPixelType > ::KspaceImageFilter() : m_Z(0) , m_UseConstantRandSeed(false) , m_SpikesPerSlice(0) , m_IsBaseline(true) { m_DiffusionGradientDirection.Fill(0.0); m_CoilPosition.Fill(0.0); } template< class TPixelType > void KspaceImageFilter< TPixelType > ::BeforeThreadedGenerateData() { m_Spike = vcl_complex<double>(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); m_Transform = m_Parameters->m_SignalGen.m_ImageDirection; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { m_Transform[i][j] *= m_Parameters->m_SignalGen.m_ImageSpacing[j]; } } 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 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(); } template< class TPixelType > double KspaceImageFilter< TPixelType >::CoilSensitivity(DoubleVectorType& 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: { DoubleVectorType diff = pos-m_CoilPosition; double sens = diff.GetNorm()*m_CoilSensitivityFactor + 1; if (sens<0) sens = 0; return sens; } case SignalGenerationParameters::COIL_EXPONENTIAL: { DoubleVectorType diff = pos-m_CoilPosition; double dist = diff.GetNorm(); return exp(-dist*m_CoilSensitivityFactor); } default: return 1; } } template< class TPixelType > void KspaceImageFilter< TPixelType > ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadID) { 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; double kxMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0); double kyMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1); double xMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(0); // scanner coverage in x-direction double yMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(1); // scanner coverage in y-direction double yMaxFov = yMax*m_Parameters->m_SignalGen.m_CroppingFactor; // actual FOV in y-direction (in x-direction FOV=xMax) double numPix = kxMax*kyMax; // Adjust noise variance since it is the intended variance in physical space and not in k-space: double noiseVar = m_Parameters->m_SignalGen.m_PartialFourier*m_Parameters->m_SignalGen.m_NoiseVariance/(kyMax*kxMax); while( !oit.IsAtEnd() ) { // time from maximum echo double t= m_ReadoutScheme->GetTimeFromMaxEcho(oit.GetIndex()); // time passed since k-space readout started double tRead = m_ReadoutScheme->GetRedoutTime(oit.GetIndex()); // time passes since application of the RF pulse double 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.) double eddyDecay = 0; if ( m_Parameters->m_Misc.m_CheckAddEddyCurrentsBox && m_Parameters->m_SignalGen.m_EddyStrength>0) { eddyDecay = exp(-tRead/m_Parameters->m_SignalGen.m_Tau ); } // calcualte signal relaxation factors std::vector< double > relaxFactor; if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation) { for (unsigned int i=0; i<m_CompartmentImages.size(); i++) { relaxFactor.push_back( exp(-tRf/m_T2.at(i) -fabs(t)/ m_Parameters->m_SignalGen.m_tInhom) * (1.0-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(oit.GetIndex()); // 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) double kx = kIdx[0]; double 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 if (oit.GetIndex()[1]%2 == 1) { kx -= m_Parameters->m_SignalGen.m_KspaceLineOffset; // add gradient delay induced offset } else { kx += m_Parameters->m_SignalGen.m_KspaceLineOffset; // add gradient delay induced offset } vcl_complex<double> s(0,0); InputIteratorType it(m_CompartmentImages.at(0), m_CompartmentImages.at(0)->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { double x = it.GetIndex()[0]; double y = it.GetIndex()[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; } DoubleVectorType 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<double> f(0, 0); // sum compartment signals and simulate relaxation for (unsigned int i=0; i<m_CompartmentImages.size(); i++) if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation) f += std::complex<double>( m_CompartmentImages.at(i)->GetPixel(it.GetIndex()) * relaxFactor.at(i) * m_Parameters->m_SignalGen.m_SignalScale, 0); else f += std::complex<double>( m_CompartmentImages.at(i)->GetPixel(it.GetIndex()) * m_Parameters->m_SignalGen.m_SignalScale ); if (m_Parameters->m_SignalGen.m_CoilSensitivityProfile!=SignalGenerationParameters::COIL_CONSTANT) f *= CoilSensitivity(pos); // simulate eddy currents and other distortions double 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<double, 3> point3D; ItkDoubleImgType::IndexType index; index[0] = it.GetIndex()[0]; index[1] = it.GetIndex()[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 += InterpolateFmapValue(point3D); } 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 * exp( std::complex<double>(0, 2 * M_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<double>(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 TPixelType > void KspaceImageFilter< TPixelType > ::AfterThreadedGenerateData() { delete m_ReadoutScheme; typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); double kxMax = outputImage->GetLargestPossibleRegion().GetSize(0); // k-space size in x-direction double 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<double> s = outputImage->GetPixel(kIdx2); s = vcl_complex<double>(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; i<m_SpikesPerSlice; i++) { spikeIdx[0] = randGen->GetIntegerVariate()%(int)kxMax; spikeIdx[1] = randGen->GetIntegerVariate()%(int)kyMax; outputImage->SetPixel(spikeIdx, m_Spike); m_SpikeLog += "[" + boost::lexical_cast<std::string>(spikeIdx[0]) + "," + boost::lexical_cast<std::string>(spikeIdx[1]) + "," + boost::lexical_cast<std::string>(m_Zidx) + "] Magnitude: " + boost::lexical_cast<std::string>(m_Spike.real()) + "+" + boost::lexical_cast<std::string>(m_Spike.imag()) + "i\n"; } } template< class TPixelType > double KspaceImageFilter< TPixelType >::InterpolateFmapValue(itk::Point<float, 3> itkP) { itk::Index<3> idx; itk::ContinuousIndex< double, 3> cIdx; m_Parameters->m_SignalGen.m_FrequencyMap->TransformPhysicalPointToIndex(itkP, idx); m_Parameters->m_SignalGen.m_FrequencyMap->TransformPhysicalPointToContinuousIndex(itkP, cIdx); double pix = 0; if ( m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().IsInside(idx) ) pix = m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(idx); else return pix; double frac_x = cIdx[0] - idx[0]; double frac_y = cIdx[1] - idx[1]; double frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx[0] >= 0 && idx[0] < m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(0)-1 && - idx[1] >= 0 && idx[1] < m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(1)-1 && - idx[2] >= 0 && idx[2] < m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(2)-1) + if (idx[0] >= 0 && idx[0] < static_cast<itk::IndexValueType>(m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(0) - 1) && + idx[1] >= 0 && idx[1] < static_cast<itk::IndexValueType>(m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(1) - 1) && + idx[2] >= 0 && idx[2] < static_cast<itk::IndexValueType>(m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(2) - 1)) { vnl_vector_fixed<double, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); pix = m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(idx) * interpWeights[0]; ItkDoubleImgType::IndexType tmpIdx = idx; tmpIdx[0]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[7]; } return pix; } } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp index 33c2a5a540..7246951868 100755 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp @@ -1,1737 +1,1735 @@ /*=================================================================== 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 <boost/progress.hpp> #include <vtkSmartPointer.h> #include <vtkPolyData.h> #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkPolyLine.h> #include <itkImageRegionIteratorWithIndex.h> #include <itkResampleImageFilter.h> #include <itkNearestNeighborInterpolateImageFunction.h> #include <itkBSplineInterpolateImageFunction.h> #include <itkCastImageFilter.h> #include <itkImageFileWriter.h> #include <itkRescaleIntensityImageFilter.h> #include <itkWindowedSincInterpolateImageFunction.h> #include <itkResampleDwiImageFilter.h> #include <itkKspaceImageFilter.h> #include <itkDftImageFilter.h> #include <itkAddImageFilter.h> #include <itkConstantPadImageFilter.h> #include <itkCropImageFilter.h> #include <mitkAstroStickModel.h> #include <vtkTransform.h> #include <iostream> #include <fstream> #include <exception> #include <itkImageDuplicator.h> #include <itksys/SystemTools.hxx> #include <mitkIOUtil.h> #include <itkDiffusionTensor3DReconstructionImageFilter.h> #include <itkDiffusionTensor3D.h> #include <itkTractDensityImageFilter.h> #include <boost/lexical_cast.hpp> #include <itkTractsToVectorImageFilter.h> #include <itkInvertIntensityImageFilter.h> #include <itkShiftScaleImageFilter.h> #include <itkExtractImageFilter.h> #include <itkResampleDwiImageFilter.h> #include <boost/algorithm/string/replace.hpp> 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(); } template< class PixelType > TractsToDWIImageFilter< PixelType >::~TractsToDWIImageFilter() { } template< class PixelType > TractsToDWIImageFilter< PixelType >::DoubleDwiType::Pointer TractsToDWIImageFilter< PixelType >:: SimulateKspaceAcquisition( std::vector< DoubleDwiType::Pointer >& images ) { - int numFiberCompartments = m_Parameters.m_FiberModelList.size(); + 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(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( 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( 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; i<m_Parameters.m_SignalGen.m_Spikes; i++) spikeVolume.push_back(m_RandGen->GetIntegerVariate()%(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<double, 3> > coilPositions; itk::Vector<double, 3> pos; pos.Fill(0.0); pos[1] = -diagonal/2; itk::Vector<double, 3> 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; c<m_Parameters.m_SignalGen.m_NumberOfCoils; c++) { coilPositions.push_back(pos); m_CoilPointset->InsertPoint(c, pos*1000 + m_Parameters.m_SignalGen.m_ImageOrigin.GetVectorFromOrigin() + center ); double rz = 360.0/m_Parameters.m_SignalGen.m_NumberOfCoils * M_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(images.at(0)->GetVectorLength()*images.at(0)->GetLargestPossibleRegion().GetSize(2)); for (unsigned int g=0; g<images.at(0)->GetVectorLength(); g++) { std::vector< unsigned int > spikeSlice; while (!spikeVolume.empty() && spikeVolume.back()==g) { spikeSlice.push_back(m_RandGen->GetIntegerVariate()%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; z<images.at(0)->GetLargestPossibleRegion().GetSize(2); z++) { std::vector< SliceType::Pointer > compartmentSlices; std::vector< double > t2Vector; std::vector< double > t1Vector; for (unsigned int i=0; i<images.size(); i++) { DiffusionSignalModel<double>* signalModel; if (i<numFiberCompartments) signalModel = m_Parameters.m_FiberModelList.at(i); else signalModel = m_Parameters.m_NonFiberModelList.at(i-numFiberCompartments); auto slice = SliceType::New(); slice->SetLargestPossibleRegion( 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; y<images.at(0)->GetLargestPossibleRegion().GetSize(1); y++) for (unsigned int x=0; x<images.at(0)->GetLargestPossibleRegion().GetSize(0); x++) { SliceType::IndexType index2D; index2D[0]=x; index2D[1]=y; DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; slice->SetPixel(index2D, images.at(i)->GetPixel(index3D)[g]); } compartmentSlices.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()) return nullptr; #pragma omp parallel for for (int c=0; c<m_Parameters.m_SignalGen.m_NumberOfCoils; c++) { // create k-sapce (inverse fourier transform slices) auto idft = itk::KspaceImageFilter< SliceType::PixelType >::New(); idft->SetCompartmentImages(compartmentSlices); idft->SetT2(t2Vector); idft->SetT1(t1Vector); idft->SetUseConstantRandSeed(m_UseConstantRandSeed); idft->SetParameters(&m_Parameters); idft->SetZ((double)z-(double)( images.at(0)->GetLargestPossibleRegion().GetSize(2) -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<std::string>(g) + " Coil " + boost::lexical_cast<std::string>(c) + "\n"; m_SpikeLog += idft->GetSpikeLog(); } ComplexSliceType::Pointer fSlice; fSlice = idft->GetOutput(); // fourier transform slice ComplexSliceType::Pointer newSlice; auto dft = itk::DftImageFilter< SliceType::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; y<fSlice->GetLargestPossibleRegion().GetSize(1); y++) for (unsigned int x=0; x<fSlice->GetLargestPossibleRegion().GetSize(0); x++) { DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; ComplexSliceType::IndexType index2D; index2D[0]=x; index2D[1]=y; ComplexSliceType::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) { #ifdef WIN32 #pragma omp parallel for #else #pragma omp parallel for collapse(2) #endif - for (int y=0; y<magnitudeDwiImage->GetLargestPossibleRegion().GetSize(1); y++) - for (int x=0; x<magnitudeDwiImage->GetLargestPossibleRegion().GetSize(0); x++) + for (int y=0; y<static_cast<int>(magnitudeDwiImage->GetLargestPossibleRegion().GetSize(1)); y++) + for (int x=0; x<static_cast<int>(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()>900) // it.Set(900); if (it.Get()>max) max = it.Get(); if (it.Get()<min) min = it.Get(); ++it; } auto scaler = itk::ShiftScaleImageFilter< ItkDoubleImgType, ItkDoubleImgType >::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 int fibVolImages = 0; for (int i=0; i<m_Parameters.m_FiberModelList.size(); i++) { if (m_Parameters.m_FiberModelList[i]->GetVolumeFractionImage().IsNotNull()) { PrintToLog("Using volume fraction map for fiber compartment " + boost::lexical_cast<std::string>(i+1)); fibVolImages++; } } // check for non-fiber volume fraction maps int nonfibVolImages = 0; for (int i=0; i<m_Parameters.m_NonFiberModelList.size(); i++) { if (m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage().IsNotNull()) { PrintToLog("Using volume fraction map for non-fiber compartment " + boost::lexical_cast<std::string>(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<m_Parameters.m_FiberModelList.size() && nonfibVolImages==1 && m_Parameters.m_NonFiberModelList.size()==2) { PrintToLog("Calculating missing non-fiber volume fraction image by inverting the other.\n" "Assuming non-fiber volume fraction images to contain values relative to the" " remaining non-fiber volume, not absolute values."); auto inverter = itk::InvertIntensityImageFilter< ItkDoubleImgType, ItkDoubleImgType >::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 (fibVolImages<m_Parameters.m_FiberModelList.size() && nonfibVolImages>0) { 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<ItkDoubleImgType>::Pointer wr = itk::ImageFileWriter<ItkDoubleImgType>::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 (int i=0; i<m_Parameters.m_FiberModelList.size()+m_Parameters.m_NonFiberModelList.size(); i++) { auto doubleImg = ItkDoubleImgType::New(); doubleImg->SetSpacing( 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<double,3> 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; i<numFiberCompartments+numNonFiberCompartments; i++) { auto doubleDwi = DoubleDwiType::New(); doubleDwi->SetSpacing( 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<m_Parameters.m_SignalGen.GetNumVolumes(); g++) { m_Rotation.Fill(0.0); m_Translation.Fill(0.0); m_Rotations.push_back(m_Rotation); m_Translations.push_back(m_Translation); } } // resample mask image and frequency map to fit upsampled geometry if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging) { if (m_Parameters.m_SignalGen.m_MaskImage.IsNotNull()) { // rescale mask image (otherwise there are problems with the resampling) auto rescaler = itk::RescaleIntensityImageFilter<ItkUcharImgType,ItkUcharImgType>::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<ItkUcharImgType, ItkUcharImgType>::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<ItkUcharImgType, double>::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<ItkDoubleImgType, ItkDoubleImgType>::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<ItkDoubleImgType, double>::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<std::string>(m_Parameters.m_SignalGen.m_Rotation) + "°", false); PrintToLog("Maximum translation: +/-" + boost::lexical_cast<std::string>(m_Parameters.m_SignalGen.m_Translation) + "mm", false); } else { PrintToLog("Linear motion artifacts:", false); PrintToLog("Maximum rotation: " + boost::lexical_cast<std::string>(m_Parameters.m_SignalGen.m_Rotation) + "°", false); PrintToLog("Maximum translation: " + boost::lexical_cast<std::string>(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()<m_Parameters.m_SignalGen.GetNumVolumes() ) { m_Parameters.m_SignalGen.m_MotionVolumes.push_back(false); } m_NumMotionVolumes = 0; - for (int i=0; i<m_Parameters.m_SignalGen.GetNumVolumes(); i++) + for (unsigned int i=0; i<m_Parameters.m_SignalGen.GetNumVolumes(); ++i) { if (m_Parameters.m_SignalGen.m_MotionVolumes[i]) - { - m_NumMotionVolumes++; - } + ++m_NumMotionVolumes; } m_MotionCounter = 0; // creat image to hold transformed mask (motion artifact) m_TransformedMaskImage = ItkUcharImgType::New(); auto duplicator = itk::ImageDuplicator<ItkUcharImgType>::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<double,3> 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<ItkUcharImgType, ItkUcharImgType>::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<ItkUcharImgType, double>::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]<m_WorkingSpacing[1] && m_WorkingSpacing[0]<m_WorkingSpacing[2]) { minSpacing = m_WorkingSpacing[0]; } else if (m_WorkingSpacing[1] < m_WorkingSpacing[2]) { minSpacing = m_WorkingSpacing[1]; } else { minSpacing = m_WorkingSpacing[2]; } // working copy is needed because we need to resample the fibers but do not want to change the original bundle m_FiberBundleWorkingCopy = m_FiberBundle->GetDeepCopy(); double volumeAccuracy = 10; m_FiberBundleWorkingCopy->ResampleLinear(minSpacing/volumeAccuracy); m_mmRadius = m_Parameters.m_SignalGen.m_AxonRadius/1000; auto caster = itk::CastImageFilter< itk::Image<unsigned char, 3>, itk::Image<float, 3> >::New(); caster->SetInput(m_TransformedMaskImage); caster->Update(); auto density_calculator = itk::TractDensityImageFilter< itk::Image<float, 3> >::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; stringstream stream; stream << fixed << setprecision(2) << max_density * 100 * m_SegmentVolume; string s = stream.str(); PrintToLog("\nMax. fiber volume: " + s + "mm².", false, true, true); } else { stringstream stream; stream << fixed << setprecision(2) << max_density * 100 * m_SegmentVolume; 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 * 100.0); float new_fib_radius = 1000*std::sqrt(new_seg_vol*volumeAccuracy/(minSpacing*M_PI)); stringstream stream; stream << fixed << setprecision(2) << new_fib_radius; 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<int>::max() ) { fileName = NameTest + "_" + boost::lexical_cast<std::string>(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<std::string>(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<std::string>(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()); 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; g<m_Parameters.m_SignalGen.GetNumVolumes(); g++) { // move fibers SimulateMotion(g); // Set signal model random generator seeds to get same configuration in each voxel for (int i=0; i<m_Parameters.m_FiberModelList.size(); i++) m_Parameters.m_FiberModelList.at(i)->SetSeed(signalModelSeed); for (int i=0; i<m_Parameters.m_NonFiberModelList.size(); i++) m_Parameters.m_NonFiberModelList.at(i)->SetSeed(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; vtkPolyData* fiberPolyData = m_FiberBundleTransformed->GetFiberPolyData(); // generate fiber signal (if there are any fiber models present) if (!m_Parameters.m_FiberModelList.empty()) for( int i=0; i<numFibers; i++ ) { float fiberWeight = m_FiberBundleTransformed->GetFiberWeight(i); vtkCell* cell = fiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (numPoints<2) continue; for( int j=0; j<numPoints; j++) { if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } double* temp = points->GetPoint(j); itk::Point<float, 3> vertex = GetItkPoint(temp); itk::Vector<double> v = GetItkVector(temp); itk::Vector<double, 3> dir(3); if (j<numPoints-1) { dir = GetItkVector(points->GetPoint(j+1))-v; } else { dir = v-GetItkVector(points->GetPoint(j-1)); } if ( dir.GetSquaredNorm()<0.0001 || dir[0]!=dir[0] || dir[1]!=dir[1] || dir[2]!=dir[2] ) { continue; } itk::Index<3> idx; itk::ContinuousIndex<float, 3> contIndex; m_TransformedMaskImage->TransformPhysicalPointToIndex(vertex, idx); m_TransformedMaskImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); if (!m_TransformedMaskImage->GetLargestPossibleRegion().IsInside(idx) || m_TransformedMaskImage->GetPixel(idx)<=0) { continue; } // generate signal for each fiber compartment for (int k=0; k<numFiberCompartments; k++) { m_Parameters.m_FiberModelList[k]->SetFiberDirection(dir); DoubleDwiType::PixelType pix = m_CompartmentImages.at(k)->GetPixel(idx); pix[g] += fiberWeight*m_SegmentVolume*m_Parameters.m_FiberModelList[k]->SimulateMeasurement(g); 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; } } // 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<ItkUcharImgType> it3(m_TransformedMaskImage, m_TransformedMaskImage->GetLargestPossibleRegion()); double fact = 1; // density correction factor in mm³ if (m_Parameters.m_SignalGen.m_AxonRadius<0.0001 || maxVolume>m_VoxelVolume) // 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<double, 3> 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); // if volume fraction image is set use it, otherwise use scaling factor to obtain one full fiber voxel double fact2 = fact; if ( m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()!=nullptr && iAxVolume>0.0001 ) { double val = InterpolateValue(point, m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()); 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; i<numFiberCompartments; i++) { DoubleDwiType::PixelType pix = m_CompartmentImages.at(i)->GetPixel(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<std::string>(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) 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<m_CompartmentImages.size(); i++) { auto adder = itk::AddImageFilter< DoubleDwiType, DoubleDwiType, DoubleDwiType>::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<unsigned int>::max(); ImageRegionIterator<OutputImageType> 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; i<signal.Size(); i++) { if (signal[i]>0) 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]<min) min = signal[i]; } it4.Set(signal); ++it4; } window -= min; unsigned int level = window/2 + min; m_LevelWindow.SetLevelWindow(level, window); this->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(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() ) + && g<static_cast<int>(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<ItkUcharImgType> 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<double, 3> 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<std::string>(g) + " rotation: " + boost::lexical_cast<std::string>(m_Rotation[0]) + "," + boost::lexical_cast<std::string>(m_Rotation[1]) + "," + boost::lexical_cast<std::string>(m_Rotation[2]) + ";"; m_MotionLog += " translation: " + boost::lexical_cast<std::string>(m_Translation[0]) + "," + boost::lexical_cast<std::string>(m_Translation[1]) + "," + boost::lexical_cast<std::string>(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<std::string>(g) + " rotation: " + boost::lexical_cast<std::string>(m_Rotation[0]*m_MotionCounter) + "," + boost::lexical_cast<std::string>(m_Rotation[1]*m_MotionCounter) + "," + boost::lexical_cast<std::string>(m_Rotation[2]*m_MotionCounter) + ";"; m_MotionLog += " translation: " + boost::lexical_cast<std::string>(m_Translation[0]*m_MotionCounter) + "," + boost::lexical_cast<std::string>(m_Translation[1]*m_MotionCounter) + "," + boost::lexical_cast<std::string>(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<std::string>(g) + " rotation: " + boost::lexical_cast<std::string>(m_Rotation[0]) + "," + boost::lexical_cast<std::string>(m_Rotation[1]) + "," + boost::lexical_cast<std::string>(m_Rotation[2]) + ";"; m_MotionLog += " translation: " + boost::lexical_cast<std::string>(m_Translation[0]) + "," + boost::lexical_cast<std::string>(m_Translation[1]) + "," + boost::lexical_cast<std::string>(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; i<numFiberCompartments; i++) { DoubleDwiType::PixelType pix = m_CompartmentImages.at(i)->GetPixel(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<double, 3> 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; i<numNonFiberCompartments; i++) { double weight = 0; if (numNonFiberCompartments>1) { double val = InterpolateValue(point, m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()); 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_VoxelVolume; else pix += m_Parameters.m_NonFiberModelList[maxVolumeIndex]->SimulateMeasurement()*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; i<numFiberCompartments; i++) { double weight = interAxonalVolume; DoubleDwiType::PixelType pix = m_CompartmentImages.at(i)->GetPixel(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) { double val = InterpolateValue(point, m_Parameters.m_FiberModelList[i]->GetVolumeFractionImage()); 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; i<numNonFiberCompartments; i++) { double weight = other; DoubleDwiType::PixelType pix = m_CompartmentImages.at(i+numFiberCompartments)->GetPixel(index); if (m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()!=nullptr) { double val = InterpolateValue(point, m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()); 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)*weight; else pix += m_Parameters.m_NonFiberModelList[i]->SimulateMeasurement()*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 > double TractsToDWIImageFilter< PixelType >:: InterpolateValue(itk::Point<float, 3> itkP, ItkDoubleImgType::Pointer img) { itk::Index<3> idx; itk::ContinuousIndex< double, 3> cIdx; img->TransformPhysicalPointToIndex(itkP, idx); img->TransformPhysicalPointToContinuousIndex(itkP, cIdx); double pix = 0; if ( img->GetLargestPossibleRegion().IsInside(idx) ) pix = img->GetPixel(idx); else return pix; double frac_x = cIdx[0] - idx[0]; double frac_y = cIdx[1] - idx[1]; double frac_z = cIdx[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; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? - if (idx[0] >= 0 && idx[0] < img->GetLargestPossibleRegion().GetSize(0)-1 && - idx[1] >= 0 && idx[1] < img->GetLargestPossibleRegion().GetSize(1)-1 && - idx[2] >= 0 && idx[2] < img->GetLargestPossibleRegion().GetSize(2)-1) + if (idx[0] >= 0 && idx[0] < static_cast<itk::IndexValueType>(img->GetLargestPossibleRegion().GetSize(0) - 1) && + idx[1] >= 0 && idx[1] < static_cast<itk::IndexValueType>(img->GetLargestPossibleRegion().GetSize(1) - 1) && + idx[2] >= 0 && idx[2] < static_cast<itk::IndexValueType>(img->GetLargestPossibleRegion().GetSize(2) - 1)) { vnl_vector_fixed<double, 8> interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); pix = img->GetPixel(idx) * interpWeights[0]; ItkDoubleImgType::IndexType tmpIdx = idx; tmpIdx[0]++; pix += img->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; pix += img->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; pix += img->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; pix += img->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; pix += img->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; pix += img->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; pix += img->GetPixel(tmpIdx) * interpWeights[7]; } return pix; } template< class PixelType > itk::Point<float, 3> TractsToDWIImageFilter< PixelType >::GetItkPoint(double point[3]) { itk::Point<float, 3> itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } template< class PixelType > itk::Vector<double, 3> TractsToDWIImageFilter< PixelType >::GetItkVector(double point[3]) { itk::Vector<double, 3> itkVector; itkVector[0] = point[0]; itkVector[1] = point[1]; itkVector[2] = point[2]; return itkVector; } template< class PixelType > vnl_vector_fixed<double, 3> TractsToDWIImageFilter< PixelType >::GetVnlVector(double point[3]) { vnl_vector_fixed<double, 3> vnlVector; vnlVector[0] = point[0]; vnlVector[1] = point[1]; vnlVector[2] = point[2]; return vnlVector; } template< class PixelType > vnl_vector_fixed<double, 3> TractsToDWIImageFilter< PixelType >::GetVnlVector(Vector<float,3>& vector) { vnl_vector_fixed<double, 3> 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<std::string>(hours)); out.append(":"); out.append(boost::lexical_cast<std::string>(minutes)); out.append(":"); out.append(boost::lexical_cast<std::string>(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 b74015b38c..467678d9c4 100755 --- a/Modules/DiffusionImaging/FiberTracking/IODataStructures/FiberBundle/mitkFiberBundle.cpp +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/FiberBundle/mitkFiberBundle.cpp @@ -1,2228 +1,2228 @@ /*=================================================================== 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 <mitkPlanarCircle.h> #include <mitkPlanarPolygon.h> #include <mitkPlanarFigureComposite.h> #include "mitkImagePixelReadAccessor.h" #include <mitkPixelTypeMultiplex.h> #include <vtkPointData.h> #include <vtkDataArray.h> #include <vtkUnsignedCharArray.h> #include <vtkPolyLine.h> #include <vtkCellArray.h> #include <vtkCellData.h> #include <vtkIdFilter.h> #include <vtkClipPolyData.h> #include <vtkPlane.h> #include <vtkDoubleArray.h> #include <vtkKochanekSpline.h> #include <vtkParametricFunctionSource.h> #include <vtkParametricSpline.h> #include <vtkPolygon.h> #include <vtkCleanPolyData.h> #include <cmath> #include <boost/progress.hpp> #include <vtkTransformPolyDataFilter.h> #include <mitkTransferFunction.h> #include <vtkLookupTable.h> #include <mitkLookupTable.h> #include <vtkCardinalSpline.h> const char* mitk::FiberBundle::FIBER_ID_ARRAY = "Fiber_IDs"; using namespace std; mitk::FiberBundle::FiberBundle( vtkPolyData* fiberPolyData ) : m_NumFibers(0) { m_FiberWeights = vtkSmartPointer<vtkFloatArray>::New(); m_FiberWeights->SetName("FIBER_WEIGHTS"); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); if (fiberPolyData != nullptr) { m_FiberPolyData = fiberPolyData; this->ColorFibersByOrientation(); } this->UpdateFiberGeometry(); this->GenerateFiberIds(); } 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<vtkPolyData> mitk::FiberBundle::GeneratePolyDataByIds(std::vector<long> fiberIds) { vtkSmartPointer<vtkPolyData> newFiberPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> newLineSet = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> newPointSet = vtkSmartPointer<vtkPoints>::New(); 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<vtkCell> fiber = m_FiberIdDataSet->GetCell(*finIt);//->DeepCopy(fiber); vtkSmartPointer<vtkPoints> fibPoints = fiber->GetPoints(); vtkSmartPointer<vtkPolyLine> newFiber = vtkSmartPointer<vtkPolyLine>::New(); newFiber->GetPointIds()->SetNumberOfIds( fibPoints->GetNumberOfPoints() ); for(int i=0; i<fibPoints->GetNumberOfPoints(); i++) { newFiber->GetPointIds()->SetId(i, newPointSet->GetNumberOfPoints()); newPointSet->InsertNextPoint(fibPoints->GetPoint(i)[0], fibPoints->GetPoint(i)[1], fibPoints->GetPoint(i)[2]); } newLineSet->InsertNextCell(newFiber); ++finIt; } newFiberPolyData->SetPoints(newPointSet); newFiberPolyData->SetLines(newLineSet); return newFiberPolyData; } // merge two fiber bundles mitk::FiberBundle::Pointer mitk::FiberBundle::AddBundle(mitk::FiberBundle* fib) { if (fib==nullptr) { MITK_WARN << "trying to call AddBundle with nullptr argument"; return nullptr; } MITK_INFO << "Adding fibers"; vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); // add current fiber bundle vtkSmartPointer<vtkFloatArray> weights = vtkSmartPointer<vtkFloatArray>::New(); weights->SetNumberOfValues(this->GetNumFibers()+fib->GetNumFibers()); unsigned int counter = 0; for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, this->GetFiberWeight(i)); vNewLines->InsertNextCell(container); counter++; } // add new fiber bundle for (int i=0; i<fib->GetFiberPolyData()->GetNumberOfCells(); i++) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double p[3]; points->GetPoint(j, p); vtkIdType id = vNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } weights->InsertValue(counter, 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; } // subtract two fiber bundles mitk::FiberBundle::Pointer mitk::FiberBundle::SubtractBundle(mitk::FiberBundle* fib) { MITK_INFO << "Subtracting fibers"; vtkSmartPointer<vtkPolyData> vNewPolyData = vtkSmartPointer<vtkPolyData>::New(); vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New(); std::vector< std::vector< itk::Point<float, 3> > > points1; for( int i=0; i<m_NumFibers; i++ ) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; itk::Point<float, 3> start = GetItkPoint(points->GetPoint(0)); itk::Point<float, 3> end = GetItkPoint(points->GetPoint(numPoints-1)); points1.push_back( {start, end} ); } std::vector< std::vector< itk::Point<float, 3> > > points2; for( int i=0; i<fib->GetNumFibers(); i++ ) { vtkCell* cell = fib->GetFiberPolyData()->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; itk::Point<float, 3> start = GetItkPoint(points->GetPoint(0)); itk::Point<float, 3> 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<points1.size(); i++) { #pragma omp critical { progress++; std::cout << (int)(100*(float)progress/points1.size()) << "%" << '\r'; cout.flush(); } bool match = false; for (int j=0; j<points2.size(); j++) { auto v1 = points1.at(i); auto v2 = points2.at(j); unsigned int matches = 0; unsigned int reverse_matches = 0; for (int c=0; c<v1.size(); c++) { if (v1[c].SquaredEuclideanDistanceTo(v2[c])<mitk::eps) matches++; if (v1[v1.size() - c - 1].SquaredEuclideanDistanceTo(v2[c])<mitk::eps) reverse_matches++; } if (matches==v1.size() || reverse_matches==v1.size()) { match = true; j=points2.size(); } } #pragma omp critical if (!match) ids.push_back(i); } for( int i : ids ) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (points==nullptr || numPoints<=0) continue; vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for( int j=0; j<numPoints; j++) { vtkIdType id = vNewPoints->InsertNextPoint(points->GetPoint(j)); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } if(vNewLines->GetNumberOfCells()==0) return nullptr; // initialize PolyData vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle return mitk::FiberBundle::New(vNewPolyData); } itk::Point<float, 3> mitk::FiberBundle::GetItkPoint(double point[3]) { itk::Point<float, 3> 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<vtkPolyData> fiberPD, bool updateGeometry) { if (fiberPD == nullptr) this->m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); else { m_FiberPolyData->DeepCopy(fiberPD); ColorFibersByOrientation(); } m_NumFibers = m_FiberPolyData->GetNumberOfLines(); if (updateGeometry) UpdateFiberGeometry(); GenerateFiberIds(); } /* * return vtkPolyData */ vtkSmartPointer<vtkPolyData> 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<vtkUnsignedCharArray>::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; fi<numOfFibers; ++fi) { vtkIdType* idList; // contains the point id's of the line vtkIdType pointsPerFiber; // number of points for current line fiberList->GetNextCell(pointsPerFiber, idList); /* single fiber checkpoints: is number of points valid */ if (pointsPerFiber > 1) { /* operate on points of single fiber */ for (int i=0; i <pointsPerFiber; ++i) { /* process all points elastV[0]ept starting and endpoint for calculating color value take current point, previous point and next point */ if (i<pointsPerFiber-1 && i > 0) { /* The color value of the current point is influenced by the previous point and next point. */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; vnl_vector_fixed< double, 3 > diff; diff = (diff1 - diff2) / 2.0; diff.normalize(); rgba[0] = (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->InsertTupleValue(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 minMaxNorm) { 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<vtkUnsignedCharArray>::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<vtkLookupTable> lookupTable = vtkSmartPointer<vtkLookupTable>::New(); lookupTable->SetTableRange(0.0, 0.8); lookupTable->Build(); mitkLookup->SetVtkLookupTable(lookupTable); mitkLookup->SetType(mitk::LookupTable::JET); 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; i<m_FiberPolyData->GetNumberOfCells(); i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures for (int j=0; j<numPoints; j++) { double dist = 0; int c = j; std::vector< vnl_vector_fixed< float, 3 > > vectors; vnl_vector_fixed< float, 3 > meanV; meanV.fill(0.0); while(dist<window/2 && c>1) { double p1[3]; points->GetPoint(c-1, p1); double p2[3]; points->GetPoint(c, p2); vnl_vector_fixed< 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); if (c==j) meanV += v; c--; } c = j; dist = 0; while(dist<window/2 && c<numPoints-1) { double p1[3]; points->GetPoint(c, p1); double p2[3]; points->GetPoint(c+1, p2); vnl_vector_fixed< 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); if (c==j) meanV += v; c++; } meanV.normalize(); double dev = 0; for (unsigned int c=0; c<vectors.size(); c++) { double angle = dot_product(meanV, vectors.at(c)); if (angle>1.0) angle = 1.0; if (angle<-1.0) angle = -1.0; dev += acos(angle)*180/M_PI; } if (vectors.size()>0) dev /= vectors.size(); dev = 1.0-dev/180.0; values.push_back(dev); if (dev<min) min = dev; if (dev>max) max = dev; } } unsigned int count = 0; for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); for (int j=0; j<numPoints; j++) { double color[3]; double dev = values.at(count); if (minMaxNorm) dev = (dev-min)/(max-min); // double dev = values.at(count)*values.at(count); 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->InsertTupleValue(cell->GetPointId(j), rgba); count++; } } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::SetFiberOpacity(vtkDoubleArray* FAValArray) { for(long i=0; i<m_FiberColors->GetNumberOfTuples(); i++) { double faValue = FAValArray->GetValue(i); faValue = faValue * 255.0; m_FiberColors->SetComponent(i,3, (unsigned char) faValue ); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ResetFiberOpacity() { for(long i=0; i<m_FiberColors->GetNumberOfTuples(); i++) m_FiberColors->SetComponent(i,3, 255.0 ); m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::ColorFibersByScalarMap(mitk::Image::Pointer FAimage, bool opacity) { mitkPixelTypeMultiplex2( ColorFibersByScalarMap, FAimage->GetPixelType(), FAimage, opacity ); m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } template <typename TPixel> void mitk::FiberBundle::ColorFibersByScalarMap(const mitk::PixelType, mitk::Image::Pointer image, bool opacity) { m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); mitk::ImagePixelReadAccessor<TPixel,3> 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<vtkLookupTable> lookupTable = vtkSmartPointer<vtkLookupTable>::New(); lookupTable->SetTableRange(0.0, 0.8); lookupTable->Build(); mitkLookup->SetVtkLookupTable(lookupTable); mitkLookup->SetType(mitk::LookupTable::JET); for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++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); 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->InsertTupleValue(i, rgba); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::SetFiberColors(float r, float g, float b, float alpha) { m_FiberColors = vtkSmartPointer<vtkUnsignedCharArray>::New(); m_FiberColors->Allocate(m_FiberPolyData->GetNumberOfPoints() * 4); m_FiberColors->SetNumberOfComponents(4); m_FiberColors->SetName("FIBER_COLORS"); unsigned char rgba[4] = {0,0,0,0}; for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++i) { rgba[0] = (unsigned char) r; rgba[1] = (unsigned char) g; rgba[2] = (unsigned char) b; rgba[3] = (unsigned char) alpha; m_FiberColors->InsertTupleValue(i, rgba); } m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } void mitk::FiberBundle::GenerateFiberIds() { if (m_FiberPolyData == nullptr) return; vtkSmartPointer<vtkIdFilter> idFiberFilter = vtkSmartPointer<vtkIdFilter>::New(); idFiberFilter->SetInputData(m_FiberPolyData); idFiberFilter->CellIdsOn(); // idFiberFilter->PointIdsOn(); // point id's are not needed idFiberFilter->SetIdsArrayName(FIBER_ID_ARRAY); idFiberFilter->FieldDataOn(); idFiberFilter->Update(); m_FiberIdDataSet = idFiberFilter->GetOutput(); } mitk::FiberBundle::Pointer mitk::FiberBundle::ExtractFiberSubset(ItkUcharImgType* mask, bool anyPoint, bool invert, bool bothEnds, float fraction) { vtkSmartPointer<vtkPolyData> PolyData = m_FiberPolyData; if (anyPoint) { float minSpacing = 1; if(mask->GetSpacing()[0]<mask->GetSpacing()[1] && mask->GetSpacing()[0]<mask->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/5); PolyData = fibCopy->GetFiberPolyData(); } vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Extracting fibers"; boost::progress_display disp(m_NumFibers); for (int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = PolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkCell* cellOriginal = m_FiberPolyData->GetCell(i); int numPointsOriginal = cellOriginal->GetNumberOfPoints(); vtkPoints* pointsOriginal = cellOriginal->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); if (numPoints>1 && numPointsOriginal) { if (anyPoint) { int inside = 0; int outside = 0; if (!invert) { for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); itk::Point<float, 3> 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++; if (fraction==0) break; } else outside++; } float current_fraction = 0.0; if (inside+outside>0) current_fraction = (float)inside/(inside+outside); if (current_fraction>fraction) { for (int k=0; k<numPoints; k++) { double* p = points->GetPoint(k); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } } } else { bool includeFiber = true; for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); itk::Point<float, 3> 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) ) { inside++; includeFiber = false; break; } else outside++; } if (includeFiber) { for (int k=0; k<numPoints; k++) { double* p = points->GetPoint(k); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } } } } else { double* start = pointsOriginal->GetPoint(0); itk::Point<float, 3> itkStart; itkStart[0] = start[0]; itkStart[1] = start[1]; itkStart[2] = start[2]; itk::Index<3> idxStart; mask->TransformPhysicalPointToIndex(itkStart, idxStart); double* end = pointsOriginal->GetPoint(numPointsOriginal-1); itk::Point<float, 3> itkEnd; itkEnd[0] = end[0]; itkEnd[1] = end[1]; itkEnd[2] = end[2]; itk::Index<3> idxEnd; mask->TransformPhysicalPointToIndex(itkEnd, idxEnd); if (invert) { if (bothEnds) { if ( !mask->GetPixel(idxStart)>0 && !mask->GetPixel(idxEnd)>0 ) { for (int j=0; j<numPointsOriginal; j++) { double* p = pointsOriginal->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } } } else if ( !mask->GetPixel(idxStart)>0 || !mask->GetPixel(idxEnd)>0 ) { for (int j=0; j<numPointsOriginal; j++) { double* p = pointsOriginal->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } } } else { if (bothEnds) { if ( mask->GetPixel(idxStart)>0 && mask->GetPixel(idxEnd)>0 && mask->GetLargestPossibleRegion().IsInside(idxStart) && mask->GetLargestPossibleRegion().IsInside(idxEnd) ) { for (int j=0; j<numPointsOriginal; j++) { double* p = pointsOriginal->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } } } else if ( (mask->GetPixel(idxStart)>0 && mask->GetLargestPossibleRegion().IsInside(idxStart)) || (mask->GetPixel(idxEnd)>0 && mask->GetLargestPossibleRegion().IsInside(idxEnd)) ) { for (int j=0; j<numPointsOriginal; j++) { double* p = pointsOriginal->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } } } } } vtkNewCells->InsertNextCell(container); } if (vtkNewCells->GetNumberOfCells()<=0) return nullptr; vtkSmartPointer<vtkPolyData> newPolyData = vtkSmartPointer<vtkPolyData>::New(); newPolyData->SetPoints(vtkNewPoints); newPolyData->SetLines(vtkNewCells); return mitk::FiberBundle::New(newPolyData); } mitk::FiberBundle::Pointer mitk::FiberBundle::RemoveFibersOutside(ItkUcharImgType* mask, bool invert) { float minSpacing = 1; if(mask->GetSpacing()[0]<mask->GetSpacing()[1] && mask->GetSpacing()[0]<mask->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<vtkPolyData> PolyData =fibCopy->GetFiberPolyData(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Cutting fibers"; boost::progress_display disp(m_NumFibers); for (int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = PolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); if (numPoints>1) { int newNumPoints = 0; for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); itk::Point<float, 3> 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<vtkPolyLine>::New(); } } if (newNumPoints>0) vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()<=0) return nullptr; vtkSmartPointer<vtkPolyData> newPolyData = vtkSmartPointer<vtkPolyData>::New(); newPolyData->SetPoints(vtkNewPoints); newPolyData->SetLines(vtkNewCells); mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(newPolyData); newFib->Compress(0.1); return newFib; } mitk::FiberBundle::Pointer mitk::FiberBundle::ExtractFiberSubset(DataNode* roi, DataStorage* storage) { if (roi==nullptr || !(dynamic_cast<PlanarFigure*>(roi->GetData()) || dynamic_cast<PlanarFigureComposite*>(roi->GetData())) ) return nullptr; std::vector<long> tmp = ExtractFiberIdSubset(roi, storage); if (tmp.size()<=0) return mitk::FiberBundle::New(); vtkSmartPointer<vtkPolyData> pTmp = GeneratePolyDataByIds(tmp); return mitk::FiberBundle::New(pTmp); } std::vector<long> mitk::FiberBundle::ExtractFiberIdSubset(DataNode *roi, DataStorage* storage) { std::vector<long> result; if (roi==nullptr || roi->GetData()==nullptr) return result; mitk::PlanarFigureComposite::Pointer pfc = dynamic_cast<mitk::PlanarFigureComposite*>(roi->GetData()); if (!pfc.IsNull()) // handle composite { DataStorage::SetOfObjects::ConstPointer children = storage->GetDerivations(roi); if (children->size()==0) return result; switch (pfc->getOperationType()) { case 0: // AND { MITK_INFO << "AND"; result = this->ExtractFiberIdSubset(children->ElementAt(0), storage); std::vector<long>::iterator it; for (unsigned int i=1; i<children->Size(); ++i) { std::vector<long> inRoi = this->ExtractFiberIdSubset(children->ElementAt(i), storage); std::vector<long> 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<long>::iterator it; for (unsigned int i=1; i<children->Size(); ++i) { it = result.end(); std::vector<long> 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; i<this->GetNumFibers(); i++) result.push_back(i); std::vector<long>::iterator it; - for (long i=0; i<children->Size(); ++i) + for (unsigned int i=0; i<children->Size(); ++i) { std::vector<long> inRoi = ExtractFiberIdSubset(children->ElementAt(i), storage); std::vector<long> 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<mitk::PlanarFigure*>(roi->GetData()) ) // actual extraction { if ( dynamic_cast<mitk::PlanarPolygon*>(roi->GetData()) ) { mitk::PlanarFigure::Pointer planarPoly = dynamic_cast<mitk::PlanarFigure*>(roi->GetData()); //create vtkPolygon using controlpoints from planarFigure polygon vtkSmartPointer<vtkPolygon> polygonVtk = vtkSmartPointer<vtkPolygon>::New(); for (unsigned int i=0; i<planarPoly->GetNumberOfControlPoints(); ++i) { itk::Point<double,3> p = planarPoly->GetWorldControlPoint(i); vtkIdType id = polygonVtk->GetPoints()->InsertNextPoint(p[0], p[1], p[2] ); polygonVtk->GetPointIds()->InsertNextId(id); } MITK_INFO << "Extracting with polygon"; boost::progress_display disp(m_NumFibers); for (int i=0; i<m_NumFibers; i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints-1; j++) { // Inputs double p1[3] = {0,0,0}; points->GetPoint(j, p1); double p2[3] = {0,0,0}; points->GetPoint(j+1, p2); double tolerance = 0.001; // Outputs double t = 0; // Parametric coordinate of intersection (0 (corresponding to p1) to 1 (corresponding to p2)) double x[3] = {0,0,0}; // The coordinate of the intersection double pcoords[3] = {0,0,0}; int subId = 0; int iD = polygonVtk->IntersectWithLine(p1, p2, tolerance, t, x, pcoords, subId); if (iD!=0) { result.push_back(i); break; } } } } else if ( dynamic_cast<mitk::PlanarCircle*>(roi->GetData()) ) { mitk::PlanarFigure::Pointer planarFigure = dynamic_cast<mitk::PlanarFigure*>(roi->GetData()); Vector3D planeNormal = planarFigure->GetPlaneGeometry()->GetNormal(); planeNormal.Normalize(); //calculate circle radius mitk::Point3D V1w = planarFigure->GetWorldControlPoint(0); //centerPoint mitk::Point3D V2w = planarFigure->GetWorldControlPoint(1); //radiusPoint double radius = V1w.EuclideanDistanceTo(V2w); radius *= radius; MITK_INFO << "Extracting with circle"; boost::progress_display disp(m_NumFibers); for (int i=0; i<m_NumFibers; i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints-1; j++) { // Inputs double p1[3] = {0,0,0}; points->GetPoint(j, p1); double p2[3] = {0,0,0}; points->GetPoint(j+1, p2); // Outputs double t = 0; // Parametric coordinate of intersection (0 (corresponding to p1) to 1 (corresponding to p2)) double x[3] = {0,0,0}; // The coordinate of the intersection int iD = vtkPlane::IntersectWithLine(p1,p2,planeNormal.GetDataPointer(),V1w.GetDataPointer(),t,x); if (iD!=0) { double dist = (x[0]-V1w[0])*(x[0]-V1w[0])+(x[1]-V1w[1])*(x[1]-V1w[1])+(x[2]-V1w[2])*(x[2]-V1w[2]); if( dist <= radius) { result.push_back(i); break; } } } } } return result; } return result; } void mitk::FiberBundle::UpdateFiberGeometry() { vtkSmartPointer<vtkCleanPolyData> cleaner = vtkSmartPointer<vtkCleanPolyData>::New(); cleaner->SetInputData(m_FiberPolyData); cleaner->PointMergingOff(); cleaner->Update(); m_FiberPolyData = cleaner->GetOutput(); m_FiberLengths.clear(); m_MeanFiberLength = 0; m_MedianFiberLength = 0; m_LengthStDev = 0; m_NumFibers = m_FiberPolyData->GetNumberOfCells(); if (m_FiberColors==nullptr || m_FiberColors->GetNumberOfTuples()!=m_FiberPolyData->GetNumberOfPoints()) this->ColorFibersByOrientation(); if (m_FiberWeights->GetSize()!=m_NumFibers) { m_FiberWeights = vtkSmartPointer<vtkFloatArray>::New(); m_FiberWeights->SetName("FIBER_WEIGHTS"); m_FiberWeights->SetNumberOfValues(m_NumFibers); this->SetFiberWeights(1); } if (m_NumFibers<=0) // no fibers present; apply default geometry { m_MinFiberLength = 0; m_MaxFiberLength = 0; mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetImageGeometry(false); float b[] = {0, 1, 0, 1, 0, 1}; geometry->SetFloatBounds(b); SetGeometry(geometry); return; } double b[6]; m_FiberPolyData->GetBounds(b); // calculate statistics for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int p = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); float length = 0; for (int j=0; j<p-1; j++) { double p1[3]; points->GetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); 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 (length<m_MinFiberLength) m_MinFiberLength = length; if (length>m_MaxFiberLength) m_MaxFiberLength = length; } } m_MeanFiberLength /= m_NumFibers; std::vector< float > sortedLengths = m_FiberLengths; std::sort(sortedLengths.begin(), sortedLengths.end()); for (int i=0; i<m_NumFibers; i++) m_LengthStDev += (m_MeanFiberLength-sortedLengths.at(i))*(m_MeanFiberLength-sortedLengths.at(i)); if (m_NumFibers>1) m_LengthStDev /= (m_NumFibers-1); else m_LengthStDev = 0; m_LengthStDev = std::sqrt(m_LengthStDev); m_MedianFiberLength = sortedLengths.at(m_NumFibers/2); mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetFloatBounds(b); this->SetGeometry(geometry); m_UpdateTime3D.Modified(); m_UpdateTime2D.Modified(); } float mitk::FiberBundle::GetFiberWeight(unsigned int fiber) const { return m_FiberWeights->GetValue(fiber); } void mitk::FiberBundle::SetFiberWeights(float newWeight) { for (int i=0; i<m_FiberWeights->GetSize(); i++) m_FiberWeights->SetValue(i, newWeight); } void mitk::FiberBundle::SetFiberWeights(vtkSmartPointer<vtkFloatArray> weights) { if (m_NumFibers!=weights->GetSize()) { MITK_INFO << "Weights array not equal to number of fibers!"; return; } for (int i=0; i<weights->GetSize(); i++) m_FiberWeights->SetValue(i, weights->GetValue(i)); m_FiberWeights->SetName("FIBER_WEIGHTS"); } void mitk::FiberBundle::SetFiberWeight(unsigned int fiber, float weight) { m_FiberWeights->SetValue(fiber, weight); } void mitk::FiberBundle::SetFiberColors(vtkSmartPointer<vtkUnsignedCharArray> fiberColors) { for(long i=0; i<m_FiberPolyData->GetNumberOfPoints(); ++i) { unsigned char source[4] = {0,0,0,0}; fiberColors->GetTupleValue(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->InsertTupleValue(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; 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<float, 3> 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; 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<float, 3> out; out[0] = point[0]; out[1] = point[1]; out[2] = point[2]; return out; } 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; 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<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vnl_vector_fixed< double, 3 > dir; dir[0] = p[0]-center[0]; dir[1] = p[1]-center[1]; dir[2] = p[2]-center[2]; dir = rot*dir; dir[0] += center[0]+tx; dir[1] += center[1]+ty; dir[2] += center[2]+tz; vtkIdType id = vtkNewPoints->InsertNextPoint(dir.data_block()); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::RotateAroundAxis(double x, double y, double z) { x = x*M_PI/180; y = y*M_PI/180; z = z*M_PI/180; vnl_matrix_fixed< double, 3, 3 > rotX; rotX.set_identity(); rotX[1][1] = cos(x); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(x); rotX[2][1] = -rotX[1][2]; vnl_matrix_fixed< double, 3, 3 > rotY; rotY.set_identity(); rotY[0][0] = cos(y); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(y); rotY[2][0] = -rotY[0][2]; vnl_matrix_fixed< double, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(z); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(z); rotZ[1][0] = -rotZ[0][1]; mitk::BaseGeometry::Pointer geom = this->GetGeometry(); mitk::Point3D center = geom->GetCenter(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vnl_vector_fixed< double, 3 > dir; dir[0] = p[0]-center[0]; dir[1] = p[1]-center[1]; dir[2] = p[2]-center[2]; dir = rotZ*rotY*rotX*dir; dir[0] += center[0]; dir[1] += center[1]; dir[2] += center[2]; vtkIdType id = vtkNewPoints->InsertNextPoint(dir.data_block()); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); 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<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (int i=0; i<m_NumFibers; i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); if (subtractCenter) { p[0] -= c[0]; p[1] -= c[1]; p[2] -= c[2]; } p[0] *= x; p[1] *= y; p[2] *= z; if (subtractCenter) { p[0] += c[0]; p[1] += c[1]; p[2] += c[2]; } vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::TranslateFibers(double x, double y, double z) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); p[0] += x; p[1] += y; p[2] += z; vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::MirrorFibers(unsigned int axis) { if (axis>2) return; MITK_INFO << "Mirroring fibers"; boost::progress_display disp(m_NumFibers); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); for (int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); p[axis] = -p[axis]; vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::RemoveDir(vnl_vector_fixed<double,3> dir, double threshold) { dir.normalize(); vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); bool discard = false; for (int j=0; j<numPoints-1; j++) { double p1[3]; points->GetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); vnl_vector_fixed< double, 3 > v1; v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; if (v1.magnitude()>0.001) { v1.normalize(); if (fabs(dot_product(v1,dir))>threshold) { discard = true; break; } } } if (!discard) { for (int j=0; j<numPoints; j++) { double p1[3]; points->GetPoint(j, p1); vtkIdType id = vtkNewPoints->InsertNextPoint(p1); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } } m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); // UpdateColorCoding(); // UpdateFiberGeometry(); } bool mitk::FiberBundle::ApplyCurvatureThreshold(float minRadius, bool deleteFibers) { if (minRadius<0) return true; vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Applying curvature threshold"; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { ++disp ; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); // calculate curvatures vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints-2; j++) { double p1[3]; points->GetPoint(j, p1); double p2[3]; points->GetPoint(j+1, p2); double p3[3]; points->GetPoint(j+2, p3); vnl_vector_fixed< float, 3 > v1, v2, v3; v1[0] = 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 && r<minRadius) break; if (r<minRadius) { j += 2; vtkNewCells->InsertNextCell(container); container = vtkSmartPointer<vtkPolyLine>::New(); } else if (j==numPoints-3) { id = vtkNewPoints->InsertNextPoint(p2); container->GetPointIds()->InsertNextId(id); id = vtkNewPoints->InsertNextPoint(p3); container->GetPointIds()->InsertNextId(id); vtkNewCells->InsertNextCell(container); } } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); return true; } bool mitk::FiberBundle::RemoveShortFibers(float lengthInMM) { MITK_INFO << "Removing short fibers"; if (lengthInMM<=0 || lengthInMM<m_MinFiberLength) { MITK_INFO << "No fibers shorter than " << lengthInMM << " mm found!"; return true; } if (lengthInMM>m_MaxFiberLength) // can't remove all fibers { MITK_WARN << "Process aborted. No fibers would be left!"; return false; } vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); float min = m_MaxFiberLength; boost::progress_display disp(m_NumFibers); for (int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (m_FiberLengths.at(i)>=lengthInMM) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); if (m_FiberLengths.at(i)<min) min = m_FiberLengths.at(i); } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); return true; } bool mitk::FiberBundle::RemoveLongFibers(float lengthInMM) { if (lengthInMM<=0 || lengthInMM>m_MaxFiberLength) return true; if (lengthInMM<m_MinFiberLength) // can't remove all fibers return false; vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Removing long fibers"; boost::progress_display disp(m_NumFibers); for (int i=0; i<m_NumFibers; i++) { ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (m_FiberLengths.at(i)<=lengthInMM) { vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); for (int j=0; j<numPoints; j++) { double* p = points->GetPoint(j); vtkIdType id = vtkNewPoints->InsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()<=0) return false; m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); return true; } void mitk::FiberBundle::ResampleSpline(float pointDistance, double tension, double continuity, double bias ) { if (pointDistance<=0) return; vtkSmartPointer<vtkPoints> vtkSmoothPoints = vtkSmartPointer<vtkPoints>::New(); //in smoothpoints the interpolated points representing a fiber are stored. //in vtkcells all polylines are stored, actually all id's of them are stored vtkSmartPointer<vtkCellArray> vtkSmoothCells = vtkSmartPointer<vtkCellArray>::New(); //cellcontainer for smoothed lines vtkIdType pointHelperCnt = 0; MITK_INFO << "Smoothing fibers"; vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); boost::progress_display disp(m_NumFibers); #pragma omp parallel for for (int i=0; i<m_NumFibers; i++) { vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New(); float length = 0; float weight = 1; #pragma omp critical { length = m_FiberLengths.at(i); weight = m_FiberWeights->GetValue(i); ++disp; vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) newPoints->InsertNextPoint(points->GetPoint(j)); } int sampling = std::ceil(length/pointDistance); vtkSmartPointer<vtkKochanekSpline> xSpline = vtkSmartPointer<vtkKochanekSpline>::New(); vtkSmartPointer<vtkKochanekSpline> ySpline = vtkSmartPointer<vtkKochanekSpline>::New(); vtkSmartPointer<vtkKochanekSpline> zSpline = vtkSmartPointer<vtkKochanekSpline>::New(); xSpline->SetDefaultBias(bias); xSpline->SetDefaultTension(tension); xSpline->SetDefaultContinuity(continuity); ySpline->SetDefaultBias(bias); ySpline->SetDefaultTension(tension); ySpline->SetDefaultContinuity(continuity); zSpline->SetDefaultBias(bias); zSpline->SetDefaultTension(tension); zSpline->SetDefaultContinuity(continuity); vtkSmartPointer<vtkParametricSpline> spline = vtkSmartPointer<vtkParametricSpline>::New(); spline->SetXSpline(xSpline); spline->SetYSpline(ySpline); spline->SetZSpline(zSpline); spline->SetPoints(newPoints); vtkSmartPointer<vtkParametricFunctionSource> functionSource = vtkSmartPointer<vtkParametricFunctionSource>::New(); functionSource->SetParametricFunction(spline); functionSource->SetUResolution(sampling); functionSource->SetVResolution(sampling); functionSource->SetWResolution(sampling); functionSource->Update(); vtkPolyData* outputFunction = functionSource->GetOutput(); vtkPoints* tmpSmoothPnts = outputFunction->GetPoints(); //smoothPoints of current fiber vtkSmartPointer<vtkPolyLine> smoothLine = vtkSmartPointer<vtkPolyLine>::New(); smoothLine->GetPointIds()->SetNumberOfIds(tmpSmoothPnts->GetNumberOfPoints()); #pragma omp critical { for (int j=0; j<smoothLine->GetNumberOfPoints(); j++) { smoothLine->GetPointIds()->SetId(j, j+pointHelperCnt); vtkSmoothPoints->InsertNextPoint(tmpSmoothPnts->GetPoint(j)); } newFiberWeights->SetValue(vtkSmoothCells->GetNumberOfCells(), weight); vtkSmoothCells->InsertNextCell(smoothLine); pointHelperCnt += tmpSmoothPnts->GetNumberOfPoints(); } } SetFiberWeights(newFiberWeights); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkSmoothPoints); m_FiberPolyData->SetLines(vtkSmoothCells); this->SetFiberPolyData(m_FiberPolyData, true); } void mitk::FiberBundle::ResampleSpline(float pointDistance) { ResampleSpline(pointDistance, 0, 0, 0 ); } unsigned long mitk::FiberBundle::GetNumberOfPoints() const { unsigned long points = 0; for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); points += cell->GetNumberOfPoints(); } return points; } void mitk::FiberBundle::Compress(float error) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Compressing fibers"; unsigned long numRemovedPoints = 0; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); #pragma omp parallel for for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; float weight = 1; #pragma omp critical { ++disp; weight = m_FiberWeights->GetValue(i); vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) { double cand[3]; points->GetPoint(j, cand); vnl_vector_fixed< double, 3 > candV; candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; vertices.push_back(candV); } } // calculate curvatures int numPoints = vertices.size(); std::vector< int > removedPoints; removedPoints.resize(numPoints, 0); removedPoints[0]=-1; removedPoints[numPoints-1]=-1; vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); int remCounter = 0; bool pointFound = true; while (pointFound) { pointFound = false; double minError = error; int removeIndex = -1; for (int j=0; j<vertices.size(); j++) { if (removedPoints[j]==0) { vnl_vector_fixed< double, 3 > candV = vertices.at(j); int validP = -1; vnl_vector_fixed< double, 3 > pred; for (int k=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<numPoints; k++) if (removedPoints[k]<=0) { succ = vertices.at(k); validS = k; break; } if (validP>=0 && validS>=0) { double a = (candV-pred).magnitude(); double b = (candV-succ).magnitude(); double c = (pred-succ).magnitude(); double s=0.5*(a+b+c); double hc=(2.0/c)*sqrt(fabs(s*(s-a)*(s-b)*(s-c))); if (hc<minError) { removeIndex = j; minError = hc; pointFound = true; } } } } if (pointFound) { removedPoints[removeIndex] = 1; remCounter++; } } for (int j=0; j<numPoints; j++) { if (removedPoints[j]<=0) { #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(vertices.at(j).data_block()); container->GetPointIds()->InsertNextId(id); } } } #pragma omp critical { newFiberWeights->SetValue(vtkNewCells->GetNumberOfCells(), weight); numRemovedPoints += remCounter; vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()>0) { MITK_INFO << "Removed points: " << numRemovedPoints; SetFiberWeights(newFiberWeights); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } } void mitk::FiberBundle::ResampleLinear(double pointDistance) { vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> vtkNewCells = vtkSmartPointer<vtkCellArray>::New(); MITK_INFO << "Resampling fibers (linear)"; boost::progress_display disp(m_FiberPolyData->GetNumberOfCells()); vtkSmartPointer<vtkFloatArray> newFiberWeights = vtkSmartPointer<vtkFloatArray>::New(); newFiberWeights->SetName("FIBER_WEIGHTS"); newFiberWeights->SetNumberOfValues(m_NumFibers); #pragma omp parallel for for (int i=0; i<m_FiberPolyData->GetNumberOfCells(); i++) { std::vector< vnl_vector_fixed< double, 3 > > vertices; float weight = 1; #pragma omp critical { ++disp; weight = m_FiberWeights->GetValue(i); vtkCell* cell = m_FiberPolyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; j<numPoints; j++) { double cand[3]; points->GetPoint(j, cand); vnl_vector_fixed< double, 3 > candV; candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; vertices.push_back(candV); } } vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New(); vnl_vector_fixed< double, 3 > lastV = vertices.at(0); #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(lastV.data_block()); container->GetPointIds()->InsertNextId(id); } for (unsigned int j=1; j<vertices.size(); j++) { vnl_vector_fixed< double, 3 > vec = vertices.at(j) - lastV; double new_dist = vec.magnitude(); if (new_dist >= pointDistance) { vnl_vector_fixed< double, 3 > newV = lastV; if ( new_dist-pointDistance <= mitk::eps ) { vec.normalize(); newV += vec * pointDistance; } else { // intersection between sphere (radius 'pointDistance', center 'lastV') and line (direction 'd' and point 'p') vnl_vector_fixed< double, 3 > p = vertices.at(j-1); vnl_vector_fixed< double, 3 > d = vertices.at(j) - p; double a = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; double b = 2 * (d[0] * (p[0] - lastV[0]) + d[1] * (p[1] - lastV[1]) + d[2] * (p[2] - lastV[2])); double c = (p[0] - lastV[0])*(p[0] - lastV[0]) + (p[1] - lastV[1])*(p[1] - lastV[1]) + (p[2] - lastV[2])*(p[2] - lastV[2]) - pointDistance*pointDistance; double v1 =(-b + std::sqrt(b*b-4*a*c))/(2*a); double v2 =(-b - std::sqrt(b*b-4*a*c))/(2*a); if (v1>0) newV = p + d * v1; else if (v2>0) newV = p + d * v2; else MITK_INFO << "ERROR1 - linear resampling"; j--; } #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(newV.data_block()); container->GetPointIds()->InsertNextId(id); } lastV = newV; } else if (j==vertices.size()-1 && new_dist>0.0001) { #pragma omp critical { vtkIdType id = vtkNewPoints->InsertNextPoint(vertices.at(j).data_block()); container->GetPointIds()->InsertNextId(id); } } } #pragma omp critical { newFiberWeights->SetValue(vtkNewCells->GetNumberOfCells(), weight); vtkNewCells->InsertNextCell(container); } } if (vtkNewCells->GetNumberOfCells()>0) { SetFiberWeights(newFiberWeights); m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New(); m_FiberPolyData->SetPoints(vtkNewPoints); m_FiberPolyData->SetLines(vtkNewCells); this->SetFiberPolyData(m_FiberPolyData, true); } } // reapply selected colorcoding in case PolyData structure has changed bool mitk::FiberBundle::Equals(mitk::FiberBundle* fib, double eps) { if (fib==nullptr) { MITK_INFO << "Reference bundle is nullptr!"; return false; } if (m_NumFibers!=fib->GetNumFibers()) { MITK_INFO << "Unequal number of fibers!"; MITK_INFO << m_NumFibers << " vs. " << fib->GetNumFibers(); return false; } for (int i=0; i<m_NumFibers; i++) { vtkCell* cell = m_FiberPolyData->GetCell(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; j<numPoints; j++) { double* p1 = points->GetPoint(j); double* p2 = points2->GetPoint(j); if (fabs(p1[0]-p2[0])>eps || fabs(p1[1]-p2[1])>eps || fabs(p1[2]-p2[2])>eps) { MITK_INFO << "Unequal points in fiber " << i << " at position " << j << "!"; MITK_INFO << "p1: " << p1[0] << ", " << p1[1] << ", " << p1[2]; MITK_INFO << "p2: " << p2[0] << ", " << p2[1] << ", " << p2[2]; return false; } } } return true; } /* 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 d3f987ee0a..a45b9c8238 100644 --- a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp @@ -1,871 +1,867 @@ /*=================================================================== 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 <boost/foreach.hpp> #include <boost/lexical_cast.hpp> #include <itkImageFileWriter.h> #include <itkImageFileReader.h> #include <mitkLog.h> #include <algorithm> template< class ScalarType > mitk::FiberfoxParameters< ScalarType >::FiberfoxParameters() : m_NoiseModel(nullptr) { } template< class ScalarType > mitk::FiberfoxParameters< ScalarType >::~FiberfoxParameters() { // if (m_NoiseModel!=nullptr) // delete m_NoiseModel; } void mitk::SignalGenerationParameters::GenerateGradientHalfShell() { int NPoints = 2*m_NumGradients; m_GradientDirections.clear(); m_NumBaseline = NPoints/20; if (m_NumBaseline==0) m_NumBaseline=1; GradientType g; g.Fill(0.0); for (unsigned int i=0; i<m_NumBaseline; i++) m_GradientDirections.push_back(g); if (NPoints==0) return; vnl_vector<double> theta; theta.set_size(NPoints); vnl_vector<double> phi; phi.set_size(NPoints); double C = sqrt(4*M_PI); phi(0) = 0.0; phi(NPoints-1) = 0.0; for(int i=0; i<NPoints; i++) { theta(i) = acos(-1.0+2.0*i/(NPoints-1.0)) - M_PI / 2.0; if( i>0 && i<NPoints-1) { phi(i) = (phi(i-1) + C / sqrt(NPoints*(1-(-1.0+2.0*i/(NPoints-1.0))*(-1.0+2.0*i/(NPoints-1.0))))); } } for(int i=0; i<NPoints; i++) { g[2] = sin(theta(i)); if (g[2]<0) continue; g[0] = cos(theta(i)) * cos(phi(i)); g[1] = cos(theta(i)) * sin(phi(i)); m_GradientDirections.push_back(g); } } std::vector< int > mitk::SignalGenerationParameters::GetBaselineIndices() { std::vector< int > result; for( unsigned int i=0; i<this->m_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; i<this->m_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; } void mitk::SignalGenerationParameters::SetGradienDirections(GradientListType gradientList) { m_GradientDirections = gradientList; m_NumGradients = 0; m_NumBaseline = 0; for( unsigned int i=0; i<this->m_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; i<gradientList->Size(); 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++; } } template< class ScalarType > void mitk::FiberfoxParameters< ScalarType >::SaveParameters(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; i<this->m_SignalGen.GetNumVolumes(); i++) { parameters.put("fiberfox.image.gradients."+boost::lexical_cast<string>(i)+".x", m_SignalGen.GetGradientDirection(i)[0]); parameters.put("fiberfox.image.gradients."+boost::lexical_cast<string>(i)+".y", m_SignalGen.GetGradientDirection(i)[1]); parameters.put("fiberfox.image.gradients."+boost::lexical_cast<string>(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<mitk::RicianNoiseModel<ScalarType>*>(m_NoiseModel.get())) parameters.put("fiberfox.image.artifacts.noisetype", "rice"); else if (dynamic_cast<mitk::ChiSquareNoiseModel<ScalarType>*>(m_NoiseModel.get())) parameters.put("fiberfox.image.artifacts.noisetype", "chisquare"); } for (int i=0; i<m_FiberModelList.size()+m_NonFiberModelList.size(); i++) { mitk::DiffusionSignalModel<ScalarType>* signalModel = nullptr; if (i<m_FiberModelList.size()) { signalModel = m_FiberModelList.at(i); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".type", "fiber"); } else { signalModel = m_NonFiberModelList.at(i-m_FiberModelList.size()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".type", "non-fiber"); } if (dynamic_cast<mitk::StickModel<ScalarType>*>(signalModel)) { mitk::StickModel<ScalarType>* model = dynamic_cast<mitk::StickModel<ScalarType>*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".model", "stick"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t1", model->GetT1()); } else if (dynamic_cast<mitk::TensorModel<ScalarType>*>(signalModel)) { mitk::TensorModel<ScalarType>* model = dynamic_cast<mitk::TensorModel<ScalarType>*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".model", "tensor"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".d1", model->GetDiffusivity1()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".d2", model->GetDiffusivity2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".d3", model->GetDiffusivity3()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t1", model->GetT1()); } else if (dynamic_cast<mitk::RawShModel<ScalarType>*>(signalModel)) { mitk::RawShModel<ScalarType>* model = dynamic_cast<mitk::RawShModel<ScalarType>*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".model", "prototype"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".minFA", model->GetFaRange().first); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".maxFA", model->GetFaRange().second); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".minADC", model->GetAdcRange().first); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".maxADC", model->GetAdcRange().second); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".maxNumSamples", model->GetMaxNumKernels()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".numSamples", model->GetNumberOfKernels()); int shOrder = model->GetShOrder(); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".numCoeffs", (shOrder*shOrder + shOrder + 2)/2 + shOrder); for (unsigned int j=0; j<model->GetNumberOfKernels(); j++) { vnl_vector< double > coeffs = model->GetCoefficients(j); for (unsigned int k=0; k<coeffs.size(); k++) parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".kernels."+boost::lexical_cast<string>(j)+".coeffs."+boost::lexical_cast<string>(k), coeffs[k]); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".kernels."+boost::lexical_cast<string>(j)+".B0", model->GetBaselineSignal(j)); } } else if (dynamic_cast<mitk::BallModel<ScalarType>*>(signalModel)) { mitk::BallModel<ScalarType>* model = dynamic_cast<mitk::BallModel<ScalarType>*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".model", "ball"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t1", model->GetT1()); } else if (dynamic_cast<mitk::AstroStickModel<ScalarType>*>(signalModel)) { mitk::AstroStickModel<ScalarType>* model = dynamic_cast<mitk::AstroStickModel<ScalarType>*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".model", "astrosticks"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t1", model->GetT1()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".randomize", model->GetRandomizeSticks()); } else if (dynamic_cast<mitk::DotModel<ScalarType>*>(signalModel)) { mitk::DotModel<ScalarType>* model = dynamic_cast<mitk::DotModel<ScalarType>*>(signalModel); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".model", "dot"); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".t1", model->GetT1()); } if (signalModel!=nullptr) { parameters.put("fiberfox.image.compartments."+boost::lexical_cast<string>(i)+".ID", signalModel->m_CompartmentId); if (signalModel->GetVolumeFractionImage().IsNotNull()) { try{ itk::ImageFileWriter<ItkDoubleImgType>::Pointer writer = itk::ImageFileWriter<ItkDoubleImgType>::New(); writer->SetFileName(filename+"_VOLUME"+boost::lexical_cast<string>(signalModel->m_CompartmentId)+".nrrd"); writer->SetInput(signalModel->GetVolumeFractionImage()); writer->Update(); MITK_INFO << "Volume fraction image for compartment "+boost::lexical_cast<string>(signalModel->m_CompartmentId)+" saved."; } catch(...) { } } } } boost::property_tree::xml_writer_settings<std::string> writerSettings(' ', 2); boost::property_tree::xml_parser::write_xml(filename, parameters, std::locale(), writerSettings); try{ itk::ImageFileWriter<ItkDoubleImgType>::Pointer writer = itk::ImageFileWriter<ItkDoubleImgType>::New(); writer->SetFileName(filename+"_FMAP.nrrd"); writer->SetInput(m_SignalGen.m_FrequencyMap); writer->Update(); } catch(...) { MITK_INFO << "No frequency map saved."; } try{ itk::ImageFileWriter<ItkUcharImgType>::Pointer writer = itk::ImageFileWriter<ItkUcharImgType>::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 ScalarType > template< class ParameterType > ParameterType mitk::FiberfoxParameters< ScalarType >::ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential) { try { return v.second.get<ParameterType>(tag); } catch (...) { if (essential) { mitkThrow() << "Parameter file corrupted. Essential tag is missing: '" << tag << "'"; } if (tag!="artifacts.noisetype") { MITK_INFO << "Tag '" << tag << "' not found. Using default value '" << defaultValue << "'."; m_MissingTags += "\n- "; m_MissingTags += tag; } return defaultValue; } } template< class ScalarType > void mitk::FiberfoxParameters< ScalarType >::LoadParameters(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<bool>(v1,"realtime", m_Misc.m_CheckRealTimeFibersBox); m_Misc.m_CheckAdvancedFiberOptionsBox = ReadVal<bool>(v1,"showadvanced", m_Misc.m_CheckAdvancedFiberOptionsBox); m_Misc.m_CheckConstantRadiusBox = ReadVal<bool>(v1,"constantradius", m_Misc.m_CheckConstantRadiusBox); m_Misc.m_CheckIncludeFiducialsBox = ReadVal<bool>(v1,"includeFiducials", m_Misc.m_CheckIncludeFiducialsBox); switch (ReadVal<unsigned int>(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<double>(v1,"variance", m_FiberGen.m_Variance); m_FiberGen.m_Density = ReadVal<unsigned int>(v1,"density", m_FiberGen.m_Density); m_FiberGen.m_Sampling = ReadVal<double>(v1,"spline.sampling", m_FiberGen.m_Sampling); m_FiberGen.m_Tension = ReadVal<double>(v1,"spline.tension", m_FiberGen.m_Tension); m_FiberGen.m_Continuity = ReadVal<double>(v1,"spline.continuity", m_FiberGen.m_Continuity); m_FiberGen.m_Bias = ReadVal<double>(v1,"spline.bias", m_FiberGen.m_Bias); m_FiberGen.m_Rotation[0] = ReadVal<double>(v1,"rotation.x", m_FiberGen.m_Rotation[0]); m_FiberGen.m_Rotation[1] = ReadVal<double>(v1,"rotation.y", m_FiberGen.m_Rotation[1]); m_FiberGen.m_Rotation[2] = ReadVal<double>(v1,"rotation.z", m_FiberGen.m_Rotation[2]); m_FiberGen.m_Translation[0] = ReadVal<double>(v1,"translation.x", m_FiberGen.m_Translation[0]); m_FiberGen.m_Translation[1] = ReadVal<double>(v1,"translation.y", m_FiberGen.m_Translation[1]); m_FiberGen.m_Translation[2] = ReadVal<double>(v1,"translation.z", m_FiberGen.m_Translation[2]); m_FiberGen.m_Scale[0] = ReadVal<double>(v1,"scale.x", m_FiberGen.m_Scale[0]); m_FiberGen.m_Scale[1] = ReadVal<double>(v1,"scale.y", m_FiberGen.m_Scale[1]); m_FiberGen.m_Scale[2] = ReadVal<double>(v1,"scale.z", m_FiberGen.m_Scale[2]); } else if ( v1.first == "image" ) { m_Misc.m_SignalModelString = ReadVal<string>(v1,"signalmodelstring", m_Misc.m_SignalModelString); m_Misc.m_ArtifactModelString = ReadVal<string>(v1,"artifactmodelstring", m_Misc.m_ArtifactModelString); m_Misc.m_OutputPath = ReadVal<string>(v1,"outpath", m_Misc.m_OutputPath); m_Misc.m_CheckOutputVolumeFractionsBox = ReadVal<bool>(v1,"outputvolumefractions", m_Misc.m_CheckOutputVolumeFractionsBox); m_Misc.m_CheckAdvancedSignalOptionsBox = ReadVal<bool>(v1,"showadvanced", m_Misc.m_CheckAdvancedSignalOptionsBox); m_Misc.m_CheckAddDistortionsBox = ReadVal<bool>(v1,"artifacts.doAddDistortions", m_Misc.m_CheckAddDistortionsBox); m_Misc.m_CheckAddNoiseBox = ReadVal<bool>(v1,"artifacts.addnoise", m_Misc.m_CheckAddNoiseBox); m_Misc.m_CheckAddGhostsBox = ReadVal<bool>(v1,"artifacts.addghosts", m_Misc.m_CheckAddGhostsBox); m_Misc.m_CheckAddAliasingBox = ReadVal<bool>(v1,"artifacts.addaliasing", m_Misc.m_CheckAddAliasingBox); m_Misc.m_CheckAddSpikesBox = ReadVal<bool>(v1,"artifacts.addspikes", m_Misc.m_CheckAddSpikesBox); m_Misc.m_CheckAddEddyCurrentsBox = ReadVal<bool>(v1,"artifacts.addeddycurrents", m_Misc.m_CheckAddEddyCurrentsBox); m_SignalGen.m_ImageRegion.SetSize(0, ReadVal<int>(v1,"basic.size.x",m_SignalGen.m_ImageRegion.GetSize(0))); m_SignalGen.m_ImageRegion.SetSize(1, ReadVal<int>(v1,"basic.size.y",m_SignalGen.m_ImageRegion.GetSize(1))); m_SignalGen.m_ImageRegion.SetSize(2, ReadVal<int>(v1,"basic.size.z",m_SignalGen.m_ImageRegion.GetSize(2))); m_SignalGen.m_ImageSpacing[0] = ReadVal<double>(v1,"basic.spacing.x",m_SignalGen.m_ImageSpacing[0]); m_SignalGen.m_ImageSpacing[1] = ReadVal<double>(v1,"basic.spacing.y",m_SignalGen.m_ImageSpacing[1]); m_SignalGen.m_ImageSpacing[2] = ReadVal<double>(v1,"basic.spacing.z",m_SignalGen.m_ImageSpacing[2]); m_SignalGen.m_ImageOrigin[0] = ReadVal<double>(v1,"basic.origin.x",m_SignalGen.m_ImageOrigin[0]); m_SignalGen.m_ImageOrigin[1] = ReadVal<double>(v1,"basic.origin.y",m_SignalGen.m_ImageOrigin[1]); m_SignalGen.m_ImageOrigin[2] = ReadVal<double>(v1,"basic.origin.z",m_SignalGen.m_ImageOrigin[2]); m_SignalGen.m_ImageDirection[0][0] = ReadVal<double>(v1,"basic.direction.1",m_SignalGen.m_ImageDirection[0][0]); m_SignalGen.m_ImageDirection[0][1] = ReadVal<double>(v1,"basic.direction.2",m_SignalGen.m_ImageDirection[0][1]); m_SignalGen.m_ImageDirection[0][2] = ReadVal<double>(v1,"basic.direction.3",m_SignalGen.m_ImageDirection[0][2]); m_SignalGen.m_ImageDirection[1][0] = ReadVal<double>(v1,"basic.direction.4",m_SignalGen.m_ImageDirection[1][0]); m_SignalGen.m_ImageDirection[1][1] = ReadVal<double>(v1,"basic.direction.5",m_SignalGen.m_ImageDirection[1][1]); m_SignalGen.m_ImageDirection[1][2] = ReadVal<double>(v1,"basic.direction.6",m_SignalGen.m_ImageDirection[1][2]); m_SignalGen.m_ImageDirection[2][0] = ReadVal<double>(v1,"basic.direction.7",m_SignalGen.m_ImageDirection[2][0]); m_SignalGen.m_ImageDirection[2][1] = ReadVal<double>(v1,"basic.direction.8",m_SignalGen.m_ImageDirection[2][1]); m_SignalGen.m_ImageDirection[2][2] = ReadVal<double>(v1,"basic.direction.9",m_SignalGen.m_ImageDirection[2][2]); m_SignalGen.m_AcquisitionType = (SignalGenerationParameters::AcquisitionType) ReadVal<int>(v1,"acquisitiontype", m_SignalGen.m_AcquisitionType); m_SignalGen.m_CoilSensitivityProfile = (SignalGenerationParameters::CoilSensitivityProfile) ReadVal<int>(v1,"coilsensitivityprofile", m_SignalGen.m_CoilSensitivityProfile); m_SignalGen.m_NumberOfCoils = ReadVal<int>(v1,"numberofcoils", m_SignalGen.m_NumberOfCoils); m_SignalGen.m_ReversePhase = ReadVal<bool>(v1,"reversephase", m_SignalGen.m_ReversePhase); m_SignalGen.m_PartialFourier = ReadVal<double>(v1,"partialfourier", m_SignalGen.m_PartialFourier); m_SignalGen.m_NoiseVariance = ReadVal<double>(v1,"noisevariance", m_SignalGen.m_NoiseVariance); m_SignalGen.m_tRep = ReadVal<double>(v1,"trep", m_SignalGen.m_tRep); m_SignalGen.m_SignalScale = ReadVal<double>(v1,"signalScale", m_SignalGen.m_SignalScale); m_SignalGen.m_tEcho = ReadVal<double>(v1,"tEcho", m_SignalGen.m_tEcho); m_SignalGen.m_tLine = ReadVal<double>(v1,"tLine", m_SignalGen.m_tLine); m_SignalGen.m_tInhom = ReadVal<double>(v1,"tInhom", m_SignalGen.m_tInhom); m_SignalGen.m_Bvalue = ReadVal<double>(v1,"bvalue", m_SignalGen.m_Bvalue); m_SignalGen.m_SimulateKspaceAcquisition = ReadVal<bool>(v1,"simulatekspace", m_SignalGen.m_SimulateKspaceAcquisition); m_SignalGen.m_AxonRadius = ReadVal<double>(v1,"axonRadius", m_SignalGen.m_AxonRadius); m_SignalGen.m_Spikes = ReadVal<unsigned int>(v1,"artifacts.spikesnum", m_SignalGen.m_Spikes); m_SignalGen.m_SpikeAmplitude = ReadVal<double>(v1,"artifacts.spikesscale", m_SignalGen.m_SpikeAmplitude); m_SignalGen.m_KspaceLineOffset = ReadVal<double>(v1,"artifacts.kspaceLineOffset", m_SignalGen.m_KspaceLineOffset); m_SignalGen.m_EddyStrength = ReadVal<double>(v1,"artifacts.eddyStrength", m_SignalGen.m_EddyStrength); m_SignalGen.m_Tau = ReadVal<double>(v1,"artifacts.eddyTau", m_SignalGen.m_Tau); m_SignalGen.m_CroppingFactor = ReadVal<double>(v1,"artifacts.aliasingfactor", m_SignalGen.m_CroppingFactor); m_SignalGen.m_DoAddGibbsRinging = ReadVal<bool>(v1,"artifacts.addringing", m_SignalGen.m_DoAddGibbsRinging); m_SignalGen.m_DoSimulateRelaxation = ReadVal<bool>(v1,"doSimulateRelaxation", m_SignalGen.m_DoSimulateRelaxation); m_SignalGen.m_DoDisablePartialVolume = ReadVal<bool>(v1,"doDisablePartialVolume", m_SignalGen.m_DoDisablePartialVolume); m_SignalGen.m_DoAddMotion = ReadVal<bool>(v1,"artifacts.doAddMotion", m_SignalGen.m_DoAddMotion); m_SignalGen.m_DoRandomizeMotion = ReadVal<bool>(v1,"artifacts.randomMotion", m_SignalGen.m_DoRandomizeMotion); m_SignalGen.m_Translation[0] = ReadVal<double>(v1,"artifacts.translation0", m_SignalGen.m_Translation[0]); m_SignalGen.m_Translation[1] = ReadVal<double>(v1,"artifacts.translation1", m_SignalGen.m_Translation[1]); m_SignalGen.m_Translation[2] = ReadVal<double>(v1,"artifacts.translation2", m_SignalGen.m_Translation[2]); m_SignalGen.m_Rotation[0] = ReadVal<double>(v1,"artifacts.rotation0", m_SignalGen.m_Rotation[0]); m_SignalGen.m_Rotation[1] = ReadVal<double>(v1,"artifacts.rotation1", m_SignalGen.m_Rotation[1]); m_SignalGen.m_Rotation[2] = ReadVal<double>(v1,"artifacts.rotation2", m_SignalGen.m_Rotation[2]); // m_SignalGen.SetNumWeightedVolumes(ReadVal<unsigned int>(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<double>(v2,"x",0); g[1] = ReadVal<double>(v2,"y",0); g[2] = ReadVal<double>(v2,"z",0); gradients.push_back(g); } m_SignalGen.SetGradienDirections(gradients); m_Misc.m_MotionVolumesBox = ReadVal<string>(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() ) { stringstream stream( m_Misc.m_MotionVolumesBox ); std::vector<int> numbers; int nummer = std::numeric_limits<int>::max(); while( stream >> nummer ) { if( nummer < std::numeric_limits<int>::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(); ++i ) { m_SignalGen.m_MotionVolumes.push_back( true ); } // set all true except those given. - for( auto iter = std::begin( numbers ); iter != std::end( numbers ); ++iter ) + for (auto number : numbers) { - if ( -(*iter) < m_SignalGen.GetNumVolumes() && -(*iter) >= 0 ) - { - m_SignalGen.m_MotionVolumes.at( -(*iter) ) = false; - } + if (-number < static_cast<int>(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(); ++i ) { m_SignalGen.m_MotionVolumes.push_back( false ); } // set all false except those given. - for( auto iter = std::begin( numbers ); iter != std::end( numbers ); ++iter ) + for (auto number : numbers) { - if ( *iter < m_SignalGen.GetNumVolumes() && *iter >= 0 ) - { - m_SignalGen.m_MotionVolumes.at( *iter ) = true; - } + if (number < static_cast<int>(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<string>(v1,"artifacts.noisetype","")=="rice") { m_NoiseModel = std::make_shared< mitk::RicianNoiseModel<ScalarType> >(); m_NoiseModel->SetNoiseVariance(ReadVal<double>(v1,"artifacts.noisevariance",m_NoiseModel->GetNoiseVariance())); } } catch(...) { MITK_DEBUG << "mitkFiberfoxParameters.cpp: caught some error while trying m_NoiseModel->SetNoiseVariance()"; // throw; } try { if (ReadVal<string>(v1,"artifacts.noisetype","")=="chisquare") { m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel<ScalarType> >(); m_NoiseModel->SetNoiseVariance(ReadVal<double>(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<ScalarType>* signalModel = nullptr; std::string model = ReadVal<std::string>(v2,"model","",true); if (model=="stick") { mitk::StickModel<ScalarType>* model = new mitk::StickModel<ScalarType>(); model->SetDiffusivity(ReadVal<double>(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal<double>(v2,"t2",model->GetT2())); model->SetT1(ReadVal<double>(v2,"t1",model->GetT1())); model->m_CompartmentId = ReadVal<unsigned int>(v2,"ID",0,true); if (ReadVal<string>(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal<string>(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="tensor") { mitk::TensorModel<ScalarType>* model = new mitk::TensorModel<ScalarType>(); model->SetDiffusivity1(ReadVal<double>(v2,"d1",model->GetDiffusivity1())); model->SetDiffusivity2(ReadVal<double>(v2,"d2",model->GetDiffusivity2())); model->SetDiffusivity3(ReadVal<double>(v2,"d3",model->GetDiffusivity3())); model->SetT2(ReadVal<double>(v2,"t2",model->GetT2())); model->SetT1(ReadVal<double>(v2,"t1",model->GetT1())); model->m_CompartmentId = ReadVal<unsigned int>(v2,"ID",0,true); if (ReadVal<string>(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal<string>(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="ball") { mitk::BallModel<ScalarType>* model = new mitk::BallModel<ScalarType>(); model->SetDiffusivity(ReadVal<double>(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal<double>(v2,"t2",model->GetT2())); model->SetT1(ReadVal<double>(v2,"t1",model->GetT1())); model->m_CompartmentId = ReadVal<unsigned int>(v2,"ID",0,true); if (ReadVal<string>(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal<string>(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="astrosticks") { mitk::AstroStickModel<ScalarType>* model = new AstroStickModel<ScalarType>(); model->SetDiffusivity(ReadVal<double>(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal<double>(v2,"t2",model->GetT2())); model->SetT1(ReadVal<double>(v2,"t1",model->GetT1())); model->SetRandomizeSticks(ReadVal<bool>(v2,"randomize",model->GetRandomizeSticks())); model->m_CompartmentId = ReadVal<unsigned int>(v2,"ID",0,true); if (ReadVal<string>(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal<string>(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="dot") { mitk::DotModel<ScalarType>* model = new mitk::DotModel<ScalarType>(); model->SetT2(ReadVal<double>(v2,"t2",model->GetT2())); model->SetT1(ReadVal<double>(v2,"t1",model->GetT1())); model->m_CompartmentId = ReadVal<unsigned int>(v2,"ID",0,true); if (ReadVal<string>(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal<string>(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="prototype") { mitk::RawShModel<ScalarType>* model = new mitk::RawShModel<ScalarType>(); model->SetMaxNumKernels(ReadVal<unsigned int>(v2,"maxNumSamples",model->GetMaxNumKernels())); model->SetFaRange(ReadVal<double>(v2,"minFA",model->GetFaRange().first), ReadVal<double>(v2,"maxFA",model->GetFaRange().second)); model->SetAdcRange(ReadVal<double>(v2,"minADC",model->GetAdcRange().first), ReadVal<double>(v2,"maxADC",model->GetAdcRange().second)); model->m_CompartmentId = ReadVal<unsigned int>(v2,"ID",0,true); unsigned int numCoeffs = ReadVal<unsigned int>(v2,"numCoeffs",0,true); unsigned int numSamples = ReadVal<unsigned int>(v2,"numSamples",0,true); for (unsigned int j=0; j<numSamples; j++) { vnl_vector< double > coeffs(numCoeffs); for (unsigned int k=0; k<numCoeffs; k++) { coeffs[k] = ReadVal<double>(v2,"kernels."+boost::lexical_cast<string>(j)+".coeffs."+boost::lexical_cast<string>(k),0,true); } model->SetShCoefficients( coeffs, ReadVal<double>(v2,"kernels."+boost::lexical_cast<string>(j)+".B0",0,true) ); } if (ReadVal<string>(v2,"type","",true)=="fiber") { m_FiberModelList.push_back(model); } else if (ReadVal<string>(v2,"type","",true)=="non-fiber") { m_NonFiberModelList.push_back(model); } // else ? signalModel = model; } if (signalModel!=nullptr) { signalModel->SetGradientList(gradients); try { itk::ImageFileReader<ItkDoubleImgType>::Pointer reader = itk::ImageFileReader<ItkDoubleImgType>::New(); reader->SetFileName(filename+"_VOLUME"+ReadVal<string>(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<ItkDoubleImgType>::Pointer reader = itk::ImageFileReader<ItkDoubleImgType>::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<ItkUcharImgType>::Pointer reader = itk::ImageFileReader<ItkUcharImgType>::New(); reader->SetFileName(filename+"_MASK.nrrd"); reader->Update(); m_SignalGen.m_MaskImage = reader->GetOutput(); MITK_INFO << "Mask image loaded."; } catch(...) { MITK_INFO << "No mask image found."; } setlocale(LC_ALL, currLocale.c_str()); } template< class ScalarType > void mitk::FiberfoxParameters< ScalarType >::PrintSelf() { MITK_INFO << "Not implemented :("; } diff --git a/Modules/IGT/IO/mitkNavigationDataPlayerBase.cpp b/Modules/IGT/IO/mitkNavigationDataPlayerBase.cpp index f62b6e171a..8075810bd2 100644 --- a/Modules/IGT/IO/mitkNavigationDataPlayerBase.cpp +++ b/Modules/IGT/IO/mitkNavigationDataPlayerBase.cpp @@ -1,110 +1,110 @@ /*=================================================================== 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 "mitkNavigationDataPlayerBase.h" // include for exceptions #include "mitkIGTException.h" mitk::NavigationDataPlayerBase::NavigationDataPlayerBase() : m_Repeat(false) { this->SetName("Navigation Data Player Source"); } mitk::NavigationDataPlayerBase::~NavigationDataPlayerBase() { } void mitk::NavigationDataPlayerBase::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } bool mitk::NavigationDataPlayerBase::IsAtEnd() { return m_NavigationDataSetIterator == m_NavigationDataSet->End(); } void mitk::NavigationDataPlayerBase::SetNavigationDataSet(NavigationDataSet::Pointer navigationDataSet) { m_NavigationDataSet = navigationDataSet; m_NavigationDataSetIterator = navigationDataSet->Begin(); this->InitPlayer(); } unsigned int mitk::NavigationDataPlayerBase::GetNumberOfSnapshots() { return m_NavigationDataSet.IsNull() ? 0 : m_NavigationDataSet->Size(); } unsigned int mitk::NavigationDataPlayerBase::GetCurrentSnapshotNumber() { return m_NavigationDataSet.IsNull() ? 0 : m_NavigationDataSetIterator - m_NavigationDataSet->Begin(); } void mitk::NavigationDataPlayerBase::InitPlayer() { if ( m_NavigationDataSet.IsNull() ) { mitkThrowException(mitk::IGTException) << "NavigationDataSet has to be set before initializing player."; } if (GetNumberOfOutputs() == 0) { - int requiredOutputs = m_NavigationDataSet->GetNumberOfTools(); + unsigned int requiredOutputs = m_NavigationDataSet->GetNumberOfTools(); this->SetNumberOfRequiredOutputs(requiredOutputs); for (unsigned int n = this->GetNumberOfOutputs(); n < requiredOutputs; ++n) { DataObjectPointer newOutput = this->MakeOutput(n); this->SetNthOutput(n, newOutput); this->Modified(); } } else if (GetNumberOfOutputs() != m_NavigationDataSet->GetNumberOfTools()) { mitkThrowException(mitk::IGTException) << "Number of tools cannot be changed in existing player. Please create " << "a new player, if the NavigationDataSet has another number of tools now."; } this->Modified(); this->GenerateData(); } void mitk::NavigationDataPlayerBase::GraftEmptyOutput() { for (unsigned int index = 0; index < m_NavigationDataSet->GetNumberOfTools(); index++) { mitk::NavigationData* output = this->GetOutput(index); assert(output); mitk::NavigationData::Pointer nd = mitk::NavigationData::New(); mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0,0.0,0.0,0.0); position.Fill(0.0); nd->SetPosition(position); nd->SetOrientation(orientation); nd->SetDataValid(false); output->Graft(nd); } } diff --git a/Modules/IGT/IO/mitkNavigationDataRecorder.cpp b/Modules/IGT/IO/mitkNavigationDataRecorder.cpp index 9ac8ba981e..fa2a457668 100644 --- a/Modules/IGT/IO/mitkNavigationDataRecorder.cpp +++ b/Modules/IGT/IO/mitkNavigationDataRecorder.cpp @@ -1,115 +1,116 @@ /*=================================================================== 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 "mitkNavigationDataRecorder.h" #include <mitkIGTTimeStamp.h> mitk::NavigationDataRecorder::NavigationDataRecorder() { //set default values m_NumberOfInputs = 0; m_Recording = false; m_StandardizedTimeInitialized = false; m_RecordCountLimit = -1; } mitk::NavigationDataRecorder::~NavigationDataRecorder() { //mitk::IGTTimeStamp::GetInstance()->Stop(this); //commented out because of bug 18952 } void mitk::NavigationDataRecorder::GenerateData() { // get each input, lookup the associated BaseData and transfer the data DataObjectPointerArray inputs = this->GetIndexedInputs(); //get all inputs //This vector will hold the NavigationDatas that are copied from the inputs std::vector< mitk::NavigationData::Pointer > clonedDatas; // For each input for (unsigned int index=0; index < inputs.size(); index++) { // First copy input to output this->GetOutput(index)->Graft(this->GetInput(index)); // if we are not recording, that's all there is to do if (! m_Recording) continue; // Clone a Navigation Data mitk::NavigationData::Pointer clone = mitk::NavigationData::New(); clone->Graft(this->GetInput(index)); clonedDatas.push_back(clone); if (m_StandardizeTime) { mitk::NavigationData::TimeStampType igtTimestamp = mitk::IGTTimeStamp::GetInstance()->GetElapsed(this); clonedDatas[index]->SetIGTTimeStamp(igtTimestamp); } } // if limitation is set and has been reached, stop recording - if ((m_RecordCountLimit > 0) && (m_NavigationDataSet->Size() >= m_RecordCountLimit)) m_Recording = false; + if ((m_RecordCountLimit > 0) && (m_NavigationDataSet->Size() >= static_cast<unsigned int>(m_RecordCountLimit))) + m_Recording = false; // We can skip the rest of the method, if recording is deactivated - if (!m_Recording) return; - - + if (!m_Recording) + return; + // Add data to set m_NavigationDataSet->AddNavigationDatas(clonedDatas); } void mitk::NavigationDataRecorder::StartRecording() { if (m_Recording) { MITK_WARN << "Already recording please stop before start new recording session"; return; } m_Recording = true; // The first time this StartRecording is called, we initialize the standardized time. // Afterwards, it can be reset via ResetNavigationDataSet(); if (! m_StandardizedTimeInitialized) mitk::IGTTimeStamp::GetInstance()->Start(this); if (m_NavigationDataSet.IsNull()) m_NavigationDataSet = mitk::NavigationDataSet::New(GetNumberOfIndexedInputs()); } void mitk::NavigationDataRecorder::StopRecording() { if (!m_Recording) { std::cout << "You have to start a recording first" << std::endl; return; } m_Recording = false; } void mitk::NavigationDataRecorder::ResetRecording() { m_NavigationDataSet = mitk::NavigationDataSet::New(GetNumberOfIndexedInputs()); if (m_Recording) { mitk::IGTTimeStamp::GetInstance()->Stop(this); mitk::IGTTimeStamp::GetInstance()->Start(this); } } int mitk::NavigationDataRecorder::GetNumberOfRecordedSteps() { return m_NavigationDataSet->Size(); } \ No newline at end of file diff --git a/Modules/IGTUI/Qmitk/QmitkIGTPlayerWidget.cpp b/Modules/IGTUI/Qmitk/QmitkIGTPlayerWidget.cpp index d8ed4d379f..d607dbf839 100644 --- a/Modules/IGTUI/Qmitk/QmitkIGTPlayerWidget.cpp +++ b/Modules/IGTUI/Qmitk/QmitkIGTPlayerWidget.cpp @@ -1,573 +1,573 @@ /*=================================================================== 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 "QmitkIGTPlayerWidget.h" //mitk headers #include "mitkTrackingTypes.h" #include <mitkSurface.h> #include <mitkNavigationToolReader.h> #include <mitkNavigationToolWriter.h> #include <mitkNavigationToolStorage.h> #include <mitkNavigationToolStorageDeserializer.h> #include <mitkNavigationToolStorageSerializer.h> #include <mitkIGTException.h> #include <mitkIOUtil.h> //qt headers #include <qfiledialog.h> #include <qmessagebox.h> #include <qtimer.h> QmitkIGTPlayerWidget::QmitkIGTPlayerWidget(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f), m_RealTimePlayer(mitk::NavigationDataPlayer::New()), m_SequentialPlayer(mitk::NavigationDataSequentialPlayer::New()), m_StartTime(-1.0), m_CurrentSequentialPointNumber(0), m_Controls(new Ui::QmitkIGTPlayerWidgetControls) { m_Controls->setupUi(this); m_PlayingTimer = new QTimer(this); // initialize update timer CreateConnections(); m_Controls->samplePositionHorizontalSlider->setVisible(false); this->ResetLCDNumbers(); // reset lcd numbers at start } QmitkIGTPlayerWidget::~QmitkIGTPlayerWidget() { m_PlayingTimer->stop(); delete m_Controls; } void QmitkIGTPlayerWidget::CreateConnections() { connect( (QObject*)(m_Controls->playPushButton), SIGNAL(clicked(bool)), this, SLOT(OnPlayButtonClicked(bool)) ); // play button connect( (QObject*)(m_PlayingTimer), SIGNAL(timeout()), this, SLOT(OnPlaying()) ); // update timer connect( (QObject*) (m_Controls->beginPushButton), SIGNAL(clicked()), this, SLOT(OnGoToBegin()) ); // reset player and go to begin connect( (QObject*) (m_Controls->stopPushButton), SIGNAL(clicked()), this, SLOT(OnGoToEnd()) ); // reset player // pass this widgets protected combobox signal to public signal connect( (QObject*) (m_Controls->trajectorySelectComboBox), SIGNAL(currentIndexChanged(int)), this, SIGNAL(SignalCurrentTrajectoryChanged(int)) ); // pass this widgets protected checkbox signal to public signal connect( m_Controls->splineModeCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(SignalSplineModeToggled(bool)) ); //connect( m_Controls->sequencialModeCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnSequencialModeToggled(bool)) ); connect( m_Controls->samplePositionHorizontalSlider, SIGNAL(sliderPressed()), this, SLOT(OnSliderPressed()) ); connect( m_Controls->samplePositionHorizontalSlider, SIGNAL(sliderReleased()), this, SLOT(OnSliderReleased()) ); connect( m_Controls->m_OpenFileButton, SIGNAL(clicked()), this, SLOT(OnOpenFileButtonPressed()) ); } bool QmitkIGTPlayerWidget::IsTrajectoryInSplineMode() { return m_Controls->splineModeCheckBox->isChecked(); } bool QmitkIGTPlayerWidget::CheckInputFileValid() { QFile file(m_CmpFilename); // check if file exists if(!file.exists()) { QMessageBox::warning(nullptr, "IGTPlayer: Error", "No valid input file was loaded. Please load input file first!"); return false; } return true; } unsigned int QmitkIGTPlayerWidget::GetNumberOfTools() { unsigned int result = 0; if(this->GetCurrentPlaybackMode() == RealTimeMode) { if(m_RealTimePlayer.IsNotNull()) result = m_RealTimePlayer->GetNumberOfOutputs(); } else if(this->GetCurrentPlaybackMode() == SequentialMode) { if(m_SequentialPlayer.IsNotNull()) result = m_SequentialPlayer->GetNumberOfOutputs(); } // at the moment this works only if player is initialized return result; } void QmitkIGTPlayerWidget::SetUpdateRate(unsigned int msecs) { m_PlayingTimer->setInterval((int) msecs); // set update timer update rate } void QmitkIGTPlayerWidget::OnPlayButtonClicked(bool checked) { if ( ! checked ) { if ( this->GetCurrentPlaybackMode() == RealTimeMode ) { m_RealTimePlayer->StopPlaying(); } else if ( this->GetCurrentPlaybackMode() == SequentialMode ) { // m_SequentialPlayer-> } } if(CheckInputFileValid()) // no playing possible without valid input file { switch ( this->GetCurrentPlaybackMode() ) { case RealTimeMode: { break; } case SequentialMode: { break; } } PlaybackMode currentMode = this->GetCurrentPlaybackMode(); bool isRealTimeMode = currentMode == RealTimeMode; bool isSequentialMode = currentMode == SequentialMode; if(checked) // play { if( (isRealTimeMode && m_RealTimePlayer.IsNull()) || (isSequentialMode && m_SequentialPlayer.IsNull())) // start play { mitk::NavigationDataSet::Pointer navigationDataSet; try { navigationDataSet = dynamic_cast<mitk::NavigationDataSet*> (mitk::IOUtil::LoadBaseData(m_CmpFilename.toStdString()).GetPointer()); } catch(mitk::IGTException) { std::string errormessage = "Error during start playing. Invalid or wrong file?"; QMessageBox::warning(nullptr, "IGTPlayer: Error", errormessage.c_str()); m_Controls->playPushButton->setChecked(false); m_RealTimePlayer = nullptr; return; } if(isRealTimeMode) { m_RealTimePlayer = mitk::NavigationDataPlayer::New(); m_RealTimePlayer->SetNavigationDataSet(navigationDataSet); try { m_RealTimePlayer->StartPlaying(); } catch(mitk::IGTException) { std::string errormessage = "Error during start playing. Invalid or wrong file?"; QMessageBox::warning(nullptr, "IGTPlayer: Error", errormessage.c_str()); m_Controls->playPushButton->setChecked(false); m_RealTimePlayer = nullptr; return; } } else if(isSequentialMode) { m_SequentialPlayer = mitk::NavigationDataSequentialPlayer::New(); try { m_SequentialPlayer->SetNavigationDataSet(navigationDataSet); } catch(mitk::IGTException) { std::string errormessage = "Error during start playing. Invalid or wrong file type?"; QMessageBox::warning(nullptr, "IGTPlayer: Error", errormessage.c_str()); m_Controls->playPushButton->setChecked(false); m_RealTimePlayer = nullptr; return; } m_Controls->samplePositionHorizontalSlider->setMinimum(0); m_Controls->samplePositionHorizontalSlider->setMaximum(m_SequentialPlayer->GetNumberOfSnapshots()); m_Controls->samplePositionHorizontalSlider->setEnabled(true); } m_PlayingTimer->start(100); emit SignalPlayingStarted(); } else // resume play { if(isRealTimeMode) m_RealTimePlayer->Resume(); m_PlayingTimer->start(100); emit SignalPlayingResumed(); } } else // pause { if(isRealTimeMode) m_RealTimePlayer->Pause(); m_PlayingTimer->stop(); emit SignalPlayingPaused(); } } else { m_Controls->playPushButton->setChecked(false); // uncheck play button if file unvalid } } QmitkIGTPlayerWidget::PlaybackMode QmitkIGTPlayerWidget::GetCurrentPlaybackMode() { /*if(m_Controls->sequencialModeCheckBox->isChecked()) return SequentialMode; else*/ return RealTimeMode; } QTimer* QmitkIGTPlayerWidget::GetPlayingTimer() { return m_PlayingTimer; } void QmitkIGTPlayerWidget::OnStopPlaying() { this->StopPlaying(); } void QmitkIGTPlayerWidget::StopPlaying() { m_PlayingTimer->stop(); emit SignalPlayingStopped(); if(m_RealTimePlayer.IsNotNull()) m_RealTimePlayer->StopPlaying(); m_StartTime = -1; // set starttime back m_CurrentSequentialPointNumber = 0; m_Controls->samplePositionHorizontalSlider->setSliderPosition(m_CurrentSequentialPointNumber); m_Controls->sampleLCDNumber->display(static_cast<int>(m_CurrentSequentialPointNumber)); this->ResetLCDNumbers(); m_Controls->playPushButton->setChecked(false); // set play button unchecked } void QmitkIGTPlayerWidget::OnPlaying() { switch ( this->GetCurrentPlaybackMode() ) { case RealTimeMode: { if ( m_RealTimePlayer.IsNull() ) { return; } if ( m_StartTime < 0 ) { // get playback start time m_StartTime = m_RealTimePlayer->GetOutput()->GetTimeStamp(); } if( ! m_RealTimePlayer->IsAtEnd() ) { m_RealTimePlayer->Update(); // update player int msc = (int) (m_RealTimePlayer->GetOutput()->GetTimeStamp() - m_StartTime); // calculation for playing time display int ms = msc % 1000; msc = (msc - ms) / 1000; int s = msc % 60; int min = (msc-s) / 60; // set lcd numbers m_Controls->msecLCDNumber->display(ms); m_Controls->secLCDNumber->display(s); m_Controls->minLCDNumber->display(min); emit SignalPlayerUpdated(); // player successfully updated } else { this->StopPlaying(); // if player is at EOF } break; } case SequentialMode: { if ( m_SequentialPlayer.IsNull() ) { return; } if ( m_CurrentSequentialPointNumber < m_SequentialPlayer->GetNumberOfSnapshots() ) { m_SequentialPlayer->Update(); // update sequential player m_Controls->samplePositionHorizontalSlider->setSliderPosition(m_CurrentSequentialPointNumber++); // refresh slider position m_Controls->sampleLCDNumber->display(static_cast<int>(m_CurrentSequentialPointNumber)); //for debugging purposes //std::cout << "Sample: " << m_CurrentSequentialPointNumber << " X: " << m_SequentialPlayer->GetOutput()->GetPosition()[0] << " Y: " << m_SequentialPlayer->GetOutput()->GetPosition()[1] << " Y: " << m_SequentialPlayer->GetOutput()->GetPosition()[2] << std::endl; emit SignalPlayerUpdated(); // player successfully updated } else { this->StopPlaying(); // if player is at EOF } break; } } } const std::vector<mitk::NavigationData::Pointer> QmitkIGTPlayerWidget::GetNavigationDatas() { std::vector<mitk::NavigationData::Pointer> navDatas; if(this->GetCurrentPlaybackMode() == RealTimeMode && m_RealTimePlayer.IsNotNull()) { for(unsigned int i=0; i < m_RealTimePlayer->GetNumberOfOutputs(); ++i) { navDatas.push_back(m_RealTimePlayer->GetOutput(i)); // push back current navigation data for each tool } } else if(this->GetCurrentPlaybackMode() == SequentialMode && m_SequentialPlayer.IsNotNull()) { for(unsigned int i=0; i < m_SequentialPlayer->GetNumberOfOutputs(); ++i) { navDatas.push_back(m_SequentialPlayer->GetOutput(i)); // push back current navigation data for each tool } } return navDatas; } const mitk::PointSet::Pointer QmitkIGTPlayerWidget::GetNavigationDatasPointSet() { mitk::PointSet::Pointer result = mitk::PointSet::New(); mitk::PointSet::PointType pointType; PlaybackMode currentMode = this->GetCurrentPlaybackMode(); bool isRealTimeMode = currentMode == RealTimeMode; bool isSequentialMode = currentMode == SequentialMode; if( (isRealTimeMode && m_RealTimePlayer.IsNotNull()) || (isSequentialMode && m_SequentialPlayer.IsNotNull())) { int numberOfOutputs = 0; if(isRealTimeMode) numberOfOutputs = m_RealTimePlayer->GetNumberOfOutputs(); else if(isSequentialMode) numberOfOutputs = m_SequentialPlayer->GetNumberOfOutputs(); for(unsigned int i=0; i < m_RealTimePlayer->GetNumberOfOutputs(); ++i) { mitk::NavigationData::PositionType position; if(isRealTimeMode) position = m_RealTimePlayer->GetOutput(i)->GetPosition(); else if(isSequentialMode) position = m_SequentialPlayer->GetOutput(i)->GetPosition(); pointType[0] = position[0]; pointType[1] = position[1]; pointType[2] = position[2]; result->InsertPoint(i,pointType); // insert current ND as Pointtype in PointSet for return } } return result; } const mitk::PointSet::PointType QmitkIGTPlayerWidget::GetNavigationDataPoint(unsigned int index) { if( index > this->GetNumberOfTools() || index < 0 ) throw std::out_of_range("Tool Index out of range!"); PlaybackMode currentMode = this->GetCurrentPlaybackMode(); bool isRealTimeMode = currentMode == RealTimeMode; bool isSequentialMode = currentMode == SequentialMode; // create return PointType from current ND for tool index mitk::PointSet::PointType result; if( (isRealTimeMode && m_RealTimePlayer.IsNotNull()) || (isSequentialMode && m_SequentialPlayer.IsNotNull())) { mitk::NavigationData::PositionType position; if(isRealTimeMode) position = m_RealTimePlayer->GetOutput(index)->GetPosition(); else if(isSequentialMode) position = m_SequentialPlayer->GetOutput(index)->GetPosition(); result[0] = position[0]; result[1] = position[1]; result[2] = position[2]; } return result; } /*void QmitkIGTPlayerWidget::SetRealTimePlayer( mitk::NavigationDataPlayer::Pointer player ) { if(player.IsNotNull()) m_RealTimePlayer = player; } void QmitkIGTPlayerWidget::SetSequentialPlayer( mitk::NavigationDataSequentialPlayer::Pointer player ) { if(player.IsNotNull()) m_SequentialPlayer = player; }*/ void QmitkIGTPlayerWidget::OnOpenFileButtonPressed() { QString filename = QFileDialog::getOpenFileName(this, "Load tracking data", QDir::currentPath(),"XML files (*.xml)"); QFile file(filename); // if something went wrong or user pressed cancel in the save dialog if ( filename.isEmpty() || ! file.exists() ) { QMessageBox::warning(nullptr, "Warning", QString("Please enter valid path. Using previous path again.")); return; } m_CmpFilename = filename; this->OnGoToEnd(); /// stops playing and resets lcd numbers m_Controls->m_ActiveFileLabel->setText(m_CmpFilename); emit SignalInputFileChanged(); mitk::NavigationDataSet::Pointer navigationDataSet = dynamic_cast<mitk::NavigationDataSet*> (mitk::IOUtil::LoadBaseData(m_CmpFilename.toStdString()).GetPointer()); m_RealTimePlayer->SetNavigationDataSet(navigationDataSet); m_SequentialPlayer->SetNavigationDataSet(navigationDataSet); m_Controls->m_PlayerControlsGroupBox->setEnabled(true); } void QmitkIGTPlayerWidget::OnGoToEnd() { this->StopPlaying(); // reset lcd numbers this->ResetLCDNumbers(); } void QmitkIGTPlayerWidget::OnGoToBegin() { // stop player manual so no PlayingStopped() m_PlayingTimer->stop(); if(this->GetCurrentPlaybackMode() == RealTimeMode && m_RealTimePlayer.IsNotNull()) { m_RealTimePlayer->StopPlaying(); m_RealTimePlayer = nullptr; // set player to nullptr so it can be initialized again if playback is called afterwards } m_StartTime = -1; // set starttime back //reset view elements m_Controls->playPushButton->setChecked(false); this->ResetLCDNumbers(); } void QmitkIGTPlayerWidget::ResetLCDNumbers() { m_Controls->minLCDNumber->display(QString("00")); m_Controls->secLCDNumber->display(QString("00")); m_Controls->msecLCDNumber->display(QString("000")); } void QmitkIGTPlayerWidget::SetTrajectoryNames(const QStringList toolNames) { QComboBox* cBox = m_Controls->trajectorySelectComboBox; if(cBox->count() > 0) this->ClearTrajectorySelectCombobox(); // before making changed to QComboBox it is recommended to disconnet it's SIGNALS and SLOTS disconnect( (QObject*) (m_Controls->trajectorySelectComboBox), SIGNAL(currentIndexChanged(int)), this, SIGNAL(SignalCurrentTrajectoryChanged(int)) ); if(!toolNames.isEmpty()) m_Controls->trajectorySelectComboBox->insertItems(0, toolNames); // adding current tool names to combobox // reconnect after performed changes connect( (QObject*) (m_Controls->trajectorySelectComboBox), SIGNAL(currentIndexChanged(int)), this, SIGNAL(SignalCurrentTrajectoryChanged(int)) ); } int QmitkIGTPlayerWidget::GetResolution() { return m_Controls->resolutionSpinBox->value(); // return currently selected trajectory resolution } void QmitkIGTPlayerWidget::ClearTrajectorySelectCombobox() { // before making changed to QComboBox it is recommended to disconnet it's SIGNALS and SLOTS disconnect( (QObject*) (m_Controls->trajectorySelectComboBox), SIGNAL(currentIndexChanged(int)), this, SIGNAL(SignalCurrentTrajectoryChanged(int)) ); m_Controls->trajectorySelectComboBox->clear(); // reconnect after performed changes connect( (QObject*) (m_Controls->trajectorySelectComboBox), SIGNAL(currentIndexChanged(int)), this, SIGNAL(SignalCurrentTrajectoryChanged(int)) ); } void QmitkIGTPlayerWidget::OnSequencialModeToggled(bool toggled) { this->StopPlaying(); // stop playing when mode is changed if(toggled) { m_Controls->samplePositionHorizontalSlider->setEnabled(true); // enable slider if sequential mode } else if(!toggled) { m_Controls->samplePositionHorizontalSlider->setSliderPosition(0); // set back and disable slider m_Controls->samplePositionHorizontalSlider->setDisabled(true); } } void QmitkIGTPlayerWidget::OnSliderReleased() { int currentSliderValue = m_Controls->samplePositionHorizontalSlider->value(); // current slider value selected through user movement - if(currentSliderValue > m_CurrentSequentialPointNumber) // at the moment only forward scrolling is possible + if(currentSliderValue > static_cast<int>(m_CurrentSequentialPointNumber)) // at the moment only forward scrolling is possible { - unsigned int snapshotNumber = currentSliderValue; + auto snapshotNumber = static_cast<unsigned int>(currentSliderValue); m_SequentialPlayer->GoToSnapshot(snapshotNumber); // move player to selected snapshot m_CurrentSequentialPointNumber = currentSliderValue; m_Controls->sampleLCDNumber->display(currentSliderValue); // update lcdnumber in widget } else m_Controls->samplePositionHorizontalSlider->setValue(m_CurrentSequentialPointNumber); } void QmitkIGTPlayerWidget::OnSliderPressed() { if(m_Controls->playPushButton->isChecked()) // check if widget is playing m_Controls->playPushButton->click(); // perform click to pause the play } diff --git a/Modules/IGTUI/Qmitk/QmitkToolTrackingStatusWidget.cpp b/Modules/IGTUI/Qmitk/QmitkToolTrackingStatusWidget.cpp index dc7fc044f6..8e188a48d3 100644 --- a/Modules/IGTUI/Qmitk/QmitkToolTrackingStatusWidget.cpp +++ b/Modules/IGTUI/Qmitk/QmitkToolTrackingStatusWidget.cpp @@ -1,272 +1,272 @@ /*=================================================================== 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 "QmitkToolTrackingStatusWidget.h" #include <usGetModuleContext.h> QmitkToolTrackingStatusWidget::QmitkToolTrackingStatusWidget(QWidget* parent) : QWidget(parent), m_Controls(nullptr), m_StatusLabels(nullptr), m_NavigationDatas(nullptr), m_NavDatasNewFlag(false) { m_ShowPositions = false; m_ShowQuaternions = false; m_Alignment = Qt::AlignHCenter; m_Style = QmitkToolTrackingStatusWidget::VerticalUpperStyle; CreateQtPartControl( this ); } void QmitkToolTrackingStatusWidget::SetStyle(QmitkToolTrackingStatusWidget::Style newStyle) { //set new style m_Style = newStyle; //update current labels to show them in the new style Refresh(); } void QmitkToolTrackingStatusWidget::SetShowPositions(bool enable) { m_ShowPositions = enable; //update current labels Refresh(); } void QmitkToolTrackingStatusWidget::SetShowQuaternions(bool enable) { m_ShowQuaternions = enable; //update current labels Refresh(); } void QmitkToolTrackingStatusWidget::SetTextAlignment(Qt::AlignmentFlag alignment) { m_Alignment = alignment; //update current labels Refresh(); } QmitkToolTrackingStatusWidget::~QmitkToolTrackingStatusWidget() { //m_Controls = nullptr; if (m_StatusLabels!=nullptr) {delete m_StatusLabels;} if (m_NavigationDatas != nullptr) { m_NavigationDatas->clear(); if (m_NavDatasNewFlag) { delete m_NavigationDatas; } } } void QmitkToolTrackingStatusWidget::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkToolTrackingStatusWidgetControls; m_Controls->setupUi(parent); this->CreateConnections(); //add empty label AddEmptyLabel(); } m_Context = us::GetModuleContext(); std::string m_Filter = "(" + us::ServiceConstants::OBJECTCLASS() + "=" + "org.mitk.services.NavigationToolStorage" + ")"; m_Context->AddServiceListener(this, &QmitkToolTrackingStatusWidget::OnServiceEvent, m_Filter); } void QmitkToolTrackingStatusWidget::OnServiceEvent(const us::ServiceEvent event) { if ((event.GetType() == us::ServiceEvent::MODIFIED) && (m_previewToolStorage.IsNotNull())) {this->PreShowTools(m_previewToolStorage);} } void QmitkToolTrackingStatusWidget::CreateConnections() { } void QmitkToolTrackingStatusWidget::SetNavigationDatas(std::vector<mitk::NavigationData::Pointer>* navDatas) { m_NavigationDatas = navDatas; m_previewToolStorage = nullptr; } void QmitkToolTrackingStatusWidget::AddNavigationData(mitk::NavigationData::Pointer nd) { if(m_NavigationDatas == nullptr) { m_NavigationDatas = new std::vector<mitk::NavigationData::Pointer>(); m_NavDatasNewFlag = true; m_previewToolStorage = nullptr; } m_NavigationDatas->push_back(nd); } void QmitkToolTrackingStatusWidget::Refresh(int posPrecision, int quatPrecision) { if(m_NavigationDatas == nullptr || m_NavigationDatas->size() <= 0) { RemoveGuiLabels(); AddEmptyLabel(); return; } mitk::NavigationData* navData; for(unsigned int i = 0; i < m_NavigationDatas->size(); i++) { navData = m_NavigationDatas->at(i).GetPointer(); QString name(navData->GetName()); QString pos = ""; QString quat = ""; if (m_ShowPositions) { mitk::Point3D position = navData->GetPosition(); pos = " [" + QString::number(position[0],'f',posPrecision) + ";" + QString::number(position[1],'f',posPrecision) + ";" + QString::number(position[2],'f',posPrecision) + "]"; } if (m_ShowQuaternions) { mitk::Quaternion quaternion = navData->GetOrientation(); quat = " / [qx:" + QString::number(quaternion.x(),'f',quatPrecision) + ";qy:" + QString::number(quaternion.y(),'f',quatPrecision) + ";qz:" + QString::number(quaternion.z(),'f',quatPrecision) + ";qr:" + QString::number(quaternion.r()) + "]"; } if(!(m_StatusLabels->at(i)->text() == name+pos+quat)) m_StatusLabels->at(i)->setText(name+pos+quat); if(navData->IsDataValid()) m_StatusLabels->at(i)->setStyleSheet("QLabel{background-color: #8bff8b }"); else m_StatusLabels->at(i)->setStyleSheet("QLabel{background-color: #ff7878 }"); } } void QmitkToolTrackingStatusWidget::ShowStatusLabels() { RemoveGuiLabels(); if(m_NavigationDatas == nullptr || m_NavigationDatas->size() <= 0) { RemoveGuiLabels(); AddEmptyLabel(); return; } m_StatusLabels = new QVector<QLabel*>(); mitk::NavigationData* navData; QLabel* label; for(unsigned int i = 0; i < m_NavigationDatas->size(); i++) { navData = m_NavigationDatas->at(i).GetPointer(); QString name(navData->GetName()); label = new QLabel(name, this); label->setObjectName(name); label->setAlignment(m_Alignment | Qt::AlignVCenter); label->setFrameStyle(QFrame::Panel | QFrame::Sunken); m_StatusLabels->append(label); if (m_Style == QmitkToolTrackingStatusWidget::VerticalUpperStyle) m_Controls->m_VerticalLayout->addWidget(m_StatusLabels->at(i)); else m_Controls->m_GridLayout->addWidget(m_StatusLabels->at(i),0,i); } } void QmitkToolTrackingStatusWidget::PreShowTools(mitk::NavigationToolStorage::Pointer toolStorage) { RemoveGuiLabels(); QLabel* label; - for(unsigned int i = 0; i < toolStorage->GetToolCount(); i++) + for(int i = 0; i < toolStorage->GetToolCount(); i++) { QString name(toolStorage->GetTool(i)->GetToolName().c_str()); label = new QLabel(name, this); label->setObjectName(name); label->setAlignment(m_Alignment | Qt::AlignVCenter); label->setFrameStyle(QFrame::Panel | QFrame::Sunken); label->setStyleSheet("QLabel{background-color: #dddddd }"); if (m_Style == QmitkToolTrackingStatusWidget::VerticalUpperStyle) m_Controls->m_VerticalLayout->addWidget(label); else m_Controls->m_GridLayout->addWidget(label); } m_previewToolStorage = toolStorage; } void QmitkToolTrackingStatusWidget::RemoveStatusLabels() { //remove GUI elements RemoveGuiLabels(); //clear members if(m_StatusLabels != nullptr && m_StatusLabels->size() > 0) { delete m_StatusLabels; m_StatusLabels = new QVector< QLabel* >(); } if(m_NavigationDatas != nullptr && m_NavigationDatas->size() > 0) { if (m_NavDatasNewFlag) { delete m_NavigationDatas; m_NavDatasNewFlag = false; } m_NavigationDatas = new std::vector<mitk::NavigationData::Pointer>(); m_NavDatasNewFlag = true; } //add empty label AddEmptyLabel(); } void QmitkToolTrackingStatusWidget::RemoveGuiLabels() { while(m_Controls->m_GridLayout->count() > 0 || m_Controls->m_VerticalLayout->count() > 0) { if (m_Controls->m_GridLayout->count() > 0) { QWidget* actWidget = m_Controls->m_GridLayout->itemAt(0)->widget(); m_Controls->m_GridLayout->removeWidget(actWidget); delete actWidget; } else if (m_Controls->m_VerticalLayout->count() > 0) { QWidget* actWidget = m_Controls->m_VerticalLayout->itemAt(0)->widget(); m_Controls->m_VerticalLayout->removeWidget(actWidget); delete actWidget; } } } void QmitkToolTrackingStatusWidget::AddEmptyLabel() { //add a label which tells that no tools are loaded yet QLabel* label = new QLabel("No tools loaded yet.", this); label->setObjectName("No tools loaded yet."); label->setAlignment(m_Alignment | Qt::AlignVCenter); label->setFrameStyle(QFrame::Panel | QFrame::Sunken); label->setStyleSheet("QLabel{background-color: #dddddd }"); if (m_Style == QmitkToolTrackingStatusWidget::VerticalUpperStyle) m_Controls->m_VerticalLayout->addWidget(label); else m_Controls->m_GridLayout->addWidget(label); } diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp index d5d888d586..11f751fa84 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp @@ -1,235 +1,235 @@ /*=================================================================== 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 "itkImage.h" #include "mitkExtendedLabelStatisticsImageFilter.h" #include "mitkExtendedStatisticsImageFilter.h" #include "mitkNumericConstants.h" /** \section Testing of Skewness and Kurtosis * This test class is for testing the added coefficients Skewness and Kurtosis * for the mitkExtendedLabelStatisticsImageFilter (Masked Images) and for the * mitkExtendedStatisticsImageFilter (Unmasked Images). Both filter will be tested * against two pictures. */ class mitkImageStatisticsTextureAnalysisTestClass { /** * \brief Explanation of the mitkImageStatisticsTextureAnalysisTestClass test class * * this test class produce test images and masking images with the method CreatingTestImageForDifferentLabelSize. * TestInstanceFortheMaskedStatisticsFilter and TestInstanceFortheUnmaskedStatisticsFilter are the two Instances * for the filters of masking and unmasking images. * TestofSkewnessAndKurtosisForMaskedImagesand TestofSkewnessAndKurtosisForUnmaskedImages are the correlated test * for the checking the values. */ public: typedef itk::Image< int,3 >ImageType; typedef ImageType::Pointer PointerOfImage; typedef itk::ExtendedLabelStatisticsImageFilter< ImageType, ImageType > LabelStatisticsFilterType; typedef LabelStatisticsFilterType::Pointer labelStatisticsFilterPointer; typedef itk::ExtendedStatisticsImageFilter< ImageType > StatisticsFilterType; typedef StatisticsFilterType::Pointer StatisticsFilterPointer; ImageType::Pointer CreatingTestImageForDifferentLabelSize( int factorOfDividingThePicture, int bufferValue, int labelValue) { ImageType::Pointer image = ImageType :: New(); ImageType::IndexType start; ImageType::SizeType size; start[0] = 0; start[1] = 0; start[2] = 0; size[0] = 100; size[1] = 100; size[2] = 100; ImageType:: RegionType region; region.SetSize( size ); region.SetIndex( start ); image->SetRegions(region); image->Allocate(); image->FillBuffer(bufferValue); int i = 0; for(unsigned int r = 0; r < 50; r++) { - for(unsigned int c = 0; c < factorOfDividingThePicture; c++) + for(int c = 0; c < factorOfDividingThePicture; c++) { for(unsigned int l = 0; l < 100; l++) { ImageType::IndexType pixelIndex; pixelIndex[0] = r; pixelIndex[1] = c; pixelIndex[2] = l; image->SetPixel(pixelIndex, labelValue); } } } return image; } LabelStatisticsFilterType::Pointer TestInstanceFortheMaskedStatisticsFilter(ImageType::Pointer image, ImageType::Pointer maskImage) { LabelStatisticsFilterType::Pointer labelStatisticsFilter; labelStatisticsFilter = LabelStatisticsFilterType::New(); labelStatisticsFilter->SetInput( image ); labelStatisticsFilter->UseHistogramsOn(); labelStatisticsFilter->SetHistogramParameters( 20, -10, 10); labelStatisticsFilter->SetLabelInput( maskImage ); labelStatisticsFilter->Update(); return labelStatisticsFilter; } StatisticsFilterType::Pointer TestInstanceFortheUnmaskedStatisticsFilter(ImageType::Pointer image ) { StatisticsFilterType::Pointer StatisticsFilter; StatisticsFilter = StatisticsFilterType::New(); StatisticsFilter->SetInput( image ); StatisticsFilter->SetHistogramParameters( 20, -10, 10 ); StatisticsFilter->Update(); return StatisticsFilter; } //test for Skewness,Kurtosis and MPP for masked Images void TestofSkewnessKurtosisAndMPPForMaskedImages(LabelStatisticsFilterType::Pointer labelStatisticsFilter, double expectedSkewness, double expectedKurtosis, double expectedMPP) { // let's create an object of our class bool isSkewsnessLowerlimitCorrect = labelStatisticsFilter->GetSkewness( 1 )- expectedKurtosis+ std::pow(10,-3) <= expectedSkewness; bool isSkewsnessUpperlimitCorrect = labelStatisticsFilter->GetSkewness( 1 )+ expectedKurtosis+ std::pow(10,-3) >= expectedSkewness; MITK_TEST_CONDITION( isSkewsnessLowerlimitCorrect && isSkewsnessUpperlimitCorrect,"expectedSkewness: " << expectedSkewness << " actual Value: " << labelStatisticsFilter->GetSkewness( 1 ) ); bool isKurtosisUpperlimitCorrect = labelStatisticsFilter->GetKurtosis( 1 ) <= expectedKurtosis+ std::pow(10,-3); bool isKurtosisLowerlimitCorrect = expectedKurtosis- std::pow(10,-3) <= labelStatisticsFilter->GetKurtosis( 1 ); MITK_TEST_CONDITION( isKurtosisUpperlimitCorrect && isKurtosisLowerlimitCorrect,"expectedKurtosis: " << expectedKurtosis << " actual Value: " << labelStatisticsFilter->GetKurtosis( 1 ) ); MITK_TEST_CONDITION( ( expectedMPP - labelStatisticsFilter->GetMPP( 1 ) ) < 1, "expected MPP: " << expectedMPP << " actual Value: " << labelStatisticsFilter->GetMPP( 1 ) ); } //test for Entropy,Uniformity and UPP for masked Images void TestofEntropyUniformityAndUppForMaskedImages(LabelStatisticsFilterType::Pointer labelStatisticsFilter, double expectedEntropy, double expectedUniformity, double expectedUPP) { bool calculatedEntropyLowerLimit = labelStatisticsFilter->GetEntropy( 1 ) >= expectedEntropy - std::pow(10,-3); bool calculatedUniformityLowerLimit = labelStatisticsFilter->GetUniformity( 1 ) >= expectedUniformity - std::pow(10,-3); bool calculatedUppLowerLimit = labelStatisticsFilter->GetUPP( 1 ) >= expectedUPP - std::pow(10,-3); bool calculatedEntropyUpperLimit = labelStatisticsFilter->GetEntropy( 1 ) <= expectedEntropy + std::pow(10,-3); bool calculatedUniformityUpperLimit = labelStatisticsFilter->GetUniformity( 1 ) <= expectedUniformity + std::pow(10,-3); bool calculatedUppUpperLimit = labelStatisticsFilter->GetUPP( 1 ) <= expectedUPP + std::pow(10,-3); MITK_TEST_CONDITION( calculatedEntropyLowerLimit && calculatedEntropyUpperLimit, "expected Entropy: " << expectedEntropy << " actual Value: " << labelStatisticsFilter->GetEntropy( 1 ) ); MITK_TEST_CONDITION( calculatedUniformityLowerLimit && calculatedUniformityUpperLimit, "expected Uniformity: " << expectedUniformity << " actual Value: " << labelStatisticsFilter->GetUniformity( 1 ) ); MITK_TEST_CONDITION( calculatedUppLowerLimit && calculatedUppUpperLimit, "expected UPP: " << expectedUPP << " actual Value: " << labelStatisticsFilter->GetUPP( 1 ) ); } //test for Skewness,Kurtosis and MPP for unmasked Images void TestofSkewnessKurtosisAndMPPForUnmaskedImages(StatisticsFilterType::Pointer StatisticsFilter, double expectedSkewness, double expectedKurtosis, double expectedMPP) { // let's create an object of our class bool isSkewsnessLowerlimitCorrect = StatisticsFilter->GetSkewness()- expectedKurtosis+ std::pow(10,-3) <= expectedSkewness; bool isSkewsnessUpperlimitCorrect = StatisticsFilter->GetSkewness()+ expectedKurtosis+ std::pow(10,-3) >= expectedSkewness; MITK_TEST_CONDITION( isSkewsnessLowerlimitCorrect && isSkewsnessUpperlimitCorrect,"expectedSkewness: " << expectedSkewness << " actual Value: " << StatisticsFilter->GetSkewness() ); bool isKurtosisUpperlimitCorrect = StatisticsFilter->GetKurtosis() <= expectedKurtosis+ std::pow(10,-3); bool isKurtosisLowerlimitCorrect = expectedKurtosis- std::pow(10,-3) <= StatisticsFilter->GetKurtosis(); MITK_TEST_CONDITION( isKurtosisUpperlimitCorrect && isKurtosisLowerlimitCorrect,"expectedKurtosis: " << expectedKurtosis << " actual Value: " << StatisticsFilter->GetKurtosis() ); MITK_TEST_CONDITION( ( expectedMPP - StatisticsFilter->GetMPP() ) < mitk::eps, "expected MPP: " << expectedMPP << " actual Value: " << StatisticsFilter->GetMPP() ); } //test for Entropy,Uniformity and UPP for unmasked Images void TestofEntropyUniformityAndUppForUnmaskedImages(StatisticsFilterType::Pointer StatisticsFilter, double expectedEntropy, double expectedUniformity, double expectedUPP) { bool calculatedEntropyLowerLimit = StatisticsFilter->GetEntropy() >= expectedEntropy - std::pow(10,-3); bool calculatedUniformityLowerLimit = StatisticsFilter->GetUniformity() >= expectedUniformity - std::pow(10,-3); bool calculatedUppLowerLimit = StatisticsFilter->GetUPP() >= expectedUPP - std::pow(10,-3); bool calculatedEntropyUpperLimit = StatisticsFilter->GetEntropy() <= expectedEntropy + std::pow(10,-3); bool calculatedUniformityUpperLimit = StatisticsFilter->GetUniformity() <= expectedUniformity + std::pow(10,-3); bool calculatedUppUpperLimit = StatisticsFilter->GetUPP() <= expectedUPP + std::pow(10,-3); MITK_TEST_CONDITION( calculatedEntropyLowerLimit && calculatedEntropyUpperLimit, "expected Entropy: " << expectedEntropy << " actual Value: " << StatisticsFilter->GetEntropy() ); MITK_TEST_CONDITION( calculatedUniformityLowerLimit && calculatedUniformityUpperLimit, "expected Uniformity: " << expectedUniformity << " actual Value: " << StatisticsFilter->GetUniformity() ); MITK_TEST_CONDITION( calculatedUppLowerLimit && calculatedUppUpperLimit, "expected UPP: " << expectedUPP << " actual Value: " << StatisticsFilter->GetUPP() ); } }; int mitkImageStatisticsTextureAnalysisTest(int, char* []) { // always start with this! MITK_TEST_BEGIN("mitkImageStatisticsTextureAnalysisTest") mitkImageStatisticsTextureAnalysisTestClass testclassInstance; mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage labelImage = testclassInstance.CreatingTestImageForDifferentLabelSize(100, 1, 1); mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage image = testclassInstance.CreatingTestImageForDifferentLabelSize(100, 3, 2); mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage image2 = testclassInstance.CreatingTestImageForDifferentLabelSize(50, 3, 2); //test for masked images mitkImageStatisticsTextureAnalysisTestClass::labelStatisticsFilterPointer mitkLabelFilter= testclassInstance.TestInstanceFortheMaskedStatisticsFilter( image,labelImage); testclassInstance.TestofSkewnessKurtosisAndMPPForMaskedImages(mitkLabelFilter, 0, 0.999998, 2.5); testclassInstance.TestofEntropyUniformityAndUppForMaskedImages(mitkLabelFilter, 1, 0.5, 0.5); mitkImageStatisticsTextureAnalysisTestClass::labelStatisticsFilterPointer mitkLabelFilter2= testclassInstance.TestInstanceFortheMaskedStatisticsFilter( image2,labelImage); testclassInstance.TestofSkewnessKurtosisAndMPPForMaskedImages(mitkLabelFilter2, -1.1547, 2.33333, 2.75); testclassInstance.TestofEntropyUniformityAndUppForMaskedImages(mitkLabelFilter2, 0.811278, 0.625, 0.625); //test for unmasked images mitkImageStatisticsTextureAnalysisTestClass::StatisticsFilterPointer mitkFilter= testclassInstance.TestInstanceFortheUnmaskedStatisticsFilter( image); testclassInstance.TestofSkewnessKurtosisAndMPPForUnmaskedImages(mitkFilter, 0, 0.999998, 2.5); testclassInstance.TestofEntropyUniformityAndUppForUnmaskedImages(mitkFilter, 1, 0.5, 0.5); mitkImageStatisticsTextureAnalysisTestClass::StatisticsFilterPointer mitkFilter2= testclassInstance.TestInstanceFortheUnmaskedStatisticsFilter( image2); testclassInstance.TestofSkewnessKurtosisAndMPPForUnmaskedImages(mitkFilter2, -1.1547, 2.33333, 2.75); testclassInstance.TestofEntropyUniformityAndUppForUnmaskedImages( mitkFilter2, 0.811278, 0.625, 0.625); MITK_TEST_END() } diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx index 37085f67f2..9413e33c0c 100644 --- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx +++ b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx @@ -1,477 +1,477 @@ /*=================================================================== 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 __mitkExtendedStatisticsImageFilter_hxx #define __mitkExtendedStatisticsImageFilter_hxx #include "mitkExtendedStatisticsImageFilter.h" #include <mitkHistogramStatisticsCalculator.h> namespace itk { template< class TInputImage > ExtendedStatisticsImageFilter< TInputImage >::ExtendedStatisticsImageFilter() : StatisticsImageFilter< TInputImage >(), m_ThreadSum(1), m_SumOfSquares(1), m_SumOfCubes(1), m_SumOfQuadruples(1), m_Count(1), m_ThreadMin(1), m_ThreadMax(1), m_PositivePixelCount(1), m_ThreadSumOfPositivePixels(1), m_UseHistogram(false), m_HistogramCalculated(false) { /* * add the Skewness,Kurtosis,Entropy,Uniformity,MPP, UPP, Median to the other statistical calculated Values * of the mitkStatisticsImageFilter as the 7th to the 13th Output */ for ( int i = 7; i < 14; ++i ) { typename RealObjectType::Pointer output = static_cast< RealObjectType * >( this->MakeOutput(i).GetPointer() ); this->ProcessObject::SetNthOutput( i, output.GetPointer() ); } this->GetSkewnessOutput()->Set( 0.0 ); this->GetKurtosisOutput()->Set( 0.0 ); this->GetEntropyOutput()->Set( -1.0 ); this->GetUniformityOutput()->Set( 0.0 ); this->GetUPPOutput()->Set( 0.0 ); this->GetMPPOutput()->Set( 0.0 ); this->GetMedianOutput()->Set( 0.0 ); m_Histogram = ITK_NULLPTR; m_HistogramPerThread.resize(0); } template< class TInputImage > DataObject::Pointer ExtendedStatisticsImageFilter< TInputImage >::MakeOutput( ProcessObject::DataObjectPointerArraySizeType output) { switch ( output ) { case 7: case 8: case 9: case 10: case 11: case 12: { return RealObjectType::New().GetPointer(); break; } case 13: { return RealObjectType::New().GetPointer(); break; } default: { // might as well make an image return Superclass::MakeOutput( output ); break; } } } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetSkewnessOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(7) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetSkewnessOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(7) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetKurtosisOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(8) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetKurtosisOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(8) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUniformityOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(9) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUniformityOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(9) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetEntropyOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(10) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetEntropyOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(10) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUPPOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(11) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUPPOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(11) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMPPOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(12) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMPPOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(12) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMedianOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(13) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMedianOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(13) ); } template< typename TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::BeforeThreadedGenerateData() { ThreadIdType numberOfThreads = this->GetNumberOfThreads(); if (m_UseHistogram) { // Histogram m_Histogram = HistogramType::New(); typename HistogramType::SizeType hsize; typename HistogramType::MeasurementVectorType lb; typename HistogramType::MeasurementVectorType ub; hsize.SetSize(1); lb.SetSize(1); ub.SetSize(1); m_Histogram->SetMeasurementVectorSize(1); hsize[0] = m_NumBins; lb[0] = m_LowerBound; ub[0] = m_UpperBound; m_Histogram->Initialize(hsize, lb, ub); m_HistogramPerThread.resize(numberOfThreads); for (unsigned int i = 0; i < numberOfThreads; i++) { typename HistogramType::Pointer hist = HistogramType::New(); typename HistogramType::SizeType hsize; typename HistogramType::MeasurementVectorType lb; typename HistogramType::MeasurementVectorType ub; hsize.SetSize(1); lb.SetSize(1); ub.SetSize(1); hist->SetMeasurementVectorSize(1); hsize[0] = m_NumBins; lb[0] = m_LowerBound; ub[0] = m_UpperBound; hist->Initialize(hsize, lb, ub); m_HistogramPerThread[i] = hist; } } // Resize the thread temporaries m_Count.SetSize(numberOfThreads); m_SumOfSquares.SetSize(numberOfThreads); m_SumOfCubes.SetSize(numberOfThreads); m_SumOfQuadruples.SetSize(numberOfThreads); m_ThreadSum.SetSize(numberOfThreads); m_ThreadMin.SetSize(numberOfThreads); m_ThreadMax.SetSize(numberOfThreads); m_PositivePixelCount.SetSize(numberOfThreads); m_ThreadSumOfPositivePixels.SetSize(numberOfThreads); // Initialize the temporaries m_Count.Fill(NumericTraits< SizeValueType >::Zero); m_ThreadSum.Fill(NumericTraits< RealType >::Zero); m_SumOfSquares.Fill(NumericTraits< RealType >::Zero); m_SumOfCubes.Fill(NumericTraits< RealType >::Zero); m_SumOfQuadruples.Fill(NumericTraits< RealType >::Zero); m_ThreadMin.Fill( NumericTraits< PixelType >::max() ); m_ThreadMax.Fill( NumericTraits< PixelType >::NonpositiveMin() ); m_PositivePixelCount.Fill(NumericTraits< SizeValueType >::Zero); m_ThreadSumOfPositivePixels.Fill(NumericTraits< SizeValueType >::Zero); } template< typename TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::ThreadedGenerateData(const typename StatisticsImageFilter<TInputImage>::RegionType & outputRegionForThread, ThreadIdType threadId) { const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } RealType realValue; PixelType value; typename HistogramType::IndexType histogramIndex(1); typename HistogramType::MeasurementVectorType histogramMeasurement(1); RealType sum = NumericTraits< RealType >::ZeroValue(); RealType sumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); RealType sumOfSquares = NumericTraits< RealType >::ZeroValue(); RealType sumOfCubes = NumericTraits< RealType >::ZeroValue(); RealType sumOfQuadruples = NumericTraits< RealType >::ZeroValue(); SizeValueType count = NumericTraits< SizeValueType >::ZeroValue(); SizeValueType countOfPositivePixels = NumericTraits< SizeValueType >::ZeroValue(); PixelType min = NumericTraits< PixelType >::max(); PixelType max = NumericTraits< PixelType >::NonpositiveMin(); ImageScanlineConstIterator< TInputImage > it (this->GetInput(), outputRegionForThread); // support progress methods/callbacks const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0; ProgressReporter progress( this, threadId, numberOfLinesToProcess ); // do the work while ( !it.IsAtEnd() ) { while ( !it.IsAtEndOfLine() ) { value = it.Get(); realValue = static_cast< RealType >( value ); if (m_UseHistogram) { histogramMeasurement[0] = value; m_HistogramPerThread[threadId]->GetIndex(histogramMeasurement, histogramIndex); m_HistogramPerThread[threadId]->IncreaseFrequencyOfIndex(histogramIndex, 1); } if ( value < min ) { min = value; } if ( value > max ) { max = value; } if (value > 0) { sumOfPositivePixels += realValue; ++countOfPositivePixels; } sum += realValue; sumOfSquares += ( realValue * realValue ); sumOfCubes += std::pow(realValue, 3.); sumOfQuadruples += std::pow(realValue, 4.); ++count; ++it; } it.NextLine(); progress.CompletedPixel(); } m_ThreadSum[threadId] = sum; m_SumOfSquares[threadId] = sumOfSquares; m_Count[threadId] = count; m_ThreadMin[threadId] = min; m_ThreadMax[threadId] = max; m_ThreadSumOfPositivePixels[threadId] = sumOfPositivePixels; m_PositivePixelCount[threadId] = countOfPositivePixels; m_SumOfCubes[threadId] = sumOfCubes; m_SumOfQuadruples[threadId] = sumOfQuadruples; } template< class TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::AfterThreadedGenerateData() { ThreadIdType i; SizeValueType count = 0; SizeValueType countOfPositivePixels = 0; RealType sumOfSquares = 0; RealType sumOfCubes = 0; RealType sumOfQuadruples = 0; RealType sumOfPositivePixels = 0; ThreadIdType numberOfThreads = this->GetNumberOfThreads(); PixelType minimum; PixelType maximum; RealType mean; RealType meanOfPositivePixels; RealType sigma; RealType variance; RealType skewness; RealType kurtosis; RealType sum; sum = sumOfSquares = sumOfCubes = sumOfQuadruples = NumericTraits< RealType >::ZeroValue(); count = countOfPositivePixels = 0; // Find the min/max over all threads and accumulate count, sum and // sum of squares minimum = NumericTraits< PixelType >::max(); maximum = NumericTraits< PixelType >::NonpositiveMin(); for ( i = 0; i < numberOfThreads; i++ ) { count += m_Count[i]; sum += m_ThreadSum[i]; sumOfSquares += m_SumOfSquares[i]; sumOfCubes += m_SumOfCubes[i]; sumOfQuadruples += m_SumOfQuadruples[i]; sumOfPositivePixels += m_ThreadSumOfPositivePixels[i]; countOfPositivePixels += m_PositivePixelCount[i]; if ( m_ThreadMin[i] < minimum ) { minimum = m_ThreadMin[i]; } if ( m_ThreadMax[i] > maximum ) { maximum = m_ThreadMax[i]; } // if enabled, update the histogram for this label if ( m_UseHistogram ) { typename HistogramType::IndexType index; index.SetSize(1); - for ( unsigned int bin = 0; bin < m_NumBins; bin++ ) + for ( int bin = 0; bin < m_NumBins; ++bin ) { index[0] = bin; m_Histogram->IncreaseFrequency( bin, m_HistogramPerThread[i]->GetFrequency(bin) ); } } } // compute statistics mean = sum / static_cast< RealType >( count ); meanOfPositivePixels = sumOfPositivePixels / static_cast< RealType >( countOfPositivePixels ); // unbiased estimate variance = ( sumOfSquares - ( sum * sum / static_cast< RealType >( count ) ) ) / ( static_cast< RealType >( count ) ); RealType secondMoment, thirdMoment, fourthMoment; secondMoment = sumOfSquares / static_cast< RealType >( count ); thirdMoment = sumOfCubes / static_cast< RealType >( count ); fourthMoment = sumOfQuadruples / static_cast< RealType >( count ); skewness = (thirdMoment - 3. * secondMoment * mean + 2. * std::pow(mean, 3.)) / std::pow(secondMoment - std::pow(mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html kurtosis = (fourthMoment - 4. * thirdMoment * mean + 6. * secondMoment * std::pow(mean, 2.) - 3. * std::pow(mean, 4.)) / std::pow(secondMoment - std::pow(mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_46_1/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3 sigma = std::sqrt(variance); // Set the outputs this->GetMinimumOutput()->Set(minimum); this->GetMaximumOutput()->Set(maximum); this->GetMeanOutput()->Set(mean); this->GetSigmaOutput()->Set(sigma); this->GetVarianceOutput()->Set(variance); this->GetSumOutput()->Set(sum); this->GetKurtosisOutput()->Set(kurtosis); this->GetSkewnessOutput()->Set(skewness); this->GetMPPOutput()->Set(meanOfPositivePixels); if (m_UseHistogram) { mitk::HistogramStatisticsCalculator histStatCalc; histStatCalc.SetHistogram(m_Histogram); histStatCalc.CalculateStatistics(); this->GetUniformityOutput()->Set(histStatCalc.GetUniformity()); this->GetUPPOutput()->Set(histStatCalc.GetUPP()); this->GetEntropyOutput()->Set(histStatCalc.GetEntropy()); this->GetMedianOutput()->Set(histStatCalc.GetMedian()); } m_HistogramCalculated = true; } template< typename TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound) { m_NumBins = numBins; m_LowerBound = lowerBound; m_UpperBound = upperBound; m_UseHistogram = true; } } #endif diff --git a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp index f4db7bdfa9..5625d31192 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp @@ -1,297 +1,297 @@ /*=================================================================== 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 "mitkImageToOpenCVImageFilter.h" #include "mitkOpenCVToMitkImageFilter.h" #include <mitkTestingMacros.h> #include <mitkITKImageImport.h> #include <mitkImageAccessByItk.h> #include <mitkIOUtil.h> #include "mitkImageReadAccessor.h" #include "mitkImageSliceSelector.h" // itk includes #include <itkRGBPixel.h> #include <iostream> #include <highgui.h> // define test pixel indexes and intensities and other values typedef itk::RGBPixel< unsigned char > TestUCRGBPixelType; cv::Size testImageSize; cv::Point pos1; cv::Point pos2; cv::Point pos3; cv::Vec3b color1; cv::Vec3b color2; cv::Vec3b color3; uchar greyValue1; uchar greyValue2; uchar greyValue3; /*! Documentation * Test for image conversion of OpenCV images and mitk::Images. It tests the classes * OpenCVToMitkImageFilter and ImageToOpenCVImageFilter */ // Some declarations template<typename TPixel, unsigned int VImageDimension> void ComparePixels( itk::Image<itk::RGBPixel<TPixel>,VImageDimension>* image ); void ReadImageDataAndConvertForthAndBack(std::string imageFileName); void ConvertIplImageForthAndBack(mitk::Image::Pointer inputForCVMat, std::string imageFileName); void ConvertCVMatForthAndBack(mitk::Image::Pointer inputForCVMat, std::string imageFileName); // Begin the test for mitkImage to OpenCV image conversion and back. int mitkOpenCVMitkConversionTest(int argc, char* argv[]) { MITK_TEST_BEGIN("ImageToOpenCVImageFilter") // the first part of this test checks the conversion of a cv::Mat style OpenCV image. // we build an cv::Mat image MITK_INFO << "setting test values"; testImageSize = cv::Size(11,11); pos1 = cv::Point(0,0); pos2 = cv::Point(5,5); pos3 = cv::Point(10,10); color1 = cv::Vec3b(50,0,0); color2 = cv::Vec3b(0,128,0); color3 = cv::Vec3b(0,0,255); greyValue1 = 0; greyValue2 = 128; greyValue3 = 255; MITK_INFO << "generating test OpenCV image (RGB)"; cv::Mat testRGBImage = cv::Mat::zeros( testImageSize, CV_8UC3 ); // generate some test intensity values testRGBImage.at<cv::Vec3b>(pos1)= color1; testRGBImage.at<cv::Vec3b>(pos2)= color2; testRGBImage.at<cv::Vec3b>(pos3)= color3; //cv::namedWindow("debug", CV_WINDOW_FREERATIO ); //cv::imshow("debug", testRGBImage.clone()); //cv::waitKey(0); // convert it to a mitk::Image MITK_INFO << "converting OpenCV test image to mitk image and comparing scalar rgb values"; mitk::OpenCVToMitkImageFilter::Pointer openCvToMitkFilter = mitk::OpenCVToMitkImageFilter::New(); openCvToMitkFilter->SetOpenCVMat( testRGBImage ); openCvToMitkFilter->Update(); mitk::Image::Pointer mitkImage = openCvToMitkFilter->GetOutput(); AccessFixedTypeByItk(mitkImage.GetPointer(), ComparePixels, (itk::RGBPixel<unsigned char>), // rgb image (2) ); // convert it back to OpenCV image MITK_INFO << "converting mitk image to OpenCV image and comparing scalar rgb values"; mitk::ImageToOpenCVImageFilter::Pointer mitkToOpenCv = mitk::ImageToOpenCVImageFilter::New(); mitkToOpenCv->SetImage( mitkImage ); cv::Mat openCvImage = mitkToOpenCv->GetOpenCVMat(); // and test equality of the sentinel pixel cv::Vec3b convertedColor1 = openCvImage.at<cv::Vec3b>(pos1); cv::Vec3b convertedColor2 = openCvImage.at<cv::Vec3b>(pos2); cv::Vec3b convertedColor3 = openCvImage.at<cv::Vec3b>(pos3); MITK_TEST_CONDITION( color1 == convertedColor1, "Testing if initially created color values " << static_cast<int>( color1[0] ) << ", " << static_cast<int>( color1[1] ) << ", " << static_cast<int>( color1[2] ) << " matches the color values " << static_cast<int>( convertedColor1[0] ) << ", " << static_cast<int>( convertedColor1[1] ) << ", " << static_cast<int>( convertedColor1[2] ) << " at the same position " << pos1.x << ", " << pos1.y << " in the back converted OpenCV image" ) MITK_TEST_CONDITION( color2 == convertedColor2, "Testing if initially created color values " << static_cast<int>( color2[0] ) << ", " << static_cast<int>( color2[1] ) << ", " << static_cast<int>( color2[2] ) << " matches the color values " << static_cast<int>( convertedColor2[0] ) << ", " << static_cast<int>( convertedColor2[1] ) << ", " << static_cast<int>( convertedColor2[2] ) << " at the same position " << pos2.x << ", " << pos2.y << " in the back converted OpenCV image" ) MITK_TEST_CONDITION( color3 == convertedColor3, "Testing if initially created color values " << static_cast<int>( color3[0] ) << ", " << static_cast<int>( color3[1] ) << ", " << static_cast<int>( color3[2] ) << " matches the color values " << static_cast<int>( convertedColor3[0] ) << ", " << static_cast<int>( convertedColor3[1] ) << ", " << static_cast<int>( convertedColor3[2] ) << " at the same position " << pos3.x << ", " << pos3.y << " in the back converted OpenCV image" ) // the second part of this test checks the conversion of mitk::Images to Ipl images and cv::Mat and back. - for(unsigned int i = 1; i < argc; ++i ) + for (int i = 1; i < argc; ++i) { ReadImageDataAndConvertForthAndBack(argv[i]); } MITK_TEST_END(); } template<typename TPixel, unsigned int VImageDimension> void ComparePixels( itk::Image<itk::RGBPixel<TPixel>,VImageDimension>* image ) { typedef itk::RGBPixel<TPixel> PixelType; typedef itk::Image<PixelType, VImageDimension> ImageType; typename ImageType::IndexType pixelIndex; pixelIndex[0] = pos1.x; pixelIndex[1] = pos1.y; PixelType onePixel = image->GetPixel( pixelIndex ); MITK_TEST_CONDITION( color1[0] == onePixel.GetBlue(), "Testing if blue value (= " << static_cast<int>(color1[0]) << ") at postion " << pos1.x << ", " << pos1.y << " in OpenCV image is " << "equals the blue value (= " << static_cast<int>(onePixel.GetBlue()) << ")" << " in the generated mitk image"); pixelIndex[0] = pos2.x; pixelIndex[1] = pos2.y; onePixel = image->GetPixel( pixelIndex ); MITK_TEST_CONDITION( color2[1] == onePixel.GetGreen(), "Testing if green value (= " << static_cast<int>(color2[1]) << ") at postion " << pos2.x << ", " << pos2.y << " in OpenCV image is " << "equals the green value (= " << static_cast<int>(onePixel.GetGreen()) << ")" << " in the generated mitk image"); pixelIndex[0] = pos3.x; pixelIndex[1] = pos3.y; onePixel = image->GetPixel( pixelIndex ); MITK_TEST_CONDITION( color3[2] == onePixel.GetRed(), "Testing if red value (= " << static_cast<int>(color3[2]) << ") at postion " << pos3.x << ", " << pos3.y << " in OpenCV image is " << "equals the red value (= " << static_cast<int>(onePixel.GetRed()) << ")" << " in the generated mitk image"); } void ReadImageDataAndConvertForthAndBack(std::string imageFileName) { // first we load an mitk::Image from the data repository mitk::Image::Pointer mitkTestImage = mitk::IOUtil::LoadImage(imageFileName); // some format checking mitk::Image::Pointer resultImg = nullptr; if( mitkTestImage->GetDimension() <= 3 ) { if( mitkTestImage->GetDimension() > 2 && mitkTestImage->GetDimension(2) == 1 ) { mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(mitkTestImage); sliceSelector->SetSliceNr(0); sliceSelector->Update(); resultImg = sliceSelector->GetOutput()->Clone(); } else if(mitkTestImage->GetDimension() < 3) { resultImg = mitkTestImage; } else { return; // 3D images are not supported, except with just one slice. } } else { return; // 4D images are not supported! } ConvertIplImageForthAndBack(resultImg, imageFileName); ConvertCVMatForthAndBack(resultImg, imageFileName); } void ConvertCVMatForthAndBack(mitk::Image::Pointer inputForCVMat, std::string imageFileName) { // now we convert it to OpenCV IplImage mitk::ImageToOpenCVImageFilter::Pointer toOCvConverter = mitk::ImageToOpenCVImageFilter::New(); toOCvConverter->SetImage(inputForCVMat); cv::Mat cvmatTestImage = toOCvConverter->GetOpenCVMat(); MITK_TEST_CONDITION_REQUIRED( !cvmatTestImage.empty(), "Conversion to cv::Mat successful!"); mitk::OpenCVToMitkImageFilter::Pointer toMitkConverter = mitk::OpenCVToMitkImageFilter::New(); toMitkConverter->SetOpenCVMat(cvmatTestImage); toMitkConverter->Update(); // initialize the image with the input image, since we want to test equality and OpenCV does not feature geometries and spacing mitk::Image::Pointer result = inputForCVMat->Clone(); mitk::ImageReadAccessor resultAcc(toMitkConverter->GetOutput(), toMitkConverter->GetOutput()->GetSliceData()); result->SetImportSlice(const_cast<void*>(resultAcc.GetData())); if( result->GetPixelType().GetNumberOfComponents() == 1 ) { MITK_TEST_EQUAL( result, inputForCVMat, "Testing equality of input and output image of cv::Mat conversion for " << imageFileName ); } else if( result->GetPixelType().GetNumberOfComponents() == 3 ) { MITK_TEST_EQUAL( result, inputForCVMat, "Testing equality of input and output image of cv::Mat conversion for " << imageFileName ); } else { MITK_WARN << "Unhandled number of components used to test equality, please enhance test!"; } // change OpenCV image to test if the filter gets updated cv::Mat changedcvmatTestImage = cvmatTestImage.clone(); std::size_t numBits = result->GetPixelType().GetBitsPerComponent(); if (result->GetPixelType().GetBitsPerComponent() == sizeof(char)*8) { changedcvmatTestImage.at<char>(0,0) = cvmatTestImage.at<char>(0,0) != 0 ? 0 : 1; } else if (result->GetPixelType().GetBitsPerComponent() == sizeof(float)*8) { changedcvmatTestImage.at<float>(0,0) = cvmatTestImage.at<float>(0,0) != 0 ? 0 : 1; } /* if (result->GetPixelType().GetBitsPerComponent() == 3*sizeof(char)) { changedcvmatTestImage.at<char>(0,0) = cvmatTestImage.at<char>(0,0) != 0 ? 0 : 1; } */ toMitkConverter->SetOpenCVMat(changedcvmatTestImage); toMitkConverter->Update(); MITK_TEST_NOT_EQUAL(toMitkConverter->GetOutput(), inputForCVMat, "Converted image must not be the same as before."); } void ConvertIplImageForthAndBack(mitk::Image::Pointer inputForIpl, std::string imageFileName) { // now we convert it to OpenCV IplImage mitk::ImageToOpenCVImageFilter::Pointer toOCvConverter = mitk::ImageToOpenCVImageFilter::New(); toOCvConverter->SetImage(inputForIpl); IplImage* iplTestImage = toOCvConverter->GetOpenCVImage(); MITK_TEST_CONDITION_REQUIRED( iplTestImage != nullptr, "Conversion to OpenCv IplImage successful!"); mitk::OpenCVToMitkImageFilter::Pointer toMitkConverter = mitk::OpenCVToMitkImageFilter::New(); toMitkConverter->SetOpenCVImage(iplTestImage); toMitkConverter->Update(); // initialize the image with the input image, since we want to test equality and OpenCV does not feature geometries and spacing mitk::Image::Pointer result = inputForIpl->Clone(); mitk::ImageReadAccessor resultAcc(toMitkConverter->GetOutput(), toMitkConverter->GetOutput()->GetSliceData()); result->SetImportSlice(const_cast<void*>(resultAcc.GetData())); if( result->GetPixelType().GetNumberOfComponents() == 1 ) { MITK_TEST_EQUAL( result, inputForIpl, "Testing equality of input and output image of IplImage conversion for " << imageFileName ); } else if( result->GetPixelType().GetNumberOfComponents() == 3 ) { MITK_TEST_EQUAL( result, inputForIpl, "Testing equality of input and output image of cv::Mat conversion for " << imageFileName ); } else { MITK_WARN << "Unhandled number of components used to test equality, please enhance test!"; } } diff --git a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp index 067158bd23..cff70f9384 100644 --- a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp @@ -1,295 +1,295 @@ /*=================================================================== 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 "mitkSurfaceStampImageFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageWriteAccessor.h" #include "mitkTimeHelper.h" #include <mitkImageCast.h> #include <vtkCell.h> #include <vtkLinearTransform.h> #include <vtkPolyData.h> #include <vtkSmartPointer.h> #include <vtkTransform.h> #include <vtkTransformPolyDataFilter.h> #include <itkTriangleMeshToBinaryImageFilter.h> mitk::SurfaceStampImageFilter::SurfaceStampImageFilter() : m_MakeOutputBinary(false), m_OverwriteBackground(false), m_ForegroundValue(1.0), m_BackgroundValue(0.0) { } mitk::SurfaceStampImageFilter::~SurfaceStampImageFilter() { } void mitk::SurfaceStampImageFilter::GenerateInputRequestedRegion() { mitk::Image *outputImage = this->GetOutput(); if ((outputImage->IsInitialized() == false)) return; GenerateTimeInInputRegion(outputImage, const_cast<mitk::Image *>(this->GetInput())); } void mitk::SurfaceStampImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer inputImage = this->GetInput(); mitk::Image::Pointer outputImage = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); if (inputImage.IsNull() || (inputImage->IsInitialized() == false) || (inputImage->GetTimeGeometry() == nullptr)) return; if (m_MakeOutputBinary) outputImage->Initialize(mitk::MakeScalarPixelType<unsigned char>(), *inputImage->GetTimeGeometry()); else outputImage->Initialize(inputImage->GetPixelType(), *inputImage->GetTimeGeometry()); outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone()); } void mitk::SurfaceStampImageFilter::SetSurface(mitk::Surface *surface) { m_Surface = surface; } void mitk::SurfaceStampImageFilter::GenerateData() { mitk::Image::ConstPointer inputImage = this->GetInput(); if (inputImage.IsNull()) return; mitk::Image::Pointer outputImage = this->GetOutput(); if (outputImage->IsInitialized() == false) return; if (m_Surface.IsNull()) return; mitk::Image::RegionType outputRegion = outputImage->GetRequestedRegion(); int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); if (tmax > 0) { int t; for (t = tstart; t < tmax; ++t) { this->SurfaceStamp(t); } } else { this->SurfaceStamp(0); } } void mitk::SurfaceStampImageFilter::SurfaceStamp(int time) { mitk::Image::Pointer inputImage = this->GetInput(); const mitk::TimeGeometry *surfaceTimeGeometry = GetInput()->GetTimeGeometry(); const mitk::TimeGeometry *imageTimeGeometry = inputImage->GetTimeGeometry(); // Convert time step from image time-frame to surface time-frame mitk::TimePointType matchingTimePoint = imageTimeGeometry->TimeStepToTimePoint(time); mitk::TimeStepType surfaceTimeStep = surfaceTimeGeometry->TimePointToTimeStep(matchingTimePoint); vtkPolyData *polydata = m_Surface->GetVtkPolyData(surfaceTimeStep); if (!polydata) mitkThrow() << "Polydata is null."; vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); transformFilter->SetInputData(polydata); // transformFilter->ReleaseDataFlagOn(); vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New(); BaseGeometry::Pointer geometry = surfaceTimeGeometry->GetGeometryForTimeStep(surfaceTimeStep); transform->PostMultiply(); transform->Concatenate(geometry->GetVtkTransform()->GetMatrix()); // take image geometry into account. vtk-Image information will be changed to unit spacing and zero origin below. BaseGeometry::Pointer imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time); transform->Concatenate(imageGeometry->GetVtkTransform()->GetLinearInverse()); transformFilter->SetTransform(transform); transformFilter->Update(); polydata = transformFilter->GetOutput(); if (!polydata || !polydata->GetNumberOfPoints()) mitkThrow() << "Polydata retrieved from transformation is null or has no points."; MeshType::Pointer mesh = MeshType::New(); mesh->SetCellsAllocationMethod(MeshType::CellsAllocatedDynamicallyCellByCell); unsigned int numberOfPoints = polydata->GetNumberOfPoints(); mesh->GetPoints()->Reserve(numberOfPoints); vtkPoints *points = polydata->GetPoints(); MeshType::PointType point; - for (int i = 0; i < numberOfPoints; i++) + for (unsigned int i = 0; i < numberOfPoints; i++) { double *aux = points->GetPoint(i); point[0] = aux[0]; point[1] = aux[1]; point[2] = aux[2]; mesh->SetPoint(i, point); } // Load the polygons into the itk::Mesh typedef MeshType::CellAutoPointer CellAutoPointerType; typedef MeshType::CellType CellType; typedef itk::TriangleCell<CellType> TriangleCellType; typedef MeshType::PointIdentifier PointIdentifierType; typedef MeshType::CellIdentifier CellIdentifierType; // Read the number of polygons CellIdentifierType numberOfPolygons = 0; numberOfPolygons = polydata->GetNumberOfPolys(); vtkCellArray *polys = polydata->GetPolys(); PointIdentifierType numberOfCellPoints = 3; CellIdentifierType i = 0; for (i = 0; i < numberOfPolygons; i++) { vtkIdList *cellIds; vtkCell *vcell = polydata->GetCell(i); cellIds = vcell->GetPointIds(); CellAutoPointerType cell; TriangleCellType *triangleCell = new TriangleCellType; PointIdentifierType k; for (k = 0; k < numberOfCellPoints; k++) { triangleCell->SetPointId(k, cellIds->GetId(k)); } cell.TakeOwnership(triangleCell); mesh->SetCell(i, cell); } if (!mesh->GetNumberOfPoints()) mitkThrow() << "Generated itk mesh is empty."; if (m_MakeOutputBinary) { this->SurfaceStampBinaryOutputProcessing(mesh); } else { AccessFixedDimensionByItk_1(inputImage, SurfaceStampProcessing, 3, mesh); } } void mitk::SurfaceStampImageFilter::SurfaceStampBinaryOutputProcessing(MeshType *mesh) { mitk::Image *inputImage = const_cast<mitk::Image *>(this->GetInput()); mitk::Image::Pointer outputImage = this->GetOutput(); typedef itk::Image<unsigned char, 3> BinaryImageType; BinaryImageType::Pointer itkInputImage; mitk::CastToItkImage(inputImage, itkInputImage); typedef itk::TriangleMeshToBinaryImageFilter<MeshType, BinaryImageType> FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(mesh); filter->SetInfoImage(itkInputImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); BinaryImageType::Pointer resultImage = filter->GetOutput(); resultImage->DisconnectPipeline(); mitk::CastToMitkImage(resultImage, outputImage); } template <typename TPixel> void mitk::SurfaceStampImageFilter::SurfaceStampProcessing(itk::Image<TPixel, 3> *input, MeshType *mesh) { typedef itk::Image<TPixel, 3> ImageType; typedef itk::Image<unsigned char, 3> BinaryImageType; typedef itk::TriangleMeshToBinaryImageFilter<mitk::SurfaceStampImageFilter::MeshType, BinaryImageType> FilterType; BinaryImageType::Pointer binaryInput = BinaryImageType::New(); binaryInput->SetSpacing(input->GetSpacing()); binaryInput->SetOrigin(input->GetOrigin()); binaryInput->SetDirection(input->GetDirection()); binaryInput->SetRegions(input->GetLargestPossibleRegion()); binaryInput->Allocate(); binaryInput->FillBuffer(0); FilterType::Pointer filter = FilterType::New(); filter->SetInput(mesh); filter->SetInfoImage(binaryInput); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); BinaryImageType::Pointer resultImage = filter->GetOutput(); resultImage->DisconnectPipeline(); mitk::Image::Pointer outputImage = this->GetOutput(); typename ImageType::Pointer itkOutputImage; mitk::CastToItkImage(outputImage, itkOutputImage); typedef itk::ImageRegionConstIterator<BinaryImageType> BinaryIteratorType; typedef itk::ImageRegionConstIterator<ImageType> InputIteratorType; typedef itk::ImageRegionIterator<ImageType> OutputIteratorType; BinaryIteratorType sourceIter(resultImage, resultImage->GetLargestPossibleRegion()); sourceIter.GoToBegin(); InputIteratorType inputIter(input, input->GetLargestPossibleRegion()); inputIter.GoToBegin(); OutputIteratorType outputIter(itkOutputImage, itkOutputImage->GetLargestPossibleRegion()); outputIter.GoToBegin(); typename ImageType::PixelType inputValue; unsigned char sourceValue; typename ImageType::PixelType fgValue = static_cast<typename ImageType::PixelType>(m_ForegroundValue); typename ImageType::PixelType bgValue = static_cast<typename ImageType::PixelType>(m_BackgroundValue); while (!sourceIter.IsAtEnd()) { sourceValue = static_cast<unsigned char>(sourceIter.Get()); inputValue = static_cast<typename ImageType::PixelType>(inputIter.Get()); if (sourceValue != 0) outputIter.Set(fgValue); else if (m_OverwriteBackground) outputIter.Set(bgValue); else outputIter.Set(inputValue); ++sourceIter; ++inputIter; ++outputIter; } } diff --git a/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp index cbab64ee57..2dd9a5a8ac 100644 --- a/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp +++ b/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp @@ -1,446 +1,446 @@ /*=================================================================== 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 "mitkSliceBasedInterpolationController.h" #include "mitkExtractSliceFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkImageTimeSelector.h" #include "mitkShapeBasedInterpolationAlgorithm.h" #include <itkCommand.h> #include <itkImage.h> #include <itkImageSliceConstIteratorWithIndex.h> mitk::SliceBasedInterpolationController::InterpolatorMapType mitk::SliceBasedInterpolationController::s_InterpolatorForImage; // static member initialization mitk::SliceBasedInterpolationController *mitk::SliceBasedInterpolationController::InterpolatorForImage( const Image *image) { InterpolatorMapType::iterator iter = s_InterpolatorForImage.find(image); if (iter != s_InterpolatorForImage.end()) { return iter->second; } else { return nullptr; } } mitk::SliceBasedInterpolationController::SliceBasedInterpolationController() : m_WorkingImage(nullptr), m_ReferenceImage(nullptr) { } mitk::SliceBasedInterpolationController::~SliceBasedInterpolationController() { // remove this from the list of interpolators for (InterpolatorMapType::iterator iter = s_InterpolatorForImage.begin(); iter != s_InterpolatorForImage.end(); ++iter) { if (iter->second == this) { s_InterpolatorForImage.erase(iter); break; } } } void mitk::SliceBasedInterpolationController::ResetLabelCount() { m_LabelCountInSlice.clear(); int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); m_LabelCountInSlice.resize(m_WorkingImage->GetTimeSteps()); for (unsigned int timeStep = 0; timeStep < m_WorkingImage->GetTimeSteps(); ++timeStep) { m_LabelCountInSlice[timeStep].resize(3); for (unsigned int dim = 0; dim < 3; ++dim) { m_LabelCountInSlice[timeStep][dim].clear(); m_LabelCountInSlice[timeStep][dim].resize(m_WorkingImage->GetDimension(dim)); for (unsigned int slice = 0; slice < m_WorkingImage->GetDimension(dim); ++slice) { m_LabelCountInSlice[timeStep][dim][slice].clear(); m_LabelCountInSlice[timeStep][dim][slice].resize(numberOfLabels); m_LabelCountInSlice[timeStep][dim][slice].assign(numberOfLabels, 0); } } } } void mitk::SliceBasedInterpolationController::SetWorkingImage(LabelSetImage *newImage) { if (m_WorkingImage != newImage) { // delete the current working image from the list of interpolators InterpolatorMapType::iterator iter = s_InterpolatorForImage.find(m_WorkingImage); if (iter != s_InterpolatorForImage.end()) { s_InterpolatorForImage.erase(iter); } m_WorkingImage = newImage; s_InterpolatorForImage.insert(std::make_pair(m_WorkingImage, this)); } this->ResetLabelCount(); AccessFixedDimensionByItk_1(m_WorkingImage, ScanImageITKProcessing, 3, 0); // for all timesteps, scan whole image: TODO: enable this again for 3D+time /* for (unsigned int timeStep = 0; timeStep < m_WorkingImage->GetTimeSteps(); ++timeStep) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( m_WorkingImage ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer segmentation3D = timeSelector->GetOutput(); this->SetChangedVolume( segmentation3D.GetPointer(), timeStep ); } */ // this->Modified(); } void mitk::SliceBasedInterpolationController::SetReferenceImage(Image *newImage) { if (!newImage) return; m_ReferenceImage = newImage; // ensure the reference image has the same dimensionality and extents as the segmentation image if (m_WorkingImage.IsNull() || m_ReferenceImage->GetDimension() != m_WorkingImage->GetDimension() || m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 || m_WorkingImage->GetPixelType().GetNumberOfComponents() != 1) { MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl; m_ReferenceImage = nullptr; return; } for (unsigned int dim = 0; dim < m_WorkingImage->GetDimension(); ++dim) { if (m_ReferenceImage->GetDimension(dim) != m_WorkingImage->GetDimension(dim)) { MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim << "), ignoring patient image" << std::endl; m_ReferenceImage = nullptr; return; } } } void mitk::SliceBasedInterpolationController::SetChangedSlice(const Image *slice, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep) { if (!slice) return; if (slice->GetDimension() != 2) return; if (sliceDimension > 2) return; if (m_WorkingImage.IsNull()) return; // check if the number of labels has changed int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); if (m_LabelCountInSlice[0][0][0].size() != numberOfLabels) return; unsigned int dim0(0); unsigned int dim1(1); // determine the other two dimensions switch (sliceDimension) { default: case 2: dim0 = 0; dim1 = 1; break; case 1: dim0 = 0; dim1 = 2; break; case 0: dim0 = 1; dim1 = 2; break; } AccessFixedDimensionByItk_1( slice, ScanSliceITKProcessing, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep)); // this->Modified(); } template <typename PixelType> void mitk::SliceBasedInterpolationController::ScanSliceITKProcessing(const itk::Image<PixelType, 2> *input, const SetChangedSliceOptions &options) { unsigned int timeStep = options.timeStep; unsigned int sliceDimension = options.sliceDimension; unsigned int sliceIndex = options.sliceIndex; if (sliceDimension > 2) return; if (sliceIndex >= m_LabelCountInSlice[timeStep][sliceDimension].size()) return; unsigned int dim0(options.dim0); unsigned int dim1(options.dim1); std::vector<int> numberOfPixels; // number of pixels in the current slice that are equal to the active label - int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); + unsigned int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); numberOfPixels.resize(numberOfLabels); typedef itk::Image<PixelType, 2> ImageType; typedef itk::ImageRegionConstIteratorWithIndex<ImageType> IteratorType; IteratorType iter(input, input->GetLargestPossibleRegion()); iter.GoToBegin(); typename IteratorType::IndexType index; while (!iter.IsAtEnd()) { index = iter.GetIndex(); int value = static_cast<int>(iter.Get()); ++m_LabelCountInSlice[timeStep][dim0][index[0]][value]; ++m_LabelCountInSlice[timeStep][dim1][index[1]][value]; ++numberOfPixels[value]; ++iter; } for (unsigned int label = 0; label < numberOfLabels; ++label) { m_LabelCountInSlice[timeStep][sliceDimension][sliceIndex][label] = numberOfPixels[label]; } } template <typename TPixel, unsigned int VImageDimension> void mitk::SliceBasedInterpolationController::ScanImageITKProcessing(itk::Image<TPixel, VImageDimension> *input, unsigned int timeStep) { typedef itk::ImageSliceConstIteratorWithIndex<itk::Image<TPixel, VImageDimension>> IteratorType; IteratorType iter(input, input->GetLargestPossibleRegion()); iter.SetFirstDirection(0); iter.SetSecondDirection(1); typename IteratorType::IndexType index; unsigned int x = 0; unsigned int y = 0; unsigned int z = 0; std::vector<int> numberOfPixels; // number of pixels per slice that are equal to the active label - int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); + unsigned int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); numberOfPixels.resize(numberOfLabels); iter.GoToBegin(); while (!iter.IsAtEnd()) { while (!iter.IsAtEndOfSlice()) { while (!iter.IsAtEndOfLine()) { index = iter.GetIndex(); x = index[0]; y = index[1]; z = index[2]; int value = static_cast<unsigned int>(iter.Get()); ++m_LabelCountInSlice[timeStep][0][x][value]; ++m_LabelCountInSlice[timeStep][1][y][value]; ++numberOfPixels[value]; ++iter; } iter.NextLine(); } for (unsigned int label = 0; label < numberOfLabels; ++label) m_LabelCountInSlice[timeStep][2][z][label] += numberOfPixels[label]; // clear label counter numberOfPixels.assign(numberOfLabels, 0); iter.NextSlice(); } } mitk::Image::Pointer mitk::SliceBasedInterpolationController::Interpolate(unsigned int sliceDimension, unsigned int sliceIndex, const mitk::PlaneGeometry *currentPlane, unsigned int timeStep) { if (m_WorkingImage.IsNull()) return nullptr; if (!currentPlane) return nullptr; if (timeStep >= m_LabelCountInSlice.size()) return nullptr; if (sliceDimension > 2) return nullptr; - int upperLimit = m_LabelCountInSlice[timeStep][sliceDimension].size(); + unsigned int upperLimit = m_LabelCountInSlice[timeStep][sliceDimension].size(); if (sliceIndex >= upperLimit - 1) return nullptr; // can't interpolate first and last slice if (sliceIndex < 1) return nullptr; int pixelValue = m_WorkingImage->GetActiveLabel()->GetValue(); // slice contains a segmentation, won't interpolate anything then if (m_LabelCountInSlice[timeStep][sliceDimension][sliceIndex][pixelValue] > 0) return nullptr; - int lowerBound(0); - int upperBound(0); + unsigned int lowerBound(0); + unsigned int upperBound(0); bool bounds(false); for (lowerBound = sliceIndex - 1; /*lowerBound >= 0*/; --lowerBound) { if (m_LabelCountInSlice[timeStep][sliceDimension][lowerBound][pixelValue] > 0) { bounds = true; break; } if (lowerBound == 0) break; } if (!bounds) return nullptr; bounds = false; for (upperBound = sliceIndex + 1; upperBound < upperLimit; ++upperBound) { if (m_LabelCountInSlice[timeStep][sliceDimension][upperBound][pixelValue] > 0) { bounds = true; break; } } if (!bounds) return nullptr; // ok, we have found two neighboring slices with the active label // (and we made sure that the current slice does NOT contain the active label // Setting up the ExtractSliceFilter mitk::ExtractSliceFilter::Pointer extractor = ExtractSliceFilter::New(); extractor->SetInput(m_WorkingImage); extractor->SetTimeStep(timeStep); extractor->SetResliceTransformByGeometry(m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); extractor->SetVtkOutputRequest(false); // Reslicing the current plane extractor->SetWorldGeometry(currentPlane); extractor->Modified(); try { extractor->Update(); } catch (const std::exception &e) { MITK_ERROR << "Error in 2D interpolation: " << e.what(); return nullptr; } mitk::Image::Pointer resultImage = extractor->GetOutput(); resultImage->DisconnectPipeline(); // Creating PlaneGeometry for lower slice mitk::PlaneGeometry::Pointer reslicePlane = currentPlane->Clone(); // Transforming the current origin so that it matches the lower slice mitk::Point3D origin = currentPlane->GetOrigin(); m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin); origin[sliceDimension] = lowerBound; m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); // Extract the lower slice extractor->SetWorldGeometry(reslicePlane); extractor->Modified(); try { extractor->Update(); } catch (const std::exception &e) { MITK_ERROR << "Error in 2D interpolation: " << e.what(); return nullptr; } mitk::Image::Pointer lowerMITKSlice = extractor->GetOutput(); lowerMITKSlice->DisconnectPipeline(); // Transforming the current origin so that it matches the upper slice m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin); origin[sliceDimension] = upperBound; m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); // Extract the upper slice extractor->SetWorldGeometry(reslicePlane); extractor->Modified(); try { extractor->Update(); } catch (const std::exception &e) { MITK_ERROR << "Error in 2D interpolation: " << e.what(); return nullptr; } mitk::Image::Pointer upperMITKSlice = extractor->GetOutput(); upperMITKSlice->DisconnectPipeline(); if (lowerMITKSlice.IsNull() || upperMITKSlice.IsNull()) return nullptr; // interpolation algorithm gets some inputs // two segmentations (guaranteed to be of the same data type, but no special data type guaranteed) // orientation (sliceDimension) of the segmentations // position of the two slices (sliceIndices) // one volume image (original patient image) // // interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to // inspect the original patient image at appropriate positions mitk::SegmentationInterpolationAlgorithm::Pointer algorithm = mitk::ShapeBasedInterpolationAlgorithm::New().GetPointer(); algorithm->Interpolate( lowerMITKSlice.GetPointer(), lowerBound, upperMITKSlice.GetPointer(), upperBound, sliceIndex, 0, resultImage); return resultImage; } diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index 21a4a4135c..6d9703b5d4 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,671 +1,669 @@ /*=================================================================== 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 <itkGradientMagnitudeImageFilter.h> #include <mitkBaseRenderer.h> #include <mitkContour.h> #include <mitkContourModelUtils.h> #include <mitkContourUtils.h> #include <mitkInteractionConst.h> #include <mitkRenderingManager.h> #include <mitkToolManager.h> #include <usGetModuleContext.h> #include <usModule.h> #include <usModuleContext.h> #include <usModuleResource.h> #include "mitkLiveWireTool2D.h" #include "mitkLiveWireTool2D.xpm" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LiveWireTool2D, "LiveWire tool"); } class RemoveFromDataStorage { public: RemoveFromDataStorage(mitk::DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) {} void operator()(mitk::DataNode *dataNode) { m_DataStorage->Remove(dataNode); } void operator()(const std::pair<mitk::DataNode::Pointer, mitk::PlaneGeometry::Pointer> &dataNode) { m_DataStorage->Remove(dataNode.first); } private: mitk::DataStorage::Pointer m_DataStorage; }; mitk::LiveWireTool2D::LiveWireTool2D() : SegTool2D("LiveWireTool"), m_PlaneGeometry(nullptr) { } mitk::LiveWireTool2D::~LiveWireTool2D() { this->ClearSegmentation(); } void mitk::LiveWireTool2D::RemoveHelperObjects() { DataStorage *dataStorage = m_ToolManager->GetDataStorage(); if (!m_EditingContours.empty()) std::for_each(m_EditingContours.begin(), m_EditingContours.end(), RemoveFromDataStorage(dataStorage)); if (!m_WorkingContours.empty()) std::for_each(m_WorkingContours.begin(), m_WorkingContours.end(), RemoveFromDataStorage(dataStorage)); if (m_EditingContourNode.IsNotNull()) dataStorage->Remove(m_EditingContourNode); if (m_LiveWireContourNode.IsNotNull()) dataStorage->Remove(m_LiveWireContourNode); if (m_ContourModelNode.IsNotNull()) dataStorage->Remove(m_ContourModelNode); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::LiveWireTool2D::ReleaseHelperObjects() { this->RemoveHelperObjects(); if (!m_EditingContours.empty()) m_EditingContours.clear(); if (!m_WorkingContours.empty()) m_WorkingContours.clear(); m_EditingContourNode = nullptr; m_EditingContour = nullptr; m_LiveWireContourNode = nullptr; m_LiveWireContour = nullptr; m_ContourModelNode = nullptr; m_Contour = nullptr; } void mitk::LiveWireTool2D::ReleaseInteractors() { this->EnableContourLiveWireInteraction(false); m_LiveWireNodes.clear(); } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { CONNECT_CONDITION("CheckContourClosed", OnCheckPoint); CONNECT_FUNCTION("InitObject", OnInitLiveWire); CONNECT_FUNCTION("AddPoint", OnAddPoint); CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); CONNECT_FUNCTION("MovePoint", OnMouseMoveNoDynamicCosts); CONNECT_FUNCTION("FinishContour", OnFinish); CONNECT_FUNCTION("DeletePoint", OnLastSegmentDelete); CONNECT_FUNCTION("CtrlMovePoint", OnMouseMoved); } const char **mitk::LiveWireTool2D::GetXPM() const { return mitkLiveWireTool2D_xpm; } us::ModuleResource mitk::LiveWireTool2D::GetIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_48x48.png"); } us::ModuleResource mitk::LiveWireTool2D::GetCursorIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_Cursor_32x32.png"); } const char *mitk::LiveWireTool2D::GetName() const { return "Live Wire"; } void mitk::LiveWireTool2D::Activated() { Superclass::Activated(); this->ResetToStartState(); this->EnableContourLiveWireInteraction(true); } void mitk::LiveWireTool2D::Deactivated() { this->ConfirmSegmentation(); Superclass::Deactivated(); } void mitk::LiveWireTool2D::EnableContourLiveWireInteraction(bool on) { for (auto interactor : m_LiveWireNodes) { if (on) interactor->EnableInteraction(true); else interactor->EnableInteraction(false); } } void mitk::LiveWireTool2D::ConfirmSegmentation() { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; Image *workingImage = dynamic_cast<Image *>(workingNode->GetData()); if (!workingImage) return; // for all contours in list (currently created by tool) std::vector<std::pair<mitk::DataNode::Pointer, mitk::PlaneGeometry::Pointer>>::iterator itWorkingContours = this->m_WorkingContours.begin(); std::vector<SliceInformation> sliceList; sliceList.reserve(m_WorkingContours.size()); while (itWorkingContours != this->m_WorkingContours.end()) { // if node contains data if (itWorkingContours->first->GetData()) { // if this is a contourModel mitk::ContourModel *contourModel = dynamic_cast<mitk::ContourModel *>(itWorkingContours->first->GetData()); if (contourModel) { // for each timestep of this contourModel for (TimeStepType currentTimestep = 0; currentTimestep < contourModel->GetTimeGeometry()->CountTimeSteps(); ++currentTimestep) { // get the segmentation image slice at current timestep mitk::Image::Pointer workingSlice = this->GetAffectedImageSliceAs2DImage(itWorkingContours->second, workingImage, currentTimestep); mitk::ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contourModel, true, false); mitk::ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, 1.0); // write back to image volume SliceInformation sliceInfo(workingSlice, itWorkingContours->second, currentTimestep); sliceList.push_back(sliceInfo); this->WriteSliceToVolume(sliceInfo); } } } ++itWorkingContours; } this->WriteBackSegmentationResult(sliceList, false); this->ClearSegmentation(); } void mitk::LiveWireTool2D::ClearSegmentation() { this->ReleaseHelperObjects(); this->ReleaseInteractors(); this->ResetToStartState(); } bool mitk::LiveWireTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent *positionEvent, mitk::BaseData *data) { bool IsPositionEventInsideImageRegion = data != nullptr && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); if (!IsPositionEventInsideImageRegion) { MITK_WARN("LiveWireTool2D") << "PositionEvent is outside ImageRegion!"; return false; } return true; } void mitk::LiveWireTool2D::OnInitLiveWire(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent); if (!positionEvent) return; mitk::DataNode *workingDataNode = m_ToolManager->GetWorkingData(0); if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) { this->ResetToStartState(); return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timestep = positionEvent->GetSender()->GetTimeStep(); m_Contour = mitk::ContourModel::New(); m_Contour->Expand(timestep + 1); m_ContourModelNode = mitk::DataNode::New(); m_ContourModelNode->SetData(m_Contour); m_ContourModelNode->SetName("working contour node"); m_ContourModelNode->SetProperty("layer", IntProperty::New(100)); m_ContourModelNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_ContourModelNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ContourModelNode->AddProperty("contour.color", ColorProperty::New(1, 1, 0), nullptr, true); m_ContourModelNode->AddProperty("contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), nullptr, true); m_ContourModelNode->AddProperty("contour.controlpoints.show", BoolProperty::New(true), nullptr, true); m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContour->Expand(timestep + 1); m_LiveWireContourNode = mitk::DataNode::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); m_LiveWireContourNode->SetName("active livewire node"); m_LiveWireContourNode->SetProperty("layer", IntProperty::New(101)); m_LiveWireContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_LiveWireContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_LiveWireContourNode->AddProperty("contour.color", ColorProperty::New(0.1, 1.0, 0.1), nullptr, true); m_LiveWireContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0), nullptr, true); m_EditingContour = mitk::ContourModel::New(); m_EditingContour->Expand(timestep + 1); m_EditingContourNode = mitk::DataNode::New(); m_EditingContourNode->SetData(m_EditingContour); m_EditingContourNode->SetName("editing node"); m_EditingContourNode->SetProperty("layer", IntProperty::New(102)); m_EditingContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_EditingContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_EditingContourNode->AddProperty("contour.color", ColorProperty::New(0.1, 1.0, 0.1), nullptr, true); m_EditingContourNode->AddProperty("contour.points.color", ColorProperty::New(0.0, 0.0, 1.0), nullptr, true); m_EditingContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0), nullptr, true); m_ToolManager->GetDataStorage()->Add(m_ContourModelNode, workingDataNode); m_ToolManager->GetDataStorage()->Add(m_LiveWireContourNode, workingDataNode); m_ToolManager->GetDataStorage()->Add(m_EditingContourNode, workingDataNode); // set current slice as input for ImageToLiveWireContourFilter m_WorkingSlice = this->GetAffectedReferenceSlice(positionEvent); mitk::Point3D newOrigin = m_WorkingSlice->GetSlicedGeometry()->GetOrigin(); m_WorkingSlice->GetSlicedGeometry()->WorldToIndex(newOrigin, newOrigin); m_WorkingSlice->GetSlicedGeometry()->IndexToWorld(newOrigin, newOrigin); m_WorkingSlice->GetSlicedGeometry()->SetOrigin(newOrigin); m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetInput(m_WorkingSlice); // map click to pixel coordinates mitk::Point3D click = positionEvent->GetPositionInWorld(); itk::Index<3> idx; m_WorkingSlice->GetGeometry()->WorldToIndex(click, idx); // get the pixel the gradient in region of 5x5 itk::Index<3> indexWithHighestGradient; AccessFixedDimensionByItk_2(m_WorkingSlice, FindHighestGradientMagnitudeByITK, 2, idx, indexWithHighestGradient); // itk::Index to mitk::Point3D click[0] = indexWithHighestGradient[0]; click[1] = indexWithHighestGradient[1]; click[2] = indexWithHighestGradient[2]; m_WorkingSlice->GetGeometry()->IndexToWorld(click, click); // set initial start point m_Contour->AddVertex(click, true, timestep); m_LiveWireFilter->SetStartPoint(click); // remember plane geometry to determine if events were triggered in same plane m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); m_CreateAndUseDynamicCosts = true; // render assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { // complete LiveWire interaction for last segment // add current LiveWire contour to the finished contour and reset // to start new segment and computation mitk::InteractionPositionEvent *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent); if (!positionEvent) return; if (m_PlaneGeometry != nullptr) { // this checks that the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) { return; } } int timestep = positionEvent->GetSender()->GetTimeStep(); // add repulsive points to avoid to get the same path again typedef mitk::ImageLiveWireContourModelFilter::InternalImageType::IndexType IndexType; mitk::ContourModel::ConstVertexIterator iter = m_LiveWireContour->IteratorBegin(timestep); for (; iter != m_LiveWireContour->IteratorEnd(timestep); iter++) { IndexType idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); } // remove duplicate first vertex, it's already contained in m_Contour m_LiveWireContour->RemoveVertexAt(0, timestep); // set last added point as control point m_LiveWireContour->SetControlVertexAt(m_LiveWireContour->GetNumberOfVertices(timestep) - 1, timestep); // merge contours m_Contour->Concatenate(m_LiveWireContour, timestep); // clear the livewire contour and reset the corresponding datanode m_LiveWireContour->Clear(timestep); // set new start point m_LiveWireFilter->SetStartPoint(positionEvent->GetPositionInWorld()); if (m_CreateAndUseDynamicCosts) { // use dynamic cost map for next update m_LiveWireFilter->CreateDynamicCostMap(m_Contour); m_LiveWireFilter->SetUseDynamicCostMap(true); // m_CreateAndUseDynamicCosts = false; } // render assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { // compute LiveWire segment from last control point to current mouse position mitk::InteractionPositionEvent *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent); if (!positionEvent) return; // actual LiveWire computation int timestep = positionEvent->GetSender()->GetTimeStep(); m_LiveWireFilter->SetEndPoint(positionEvent->GetPositionInWorld()); m_LiveWireFilter->SetTimeStep(timestep); m_LiveWireFilter->Update(); m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); m_LiveWireContourNode->SetData(this->m_LiveWireContour); // render assert(positionEvent->GetSender()->GetRenderWindow()); positionEvent->GetSender()->GetRenderingManager()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent) { // do not use dynamic cost map m_LiveWireFilter->SetUseDynamicCostMap(false); OnMouseMoved(nullptr, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); } bool mitk::LiveWireTool2D::OnCheckPoint(const InteractionEvent *interactionEvent) { // check double click on first control point to finish the LiveWire tool // // Check distance to first point. // Transition YES if click close to first control point // const mitk::InteractionPositionEvent *positionEvent = dynamic_cast<const mitk::InteractionPositionEvent *>(interactionEvent); if (positionEvent) { int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::Point3D click = positionEvent->GetPositionInWorld(); mitk::Point3D first = this->m_Contour->GetVertexAt(0, timestep)->Coordinates; if (first.EuclideanDistanceTo(click) < 4.5) { // allow to finish return true; } else { return false; } } return false; } void mitk::LiveWireTool2D::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) { // finish livewire tool interaction mitk::InteractionPositionEvent *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent); if (!positionEvent) return; // Have to do that here so that the m_LastEventSender is set correctly mitk::SegTool2D::AddContourmarker(); // actual timestep int timestep = positionEvent->GetSender()->GetTimeStep(); // remove last control point being added by double click m_Contour->RemoveVertexAt(m_Contour->GetNumberOfVertices(timestep) - 1, timestep); // save contour and corresponding plane geometry to list std::pair<mitk::DataNode::Pointer, mitk::PlaneGeometry::Pointer> cp( m_ContourModelNode, (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone().GetPointer())); this->m_WorkingContours.push_back(cp); std::pair<mitk::DataNode::Pointer, mitk::PlaneGeometry::Pointer> ecp( m_EditingContourNode, (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone().GetPointer())); this->m_EditingContours.push_back(ecp); m_LiveWireFilter->SetUseDynamicCostMap(false); this->FinishTool(); } void mitk::LiveWireTool2D::FinishTool() { int numberOfTimesteps = static_cast<int>(m_Contour->GetTimeGeometry()->CountTimeSteps()); // close contour in each timestep - for (unsigned int i = 0; i <= numberOfTimesteps; i++) - { + for (int i = 0; i <= numberOfTimesteps; i++) m_Contour->Close(i); - } m_ToolManager->GetDataStorage()->Remove(m_LiveWireContourNode); // clear live wire contour node m_LiveWireContourNode = nullptr; m_LiveWireContour = nullptr; // A new ContourModelLiveWireInteractor is created that will listen to new events // set the livewire interactor to edit control points m_ContourInteractor = mitk::ContourModelLiveWireInteractor::New(); m_ContourInteractor->SetDataNode(m_ContourModelNode); // TODO load statemachine and config m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); // Set the configuration file that defines the triggers for the transitions m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); m_ContourInteractor->SetWorkingImage(this->m_WorkingSlice); m_ContourInteractor->SetEditingContourModelNode(this->m_EditingContourNode); m_ContourModelNode->SetDataInteractor(m_ContourInteractor.GetPointer()); this->m_LiveWireNodes.push_back(m_ContourInteractor); } void mitk::LiveWireTool2D::OnLastSegmentDelete(StateMachineAction *, InteractionEvent *interactionEvent) { int timestep = interactionEvent->GetSender()->GetTimeStep(); // if last point of current contour will be removed go to start state and remove nodes if (m_Contour->GetNumberOfVertices(timestep) <= 1) { m_ToolManager->GetDataStorage()->Remove(m_LiveWireContourNode); m_ToolManager->GetDataStorage()->Remove(m_ContourModelNode); m_ToolManager->GetDataStorage()->Remove(m_EditingContourNode); m_LiveWireContour = mitk::ContourModel::New(); m_Contour = mitk::ContourModel::New(); m_ContourModelNode->SetData(m_Contour); m_LiveWireContourNode->SetData(m_LiveWireContour); this->ResetToStartState(); // go to start state } else // remove last segment from contour and reset livewire contour { m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(m_Contour->GetTimeSteps()); mitk::ContourModel::VertexIterator begin = m_Contour->IteratorBegin(); // iterate from last point to next active point mitk::ContourModel::VertexIterator newLast = m_Contour->IteratorBegin() + (m_Contour->GetNumberOfVertices() - 1); // go at least one down if (newLast != begin) { newLast--; } // search next active control point while (newLast != begin && !((*newLast)->IsControlPoint)) { newLast--; } // set position of start point for livewire filter to coordinates of the new last point m_LiveWireFilter->SetStartPoint((*newLast)->Coordinates); mitk::ContourModel::VertexIterator it = m_Contour->IteratorBegin(); // fill new Contour while (it <= newLast) { newContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } newContour->SetClosed(m_Contour->IsClosed()); // set new contour visible m_ContourModelNode->SetData(newContour); m_Contour = newContour; assert(interactionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } } template <typename TPixel, unsigned int VImageDimension> void mitk::LiveWireTool2D::FindHighestGradientMagnitudeByITK(itk::Image<TPixel, VImageDimension> *inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex) { typedef itk::Image<TPixel, VImageDimension> InputImageType; typedef typename InputImageType::IndexType IndexType; unsigned long xMAX = inputImage->GetLargestPossibleRegion().GetSize()[0]; unsigned long yMAX = inputImage->GetLargestPossibleRegion().GetSize()[1]; returnIndex[0] = index[0]; returnIndex[1] = index[1]; returnIndex[2] = 0.0; double gradientMagnitude = 0.0; double maxGradientMagnitude = 0.0; /* the size and thus the region of 7x7 is only used to calculate the gradient magnitude in that region not for searching the maximum value */ // maximum value in each direction for size typename InputImageType::SizeType size; size[0] = 7; size[1] = 7; // minimum value in each direction for startRegion IndexType startRegion; startRegion[0] = index[0] - 3; startRegion[1] = index[1] - 3; if (startRegion[0] < 0) startRegion[0] = 0; if (startRegion[1] < 0) startRegion[1] = 0; if (xMAX - index[0] < 7) startRegion[0] = xMAX - 7; if (yMAX - index[1] < 7) startRegion[1] = yMAX - 7; index[0] = startRegion[0] + 3; index[1] = startRegion[1] + 3; typename InputImageType::RegionType region; region.SetSize(size); region.SetIndex(startRegion); typedef typename itk::GradientMagnitudeImageFilter<InputImageType, InputImageType> GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(inputImage); gradientFilter->GetOutput()->SetRequestedRegion(region); gradientFilter->Update(); typename InputImageType::Pointer gradientMagnImage; gradientMagnImage = gradientFilter->GetOutput(); IndexType currentIndex; currentIndex[0] = 0; currentIndex[1] = 0; // search max (approximate) gradient magnitude for (int x = -1; x <= 1; ++x) { currentIndex[0] = index[0] + x; for (int y = -1; y <= 1; ++y) { currentIndex[1] = index[1] + y; gradientMagnitude = gradientMagnImage->GetPixel(currentIndex); // check for new max if (maxGradientMagnitude < gradientMagnitude) { maxGradientMagnitude = gradientMagnitude; returnIndex[0] = currentIndex[0]; returnIndex[1] = currentIndex[1]; returnIndex[2] = 0.0; } // end if } // end for y currentIndex[1] = index[1]; } // end for x } diff --git a/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp index 72e9effa03..c8866a2fdc 100644 --- a/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp +++ b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp @@ -1,163 +1,163 @@ /*=================================================================== 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 "mitkContourSetVtkMapper3D.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include <vtkActor.h> #include <vtkActor.h> #include <vtkAppendPolyData.h> #include <vtkAssembly.h> #include <vtkCellArray.h> #include <vtkFollower.h> #include <vtkLinearTransform.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper.h> #include <vtkPolygon.h> #include <vtkProp3DCollection.h> #include <vtkRenderer.h> #include <vtkTubeFilter.h> #include <stdlib.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> mitk::ContourSetVtkMapper3D::ContourSetVtkMapper3D() { m_VtkPolyDataMapper = vtkPolyDataMapper::New(); m_Actor = vtkActor::New(); m_Actor->SetMapper(m_VtkPolyDataMapper); m_ContourSet = vtkPolyData::New(); m_TubeFilter = vtkTubeFilter::New(); } mitk::ContourSetVtkMapper3D::~ContourSetVtkMapper3D() { if (m_VtkPolyDataMapper) m_VtkPolyDataMapper->Delete(); ; if (m_TubeFilter) m_TubeFilter->Delete(); ; if (m_ContourSet) m_ContourSet->Delete(); ; if (m_Actor) m_Actor->Delete(); ; } vtkProp *mitk::ContourSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_Actor; } void mitk::ContourSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_Actor->VisibilityOff(); return; } m_Actor->VisibilityOn(); - if (renderer->GetCurrentWorldPlaneGeometryUpdateTime() > 0 < this->GetInput()->GetMTime()) + if (renderer->GetCurrentWorldPlaneGeometryUpdateTime() > 0UL && renderer->GetCurrentWorldPlaneGeometryUpdateTime() < this->GetInput()->GetMTime()) { m_ContourSet = vtkPolyData::New(); vtkPoints *points = vtkPoints::New(); vtkCellArray *lines = vtkCellArray::New(); mitk::ContourSet::Pointer input = const_cast<mitk::ContourSet *>(this->GetInput()); mitk::ContourSet::ContourVectorType contourVec = input->GetContours(); mitk::ContourSet::ContourIterator contourIt = contourVec.begin(); vtkIdType firstPointIndex = 0, lastPointIndex = 0; vtkIdType ptIndex = 0; while (contourIt != contourVec.end()) { mitk::Contour *nextContour = (mitk::Contour *)(*contourIt).second; Contour::InputType idx = nextContour->GetContourPath()->StartOfInput(); Contour::InputType end = nextContour->GetContourPath()->EndOfInput(); if (end > 50000) end = 0; mitk::Contour::PointsContainerPointer contourPoints = nextContour->GetPoints(); mitk::Contour::PointsContainerIterator pointsIt = contourPoints->Begin(); unsigned int counter = 0; firstPointIndex = ptIndex; while (pointsIt != contourPoints->End()) { if (counter % 2 == 0) { Contour::BoundingBoxType::PointType point; point = pointsIt.Value(); points->InsertPoint(ptIndex, point[0], point[1], point[2]); if (ptIndex > firstPointIndex) { vtkIdType cell[2] = {ptIndex - 1, ptIndex}; lines->InsertNextCell((vtkIdType)2, cell); } lastPointIndex = ptIndex; ptIndex++; } pointsIt++; idx += 1; } if (nextContour->GetClosed()) { vtkIdType cell[2] = {lastPointIndex, firstPointIndex}; lines->InsertNextCell((vtkIdType)2, cell); } contourIt++; } m_ContourSet->SetPoints(points); m_ContourSet->SetLines(lines); m_TubeFilter->SetInputData(m_ContourSet); m_TubeFilter->SetRadius(1); m_TubeFilter->Update(); m_VtkPolyDataMapper->SetInputConnection(m_TubeFilter->GetOutputPort()); double rgba[4] = {0.0f, 1.0f, 0.0f, 0.6f}; m_Actor->GetProperty()->SetColor(rgba); m_Actor->SetMapper(m_VtkPolyDataMapper); } SetVtkMapperImmediateModeRendering(m_VtkPolyDataMapper); } const mitk::ContourSet *mitk::ContourSetVtkMapper3D::GetInput() { return static_cast<const mitk::ContourSet *>(GetDataNode()->GetData()); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index 3c0b9c93b0..ac6d3e2e8c 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,969 +1,969 @@ /*=================================================================== 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 "QmitkAdaptiveRegionGrowingToolGUI.h" #include "QmitkStdMultiWidget.h" #include <qmessagebox.h> #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageStatisticsHolder.h" #include "itkMaskImageFilter.h" #include "itkNumericTraits.h" #include <itkBinaryThresholdImageFilter.h> #include <itkConnectedAdaptiveThresholdImageFilter.h> #include <itkImageIterator.h> #include <itkMinimumMaximumImageCalculator.h> #include "QmitkConfirmSegmentationDialog.h" #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImageCast.h" MITK_TOOL_GUI_MACRO(, QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget *parent) : QmitkToolGUI(), m_MultiWidget(nullptr), m_DataStorage(nullptr), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); // Not yet available // m_Controls.m_PreviewSlider->InvertedAppearance(true); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation", "RGResult", "RGFeedbackSurface", "maskedSegmentation"); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { // Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetMoveObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool *tool) { m_RegionGrow3DTool = dynamic_cast<mitk::AdaptiveRegionGrowingTool *>(tool); if (m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode(this->m_RegionGrow3DTool->GetReferenceData()); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); // Watch for point added or modified itk::SimpleMemberCommand<QmitkAdaptiveRegionGrowingToolGUI>::Pointer pointAddedCommand = itk::SimpleMemberCommand<QmitkAdaptiveRegionGrowingToolGUI>::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); m_PointSetMoveObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (imageNode.IsNotNull()) { m_DataStorage->Remove(imageNode); } mitk::DataNode::Pointer maskedSegmentationNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (maskedSegmentationNode.IsNotNull()) { m_DataStorage->Remove(maskedSegmentationNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { // Connecting GUI components connect((QObject *)(m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect(m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect((QObject *)(m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect((QObject *)(m_Controls.m_cbVolumeRendering), SIGNAL(toggled(bool)), this, SLOT(UseVolumeRendering(bool))); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface, std::string maskedSegmentation) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; m_NAMEFORMASKEDSEGMENTATION = maskedSegmentation; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetMultiWidget(QmitkStdMultiWidget *multiWidget) { m_MultiWidget = multiWidget; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode *node) { m_InputImageNode = node; mitk::Image *inputImage = dynamic_cast<mitk::Image *>(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template <typename TPixel> static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image::Pointer im, mitk::Point3D p, int &val) { mitk::ImagePixelReadAccessor<TPixel, 3> access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode *node = m_RegionGrow3DTool->GetPointSetNode(); if (node != nullptr) { mitk::PointSet::Pointer pointSet = dynamic_cast<mitk::PointSet *>(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(nullptr, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); mitk::Image *image = dynamic_cast<mitk::Image *>(m_InputImageNode->GetData()); mitk::Point3D seedPoint = pointSet ->GetPointSet( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep()) ->GetPoints() ->ElementAt(0); if (image->GetGeometry()->IsInside(seedPoint)) mitkPixelTypeMultiplex3( AccessPixel, image->GetChannelDescriptor().GetPixelType(), image, seedPoint, m_SeedpointValue) else return; /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; itk::Index<3> currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos(0); image->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for (int i = runningIndex[0] - 2; i <= runningIndex[0] + 2; i++) { for (int j = runningIndex[1] - 2; j <= runningIndex[1] + 2; j++) { for (int k = runningIndex[2] - 2; k <= runningIndex[2] + 2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if (image->GetGeometry()->IsIndexInside(currentIndex)) { int component = 0; m_InputImageNode->GetIntProperty("Image.Displayed Component", component); mitkPixelTypeMultiplex4(mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, nullptr, currentIndex, pixelValues[pos]); pos++; } else { pixelValues[pos] = std::numeric_limits<long>::min(); pos++; } } } } // Now calculation mean of the pixelValues // Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits<long>::min()) { m_SeedPointValueMean += pixelValue; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean / numberOfValues; mitk::ScalarType var = 0; if (numberOfValues > 1) { for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits<mitk::ScalarType>::min()) { var += (pixelValue - m_SeedPointValueMean) * (pixelValue - m_SeedPointValueMean); } } var /= numberOfValues - 1; } mitk::ScalarType stdDev = sqrt(var); /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is * meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is * meanSeedValue+0.15*windowsSize */ mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType windowSize = max - min; windowSize = 0.15 * windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean - stdDev; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please insert a seed point inside the " "image.\n\nFirst press the \"Define Seed " "Point\" button,\nthen click left mouse " "button inside the image."); return; } // safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast<mitk::PointSet *>(node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep(); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); mitk::Image::Pointer orgImage = dynamic_cast<mitk::Image *>(m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); mitk::Image *timedImage = timeSelector->GetOutput(); AccessByItk_2(timedImage, StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { // QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); // QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor(); // reset cursor QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor(); // reset cursor } template <typename TPixel, unsigned int VImageDimension> void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image<TPixel, VImageDimension> *itkImage, mitk::BaseGeometry *imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image<TPixel, VImageDimension> InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter<InputImageType, InputImageType> RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::MinimumMaximumImageCalculator<InputImageType> MinMaxValueFilterType; typedef itk::BinaryThresholdImageFilter<InputImageType, InputImageType> ThresholdFilterType; typedef itk::MaskImageFilter<InputImageType, InputImageType, InputImageType> MaskImageFilterType; if (!imageGeometry->IsInside(seedPoint)) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information(nullptr, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex(seedPoint, seedIndex); // convert world coordinates to image indices if (m_SeedpointValue > m_UPPERTHRESHOLD || m_SeedpointValue < m_LOWERTHRESHOLD) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( nullptr, "Segmentation functionality", "The seed point is outside the defined thresholds! Please set a new seed point or adjust the thresholds."); MITK_INFO << "Mean: " << m_SeedPointValueMean; return; } // Setting the direction of the regiongrowing. For dark structures e.g. the lung the regiongrowing // is performed starting at the upper value going to the lower one regionGrower->SetGrowingDirectionIsUpwards(m_CurrentRGDirectionIsUpwards); regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); // In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. // Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter regionGrower->SetLower(m_LOWERTHRESHOLD - 1); regionGrower->SetUpper(m_UPPERTHRESHOLD + 1); try { regionGrower->Update(); } catch (itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch (...) { QMessageBox::critical(nullptr, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); // initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_SeedpointValue + resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if (m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean - 1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean + 1); } this->m_SliderInitialized = true; // create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(resultImage); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); // delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add(newNode, m_InputImageNode); typename InputImageType::Pointer inputImageItk; mitk::CastToItkImage<InputImageType>(resultImage, inputImageItk); // volume rendering preview masking typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(inputImageItk); thresholdFilter->SetInsideValue(1); thresholdFilter->SetOutsideValue(0); double sliderVal = this->m_Controls.m_PreviewSlider->value(); if (m_CurrentRGDirectionIsUpwards) { thresholdFilter->SetLowerThreshold(sliderVal); thresholdFilter->SetUpperThreshold(itk::NumericTraits<TPixel>::max()); } else { thresholdFilter->SetLowerThreshold(itk::NumericTraits<TPixel>::min()); thresholdFilter->SetUpperThreshold(sliderVal); } thresholdFilter->SetInPlace(false); typename MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(inputImageItk); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMask; mitk::CastToMitkImage<InputImageType>(maskFilter->GetOutput(), mitkMask); mitk::DataNode::Pointer maskedNode = mitk::DataNode::New(); maskedNode->SetData(mitkMask); // set some properties maskedNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORMASKEDSEGMENTATION)); maskedNode->SetProperty("helper object", mitk::BoolProperty::New(true)); maskedNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); maskedNode->SetProperty("layer", mitk::IntProperty::New(1)); maskedNode->SetProperty("opacity", mitk::FloatProperty::New(0.0)); // delete the old image, if there was one: mitk::DataNode::Pointer deprecatedMask = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); m_DataStorage->Remove(deprecatedMask); // now add result to data tree m_DataStorage->Add(maskedNode, m_InputImageNode); this->InitializeLevelWindow(); if (m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true; // reset first stored threshold value // Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { // get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); mitk::ScalarType *level = new mitk::ScalarType(0.0); mitk::ScalarType *window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); // get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue)-m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; // inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1; if (m_MultiWidget) { this->m_MultiWidget->levelWindowWidget->GetManager()->SetAutoTopMostImage(false); this->m_MultiWidget->levelWindowWidget->GetManager()->SetLevelWindowProperty( static_cast<mitk::LevelWindowProperty *>(newNode->GetProperty("levelwindow"))); } if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(*level + 0.5)); // lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { // do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // get the levelWindow associated with the preview mitk::ScalarType level; // = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType *window = new mitk::ScalarType(1); // adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(level - 0.5)); // lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { // moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { // moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { // get image node if (m_InputImageNode.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } // get image data mitk::Image::Pointer orgImage = dynamic_cast<mitk::Image *>(m_InputImageNode->GetData()); if (orgImage.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Image found!"); return; } // get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image *)m_DataStorage->GetNamedObject<mitk::Image>(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (labeledSeg.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch (result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image::Pointer img = dynamic_cast<mitk::Image *>(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); // TODO disable slider etc... if (m_RegionGrow3DTool.IsNotNull()) { m_RegionGrow3DTool->ConfirmSegmentation(); } } template <typename TPixel, unsigned int VImageDimension> void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image<TPixel, VImageDimension> *itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast<mitk::Image *>(this->m_RegionGrow3DTool->GetTargetSegmentationNode()->GetData()); int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep(); if (originalSegmentation) { typedef itk::Image<TPixel, VImageDimension> InputImageType; typedef itk::Image<mitk::Tool::DefaultSegmentationDataType, VImageDimension> SegmentationType; // select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if (originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(originalSegmentation); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage(timeSelector->GetOutput(), originalSegmentationInITK); } else // use original { CastToItkImage(originalSegmentation, originalSegmentationInITK); } // Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator<SegmentationType> itOutput(originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion()); itk::ImageRegionIterator<InputImageType> itInput(itkImage, itkImage->GetLargestPossibleRegion()); itOutput.GoToBegin(); itInput.GoToBegin(); // calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } // iterate over image and set pixel in segmentation according to thresholded labeled image while (!itOutput.IsAtEnd() && !itInput.IsAtEnd()) { // Use threshold slider to determine if pixel is set to 1 - if (itInput.Value() != 0 && itInput.Value() >= currentTreshold) + if (itInput.Value() != 0 && itInput.Value() >= static_cast<itk::ImageRegionIterator<InputImageType>::PixelType>(currentTreshold)) { itOutput.Set(1); } ++itOutput; ++itInput; } // combine current working segmentation image with our region growing result originalSegmentation->SetVolume((void *)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled // if even m_DataStorage is nullptr leave node nullptr mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. // if even m_DataStorage is nullptr leave node nullptr node = m_DataStorage ? m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE) : nullptr; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (node.IsNull()) return; if (m_MultiWidget) m_MultiWidget->SetWidgetPlanesVisibility(!enable); if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } double val = this->m_Controls.m_PreviewSlider->value(); this->ChangeLevelWindow(val); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int thValue) { typedef short PixelType; typedef itk::Image<PixelType, 3> InputImageType; typedef itk::BinaryThresholdImageFilter<InputImageType, InputImageType> ThresholdFilterType; typedef itk::MaskImageFilter<InputImageType, InputImageType, InputImageType> MaskImageFilterType; mitk::DataNode::Pointer grownImageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::Image::Pointer grownImage = dynamic_cast<mitk::Image *>(grownImageNode->GetData()); if (!grownImage) { MITK_ERROR << "Missing data node for labeled segmentation image."; return; } InputImageType::Pointer itkGrownImage; mitk::CastToItkImage(grownImage, itkGrownImage); ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(itkGrownImage); thresholdFilter->SetInPlace(false); double sliderVal = this->m_Controls.m_PreviewSlider->value(); PixelType threshold = itk::NumericTraits<PixelType>::min(); if (m_CurrentRGDirectionIsUpwards) { threshold = static_cast<PixelType>(m_UPPERTHRESHOLD - sliderVal + 0.5); thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetUpperThreshold(itk::NumericTraits<PixelType>::max()); } else { threshold = sliderVal - m_LOWERTHRESHOLD + 0.5; thresholdFilter->SetLowerThreshold(itk::NumericTraits<PixelType>::min()); thresholdFilter->SetUpperThreshold(threshold); } thresholdFilter->UpdateLargestPossibleRegion(); MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(itkGrownImage); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMaskedImage; mitk::CastToMitkImage<InputImageType>(maskFilter->GetOutput(), mitkMaskedImage); mitk::DataNode::Pointer maskNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); maskNode->SetData(mitkMaskedImage); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue(double lowerThreshold) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue(double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (node.IsNotNull()) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp b/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp index adb80b6aab..91edd7b950 100644 --- a/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp +++ b/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp @@ -1,281 +1,281 @@ /*=================================================================== 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 <mitkToFNrrdImageWriter.h> // itk includes #include "itksys/SystemTools.hxx" #include "itkNrrdImageIO.h" namespace mitk { ToFNrrdImageWriter::ToFNrrdImageWriter(): ToFImageWriter(), m_DistanceOutfile(), m_AmplitudeOutfile(), m_IntensityOutfile() { m_Extension = std::string(".nrrd"); } ToFNrrdImageWriter::~ToFNrrdImageWriter() { } void ToFNrrdImageWriter::Open() { this->CheckForFileExtension(this->m_DistanceImageFileName); this->CheckForFileExtension(this->m_AmplitudeImageFileName); this->CheckForFileExtension(this->m_IntensityImageFileName); this->CheckForFileExtension(this->m_RGBImageFileName); this->m_ToFPixelNumber = this->m_ToFCaptureWidth * this->m_ToFCaptureHeight; this->m_ToFImageSizeInBytes = this->m_ToFPixelNumber * sizeof(float); this->m_RGBPixelNumber = this->m_RGBCaptureWidth * this->m_RGBCaptureHeight; this->m_RGBImageSizeInBytes = this->m_RGBPixelNumber * sizeof(unsigned char) * 3; if (this->m_DistanceImageSelected) { this->OpenStreamFile( this->m_DistanceOutfile, this->m_DistanceImageFileName); } if (this->m_AmplitudeImageSelected) { this->OpenStreamFile(this->m_AmplitudeOutfile, this->m_AmplitudeImageFileName); } if (this->m_IntensityImageSelected) { this->OpenStreamFile(this->m_IntensityOutfile, this->m_IntensityImageFileName); } if (this->m_RGBImageSelected) { this->OpenStreamFile(this->m_RGBOutfile, this->m_RGBImageFileName); } this->m_NumOfFrames = 0; } void ToFNrrdImageWriter::Close() { if (this->m_DistanceImageSelected) { this->CloseStreamFile(this->m_DistanceOutfile, this->m_DistanceImageFileName); } if (this->m_AmplitudeImageSelected) { this->CloseStreamFile(this->m_AmplitudeOutfile, this->m_AmplitudeImageFileName); } if (this->m_IntensityImageSelected) { this->CloseStreamFile(this->m_IntensityOutfile, this->m_IntensityImageFileName); } if (this->m_RGBImageSelected) { this->CloseStreamFile(this->m_RGBOutfile, this->m_RGBImageFileName); } } void ToFNrrdImageWriter::Add(float* distanceFloatData, float* amplitudeFloatData, float* intensityFloatData, unsigned char* rgbData) { if (this->m_DistanceImageSelected) { this->m_DistanceOutfile.write( (char*) distanceFloatData, this->m_ToFImageSizeInBytes); } if (this->m_AmplitudeImageSelected) { this->m_AmplitudeOutfile.write( (char*)amplitudeFloatData, this->m_ToFImageSizeInBytes); } if (this->m_IntensityImageSelected) { this->m_IntensityOutfile.write(( char* )intensityFloatData, this->m_ToFImageSizeInBytes); } if (this->m_RGBImageSelected) { this->m_RGBOutfile.write(( char* )rgbData, this->m_RGBImageSizeInBytes); } this->m_NumOfFrames++; } void ToFNrrdImageWriter::OpenStreamFile( std::ofstream &outfile, std::string outfileName ) { outfile.open(outfileName.c_str(), std::ofstream::binary); if(!outfile.is_open()) { MITK_ERROR << "Error opening outfile: " << outfileName; throw std::logic_error("Error opening outfile."); return; } } void ToFNrrdImageWriter::CloseStreamFile( std::ofstream &outfile, std::string fileName ) { if (this->m_NumOfFrames == 0) { outfile.close(); throw std::logic_error("File is empty."); return; } // flush the last data to the file and convert the stream data to nrrd file outfile.flush(); this->ConvertStreamToNrrdFormat( fileName ); outfile.close(); } void ToFNrrdImageWriter::ConvertStreamToNrrdFormat( std::string fileName ) { int CaptureWidth = 0; int CaptureHeight = 0; int PixelNumber = 0; int ImageSizeInBytes = 0; if (fileName==this->m_RGBImageFileName) { CaptureWidth = this->m_RGBCaptureWidth; CaptureHeight = this->m_RGBCaptureHeight; PixelNumber = this->m_RGBPixelNumber; ImageSizeInBytes = this->m_RGBImageSizeInBytes; } else { CaptureWidth = this->m_ToFCaptureWidth; CaptureHeight = this->m_ToFCaptureHeight; PixelNumber = this->m_ToFPixelNumber; ImageSizeInBytes = this->m_ToFImageSizeInBytes; } Image::Pointer imageTemplate = Image::New(); - int dimension ; + unsigned int dimension; unsigned int* dimensions; if(m_ToFImageType == ToFImageType2DPlusT) { dimension = 4; dimensions = new unsigned int[dimension]; dimensions[0] = CaptureWidth; dimensions[1] = CaptureHeight; dimensions[2] = 1; dimensions[3] = this->m_NumOfFrames; } else if( m_ToFImageType == ToFImageType3D) { dimension = 3; dimensions = new unsigned int[dimension]; dimensions[0] = CaptureWidth; dimensions[1] = CaptureHeight; dimensions[2] = this->m_NumOfFrames; } else { throw std::logic_error("No image type set, please choose between 2D+t and 3D!"); } float* floatData; unsigned char* rgbData; if (fileName==this->m_RGBImageFileName) { rgbData = new unsigned char[PixelNumber*3]; for(int i=0; i<PixelNumber*3; i++) { rgbData[i] = i + 0.0; } mitk::PixelType RGBType = MakePixelType<unsigned char, itk::RGBPixel<unsigned char>, 3>(); imageTemplate->Initialize( RGBType,dimension, dimensions, 1); imageTemplate->SetSlice(rgbData, 0, 0, 0); } else { floatData = new float[PixelNumber]; for(int i=0; i<PixelNumber; i++) { floatData[i] = i + 0.0; } mitk::PixelType FloatType = MakeScalarPixelType<float>(); imageTemplate->Initialize( FloatType,dimension, dimensions, 1); imageTemplate->SetSlice(floatData, 0, 0, 0); } itk::NrrdImageIO::Pointer nrrdWriter = itk::NrrdImageIO::New(); nrrdWriter->SetNumberOfDimensions(dimension); nrrdWriter->SetPixelType( imageTemplate->GetPixelType().GetPixelType()); nrrdWriter->SetComponentType( (itk::ImageIOBase::IOComponentType) imageTemplate->GetPixelType().GetComponentType()); if(imageTemplate->GetPixelType().GetNumberOfComponents() > 1) { nrrdWriter->SetNumberOfComponents(imageTemplate->GetPixelType().GetNumberOfComponents()); } itk::ImageIORegion ioRegion( dimension ); mitk::Vector3D spacing = imageTemplate->GetGeometry()->GetSpacing(); mitk::Point3D origin = imageTemplate->GetGeometry()->GetOrigin(); for(unsigned int i = 0; i < dimension; i++) { nrrdWriter->SetDimensions(i,dimensions[i]); nrrdWriter->SetSpacing(i,spacing[i]); nrrdWriter->SetOrigin(i,origin[i]); mitk::Vector3D direction; direction.Set_vnl_vector(imageTemplate->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); vnl_vector< double > axisDirection(dimension); for(unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction[j]/spacing[i]; } nrrdWriter->SetDirection( i, axisDirection ); ioRegion.SetSize(i, imageTemplate->GetLargestPossibleRegion().GetSize(i) ); ioRegion.SetIndex(i, imageTemplate->GetLargestPossibleRegion().GetIndex(i) ); } nrrdWriter->SetIORegion(ioRegion); nrrdWriter->SetFileName(fileName); nrrdWriter->SetUseStreamedWriting(true); std::ifstream stream(fileName.c_str(), std::ifstream::binary); if (fileName==m_RGBImageFileName) { unsigned int size = PixelNumber*3 * this->m_NumOfFrames; unsigned int sizeInBytes = size * sizeof(unsigned char); unsigned char* data = new unsigned char[size]; stream.read((char*)data, sizeInBytes); nrrdWriter->Write(data); stream.close(); delete[] data; } else { unsigned int size = PixelNumber * this->m_NumOfFrames; unsigned int sizeInBytes = size * sizeof(float); float* data = new float[size]; stream.read((char*)data, sizeInBytes); try { nrrdWriter->Write(data); } catch (itk::ExceptionObject* e) { MITK_ERROR<< e->what(); return; } stream.close(); delete[] data; } delete[] dimensions; if (fileName==m_RGBImageFileName) { delete[] rgbData; } else { delete[] floatData; } } } // end namespace mitk diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp index 36c0af5abd..98fb84f68e 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp +++ b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp @@ -1,218 +1,220 @@ /*=================================================================== 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 "mitkToFDistanceImageToPointSetFilter.h" #include "mitkImageDataItem.h" #include "mitkPointSet.h" #include <mitkImagePixelReadAccessor.h> #include "mitkToFProcessingCommon.h" mitk::ToFDistanceImageToPointSetFilter::ToFDistanceImageToPointSetFilter() : m_Subset(0), m_CameraIntrinsics(), m_InterPixelDistance() { m_InterPixelDistance.Fill(0.045); m_CameraIntrinsics = mitk::CameraIntrinsics::New(); m_CameraIntrinsics->SetFocalLength(5.9421434211923247e+02,5.9104053696870778e+02); m_CameraIntrinsics->SetPrincipalPoint(3.3930780975300314e+02,2.4273913761751615e+02); m_CameraIntrinsics->SetDistorsionCoeffs(-0.36874385358645773f,-0.14339503290129013,0.0033210108720361795,-0.004277703352074105); m_ReconstructionMode = true; } mitk::ToFDistanceImageToPointSetFilter::~ToFDistanceImageToPointSetFilter() { } void mitk::ToFDistanceImageToPointSetFilter::SetInput(const mitk::Image* distanceImage ) { this->SetInput(0,distanceImage); } void mitk::ToFDistanceImageToPointSetFilter::SetInput( unsigned int idx,const mitk::Image* distanceImage ) { if ((distanceImage == nullptr) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to nullptr, reduce the number of inputs by one { this->SetNumberOfInputs(this->GetNumberOfInputs() - 1); } else { this->ProcessObject::SetNthInput(idx, const_cast<mitk::Image*>(distanceImage)); // Process object is not const-correct so the const_cast is required here this->CreateOutputsForAllInputs(); } } mitk::Image* mitk::ToFDistanceImageToPointSetFilter::GetInput() { return this->GetInput(0); } mitk::Image* mitk::ToFDistanceImageToPointSetFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx)); } void mitk::ToFDistanceImageToPointSetFilter::SetSubset(std::vector<itk::Index<3> > subset) { // check if points of PointSet are inside the input image mitk::Image::Pointer input = this->GetInput(); unsigned int xDim = UINT_MAX; unsigned int yDim = UINT_MAX; + if(input.IsNotNull() && input->IsInitialized()) { - unsigned int xDim = input->GetDimension(0); - unsigned int yDim = input->GetDimension(1); + xDim = input->GetDimension(0); + yDim = input->GetDimension(1); } bool pointSetValid = true; for (unsigned int i=0; i<subset.size(); i++) { itk::Index<3> currentIndex = subset.at(i); - if (currentIndex[0]<0||currentIndex[0]>xDim||currentIndex[1]<0||currentIndex[1]>yDim) + if (currentIndex[0] < 0 || currentIndex[0] > static_cast<itk::IndexValueType>(xDim) || + currentIndex[1] < 0 || currentIndex[1] > static_cast<itk::IndexValueType>(yDim)) { pointSetValid = false; } } if (pointSetValid) { m_Subset = subset; } else { MITK_ERROR<<"One or more indizes are located outside the image domain"; } } void mitk::ToFDistanceImageToPointSetFilter::SetSubset( mitk::PointSet::Pointer pointSet) { std::vector<itk::Index<3> > subset; for (int i=0; i<pointSet->GetSize(); i++) { mitk::Point3D currentPoint = pointSet->GetPoint(i); itk::Index<3> currentIndex; currentIndex[0] = currentPoint[0]; currentIndex[1] = currentPoint[1]; currentIndex[2] = currentPoint[2]; subset.push_back(currentIndex); } this->SetSubset(subset); } void mitk::ToFDistanceImageToPointSetFilter::GenerateData() { //calculate world coordinates mitk::ToFProcessingCommon::ToFPoint2D focalLengthInPixelUnits; mitk::ToFProcessingCommon::ToFScalarType focalLengthInMm; if (m_ReconstructionMode) { focalLengthInPixelUnits[0] = m_CameraIntrinsics->GetFocalLengthX(); focalLengthInPixelUnits[1] = m_CameraIntrinsics->GetFocalLengthY(); } else focalLengthInMm = (m_CameraIntrinsics->GetFocalLengthX()*m_InterPixelDistance[0]+m_CameraIntrinsics->GetFocalLengthY()*m_InterPixelDistance[1])/2.0; mitk::ToFProcessingCommon::ToFPoint2D principalPoint; principalPoint[0] = m_CameraIntrinsics->GetPrincipalPointX(); principalPoint[1] = m_CameraIntrinsics->GetPrincipalPointY(); mitk::PointSet::Pointer output = this->GetOutput(); assert(output); mitk::Image::Pointer input = this->GetInput(); assert(input); //compute subset of points if input PointSet is defined if (m_Subset.size()!=0) { mitk::ImagePixelReadAccessor<float,2> imageAcces(input, input->GetSliceData(0)); for (unsigned int i=0; i<m_Subset.size(); i++) { itk::Index<3> currentIndex = m_Subset.at(i); itk::Index<2> index2D; index2D[0] = currentIndex[0]; index2D[1] = currentIndex[1]; mitk::ToFProcessingCommon::ToFScalarType distance = (double)imageAcces.GetPixelByIndex(index2D); mitk::Point3D currentPoint; if (m_ReconstructionMode) currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(currentIndex,distance,focalLengthInPixelUnits,principalPoint); else currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(currentIndex,distance,focalLengthInMm,m_InterPixelDistance,principalPoint); output->InsertPoint(i,currentPoint); } } else //compute PointSet holding cartesian coordinates for every image point { int xDimension = (int)input->GetDimension(0); int yDimension = (int)input->GetDimension(1); int pointCount = 0; mitk::ImagePixelReadAccessor<float,2> imageAcces(input, input->GetSliceData(0)); for (int j=0; j<yDimension; j++) { for (int i=0; i<xDimension; i++) { itk::Index<2> pixel; pixel[0] = i; pixel[1] = j; mitk::ToFProcessingCommon::ToFScalarType distance = (double)imageAcces.GetPixelByIndex(pixel); mitk::Point3D currentPoint; if (m_ReconstructionMode) currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLengthInPixelUnits,principalPoint); else currentPoint = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(i,j,distance,focalLengthInMm,m_InterPixelDistance,principalPoint); if (distance>mitk::eps) { output->InsertPoint( pointCount, currentPoint ); pointCount++; } } } } } void mitk::ToFDistanceImageToPointSetFilter::CreateOutputsForAllInputs() { this->SetNumberOfOutputs(this->GetNumberOfInputs()); // create outputs for all inputs for (unsigned int idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx) if (this->GetOutput(idx) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } this->Modified(); } void mitk::ToFDistanceImageToPointSetFilter::GenerateOutputInformation() { this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); } void mitk::ToFDistanceImageToPointSetFilter::SetReconstructionMode(bool withoutInterpixdist) { this->m_ReconstructionMode = withoutInterpixdist; } bool mitk::ToFDistanceImageToPointSetFilter::GetReconstructionMode() { return (this->m_ReconstructionMode); }